diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py index 7aca6df336e..58c9825164e 100644 --- a/addons/account/models/account_bank_statement.py +++ b/addons/account/models/account_bank_statement.py @@ -235,14 +235,18 @@ class AccountBankStatement(models.Model): statements = self.filtered(lambda r: r.state == 'open') for statement in statements: moves = self.env['account.move'] + # `line.journal_entry_ids` gets invalidated from the cache during the loop + # because new move lines are being created at each iteration. + # The below dict is to prevent the ORM to permanently refetch `line.journal_entry_ids` + line_journal_entries = {line: line.journal_entry_ids for line in statement.line_ids} for st_line in statement.line_ids: #upon bank statement confirmation, look if some lines have the account_id set. It would trigger a journal entry #creation towards that account, with the wanted side-effect to skip that line in the bank reconciliation widget. + journal_entries = line_journal_entries[st_line] st_line.fast_counterpart_creation() - if not st_line.account_id and not st_line.journal_entry_ids.ids and not st_line.statement_id.currency_id.is_zero(st_line.amount): + if not st_line.account_id and not journal_entries.ids and not st_line.statement_id.currency_id.is_zero(st_line.amount): raise UserError(_('All the account entries lines must be processed in order to close the statement.')) - for aml in st_line.journal_entry_ids: - moves |= aml.move_id + moves = statement.mapped('line_ids.journal_entry_ids.move_id') if moves: moves.filtered(lambda m: m.state != 'posted').post() statement.message_post(body=_('Statement %s confirmed, journal items were created.') % (statement.name,)) diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js index 0aedc28e770..c9635c02abc 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js @@ -560,7 +560,10 @@ var LineRenderer = Widget.extend(FieldManagerMixin, { type: 'char', //TODO is it a bug or a feature when type date exists ? name: 'date', }], { - account_id: {string: _t("Account")}, + account_id: { + string: _t("Account"), + domain: [['deprecated', '=', false]], + }, label: {string: _t("Label")}, amount: {string: _t("Account")}, }).then(function (recordID) { diff --git a/addons/account/static/tests/reconciliation_tests.js b/addons/account/static/tests/reconciliation_tests.js index e1224663214..1475a5cc308 100644 --- a/addons/account/static/tests/reconciliation_tests.js +++ b/addons/account/static/tests/reconciliation_tests.js @@ -54,6 +54,7 @@ var db = { code: {string: "code", type: 'integer'}, name: {string: "Displayed name", type: 'char'}, company_id: {string: "Company", type: 'many2one', relation: 'res.company'}, + deprecated: {string: "Deprecated", type: 'boolean'}, }, records: [ {id: 282, code: 100000, name: "100000 Fixed Asset Account", company_id: 1}, diff --git a/addons/base_geolocalize/models/res_partner.py b/addons/base_geolocalize/models/res_partner.py index 3594e1cb61b..01e2c36baeb 100644 --- a/addons/base_geolocalize/models/res_partner.py +++ b/addons/base_geolocalize/models/res_partner.py @@ -30,7 +30,13 @@ def geo_find(addr, apikey=False): if result['status'] != 'OK': if result.get('error_message'): _logger.error(result['error_message']) - return None + error_msg = _('Unable to geolocate, received the error:\n%s' + '\n\nGoogle made this a paid feature.\n' + 'You should first enable billing on your Google account.\n' + 'Then, go to Developer Console, and enable the APIs:\n' + 'Geocoding, Maps Static, Maps Javascript.\n' + % result['error_message']) + raise UserError(error_msg) try: geo = result['results'][0]['geometry']['location'] diff --git a/addons/l10n_jp/data/account.tax.template.csv b/addons/l10n_jp/data/account.tax.template.csv index 1b8e083b42d..a9ef3e9ae8e 100644 --- a/addons/l10n_jp/data/account.tax.template.csv +++ b/addons/l10n_jp/data/account.tax.template.csv @@ -1,6 +1,6 @@ id,name,description,chart_template_id/id,type_tax_use,amount_type,amount,price_include,sequence,include_base_amount,account_id/id,refund_account_id/id,tag_ids/id,tax_group_id/id -tax_in_e,仮受消費税(外),仮受消費税(外),l10n_jp1,sale,percent,8,FALSE,1,FALSE,A21807,A21807,"tag_jp_03,tag_jp_12",tax_group_8 -tax_in_i,仮受消費税(内),仮受消費税(内),l10n_jp1,sale,percent,8,TRUE,1,FALSE,A21807,A21807,"tag_jp_03,tag_jp_12",tax_group_8 +tax_in_e,仮受消費税(外),仮受消費税(外),l10n_jp1,sale,percent,8,FALSE,1,FALSE,A21809,A21809,"tag_jp_03,tag_jp_12",tax_group_8 +tax_in_i,仮受消費税(内),仮受消費税(内),l10n_jp1,sale,percent,8,TRUE,1,FALSE,A21809,A21809,"tag_jp_03,tag_jp_12",tax_group_8 tax_in_x,輸出免税,輸出免税,l10n_jp1,sale,percent,,FALSE,1,FALSE,,,"tag_jp_04,tag_jp_13",tax_group_0 tax_in_o,非課税販売,非課税販売,l10n_jp1,sale,percent,,FALSE,1,FALSE,,,"tag_jp_09,tag_jp_14",tax_group_0 tax_out_e,仮払消費税(外),仮払消費税(外),l10n_jp1,purchase,percent,8,FALSE,1,FALSE,A11807,A11807,"tag_jp_07,tag_jp_16",tax_group_8 diff --git a/addons/l10n_mx/data/account_tax_data.xml b/addons/l10n_mx/data/account_tax_data.xml index 12555ba9ed4..5bc9932a5a0 100644 --- a/addons/l10n_mx/data/account_tax_data.xml +++ b/addons/l10n_mx/data/account_tax_data.xml @@ -34,6 +34,10 @@ DIOT: 8% taxes + + DIOT: 8% NO ACREDITABLE + taxes + DIOT: Retención taxes diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py index 7553cee2db9..97a93d004d2 100644 --- a/addons/point_of_sale/models/pos_session.py +++ b/addons/point_of_sale/models/pos_session.py @@ -277,6 +277,7 @@ class PosSession(models.Model): for session in self: company_id = session.config_id.company_id.id ctx = dict(self.env.context, force_company=company_id, company_id=company_id) + ctx_notrack = dict(ctx, mail_notrack=True) for st in session.statement_ids: if abs(st.difference) > st.journal_id.amount_authorized_diff: # The pos manager can close statements with maximums. @@ -284,7 +285,7 @@ class PosSession(models.Model): raise UserError(_("Your ending balance is too different from the theoretical cash closing (%.2f), the maximum allowed is: %.2f. You can contact your manager to force it.") % (st.difference, st.journal_id.amount_authorized_diff)) if (st.journal_id.type not in ['bank', 'cash']): raise UserError(_("The journal type for your payment method should be bank or cash.")) - st.with_context(ctx).sudo().button_confirm_bank() + st.with_context(ctx_notrack).sudo().button_confirm_bank() self.with_context(ctx)._confirm_orders() self.write({'state': 'closed'}) return { diff --git a/addons/stock_account/models/stock.py b/addons/stock_account/models/stock.py index dc8d8b66346..377acea0444 100644 --- a/addons/stock_account/models/stock.py +++ b/addons/stock_account/models/stock.py @@ -156,24 +156,50 @@ class StockMove(models.Model): @api.model def _get_in_base_domain(self, company_id=False): + # Domain: + # - state is done + # - coming from a location without company, or an inventory location within the same company + # - going to a location within the same company domain = [ ('state', '=', 'done'), - ('location_id.company_id', '=', False), - ('location_dest_id.company_id', '=', company_id or self.env.user.company_id.id) + '&', + '|', + ('location_id.company_id', '=', False), + '&', + ('location_id.usage', 'in', ['inventory', 'production']), + ('location_id.company_id', '=', company_id or self.env.user.company_id.id), + ('location_dest_id.company_id', '=', company_id or self.env.user.company_id.id), ] return domain @api.model def _get_all_base_domain(self, company_id=False): + # Domain: + # - state is done + # Then, either 'in' or 'out' moves. + # 'in' moves: + # - coming from a location without company, or an inventory location within the same company + # - going to a location within the same company + # 'out' moves: + # - coming from to a location within the same company + # - going to a location without company, or an inventory location within the same company domain = [ ('state', '=', 'done'), '|', '&', - ('location_id.company_id', '=', False), + '|', + ('location_id.company_id', '=', False), + '&', + ('location_id.usage', 'in', ['inventory', 'production']), + ('location_id.company_id', '=', company_id or self.env.user.company_id.id), ('location_dest_id.company_id', '=', company_id or self.env.user.company_id.id), '&', ('location_id.company_id', '=', company_id or self.env.user.company_id.id), - ('location_dest_id.company_id', '=', False) + '|', + ('location_dest_id.company_id', '=', False), + '&', + ('location_dest_id.usage', '=', 'inventory'), + ('location_dest_id.company_id', '=', company_id or self.env.user.company_id.id), ] return domain diff --git a/addons/stock_account/tests/test_stockvaluation.py b/addons/stock_account/tests/test_stockvaluation.py index 9d26c639649..5efd5ca5bbc 100644 --- a/addons/stock_account/tests/test_stockvaluation.py +++ b/addons/stock_account/tests/test_stockvaluation.py @@ -14,6 +14,7 @@ class TestStockValuation(TransactionCase): self.stock_location = self.env.ref('stock.stock_location_stock') self.customer_location = self.env.ref('stock.stock_location_customers') self.supplier_location = self.env.ref('stock.stock_location_suppliers') + self.inventory_location = self.env.ref('stock.location_inventory') self.partner = self.env['res.partner'].create({'name': 'xxx'}) self.owner1 = self.env['res.partner'].create({'name': 'owner1'}) self.uom_unit = self.env.ref('uom.product_uom_unit') @@ -3899,3 +3900,47 @@ class TestStockValuation(TransactionCase): self.assertEqual(self.product1.with_context(to_date=Datetime.to_string(date6)).stock_value, 100) self.assertEqual(self.product1.qty_at_date, 10) self.assertEqual(self.product1.stock_value, 100) + + def test_inventory_fifo_1(self): + """ Make an inventory from a location with a company set, and ensure the product has a stock + value. When the product is sold, ensure there is no remaining quantity on the original move + and no stock value. + """ + self.product1.standard_price = 15 + self.product1.product_tmpl_id.cost_method = 'fifo' + self.inventory_location.company_id = self.env.user.company_id.id + + # Start Inventory: 12 units + move1 = self.env['stock.move'].create({ + 'name': 'Adjustment of 12 units', + 'location_id': self.inventory_location.id, + 'location_dest_id': self.stock_location.id, + 'product_id': self.product1.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 12.0, + }) + move1._action_confirm() + move1._action_assign() + move1.move_line_ids.qty_done = 12.0 + move1._action_done() + + self.assertAlmostEqual(move1.value, 180.0) + self.assertAlmostEqual(move1.remaining_qty, 12.0) + self.assertAlmostEqual(self.product1.stock_value, 180.0) + + # Sell the 12 units + move2 = self.env['stock.move'].create({ + 'name': 'Sell 12 units', + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'product_id': self.product1.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 12.0, + }) + move2._action_confirm() + move2._action_assign() + move2.move_line_ids.qty_done = 12.0 + move2._action_done() + + self.assertAlmostEqual(move1.remaining_qty, 0.0) + self.assertAlmostEqual(self.product1.stock_value, 0.0) diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 606397885ae..8cb8bf94c67 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -113,11 +113,26 @@ class SaleOrder(models.Model): 'pricelist': order.pricelist_id.id, }) product = self.env['product.product'].with_context(product_context).browse(product_id) - pu = product.price - 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_company(pu, product.taxes_id, order_line[0].tax_id, self.company_id) + discount = 0 + + if order.pricelist_id.discount_policy == 'without_discount': + # This part is pretty much a copy-paste of the method '_onchange_discount' of + # 'sale.order.line'. + price, rule_id = order.pricelist_id.with_context(product_context).get_product_price_rule(product, qty or 1.0, order.partner_id) + pu, currency_id = request.env['sale.order.line'].with_context(product_context)._get_real_price_currency(product, rule_id, qty, product.uom_id, order.pricelist_id.id) + if pu != 0: + if order.pricelist_id.currency_id.id != currency_id: + # we need new_list_price in the same currency as price, which is in the SO's pricelist's currency + pu = request.env['res.currency'].browse(currency_id).with_context(product_context).compute(pu, order.pricelist_id.currency_id) + discount = (pu - price) / pu * 100 + if discount < 0: + discount = 0 + else: + pu = product.price + 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_company(pu, product.taxes_id, order_line[0].tax_id, self.company_id) return { 'product_id': product_id, @@ -125,6 +140,7 @@ class SaleOrder(models.Model): 'order_id': order_id, 'product_uom': product.uom_id.id, 'price_unit': pu, + 'discount': discount, } @api.multi diff --git a/odoo/addons/base/models/res_bank.py b/odoo/addons/base/models/res_bank.py index 727429a453b..45655ec245c 100644 --- a/odoo/addons/base/models/res_bank.py +++ b/odoo/addons/base/models/res_bank.py @@ -67,7 +67,7 @@ class ResPartnerBank(models.Model): _name = 'res.partner.bank' _rec_name = 'acc_number' _description = 'Bank Accounts' - _order = 'sequence' + _order = 'sequence, id' @api.model def get_supported_account_types(self): @@ -85,7 +85,7 @@ class ResPartnerBank(models.Model): bank_id = fields.Many2one('res.bank', string='Bank') bank_name = fields.Char(related='bank_id.name', readonly=False) bank_bic = fields.Char(related='bank_id.bic', readonly=False) - sequence = fields.Integer() + sequence = fields.Integer(default=10) currency_id = fields.Many2one('res.currency', string='Currency') company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.user.company_id, ondelete='cascade') qr_code_valid = fields.Boolean(string="Has all required arguments", compute="_validate_qr_code_arguments") diff --git a/odoo/addons/base/views/res_bank_views.xml b/odoo/addons/base/views/res_bank_views.xml index 950472edd15..ce3d4bc7ec5 100644 --- a/odoo/addons/base/views/res_bank_views.xml +++ b/odoo/addons/base/views/res_bank_views.xml @@ -65,6 +65,7 @@
+ @@ -84,7 +85,7 @@ res.partner.bank - +