diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 6914aafc6cb..1850797ca04 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1083,10 +1083,13 @@ class AccountMoveLine(models.Model): # the provided values were not already multi-currency if account.currency_id and 'amount_currency' not in vals and account.currency_id.id != account.company_id.currency_id.id: vals['currency_id'] = account.currency_id.id - ctx = {} - if 'date' in vals: - ctx['date'] = vals['date'] - vals['amount_currency'] = account.company_id.currency_id.with_context(ctx).compute(amount, account.currency_id) + if self._context.get('skip_full_reconcile_check') == 'amount_currency_excluded': + vals['amount_currency'] = 0.0 + else: + ctx = {} + if 'date' in vals: + ctx['date'] = vals['date'] + vals['amount_currency'] = account.company_id.currency_id.with_context(ctx).compute(amount, account.currency_id) if not ok: raise UserError(_('You cannot use this general account in this journal, check the tab \'Entry Controls\' on the related journal.')) diff --git a/addons/account_asset/account_asset.py b/addons/account_asset/account_asset.py index b8727bbe76c..745358e67b9 100644 --- a/addons/account_asset/account_asset.py +++ b/addons/account_asset/account_asset.py @@ -372,7 +372,7 @@ class AccountAssetAsset(models.Model): @api.multi def _compute_entries(self, date, group_entries=False): - depreciation_ids = self.env['account.asset.depreciation.line'].with_context(depreciation_date=date).search([ + depreciation_ids = self.env['account.asset.depreciation.line'].search([ ('asset_id', 'in', self.ids), ('depreciation_date', '<=', date), ('move_check', '=', False)]) if group_entries: diff --git a/addons/account_asset/i18n/account_asset.pot b/addons/account_asset/i18n/account_asset.pot index 36cb901c308..d7effc2ba4d 100644 --- a/addons/account_asset/i18n/account_asset.pot +++ b/addons/account_asset/i18n/account_asset.pot @@ -460,6 +460,7 @@ msgstr "" #. module: account_asset #: model:ir.model.fields,field_description:account_asset.field_account_asset_category_account_depreciation_id +#: model:ir.ui.view,arch_db:account_asset.view_account_asset_asset_form msgid "Depreciation Entries: Asset Account" msgstr "" diff --git a/addons/account_asset/views/account_asset_view.xml b/addons/account_asset/views/account_asset_view.xml index 1c9c07ab107..76921306a90 100644 --- a/addons/account_asset/views/account_asset_view.xml +++ b/addons/account_asset/views/account_asset_view.xml @@ -29,7 +29,7 @@
-
diff --git a/addons/anonymization/anonymization.py b/addons/anonymization/anonymization.py index 5e2d5bf89da..728745354b7 100644 --- a/addons/anonymization/anonymization.py +++ b/addons/anonymization/anonymization.py @@ -425,6 +425,8 @@ class ir_model_fields_anonymize_wizard(osv.osv_memory): anonymized_value = '2011-11-11 11:11:11' elif field_type == 'float': anonymized_value = 0.0 + elif field_type == 'monetary': + anonymized_value = 0.0 elif field_type == 'integer': anonymized_value = 0 elif field_type in ['binary', 'many2many', 'many2one', 'one2many', 'reference']: # cannot anonymize these kind of fields diff --git a/addons/anonymization/ir.model.fields.anonymization.csv b/addons/anonymization/ir.model.fields.anonymization.csv index 2cfbeb0b223..6a4a20155cd 100644 --- a/addons/anonymization/ir.model.fields.anonymization.csv +++ b/addons/anonymization/ir.model.fields.anonymization.csv @@ -33,7 +33,6 @@ anonymization_field_purchase_order_amount_tax,purchase.order,amount_tax anonymization_field_purchase_order_amount_untaxed,purchase.order,amount_untaxed anonymization_field_purchase_order_line_price_unit,purchase.order.line,price_unit anonymization_field_account_invoice_tax_amount,account.invoice.tax,amount -anonymization_field_account_invoice_tax_base,account.invoice.tax,base anonymization_field_product_name,product.template,name anonymization_field_res_users_signature,res.users,signature anonymization_field_res_partner_contact_name,res.partner.contact,name diff --git a/addons/barcodes/static/src/js/form_view_barcode_handler.js b/addons/barcodes/static/src/js/form_view_barcode_handler.js index b00bce51c5e..ffc6dfc7383 100644 --- a/addons/barcodes/static/src/js/form_view_barcode_handler.js +++ b/addons/barcodes/static/src/js/form_view_barcode_handler.js @@ -81,7 +81,7 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, { if (this.last_scanned_barcode) { var new_qty = window.prompt(_t('Set quantity'), character) || "0"; new_qty = new_qty.replace(',', '.'); - var record = this._get_records(field).find(function(record) { + var record = _.find(this._get_records(field), function (record) { return record.get('product_barcode') === self.last_scanned_barcode; }); if (record) { @@ -159,8 +159,13 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, { }, _get_records: function(field) { - return field.viewmanager.active_view.controller.records || // tree view - field.viewmanager.active_view.controller.widgets; // kanban view + var active_view = field.viewmanager.active_view; + if (active_view.type === "kanban") { + return active_view.controller.widgets; + } else { + // tree view case + return active_view.controller.records.records; + } }, }); diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml index 5d6dc25dd49..295f08ff0ae 100644 --- a/addons/crm/views/crm_lead_views.xml +++ b/addons/crm/views/crm_lead_views.xml @@ -527,7 +527,7 @@ - + diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 312b99ba668..b457e92d4d0 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -137,6 +137,7 @@ class HrExpense(models.Model): 'product_id': line.get('product_id'), 'product_uom_id': line.get('uom_id'), 'analytic_account_id': line.get('analytic_account_id'), + 'payment_id': line.get('payment_id'), } @api.multi @@ -195,11 +196,29 @@ class HrExpense(models.Model): move_lines = expense._move_line_get() #create one more move line, a counterline for the total on payable account + payment_id = False total, total_currency, move_lines = expense._compute_expense_totals(company_currency, move_lines, acc_date) if expense.payment_mode == 'company_account': if not expense.sheet_id.bank_journal_id.default_credit_account_id: raise UserError(_("No credit account found for the %s journal, please configure one.") % (expense.sheet_id.bank_journal_id.name)) emp_account = expense.sheet_id.bank_journal_id.default_credit_account_id.id + journal = expense.bank_journal_id + #create payment + payment_methods = (total < 0) and journal.outbound_payment_method_ids or journal.inbound_payment_method_ids + journal_currency = journal.currency_id or journal.company_id.currency_id + payment = self.env['account.payment'].create({ + 'payment_method_id': payment_methods and payment_methods[0].id or False, + 'payment_type': total < 0 and 'outbound' or 'inbound', + 'partner_id': expense.employee_id.address_home_id.commercial_partner_id.id, + 'partner_type': 'supplier', + 'journal_id': journal.id, + 'payment_date': expense.date, + 'state': 'reconciled', + 'currency_id': diff_currency_p and expense.currency_id.id or journal_currency.id, + 'amount': diff_currency_p and abs(total_currency) or abs(total), + 'name': expense.name, + }) + payment_id = payment.id else: if not expense.employee_id.address_home_id: raise UserError(_("No Home Address found for the employee %s, please configure one.") % (expense.employee_id.name)) @@ -213,7 +232,8 @@ class HrExpense(models.Model): 'date_maturity': acc_date, 'amount_currency': diff_currency_p and total_currency or False, 'currency_id': diff_currency_p and expense.currency_id.id or False, - 'ref': expense.employee_id.address_home_id.ref or False + 'ref': expense.employee_id.address_home_id.ref or False, + 'payment_id': payment_id, }) #convert eml into an osv-valid format diff --git a/addons/l10n_in/l10n_in_standard_tax_template.xml b/addons/l10n_in/l10n_in_standard_tax_template.xml index bf517691e55..03c239bb4c8 100644 --- a/addons/l10n_in/l10n_in_standard_tax_template.xml +++ b/addons/l10n_in/l10n_in_standard_tax_template.xml @@ -464,7 +464,7 @@ - + (standard)Input Sales Tax @ 12% @@ -476,7 +476,7 @@ - + (standard)Input Sales Tax @ 4% @@ -488,7 +488,7 @@ - + (standard)Additional Input VAT @ 1% @@ -607,7 +607,7 @@ - + (standard)Input Education Cess @ 2% On Service Tax diff --git a/addons/purchase/invoice.py b/addons/purchase/invoice.py index c3e45321371..65bc9a826e3 100644 --- a/addons/purchase/invoice.py +++ b/addons/purchase/invoice.py @@ -30,6 +30,35 @@ class AccountInvoice(models.Model): ]} return result + def _prepare_invoice_line_from_po_line(self, line): + if line.product_id.purchase_method == 'purchase': + qty = line.product_qty - line.qty_invoiced + else: + qty = line.qty_received - line.qty_invoiced + if float_compare(qty, 0.0, precision_rounding=line.product_uom.rounding) <= 0: + qty = 0.0 + taxes = line.taxes_id + invoice_line_tax_ids = self.purchase_id.fiscal_position_id.map_tax(taxes) + invoice_line = self.env['account.invoice.line'] + data = { + 'purchase_line_id': line.id, + 'name': self.purchase_id.name+': '+line.name, + 'origin': self.purchase_id.origin, + 'uom_id': line.product_uom.id, + 'product_id': line.product_id.id, + 'account_id': invoice_line.with_context({'journal_id': self.journal_id.id, 'type': 'in_invoice'})._default_account(), + 'price_unit': line.order_id.currency_id.compute(line.price_unit, self.currency_id, round=False), + 'quantity': qty, + 'discount': 0.0, + 'account_analytic_id': line.account_analytic_id.id, + 'analytic_tag_ids': line.analytic_tag_ids.ids, + 'invoice_line_tax_ids': invoice_line_tax_ids.ids + } + account = invoice_line.get_invoice_line_account('in_invoice', line.product_id, self.purchase_id.fiscal_position_id, self.env.user.company_id) + if account: + data['account_id'] = account.id + return data + # Load all unsold PO lines @api.onchange('purchase_id') def purchase_order_change(self): @@ -43,31 +72,7 @@ class AccountInvoice(models.Model): # Load a PO line only once if line in self.invoice_line_ids.mapped('purchase_line_id'): continue - if line.product_id.purchase_method == 'purchase': - qty = line.product_qty - line.qty_invoiced - else: - qty = line.qty_received - line.qty_invoiced - if float_compare(qty, 0.0, precision_rounding=line.product_uom.rounding) <= 0: - qty = 0.0 - taxes = line.taxes_id - invoice_line_tax_ids = self.purchase_id.fiscal_position_id.map_tax(taxes) - data = { - 'purchase_line_id': line.id, - 'name': self.purchase_id.name+': '+line.name, - 'origin': self.purchase_id.origin, - 'uom_id': line.product_uom.id, - 'product_id': line.product_id.id, - 'account_id': self.env['account.invoice.line'].with_context({'journal_id': self.journal_id.id, 'type': 'in_invoice'})._default_account(), - 'price_unit': line.order_id.currency_id.compute(line.price_unit, self.currency_id, round=False), - 'quantity': qty, - 'discount': 0.0, - 'account_analytic_id': line.account_analytic_id.id, - 'analytic_tag_ids': line.analytic_tag_ids.ids, - 'invoice_line_tax_ids': invoice_line_tax_ids.ids - } - account = new_lines.get_invoice_line_account('in_invoice', line.product_id, self.purchase_id.fiscal_position_id, self.env.user.company_id) - if account: - data['account_id'] = account.id + data = self._prepare_invoice_line_from_po_line(line) new_line = new_lines.new(data) new_line._set_additional_fields(self) new_lines += new_line diff --git a/addons/sale/wizard/sale_make_invoice_advance.py b/addons/sale/wizard/sale_make_invoice_advance.py index 3c34c67e93f..13b80cb67cc 100644 --- a/addons/sale/wizard/sale_make_invoice_advance.py +++ b/addons/sale/wizard/sale_make_invoice_advance.py @@ -81,6 +81,10 @@ class SaleAdvancePaymentInv(models.TransientModel): else: amount = self.amount name = _('Down Payment') + if order.fiscal_position_id and self.product_id.taxes_id: + tax_ids = order.fiscal_position_id.map_tax(self.product_id.taxes_id).ids + else: + tax_ids = self.product_id.taxes_id.ids invoice = inv_obj.create({ 'name': order.client_order_ref or order.name, @@ -100,7 +104,7 @@ class SaleAdvancePaymentInv(models.TransientModel): 'uom_id': self.product_id.uom_id.id, 'product_id': self.product_id.id, 'sale_line_ids': [(6, 0, [so_line.id])], - 'invoice_line_tax_ids': [(6, 0, [x.id for x in self.product_id.taxes_id])], + 'invoice_line_tax_ids': [(6, 0, tax_ids)], 'account_analytic_id': order.project_id.id or False, })], 'currency_id': order.pricelist_id.currency_id.id, @@ -140,6 +144,10 @@ class SaleAdvancePaymentInv(models.TransientModel): raise UserError(_('The product used to invoice a down payment should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.')) if self.product_id.type != 'service': raise UserError(_("The product used to invoice a down payment should be of type 'Service'. Please use another product or update this product.")) + if order.fiscal_position_id and self.product_id.taxes_id: + tax_ids = order.fiscal_position_id.map_tax(self.product_id.taxes_id).ids + else: + tax_ids = self.product_id.taxes_id.ids so_line = sale_line_obj.create({ 'name': _('Advance: %s') % (time.strftime('%m %Y'),), 'price_unit': amount, @@ -148,7 +156,7 @@ class SaleAdvancePaymentInv(models.TransientModel): 'discount': 0.0, 'product_uom': self.product_id.uom_id.id, 'product_id': self.product_id.id, - 'tax_id': [(6, 0, self.product_id.taxes_id.ids)], + 'tax_id': [(6, 0, tax_ids)], }) self._create_invoice(order, so_line, amount) if self._context.get('open_invoices', False): diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py index c2e057e552f..61d5b005618 100644 --- a/addons/stock/models/stock_inventory.py +++ b/addons/stock/models/stock_inventory.py @@ -91,14 +91,12 @@ class Inventory(models.Model): ('category', _('One product category')), ('product', _('One product only')), ('partial', _('Select products manually'))] - stock_settings = self.env['stock.config.settings'].search([], limit=1, order='id DESC') - if not stock_settings: - return res_filter - if stock_settings.group_stock_tracking_owner: + + if self.user_has_groups('stock.group_tracking_owner'): res_filter += [('owner', _('One owner only')), ('product_owner', _('One product for a specific owner'))] - if stock_settings.group_stock_production_lot: + if self.user_has_groups('stock.group_production_lot'): res_filter.append(('lot', _('One Lot/Serial Number'))) - if stock_settings.group_stock_tracking_lot: + if self.user_has_groups('stock.group_tracking_lot'): res_filter.append(('pack', _('A Pack'))) return res_filter diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index f824bf1b549..eadff5b6346 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -194,7 +194,6 @@ class StockMove(models.Model): @api.multi def _compute_string_qty_information(self): - StockConfig = self.env['stock.config.settings'] UOM = self.env['product.uom'] precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') void_moves = self.filtered(lambda move: move.state in ('draft', 'done', 'cancel') or move.location_id.usage != 'internal') @@ -206,9 +205,7 @@ class StockMove(models.Model): total_available = UOM._compute_qty_obj(move.product_id.uom_id, total_available, move.product_uom, round=False) total_available = float_round(total_available, precision_digits=precision) info = str(total_available) - # look in the settings if we need to display the UoM name or not - config = StockConfig.search([], limit=1, order='id DESC') - if config and config.group_uom: + if self.user_has_groups('product.group_uom'): info += ' ' + move.product_uom.name if move.reserved_availability: if move.reserved_availability != total_available: @@ -380,7 +377,7 @@ class StockMove(models.Model): # if no specialized push rule has been found yet, we try to find a general one (without route) rules = Push.search(domain + [('route_id', '=', False)], order='sequence') # Make sure it is not returning the return - if rules and (not move.origin_returned_move_id or move.origin_returned_move_id.location_id.id != rules.location_dest_id.id): + if rules and (not move.origin_returned_move_id or move.origin_returned_move_id.location_dest_id.id != rules.location_dest_id.id): rules._apply(move) return True diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index 0f927b43561..b545b44ce67 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -221,6 +221,13 @@ class stock_quant(osv.osv): else: self._create_account_move_line(cr, uid, quants, move, acc_valuation, acc_dest, journal_id, context=ctx) + if move.company_id.anglo_saxon_accounting and move.location_id.usage == 'supplier' and move.location_dest_id.usage == 'customer': + # Creates an account entry from stock_input to stock_output on a dropship move. https://github.com/odoo/odoo/issues/12687 + ctx = context.copy() + ctx['force_company'] = move.company_id.id + journal_id, acc_src, acc_dest, acc_valuation = self._get_accounting_data_for_valuation(cr, uid, move, context=ctx) + self._create_account_move_line(cr, uid, quants, move, acc_src, acc_dest, journal_id, context=ctx) + def _quant_create_from_move(self, cr, uid, qty, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, force_location_from=False, force_location_to=False, context=None): quant_obj = self.pool.get('stock.quant') quant = super(stock_quant, self)._quant_create_from_move(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location_from=force_location_from, force_location_to=force_location_to, context=context) diff --git a/addons/utm/models/ir_http.py b/addons/utm/models/ir_http.py index d690d42399c..85812d6d0c1 100644 --- a/addons/utm/models/ir_http.py +++ b/addons/utm/models/ir_http.py @@ -10,8 +10,16 @@ class IrHttp(models.AbstractModel): return request.httprequest.host def _dispatch(self): + tracked_fields = self.pool['utm.mixin'].tracking_fields() response = super(IrHttp, self)._dispatch() - for var, dummy, cook in self.pool['utm.mixin'].tracking_fields(): + + + # no set_cookie on Exception + if isinstance(response, Exception): + return response + + for var, dummy, cook in tracked_fields: if var in request.params and request.httprequest.cookies.get(var) != request.params[var]: response.set_cookie(cook, request.params[var], domain=self.get_utm_domain_cookies()) + return response diff --git a/addons/web/static/src/js/view_manager.js b/addons/web/static/src/js/view_manager.js index 6a8a162e144..ec7ce95313a 100644 --- a/addons/web/static/src/js/view_manager.js +++ b/addons/web/static/src/js/view_manager.js @@ -458,6 +458,9 @@ var ViewManager = Widget.extend(ControlPanelMixin, { } }, do_load_state: function(state, warm) { + if (state.view_type && state.view_type !== this.active_view.type) { + this.switch_mode(state.view_type, true); + } this.active_view.controller.do_load_state(state, warm); }, }); diff --git a/addons/web/static/src/js/views/list_view.js b/addons/web/static/src/js/views/list_view.js index 39aa8930eac..aece38b9d71 100644 --- a/addons/web/static/src/js/views/list_view.js +++ b/addons/web/static/src/js/views/list_view.js @@ -1733,7 +1733,7 @@ var Column = Class.extend({ } var aggregation_func = (this.sum && 'sum') || (this.avg && 'avg') || - (this.max && 'max') || (this.min && 'min') || this.group_operator; + (this.max && 'max') || (this.min && 'min'); if (!aggregation_func) { return {}; diff --git a/addons/web/static/src/js/web_client.js b/addons/web/static/src/js/web_client.js index 8157270638e..a364e2a2de3 100644 --- a/addons/web/static/src/js/web_client.js +++ b/addons/web/static/src/js/web_client.js @@ -112,25 +112,37 @@ return AbstractWebClient.extend({ } }, on_hashchange: function(event) { - var self = this; - var stringstate = event.getState(false); - if (!_.isEqual(this._current_state, stringstate)) { - var state = event.getState(true); - if(!state.action && state.menu_id) { - self.menu.is_bound.done(function() { - self.menu.menu_click(state.menu_id); - }); - } else { - state._push_me = false; // no need to push state back... - this.action_manager.do_load_state(state, !!this._current_state).then(function () { - var action = self.action_manager.get_inner_action(); - if (action) { - self.menu.open_action(action.action_descr.id); - } - }); - } + if (this._ignore_hashchange) { + this._ignore_hashchange = false; + return; } - this._current_state = stringstate; + + var self = this; + this.clear_uncommitted_changes().then(function () { + var stringstate = event.getState(false); + if (!_.isEqual(self._current_state, stringstate)) { + var state = event.getState(true); + if(!state.action && state.menu_id) { + self.menu.is_bound.done(function() { + self.menu.menu_click(state.menu_id); + }); + } else { + state._push_me = false; // no need to push state back... + self.action_manager.do_load_state(state, !!self._current_state).then(function () { + var action = self.action_manager.get_inner_action(); + if (action) { + self.menu.open_action(action.action_descr.id); + } + }); + } + } + self._current_state = stringstate; + }, function () { + if (event) { + self._ignore_hashchange = true; + window.location = event.originalEvent.oldURL; + } + }); }, on_menu_action: function(options) { var self = this; diff --git a/addons/web_kanban/static/src/js/kanban_view.js b/addons/web_kanban/static/src/js/kanban_view.js index 7f99ceed2fb..cbfa11ad8c3 100644 --- a/addons/web_kanban/static/src/js/kanban_view.js +++ b/addons/web_kanban/static/src/js/kanban_view.js @@ -62,7 +62,7 @@ var KanbanView = View.extend({ this.qweb = new QWeb(session.debug, {_s: session.origin}); - this.limit = this.options.limit || 40; + this.limit = this.options.limit || parseInt(this.fields_view.arch.attrs.limit, 10) || 40; this.fields = {}; this.fields_keys = _.keys(this.fields_view.fields); this.grouped = undefined; diff --git a/addons/website_quote/views/website_quotation.xml b/addons/website_quote/views/website_quotation.xml index 14a49f16add..5453719966a 100644 --- a/addons/website_quote/views/website_quotation.xml +++ b/addons/website_quote/views/website_quotation.xml @@ -294,7 +294,7 @@