summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Ledoux <dle@odoo.com>2015-09-21 16:01:58 +0200
committerDenis Ledoux <dle@odoo.com>2015-09-21 16:01:58 +0200
commita3648fd23a21efe3d2d507fb926b7f7cc55c1bba (patch)
treeef5f6ae4fb1080319fada8e5b31571ae7b8e4c05
parent9975a04e4d81f718b13fef41b798d82d709a718b (diff)
parentaabbdc7339b9c2e1c10824ca2fc0a86419c46685 (diff)
[MERGE] forward port of branch 8.0 up to aabbdc7
-rw-r--r--addons/delivery/sale.py10
-rw-r--r--addons/event/email_template.xml3
-rw-r--r--addons/hw_escpos/controllers/main.py8
-rw-r--r--addons/hw_escpos/escpos/exceptions.py8
-rw-r--r--addons/hw_escpos/escpos/printer.py2
-rw-r--r--addons/hw_proxy/controllers/main.py19
-rw-r--r--addons/l10n_sa/__openerp__.py1
-rw-r--r--addons/mail/security/ir.model.access.csv2
-rw-r--r--addons/membership/membership.py2
-rw-r--r--addons/payment/models/payment_acquirer.py26
-rw-r--r--addons/payment_ogone/models/ogone.py2
-rw-r--r--addons/stock_account/wizard/stock_valuation_history.py3
-rw-r--r--addons/web/controllers/main.py4
-rw-r--r--addons/web/static/test/fulltest.html54
-rw-r--r--addons/website/controllers/main.py2
-rw-r--r--addons/website/static/src/js/website.editor.js2
-rw-r--r--addons/website/static/src/xml/website.xml2
-rw-r--r--addons/website_membership/controllers/main.py8
-rw-r--r--addons/website_sale/controllers/main.py35
-rw-r--r--doc/_extensions/odoo/__init__.py6
-rw-r--r--doc/_extensions/odoo/static/style.css142
-rw-r--r--doc/_extensions/odoo/static/style.less58
-rw-r--r--doc/howtos/backend.rst11
-rw-r--r--doc/howtos/backend/exercise-calendar59
-rw-r--r--doc/howtos/backend/exercise-computed27
-rw-r--r--doc/howtos/backend/exercise-constraint-python16
-rw-r--r--doc/howtos/backend/exercise-copy-override12
-rw-r--r--doc/howtos/backend/exercise-gantt35
-rw-r--r--doc/howtos/backend/exercise-graph33
-rw-r--r--doc/howtos/backend/exercise-onchange16
-rw-r--r--doc/howtos/backend/exercise-state-basic31
-rw-r--r--doc/howtos/backend/exercise-state-workflow40
-rw-r--r--doc/howtos/backend/exercise-translations24
-rw-r--r--doc/reference/orm.rst4
-rw-r--r--doc/reference/upgrade_api.rst34
-rw-r--r--openerp/api.py9
-rw-r--r--openerp/models.py13
-rw-r--r--openerp/osv/fields.py15
-rw-r--r--openerp/tools/translate.py5
-rw-r--r--requirements.txt4
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