From bdcf4160eb73d10f82d0acf19aca62522fefdb4a Mon Sep 17 00:00:00 2001 From: ahenriquez Date: Fri, 15 May 2020 15:12:20 +0200 Subject: [PATCH 01/14] [ADD]account_move_line_sale_info --- account_move_line_sale_info/README.rst | 65 ++++++ account_move_line_sale_info/__init__.py | 4 + account_move_line_sale_info/__manifest__.py | 20 ++ account_move_line_sale_info/hooks.py | 39 ++++ .../models/__init__.py | 6 + .../models/account_invoice.py | 28 +++ .../models/account_move.py | 23 ++ .../models/sale_order_line.py | 20 ++ .../models/stock_move.py | 16 ++ .../security/account_security.xml | 10 + .../static/description/icon.png | Bin 0 -> 9455 bytes account_move_line_sale_info/tests/__init__.py | 3 + .../tests/test_account_move_line_sale_info.py | 210 ++++++++++++++++++ .../views/account_move_view.xml | 70 ++++++ 14 files changed, 514 insertions(+) create mode 100644 account_move_line_sale_info/README.rst create mode 100644 account_move_line_sale_info/__init__.py create mode 100644 account_move_line_sale_info/__manifest__.py create mode 100644 account_move_line_sale_info/hooks.py create mode 100644 account_move_line_sale_info/models/__init__.py create mode 100644 account_move_line_sale_info/models/account_invoice.py create mode 100644 account_move_line_sale_info/models/account_move.py create mode 100644 account_move_line_sale_info/models/sale_order_line.py create mode 100644 account_move_line_sale_info/models/stock_move.py create mode 100644 account_move_line_sale_info/security/account_security.xml create mode 100644 account_move_line_sale_info/static/description/icon.png create mode 100644 account_move_line_sale_info/tests/__init__.py create mode 100644 account_move_line_sale_info/tests/test_account_move_line_sale_info.py create mode 100644 account_move_line_sale_info/views/account_move_view.xml diff --git a/account_move_line_sale_info/README.rst b/account_move_line_sale_info/README.rst new file mode 100644 index 000000000..bd79bd9d8 --- /dev/null +++ b/account_move_line_sale_info/README.rst @@ -0,0 +1,65 @@ +.. image:: https://img.shields.io/badge/license-AGPLv3-blue.svg + :target: https://www.gnu.org/licenses/agpl.html + :alt: License: AGPL-3 + +=========================== +Account Move Line Sale Info +=========================== + +This module will add the sale order line to journal items. + +The ultimate goal is to establish the purchase order line as one of the key +fields to reconcile the Goods Delivered Not Invoiced accrual account. + + +Usage +===== + +The sale order line will be automatically copied to the journal items. + +* When a supplier invoice is created referencing sales orders, the + sale order line will be copied to the corresponding journal item. + +* When a stock move is validated and generates a journal entry, the sale + order line is copied to the account move line. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/92/11.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Aaron Henriquez + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/account_move_line_sale_info/__init__.py b/account_move_line_sale_info/__init__.py new file mode 100644 index 000000000..e66fb1c2e --- /dev/null +++ b/account_move_line_sale_info/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import models +from .hooks import post_init_hook diff --git a/account_move_line_sale_info/__manifest__.py b/account_move_line_sale_info/__manifest__.py new file mode 100644 index 000000000..c7c4b8533 --- /dev/null +++ b/account_move_line_sale_info/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2020 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Account Move Line Sale Info", + "summary": "Introduces the purchase order line to the journal items", + "version": "11.0.1.0.0", + "author": "ForgeFlow S.L., " + "Odoo Community Association (OCA)", + "website": "http://www.github.com/OCA/account-financial-tools", + "category": "Generic", + "depends": ["account", "sale"], + "license": "AGPL-3", + "data": [ + "security/account_security.xml", + "views/account_move_view.xml", + ], + 'installable': True, + 'post_init_hook': 'post_init_hook', +} diff --git a/account_move_line_sale_info/hooks.py b/account_move_line_sale_info/hooks.py new file mode 100644 index 000000000..d3761b94b --- /dev/null +++ b/account_move_line_sale_info/hooks.py @@ -0,0 +1,39 @@ +# Copyright 2019 ForgeFlow S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import ast +from odoo import api, SUPERUSER_ID + + +def post_init_hook(cr, registry): + + """ INIT sale references in acount move line """ + # FOR stock moves + cr.execute(""" + update account_move_line aml set sale_line_id = sm.sale_line_id + FROM account_move_line aml2 + INNER JOIN stock_move sm ON + aml2.stock_move_id = sm.id + WHERE aml.id = aml2.id; + """) + # FOR invoices + cr.execute(""" + update account_move_line aml set sale_line_id = sol.id + FROM account_move_line aml2 + INNER JOIN account_invoice ai ON + ai.id = aml2.invoice_id + INNER JOIN account_invoice_line ail ON + ail.invoice_id = ai.id + INNER JOIN sale_order_line_invoice_rel rel ON + rel.invoice_line_id = ail.id + INNER JOIN sale_order_line sol ON + rel.order_line_id = sol.id + WHERE aml.id = aml2.id; + """) + + # NOW we can fill the SO + cr.execute(""" + UPDATE account_move_line aml + SET sale_id = sol.order_id + FROM sale_order_line AS sol + WHERE aml.sale_line_id = sol.id + """) diff --git a/account_move_line_sale_info/models/__init__.py b/account_move_line_sale_info/models/__init__.py new file mode 100644 index 000000000..f4c1f16e6 --- /dev/null +++ b/account_move_line_sale_info/models/__init__.py @@ -0,0 +1,6 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import account_move +from . import account_invoice +from . import sale_order_line +from . import stock_move diff --git a/account_move_line_sale_info/models/account_invoice.py b/account_move_line_sale_info/models/account_invoice.py new file mode 100644 index 000000000..859b45247 --- /dev/null +++ b/account_move_line_sale_info/models/account_invoice.py @@ -0,0 +1,28 @@ +# Copyright 2020 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, models + + +class AccountInvoice(models.Model): + + _inherit = 'account.invoice' + + @api.model + def invoice_line_move_line_get(self): + res = super(AccountInvoice, self).invoice_line_move_line_get() + + invoice_line_model = self.env['account.invoice.line'] + for move_line_dict in res: + if 'invl_id' in move_line_dict: + line = invoice_line_model.browse(move_line_dict['invl_id']) + if line.sale_line_ids and len(line.sale_line_ids) == 1: + move_line_dict['sale_line_id'] = line.sale_line_ids[0].id + return res + + @api.model + def line_get_convert(self, line, part): + res = super(AccountInvoice, self).line_get_convert(line, part) + if line.get('sale_line_id', False): + res['sale_line_id'] = line.get('sale_line_id') + return res diff --git a/account_move_line_sale_info/models/account_move.py b/account_move_line_sale_info/models/account_move.py new file mode 100644 index 000000000..01f083681 --- /dev/null +++ b/account_move_line_sale_info/models/account_move.py @@ -0,0 +1,23 @@ +# Copyright 2020 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class AccountMoveLine(models.Model): + + _inherit = 'account.move.line' + + sale_line_id = fields.Many2one( + comodel_name='sale.order.line', + string='Sale Order Line', + ondelete='set null', index=True, + ) + + sale_id = fields.Many2one( + comodel_name='sale.order', + related='sale_line_id.order_id', + string='Sales Order', + ondelete='set null', + store=True, index=True, + ) diff --git a/account_move_line_sale_info/models/sale_order_line.py b/account_move_line_sale_info/models/sale_order_line.py new file mode 100644 index 000000000..ccb9043c4 --- /dev/null +++ b/account_move_line_sale_info/models/sale_order_line.py @@ -0,0 +1,20 @@ +# Copyright 2020 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + @api.multi + def name_get(self): + result = [] + orig_name = dict(super(SaleOrderLine, self).name_get()) + for line in self: + name = orig_name[line.id] + if self.env.context.get('so_line_info', False): + name = "[%s] %s (%s)" % (line.order_id.name, name, + line.order_id.state) + result.append((line.id, name)) + return result diff --git a/account_move_line_sale_info/models/stock_move.py b/account_move_line_sale_info/models/stock_move.py new file mode 100644 index 000000000..a346a0168 --- /dev/null +++ b/account_move_line_sale_info/models/stock_move.py @@ -0,0 +1,16 @@ +# Copyright 2020 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + @api.model + def _prepare_account_move_line(self, qty, cost, + credit_account_id, debit_account_id): + res = super(StockMove, self)._prepare_account_move_line( + qty, cost, credit_account_id, debit_account_id) + for line in res: + line[2]['sale_line_id'] = self.sale_line_id.id + return res diff --git a/account_move_line_sale_info/security/account_security.xml b/account_move_line_sale_info/security/account_security.xml new file mode 100644 index 000000000..a6d3ed040 --- /dev/null +++ b/account_move_line_sale_info/security/account_security.xml @@ -0,0 +1,10 @@ + + + + + Sale info in Journal Items + + + + + diff --git a/account_move_line_sale_info/static/description/icon.png b/account_move_line_sale_info/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/account_move_line_sale_info/tests/__init__.py b/account_move_line_sale_info/tests/__init__.py new file mode 100644 index 000000000..a2d0246f8 --- /dev/null +++ b/account_move_line_sale_info/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import test_account_move_line_sale_info diff --git a/account_move_line_sale_info/tests/test_account_move_line_sale_info.py b/account_move_line_sale_info/tests/test_account_move_line_sale_info.py new file mode 100644 index 000000000..a3eaa9745 --- /dev/null +++ b/account_move_line_sale_info/tests/test_account_move_line_sale_info.py @@ -0,0 +1,210 @@ +# Copyright 2020 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo.tests import common +from odoo import fields + + +class TestAccountMoveLineSaleInfo(common.TransactionCase): + + def setUp(self): + super(TestAccountMoveLineSaleInfo, self).setUp() + self.sale_model = self.env['sale.order'] + self.sale_line_model = self.env['sale.order.line'] + self.invoice_model = self.env['account.invoice'] + self.invoice_line_model = self.env['account.invoice.line'] + self.product_model = self.env['product.product'] + self.product_ctg_model = self.env['product.category'] + self.acc_type_model = self.env['account.account.type'] + self.account_model = self.env['account.account'] + self.aml_model = self.env['account.move.line'] + self.res_users_model = self.env['res.users'] + + self.partner1 = self.env.ref('base.res_partner_1') + self.location_stock = self.env.ref('stock.stock_location_stock') + self.company = self.env.ref('base.main_company') + self.group_sale_user = self.env.ref('sales_team.group_sale_salesman') + self.group_account_invoice = self.env.ref( + 'account.group_account_invoice') + self.group_account_manager = self.env.ref( + 'account.group_account_manager') + + # Create account for Goods Received Not Invoiced + acc_type = self._create_account_type('equity', 'other') + name = 'Goods Received Not Invoiced' + code = 'grni' + self.account_grni = self._create_account(acc_type, name, code, + self.company) + + # Create account for Cost of Goods Sold + acc_type = self._create_account_type('expense', 'other') + name = 'Cost of Goods Sold' + code = 'cogs' + self.account_cogs = self._create_account(acc_type, name, code, + self.company) + # Create account for Inventory + acc_type = self._create_account_type('asset', 'other') + name = 'Inventory' + code = 'inventory' + self.account_inventory = self._create_account(acc_type, name, code, + self.company) + # Create Product + self.product = self._create_product() + + # Create users + self.sale_user = self._create_user('sale_user', + [self.group_sale_user, + self.group_account_invoice], + self.company) + self.account_invoice = self._create_user('account_invoice', + [self.group_account_invoice], + self.company) + self.account_manager = self._create_user('account_manager', + [self.group_account_manager], + self.company) + + def _create_user(self, login, groups, company): + """ Create a user.""" + group_ids = [group.id for group in groups] + user = \ + self.res_users_model.with_context( + {'no_reset_password': True}).create({ + 'name': 'Test User', + 'login': login, + 'password': 'demo', + 'email': 'test@yourcompany.com', + 'company_id': company.id, + 'company_ids': [(4, company.id)], + 'groups_id': [(6, 0, group_ids)] + }) + return user.id + + def _create_account_type(self, name, type): + acc_type = self.acc_type_model.create({ + 'name': name, + 'type': type + }) + return acc_type + + def _create_account(self, acc_type, name, code, company): + """Create an account.""" + account = self.account_model.create({ + 'name': name, + 'code': code, + 'user_type_id': acc_type.id, + 'company_id': company.id + }) + return account + + def _create_product(self): + """Create a Product.""" + # group_ids = [group.id for group in groups] + product_ctg = self.product_ctg_model.create({ + 'name': 'test_product_ctg', + 'property_stock_valuation_account_id': self.account_inventory.id, + 'property_valuation': 'real_time', + 'property_stock_account_input_categ_id': self.account_grni.id, + 'property_stock_account_output_categ_id': self.account_cogs.id, + }) + product = self.product_model.create({ + 'name': 'test_product', + 'categ_id': product_ctg.id, + 'type': 'product', + 'standard_price': 1.0, + 'list_price': 1.0, + }) + return product + + def _create_sale(self, line_products): + """ Create a sale order. + + ``line_products`` is a list of tuple [(product, qty)] + """ + lines = [] + for product, qty in line_products: + line_values = { + 'name': product.name, + 'product_id': product.id, + 'product_qty': qty, + 'product_uom': product.uom_id.id, + 'price_unit': 500, + 'date_planned': fields.datetime.now() + } + lines.append( + (0, 0, line_values) + ) + return self.sale_model.create({ + 'partner_id': self.partner1.id, + 'order_line': lines + }) + + def _get_balance(self, domain): + """ + Call read_group method and return the balance of particular account. + """ + aml_rec = self.aml_model.read_group(domain, + ['debit', 'credit', 'account_id'], + ['account_id']) + if aml_rec: + return aml_rec[0].get('debit', 0) - aml_rec[0].get('credit', 0) + else: + return 0.0 + + def _check_account_balance(self, account_id, sale_line=None, + expected_balance=0.0): + """ + Check the balance of the account + """ + domain = [('account_id', '=', account_id)] + if sale_line: + domain.extend([('sale_line_id', '=', sale_line.id)]) + + balance = self._get_balance(domain) + if sale_line: + self.assertEqual(balance, expected_balance, + 'Balance is not %s for sale Line %s.' + % (str(expected_balance), sale_line.name)) + + def test_sale_invoice(self): + """Test that the po line moves from the sale order to the + account move line and to the invoice line. + """ + sale = self._create_sale([(self.product, 1)]) + po_line = False + for line in sale.order_line: + po_line = line + break + sale.button_confirm() + picking = sale.picking_ids[0] + picking.force_assign() + picking.move_lines.write({'quantity_done': 1.0}) + picking.button_validate() + + expected_balance = 1.0 + self._check_account_balance(self.account_inventory.id, + sale_line=po_line, + expected_balance=expected_balance) + + invoice = self.invoice_model.create({ + 'partner_id': self.partner1.id, + 'sale_id': sale.id, + 'account_id': sale.partner_id.property_account_payable_id.id, + }) + invoice.sale_order_change() + invoice.action_invoice_open() + + for aml in invoice.move_id.line_ids: + if aml.product_id == po_line.product_id and aml.invoice_id: + self.assertEqual(aml.sale_line_id, po_line, + 'sale Order line has not been copied ' + 'from the invoice to the account move line.') + + def test_name_get(self): + sale = self._create_sale([(self.product, 1)]) + po_line = sale.order_line[0] + name_get = po_line.with_context({'po_line_info': True}).name_get() + self.assertEqual(name_get, [(po_line.id, "[%s] %s (%s)" % ( + po_line.order_id.name, po_line.name, + po_line.order_id.state, + ))]) + name_get_no_ctx = po_line.name_get() + self.assertEqual(name_get_no_ctx, [(po_line.id, po_line.name)]) diff --git a/account_move_line_sale_info/views/account_move_view.xml b/account_move_line_sale_info/views/account_move_view.xml new file mode 100644 index 000000000..8fa88ec9a --- /dev/null +++ b/account_move_line_sale_info/views/account_move_view.xml @@ -0,0 +1,70 @@ + + + + + account.move.line.form + account.move.line + + + + + + + + + + + account.move.line.tree + account.move.line + + + + + + + + + + + + Journal Items + account.move.line + + + + + + + + + + + + + + + + account.move.form + account.move + + + + + + + + + + From 3de61787082f38546050955b355aa296fa754198 Mon Sep 17 00:00:00 2001 From: ahenriquez Date: Mon, 18 May 2020 15:34:53 +0200 Subject: [PATCH 02/14] [IMP]hook. Odoo does not add the invoice_id in payable entries so need to use the product. --- account_move_line_sale_info/__manifest__.py | 2 +- account_move_line_sale_info/hooks.py | 25 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/account_move_line_sale_info/__manifest__.py b/account_move_line_sale_info/__manifest__.py index c7c4b8533..85625fb5f 100644 --- a/account_move_line_sale_info/__manifest__.py +++ b/account_move_line_sale_info/__manifest__.py @@ -9,7 +9,7 @@ "Odoo Community Association (OCA)", "website": "http://www.github.com/OCA/account-financial-tools", "category": "Generic", - "depends": ["account", "sale"], + "depends": ["account_move_line_stock_info", "sale"], "license": "AGPL-3", "data": [ "security/account_security.xml", diff --git a/account_move_line_sale_info/hooks.py b/account_move_line_sale_info/hooks.py index d3761b94b..3d5bbab94 100644 --- a/account_move_line_sale_info/hooks.py +++ b/account_move_line_sale_info/hooks.py @@ -27,6 +27,7 @@ def post_init_hook(cr, registry): rel.invoice_line_id = ail.id INNER JOIN sale_order_line sol ON rel.order_line_id = sol.id + AND sol.product_id = aml2.product_id WHERE aml.id = aml2.id; """) @@ -36,4 +37,28 @@ def post_init_hook(cr, registry): SET sale_id = sol.order_id FROM sale_order_line AS sol WHERE aml.sale_line_id = sol.id + RETURNING aml.move_id + """) + + # NOW we can fill the lines without invoice_id (Odoo put it very + # complicated) + + cr.execute(""" + UPDATE account_move_line aml + SET sale_id = so.id + FROM sale_order_line so + LEFT JOIN account_move_line aml2 + ON aml2.sale_id = so.id + WHERE aml2.move_id = aml.move_id + """) + + cr.execute(""" + update account_move_line aml set sale_line_id = sol.id + FROM account_move_line aml2 + INNER JOIN sale_order so ON + so.id = aml2.sale_id + INNER JOIN sale_order_line sol ON + so.id = sol.order_id + AND sol.product_id = aml2.product_id + WHERE aml.id = aml2.id; """) From 14739a26a74105b284e85edf1b6d6f07bfcc0ed3 Mon Sep 17 00:00:00 2001 From: ahenriquez Date: Wed, 20 May 2020 17:36:38 +0200 Subject: [PATCH 03/14] [UPD]Include sale line in anglosaxon entries --- account_move_line_sale_info/models/account_invoice.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/account_move_line_sale_info/models/account_invoice.py b/account_move_line_sale_info/models/account_invoice.py index 859b45247..41a64d682 100644 --- a/account_move_line_sale_info/models/account_invoice.py +++ b/account_move_line_sale_info/models/account_invoice.py @@ -26,3 +26,14 @@ class AccountInvoice(models.Model): if line.get('sale_line_id', False): res['sale_line_id'] = line.get('sale_line_id') return res + + @api.model + def _anglo_saxon_sale_move_lines(self, i_line): + """ + We need to add the sale_line to those entries too + """ + res = super()._anglo_saxon_sale_move_lines(i_line) + for aml in res: + if i_line.sale_line_ids and len(i_line.sale_line_ids) == 1: + aml['sale_line_id'] = i_line.sale_line_ids[0].id + return res From ebd516715ee6dcd660463c884fea8bd87e2420b4 Mon Sep 17 00:00:00 2001 From: AaronHForgeFlow Date: Wed, 29 Jul 2020 17:26:15 +0200 Subject: [PATCH 04/14] [IMP] : black, isort, prettier --- account_move_line_sale_info/__manifest__.py | 12 +- account_move_line_sale_info/hooks.py | 35 ++- .../models/account_invoice.py | 16 +- .../models/account_move.py | 20 +- .../models/sale_order_line.py | 5 +- .../models/stock_move.py | 10 +- .../security/account_security.xml | 8 +- .../tests/test_account_move_line_sale_info.py | 254 +++++++++--------- .../views/account_move_view.xml | 89 +++--- 9 files changed, 244 insertions(+), 205 deletions(-) diff --git a/account_move_line_sale_info/__manifest__.py b/account_move_line_sale_info/__manifest__.py index 85625fb5f..98fc7507c 100644 --- a/account_move_line_sale_info/__manifest__.py +++ b/account_move_line_sale_info/__manifest__.py @@ -5,16 +5,12 @@ "name": "Account Move Line Sale Info", "summary": "Introduces the purchase order line to the journal items", "version": "11.0.1.0.0", - "author": "ForgeFlow S.L., " - "Odoo Community Association (OCA)", + "author": "ForgeFlow S.L., " "Odoo Community Association (OCA)", "website": "http://www.github.com/OCA/account-financial-tools", "category": "Generic", "depends": ["account_move_line_stock_info", "sale"], "license": "AGPL-3", - "data": [ - "security/account_security.xml", - "views/account_move_view.xml", - ], - 'installable': True, - 'post_init_hook': 'post_init_hook', + "data": ["security/account_security.xml", "views/account_move_view.xml",], + "installable": True, + "post_init_hook": "post_init_hook", } diff --git a/account_move_line_sale_info/hooks.py b/account_move_line_sale_info/hooks.py index 3d5bbab94..f47a472f5 100644 --- a/account_move_line_sale_info/hooks.py +++ b/account_move_line_sale_info/hooks.py @@ -1,22 +1,26 @@ # Copyright 2019 ForgeFlow S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import ast -from odoo import api, SUPERUSER_ID + +from odoo import SUPERUSER_ID, api def post_init_hook(cr, registry): """ INIT sale references in acount move line """ # FOR stock moves - cr.execute(""" + cr.execute( + """ update account_move_line aml set sale_line_id = sm.sale_line_id FROM account_move_line aml2 INNER JOIN stock_move sm ON aml2.stock_move_id = sm.id WHERE aml.id = aml2.id; - """) + """ + ) # FOR invoices - cr.execute(""" + cr.execute( + """ update account_move_line aml set sale_line_id = sol.id FROM account_move_line aml2 INNER JOIN account_invoice ai ON @@ -29,30 +33,36 @@ def post_init_hook(cr, registry): rel.order_line_id = sol.id AND sol.product_id = aml2.product_id WHERE aml.id = aml2.id; - """) + """ + ) # NOW we can fill the SO - cr.execute(""" + cr.execute( + """ UPDATE account_move_line aml SET sale_id = sol.order_id FROM sale_order_line AS sol WHERE aml.sale_line_id = sol.id RETURNING aml.move_id - """) + """ + ) # NOW we can fill the lines without invoice_id (Odoo put it very # complicated) - cr.execute(""" + cr.execute( + """ UPDATE account_move_line aml SET sale_id = so.id FROM sale_order_line so LEFT JOIN account_move_line aml2 - ON aml2.sale_id = so.id + ON aml2.sale_id = so.id WHERE aml2.move_id = aml.move_id - """) + """ + ) - cr.execute(""" + cr.execute( + """ update account_move_line aml set sale_line_id = sol.id FROM account_move_line aml2 INNER JOIN sale_order so ON @@ -61,4 +71,5 @@ def post_init_hook(cr, registry): so.id = sol.order_id AND sol.product_id = aml2.product_id WHERE aml.id = aml2.id; - """) + """ + ) diff --git a/account_move_line_sale_info/models/account_invoice.py b/account_move_line_sale_info/models/account_invoice.py index 41a64d682..73d8a555e 100644 --- a/account_move_line_sale_info/models/account_invoice.py +++ b/account_move_line_sale_info/models/account_invoice.py @@ -6,25 +6,25 @@ from odoo import api, models class AccountInvoice(models.Model): - _inherit = 'account.invoice' + _inherit = "account.invoice" @api.model def invoice_line_move_line_get(self): res = super(AccountInvoice, self).invoice_line_move_line_get() - invoice_line_model = self.env['account.invoice.line'] + invoice_line_model = self.env["account.invoice.line"] for move_line_dict in res: - if 'invl_id' in move_line_dict: - line = invoice_line_model.browse(move_line_dict['invl_id']) + if "invl_id" in move_line_dict: + line = invoice_line_model.browse(move_line_dict["invl_id"]) if line.sale_line_ids and len(line.sale_line_ids) == 1: - move_line_dict['sale_line_id'] = line.sale_line_ids[0].id + move_line_dict["sale_line_id"] = line.sale_line_ids[0].id return res @api.model def line_get_convert(self, line, part): res = super(AccountInvoice, self).line_get_convert(line, part) - if line.get('sale_line_id', False): - res['sale_line_id'] = line.get('sale_line_id') + if line.get("sale_line_id", False): + res["sale_line_id"] = line.get("sale_line_id") return res @api.model @@ -35,5 +35,5 @@ class AccountInvoice(models.Model): res = super()._anglo_saxon_sale_move_lines(i_line) for aml in res: if i_line.sale_line_ids and len(i_line.sale_line_ids) == 1: - aml['sale_line_id'] = i_line.sale_line_ids[0].id + aml["sale_line_id"] = i_line.sale_line_ids[0].id return res diff --git a/account_move_line_sale_info/models/account_move.py b/account_move_line_sale_info/models/account_move.py index 01f083681..d84d53cc0 100644 --- a/account_move_line_sale_info/models/account_move.py +++ b/account_move_line_sale_info/models/account_move.py @@ -6,18 +6,20 @@ from odoo import fields, models class AccountMoveLine(models.Model): - _inherit = 'account.move.line' + _inherit = "account.move.line" sale_line_id = fields.Many2one( - comodel_name='sale.order.line', - string='Sale Order Line', - ondelete='set null', index=True, + comodel_name="sale.order.line", + string="Sale Order Line", + ondelete="set null", + index=True, ) sale_id = fields.Many2one( - comodel_name='sale.order', - related='sale_line_id.order_id', - string='Sales Order', - ondelete='set null', - store=True, index=True, + comodel_name="sale.order", + related="sale_line_id.order_id", + string="Sales Order", + ondelete="set null", + store=True, + index=True, ) diff --git a/account_move_line_sale_info/models/sale_order_line.py b/account_move_line_sale_info/models/sale_order_line.py index ccb9043c4..fc103cc44 100644 --- a/account_move_line_sale_info/models/sale_order_line.py +++ b/account_move_line_sale_info/models/sale_order_line.py @@ -13,8 +13,7 @@ class SaleOrderLine(models.Model): orig_name = dict(super(SaleOrderLine, self).name_get()) for line in self: name = orig_name[line.id] - if self.env.context.get('so_line_info', False): - name = "[%s] %s (%s)" % (line.order_id.name, name, - line.order_id.state) + if self.env.context.get("so_line_info", False): + name = "[{}] {} ({})".format(line.order_id.name, name, line.order_id.state) result.append((line.id, name)) return result diff --git a/account_move_line_sale_info/models/stock_move.py b/account_move_line_sale_info/models/stock_move.py index a346a0168..07b1bb0b7 100644 --- a/account_move_line_sale_info/models/stock_move.py +++ b/account_move_line_sale_info/models/stock_move.py @@ -7,10 +7,12 @@ class StockMove(models.Model): _inherit = "stock.move" @api.model - def _prepare_account_move_line(self, qty, cost, - credit_account_id, debit_account_id): + def _prepare_account_move_line( + self, qty, cost, credit_account_id, debit_account_id + ): res = super(StockMove, self)._prepare_account_move_line( - qty, cost, credit_account_id, debit_account_id) + qty, cost, credit_account_id, debit_account_id + ) for line in res: - line[2]['sale_line_id'] = self.sale_line_id.id + line[2]["sale_line_id"] = self.sale_line_id.id return res diff --git a/account_move_line_sale_info/security/account_security.xml b/account_move_line_sale_info/security/account_security.xml index a6d3ed040..bd3f7e6d5 100644 --- a/account_move_line_sale_info/security/account_security.xml +++ b/account_move_line_sale_info/security/account_security.xml @@ -1,10 +1,8 @@ - + - Sale info in Journal Items - - + + - diff --git a/account_move_line_sale_info/tests/test_account_move_line_sale_info.py b/account_move_line_sale_info/tests/test_account_move_line_sale_info.py index a3eaa9745..29f62d448 100644 --- a/account_move_line_sale_info/tests/test_account_move_line_sale_info.py +++ b/account_move_line_sale_info/tests/test_account_move_line_sale_info.py @@ -1,117 +1,117 @@ # Copyright 2020 ForgeFlow S.L. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo.tests import common from odoo import fields +from odoo.tests import common class TestAccountMoveLineSaleInfo(common.TransactionCase): - def setUp(self): super(TestAccountMoveLineSaleInfo, self).setUp() - self.sale_model = self.env['sale.order'] - self.sale_line_model = self.env['sale.order.line'] - self.invoice_model = self.env['account.invoice'] - self.invoice_line_model = self.env['account.invoice.line'] - self.product_model = self.env['product.product'] - self.product_ctg_model = self.env['product.category'] - self.acc_type_model = self.env['account.account.type'] - self.account_model = self.env['account.account'] - self.aml_model = self.env['account.move.line'] - self.res_users_model = self.env['res.users'] + self.sale_model = self.env["sale.order"] + self.sale_line_model = self.env["sale.order.line"] + self.invoice_model = self.env["account.invoice"] + self.invoice_line_model = self.env["account.invoice.line"] + self.product_model = self.env["product.product"] + self.product_ctg_model = self.env["product.category"] + self.acc_type_model = self.env["account.account.type"] + self.account_model = self.env["account.account"] + self.aml_model = self.env["account.move.line"] + self.res_users_model = self.env["res.users"] - self.partner1 = self.env.ref('base.res_partner_1') - self.location_stock = self.env.ref('stock.stock_location_stock') - self.company = self.env.ref('base.main_company') - self.group_sale_user = self.env.ref('sales_team.group_sale_salesman') - self.group_account_invoice = self.env.ref( - 'account.group_account_invoice') - self.group_account_manager = self.env.ref( - 'account.group_account_manager') + self.partner1 = self.env.ref("base.res_partner_1") + self.location_stock = self.env.ref("stock.stock_location_stock") + self.company = self.env.ref("base.main_company") + self.group_sale_user = self.env.ref("sales_team.group_sale_salesman") + self.group_account_invoice = self.env.ref("account.group_account_invoice") + self.group_account_manager = self.env.ref("account.group_account_manager") # Create account for Goods Received Not Invoiced - acc_type = self._create_account_type('equity', 'other') - name = 'Goods Received Not Invoiced' - code = 'grni' - self.account_grni = self._create_account(acc_type, name, code, - self.company) + acc_type = self._create_account_type("equity", "other") + name = "Goods Received Not Invoiced" + code = "grni" + self.account_grni = self._create_account(acc_type, name, code, self.company) # Create account for Cost of Goods Sold - acc_type = self._create_account_type('expense', 'other') - name = 'Cost of Goods Sold' - code = 'cogs' - self.account_cogs = self._create_account(acc_type, name, code, - self.company) + acc_type = self._create_account_type("expense", "other") + name = "Cost of Goods Sold" + code = "cogs" + self.account_cogs = self._create_account(acc_type, name, code, self.company) # Create account for Inventory - acc_type = self._create_account_type('asset', 'other') - name = 'Inventory' - code = 'inventory' - self.account_inventory = self._create_account(acc_type, name, code, - self.company) + acc_type = self._create_account_type("asset", "other") + name = "Inventory" + code = "inventory" + self.account_inventory = self._create_account( + acc_type, name, code, self.company + ) # Create Product self.product = self._create_product() # Create users - self.sale_user = self._create_user('sale_user', - [self.group_sale_user, - self.group_account_invoice], - self.company) - self.account_invoice = self._create_user('account_invoice', - [self.group_account_invoice], - self.company) - self.account_manager = self._create_user('account_manager', - [self.group_account_manager], - self.company) + self.sale_user = self._create_user( + "sale_user", + [self.group_sale_user, self.group_account_invoice], + self.company, + ) + self.account_invoice = self._create_user( + "account_invoice", [self.group_account_invoice], self.company + ) + self.account_manager = self._create_user( + "account_manager", [self.group_account_manager], self.company + ) def _create_user(self, login, groups, company): """ Create a user.""" group_ids = [group.id for group in groups] - user = \ - self.res_users_model.with_context( - {'no_reset_password': True}).create({ - 'name': 'Test User', - 'login': login, - 'password': 'demo', - 'email': 'test@yourcompany.com', - 'company_id': company.id, - 'company_ids': [(4, company.id)], - 'groups_id': [(6, 0, group_ids)] - }) + user = self.res_users_model.with_context({"no_reset_password": True}).create( + { + "name": "Test User", + "login": login, + "password": "demo", + "email": "test@yourcompany.com", + "company_id": company.id, + "company_ids": [(4, company.id)], + "groups_id": [(6, 0, group_ids)], + } + ) return user.id def _create_account_type(self, name, type): - acc_type = self.acc_type_model.create({ - 'name': name, - 'type': type - }) + acc_type = self.acc_type_model.create({"name": name, "type": type}) return acc_type def _create_account(self, acc_type, name, code, company): """Create an account.""" - account = self.account_model.create({ - 'name': name, - 'code': code, - 'user_type_id': acc_type.id, - 'company_id': company.id - }) + account = self.account_model.create( + { + "name": name, + "code": code, + "user_type_id": acc_type.id, + "company_id": company.id, + } + ) return account def _create_product(self): """Create a Product.""" # group_ids = [group.id for group in groups] - product_ctg = self.product_ctg_model.create({ - 'name': 'test_product_ctg', - 'property_stock_valuation_account_id': self.account_inventory.id, - 'property_valuation': 'real_time', - 'property_stock_account_input_categ_id': self.account_grni.id, - 'property_stock_account_output_categ_id': self.account_cogs.id, - }) - product = self.product_model.create({ - 'name': 'test_product', - 'categ_id': product_ctg.id, - 'type': 'product', - 'standard_price': 1.0, - 'list_price': 1.0, - }) + product_ctg = self.product_ctg_model.create( + { + "name": "test_product_ctg", + "property_stock_valuation_account_id": self.account_inventory.id, + "property_valuation": "real_time", + "property_stock_account_input_categ_id": self.account_grni.id, + "property_stock_account_output_categ_id": self.account_cogs.id, + } + ) + product = self.product_model.create( + { + "name": "test_product", + "categ_id": product_ctg.id, + "type": "product", + "standard_price": 1.0, + "list_price": 1.0, + } + ) return product def _create_sale(self, line_products): @@ -122,47 +122,46 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): lines = [] for product, qty in line_products: line_values = { - 'name': product.name, - 'product_id': product.id, - 'product_qty': qty, - 'product_uom': product.uom_id.id, - 'price_unit': 500, - 'date_planned': fields.datetime.now() + "name": product.name, + "product_id": product.id, + "product_qty": qty, + "product_uom": product.uom_id.id, + "price_unit": 500, + "date_planned": fields.datetime.now(), } - lines.append( - (0, 0, line_values) - ) - return self.sale_model.create({ - 'partner_id': self.partner1.id, - 'order_line': lines - }) + lines.append((0, 0, line_values)) + return self.sale_model.create( + {"partner_id": self.partner1.id, "order_line": lines} + ) def _get_balance(self, domain): """ Call read_group method and return the balance of particular account. """ - aml_rec = self.aml_model.read_group(domain, - ['debit', 'credit', 'account_id'], - ['account_id']) + aml_rec = self.aml_model.read_group( + domain, ["debit", "credit", "account_id"], ["account_id"] + ) if aml_rec: - return aml_rec[0].get('debit', 0) - aml_rec[0].get('credit', 0) + return aml_rec[0].get("debit", 0) - aml_rec[0].get("credit", 0) else: return 0.0 - def _check_account_balance(self, account_id, sale_line=None, - expected_balance=0.0): + def _check_account_balance(self, account_id, sale_line=None, expected_balance=0.0): """ Check the balance of the account """ - domain = [('account_id', '=', account_id)] + domain = [("account_id", "=", account_id)] if sale_line: - domain.extend([('sale_line_id', '=', sale_line.id)]) + domain.extend([("sale_line_id", "=", sale_line.id)]) balance = self._get_balance(domain) if sale_line: - self.assertEqual(balance, expected_balance, - 'Balance is not %s for sale Line %s.' - % (str(expected_balance), sale_line.name)) + self.assertEqual( + balance, + expected_balance, + "Balance is not %s for sale Line %s." + % (str(expected_balance), sale_line.name), + ) def test_sale_invoice(self): """Test that the po line moves from the sale order to the @@ -176,35 +175,48 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): sale.button_confirm() picking = sale.picking_ids[0] picking.force_assign() - picking.move_lines.write({'quantity_done': 1.0}) + picking.move_lines.write({"quantity_done": 1.0}) picking.button_validate() expected_balance = 1.0 - self._check_account_balance(self.account_inventory.id, - sale_line=po_line, - expected_balance=expected_balance) + self._check_account_balance( + self.account_inventory.id, + sale_line=po_line, + expected_balance=expected_balance, + ) - invoice = self.invoice_model.create({ - 'partner_id': self.partner1.id, - 'sale_id': sale.id, - 'account_id': sale.partner_id.property_account_payable_id.id, - }) + invoice = self.invoice_model.create( + { + "partner_id": self.partner1.id, + "sale_id": sale.id, + "account_id": sale.partner_id.property_account_payable_id.id, + } + ) invoice.sale_order_change() invoice.action_invoice_open() for aml in invoice.move_id.line_ids: if aml.product_id == po_line.product_id and aml.invoice_id: - self.assertEqual(aml.sale_line_id, po_line, - 'sale Order line has not been copied ' - 'from the invoice to the account move line.') + self.assertEqual( + aml.sale_line_id, + po_line, + "sale Order line has not been copied " + "from the invoice to the account move line.", + ) def test_name_get(self): sale = self._create_sale([(self.product, 1)]) po_line = sale.order_line[0] - name_get = po_line.with_context({'po_line_info': True}).name_get() - self.assertEqual(name_get, [(po_line.id, "[%s] %s (%s)" % ( - po_line.order_id.name, po_line.name, - po_line.order_id.state, - ))]) + name_get = po_line.with_context({"po_line_info": True}).name_get() + self.assertEqual( + name_get, + [ + ( + po_line.id, + "[%s] %s (%s)" + % (po_line.order_id.name, po_line.name, po_line.order_id.state,), + ) + ], + ) name_get_no_ctx = po_line.name_get() self.assertEqual(name_get_no_ctx, [(po_line.id, po_line.name)]) diff --git a/account_move_line_sale_info/views/account_move_view.xml b/account_move_line_sale_info/views/account_move_view.xml index 8fa88ec9a..47541062a 100644 --- a/account_move_line_sale_info/views/account_move_view.xml +++ b/account_move_line_sale_info/views/account_move_view.xml @@ -1,70 +1,89 @@ - + - account.move.line.form account.move.line - + - - + + - account.move.line.tree account.move.line - + - - + + - - Journal Items account.move.line - + - - + + - - + + - - account.move.form account.move - + - - - + + + - From c3a041f1ca7622efb726979c02c4ebd361c1eb9c Mon Sep 17 00:00:00 2001 From: AaronHForgeFlow Date: Mon, 3 May 2021 14:04:28 +0200 Subject: [PATCH 05/14] [MIG]account_move_line_sale_info to v13 --- account_move_line_sale_info/__manifest__.py | 10 ++- account_move_line_sale_info/hooks.py | 25 +++--- .../migrations/13.0.1.0.0/pre-migration.py | 12 +++ .../models/__init__.py | 1 - .../models/account_invoice.py | 39 --------- .../models/account_move.py | 45 +++++++++- .../models/sale_order_line.py | 12 ++- .../models/stock_move.py | 4 +- .../security/account_security.xml | 2 +- .../tests/test_account_move_line_sale_info.py | 86 ++++++++++++------- .../views/account_move_view.xml | 18 ++-- 11 files changed, 149 insertions(+), 105 deletions(-) create mode 100644 account_move_line_sale_info/migrations/13.0.1.0.0/pre-migration.py delete mode 100644 account_move_line_sale_info/models/account_invoice.py diff --git a/account_move_line_sale_info/__manifest__.py b/account_move_line_sale_info/__manifest__.py index 98fc7507c..c9604302b 100644 --- a/account_move_line_sale_info/__manifest__.py +++ b/account_move_line_sale_info/__manifest__.py @@ -4,13 +4,17 @@ { "name": "Account Move Line Sale Info", "summary": "Introduces the purchase order line to the journal items", - "version": "11.0.1.0.0", + "version": "13.0.1.0.0", "author": "ForgeFlow S.L., " "Odoo Community Association (OCA)", "website": "http://www.github.com/OCA/account-financial-tools", "category": "Generic", - "depends": ["account_move_line_stock_info", "sale"], + "depends": [ + "account_move_line_stock_info", + "sale_stock", + "stock_account_prepare_anglo_saxon_out_lines_hook", + ], "license": "AGPL-3", - "data": ["security/account_security.xml", "views/account_move_view.xml",], + "data": ["security/account_security.xml", "views/account_move_view.xml"], "installable": True, "post_init_hook": "post_init_hook", } diff --git a/account_move_line_sale_info/hooks.py b/account_move_line_sale_info/hooks.py index f47a472f5..46c9f0eb8 100644 --- a/account_move_line_sale_info/hooks.py +++ b/account_move_line_sale_info/hooks.py @@ -1,8 +1,5 @@ # Copyright 2019 ForgeFlow S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -import ast - -from odoo import SUPERUSER_ID, api def post_init_hook(cr, registry): @@ -11,7 +8,7 @@ def post_init_hook(cr, registry): # FOR stock moves cr.execute( """ - update account_move_line aml set sale_line_id = sm.sale_line_id + UPDATE account_move_line aml SET sale_line_id = sm.sale_line_id FROM account_move_line aml2 INNER JOIN stock_move sm ON aml2.stock_move_id = sm.id @@ -21,14 +18,12 @@ def post_init_hook(cr, registry): # FOR invoices cr.execute( """ - update account_move_line aml set sale_line_id = sol.id + UPDATE account_move_line aml SET sale_line_id = sol.id FROM account_move_line aml2 - INNER JOIN account_invoice ai ON - ai.id = aml2.invoice_id - INNER JOIN account_invoice_line ail ON - ail.invoice_id = ai.id + INNER JOIN account_move am ON + am.id = aml2.move_id INNER JOIN sale_order_line_invoice_rel rel ON - rel.invoice_line_id = ail.id + rel.invoice_line_id = aml2.id INNER JOIN sale_order_line sol ON rel.order_line_id = sol.id AND sol.product_id = aml2.product_id @@ -40,7 +35,7 @@ def post_init_hook(cr, registry): cr.execute( """ UPDATE account_move_line aml - SET sale_id = sol.order_id + SET sale_order_id = sol.order_id FROM sale_order_line AS sol WHERE aml.sale_line_id = sol.id RETURNING aml.move_id @@ -53,10 +48,10 @@ def post_init_hook(cr, registry): cr.execute( """ UPDATE account_move_line aml - SET sale_id = so.id - FROM sale_order_line so + SET sale_order_id = so.id + FROM sale_order so LEFT JOIN account_move_line aml2 - ON aml2.sale_id = so.id + ON aml2.sale_order_id = so.id WHERE aml2.move_id = aml.move_id """ ) @@ -66,7 +61,7 @@ def post_init_hook(cr, registry): update account_move_line aml set sale_line_id = sol.id FROM account_move_line aml2 INNER JOIN sale_order so ON - so.id = aml2.sale_id + so.id = aml2.sale_order_id INNER JOIN sale_order_line sol ON so.id = sol.order_id AND sol.product_id = aml2.product_id diff --git a/account_move_line_sale_info/migrations/13.0.1.0.0/pre-migration.py b/account_move_line_sale_info/migrations/13.0.1.0.0/pre-migration.py new file mode 100644 index 000000000..a3055dc5c --- /dev/null +++ b/account_move_line_sale_info/migrations/13.0.1.0.0/pre-migration.py @@ -0,0 +1,12 @@ +# Copyright 2020 ForgeFlow +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from openupgradelib import openupgrade + +_field_renames = [ + ("account.move.line", "account_move_line", "sale_id", "sale_order_id",), +] + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.rename_fields(env, _field_renames) diff --git a/account_move_line_sale_info/models/__init__.py b/account_move_line_sale_info/models/__init__.py index f4c1f16e6..c8dea955a 100644 --- a/account_move_line_sale_info/models/__init__.py +++ b/account_move_line_sale_info/models/__init__.py @@ -1,6 +1,5 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import account_move -from . import account_invoice from . import sale_order_line from . import stock_move diff --git a/account_move_line_sale_info/models/account_invoice.py b/account_move_line_sale_info/models/account_invoice.py deleted file mode 100644 index 73d8a555e..000000000 --- a/account_move_line_sale_info/models/account_invoice.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2020 ForgeFlow S.L. -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from odoo import api, models - - -class AccountInvoice(models.Model): - - _inherit = "account.invoice" - - @api.model - def invoice_line_move_line_get(self): - res = super(AccountInvoice, self).invoice_line_move_line_get() - - invoice_line_model = self.env["account.invoice.line"] - for move_line_dict in res: - if "invl_id" in move_line_dict: - line = invoice_line_model.browse(move_line_dict["invl_id"]) - if line.sale_line_ids and len(line.sale_line_ids) == 1: - move_line_dict["sale_line_id"] = line.sale_line_ids[0].id - return res - - @api.model - def line_get_convert(self, line, part): - res = super(AccountInvoice, self).line_get_convert(line, part) - if line.get("sale_line_id", False): - res["sale_line_id"] = line.get("sale_line_id") - return res - - @api.model - def _anglo_saxon_sale_move_lines(self, i_line): - """ - We need to add the sale_line to those entries too - """ - res = super()._anglo_saxon_sale_move_lines(i_line) - for aml in res: - if i_line.sale_line_ids and len(i_line.sale_line_ids) == 1: - aml["sale_line_id"] = i_line.sale_line_ids[0].id - return res diff --git a/account_move_line_sale_info/models/account_move.py b/account_move_line_sale_info/models/account_move.py index d84d53cc0..6e1bff1b1 100644 --- a/account_move_line_sale_info/models/account_move.py +++ b/account_move_line_sale_info/models/account_move.py @@ -4,6 +4,49 @@ from odoo import fields, models +class AccountMove(models.Model): + + _inherit = "account.move" + + def _prepare_interim_account_line_vals(self, line, move, debit_interim_account): + res = super()._prepare_interim_account_line_vals( + line, move, debit_interim_account + ) + if ( + not res.get("move_id", False) + or not res.get("product_id", False) + or not res.get("quantity", False) + ): + return res + am = self.env["account.move"].browse(res["move_id"]) + sale_line_id = am.invoice_line_ids.filtered( + lambda il: il.product_id.id == res["product_id"] + and il.quantity == res["quantity"] + ).mapped("sale_line_id") + if sale_line_id and len(sale_line_id) == 1: + res["sale_line_id"] = sale_line_id.id + return res + + def _prepare_expense_account_line_vals(self, line, move, debit_interim_account): + res = super()._prepare_expense_account_line_vals( + line, move, debit_interim_account + ) + if ( + not res.get("move_id", False) + or not res.get("product_id", False) + or not res.get("quantity", False) + ): + return res + am = self.env["account.move"].browse(res["move_id"]) + sale_line_id = am.invoice_line_ids.filtered( + lambda il: il.product_id.id == res["product_id"] + and il.quantity == res["quantity"] + ).mapped("sale_line_id") + if sale_line_id and len(sale_line_id) == 1: + res["sale_line_id"] = sale_line_id.id + return res + + class AccountMoveLine(models.Model): _inherit = "account.move.line" @@ -15,7 +58,7 @@ class AccountMoveLine(models.Model): index=True, ) - sale_id = fields.Many2one( + sale_order_id = fields.Many2one( comodel_name="sale.order", related="sale_line_id.order_id", string="Sales Order", diff --git a/account_move_line_sale_info/models/sale_order_line.py b/account_move_line_sale_info/models/sale_order_line.py index fc103cc44..75f7eeee9 100644 --- a/account_move_line_sale_info/models/sale_order_line.py +++ b/account_move_line_sale_info/models/sale_order_line.py @@ -1,19 +1,25 @@ # Copyright 2020 ForgeFlow S.L. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import api, models +from odoo import models class SaleOrderLine(models.Model): _inherit = "sale.order.line" - @api.multi def name_get(self): result = [] orig_name = dict(super(SaleOrderLine, self).name_get()) for line in self: name = orig_name[line.id] if self.env.context.get("so_line_info", False): - name = "[{}] {} ({})".format(line.order_id.name, name, line.order_id.state) + name = "[{}] {} - ({})".format( + line.order_id.name, line.product_id.name, line.order_id.state + ) result.append((line.id, name)) return result + + def _prepare_invoice_line(self): + res = super(SaleOrderLine, self)._prepare_invoice_line() + res["sale_line_id"] = self.id + return res diff --git a/account_move_line_sale_info/models/stock_move.py b/account_move_line_sale_info/models/stock_move.py index 07b1bb0b7..4f522e110 100644 --- a/account_move_line_sale_info/models/stock_move.py +++ b/account_move_line_sale_info/models/stock_move.py @@ -8,10 +8,10 @@ class StockMove(models.Model): @api.model def _prepare_account_move_line( - self, qty, cost, credit_account_id, debit_account_id + self, qty, cost, credit_account_id, debit_account_id, description ): res = super(StockMove, self)._prepare_account_move_line( - qty, cost, credit_account_id, debit_account_id + qty, cost, credit_account_id, debit_account_id, description ) for line in res: line[2]["sale_line_id"] = self.sale_line_id.id diff --git a/account_move_line_sale_info/security/account_security.xml b/account_move_line_sale_info/security/account_security.xml index bd3f7e6d5..ab6f92cc5 100644 --- a/account_move_line_sale_info/security/account_security.xml +++ b/account_move_line_sale_info/security/account_security.xml @@ -3,6 +3,6 @@ Sale info in Journal Items - + diff --git a/account_move_line_sale_info/tests/test_account_move_line_sale_info.py b/account_move_line_sale_info/tests/test_account_move_line_sale_info.py index 29f62d448..7d14e8cef 100644 --- a/account_move_line_sale_info/tests/test_account_move_line_sale_info.py +++ b/account_move_line_sale_info/tests/test_account_move_line_sale_info.py @@ -1,6 +1,5 @@ # Copyright 2020 ForgeFlow S.L. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import fields from odoo.tests import common @@ -9,8 +8,6 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): super(TestAccountMoveLineSaleInfo, self).setUp() self.sale_model = self.env["sale.order"] self.sale_line_model = self.env["sale.order.line"] - self.invoice_model = self.env["account.invoice"] - self.invoice_line_model = self.env["account.invoice.line"] self.product_model = self.env["product.product"] self.product_ctg_model = self.env["product.category"] self.acc_type_model = self.env["account.account.type"] @@ -58,6 +55,15 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): self.account_manager = self._create_user( "account_manager", [self.group_account_manager], self.company ) + self.JournalObj = self.env["account.journal"] + self.journal_sale = self.JournalObj.create( + { + "name": "Test journal sale", + "code": "TST-JRNL-S", + "type": "sale", + "company_id": self.company.id, + } + ) def _create_user(self, login, groups, company): """ Create a user.""" @@ -75,8 +81,10 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): ) return user.id - def _create_account_type(self, name, type): - acc_type = self.acc_type_model.create({"name": name, "type": type}) + def _create_account_type(self, name, atype): + acc_type = self.acc_type_model.create( + {"name": name, "type": atype, "internal_group": name} + ) return acc_type def _create_account(self, acc_type, name, code, company): @@ -124,10 +132,9 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): line_values = { "name": product.name, "product_id": product.id, - "product_qty": qty, + "product_uom_qty": qty, "product_uom": product.uom_id.id, "price_unit": 500, - "date_planned": fields.datetime.now(), } lines.append((0, 0, line_values)) return self.sale_model.create( @@ -168,55 +175,70 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): account move line and to the invoice line. """ sale = self._create_sale([(self.product, 1)]) - po_line = False + so_line = False for line in sale.order_line: - po_line = line + so_line = line break - sale.button_confirm() + sale.action_confirm() picking = sale.picking_ids[0] - picking.force_assign() picking.move_lines.write({"quantity_done": 1.0}) picking.button_validate() - expected_balance = 1.0 + expected_balance = -1.0 self._check_account_balance( self.account_inventory.id, - sale_line=po_line, + sale_line=so_line, expected_balance=expected_balance, ) - - invoice = self.invoice_model.create( - { - "partner_id": self.partner1.id, - "sale_id": sale.id, - "account_id": sale.partner_id.property_account_payable_id.id, - } + self.context = { + "active_model": "sale.order", + "active_ids": [sale.id], + "active_id": sale.id, + "default_journal_id": self.journal_sale.id, + } + payment = ( + self.env["sale.advance.payment.inv"] + .with_context(self.context) + .create({"advance_payment_method": "delivered"}) ) - invoice.sale_order_change() - invoice.action_invoice_open() + payment.create_invoices() + invoice = sale.invoice_ids[0] + invoice.post() - for aml in invoice.move_id.line_ids: - if aml.product_id == po_line.product_id and aml.invoice_id: + for aml in invoice.line_ids: + if aml.product_id == so_line.product_id and aml.move_id: self.assertEqual( aml.sale_line_id, - po_line, + so_line, "sale Order line has not been copied " "from the invoice to the account move line.", ) def test_name_get(self): sale = self._create_sale([(self.product, 1)]) - po_line = sale.order_line[0] - name_get = po_line.with_context({"po_line_info": True}).name_get() + so_line = sale.order_line[0] + name_get = so_line.with_context({"so_line_info": True}).name_get() self.assertEqual( name_get, [ ( - po_line.id, - "[%s] %s (%s)" - % (po_line.order_id.name, po_line.name, po_line.order_id.state,), + so_line.id, + "[%s] %s - (%s)" + % ( + so_line.order_id.name, + so_line.product_id.name, + so_line.order_id.state, + ), + ) + ], + ) + name_get_no_ctx = so_line.with_context({}).name_get() + self.assertEqual( + name_get_no_ctx, + [ + ( + so_line.id, + "{} - {}".format(so_line.order_id.name, so_line.product_id.name), ) ], ) - name_get_no_ctx = po_line.name_get() - self.assertEqual(name_get_no_ctx, [(po_line.id, po_line.name)]) diff --git a/account_move_line_sale_info/views/account_move_view.xml b/account_move_line_sale_info/views/account_move_view.xml index 47541062a..984543f79 100644 --- a/account_move_line_sale_info/views/account_move_view.xml +++ b/account_move_line_sale_info/views/account_move_view.xml @@ -7,7 +7,7 @@ - + - + @@ -80,7 +82,7 @@ groups="account_move_line_sale_info.group_account_move_sale_info" /> From 0a63a5f6b51fcc45eaf224703413bd489439b1ca Mon Sep 17 00:00:00 2001 From: oca-travis Date: Thu, 10 Jun 2021 16:24:54 +0000 Subject: [PATCH 06/14] [UPD] Update account_move_line_sale_info.pot --- .../i18n/account_move_line_sale_info.pot | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 account_move_line_sale_info/i18n/account_move_line_sale_info.pot diff --git a/account_move_line_sale_info/i18n/account_move_line_sale_info.pot b/account_move_line_sale_info/i18n/account_move_line_sale_info.pot new file mode 100644 index 000000000..a768fbe16 --- /dev/null +++ b/account_move_line_sale_info/i18n/account_move_line_sale_info.pot @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_move_line_sale_info +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: account_move_line_sale_info +#: model:ir.model,name:account_move_line_sale_info.model_account_move +msgid "Journal Entries" +msgstr "" + +#. module: account_move_line_sale_info +#: model:ir.model,name:account_move_line_sale_info.model_account_move_line +msgid "Journal Item" +msgstr "" + +#. module: account_move_line_sale_info +#: model:ir.model.fields,field_description:account_move_line_sale_info.field_account_move_line__sale_line_id +msgid "Sale Order Line" +msgstr "" + +#. module: account_move_line_sale_info +#: model:res.groups,name:account_move_line_sale_info.group_account_move_sale_info +msgid "Sale info in Journal Items" +msgstr "" + +#. module: account_move_line_sale_info +#: model:ir.model.fields,field_description:account_move_line_sale_info.field_account_move_line__sale_order_id +msgid "Sales Order" +msgstr "" + +#. module: account_move_line_sale_info +#: model:ir.model,name:account_move_line_sale_info.model_sale_order_line +msgid "Sales Order Line" +msgstr "" + +#. module: account_move_line_sale_info +#: model:ir.model,name:account_move_line_sale_info.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: account_move_line_sale_info +#: model_terms:ir.ui.view,arch_db:account_move_line_sale_info.view_account_move_line_filter +msgid "sale Order" +msgstr "" + +#. module: account_move_line_sale_info +#: model_terms:ir.ui.view,arch_db:account_move_line_sale_info.view_account_move_line_filter +msgid "sale Order Line" +msgstr "" From 73d8641716dcd50e1ba56768860fd31800caa1e2 Mon Sep 17 00:00:00 2001 From: AaronHForgeFlow Date: Tue, 10 Aug 2021 10:21:16 +0200 Subject: [PATCH 07/14] [IMP] account_move_line_sale_info optional fields in tree view --- account_move_line_sale_info/views/account_move_view.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/account_move_line_sale_info/views/account_move_view.xml b/account_move_line_sale_info/views/account_move_view.xml index 984543f79..260b3783d 100644 --- a/account_move_line_sale_info/views/account_move_view.xml +++ b/account_move_line_sale_info/views/account_move_view.xml @@ -25,10 +25,12 @@ @@ -52,14 +54,14 @@ Date: Tue, 17 Aug 2021 10:54:42 +0000 Subject: [PATCH 08/14] [UPD] Update account_move_line_sale_info.pot --- .../i18n/account_move_line_sale_info.pot | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/account_move_line_sale_info/i18n/account_move_line_sale_info.pot b/account_move_line_sale_info/i18n/account_move_line_sale_info.pot index a768fbe16..2136039ef 100644 --- a/account_move_line_sale_info/i18n/account_move_line_sale_info.pot +++ b/account_move_line_sale_info/i18n/account_move_line_sale_info.pot @@ -23,8 +23,14 @@ msgstr "" msgid "Journal Item" msgstr "" +#. module: account_move_line_sale_info +#: model_terms:ir.ui.view,arch_db:account_move_line_sale_info.view_account_move_line_filter +msgid "Sale Order" +msgstr "" + #. module: account_move_line_sale_info #: model:ir.model.fields,field_description:account_move_line_sale_info.field_account_move_line__sale_line_id +#: model_terms:ir.ui.view,arch_db:account_move_line_sale_info.view_account_move_line_filter msgid "Sale Order Line" msgstr "" @@ -47,13 +53,3 @@ msgstr "" #: model:ir.model,name:account_move_line_sale_info.model_stock_move msgid "Stock Move" msgstr "" - -#. module: account_move_line_sale_info -#: model_terms:ir.ui.view,arch_db:account_move_line_sale_info.view_account_move_line_filter -msgid "sale Order" -msgstr "" - -#. module: account_move_line_sale_info -#: model_terms:ir.ui.view,arch_db:account_move_line_sale_info.view_account_move_line_filter -msgid "sale Order Line" -msgstr "" From 7610f0d397947196f9ab83846d0bd851f5a03a47 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 17 Aug 2021 11:19:29 +0000 Subject: [PATCH 09/14] account_move_line_sale_info 13.0.1.0.1 --- account_move_line_sale_info/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_move_line_sale_info/__manifest__.py b/account_move_line_sale_info/__manifest__.py index c9604302b..ccd600b36 100644 --- a/account_move_line_sale_info/__manifest__.py +++ b/account_move_line_sale_info/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Account Move Line Sale Info", "summary": "Introduces the purchase order line to the journal items", - "version": "13.0.1.0.0", + "version": "13.0.1.0.1", "author": "ForgeFlow S.L., " "Odoo Community Association (OCA)", "website": "http://www.github.com/OCA/account-financial-tools", "category": "Generic", From daae7547e032b34850f1245e3cebf1f93d502ea9 Mon Sep 17 00:00:00 2001 From: AaronHForgeFlow Date: Tue, 5 Oct 2021 09:41:58 +0200 Subject: [PATCH 10/14] [FIX] account_move_line_sale_info: do not copy sale references --- account_move_line_sale_info/models/account_move.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/account_move_line_sale_info/models/account_move.py b/account_move_line_sale_info/models/account_move.py index 6e1bff1b1..27710ab78 100644 --- a/account_move_line_sale_info/models/account_move.py +++ b/account_move_line_sale_info/models/account_move.py @@ -56,6 +56,7 @@ class AccountMoveLine(models.Model): string="Sale Order Line", ondelete="set null", index=True, + copy=False, ) sale_order_id = fields.Many2one( @@ -65,4 +66,5 @@ class AccountMoveLine(models.Model): ondelete="set null", store=True, index=True, + copy=False, ) From 4e6fde7f1147c1d1d4709e3c3600913b21d93860 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 6 Oct 2021 07:17:04 +0000 Subject: [PATCH 11/14] account_move_line_sale_info 13.0.1.0.2 --- account_move_line_sale_info/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_move_line_sale_info/__manifest__.py b/account_move_line_sale_info/__manifest__.py index ccd600b36..d5cadd332 100644 --- a/account_move_line_sale_info/__manifest__.py +++ b/account_move_line_sale_info/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Account Move Line Sale Info", "summary": "Introduces the purchase order line to the journal items", - "version": "13.0.1.0.1", + "version": "13.0.1.0.2", "author": "ForgeFlow S.L., " "Odoo Community Association (OCA)", "website": "http://www.github.com/OCA/account-financial-tools", "category": "Generic", From d4c9a436e0b82c789178941055859a678d0f2de8 Mon Sep 17 00:00:00 2001 From: AaronHForgeFlow Date: Tue, 26 Oct 2021 13:27:46 +0200 Subject: [PATCH 12/14] [IMP] account_move_sale_info: make sale fields optional in journal entries --- account_move_line_sale_info/views/account_move_view.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/account_move_line_sale_info/views/account_move_view.xml b/account_move_line_sale_info/views/account_move_view.xml index 260b3783d..93a240070 100644 --- a/account_move_line_sale_info/views/account_move_view.xml +++ b/account_move_line_sale_info/views/account_move_view.xml @@ -81,10 +81,12 @@ From 75457611ca48afb73c1b5f09208e46de7c87a8d2 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 28 Oct 2021 09:17:04 +0000 Subject: [PATCH 13/14] account_move_line_sale_info 13.0.1.0.3 --- account_move_line_sale_info/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_move_line_sale_info/__manifest__.py b/account_move_line_sale_info/__manifest__.py index d5cadd332..3c07ce3b0 100644 --- a/account_move_line_sale_info/__manifest__.py +++ b/account_move_line_sale_info/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Account Move Line Sale Info", "summary": "Introduces the purchase order line to the journal items", - "version": "13.0.1.0.2", + "version": "13.0.1.0.3", "author": "ForgeFlow S.L., " "Odoo Community Association (OCA)", "website": "http://www.github.com/OCA/account-financial-tools", "category": "Generic", From a1d1f9f6922a3fba8e58fae4c49b2c29b8076b14 Mon Sep 17 00:00:00 2001 From: Joan Mateu Jordi Date: Tue, 9 Nov 2021 14:38:50 +0100 Subject: [PATCH 14/14] [15.0][MIG] account_move_line_sale_info: Migration to 15.0 --- account_move_line_sale_info/README.rst | 1 + account_move_line_sale_info/__manifest__.py | 5 +- account_move_line_sale_info/hooks.py | 2 +- .../migrations/13.0.1.0.0/pre-migration.py | 12 ----- .../models/account_move.py | 53 ++++++------------- .../models/sale_order_line.py | 4 +- .../readme/CONFIGURE.rst | 0 .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 4 ++ account_move_line_sale_info/readme/USAGE.rst | 7 +++ .../tests/test_account_move_line_sale_info.py | 12 ++--- .../odoo/addons/account_move_line_sale_info | 1 + setup/account_move_line_sale_info/setup.py | 6 +++ 13 files changed, 47 insertions(+), 61 deletions(-) delete mode 100644 account_move_line_sale_info/migrations/13.0.1.0.0/pre-migration.py create mode 100644 account_move_line_sale_info/readme/CONFIGURE.rst create mode 100644 account_move_line_sale_info/readme/CONTRIBUTORS.rst create mode 100644 account_move_line_sale_info/readme/DESCRIPTION.rst create mode 100644 account_move_line_sale_info/readme/USAGE.rst create mode 120000 setup/account_move_line_sale_info/odoo/addons/account_move_line_sale_info create mode 100644 setup/account_move_line_sale_info/setup.py diff --git a/account_move_line_sale_info/README.rst b/account_move_line_sale_info/README.rst index bd79bd9d8..649642037 100644 --- a/account_move_line_sale_info/README.rst +++ b/account_move_line_sale_info/README.rst @@ -44,6 +44,7 @@ Images * Odoo Community Association: `Icon `_. + Contributors ------------ diff --git a/account_move_line_sale_info/__manifest__.py b/account_move_line_sale_info/__manifest__.py index 3c07ce3b0..a30d046c9 100644 --- a/account_move_line_sale_info/__manifest__.py +++ b/account_move_line_sale_info/__manifest__.py @@ -4,14 +4,13 @@ { "name": "Account Move Line Sale Info", "summary": "Introduces the purchase order line to the journal items", - "version": "13.0.1.0.3", + "version": "15.0.1.0.0", "author": "ForgeFlow S.L., " "Odoo Community Association (OCA)", - "website": "http://www.github.com/OCA/account-financial-tools", + "website": "https://github.com/OCA/account-financial-tools", "category": "Generic", "depends": [ "account_move_line_stock_info", "sale_stock", - "stock_account_prepare_anglo_saxon_out_lines_hook", ], "license": "AGPL-3", "data": ["security/account_security.xml", "views/account_move_view.xml"], diff --git a/account_move_line_sale_info/hooks.py b/account_move_line_sale_info/hooks.py index 46c9f0eb8..cd6be3739 100644 --- a/account_move_line_sale_info/hooks.py +++ b/account_move_line_sale_info/hooks.py @@ -4,7 +4,7 @@ def post_init_hook(cr, registry): - """ INIT sale references in acount move line """ + """INIT sale references in account move line""" # FOR stock moves cr.execute( """ diff --git a/account_move_line_sale_info/migrations/13.0.1.0.0/pre-migration.py b/account_move_line_sale_info/migrations/13.0.1.0.0/pre-migration.py deleted file mode 100644 index a3055dc5c..000000000 --- a/account_move_line_sale_info/migrations/13.0.1.0.0/pre-migration.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2020 ForgeFlow -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openupgradelib import openupgrade - -_field_renames = [ - ("account.move.line", "account_move_line", "sale_id", "sale_order_id",), -] - - -@openupgrade.migrate() -def migrate(env, version): - openupgrade.rename_fields(env, _field_renames) diff --git a/account_move_line_sale_info/models/account_move.py b/account_move_line_sale_info/models/account_move.py index 27710ab78..6e8cf8146 100644 --- a/account_move_line_sale_info/models/account_move.py +++ b/account_move_line_sale_info/models/account_move.py @@ -5,45 +5,24 @@ from odoo import fields, models class AccountMove(models.Model): - _inherit = "account.move" - def _prepare_interim_account_line_vals(self, line, move, debit_interim_account): - res = super()._prepare_interim_account_line_vals( - line, move, debit_interim_account - ) - if ( - not res.get("move_id", False) - or not res.get("product_id", False) - or not res.get("quantity", False) - ): - return res - am = self.env["account.move"].browse(res["move_id"]) - sale_line_id = am.invoice_line_ids.filtered( - lambda il: il.product_id.id == res["product_id"] - and il.quantity == res["quantity"] - ).mapped("sale_line_id") - if sale_line_id and len(sale_line_id) == 1: - res["sale_line_id"] = sale_line_id.id - return res - - def _prepare_expense_account_line_vals(self, line, move, debit_interim_account): - res = super()._prepare_expense_account_line_vals( - line, move, debit_interim_account - ) - if ( - not res.get("move_id", False) - or not res.get("product_id", False) - or not res.get("quantity", False) - ): - return res - am = self.env["account.move"].browse(res["move_id"]) - sale_line_id = am.invoice_line_ids.filtered( - lambda il: il.product_id.id == res["product_id"] - and il.quantity == res["quantity"] - ).mapped("sale_line_id") - if sale_line_id and len(sale_line_id) == 1: - res["sale_line_id"] = sale_line_id.id + def _stock_account_prepare_anglo_saxon_out_lines_vals(self): + res = super()._stock_account_prepare_anglo_saxon_out_lines_vals() + for i, vals in enumerate(res): + if ( + not vals.get("move_id", False) + or not vals.get("product_id", False) + or not vals.get("quantity", False) + ): + continue + am = self.env["account.move"].browse(vals["move_id"]) + sale_line_id = am.invoice_line_ids.filtered( + lambda il: il.product_id.id == vals["product_id"] + and il.quantity == vals["quantity"] + ).mapped("sale_line_id") + if sale_line_id and len(sale_line_id) == 1: + res[i]["sale_line_id"] = sale_line_id.id return res diff --git a/account_move_line_sale_info/models/sale_order_line.py b/account_move_line_sale_info/models/sale_order_line.py index 75f7eeee9..a46eeac9b 100644 --- a/account_move_line_sale_info/models/sale_order_line.py +++ b/account_move_line_sale_info/models/sale_order_line.py @@ -19,7 +19,7 @@ class SaleOrderLine(models.Model): result.append((line.id, name)) return result - def _prepare_invoice_line(self): - res = super(SaleOrderLine, self)._prepare_invoice_line() + def _prepare_invoice_line(self, **optional_values): + res = super()._prepare_invoice_line(**optional_values) res["sale_line_id"] = self.id return res diff --git a/account_move_line_sale_info/readme/CONFIGURE.rst b/account_move_line_sale_info/readme/CONFIGURE.rst new file mode 100644 index 000000000..e69de29bb diff --git a/account_move_line_sale_info/readme/CONTRIBUTORS.rst b/account_move_line_sale_info/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..2e524967e --- /dev/null +++ b/account_move_line_sale_info/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Aaron Henriquez diff --git a/account_move_line_sale_info/readme/DESCRIPTION.rst b/account_move_line_sale_info/readme/DESCRIPTION.rst new file mode 100644 index 000000000..9d1624060 --- /dev/null +++ b/account_move_line_sale_info/readme/DESCRIPTION.rst @@ -0,0 +1,4 @@ +This module will add the sale order line to journal items. + +The ultimate goal is to establish the purchase order line as one of the key +fields to reconcile the Goods Delivered Not Invoiced accrual account. diff --git a/account_move_line_sale_info/readme/USAGE.rst b/account_move_line_sale_info/readme/USAGE.rst new file mode 100644 index 000000000..d75919ab3 --- /dev/null +++ b/account_move_line_sale_info/readme/USAGE.rst @@ -0,0 +1,7 @@ +The sale order line will be automatically copied to the journal items. + +* When a supplier invoice is created referencing sales orders, the + sale order line will be copied to the corresponding journal item. + +* When a stock move is validated and generates a journal entry, the sale + order line is copied to the account move line. diff --git a/account_move_line_sale_info/tests/test_account_move_line_sale_info.py b/account_move_line_sale_info/tests/test_account_move_line_sale_info.py index 7d14e8cef..f5df5d89d 100644 --- a/account_move_line_sale_info/tests/test_account_move_line_sale_info.py +++ b/account_move_line_sale_info/tests/test_account_move_line_sale_info.py @@ -66,9 +66,9 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): ) def _create_user(self, login, groups, company): - """ Create a user.""" + """Create a user.""" group_ids = [group.id for group in groups] - user = self.res_users_model.with_context({"no_reset_password": True}).create( + user = self.res_users_model.with_context(**{"no_reset_password": True}).create( { "name": "Test User", "login": login, @@ -123,7 +123,7 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): return product def _create_sale(self, line_products): - """ Create a sale order. + """Create a sale order. ``line_products`` is a list of tuple [(product, qty)] """ @@ -198,7 +198,7 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): } payment = ( self.env["sale.advance.payment.inv"] - .with_context(self.context) + .with_context(**self.context) .create({"advance_payment_method": "delivered"}) ) payment.create_invoices() @@ -217,7 +217,7 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): def test_name_get(self): sale = self._create_sale([(self.product, 1)]) so_line = sale.order_line[0] - name_get = so_line.with_context({"so_line_info": True}).name_get() + name_get = so_line.with_context(**{"so_line_info": True}).name_get() self.assertEqual( name_get, [ @@ -232,7 +232,7 @@ class TestAccountMoveLineSaleInfo(common.TransactionCase): ) ], ) - name_get_no_ctx = so_line.with_context({}).name_get() + name_get_no_ctx = so_line.with_context(**{}).name_get() self.assertEqual( name_get_no_ctx, [ diff --git a/setup/account_move_line_sale_info/odoo/addons/account_move_line_sale_info b/setup/account_move_line_sale_info/odoo/addons/account_move_line_sale_info new file mode 120000 index 000000000..2cd130062 --- /dev/null +++ b/setup/account_move_line_sale_info/odoo/addons/account_move_line_sale_info @@ -0,0 +1 @@ +../../../../account_move_line_sale_info \ No newline at end of file diff --git a/setup/account_move_line_sale_info/setup.py b/setup/account_move_line_sale_info/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/account_move_line_sale_info/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)