diff --git a/addons/account/models/account.py b/addons/account/models/account.py index ae627db7482..89a70ee9485 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -796,6 +796,14 @@ class AccountTax(models.Model): return incl_tax.compute_all(price)['total_excluded'] return price + @api.model + def _fix_tax_included_price_company(self, price, prod_taxes, line_taxes, company_id): + if company_id: + #To keep the same behavior as in _compute_tax_id + prod_taxes = prod_taxes.filtered(lambda tax: tax.company_id == company_id) + line_taxes = line_taxes.filtered(lambda tax: tax.company_id == company_id) + return self._fix_tax_included_price(price, prod_taxes, line_taxes) + class AccountReconcileModel(models.Model): _name = "account.reconcile.model" _description = "Preset to create journal entries during a invoices and payments matching" diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index fbcedca9aaf..b50c1866d34 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -520,6 +520,7 @@ class AccountInvoice(models.Model): self.account_id = account_id self.payment_term_id = payment_term_id + self.date_due = False self.fiscal_position_id = fiscal_position if type in ('in_invoice', 'out_refund'): @@ -1128,7 +1129,7 @@ class AccountInvoice(models.Model): if self.origin: communication = '%s (%s)' % (communication, self.origin) - payment = self.env['account.payment'].create({ + payment_vals = { 'invoice_ids': [(6, 0, self.ids)], 'amount': pay_amount or self.residual, 'payment_date': date or fields.Date.context_today(self), @@ -1140,7 +1141,11 @@ class AccountInvoice(models.Model): 'payment_method_id': payment_method.id, 'payment_difference_handling': writeoff_acc and 'reconcile' or 'open', 'writeoff_account_id': writeoff_acc and writeoff_acc.id or False, - }) + } + if self.env.context.get('tx_currency_id'): + payment_vals['currency_id'] = self.env.context.get('tx_currency_id') + + payment = self.env['account.payment'].create(payment_vals) payment.post() return True diff --git a/addons/account/views/report_journal.xml b/addons/account/views/report_journal.xml index f94e20fa3b0..41abc050cd0 100644 --- a/addons/account/views/report_journal.xml +++ b/addons/account/views/report_journal.xml @@ -49,7 +49,7 @@ - + diff --git a/addons/bus/models/bus.py b/addons/bus/models/bus.py index 466592505a9..e58bc8f0276 100644 --- a/addons/bus/models/bus.py +++ b/addons/bus/models/bus.py @@ -108,6 +108,7 @@ class ImBus(models.Model): class ImDispatch(object): def __init__(self): self.channels = {} + self.started = False def poll(self, dbname, channels, last, options=None, timeout=TIMEOUT): if options is None: @@ -127,8 +128,17 @@ class ImDispatch(object): with registry.cursor() as cr: env = api.Environment(cr, SUPERUSER_ID, {}) notifications = env['bus.bus'].poll(channels, last, options) + + # immediatly returns in peek mode + if options.get('peek'): + return dict(notifications=notifications, channels=channels) + # or wait for future ones if not notifications: + if not self.started: + # Lazy start of events listener + self.start() + event = self.Event() for channel in channels: self.channels.setdefault(hashable(channel), []).append(event) @@ -178,15 +188,16 @@ class ImDispatch(object): import gevent self.Event = gevent.event.Event gevent.spawn(self.run) - elif odoo.multi_process: - # disabled in prefork mode - return else: # threaded mode self.Event = threading.Event t = threading.Thread(name="%s.Bus" % __name__, target=self.run) t.daemon = True t.start() + self.started = True return self -dispatch = ImDispatch().start() +dispatch = None +if not odoo.multi_process or odoo.evented: + # We only use the event dispatcher in threaded and gevent mode + dispatch = ImDispatch() diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index f2a04eae551..8200bb3269f 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -880,7 +880,7 @@ class PosOrderLine(models.Model): self.tax_ids = self.product_id.taxes_id.filtered(lambda r: not self.company_id or r.company_id == self.company_id) fpos = self.order_id.fiscal_position_id tax_ids_after_fiscal_position = fpos.map_tax(self.tax_ids, self.product_id, self.order_id.partner_id) if fpos else self.tax_ids - self.price_unit = self.env['account.tax']._fix_tax_included_price(price, self.product_id.taxes_id, tax_ids_after_fiscal_position) + self.price_unit = self.env['account.tax']._fix_tax_included_price_company(price, self.product_id.taxes_id, tax_ids_after_fiscal_position, self.company_id) @api.onchange('qty', 'discount', 'price_unit', 'tax_ids') def _onchange_qty(self): diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 7c98a48ca3f..0c4abea29f8 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -819,7 +819,7 @@ class PurchaseOrderLine(models.Model): if not seller: return - price_unit = self.env['account.tax']._fix_tax_included_price(seller.price, self.product_id.supplier_taxes_id, self.taxes_id) if seller else 0.0 + price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, self.product_id.supplier_taxes_id, self.taxes_id, self.company_id) if seller else 0.0 if price_unit and seller and self.order_id.currency_id and seller.currency_id != self.order_id.currency_id: price_unit = seller.currency_id.compute(price_unit, self.order_id.currency_id) @@ -892,7 +892,7 @@ class ProcurementOrder(models.Model): date=procurement.purchase_line_id.order_id.date_order and procurement.purchase_line_id.order_id.date_order[:10], uom_id=procurement.purchase_line_id.product_uom) - price_unit = self.env['account.tax']._fix_tax_included_price(seller.price, procurement.purchase_line_id.product_id.supplier_taxes_id, procurement.purchase_line_id.taxes_id) if seller else 0.0 + price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, procurement.purchase_line_id.product_id.supplier_taxes_id, procurement.purchase_line_id.taxes_id, self.company_id) if seller else 0.0 if price_unit and seller and procurement.purchase_line_id.order_id.currency_id and seller.currency_id != procurement.purchase_line_id.order_id.currency_id: price_unit = seller.currency_id.compute(price_unit, procurement.purchase_line_id.order_id.currency_id) @@ -957,7 +957,7 @@ class ProcurementOrder(models.Model): if taxes_id: taxes_id = taxes_id.filtered(lambda x: x.company_id.id == self.company_id.id) - price_unit = self.env['account.tax']._fix_tax_included_price(seller.price, self.product_id.supplier_taxes_id, taxes_id) if seller else 0.0 + price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, self.product_id.supplier_taxes_id, taxes_id, self.company_id) if seller else 0.0 if price_unit and seller and po.currency_id and seller.currency_id != po.currency_id: price_unit = seller.currency_id.compute(price_unit, po.currency_id) @@ -1082,7 +1082,7 @@ class ProcurementOrder(models.Model): date=po.date_order and po.date_order[:10], uom_id=procurement.product_id.uom_po_id) - price_unit = self.env['account.tax']._fix_tax_included_price(seller.price, line.product_id.supplier_taxes_id, line.taxes_id) if seller else 0.0 + price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, line.product_id.supplier_taxes_id, line.taxes_id, self.company_id) if seller else 0.0 if price_unit and seller and po.currency_id and seller.currency_id != po.currency_id: price_unit = seller.currency_id.compute(price_unit, po.currency_id) diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 64e3f66c252..33348bb2c41 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -914,7 +914,7 @@ class SaleOrderLine(models.Model): self._compute_tax_id() if self.order_id.pricelist_id and self.order_id.partner_id: - vals['price_unit'] = self.env['account.tax']._fix_tax_included_price(self._get_display_price(product), product.taxes_id, self.tax_id) + vals['price_unit'] = self.env['account.tax']._fix_tax_included_price_company(self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id) self.update(vals) return result @@ -934,7 +934,7 @@ class SaleOrderLine(models.Model): uom=self.product_uom.id, fiscal_position=self.env.context.get('fiscal_position') ) - self.price_unit = self.env['account.tax']._fix_tax_included_price(self._get_display_price(product), product.taxes_id, self.tax_id) + self.price_unit = self.env['account.tax']._fix_tax_included_price_company(self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id) @api.multi def unlink(self): @@ -1010,7 +1010,7 @@ class SaleOrderLine(models.Model): price, rule_id = self.order_id.pricelist_id.with_context(pricelist_context).get_product_price_rule(self.product_id, self.product_uom_qty or 1.0, self.order_id.partner_id) new_list_price, currency_id = self.with_context(context_partner)._get_real_price_currency(self.product_id, rule_id, self.product_uom_qty, self.product_uom, self.order_id.pricelist_id.id) - new_list_price = self.env['account.tax']._fix_tax_included_price(new_list_price, self.product_id.taxes_id, self.tax_id) + new_list_price = self.env['account.tax']._fix_tax_included_price_company(new_list_price, self.product_id.taxes_id, self.tax_id, self.company_id) if new_list_price != 0: if self.product_id.company_id and self.order_id.pricelist_id.currency_id != self.product_id.company_id.currency_id: diff --git a/addons/stock/models/procurement.py b/addons/stock/models/procurement.py index 099b8df303a..18251991cc4 100644 --- a/addons/stock/models/procurement.py +++ b/addons/stock/models/procurement.py @@ -288,6 +288,13 @@ class ProcurementOrder(models.Model): 1000 orderpoints. This is appropriate for batch jobs only. """ + if company_id and self.env.user.company_id.id != company_id: + # To ensure that the company_id is taken into account for + # all the processes triggered by this method + # i.e. If a PO is generated by the run of the procurements the + # sequence to use is the one for the specified company not the + # one of the user's company + self = self.with_context(company_id=company_id, force_company=company_id) OrderPoint = self.env['stock.warehouse.orderpoint'] domain = self._get_orderpoint_domain(company_id=company_id) diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index f79c17ca7b1..1e3dc32fa81 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -567,8 +567,11 @@ class Picking(models.Model): 'location_dest_id': mapping.location_dst_id, 'product_uom_id': uom.id, 'pack_lot_ids': [ - (0, 0, {'lot_id': lot, 'qty': 0.0, 'qty_todo': lots_grouped[mapping][lot]}) - for lot in lots_grouped.get(mapping, {}).keys()], + (0, 0, { + 'lot_id': lot, + 'qty': 0.0, + 'qty_todo': mapping.product.uom_id._compute_quantity(lots_grouped[mapping][lot], uom) + }) for lot in lots_grouped.get(mapping, {}).keys()], } product_id_to_vals.setdefault(mapping.product.id, list()).append(val_dict) diff --git a/addons/web/static/src/js/views/form_common.js b/addons/web/static/src/js/views/form_common.js index dbbc46f3fcf..c562bebd83e 100644 --- a/addons/web/static/src/js/views/form_common.js +++ b/addons/web/static/src/js/views/form_common.js @@ -728,6 +728,12 @@ var AbstractField = FormWidget.extend(FieldInterface, { set_value: function(value_) { this.set({'value': value_}); }, + /** + Method to set value of a field when loading a record + */ + set_value_from_record: function(record) { + this.set_value.call(this, record[this.name] || false); + }, get_value: function() { return this.get('value'); }, diff --git a/addons/web/static/src/js/views/form_relational_widgets.js b/addons/web/static/src/js/views/form_relational_widgets.js index 55b258c35ad..c82b33cb910 100644 --- a/addons/web/static/src/js/views/form_relational_widgets.js +++ b/addons/web/static/src/js/views/form_relational_widgets.js @@ -453,12 +453,18 @@ var AbstractManyField = common.AbstractField.extend({ }); }, - _on_load_record: function (record) { + set_value_from_record: function (record) { + this._super.apply(this, arguments); + // we want to update starting_ids straight away so the value can be used as soon as + // possible without inconsistency this.starting_ids = []; // don't set starting_ids for the new record if (record.id && record[this.name] && (!isNaN(record.id) || record.id.indexOf(this.dataset.virtual_id_prefix) === -1)) { this.starting_ids = this.get('value').slice(); } + }, + + _on_load_record: function (record) { this.trigger("load_record", record); }, diff --git a/addons/web/static/src/js/views/form_view.js b/addons/web/static/src/js/views/form_view.js index 98cce6e484a..3e192e144fe 100644 --- a/addons/web/static/src/js/views/form_view.js +++ b/addons/web/static/src/js/views/form_view.js @@ -308,7 +308,7 @@ var FormView = View.extend(common.FieldManagerMixin, { _(this.fields).each(function (field, f) { field._dirty_flag = false; field._inhibit_on_change_flag = true; - var result = field.set_value(self.datarecord[f] || false); + var result = field.set_value_from_record(self.datarecord); field._inhibit_on_change_flag = false; set_values.push(result); }); diff --git a/addons/website_portal_sale/models/payment.py b/addons/website_portal_sale/models/payment.py index a5095502fb6..3302ac87967 100644 --- a/addons/website_portal_sale/models/payment.py +++ b/addons/website_portal_sale/models/payment.py @@ -30,7 +30,7 @@ class PaymentTransaction(models.Model): created_invoice.action_invoice_open() if tx.acquirer_id.journal_id: - created_invoice.pay_and_reconcile(tx.acquirer_id.journal_id, pay_amount=created_invoice.amount_total) + created_invoice.with_context(tx_currency_id=tx.currency_id.id).pay_and_reconcile(tx.acquirer_id.journal_id, pay_amount=created_invoice.amount_total) if created_invoice.payment_ids: created_invoice.payment_ids[0].payment_transaction_id = tx else: diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 3cbeccdc513..4055e5dd9fe 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -71,7 +71,7 @@ class SaleOrder(models.Model): if order.pricelist_id and order.partner_id: order_line = order._cart_find_product_line(product.id) if order_line: - pu = self.env['account.tax']._fix_tax_included_price(pu, product.taxes_id, order_line[0].tax_id) + pu = self.env['account.tax']._fix_tax_included_price_company(pu, product.taxes_id, order_line[0].tax_id, self.company_id) return { 'product_id': product_id, @@ -159,10 +159,11 @@ class SaleOrder(models.Model): 'pricelist': order.pricelist_id.id, }) product = self.env['product.product'].with_context(product_context).browse(product_id) - values['price_unit'] = self.env['account.tax']._fix_tax_included_price( + values['price_unit'] = self.env['account.tax']._fix_tax_included_price_company( order_line._get_display_price(product), order_line.product_id.taxes_id, - order_line.tax_id + order_line.tax_id, + self.company_id ) order_line.write(values) diff --git a/doc/reference/testing.rst b/doc/reference/testing.rst index 7b5bb666988..ab32a9d3e0a 100644 --- a/doc/reference/testing.rst +++ b/doc/reference/testing.rst @@ -48,6 +48,11 @@ related to testing Odoo content (modules, mainly): .. autoclass:: odoo.tests.common.SingleTransactionCase :members: browse_ref, ref +.. autoclass:: odoo.tests.common.SavepointCase + +.. autoclass:: odoo.tests.common.HttpCase + :members: browse_ref, ref, url_open, phantom_js + By default, tests are run once right after the corresponding module has been installed. Test cases can also be configured to run after all modules have been installed, and not run right after the module installation: diff --git a/odoo/addons/base/res/res_partner_view.xml b/odoo/addons/base/res/res_partner_view.xml index d4bba3e1595..073632dd4d1 100644 --- a/odoo/addons/base/res/res_partner_view.xml +++ b/odoo/addons/base/res/res_partner_view.xml @@ -220,6 +220,11 @@ + + + + +