diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 44a6a24a81e..8f757c52333 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -233,6 +233,18 @@ class PosOrder(models.Model): self.ensure_one() return self.lines._prepare_tax_base_line_values(sign=sign) + @api.model + def _get_invoice_lines_values(self, line_values, pos_order_line): + return { + 'product_id': line_values['product'].id, + 'quantity': line_values['quantity'], + 'discount': line_values['discount'], + 'price_unit': line_values['price_unit'], + 'name': line_values['name'], + 'tax_ids': [(6, 0, line_values['taxes'].ids)], + 'product_uom_id': line_values['uom'].id, + } + def _prepare_invoice_lines(self): """ Prepare a list of orm commands containing the dictionaries to fill the 'invoice_line_ids' field when creating an invoice. @@ -244,15 +256,8 @@ class PosOrder(models.Model): invoice_lines = [] for line_values in line_values_list: line = line_values['record'] - invoice_lines.append((0, None, { - 'product_id': line_values['product'].id, - 'quantity': line_values['quantity'], - 'discount': line_values['discount'], - 'price_unit': line_values['price_unit'], - 'name': line_values['name'], - 'tax_ids': [(6, 0, line_values['taxes'].ids)], - 'product_uom_id': line_values['uom'].id, - })) + invoice_lines_values = self._get_invoice_lines_values(line_values, line) + invoice_lines.append((0, None, invoice_lines_values)) if line.order_id.pricelist_id.discount_policy == 'without_discount' and float_compare(line.price_unit, line.product_id.lst_price, precision_rounding=self.currency_id.rounding) < 0: invoice_lines.append((0, None, { 'name': _('Price discount from %s -> %s', diff --git a/addons/pos_sale/models/pos_order.py b/addons/pos_sale/models/pos_order.py index 0b666f54b73..0ddf0a2f56b 100644 --- a/addons/pos_sale/models/pos_order.py +++ b/addons/pos_sale/models/pos_order.py @@ -113,6 +113,14 @@ class PosOrder(models.Model): 'domain': [('id', 'in', linked_orders.ids)], } + def _get_invoice_lines_values(self, line_values, pos_line): + inv_line_vals = super()._get_invoice_lines_values(line_values, pos_line) + + if pos_line.sale_order_origin_id: + origin_line = pos_line.sale_order_line_id + origin_line._set_analytic_distribution(inv_line_vals) + + return inv_line_vals class PosOrderLine(models.Model): _inherit = 'pos.order.line' diff --git a/addons/pos_sale/static/tests/tours/PosSaleTour.js b/addons/pos_sale/static/tests/tours/PosSaleTour.js index 7c628e16e51..6422f762e6e 100644 --- a/addons/pos_sale/static/tests/tours/PosSaleTour.js +++ b/addons/pos_sale/static/tests/tours/PosSaleTour.js @@ -170,3 +170,19 @@ registry ].flat(), }); + +registry +.category("web_tour.tours") +.add('PosSettleAndInvoiceOrder', { + test: true, + url: '/pos/ui', + steps: () => [ + ProductScreen.confirmOpeningPopup(), + ProductScreen.clickQuotationButton(), + ProductScreen.selectFirstOrder(), + ProductScreen.clickPayButton(), + PaymentScreen.clickPaymentMethod("Bank"), + PaymentScreen.clickInvoiceButton(), + PaymentScreen.clickValidate(), + ].flat(), +}); diff --git a/addons/pos_sale/tests/test_pos_sale_flow.py b/addons/pos_sale/tests/test_pos_sale_flow.py index 0adeab99eca..c1d4d5ec9f3 100644 --- a/addons/pos_sale/tests/test_pos_sale_flow.py +++ b/addons/pos_sale/tests/test_pos_sale_flow.py @@ -361,3 +361,30 @@ class TestPoSSale(TestPointOfSaleHttpCommon): self.main_pos_config.open_ui() self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PosSettleOrderWithNote', login="accountman") + def test_pos_invoice_analytic_account(self): + #create a sale order with product_a + self.analytic_plan_projects = self.env['account.analytic.plan'].create({'name': 'Projects'}) + self.analytic_plan_departments = self.env['account.analytic.plan'].create({'name': 'Departments test'}) + + self.analytic_account_partner_a_1 = self.env['account.analytic.account'].create({ + 'name': 'analytic_account_partner_a_1', + 'partner_id': self.partner_a.id, + 'plan_id': self.analytic_plan_projects.id, + }) + self.env['sale.order'].create({ + 'partner_id': self.env.ref('base.res_partner_2').id, + 'order_line': [(0, 0, { + 'product_id': self.desk_pad.id, + 'name': self.desk_pad.name, + 'product_uom_qty': 3.5, + 'product_uom': self.desk_pad.uom_id.id, + 'price_unit': self.desk_pad.lst_price, + })], + 'analytic_account_id': self.analytic_account_partner_a_1.id, + }) + self.main_pos_config.open_ui() + self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PosSettleAndInvoiceOrder', login="accountman") + + pos_order = self.env['pos.order'].search([], order='id desc', limit=1) + self.assertTrue(pos_order.account_move.line_ids[0].analytic_distribution, "Analytic distribution should be set on the invoice line") + self.assertEqual(pos_order.account_move.line_ids[0].analytic_distribution.get(str(self.analytic_account_partner_a_1.id)), 100) diff --git a/addons/sale/models/sale_order_line.py b/addons/sale/models/sale_order_line.py index 75afa3cb80e..3a26cdfbbb6 100644 --- a/addons/sale/models/sale_order_line.py +++ b/addons/sale/models/sale_order_line.py @@ -1127,21 +1127,24 @@ class SaleOrderLine(models.Model): 'sale_line_ids': [Command.link(self.id)], 'is_downpayment': self.is_downpayment, } - analytic_account_id = self.order_id.analytic_account_id.id - if self.analytic_distribution and not self.display_type: - res['analytic_distribution'] = self.analytic_distribution - if analytic_account_id and not self.display_type: - analytic_account_id = str(analytic_account_id) - if 'analytic_distribution' in res: - res['analytic_distribution'][analytic_account_id] = res['analytic_distribution'].get(analytic_account_id, 0) + 100 - else: - res['analytic_distribution'] = {analytic_account_id: 100} + self._set_analytic_distribution(res, **optional_values) if optional_values: res.update(optional_values) if self.display_type: res['account_id'] = False return res + def _set_analytic_distribution(self, inv_line_vals, **optional_values): + analytic_account_id = self.order_id.analytic_account_id.id + if self.analytic_distribution and not self.display_type: + inv_line_vals['analytic_distribution'] = self.analytic_distribution + if analytic_account_id and not self.display_type: + analytic_account_id = str(analytic_account_id) + if 'analytic_distribution' in inv_line_vals: + inv_line_vals['analytic_distribution'][analytic_account_id] = inv_line_vals['analytic_distribution'].get(analytic_account_id, 0) + 100 + else: + inv_line_vals['analytic_distribution'] = {analytic_account_id: 100} + def _prepare_procurement_values(self, group_id=False): """ Prepare specific key for moves or other components that will be created from a stock rule coming from a sale order line. This method could be override in order to add other custom key that could