diff options
| author | Denis Ledoux <dle@odoo.com> | 2015-09-21 16:01:58 +0200 |
|---|---|---|
| committer | Denis Ledoux <dle@odoo.com> | 2015-09-21 16:01:58 +0200 |
| commit | a3648fd23a21efe3d2d507fb926b7f7cc55c1bba (patch) | |
| tree | ef5f6ae4fb1080319fada8e5b31571ae7b8e4c05 | |
| parent | 9975a04e4d81f718b13fef41b798d82d709a718b (diff) | |
| parent | aabbdc7339b9c2e1c10824ca2fc0a86419c46685 (diff) | |
[MERGE] forward port of branch 8.0 up to aabbdc7
40 files changed, 468 insertions, 319 deletions
diff --git a/addons/delivery/sale.py b/addons/delivery/sale.py index 63f04ef534b..f440ccdcbe1 100644 --- a/addons/delivery/sale.py +++ b/addons/delivery/sale.py @@ -85,8 +85,7 @@ class sale_order(osv.Model): if order.company_id.currency_id.id != order.pricelist_id.currency_id.id: price_unit = currency_obj.compute(cr, uid, order.company_id.currency_id.id, order.pricelist_id.currency_id.id, price_unit, context=dict(context or {}, date=order.date_order)) - #create the sale order line - line_id = line_obj.create(cr, uid, { + values = { 'order_id': order.id, 'name': grid.carrier_id.name, 'product_uom_qty': 1, @@ -94,7 +93,10 @@ class sale_order(osv.Model): 'product_id': grid.carrier_id.product_id.id, 'price_unit': price_unit, 'tax_id': [(6, 0, taxes_ids)], - 'is_delivery': True - }, context=context) + 'is_delivery': True, + } + if order.order_line: + values['sequence'] = order.order_line[-1].sequence + 1 + line_id = line_obj.create(cr, uid, values, context=context) line_ids.append(line_id) return line_ids diff --git a/addons/event/email_template.xml b/addons/event/email_template.xml index 75686a842ef..0f6c7231a46 100644 --- a/addons/event/email_template.xml +++ b/addons/event/email_template.xml @@ -7,6 +7,7 @@ <field name="model_id" ref="event.model_event_registration"/> <field name="email_from">${(object.event_id.organizer_id.email or object.event_id.user_id.email or '')|safe}</field> <field name="email_to" >${object.email|safe}</field> + <field name="lang">${object.partner_id.lang}</field> <field name="reply_to">${object.event_id.reply_to|safe}</field> <field name="subject">Your subscription at ${object.event_id.name}</field> <field name="body_html"><![CDATA[<p>Hello ${object.name},</p> @@ -21,6 +22,7 @@ You will automatically receive an email providing you more practical information <field name="model_id" ref="event.model_event_registration"/> <field name="email_from">${(object.event_id.organizer_id.email or object.event_id.user_id.email or '')|safe}</field> <field name="email_to" >${object.partner_id.email or object.email|safe}</field> + <field name="lang">${object.partner_id.lang}</field> <field name="reply_to">${object.event_id.reply_to|safe}</field> <field name="subject">Reminder of event ${object.event_id.name}</field> <field name="body_html"><![CDATA[<p>Hello ${object.name},</p> @@ -35,6 +37,7 @@ For any further information please contact our event department.</p> <field name="model_id" ref="event.model_event_registration"/> <field name="email_from">${(object.event_id.organizer_id.email or object.event_id.user_id.email or '')|safe}</field> <field name="email_to" >${object.partner_id.email or object.email|safe}</field> + <field name="lang">${object.partner_id.lang}</field> <field name="reply_to">${object.event_id.reply_to|safe}</field> <field name="subject">Thanks for attending ${object.event_id.name}</field> <field name="body_html"><![CDATA[<p>Hello ${object.name},</p> diff --git a/addons/hw_escpos/controllers/main.py b/addons/hw_escpos/controllers/main.py index 0b0f58027ab..d3635e47d0b 100644 --- a/addons/hw_escpos/controllers/main.py +++ b/addons/hw_escpos/controllers/main.py @@ -76,6 +76,14 @@ class EscposDriver(Thread): printers = usb.core.find(find_all=True, custom_match=FindUsbClass(7)) + # Currently we ask customers to put the STAR TSP650II into + # 'USB Mode' Vendor class instead of Printer class. When set + # to Printer class it doesn't show up under Linux at + # all. Vendor class does work, but that means that it's not + # going to have an interfaceClass 7. + if not printers: + printers = usb.core.find(find_all=True, idVendor=0x0519) + for printer in printers: connected.append({ 'vendor': printer.idVendor, diff --git a/addons/hw_escpos/escpos/exceptions.py b/addons/hw_escpos/escpos/exceptions.py index 51d860f1368..1bec14d5e00 100644 --- a/addons/hw_escpos/escpos/exceptions.py +++ b/addons/hw_escpos/escpos/exceptions.py @@ -86,7 +86,7 @@ class NoStatusError(Error): self.resultcode = 70 def __str__(self): - return "Impossible to get status from the printer" + return "Impossible to get status from the printer: " + str(self.msg) class TicketNotPrinted(Error): def __init__(self, msg=""): @@ -95,7 +95,7 @@ class TicketNotPrinted(Error): self.resultcode = 80 def __str__(self): - return "A part of the ticket was not been printed" + return "A part of the ticket was not been printed: " + str(self.msg) class NoDeviceError(Error): def __init__(self, msg=""): @@ -104,7 +104,7 @@ class NoDeviceError(Error): self.resultcode = 90 def __str__(self): - return "Impossible to find the printer Device" + return str(self.msg) class HandleDeviceError(Error): def __init__(self, msg=""): @@ -113,4 +113,4 @@ class HandleDeviceError(Error): self.resultcode = 100 def __str__(self): - return "Impossible to handle device" + return str(self.msg) diff --git a/addons/hw_escpos/escpos/printer.py b/addons/hw_escpos/escpos/printer.py index 37a530d8373..1f32875a5cb 100644 --- a/addons/hw_escpos/escpos/printer.py +++ b/addons/hw_escpos/escpos/printer.py @@ -58,7 +58,7 @@ class Usb(Escpos): return True except usb.core.USBError as e: i += 1 - if i > 100: + if i > 10: return False sleep(0.1) diff --git a/addons/hw_proxy/controllers/main.py b/addons/hw_proxy/controllers/main.py index eb95527efcd..173cfd599fe 100644 --- a/addons/hw_proxy/controllers/main.py +++ b/addons/hw_proxy/controllers/main.py @@ -108,24 +108,7 @@ class Proxy(http.Controller): if count == 0: resp += "<div class='device'>No USB Device Found</div>" - resp += "</div>\n" - resp += """ - <h2>Add New Printer</h2> - <p> - Copy and paste your printer's device description in the form below. You can find - your printer's description in the device list above. If you find that your printer works - well, please send your printer's description to <a href='mailto:support@odoo.com'> - support@openerp.com</a> so that we can add it to the default list of supported devices. - </p> - <form action='/hw_proxy/escpos/add_supported_device' method='GET'> - <input type='text' style='width:400px' name='device_string' placeholder='123a:b456 Sample Device description' /> - <input type='submit' value='submit' /> - </form> - <h2>Reset To Defaults</h2> - <p>If the added devices cause problems, you can <a href='/hw_proxy/escpos/reset_supported_devices'>Reset the - device list to factory default.</a> This operation cannot be undone.</p> - """ - resp += "</body>\n</html>\n\n" + resp += "</div>\n</body>\n</html>\n\n" return request.make_response(resp,{ 'Cache-Control': 'no-cache', diff --git a/addons/l10n_sa/__openerp__.py b/addons/l10n_sa/__openerp__.py index a37ea180971..182534cfacd 100644 --- a/addons/l10n_sa/__openerp__.py +++ b/addons/l10n_sa/__openerp__.py @@ -3,7 +3,6 @@ { 'name': 'Saudi Arabia - Accounting', 'version': '1.1', - 'sequence': 1, 'author': 'DVIT.ME', 'category': 'Localization/Account Charts', 'description': """ diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv index 12c808d2996..1304d1fbcb9 100644 --- a/addons/mail/security/ir.model.access.csv +++ b/addons/mail/security/ir.model.access.csv @@ -13,7 +13,7 @@ access_mail_notification_system,mail.notification.system,model_mail_notification access_mail_group_all,mail.group.all,model_mail_group,,1,0,0,0
access_mail_group_user,mail.group.user,model_mail_group,base.group_user,1,1,1,1
access_mail_alias_all,mail.alias.all,model_mail_alias,,1,0,0,0
-access_mail_alias_user,mail.alias.user,model_mail_alias,base.group_user,1,1,1,0
+access_mail_alias_user,mail.alias.user,model_mail_alias,base.group_user,1,1,1,1
access_mail_alias_system,mail.alias.system,model_mail_alias,base.group_system,1,1,1,1
access_mail_message_subtype_all,mail.message.subtype.all,model_mail_message_subtype,,1,0,0,0
access_mail_message_subtype_system,mail.message.subtype.system,model_mail_message_subtype,base.group_system,1,1,1,1
diff --git a/addons/membership/membership.py b/addons/membership/membership.py index 98c54d5548f..c9f5867af7d 100644 --- a/addons/membership/membership.py +++ b/addons/membership/membership.py @@ -232,7 +232,7 @@ class Partner(osv.osv): s = 4 if partner_data.member_lines: for mline in partner_data.member_lines: - if mline.date_to >= today: + if mline.date_to >= today and mline.date_from < today: if mline.account_invoice_line and mline.account_invoice_line.invoice_id: mstate = mline.account_invoice_line.invoice_id.state if mstate == 'paid': diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index c5a291a37f2..09290923942 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -458,6 +458,32 @@ class PaymentTransaction(osv.Model): return super(PaymentTransaction, self).create(cr, uid, values, context=context) + def write(self, cr, uid, ids, values, context=None): + Acquirer = self.pool['payment.acquirer'] + if ('acquirer_id' in values or 'amount' in values) and 'fees' not in values: + # The acquirer or the amount has changed, and the fees are not explicitely forced. Fees must be recomputed. + if isinstance(ids, (int, long)): + ids = [ids] + for txn_id in ids: + vals = dict(values) + vals['fees'] = 0.0 + transaction = self.browse(cr, uid, txn_id, context=context) + if 'acquirer_id' in values: + acquirer = Acquirer.browse(cr, uid, values['acquirer_id'], context=context) if values['acquirer_id'] else None + else: + acquirer = transaction.acquirer_id + if acquirer: + custom_method_name = '%s_compute_fees' % acquirer.provider + if hasattr(Acquirer, custom_method_name): + amount = (values['amount'] if 'amount' in values else transaction.amount) or 0.0 + currency_id = values.get('currency_id') or transaction.currency_id.id + country_id = values.get('partner_country_id') or transaction.partner_country_id.id + fees = getattr(Acquirer, custom_method_name)(cr, uid, acquirer.id, amount, currency_id, country_id, context=None) + vals['fees'] = float_round(fees, 2) + res = super(PaymentTransaction, self).write(cr, uid, txn_id, vals, context=context) + return res + return super(PaymentTransaction, self).write(cr, uid, ids, values, context=context) + def on_change_partner_id(self, cr, uid, ids, partner_id, context=None): partner = None if partner_id: diff --git a/addons/payment_ogone/models/ogone.py b/addons/payment_ogone/models/ogone.py index a70dda7d35f..1dc94c9197e 100644 --- a/addons/payment_ogone/models/ogone.py +++ b/addons/payment_ogone/models/ogone.py @@ -302,7 +302,7 @@ class PaymentTxOgone(osv.Model): 'state': 'cancel', 'acquirer_reference': data.get('PAYID'), }) - elif status in self._ogone_pending_tx_status: + elif status in self._ogone_pending_tx_status or status in self._ogone_wait_tx_status: tx.write({ 'state': 'pending', 'acquirer_reference': data.get('PAYID'), diff --git a/addons/stock_account/wizard/stock_valuation_history.py b/addons/stock_account/wizard/stock_valuation_history.py index 9865a70b736..b213c44c295 100644 --- a/addons/stock_account/wizard/stock_valuation_history.py +++ b/addons/stock_account/wizard/stock_valuation_history.py @@ -1,4 +1,5 @@ +from datetime import datetime from openerp import tools from openerp.osv import fields, osv from openerp.tools.translate import _ @@ -45,7 +46,7 @@ class stock_history(osv.osv): res = super(stock_history, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby, lazy=lazy) if context is None: context = {} - date = context.get('history_date') + date = context.get('history_date', datetime.now()) if 'inventory_value' in fields: group_lines = {} for line in res: diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 690e977e9f6..47851d40c25 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -452,8 +452,8 @@ def content_disposition(filename): version = int((request.httprequest.user_agent.version or '0').split('.')[0]) if browser == 'msie' and version < 9: return "attachment; filename=%s" % escaped - elif browser == 'safari': - return u"attachment; filename=%s" % filename + elif browser == 'safari' and version < 537: + return u"attachment; filename=%s" % filename.encode('ascii', 'replace') else: return "attachment; filename*=UTF-8''%s" % escaped diff --git a/addons/web/static/test/fulltest.html b/addons/web/static/test/fulltest.html index e08d57d4210..53463d89837 100644 --- a/addons/web/static/test/fulltest.html +++ b/addons/web/static/test/fulltest.html @@ -9,11 +9,33 @@ <script src="/web/static/lib/qunit/qunit.js" type="text/javascript"></script> <script src="/web/static/lib/underscore/underscore.js" type="text/javascript"></script> - <script src="/web/static/lib/underscore/underscore.string.js" type="text/javascript"></script> + <script src="/web/static/lib/underscore.string/lib/underscore.string.js" type="text/javascript"></script> <!-- jquery --> - <script src="/web/static/lib/jquery/jquery-1.7.2.js"></script> - <script src="/web/static/lib/jquery.ui/js/jquery-ui-1.8.17.custom.min.js"></script> + <script src="/web/static/lib/jquery/jquery.js"></script> + + + <link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css"/> + <script type="text/javascript" src="/web/static/lib/bootstrap/js/bootstrap.js"></script> + + <script type="text/javascript" charset="utf-8"> + $.fn.bstooltip = $.fn.tooltip; + $.fn.bsbutton = $.fn.button; + </script> + + <script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script> + + <script type="text/javascript" charset="utf-8"> + $.fn.tooltip = $.fn.bstooltip; + $.fn.button = $.fn.bsbutton; + </script> + + <link rel="stylesheet" href="/web/static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css"/> + <script type="text/javascript" src="/web/static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js"></script> + + <link rel="stylesheet" href="/web/static/lib/jquery.ui.notify/css/ui.notify.css"/> + <script type="text/javascript" src="/web/static/lib/jquery.ui.notify/js/jquery.notify.js"></script> + <script src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script> <script src="/web/static/lib/datejs/globalization/en-US.js"></script> @@ -23,15 +45,25 @@ <script src="/web/static/lib/datejs/extras.js"></script> <script src="/web/static/lib/qweb/qweb2.js"></script> + <script type="text/javascript" src="/web/static/src/js/openerpframework.js"></script> + <script type="text/javascript" src="/web/static/lib/backbone/backbone.js"></script> - <script src="/web/static/lib/py.js/lib/py.js"></script> - - <script src="/web/static/src/js/boot.js"></script> - <script src="/web/static/src/js/core.js"></script> - <script src="/web/static/src/js/dates.js"></script> - <script src="/web/static/src/js/formats.js"></script> - <script src="/web/static/src/js/chrome.js"></script> - <script src="/web/static/src/js/data.js"></script> + <script type="text/javascript" src="/web/static/lib/py.js/lib/py.js"></script> + <script type="text/javascript" src="/web/static/src/js/boot.js"></script> + <script type="text/javascript" src="/web/static/src/js/testing.js"></script> + <script type="text/javascript" src="/web/static/src/js/pyeval.js"></script> + <script type="text/javascript" src="/web/static/src/js/core.js"></script> + <script type="text/javascript" src="/web/static/src/js/formats.js"></script> + <script type="text/javascript" src="/web/static/src/js/chrome.js"></script> + <script type="text/javascript" src="/web/static/src/js/views.js"></script> + <script type="text/javascript" src="/web/static/src/js/data.js"></script> + <script type="text/javascript" src="/web/static/src/js/data_export.js"></script> + <script type="text/javascript" src="/web/static/src/js/search.js"></script> + <script type="text/javascript" src="/web/static/src/js/view_list.js"></script> + <script type="text/javascript" src="/web/static/src/js/view_form.js"></script> + <script type="text/javascript" src="/web/static/src/js/view_list_editable.js"></script> + <script type="text/javascript" src="/web/static/src/js/view_tree.js"></script> + <script type="text/javascript" src="/base/static/src/js/apps.js"></script> <script src="/web/static/src/js/test_support.js"></script> </head> diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 9d3e0f3aecb..587ac962846 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -441,7 +441,7 @@ class Website(openerp.addons.web.controllers.main.Home): '/website/image/<xmlid>/<field>/<int:max_width>x<int:max_height>', '/website/image/<model>/<id>/<field>', '/website/image/<model>/<id>/<field>/<int:max_width>x<int:max_height>' - ], auth="public", website=True) + ], auth="public", website=True, multilang=False) def website_image(self, model=None, id=None, field=None, xmlid=None, max_width=None, max_height=None): """ Fetches the requested field and ensures it does not go above (max_width, max_height), resizing it if necessary. diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index ef35b7be98d..80ef9a849f5 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -1122,7 +1122,7 @@ define(['summernote/summernote'], function () { // escape text nodes for xml saving var escaped_el = $el.clone(); var to_escape = escaped_el.find('*').addBack(); - to_escape = to_escape.not(to_escape.filter('script,style,[data-oe-model][data-oe-model!="ir.ui.view"]').find('*').addBack()); + to_escape = to_escape.not(to_escape.filter('object,iframe,script,style,[data-oe-model][data-oe-model!="ir.ui.view"]').find('*').addBack()); to_escape.contents().each(function(){ if(this.nodeType == 3) { this.nodeValue = $('<div />').text(this.nodeValue).html(); diff --git a/addons/website/static/src/xml/website.xml b/addons/website/static/src/xml/website.xml index a6cf26bf3d4..bfd20240677 100644 --- a/addons/website/static/src/xml/website.xml +++ b/addons/website/static/src/xml/website.xml @@ -9,7 +9,7 @@ <h3 class="modal-title">Mobile preview</h3> </div> <div class="modal-body"> - <iframe id="mobile-viewport" class="oe_mobile_viewport" src="?mobile-preview=true" style="width: 326px"></iframe> + <iframe id="mobile-viewport" class="oe_mobile_viewport" style="width: 326px"></iframe> </div> </div> </div> diff --git a/addons/website_membership/controllers/main.py b/addons/website_membership/controllers/main.py index 86671eef303..c9001445030 100644 --- a/addons/website_membership/controllers/main.py +++ b/addons/website_membership/controllers/main.py @@ -3,8 +3,10 @@ from openerp import SUPERUSER_ID from openerp.addons.web import http from openerp.addons.web.http import request from openerp.addons.website.models.website import unslug +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT from openerp.tools.translate import _ +import time import werkzeug.urls @@ -40,7 +42,11 @@ class WebsiteMembership(http.Controller): base_line_domain = [("partner.website_published", "=", True), ('state', 'in', ['free', 'paid'])] if membership_id and membership_id != 'free': membership_id = int(membership_id) - base_line_domain.append(('membership_id', '=', membership_id)) + today = time.strftime(DEFAULT_SERVER_DATE_FORMAT) + base_line_domain += [ + ('membership_id', '=', membership_id), ('date_to', '>=', today), + ('date_from', '<', today), ('state', '=', 'paid') + ] membership = product_obj.browse(cr, uid, membership_id, context=context) else: membership = None diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 6883ed79c07..f8afe06ff33 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -467,16 +467,31 @@ class website_sale(http.Controller): mandatory_shipping_fields = ["name", "phone", "street", "city", "country_id"] optional_shipping_fields = ["state_id", "zip"] + def _get_mandatory_billing_fields(self): + return self.mandatory_billing_fields + + def _get_optional_billing_fields(self): + return self.optional_billing_fields + + def _get_mandatory_shipping_fields(self): + return self.mandatory_shipping_fields + + def _get_optional_shipping_fields(self): + return self.optional_shipping_fields + + def _post_prepare_query(self, query, data, address_type): + return query + def checkout_parse(self, address_type, data, remove_prefix=False): """ data is a dict OR a partner browse record """ # set mandatory and optional fields assert address_type in ('billing', 'shipping') if address_type == 'billing': - all_fields = self.mandatory_billing_fields + self.optional_billing_fields + all_fields = self._get_mandatory_billing_fields() + self._get_optional_billing_fields() prefix = '' else: - all_fields = self.mandatory_shipping_fields + self.optional_shipping_fields + all_fields = self._get_mandatory_shipping_fields() + self._get_optional_shipping_fields() prefix = 'shipping_' # set data @@ -497,6 +512,8 @@ class website_sale(http.Controller): if query.get(prefix + 'vat'): query[prefix + 'vat_subjected'] = True + query = self._post_prepare_query(query, data, address_type) + if not remove_prefix: return query @@ -509,7 +526,7 @@ class website_sale(http.Controller): error_message = [] # Validation - for field_name in self.mandatory_billing_fields: + for field_name in self._get_mandatory_billing_fields(): if not data.get(field_name): error[field_name] = 'missing' @@ -531,7 +548,7 @@ class website_sale(http.Controller): error["vat"] = 'error' if data.get("shipping_id") == -1: - for field_name in self.mandatory_shipping_fields: + for field_name in self._get_mandatory_shipping_fields(): field_name = 'shipping_' + field_name if not data.get(field_name): error[field_name] = 'missing' @@ -542,6 +559,12 @@ class website_sale(http.Controller): return error, error_message + def _get_shipping_info(self, checkout): + shipping_info = {} + shipping_info.update(self.checkout_parse('shipping', checkout, True)) + shipping_info['type'] = 'delivery' + return shipping_info + def checkout_form_save(self, checkout): cr, uid, context, registry = request.cr, request.uid, request.context, request.registry @@ -578,11 +601,9 @@ class website_sale(http.Controller): # create a new shipping partner if checkout.get('shipping_id') == -1: - shipping_info = {} + shipping_info = self._get_shipping_info(checkout) if partner_lang: shipping_info['lang'] = partner_lang - shipping_info.update(self.checkout_parse('shipping', checkout, True)) - shipping_info['type'] = 'delivery' shipping_info['parent_id'] = partner_id checkout['shipping_id'] = orm_partner.create(cr, SUPERUSER_ID, shipping_info, context) diff --git a/doc/_extensions/odoo/__init__.py b/doc/_extensions/odoo/__init__.py index fb96c68b4b5..782a5f3b82e 100644 --- a/doc/_extensions/odoo/__init__.py +++ b/doc/_extensions/odoo/__init__.py @@ -2,13 +2,15 @@ from . import pygments_override from . import switcher -from . import translator import sphinx.environment import sphinx.builders.html from docutils import nodes def setup(app): - app.set_translator('html', translator.BootstrapTranslator) + if getattr(app.config, 'html_translator_class', None): + app.warn("Overriding the explicitly set html_translator_class setting", + location="odoo extension") + app.config.html_translator_class = 'odoo.translator.BootstrapTranslator' switcher.setup(app) app.add_config_value('odoo_cover_default', None, 'env') diff --git a/doc/_extensions/odoo/static/style.css b/doc/_extensions/odoo/static/style.css index 5165680ef4b..a24a820603a 100644 --- a/doc/_extensions/odoo/static/style.css +++ b/doc/_extensions/odoo/static/style.css @@ -9098,7 +9098,8 @@ hr.divider { width: 900%; margin-left: -13px; } -main .alert { +main .alert, +main .deprecated { padding: 15px; border-radius: 0; border-width: 0 0 0 3px; @@ -9110,25 +9111,35 @@ main .alert { color: #173038; } @media (min-width: 480px) { - main .alert { + main .alert, + main .deprecated { padding-left: 5.5em; } } main .alert > p, -main .alert > ul { +main .deprecated > p, +main .alert > ul, +main .deprecated > ul { margin: .5em 0; } main .alert > .alert-title, -main .alert > h3 { +main .deprecated > .alert-title, +main .alert > h3, +main .deprecated > h3 { color: #4b9eb6; } main .alert > .alert-title:before, -main .alert > h3:before { +main .deprecated > .alert-title:before, +main .alert > h3:before, +main .deprecated > h3:before { content: "\e639"; } main .alert > h3, -main .alert > .alert-title { - font-size: 1.642857143em; +main .deprecated > h3, +main .alert > .alert-title, +main .deprecated > .alert-title, +main .alert > p > .versionmodified, +main .deprecated > p > .versionmodified { line-height: 1em; margin: 0 0 10px 0; font-size: 14px; @@ -9136,12 +9147,15 @@ main .alert > .alert-title { font-family: Lato, sans-serif; } main .alert > h3:before, -main .alert > .alert-title:before { +main .deprecated > h3:before, +main .alert > .alert-title:before, +main .deprecated > .alert-title:before, +main .alert > p > .versionmodified:before, +main .deprecated > p > .versionmodified:before { font-family: 'Material-Design-Icons'; content: "\e639"; display: inline-block; text-rendering: geometricPrecision; - font-size: 1.6em; transform: translate(0, -0.15em); position: absolute; top: 50%; @@ -9152,83 +9166,144 @@ main .alert > .alert-title:before { } @media (max-width: 480px) { main .alert > h3:before, - main .alert > .alert-title:before { + main .deprecated > h3:before, + main .alert > .alert-title:before, + main .deprecated > .alert-title:before, + main .alert > p > .versionmodified:before, + main .deprecated > p > .versionmodified:before { display: none; } } -main .alert.alert-success { +main .alert.alert-success, +main .deprecated.alert-success { border-color: #b9dfb9; background-color: #eef7ee; color: #183518; } main .alert.alert-success > .alert-title, -main .alert.alert-success > h3 { +main .deprecated.alert-success > .alert-title, +main .alert.alert-success > h3, +main .deprecated.alert-success > h3 { color: #50af51; } main .alert.alert-success > .alert-title:before, -main .alert.alert-success > h3:before { +main .deprecated.alert-success > .alert-title:before, +main .alert.alert-success > h3:before, +main .deprecated.alert-success > h3:before { content: "\e625"; } main .alert.alert-info, -main .alert.tip { +main .deprecated.alert-info, +main .alert.tip, +main .deprecated.tip { border-color: #b8d9e2; background-color: #eef6f8; color: #173038; } main .alert.alert-info > .alert-title, +main .deprecated.alert-info > .alert-title, main .alert.tip > .alert-title, +main .deprecated.tip > .alert-title, main .alert.alert-info > h3, -main .alert.tip > h3 { +main .deprecated.alert-info > h3, +main .alert.tip > h3, +main .deprecated.tip > h3 { color: #4b9eb6; } main .alert.alert-info > .alert-title:before, +main .deprecated.alert-info > .alert-title:before, main .alert.tip > .alert-title:before, +main .deprecated.tip > .alert-title:before, main .alert.alert-info > h3:before, -main .alert.tip > h3:before { +main .deprecated.alert-info > h3:before, +main .alert.tip > h3:before, +main .deprecated.tip > h3:before { content: "\e639"; } main .alert.alert-warning, -main .alert.warning { +main .deprecated.alert-warning, +main .alert.warning, +main .deprecated.warning, +main .alert.deprecated, +main .deprecated.deprecated { border-color: #fceedb; background-color: #fef9f3; color: #81500b; } main .alert.alert-warning > .alert-title, +main .deprecated.alert-warning > .alert-title, main .alert.warning > .alert-title, +main .deprecated.warning > .alert-title, +main .alert.deprecated > .alert-title, +main .deprecated.deprecated > .alert-title, main .alert.alert-warning > h3, -main .alert.warning > h3 { +main .deprecated.alert-warning > h3, +main .alert.warning > h3, +main .deprecated.warning > h3, +main .alert.deprecated > h3, +main .deprecated.deprecated > h3, +main .alert.alert-warning > p > .versionmodified, +main .deprecated.alert-warning > p > .versionmodified, +main .alert.warning > p > .versionmodified, +main .deprecated.warning > p > .versionmodified, +main .alert.deprecated > p > .versionmodified, +main .deprecated.deprecated > p > .versionmodified { color: #f0ad4e; } main .alert.alert-warning > .alert-title:before, +main .deprecated.alert-warning > .alert-title:before, main .alert.warning > .alert-title:before, +main .deprecated.warning > .alert-title:before, +main .alert.deprecated > .alert-title:before, +main .deprecated.deprecated > .alert-title:before, main .alert.alert-warning > h3:before, -main .alert.warning > h3:before { +main .deprecated.alert-warning > h3:before, +main .alert.warning > h3:before, +main .deprecated.warning > h3:before, +main .alert.deprecated > h3:before, +main .deprecated.deprecated > h3:before, +main .alert.alert-warning > p > .versionmodified:before, +main .deprecated.alert-warning > p > .versionmodified:before, +main .alert.warning > p > .versionmodified:before, +main .deprecated.warning > p > .versionmodified:before, +main .alert.deprecated > p > .versionmodified:before, +main .deprecated.deprecated > p > .versionmodified:before { content: "\e6a4"; } -main .alert.alert-danger { +main .alert.alert-danger, +main .deprecated.alert-danger { border-color: #f4cecd; background-color: #fdf7f7; color: #611715; } main .alert.alert-danger > .alert-title, -main .alert.alert-danger > h3 { +main .deprecated.alert-danger > .alert-title, +main .alert.alert-danger > h3, +main .deprecated.alert-danger > h3 { color: #d9534f; } main .alert.alert-danger > .alert-title:before, -main .alert.alert-danger > h3:before { +main .deprecated.alert-danger > .alert-title:before, +main .alert.alert-danger > h3:before, +main .deprecated.alert-danger > h3:before { content: "\e6a4"; } -main .alert.alert-exercise { +main .alert.alert-exercise, +main .deprecated.alert-exercise { border-color: #deddde; background-color: #f7f7f7; color: #393639; } main .alert.alert-exercise > .alert-title, -main .alert.alert-exercise > h3 { +main .deprecated.alert-exercise > .alert-title, +main .alert.alert-exercise > h3, +main .deprecated.alert-exercise > h3 { color: #938e94; } main .alert.alert-exercise > .alert-title:before, -main .alert.alert-exercise > h3:before { +main .deprecated.alert-exercise > .alert-title:before, +main .alert.alert-exercise > h3:before, +main .deprecated.alert-exercise > h3:before { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); -o-transform: translate(0, 0); @@ -9237,10 +9312,14 @@ main .alert.alert-exercise > h3:before { content: "\e709"; } @media (min-width: 1170px) { - main .alert.doc-content { + main .alert.doc-content, + main .deprecated.doc-content { max-width: 55%; } } +main .deprecated .versionmodified { + display: block; +} .pq-patch { background: #c2c2c2; } @@ -9508,7 +9587,7 @@ aside { #navClone ul li { padding: 0; position: relative; - border-radius: none; + border-radius: 0; border-bottom: 1px solid rgba(35, 43, 65, 0.1); } .navbar-aside ul li > a, @@ -9520,7 +9599,7 @@ aside { color: #393f4f; font-weight: bold; display: block; - border-radius: none; + border-radius: 0; line-height: 1.2; background: #dbdbdb; border-bottom: none; @@ -9531,9 +9610,8 @@ aside { #navClone ul li > a:active, .navbar-aside ul li > a:focus, #navClone ul li > a:focus { - background-color: transparent; text-decoration: none; - border-radius: none; + border-radius: 0; background: #cecece; border-left: 5px solid rgba(33, 183, 153, 0.5); } @@ -9617,7 +9695,6 @@ aside { height: 56px; display: inline-block; z-index: 0; - background-color: transparent; border-radius: 50%; padding: 16px; box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.1), 0 6px 20px 0 rgba(0, 0, 0, 0.05); @@ -9636,7 +9713,7 @@ aside { position: absolute; z-index: 1; overflow: hidden; - right: 0%; + right: 0; width: 380px; bottom: 0; padding: 0; @@ -9650,7 +9727,6 @@ aside { box-shadow: 0 0 0 transparent; } #floating_action_menu .content { - margin-bottom: 0; margin: 0; opacity: 0; filter: alpha(opacity=0); diff --git a/doc/_extensions/odoo/static/style.less b/doc/_extensions/odoo/static/style.less index e6d0a5a3edf..e4fbdabeb21 100644 --- a/doc/_extensions/odoo/static/style.less +++ b/doc/_extensions/odoo/static/style.less @@ -353,7 +353,7 @@ hr.divider { margin-left: -13px; } -main .alert { +main .alert, main .deprecated { padding: 15px; border-radius: 0; border-width: 0 0 0 3px; @@ -368,8 +368,7 @@ main .alert { } .alert-info; // 'INFO' is the default style - > h3, > .alert-title { - font-size: 1.642857143em; + > h3, > .alert-title, > p > .versionmodified { line-height: 1em; margin: 0 0 10px 0; font-size: 14px; @@ -381,7 +380,6 @@ main .alert { content: "\e639"; display: inline-block; text-rendering: geometricPrecision; - font-size: 1.6em; transform: translate(0, -0.15em); position: absolute; top: 50%; @@ -412,22 +410,22 @@ main .alert { color: darken(@doc_info, 35%); > .alert-title, > h3 { - color: @doc_info - } - > .alert-title:before, > h3:before { - content: "\e639"; + color: @doc_info; + &:before { + content: "\e639"; + } } } - &.alert-warning, &.warning { + &.alert-warning, &.warning, &.deprecated { border-color: lighten(@doc_warning, 30%); background-color: lighten(@doc_warning, 35%); color: darken(@doc_warning, 35%); - > .alert-title, > h3 { - color: @doc_warning - } - > .alert-title:before, > h3:before { - content: "\e6a4"; + > .alert-title, > h3, > p > .versionmodified { + color: @doc_warning; + &:before { + content: "\e6a4"; + } } } &.alert-danger { @@ -436,10 +434,10 @@ main .alert { color: darken(@doc_danger, 35%); > .alert-title, > h3 { - color: @doc_danger - } - > .alert-title:before, > h3:before { - content: "\e6a4"; + color: @doc_danger; + &:before { + content: "\e6a4"; + } } } &.alert-exercise { @@ -449,11 +447,11 @@ main .alert { > .alert-title, > h3 { color: @doc_exercise; - } - > .alert-title:before, > h3:before { - .translate(0;0); - top: 28px; - content: "\e709"; + &:before { + .translate(0;0); + top: 28px; + content: "\e709"; + } } } @@ -463,6 +461,9 @@ main .alert { } } } +main .deprecated .versionmodified { + display: block; +} .pq-patch{ background: rgb(194, 194, 194); @@ -688,7 +689,7 @@ aside{ li { padding: 0; position:relative; - border-radius: none; + border-radius: 0; border-bottom: 1px solid fadeout(@doc_text, 90%); > a { font-size: 0.7em; @@ -698,15 +699,14 @@ aside{ color: @doc_heading; font-weight: bold; display: block; - border-radius: none; + border-radius: 0; line-height: 1.2; background: darken(@doc_paper_dark, 10%); border-bottom: none; &:hover, &:active, &:focus { - background-color: transparent; text-decoration: none; - border-radius: none; + border-radius: 0; background: darken(@doc_paper_dark, 15%); border-left: 5px solid fadeout(@doc_accent, 50%); } @@ -788,7 +788,6 @@ aside{ height: 56px; display: inline-block; z-index: 0; - background-color: transparent; border-radius: 50%; padding: 16px; box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.1), 0 6px 20px 0 rgba(0, 0, 0, 0.05); @@ -806,7 +805,7 @@ aside{ position: absolute; z-index: 1; overflow: hidden; - right: 0%; + right: 0; width: 380px; bottom: 0; padding: 0; @@ -817,7 +816,6 @@ aside{ .box-shadow(0 0 0 transparent); .content { - margin-bottom: 0; margin:0; li { border: none; diff --git a/doc/howtos/backend.rst b/doc/howtos/backend.rst index a47bfc93a46..0b576dcbb78 100644 --- a/doc/howtos/backend.rst +++ b/doc/howtos/backend.rst @@ -787,13 +787,6 @@ method should simply set the value of the field to compute on every record in for record in self: record.name = str(random.randint(1, 1e6)) -Our compute method is very simple: it loops over ``self`` and performs the same -operation on every record. We can make it slightly simpler by using the -decorator :func:`~openerp.api.one` to automatically loop on the collection:: - - @api.one - def _compute_name(self): - self.name = str(random.randint(1, 1e6)) Dependencies ------------ @@ -812,10 +805,10 @@ field whenever some of its dependencies have been modified:: name = fields.Char(compute='_compute_name') value = fields.Integer() - @api.one @api.depends('value') def _compute_name(self): - self.name = "Record with value %s" % self.value + for record in self: + self.name = "Record with value %s" % self.value .. exercise:: Computed fields diff --git a/doc/howtos/backend/exercise-calendar b/doc/howtos/backend/exercise-calendar index a5689111174..3079cd69b09 100644 --- a/doc/howtos/backend/exercise-calendar +++ b/doc/howtos/backend/exercise-calendar @@ -1,10 +1,10 @@ # HG changeset patch # Parent 85a8d7317b9e13480f39ad739955442d15144451 +# Parent 16fcdc4c6462a7872636f3c19550c16879af5281 -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py 2014-08-28 13:45:01.987048512 +0200 -+++ addons/openacademy/models.py 2014-08-28 13:59:30.387035620 +0200 +diff --git a/openacademy/models.py b/openacademy/models.py +--- a/openacademy/models.py ++++ b/openacademy/models.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- @@ -12,50 +12,49 @@ Index: addons/openacademy/models.py from openerp import models, fields, api, exceptions class Course(models.Model): -@@ -55,6 +56,8 @@ +@@ -55,6 +56,8 @@ class Session(models.Model): attendee_ids = fields.Many2many('res.partner', string="Attendees") taken_seats = fields.Float(string="Taken seats", compute='_taken_seats') + end_date = fields.Date(string="End Date", store=True, + compute='_get_end_date', inverse='_set_end_date') - @api.one @api.depends('seats', 'attendee_ids') -@@ -82,6 +85,30 @@ + def _taken_seats(self): +@@ -81,6 +84,30 @@ class Session(models.Model): + }, } - @api.one + @api.depends('start_date', 'duration') + def _get_end_date(self): -+ if not (self.start_date and self.duration): -+ self.end_date = self.start_date -+ return ++ for r in self: ++ if not (r.start_date and r.duration): ++ r.end_date = r.start_date ++ continue + -+ # Add duration to start_date, but: Monday + 5 days = Saturday, so -+ # subtract one second to get on Friday instead -+ start = fields.Datetime.from_string(self.start_date) -+ duration = timedelta(days=self.duration, seconds=-1) -+ self.end_date = start + duration ++ # Add duration to start_date, but: Monday + 5 days = Saturday, so ++ # subtract one second to get on Friday instead ++ start = fields.Datetime.from_string(r.start_date) ++ duration = timedelta(days=r.duration, seconds=-1) ++ r.end_date = start + duration + -+ @api.one + def _set_end_date(self): -+ if not (self.start_date and self.end_date): -+ return ++ for r in self: ++ if not (r.start_date and r.end_date): ++ continue + -+ # Compute the difference between dates, but: Friday - Monday = 4 days, -+ # so add one day to get 5 days instead -+ start_date = fields.Datetime.from_string(self.start_date) -+ end_date = fields.Datetime.from_string(self.end_date) -+ self.duration = (end_date - start_date).days + 1 ++ # Compute the difference between dates, but: Friday - Monday = 4 days, ++ # so add one day to get 5 days instead ++ start_date = fields.Datetime.from_string(r.start_date) ++ end_date = fields.Datetime.from_string(r.end_date) ++ r.duration = (end_date - start_date).days + 1 + -+ @api.one @api.constrains('instructor_id', 'attendee_ids') def _check_instructor_not_in_attendees(self): - if self.instructor_id and self.instructor_id in self.attendee_ids: -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml 2014-08-28 13:45:01.987048512 +0200 -+++ addons/openacademy/views/openacademy.xml 2014-08-28 13:54:02.000000000 +0200 + for r in self: +diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml +--- a/openacademy/views/openacademy.xml ++++ b/openacademy/views/openacademy.xml @@ -125,11 +125,24 @@ </field> </record> diff --git a/doc/howtos/backend/exercise-computed b/doc/howtos/backend/exercise-computed index 9d980094084..eedeae16a58 100644 --- a/doc/howtos/backend/exercise-computed +++ b/doc/howtos/backend/exercise-computed @@ -1,28 +1,27 @@ # HG changeset patch # Parent a358be0a577b0569831958a8ec1302825c645dee +# Parent 59107edbe5f81bab5e7db172bf2bffa504ce399a -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py 2014-08-26 17:26:03.795783315 +0200 -+++ addons/openacademy/models.py 2014-08-26 17:26:03.791783315 +0200 -@@ -28,3 +28,13 @@ +diff --git a/openacademy/models.py b/openacademy/models.py +--- a/openacademy/models.py ++++ b/openacademy/models.py +@@ -28,3 +28,13 @@ class Session(models.Model): course_id = fields.Many2one('openacademy.course', ondelete='cascade', string="Course", required=True) attendee_ids = fields.Many2many('res.partner', string="Attendees") + + taken_seats = fields.Float(string="Taken seats", compute='_taken_seats') + -+ @api.one + @api.depends('seats', 'attendee_ids') + def _taken_seats(self): -+ if not self.seats: -+ self.taken_seats = 0.0 -+ else: -+ self.taken_seats = 100.0 * len(self.attendee_ids) / self.seats -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml 2014-08-26 17:26:03.795783315 +0200 -+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:26:03.791783315 +0200 ++ for r in self: ++ if not r.seats: ++ r.taken_seats = 0.0 ++ else: ++ r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats +diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml +--- a/openacademy/views/openacademy.xml ++++ b/openacademy/views/openacademy.xml @@ -100,6 +100,7 @@ <field name="start_date"/> <field name="duration"/> diff --git a/doc/howtos/backend/exercise-constraint-python b/doc/howtos/backend/exercise-constraint-python index 9818fe8d395..290e09aa5a1 100644 --- a/doc/howtos/backend/exercise-constraint-python +++ b/doc/howtos/backend/exercise-constraint-python @@ -1,10 +1,10 @@ # HG changeset patch # Parent 7a7d003fe38426a405ce0657a627a139133ec4dd +# Parent 52f54b46487c8224a5aade4b921be77360ed3eae -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py 2014-08-26 17:26:06.591783274 +0200 -+++ addons/openacademy/models.py 2014-08-26 17:26:06.587783274 +0200 +diff --git a/openacademy/models.py b/openacademy/models.py +--- a/openacademy/models.py ++++ b/openacademy/models.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- @@ -13,13 +13,13 @@ Index: addons/openacademy/models.py class Course(models.Model): _name = 'openacademy.course' -@@ -56,3 +56,9 @@ +@@ -56,3 +56,9 @@ class Session(models.Model): 'message': "Increase seats or remove excess attendees", }, } + -+ @api.one + @api.constrains('instructor_id', 'attendee_ids') + def _check_instructor_not_in_attendees(self): -+ if self.instructor_id and self.instructor_id in self.attendee_ids: -+ raise exceptions.ValidationError("A session's instructor can't be an attendee") ++ for r in self: ++ if r.instructor_id and r.instructor_id in r.attendee_ids: ++ raise exceptions.ValidationError("A session's instructor can't be an attendee") diff --git a/doc/howtos/backend/exercise-copy-override b/doc/howtos/backend/exercise-copy-override index 3f7dce2840f..148eace9b78 100644 --- a/doc/howtos/backend/exercise-copy-override +++ b/doc/howtos/backend/exercise-copy-override @@ -1,15 +1,15 @@ # HG changeset patch # Parent 7d14b75cdfd4c7a272a13572947de5d47f3e851f +# Parent f400352a70963801f0b4732d33a0183e4f6800ff -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py 2014-08-28 11:00:44.343194847 +0200 -+++ addons/openacademy/models.py 2014-08-28 13:44:32.143048955 +0200 -@@ -13,6 +13,20 @@ +diff --git a/openacademy/models.py b/openacademy/models.py +--- a/openacademy/models.py ++++ b/openacademy/models.py +@@ -13,6 +13,20 @@ class Course(models.Model): session_ids = fields.One2many( 'openacademy.session', 'course_id', string="Sessions") -+ @api.one ++ @api.multi + def copy(self, default=None): + default = dict(default or {}) + diff --git a/doc/howtos/backend/exercise-gantt b/doc/howtos/backend/exercise-gantt index 273854b197d..da37764a010 100644 --- a/doc/howtos/backend/exercise-gantt +++ b/doc/howtos/backend/exercise-gantt @@ -1,40 +1,39 @@ # HG changeset patch # Parent dba00a105dd2a82490394b8dec5fea5f1d8847e1 +# Parent f4374b6e2e661e0782e396b24c57c1eb97d13288 -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py 2014-08-28 14:21:46.543015785 +0200 -+++ addons/openacademy/models.py 2014-08-28 14:21:46.539015785 +0200 -@@ -59,6 +59,9 @@ +diff --git a/openacademy/models.py b/openacademy/models.py +--- a/openacademy/models.py ++++ b/openacademy/models.py +@@ -59,6 +59,9 @@ class Session(models.Model): end_date = fields.Date(string="End Date", store=True, compute='_get_end_date', inverse='_set_end_date') + hours = fields.Float(string="Duration in hours", + compute='_get_hours', inverse='_set_hours') + - @api.one @api.depends('seats', 'attendee_ids') def _taken_seats(self): -@@ -109,6 +112,15 @@ - self.duration = (end_date - start_date).days + 1 + for r in self: +@@ -108,6 +111,15 @@ class Session(models.Model): + end_date = fields.Datetime.from_string(r.end_date) + r.duration = (end_date - start_date).days + 1 - @api.one + @api.depends('duration') + def _get_hours(self): -+ self.hours = self.duration * 24 ++ for r in self: ++ r.hours = r.duration * 24 + -+ @api.one + def _set_hours(self): -+ self.duration = self.hours / 24 ++ for r in self: ++ r.duration = r.hours / 24 + -+ @api.one @api.constrains('instructor_id', 'attendee_ids') def _check_instructor_not_in_attendees(self): - if self.instructor_id and self.instructor_id in self.attendee_ids: -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml 2014-08-28 14:21:46.543015785 +0200 -+++ addons/openacademy/views/openacademy.xml 2014-08-28 14:21:46.539015785 +0200 + for r in self: +diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml +--- a/openacademy/views/openacademy.xml ++++ b/openacademy/views/openacademy.xml @@ -145,11 +145,23 @@ </field> </record> diff --git a/doc/howtos/backend/exercise-graph b/doc/howtos/backend/exercise-graph index 6b913ead5be..07fdd467ef1 100644 --- a/doc/howtos/backend/exercise-graph +++ b/doc/howtos/backend/exercise-graph @@ -1,37 +1,36 @@ # HG changeset patch # Parent a6fe4d3923db1f8f5dff2c39a711a814b0a0f549 +# Parent 0687e07f570f363bf5005c6337e0c565a63a6bac -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py 2014-08-28 14:21:55.039015659 +0200 -+++ addons/openacademy/models.py 2014-08-28 14:21:55.031015659 +0200 -@@ -62,6 +62,9 @@ +diff --git a/openacademy/models.py b/openacademy/models.py +--- a/openacademy/models.py ++++ b/openacademy/models.py +@@ -62,6 +62,9 @@ class Session(models.Model): hours = fields.Float(string="Duration in hours", compute='_get_hours', inverse='_set_hours') + attendees_count = fields.Integer( + string="Attendees count", compute='_get_attendees_count', store=True) + - @api.one @api.depends('seats', 'attendee_ids') def _taken_seats(self): -@@ -121,6 +124,11 @@ - self.duration = self.hours / 24 + for r in self: +@@ -120,6 +123,11 @@ class Session(models.Model): + for r in self: + r.duration = r.hours / 24 - @api.one + @api.depends('attendee_ids') + def _get_attendees_count(self): -+ self.attendees_count = len(self.attendee_ids) ++ for r in self: ++ r.attendees_count = len(r.attendee_ids) + -+ @api.one @api.constrains('instructor_id', 'attendee_ids') def _check_instructor_not_in_attendees(self): - if self.instructor_id and self.instructor_id in self.attendee_ids: -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml 2014-08-28 14:21:55.039015659 +0200 -+++ addons/openacademy/views/openacademy.xml 2014-08-28 14:21:55.031015659 +0200 -@@ -158,11 +158,22 @@ + for r in self: +diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml +--- a/openacademy/views/openacademy.xml ++++ b/openacademy/views/openacademy.xml +@@ -157,11 +157,22 @@ </field> </record> diff --git a/doc/howtos/backend/exercise-onchange b/doc/howtos/backend/exercise-onchange index 498379b302d..15f64273256 100644 --- a/doc/howtos/backend/exercise-onchange +++ b/doc/howtos/backend/exercise-onchange @@ -1,14 +1,14 @@ # HG changeset patch # Parent 8d5573b704b2867788dd6895503f1871c2976a29 +# Parent 9eb163e5da677a0d09e01a354ba56697b576a4bc -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py 2014-08-26 17:26:05.687783287 +0200 -+++ addons/openacademy/models.py 2014-08-26 17:26:05.679783287 +0200 -@@ -39,3 +39,20 @@ - self.taken_seats = 0.0 - else: - self.taken_seats = 100.0 * len(self.attendee_ids) / self.seats +diff --git a/openacademy/models.py b/openacademy/models.py +--- a/openacademy/models.py ++++ b/openacademy/models.py +@@ -39,3 +39,20 @@ class Session(models.Model): + r.taken_seats = 0.0 + else: + r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats + + @api.onchange('seats', 'attendee_ids') + def _verify_valid_seats(self): diff --git a/doc/howtos/backend/exercise-state-basic b/doc/howtos/backend/exercise-state-basic index 55085c56c3d..f70a41b4dae 100644 --- a/doc/howtos/backend/exercise-state-basic +++ b/doc/howtos/backend/exercise-state-basic @@ -1,39 +1,38 @@ # HG changeset patch # Parent 8c721171aa16a41e94059f53d6780c67b5ef2dfc +# Parent 8d2ca42b5be2031ea9624896df53f09f7ca131be -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py 2014-08-28 14:22:01.371015565 +0200 -+++ addons/openacademy/models.py 2014-08-28 14:22:01.367015565 +0200 -@@ -66,6 +66,24 @@ +diff --git a/openacademy/models.py b/openacademy/models.py +--- a/openacademy/models.py ++++ b/openacademy/models.py +@@ -66,6 +66,24 @@ class Session(models.Model): attendees_count = fields.Integer( string="Attendees count", compute='_get_attendees_count', store=True) + state = fields.Selection([ -+ ('draft', "Draft"), -+ ('confirmed', "Confirmed"), -+ ('done', "Done"), ++ ('draft', "Draft"), ++ ('confirmed', "Confirmed"), ++ ('done', "Done"), + ], default='draft') + -+ @api.one ++ @api.multi + def action_draft(self): + self.state = 'draft' + -+ @api.one ++ @api.multi + def action_confirm(self): + self.state = 'confirmed' + -+ @api.one ++ @api.multi + def action_done(self): + self.state = 'done' + - @api.one @api.depends('seats', 'attendee_ids') def _taken_seats(self): -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml 2014-08-28 14:22:01.371015565 +0200 -+++ addons/openacademy/views/openacademy.xml 2014-08-28 14:22:01.367015565 +0200 + for r in self: +diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml +--- a/openacademy/views/openacademy.xml ++++ b/openacademy/views/openacademy.xml @@ -96,6 +96,19 @@ <field name="model">openacademy.session</field> <field name="arch" type="xml"> diff --git a/doc/howtos/backend/exercise-state-workflow b/doc/howtos/backend/exercise-state-workflow index 0e1bf4b75b3..062f8b46a6b 100644 --- a/doc/howtos/backend/exercise-state-workflow +++ b/doc/howtos/backend/exercise-state-workflow @@ -1,10 +1,10 @@ # HG changeset patch # Parent c72382bf0c5794135fa318f6ba59899b2277d8c5 +# Parent 82f902dbb7aab4d3ddd9caaa9170536afa9d274d -Index: addons/openacademy/__openerp__.py -=================================================================== ---- addons.orig/openacademy/__openerp__.py 2014-08-28 14:22:04.135015524 +0200 -+++ addons/openacademy/__openerp__.py 2014-08-28 14:22:04.131015524 +0200 +diff --git a/openacademy/__openerp__.py b/openacademy/__openerp__.py +--- a/openacademy/__openerp__.py ++++ b/openacademy/__openerp__.py @@ -29,6 +29,7 @@ 'templates.xml', 'views/openacademy.xml', @@ -13,23 +13,21 @@ Index: addons/openacademy/__openerp__.py ], # only loaded in demonstration mode 'demo': [ -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py 2014-08-28 14:22:04.135015524 +0200 -+++ addons/openacademy/models.py 2014-08-28 14:22:04.131015524 +0200 -@@ -70,7 +70,7 @@ - ('draft', "Draft"), - ('confirmed', "Confirmed"), - ('done', "Done"), +diff --git a/openacademy/models.py b/openacademy/models.py +--- a/openacademy/models.py ++++ b/openacademy/models.py +@@ -70,7 +70,7 @@ class Session(models.Model): + ('draft', "Draft"), + ('confirmed', "Confirmed"), + ('done', "Done"), - ], default='draft') + ]) - @api.one + @api.multi def action_draft(self): -Index: addons/openacademy/views/openacademy.xml -=================================================================== ---- addons.orig/openacademy/views/openacademy.xml 2014-08-28 14:22:04.135015524 +0200 -+++ addons/openacademy/views/openacademy.xml 2014-08-28 14:22:04.131015524 +0200 +diff --git a/openacademy/views/openacademy.xml b/openacademy/views/openacademy.xml +--- a/openacademy/views/openacademy.xml ++++ b/openacademy/views/openacademy.xml @@ -97,13 +97,13 @@ <field name="arch" type="xml"> <form string="Session Form"> @@ -47,10 +45,10 @@ Index: addons/openacademy/views/openacademy.xml string="Mark as done" states="confirmed" class="oe_highlight"/> <field name="state" widget="statusbar"/> -Index: addons/openacademy/views/session_workflow.xml -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ addons/openacademy/views/session_workflow.xml 2014-08-28 14:22:04.131015524 +0200 +diff --git a/openacademy/views/session_workflow.xml b/openacademy/views/session_workflow.xml +new file mode 100644 +--- /dev/null ++++ b/openacademy/views/session_workflow.xml @@ -0,0 +1,50 @@ +<openerp> + <data> diff --git a/doc/howtos/backend/exercise-translations b/doc/howtos/backend/exercise-translations index 6d6bdfb1afa..707abe27557 100644 --- a/doc/howtos/backend/exercise-translations +++ b/doc/howtos/backend/exercise-translations @@ -1,7 +1,9 @@ -Index: addons/openacademy/models.py -=================================================================== ---- addons.orig/openacademy/models.py 2014-08-28 14:02:42.203032773 +0200 -+++ addons/openacademy/models.py 2014-08-28 14:06:54.871029022 +0200 +# HG changeset patch +# Parent 7c95aad3b60e4c2006c5f706bd157e8e05318bfa + +diff --git a/openacademy/models.py b/openacademy/models.py +--- a/openacademy/models.py ++++ b/openacademy/models.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- @@ -11,7 +13,7 @@ Index: addons/openacademy/models.py class Course(models.Model): _name = 'openacademy.course' -@@ -19,11 +19,11 @@ +@@ -19,11 +19,11 @@ class Course(models.Model): default = dict(default or {}) copied_count = self.search_count( @@ -26,7 +28,7 @@ Index: addons/openacademy/models.py default['name'] = new_name return super(Course, self).copy(default) -@@ -97,15 +97,15 @@ +@@ -97,15 +97,15 @@ class Session(models.Model): if self.seats < 0: return { 'warning': { @@ -46,9 +48,9 @@ Index: addons/openacademy/models.py }, } -@@ -151,4 +151,4 @@ - @api.constrains('instructor_id', 'attendee_ids') +@@ -151,4 +151,4 @@ class Session(models.Model): def _check_instructor_not_in_attendees(self): - if self.instructor_id and self.instructor_id in self.attendee_ids: -- raise exceptions.ValidationError("A session's instructor can't be an attendee") -+ raise exceptions.ValidationError(_("A session's instructor can't be an attendee")) + for r in self: + if r.instructor_id and r.instructor_id in r.attendee_ids: +- raise exceptions.ValidationError("A session's instructor can't be an attendee") ++ raise exceptions.ValidationError(_("A session's instructor can't be an attendee")) diff --git a/doc/reference/orm.rst b/doc/reference/orm.rst index df216497688..5fbd7a59977 100644 --- a/doc/reference/orm.rst +++ b/doc/reference/orm.rst @@ -766,8 +766,8 @@ Method decorators ================= .. automodule:: openerp.api - :members: one, multi, model, depends, constrains, onchange, returns, - v7, v8 + :members: multi, model, depends, constrains, onchange, returns, + one, v7, v8 .. _reference/orm/fields: diff --git a/doc/reference/upgrade_api.rst b/doc/reference/upgrade_api.rst index 1e437707857..0581e2bf614 100644 --- a/doc/reference/upgrade_api.rst +++ b/doc/reference/upgrade_api.rst @@ -10,11 +10,14 @@ Upgrade API =========== +Introduction +~~~~~~~~~~~~ + This document describes the API used to upgrade an Odoo database to a higher version. It allows a database to be upgraded without ressorting to the html form at -https://upgrade.odoo.com/database/upload +https://upgrade.odoo.com Although the database will follow the same process described on that form. @@ -26,6 +29,9 @@ The required steps are: * obtaining the status of the database request * downloading the upgraded database dump +The methods +~~~~~~~~~~~ + .. _upgrade-api-create-method: Creating a database upgrade request @@ -40,8 +46,8 @@ This action creates a database request with the following information: * the database dump name (required but purely informative) * optionally the server timezone (for Odoo source version < 6.1) -create method -------------- +The ``create`` method +--------------------- .. py:function:: https://upgrade.odoo.com/database/v1/create @@ -56,11 +62,10 @@ create method :return: request result :rtype: json dictionary - The *create* method returns a json dictionary containing the following keys: -failures -'''''''' +``failures`` +'''''''''''' The list of errors. @@ -107,8 +112,8 @@ See a sample output aside. } -request -''''''' +``request`` +''''''''''' If the *create* method is successful, the value associated to the *request* key will be a dictionary containing various information about the created request: @@ -198,8 +203,8 @@ Uploading your database dump This action upload your database dump. -upload method -------------- +The ``upload`` method +--------------------- .. py:function:: https://upgrade.odoo.com/database/v1/upload @@ -269,8 +274,8 @@ Asking to process your request This action ask the Upgrade Platform to process your database dump. -process method --------------- +The ``process`` method +---------------------- .. py:function:: https://upgrade.odoo.com/database/v1/process @@ -335,8 +340,8 @@ Obtaining your request status This action ask the status of your database upgrade request. -status method -------------- +The ``status`` method +--------------------- .. py:function:: https://upgrade.odoo.com/database/v1/status @@ -392,7 +397,6 @@ database upgrade request. URL_PARAMS="key=${KEY}&request=${REQUEST_ID}" curl -sS "${PROCESS_URL}?${URL_PARAMS}" - Sample output ------------- diff --git a/openerp/api.py b/openerp/api.py index 497b48dd5a9..56c3ee6d408 100644 --- a/openerp/api.py +++ b/openerp/api.py @@ -383,6 +383,15 @@ def one(method): names = recs.method(args) names = model.method(cr, uid, ids, args, context=context) + + .. deprecated:: 9.0 + + :func:`~.one` often makes the code less clear and behaves in ways + developers and readers may not expect. + + It is strongly recommended to use :func:`~.multi` and either + iterate on the ``self`` recordset or ensure that the recordset + is a single record with :meth:`~openerp.models.Model.ensure_one`. """ split = get_context_split(method) downgrade = get_downgrade(method) diff --git a/openerp/models.py b/openerp/models.py index 27e89c4133f..90ca55bc24b 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -3934,8 +3934,8 @@ class BaseModel(object): # for recomputing new-style fields recs.modified(upd_todo) - unknown_fields = updend[:] - for table in self._inherits: + unknown_fields = set(updend) + for table, inherit_field in self._inherits.iteritems(): col = self._inherits[table] nids = [] for sub_ids in cr.split_for_in_conditions(ids): @@ -3944,10 +3944,11 @@ class BaseModel(object): nids.extend([x[0] for x in cr.fetchall()]) v = {} - for val in updend: - if self._inherit_fields[val][0] == table: - v[val] = vals[val] - unknown_fields.remove(val) + for fname in updend: + field = self._fields[fname] + if field.inherited and field.related[0] == inherit_field: + v[fname] = vals[fname] + unknown_fields.discard(fname) if v: self.pool[table].write(cr, user, nids, v, context) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 4a8481051e5..a254a1020e7 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -56,19 +56,8 @@ from openerp import SUPERUSER_ID, registry @contextmanager def _get_cursor(): # yield a valid cursor from any environment or create a new one if none found - from openerp.api import Environment - from openerp.http import request - try: - request.env # force request's env to be computed - except RuntimeError: - pass # ignore if not in a request - for env in Environment.envs: - if not env.cr.closed: - yield env.cr - break - else: - with registry().cursor() as cr: - yield cr + with registry().cursor() as cr: + yield cr EMPTY_DICT = frozendict() diff --git a/openerp/tools/translate.py b/openerp/tools/translate.py index 9d55f8ae657..260f4e968bd 100644 --- a/openerp/tools/translate.py +++ b/openerp/tools/translate.py @@ -1031,7 +1031,8 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True, if isinstance(res_id, (int, long)) or \ (isinstance(res_id, basestring) and res_id.isdigit()): dic['res_id'] = int(res_id) - dic['module'] = module_name + if module_name: + dic['module'] = module_name else: # res_id is an xml id dic['res_id'] = None @@ -1039,7 +1040,7 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True, if '.' in res_id: dic['module'], dic['imd_name'] = res_id.split('.', 1) else: - dic['module'], dic['imd_name'] = False, res_id + dic['module'], dic['imd_name'] = module_name, res_id irt_cursor.push(dic) diff --git a/requirements.txt b/requirements.txt index ab1c1f3a185..81e05137cb5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,8 +11,8 @@ decorator==3.4.0 docutils==0.12 feedparser==5.1.3 gdata==2.0.18 -gevent==1.0.1 -greenlet==0.4.5 +gevent==1.0.2 +greenlet==0.4.7 jcconv==0.2.3 lxml==3.4.1 mock==1.0.1 |
