summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/account/models/account.py2
-rw-r--r--addons/account/test/account_minimal_test.xml1
-rw-r--r--addons/account/tests/test_reconciliation.py2
-rw-r--r--addons/base_vat_autocomplete/views/res_partner_views.xml4
-rw-r--r--addons/calendar/models/calendar.py32
-rw-r--r--addons/calendar/tests/test_calendar.py39
-rw-r--r--addons/mail/models/mail_tracking_value.py1
-rw-r--r--addons/mrp/models/mrp_unbuild.py2
-rw-r--r--addons/mrp/views/res_config_settings_views.xml2
-rw-r--r--addons/mrp_bom_cost/models/product.py3
-rw-r--r--addons/mrp_bom_cost/tests/test_bom_price.py14
-rw-r--r--addons/payment_stripe/controllers/main.py2
-rw-r--r--addons/point_of_sale/static/src/js/screens.js6
-rw-r--r--addons/pos_restaurant/static/src/js/floors.js3
-rw-r--r--addons/project/views/project_portal_templates.xml2
-rw-r--r--addons/purchase_requisition/models/purchase_requisition.py2
-rw-r--r--addons/purchase_stock/models/account_invoice.py8
-rw-r--r--addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py73
-rw-r--r--addons/purchase_stock/tests/test_replenish_wizard.py6
-rw-r--r--addons/purchase_stock/tests/test_stockvaluation.py50
-rw-r--r--addons/sale_stock/tests/test_anglo_saxon_valuation_reconciliation.py70
-rw-r--r--addons/stock/models/procurement.py6
-rw-r--r--addons/stock/views/stock_picking_views.xml4
-rw-r--r--addons/stock_account/tests/test_anglo_saxon_valuation_reconciliation_common.py18
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js2
-rw-r--r--addons/web/static/tests/fields/basic_fields_tests.js2
-rw-r--r--addons/web_editor/static/src/js/editor/rte.js8
-rw-r--r--addons/web_editor/static/src/js/editor/snippets.options.js35
-rw-r--r--addons/website/static/src/js/editor/snippets.options.js5
-rw-r--r--addons/website_sale/controllers/main.py7
-rw-r--r--doc/_extensions/autojsdoc/parser/types.py2
-rw-r--r--doc/cla/corporate/brahoo.md15
-rw-r--r--doc/cla/individual/mhamra.md11
-rw-r--r--doc/reference/orm.rst6
-rw-r--r--odoo/addons/base/data/res.lang.csv2
-rw-r--r--odoo/addons/base/data/res_currency_data.xml3
-rw-r--r--odoo/models.py2
-rw-r--r--odoo/service/server.py3
-rw-r--r--odoo/tests/common.py3
39 files changed, 342 insertions, 116 deletions
diff --git a/addons/account/models/account.py b/addons/account/models/account.py
index 00e919180f7..01c3fc8c6b5 100644
--- a/addons/account/models/account.py
+++ b/addons/account/models/account.py
@@ -509,6 +509,8 @@ class AccountJournal(models.Model):
@api.constrains('currency_id', 'default_credit_account_id', 'default_debit_account_id')
def _check_currency(self):
if self.currency_id:
+ if self.currency_id == self.company_id.currency_id:
+ raise ValidationError(_("Currency field should only be set if the journal's currency is different from the company's. Leave the field blank to use company currency."))
if self.default_credit_account_id and not self.default_credit_account_id.currency_id.id == self.currency_id.id:
raise ValidationError(_('The currency of the journal should be the same than the default credit account.'))
if self.default_debit_account_id and not self.default_debit_account_id.currency_id.id == self.currency_id.id:
diff --git a/addons/account/test/account_minimal_test.xml b/addons/account/test/account_minimal_test.xml
index 1a0eeab5ec0..212416d6b60 100644
--- a/addons/account/test/account_minimal_test.xml
+++ b/addons/account/test/account_minimal_test.xml
@@ -281,7 +281,6 @@
<field name="type">bank</field>
<field name="default_debit_account_id" ref="usd_bnk"/>
<field name="default_credit_account_id" ref="usd_bnk"/>
- <field name="currency_id" ref="base.USD"/>
<field name="bank_account_id" ref="bank_account_usd"/>
</record>
<record id="base.main_company" model="res.company">
diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py
index 8971faa00ea..2579c198bf6 100644
--- a/addons/account/tests/test_reconciliation.py
+++ b/addons/account/tests/test_reconciliation.py
@@ -790,7 +790,7 @@ class TestReconciliation(AccountingTestCase):
currency = self.env.user.company_id.currency_id
invoice = self.create_invoice_partner(currency_id=currency.id, partner_id=partner.id)
- journal = self.env['account.journal'].create({'name': 'Bank', 'type': 'bank', 'code': 'THE', 'currency_id': currency.id})
+ journal = self.env['account.journal'].create({'name': 'Bank', 'type': 'bank', 'code': 'THE'})
statement = self.make_payment(invoice, journal, 50)
diff --git a/addons/base_vat_autocomplete/views/res_partner_views.xml b/addons/base_vat_autocomplete/views/res_partner_views.xml
index 1c56b7f93a9..8dc861c87b7 100644
--- a/addons/base_vat_autocomplete/views/res_partner_views.xml
+++ b/addons/base_vat_autocomplete/views/res_partner_views.xml
@@ -8,7 +8,7 @@
<xpath expr="//field[@name='vat']" position="replace">
</xpath>
<xpath expr="//label[@for='street']" position="before">
- <field name="vat" string="VAT" placeholder="e.g. BE0477472701"/>
+ <field name="vat" string="VAT" placeholder="e.g. BE0477472701" attrs="{'readonly': [('parent_id','!=',False)]}"/>
</xpath>
</field>
</record>
@@ -21,7 +21,7 @@
<xpath expr="//field[@name='vat']" position="replace">
</xpath>
<xpath expr="//label[@for='street']" position="before">
- <field name="vat" string="VAT" placeholder="e.g. BE0477472701"/>
+ <field name="vat" string="VAT" placeholder="e.g. BE0477472701" attrs="{'readonly': [('parent_id','!=',False)]}"/>
</xpath>
</field>
</record>
diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py
index b4280e1bc9e..f59cbfaa0f5 100644
--- a/addons/calendar/models/calendar.py
+++ b/addons/calendar/models/calendar.py
@@ -611,13 +611,16 @@ class Meeting(models.Model):
event_date = datetime.now()
use_naive_datetime = self.allday and self.rrule and 'UNTIL' in self.rrule and 'Z' not in self.rrule
- if use_naive_datetime:
- rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date.replace(tzinfo=None), forceset=True, ignoretz=True)
- else:
+ if not use_naive_datetime:
# Convert the event date to saved timezone (or context tz) as it'll
# define the correct hour/day asked by the user to repeat for recurrence.
- event_date = event_date.astimezone(timezone) # transform "+hh:mm" timezone
- rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date, forceset=True, tzinfos={})
+ event_date = event_date.astimezone(timezone)
+
+ # The start date is naive
+ # the timezone will be applied, if necessary, at the very end of the process
+ # to allow for DST timezone reevaluation
+ rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date.replace(tzinfo=None), forceset=True, ignoretz=True)
+
recurring_meetings = self.search([('recurrent_id', '=', self.id), '|', ('active', '=', False), ('active', '=', True)])
# We handle a maximum of 50,000 meetings at a time, and clear the cache at each step to
@@ -631,12 +634,15 @@ class Meeting(models.Model):
if use_naive_datetime:
recurring_date = recurring_date.replace(tzinfo=None)
else:
- recurring_date = todate(meeting.recurrent_id_date)
+ recurring_date = todate(meeting.recurrent_id_date).replace(tzinfo=None)
if date_field == "stop":
recurring_date += timedelta(hours=self.duration)
rset1.exdate(recurring_date)
invalidate = True
- return [d.astimezone(pytz.UTC) if d.tzinfo else d for d in rset1 if d.year < MAXYEAR]
+
+ def naive_tz_to_utc(d):
+ return timezone.localize(d).astimezone(pytz.UTC)
+ return [naive_tz_to_utc(d) if not use_naive_datetime else d for d in rset1 if d.year < MAXYEAR]
@api.multi
def _get_recurrency_end_date(self):
@@ -895,18 +901,18 @@ class Meeting(models.Model):
def _inverse_dates(self):
for meeting in self:
if meeting.allday:
- tz = pytz.timezone(self.env.user.tz) if self.env.user.tz else pytz.utc
+ # Convention break:
+ # stop and start are NOT in UTC in allday event
+ # in this case, they actually represent a date
+ # i.e. Christmas is on 25/12 for everyone
+ # even if people don't celebrate it simultaneously
enddate = fields.Datetime.from_string(meeting.stop_date)
- enddate = tz.localize(enddate)
enddate = enddate.replace(hour=18)
- enddate = enddate.astimezone(pytz.utc)
meeting.stop = fields.Datetime.to_string(enddate)
startdate = fields.Datetime.from_string(meeting.start_date)
- startdate = tz.localize(startdate) # Add "+hh:mm" timezone
- startdate = startdate.replace(hour=8) # Set 8 AM in localtime
- startdate = startdate.astimezone(pytz.utc) # Convert to UTC
+ startdate = startdate.replace(hour=8) # Set 8 AM
meeting.start = fields.Datetime.to_string(startdate)
else:
meeting.write({'start': meeting.start_datetime,
diff --git a/addons/calendar/tests/test_calendar.py b/addons/calendar/tests/test_calendar.py
index 12547c8f695..b764a4a9463 100644
--- a/addons/calendar/tests/test_calendar.py
+++ b/addons/calendar/tests/test_calendar.py
@@ -6,6 +6,7 @@ from datetime import datetime, timedelta
from odoo import fields
from odoo.tests.common import TransactionCase
+import pytz
class TestCalendar(TransactionCase):
@@ -275,3 +276,41 @@ class TestCalendar(TransactionCase):
self.assertEqual(test_event.res_model, test_record._name)
self.assertEqual(test_event.res_id, test_record.id)
self.assertEqual(len(test_record.activity_ids), 1)
+
+ def test_event_allday(self):
+ self.env.user.tz = 'Pacific/Honolulu'
+
+ event = self.CalendarEvent.create({
+ 'name': 'All Day',
+ 'start': "2018-10-16 00:00:00",
+ 'start_date': "2018-10-16",
+ 'start_datetime': False,
+ 'stop': "2018-10-18 00:00:00",
+ 'stop_date': "2018-10-18",
+ 'stop_datetime': False,
+ 'allday': True,
+ })
+
+ self.assertEqual(event.start, '2018-10-16 08:00:00')
+ self.assertEqual(event.stop, '2018-10-18 18:00:00')
+
+ def test_recurring_around_dst(self):
+ m = self.CalendarEvent.create({
+ 'name': "wheee",
+ 'start': '2018-10-27 14:30:00',
+ 'allday': False,
+ 'rrule': u'FREQ=DAILY;INTERVAL=1;COUNT=4',
+ 'duration': 2,
+ 'stop': '2018-10-27 16:30:00',
+ })
+
+ start_recurring_dates = m.with_context({'tz': 'Europe/Brussels'})._get_recurrent_date_by_event()
+ self.assertEqual(len(start_recurring_dates), 4)
+
+ for d in start_recurring_dates:
+ self.assertEqual(d.tzinfo, pytz.UTC)
+ if d.day < 28: # DST switch happens between 2018-10-27 and 2018-10-28
+ self.assertEqual(d.hour, 14)
+ else:
+ self.assertEqual(d.hour, 15)
+ self.assertEqual(d.minute, 30)
diff --git a/addons/mail/models/mail_tracking_value.py b/addons/mail/models/mail_tracking_value.py
index 24d3aff9207..8b6e6b97113 100644
--- a/addons/mail/models/mail_tracking_value.py
+++ b/addons/mail/models/mail_tracking_value.py
@@ -9,6 +9,7 @@ from odoo import api, fields, models, tools
class MailTracking(models.Model):
_name = 'mail.tracking.value'
_description = 'Mail Tracking Value'
+ _rec_name = 'field'
# TDE CLEANME: why not a m2o to ir model field ?
field = fields.Char('Changed Field', required=True, readonly=1)
diff --git a/addons/mrp/models/mrp_unbuild.py b/addons/mrp/models/mrp_unbuild.py
index 86ceaa6bd59..99e5b66f802 100644
--- a/addons/mrp/models/mrp_unbuild.py
+++ b/addons/mrp/models/mrp_unbuild.py
@@ -117,7 +117,7 @@ class MrpUnbuild(models.Model):
if produce_move.has_tracking != 'none':
original_move = self.mo_id.move_raw_ids.filtered(lambda move: move.product_id == produce_move.product_id)
needed_quantity = produce_move.product_qty
- for move_lines in original_move.mapped('move_line_ids'):
+ for move_lines in original_move.mapped('move_line_ids').filtered(lambda ml: ml.lot_produced_id == self.lot_id):
# Iterate over all move_lines until we unbuilded the correct quantity.
taken_quantity = min(needed_quantity, move_lines.qty_done)
if taken_quantity:
diff --git a/addons/mrp/views/res_config_settings_views.xml b/addons/mrp/views/res_config_settings_views.xml
index 05330d1588d..1606280a849 100644
--- a/addons/mrp/views/res_config_settings_views.xml
+++ b/addons/mrp/views/res_config_settings_views.xml
@@ -13,7 +13,7 @@
<div class="row mt16 o_settings_container">
<div class="col-md-6 col-xs-12 o_setting_box" id="work_order" title="Work Order Operations allow you to create and manage the manufacturing operations that should be followed within your work centers in order to produce a product. They are attached to bills of materials that will define the required raw materials.">
<div class="o_setting_left_pane">
- <field name="group_mrp_routings" widget="upgrade_boolean"/>
+ <field name="group_mrp_routings"/>
<field name="module_mrp_workorder" invisible="1"/>
</div>
<div class="o_setting_right_pane">
diff --git a/addons/mrp_bom_cost/models/product.py b/addons/mrp_bom_cost/models/product.py
index c1a276918ae..cbb955c2d6d 100644
--- a/addons/mrp_bom_cost/models/product.py
+++ b/addons/mrp_bom_cost/models/product.py
@@ -52,12 +52,11 @@ class ProductProduct(models.Model):
if not boms_to_recompute:
boms_to_recompute = []
total = 0
- quant_quantity = bom.product_uom_id._compute_quantity(bom.product_qty, bom.product_tmpl_id.uom_id)
for opt in bom.routing_id.operation_ids:
duration_expected = (
opt.workcenter_id.time_start +
opt.workcenter_id.time_stop +
- quant_quantity * opt.time_cycle)
+ opt.time_cycle)
total += (duration_expected / 60) * opt.workcenter_id.costs_hour
for line in bom.bom_line_ids:
if line._skip_bom_line(self):
diff --git a/addons/mrp_bom_cost/tests/test_bom_price.py b/addons/mrp_bom_cost/tests/test_bom_price.py
index a347a497e37..c91f6203bca 100644
--- a/addons/mrp_bom_cost/tests/test_bom_price.py
+++ b/addons/mrp_bom_cost/tests/test_bom_price.py
@@ -3,7 +3,7 @@
from odoo.exceptions import UserError
from odoo.tests import common, Form
-from odoo.tools.float_utils import float_round
+from odoo.tools.float_utils import float_round, float_compare
class TestBom(common.TransactionCase):
@@ -166,11 +166,11 @@ class TestBom(common.TransactionCase):
# Table Head Operation Cost (1 Dozen)
# --------------------------------------------------------------------------
# Operation cost calculate for 1 dozens
- # Cutting (20 * 12 / 60) * 100 = 400
- # Drilling (25 * 12 / 60) * 100 = 500
- # Fitting (30 * 12 / 60) * 100 = 600
+ # Cutting (20 * 1 / 60) * 100 = 33,33
+ # Drilling (25 * 1 / 60) * 100 = 41,67
+ # Fitting (30 * 1 / 60) * 100 = 50
# ----------------------------------------
- # Operation Cost 1 dozen (1500 per dozen) and 125 for 1 Unit
+ # Operation Cost 1 dozen (125 per dozen) and 10.42 for 1 Unit
# --------------------------------------------------------------------------
self.bom_2.routing_id = routing_1.id
@@ -180,8 +180,8 @@ class TestBom(common.TransactionCase):
# Total cost of Dining Table = (550) + Total cost of operations (125) = 675.0
self.assertEquals(float_round(self.dining_table.standard_price, precision_digits=2), 675.0, "After computing price from BoM price should be 612.5")
self.Product.browse([self.dining_table.id, self.table_head.id]).action_bom_cost()
- # Total cost of Dining Table = (718.75) + Total cost of all operations (125 + 125) = 968.75
- self.assertEquals(float_round(self.dining_table.standard_price, precision_digits=2), 968.75, "After computing price from BoM price should be 786.46")
+ # Total cost of Dining Table = (718.75) + Total cost of all operations (125 + 10.42) = 854.17
+ self.assertEquals(float_compare(self.dining_table.standard_price, 854.17, precision_digits=2), 0, "After computing price from BoM price should be 786.46")
def test_01_compute_price_inventory_valuation(self):
"""Test update cost from bom in list view when inventory valuation is real time."""
diff --git a/addons/payment_stripe/controllers/main.py b/addons/payment_stripe/controllers/main.py
index f446375d429..105de56a8c8 100644
--- a/addons/payment_stripe/controllers/main.py
+++ b/addons/payment_stripe/controllers/main.py
@@ -27,7 +27,7 @@ class StripeController(http.Controller):
try:
acquirer.s2s_process(post)
except Exception as e:
- error = e.message
+ error = str(e)
return_url = post.get('return_url', '/')
if error:
diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js
index 75030e95acc..63aff0fc7ef 100644
--- a/addons/point_of_sale/static/src/js/screens.js
+++ b/addons/point_of_sale/static/src/js/screens.js
@@ -1278,6 +1278,9 @@ var ClientListScreenWidget = ScreenWidget.extend({
} else {
fields.property_product_pricelist = false;
}
+ var contents = this.$(".client-details-contents");
+ contents.off("click", ".button.save");
+
rpc.query({
model: 'res.partner',
@@ -1297,6 +1300,7 @@ var ClientListScreenWidget = ScreenWidget.extend({
'title': _t('Error: Could not Save Changes'),
'body': error_body,
});
+ contents.on('click','.button.save',function(){ self.save_client_details(partner); });
});
},
@@ -1314,6 +1318,8 @@ var ClientListScreenWidget = ScreenWidget.extend({
// has created, and reload_partner() must have loaded the newly created partner.
self.display_client_details('hide');
}
+ }).always(function(){
+ $(".client-details-contents").on('click','.button.save',function(){ self.save_client_details(partner); });
});
},
diff --git a/addons/pos_restaurant/static/src/js/floors.js b/addons/pos_restaurant/static/src/js/floors.js
index 4ad760b8d0b..213b0afef09 100644
--- a/addons/pos_restaurant/static/src/js/floors.js
+++ b/addons/pos_restaurant/static/src/js/floors.js
@@ -259,7 +259,8 @@ var TableWidget = PosBaseWidget.extend({
args: [[['id', '=', table_id]], fields],
limit: 1,
})
- .then(function (table){
+ .then(function (result){
+ var table = result[0];
for (var field in table) {
self.table[field] = table[field];
}
diff --git a/addons/project/views/project_portal_templates.xml b/addons/project/views/project_portal_templates.xml
index 70838455505..9b5cdd5873e 100644
--- a/addons/project/views/project_portal_templates.xml
+++ b/addons/project/views/project_portal_templates.xml
@@ -201,7 +201,7 @@
</strong>
<div class='row'>
<div class="col-md-2 col-sm-3 text-center oe_attachments" t-foreach='task.attachment_ids' t-as='attachment'>
- <a t-attf-href="/web/content/#{attachment.id}?download=true&amp;access_token=#{attachment.generate_access_token()}" target="_blank">
+ <a t-attf-href="/web/content/#{attachment.id}?download=true&amp;access_token=#{attachment.sudo().generate_access_token()[0]}" target="_blank">
<div class='oe_attachment_embedded o_image o_image_small' t-att-title="attachment.name" t-att-data-mimetype="attachment.mimetype" t-attf-data-src="/web/image/#{attachment.id}/50x40"/>
<div class='oe_attachment_name'><t t-esc='attachment.name' /></div>
</a>
diff --git a/addons/purchase_requisition/models/purchase_requisition.py b/addons/purchase_requisition/models/purchase_requisition.py
index c8862d15c4a..af375ba011b 100644
--- a/addons/purchase_requisition/models/purchase_requisition.py
+++ b/addons/purchase_requisition/models/purchase_requisition.py
@@ -43,7 +43,7 @@ class PurchaseRequisition(models.Model):
_order = "id desc"
def _get_picking_in(self):
- pick_in = self.env.ref('stock.picking_type_in')
+ pick_in = self.env.ref('stock.picking_type_in', raise_if_not_found=False)
company = self.env['res.company']._company_default_get('purchase.requisition')
if not pick_in or pick_in.sudo().warehouse_id.company_id.id != company.id:
pick_in = self.env['stock.picking.type'].search(
diff --git a/addons/purchase_stock/models/account_invoice.py b/addons/purchase_stock/models/account_invoice.py
index 87431c00ef7..54c9bdc19fb 100644
--- a/addons/purchase_stock/models/account_invoice.py
+++ b/addons/purchase_stock/models/account_invoice.py
@@ -120,7 +120,7 @@ class AccountInvoice(models.Model):
# valuation difference, it means this difference is due to exchange rates,
# so we don't create anything, the exchange rate entries will
# be processed automatically by the rest of the code.
- diff_res.append({
+ diff_line = {
'type': 'src',
'name': i_line.name[:64],
'price_unit': inv.currency_id.round(price_unit_val_dif),
@@ -131,7 +131,11 @@ class AccountInvoice(models.Model):
'uom_id': line['uom_id'],
'account_analytic_id': line['account_analytic_id'],
'tax_ids': tax_ids,
- })
+ }
+ # We update the original line accordingly
+ line['price_unit'] = inv.currency_id.round(line['price_unit'] - diff_line['price_unit'])
+ line['price'] = inv.currency_id.round(line['quantity'] * line['price_unit'])
+ diff_res.append(diff_line)
return diff_res
return []
diff --git a/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py b/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py
index 6443d2bf9ea..3058713d190 100644
--- a/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py
+++ b/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py
@@ -25,7 +25,7 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
})
self.test_product_category.property_account_creditor_price_difference_categ = self.price_dif_account.id
- def _create_purchase(self, product, quantity=1.0):
+ def _create_purchase(self, product, date, quantity=1.0):
rslt = self.env['purchase.order'].create({
'partner_id': self.test_partner.id,
'currency_id': self.currency_two.id,
@@ -36,14 +36,14 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
'product_qty': quantity,
'product_uom': product.uom_po_id.id,
'price_unit': self.product_price_unit,
- 'date_planned': '2017-10-07',
+ 'date_planned': date,
})],
- 'date_order': '2017-10-01',
+ 'date_order': date,
})
rslt.button_confirm()
return rslt
- def _create_invoice_for_po(self, purchase_order):
+ def _create_invoice_for_po(self, purchase_order, date):
account_receivable = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1)
rslt = self.env['account.invoice'].create({
'purchase_id': purchase_order.id,
@@ -52,8 +52,8 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
'currency_id': self.currency_two.id,
'name': 'vendor bill',
'type': 'in_invoice',
- 'date_invoice': '2017-12-31',
- 'date': '2017-12-31',
+ 'date_invoice': date,
+ 'date': date,
'account_id': account_receivable.id,
})
rslt.purchase_order_change()
@@ -63,11 +63,16 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
""" Tests the case into which we receive the goods first, and then make the invoice.
"""
test_product = self.test_product_delivery
- purchase_order = self._create_purchase(test_product)
+ purchase_order = self._create_purchase(test_product, '2018-01-01')
self._process_pickings(purchase_order.picking_ids)
- invoice = self._create_invoice_for_po(purchase_order)
- self.currency_rate.rate = 7.76435463
+ invoice = self._create_invoice_for_po(purchase_order, '2018-02-02')
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 7.76435463,
+ 'name': '2018-02-01',
+ })
invoice.action_invoice_open()
picking = self.env['stock.picking'].search([('purchase_id','=',purchase_order.id)])
self.check_reconciliation(invoice, picking)
@@ -78,29 +83,16 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
def test_invoice_shipment(self):
""" Tests the case into which we make the invoice first, and then receive the goods.
"""
- # First, we delete the currency rate defined in demo data for USD on June 6th.
- # This is mandatory to ensure consistency of the data generated by the test,
- # as stock pickings created from a PO are by design always created for
- # the current date (there is no way forcing this), meaning that they
- # always use today's exchange rate for their valuation.
- # Despite the fact we rewrite the date of the valuation moves artificially,
- # we cannot correct the debit and credit values, since the anglosaxon
- # entries get automatically reconciled (and you cannot modify a reconciled entry).
- # So, we have to make sure that "today"'s rate will always be the last rate we
- # created in order to ensure complete control of the test.
- self.env.ref('base.rateUSDbis').unlink()
-
-
# Create a PO and an invoice for it
test_product = self.test_product_order
- purchase_order = self._create_purchase(test_product)
+ purchase_order = self._create_purchase(test_product, '2017-12-01')
- invoice = self._create_invoice_for_po(purchase_order)
+ invoice = self._create_invoice_for_po(purchase_order, '2017-12-23')
invoice_line = self.env['account.invoice.line'].search([('invoice_id', '=', invoice.id)])
invoice_line.quantity = 1
# The currency rate changes
- self.currency_rate = self.env['res.currency.rate'].create({
+ self.env['res.currency.rate'].create({
'currency_id': self.currency_one.id,
'company_id': self.company.id,
'rate': 13.834739702,
@@ -114,7 +106,7 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
self.check_reconciliation(invoice, picking)
# The currency rate changes again
- self.currency_rate = self.env['res.currency.rate'].create({
+ self.env['res.currency.rate'].create({
'currency_id': self.currency_one.id,
'company_id': self.company.id,
'rate': 10.54739702,
@@ -133,7 +125,7 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
self._change_pickings_date(return_pick, '2018-01-13')
# The currency rate changes again
- self.currency_rate = self.env['res.currency.rate'].create({
+ self.env['res.currency.rate'].create({
'currency_id': self.currency_one.id,
'company_id': self.company.id,
'rate': 9.56564564,
@@ -158,25 +150,40 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
""" Tests the case into which we receive part of the goods first, then 2 invoices at different rates, and finally the remaining quantities
"""
test_product = self.test_product_delivery
- purchase_order = self._create_purchase(test_product, quantity=5.0)
+ purchase_order = self._create_purchase(test_product, '2017-01-01', quantity=5.0)
self._process_pickings(purchase_order.picking_ids, quantity=2.0)
picking = self.env['stock.picking'].search([('purchase_id', '=', purchase_order.id)], order="id asc", limit=1)
- invoice = self._create_invoice_for_po(purchase_order)
+ invoice = self._create_invoice_for_po(purchase_order, '2017-01-15')
invoice_line = self.env['account.invoice.line'].search([('invoice_id', '=', invoice.id)])
invoice_line.quantity = 3
- self.currency_rate.rate = 7.76435463
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 7.76435463,
+ 'name': '2017-02-01',
+ })
invoice.action_invoice_open()
self.check_reconciliation(invoice, picking, full_reconcile=False)
- invoice2 = self._create_invoice_for_po(purchase_order)
+ invoice2 = self._create_invoice_for_po(purchase_order, '2017-02-15')
invoice_line = self.env['account.invoice.line'].search([('invoice_id', '=', invoice2.id)])
invoice_line.quantity = 2
- self.currency_rate.rate = 13.834739702
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 13.834739702,
+ 'name': '2017-03-01',
+ })
invoice2.action_invoice_open()
self.check_reconciliation(invoice2, picking, full_reconcile=False)
- self.currency_rate.rate = 12.195747002
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 12.195747002,
+ 'name': '2017-04-01',
+ })
self._process_pickings(purchase_order.picking_ids.filtered(lambda x: x.state != 'done'), quantity=3.0)
picking = self.env['stock.picking'].search([('purchase_id', '=', purchase_order.id)], order='id desc', limit=1)
self.check_reconciliation(invoice2, picking)
diff --git a/addons/purchase_stock/tests/test_replenish_wizard.py b/addons/purchase_stock/tests/test_replenish_wizard.py
index 2ec947765ae..c945c9c5b1d 100644
--- a/addons/purchase_stock/tests/test_replenish_wizard.py
+++ b/addons/purchase_stock/tests/test_replenish_wizard.py
@@ -1,14 +1,12 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
from odoo.addons.stock.tests.common import TestStockCommon
-from odoo.exceptions import UserError
class TestReplenishWizard(TestStockCommon):
def setUp(self):
super(TestReplenishWizard, self).setUp()
- self.vendor = self.env['res.partner'].search([('supplier', '=', True)], limit=1)
+ self.vendor = self.env['res.partner'].create(dict(name='The Replenisher', supplier=True))
self.product1_price = 500
# Create a supplier info witch the previous vendor
@@ -31,7 +29,6 @@ class TestReplenishWizard(TestStockCommon):
self.uom_unit = self.env.ref('uom.product_uom_unit')
self.wh = self.env['stock.warehouse'].search([('company_id', '=', self.env.user.id)], limit=1)
-
def test_replenish_buy_1(self):
""" Set a quantity to replenish via the "Buy" route and check if
a purchase order is created with the correct values
@@ -56,4 +53,3 @@ class TestReplenishWizard(TestStockCommon):
self.assertTrue(order_line,'The product is not in the Purchase Order')
self.assertEqual(order_line.product_qty, self.product_uom_qty, 'Quantities does not match')
self.assertEqual(order_line.price_unit, self.product1_price, 'Prices does not match')
-
diff --git a/addons/purchase_stock/tests/test_stockvaluation.py b/addons/purchase_stock/tests/test_stockvaluation.py
index 84ac5aedb37..336cb7ebd7a 100644
--- a/addons/purchase_stock/tests/test_stockvaluation.py
+++ b/addons/purchase_stock/tests/test_stockvaluation.py
@@ -5,6 +5,7 @@ import time
from datetime import datetime
from odoo import fields
+from odoo.tests import Form
from odoo.tests.common import TransactionCase, tagged
from odoo.addons.account.tests.account_test_classes import AccountingTestCase
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
@@ -417,3 +418,52 @@ class TestStockValuationWithCOA(AccountingTestCase):
# check the anglo saxon entries
price_diff_entry = self.env['account.move.line'].search([('account_id', '=', price_diff_account.id)])
self.assertEqual(price_diff_entry.credit, 100)
+
+ def test_anglosaxon_valuation(self):
+ self.env.user.company_id.anglo_saxon_accounting = True
+ self.product1.product_tmpl_id.cost_method = 'fifo'
+ self.product1.product_tmpl_id.valuation = 'real_time'
+ self.product1.product_tmpl_id.invoice_policy = 'delivery'
+ price_diff_account = self.env['account.account'].create({
+ 'name': 'price diff account',
+ 'code': 'price diff account',
+ 'user_type_id': self.env.ref('account.data_account_type_current_assets').id,
+ })
+ self.product1.property_account_creditor_price_difference = price_diff_account
+
+ # Create PO
+ po_form = Form(self.env['purchase.order'])
+ po_form.partner_id = self.partner_id
+ with po_form.order_line.new() as po_line:
+ po_line.product_id = self.product1
+ po_line.product_qty = 1
+ po_line.price_unit = 10.0
+ order = po_form.save()
+ order.button_confirm()
+
+ # Receive the goods
+ receipt = order.picking_ids[0]
+ receipt.move_lines.quantity_done = 1
+ receipt.button_validate()
+
+ # Create an invoice with a different price
+ invoice = self.env['account.invoice'].create({
+ 'partner_id': order.partner_id.id,
+ 'purchase_id': order.id,
+ 'account_id': order.partner_id.property_account_payable_id.id,
+ 'type': 'in_invoice',
+ })
+ invoice.purchase_order_change()
+ invoice.invoice_line_ids[0].price_unit = 15.0
+ invoice.action_invoice_open()
+
+ # Check what was posted in the price difference account
+ price_diff_aml = self.env['account.move.line'].search([('account_id','=',price_diff_account.id)])
+ self.assertEquals(len(price_diff_aml), 1, "Only one line should have been generated in the price difference account.")
+ self.assertAlmostEquals(price_diff_aml.debit, 5, "Price difference should be equal to 5 (15-10)")
+
+ # Check what was posted in stock input account
+ input_aml = self.env['account.move.line'].search([('account_id','=',self.stock_input_account.id)])
+ self.assertEquals(len(input_aml), 2, "Only two lines should have been generated in stock input account: one when receiving the product, one when making the invoice.")
+ self.assertAlmostEquals(sum(input_aml.mapped('debit')), 10, "Total debit value on stock input account should be equal to the original PO price of the product.")
+ self.assertAlmostEquals(sum(input_aml.mapped('credit')), 10, "Total credit value on stock input account should be equal to the original PO price of the product.")
diff --git a/addons/sale_stock/tests/test_anglo_saxon_valuation_reconciliation.py b/addons/sale_stock/tests/test_anglo_saxon_valuation_reconciliation.py
index 37c77993954..3d84f710ca3 100644
--- a/addons/sale_stock/tests/test_anglo_saxon_valuation_reconciliation.py
+++ b/addons/sale_stock/tests/test_anglo_saxon_valuation_reconciliation.py
@@ -30,7 +30,7 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
#set the invoice_policy to delivery to have an accurate COGS entry
self.test_product_delivery.invoice_policy = "delivery"
- def _create_sale(self, product, quantity=1.0):
+ def _create_sale(self, product, date, quantity=1.0):
rslt = self.env['sale.order'].create({
'partner_id': self.test_partner.id,
'currency_id': self.currency_two.id,
@@ -42,18 +42,19 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
'product_uom': product.uom_po_id.id,
'price_unit': self.product_price_unit,
})],
+ 'date_order': date,
})
rslt.action_confirm()
return rslt
- def _create_invoice_for_so(self, sale_order, product):
+ def _create_invoice_for_so(self, sale_order, product, date):
rslt = self.env['account.invoice'].create({
'partner_id': self.test_partner.id,
'reference_type': 'none',
'currency_id': self.currency_two.id,
'name': 'customer invoice',
'type': 'out_invoice',
- 'date_invoice': time.strftime('%Y') + '-12-22',
+ 'date_invoice': date,
'account_id': self.account_receivable.id,
'invoice_line_ids': [(0, 0, {
'name': 'test line',
@@ -93,11 +94,16 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
test_product = self.test_product_delivery
self._set_initial_stock_for_product(test_product)
- sale_order = self._create_sale(test_product)
+ sale_order = self._create_sale(test_product, '2108-01-01')
self._process_pickings(sale_order.picking_ids)
- invoice = self._create_invoice_for_so(sale_order, test_product)
- self.currency_rate.rate = 9.87366352
+ invoice = self._create_invoice_for_so(sale_order, test_product, '2018-02-12')
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 9.87366352,
+ 'name': '2018-02-01',
+ })
invoice.action_invoice_open()
picking = self.env['stock.picking'].search([('sale_id', '=', sale_order.id)])
self.check_reconciliation(invoice, picking, operation='sale')
@@ -111,10 +117,15 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
self.test_product_delivery.standard_price = 13
self._set_initial_stock_for_product(test_product)
- sale_order = self._create_sale(test_product)
+ sale_order = self._create_sale(test_product, '2018-01-01')
- invoice = self._create_invoice_for_so(sale_order, test_product)
- self.currency_rate.rate = 0.974784
+ invoice = self._create_invoice_for_so(sale_order, test_product, '2018-02-03')
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 0.974784,
+ 'name': '2018-02-01',
+ })
invoice.action_invoice_open()
self._process_pickings(sale_order.picking_ids)
@@ -123,7 +134,12 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
self.check_reconciliation(invoice, picking, operation='sale')
#return the goods and refund the invoice
- self.currency_rate.rate = 10.54739702
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 10.54739702,
+ 'name': '2018-03-01',
+ })
stock_return_picking = self.env['stock.return.picking']\
.with_context(active_ids=[picking.id], active_id=picking.id).create({})
stock_return_picking.product_return_moves.quantity = 1.0
@@ -132,7 +148,12 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
return_pick.action_assign()
return_pick.move_lines.quantity_done = 1
return_pick.action_done()
- self.currency_rate.rate = 9.56564564
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 9.56564564,
+ 'name': '2018-04-01',
+ })
refund_invoice_wiz = self.env['account.invoice.refund'].with_context(active_ids=[invoice.id]).create({
'description': 'test_invoice_shipment_refund',
'filter_refund': 'cancel',
@@ -148,26 +169,41 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
test_product = self.test_product_delivery
self._set_initial_stock_for_product(test_product)
- sale_order = self._create_sale(test_product, quantity=5)
+ sale_order = self._create_sale(test_product, '2018-01-01', quantity=5)
self._process_pickings(sale_order.picking_ids, quantity=2.0)
picking = self.env['stock.picking'].search([('sale_id', '=', sale_order.id)], order="id asc", limit=1)
- invoice = self._create_invoice_for_so(sale_order, test_product)
+ invoice = self._create_invoice_for_so(sale_order, test_product, '2018-02-03')
invoice_line = self.env['account.invoice.line'].search([('invoice_id', '=', invoice.id)])
invoice_line.quantity = 3
- self.currency_rate.rate = 7.76435463
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 7.76435463,
+ 'name': '2018-02-01',
+ })
invoice.action_invoice_open()
self.check_reconciliation(invoice, picking, full_reconcile=False, operation='sale')
- invoice2 = self._create_invoice_for_so(sale_order, test_product)
+ invoice2 = self._create_invoice_for_so(sale_order, test_product, '2018-03-12')
invoice_line = self.env['account.invoice.line'].search([('invoice_id', '=', invoice2.id)])
invoice_line.quantity = 2
- self.currency_rate.rate = 13.834739702
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 13.834739702,
+ 'name': '2018-03-01',
+ })
invoice2.action_invoice_open()
self.check_reconciliation(invoice2, picking, full_reconcile=False, operation='sale')
- self.currency_rate.rate = 12.195747002
+ self.env['res.currency.rate'].create({
+ 'currency_id': self.currency_one.id,
+ 'company_id': self.company.id,
+ 'rate': 12.195747002,
+ 'name': '2018-04-01',
+ })
self._process_pickings(sale_order.picking_ids.filtered(lambda x: x.state != 'done'), quantity=3.0)
picking = self.env['stock.picking'].search([('sale_id', '=', sale_order.id)], order='id desc', limit=1)
self.check_reconciliation(invoice2, picking, operation='sale')
diff --git a/addons/stock/models/procurement.py b/addons/stock/models/procurement.py
index b46505acbb5..239e472ba1d 100644
--- a/addons/stock/models/procurement.py
+++ b/addons/stock/models/procurement.py
@@ -236,8 +236,10 @@ class ProcurementGroup(models.Model):
self.sudo()._procure_orderpoint_confirm(use_new_cursor=use_new_cursor, company_id=company_id)
# Search all confirmed stock_moves and try to assign them
- confirmed_moves = self.env['stock.move'].search([('state', '=', 'confirmed'), ('product_uom_qty', '!=', 0.0)], limit=None, order='priority desc, date_expected asc')
- for moves_chunk in split_every(100, confirmed_moves.ids):
+ moves_to_assign = self.env['stock.move'].search([
+ ('state', 'in', ['confirmed', 'partially_available']), ('product_uom_qty', '!=', 0.0)
+ ], limit=None, order='priority desc, date_expected asc')
+ for moves_chunk in split_every(100, moves_to_assign.ids):
self.env['stock.move'].browse(moves_chunk)._action_assign()
if use_new_cursor:
self._cr.commit()
diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml
index 3f40edd791f..bffe0877077 100644
--- a/addons/stock/views/stock_picking_views.xml
+++ b/addons/stock/views/stock_picking_views.xml
@@ -327,9 +327,11 @@
<field name="group_id" groups="base.group_no_one"/>
<field name="priority" attrs="{'invisible': [('picking_type_code', '=', 'incoming')]}"/>
</group>
- <field name="note" string="Note" placeholder="Add an internal note that will be printed on the Picking Operations sheet"/>
</group>
</page>
+ <page string="Note" name="note">
+ <field name="note" string="Note" placeholder="Add an internal note that will be printed on the Picking Operations sheet"/>
+ </page>
</notebook>
</sheet>
<div class="oe_chatter">
diff --git a/addons/stock_account/tests/test_anglo_saxon_valuation_reconciliation_common.py b/addons/stock_account/tests/test_anglo_saxon_valuation_reconciliation_common.py
index b2e3bd0d095..cb254a392ab 100644
--- a/addons/stock_account/tests/test_anglo_saxon_valuation_reconciliation_common.py
+++ b/addons/stock_account/tests/test_anglo_saxon_valuation_reconciliation_common.py
@@ -58,12 +58,6 @@ class ValuationReconciliationTestCase(AccountingTestCase):
self.currency_one = self.company.currency_id
currency_two_name = 'USD' if self.currency_one.name != 'USD' else 'EUR'
self.currency_two = self.env['res.currency'].search([('name', '=', currency_two_name)])
- self.currency_rate = self.env['res.currency.rate'].create({
- 'currency_id': self.currency_one.id,
- 'company_id': self.company.id,
- 'rate': 1.234343354, # Totally arbitratry value
- 'name': '2017-01-01',
- })
self.input_account = self.env['account.account'].create({
'name': 'Test stock in',
@@ -127,3 +121,15 @@ class ValuationReconciliationTestCase(AccountingTestCase):
})
self.product_price_unit = 66.0
+
+ # We delete the currency rate defined in demo data for USD on June 6th.
+ # This is mandatory to ensure consistency of the data generated by the test,
+ # as stock pickings created from a PO are by design always created for
+ # the current date (there is no way forcing this), meaning that they
+ # always use today's exchange rate for their valuation.
+ # Despite the fact we rewrite the date of the valuation moves artificially,
+ # we cannot correct the debit and credit values, since the anglosaxon
+ # entries get automatically reconciled (and you cannot modify a reconciled entry).
+ # So, we have to make sure that "today"'s rate will always be the last rate we
+ # created in order to ensure complete control of the test.
+ self.env.ref('base.rateUSDbis').unlink()
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index 9b5dec6d169..c6575303342 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -2025,7 +2025,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} [options]
* @param {string[]} [options.fieldNames] the list of fields to fetch. If
* not given, fetch all the fields in record.fieldNames (+ display_name)
- * @param {string} [optinos.viewType] the type of view for which the record
+ * @param {string} [options.viewType] the type of view for which the record
* is fetched (usefull to load the adequate fields), by defaults, uses
* record.viewType
* @returns {Deferred<Object>} resolves to the record or is rejected in
diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js
index 34a2302cd18..4f78343517e 100644
--- a/addons/web/static/tests/fields/basic_fields_tests.js
+++ b/addons/web/static/tests/fields/basic_fields_tests.js
@@ -2028,7 +2028,7 @@ QUnit.module('basic_fields', {
var evt = document.createEvent("MouseEvents"); //taken ref from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
evt.initMouseEvent("mouseover", true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
$('.discreteBar')[0].dispatchEvent(evt);
- var tooltip = $('.nvtooltip').find('table').find('.key')[0].innerText;
+ var tooltip = $('.nvtooltip').find('table').find('.key')[0].innerText.trim();
assert.equal(tooltip, graph_key, "graph tooltip should be generated ");
$('.nvtooltip').remove();
diff --git a/addons/web_editor/static/src/js/editor/rte.js b/addons/web_editor/static/src/js/editor/rte.js
index bcc6a441e27..1e745d7cceb 100644
--- a/addons/web_editor/static/src/js/editor/rte.js
+++ b/addons/web_editor/static/src/js/editor/rte.js
@@ -607,6 +607,14 @@ var RTEWidget = Widget.extend({
return;
}
+ // Removes strange _moz_abspos attribute when it appears. Cannot
+ // find another solution which works in all cases. A grabber still
+ // appears at the same time which I did not manage to remove.
+ // TODO find a complete and better solution
+ _.defer(function () {
+ $editable.find('[_moz_abspos]').removeAttr('_moz_abspos');
+ });
+
if ($target.is('a')) {
/**
* Remove content editable everywhere and add it on the link only so that characters can be added
diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js
index d452dfe07b8..49285986240 100644
--- a/addons/web_editor/static/src/js/editor/snippets.options.js
+++ b/addons/web_editor/static/src/js/editor/snippets.options.js
@@ -279,15 +279,36 @@ var SnippetOption = Widget.extend({
*/
_setActive: function () {
var self = this;
- this.$el.find('[data-toggle-class], [data-select-class]')
- .addBack('[data-toggle-class], [data-select-class]')
+ this.$el.find('[data-toggle-class]')
+ .addBack('[data-toggle-class]')
.removeClass('active')
.filter(function () {
- var $elem = $(this);
- var className = $elem.data('toggleClass') || $elem.data('selectClass');
- return self.$target.hasClass(className);
+ var className = $(this).data('toggleClass');
+ return !className || self.$target.hasClass(className);
})
.addClass('active');
+
+ _processSelectClassElements(this.$el);
+ _.each(this.$el.find('.dropdown-menu'), function (group) {
+ _processSelectClassElements($(group).children());
+ });
+
+ function _processSelectClassElements($elements) {
+ var maxNbClasses = -1;
+ $elements.filter('[data-select-class]')
+ .removeClass('active')
+ .filter(function () {
+ var className = $(this).data('selectClass');
+ var nbClasses = className ? className.split(' ').length : 0;
+ if (nbClasses >= maxNbClasses && (!className || self.$target.hasClass(className))) {
+ maxNbClasses = nbClasses;
+ return true;
+ }
+ return false;
+ })
+ .last()
+ .addClass('active');
+ }
},
//--------------------------------------------------------------------------
@@ -754,7 +775,7 @@ registry.colorpicker = SnippetOption.extend({
*/
_onColorResetButtonClick: function () {
this.$target.removeClass(this.classes);
- self.$target.trigger('content_changed');
+ this.$target.trigger('content_changed');
this.$el.find('.colorpicker button.selected').removeClass('selected');
},
});
@@ -854,7 +875,9 @@ registry.background = SnippetOption.extend({
*/
setTarget: function () {
this._super.apply(this, arguments);
+ // TODO should be automatic for all options as equal to the start method
this.bindBackgroundEvents();
+ this.__customImageSrc = this._getSrcFromCssValue();
},
//--------------------------------------------------------------------------
diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js
index 0e8080299b5..1a20b68790b 100644
--- a/addons/website/static/src/js/editor/snippets.options.js
+++ b/addons/website/static/src/js/editor/snippets.options.js
@@ -248,8 +248,9 @@ options.registry.carousel = options.Class.extend({
* @override
*/
_setActive: function () {
- this.$el.find('li[data-interval]').removeClass('active')
- .filter('li[data-interval=' + this.$target.attr('data-interval') + ']').addClass('active');
+ this._super.apply(this, arguments);
+ this.$el.find('[data-interval]').removeClass('active')
+ .filter('[data-interval=' + this.$target.attr('data-interval') + ']').addClass('active');
},
/**
* Rebinds carousel events on indicators.
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index e1ba0e62572..731238150e9 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -402,7 +402,11 @@ class WebsiteSale(http.Controller):
@http.route(['/shop/cart/update'], type='http', auth="public", methods=['POST'], website=True, csrf=False)
def cart_update(self, product_id, add_qty=1, set_qty=0, **kw):
- request.website.sale_get_order(force_create=1)._cart_update(
+ sale_order = request.website.sale_get_order(force_create=1)
+ if sale_order.state != 'draft':
+ request.session['sale_order_id'] = None
+ sale_order = request.website.sale_get_order(force_create=True)
+ sale_order._cart_update(
product_id=int(product_id),
add_qty=add_qty,
set_qty=set_qty,
@@ -578,6 +582,7 @@ class WebsiteSale(http.Controller):
new_values['customer'] = True
new_values['team_id'] = request.website.salesteam_id and request.website.salesteam_id.id
+ new_values['user_id'] = request.website.salesperson_id and request.website.salesperson_id.id
lang = request.lang if request.lang in request.website.mapped('language_ids.code') else None
if lang:
diff --git a/doc/_extensions/autojsdoc/parser/types.py b/doc/_extensions/autojsdoc/parser/types.py
index 8737b26ecc9..69836f46ac4 100644
--- a/doc/_extensions/autojsdoc/parser/types.py
+++ b/doc/_extensions/autojsdoc/parser/types.py
@@ -44,6 +44,8 @@ def tokenize(typespec):
yield (LITERAL, ast.literal_eval(string))
elif toktype == token.ENDMARKER:
return
+ elif toktype == token.NEWLINE:
+ pass
else:
raise ValueError("Unknown typespec token %s" % token.tok_name[toktype])
diff --git a/doc/cla/corporate/brahoo.md b/doc/cla/corporate/brahoo.md
new file mode 100644
index 00000000000..04e4d39cb31
--- /dev/null
+++ b/doc/cla/corporate/brahoo.md
@@ -0,0 +1,15 @@
+The Netherlands, 2018-10-24
+
+Brahoo B.V. agrees to the terms of the Odoo Corporate Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Cas Vissers c.vissers@brahoo.nl https://github.com/CasVissers
+
+List of contributors:
+
+Cas Vissers c.vissers@brahoo.nl https://github.com/CasVissers \ No newline at end of file
diff --git a/doc/cla/individual/mhamra.md b/doc/cla/individual/mhamra.md
new file mode 100644
index 00000000000..260fee70284
--- /dev/null
+++ b/doc/cla/individual/mhamra.md
@@ -0,0 +1,11 @@
+Argentina, 2018-10-18
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Marcelo Ariel Hamra marcelo.hamra@gmail.com https://github.com/mhamra
diff --git a/doc/reference/orm.rst b/doc/reference/orm.rst
index e6c74883d41..57a2cbed847 100644
--- a/doc/reference/orm.rst
+++ b/doc/reference/orm.rst
@@ -947,17 +947,17 @@ them (e.g. to change their default sort order):
.. literalinclude:: ../../odoo/addons/test_documentation_examples/extension.py
:language: python
- :lines: 5-
+ :lines: 7-
.. literalinclude:: ../../odoo/addons/test_documentation_examples/tests/test_extension.py
:language: python
- :lines: 8,13
+ :lines: 10,15
will yield:
.. literalinclude:: ../../odoo/addons/test_documentation_examples/tests/test_extension.py
:language: text
- :lines: 11
+ :lines: 13
.. note:: it will also yield the various :ref:`automatic fields
<reference/orm/model/automatic>` unless they've been disabled
diff --git a/odoo/addons/base/data/res.lang.csv b/odoo/addons/base/data/res.lang.csv
index cf11d7bb4c9..207b153c5c6 100644
--- a/odoo/addons/base/data/res.lang.csv
+++ b/odoo/addons/base/data/res.lang.csv
@@ -61,7 +61,7 @@ base.lang_my,"Burmese / ဗမာစာ",my_MM,my,"Left-to-Right","[3,3]",".",",
"base.lang_es_CL","Spanish (CL) / Español (CL)","es_CL","es_CL","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","True","1"
"base.lang_es_CO","Spanish (CO) / Español (CO)","es_CO","es_CO","Left-to-Right","[3,0]",",",".","%d-%m-%Y","%H:%M:%S","True","7"
"base.lang_es_CR","Spanish (CR) / Español (CR)","es_CR","es_CR","Left-to-Right","[3,0]",","," ","%d/%m/%Y","%H:%M:%S","True","1"
-"base.lang_es_DO","Spanish (DO) / Español (DO)","es_DO","es_DO","Left-to-Right","[3,0]",".",",","%d/%m/%Y","%H:%M:%S","True","7"
+"base.lang_es_DO","Spanish (DO) / Español (DO)","es_DO","es_DO","Left-to-Right","[3,0]",".",",","%d/%m/%Y","%I:%M:%S %p","True","7"
"base.lang_es_EC","Spanish (EC) / Español (EC)","es_EC","es_EC","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","True","1"
"base.lang_es_GT","Spanish (GT) / Español (GT)","es_GT","es_GT","Left-to-Right","[3,0]",".",",","%d/%m/%Y","%H:%M:%S","True","7"
"base.lang_es_MX","Spanish (MX) / Español (MX)","es_MX","es_MX","Left-to-Right","[3,0]",".",",","%d/%m/%Y","%H:%M:%S","True","7"
diff --git a/odoo/addons/base/data/res_currency_data.xml b/odoo/addons/base/data/res_currency_data.xml
index 9966ae87ae6..f4028c0b698 100644
--- a/odoo/addons/base/data/res_currency_data.xml
+++ b/odoo/addons/base/data/res_currency_data.xml
@@ -670,8 +670,9 @@
<field name="symbol">RD$</field>
<field name="rounding">0.01</field>
<field name="active" eval="False"/>
- <field name="currency_unit_label">Peso</field>
+ <field name="currency_unit_label">Pesos</field>
<field name="currency_subunit_label">Centavos</field>
+ <field name="position">before</field>
</record>
<record id="ECS" model="res.currency">
diff --git a/odoo/models.py b/odoo/models.py
index 71dfb22e69e..85748a04832 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -2681,7 +2681,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
record._cache.update(record._convert_to_cache(values, validate=False))
if not self.env.cache.contains(self, field):
exc = AccessError("No value found for %s.%s" % (self, field.name))
- self.env.cache.set_failed(self, field, exc)
+ self.env.cache.set_failed(self, [field], exc)
@api.multi
def _read_from_database(self, field_names, inherited_field_names=[]):
diff --git a/odoo/service/server.py b/odoo/service/server.py
index ae0460961c2..c39d0310061 100644
--- a/odoo/service/server.py
+++ b/odoo/service/server.py
@@ -87,6 +87,9 @@ class BaseWSGIServerNoBind(LoggingBaseWSGIServerMixIn, werkzeug.serving.BaseWSGI
class RequestHandler(werkzeug.serving.WSGIRequestHandler):
def setup(self):
+ # timeout to avoid chrome headless preconnect during tests
+ if config['test_enable'] or config['test_file']:
+ self.timeout = 5
# flag the current thread as handling a http request
super(RequestHandler, self).setup()
me = threading.currentThread()
diff --git a/odoo/tests/common.py b/odoo/tests/common.py
index 9ee1def1aac..b6c9a2e6156 100644
--- a/odoo/tests/common.py
+++ b/odoo/tests/common.py
@@ -423,7 +423,8 @@ class ChromeBrowser():
'--user-data-dir': self.user_data_dir,
'--disable-translate': '',
'--window-size': '1366x768',
- '--remote-debugging-port': str(self.devtools_port)
+ '--remote-debugging-port': str(self.devtools_port),
+ '--no-sandbox': '',
}
cmd = [self.executable]
cmd += ['%s=%s' % (k, v) if v else k for k, v in switches.items()]