From 63419fc6eb372c7a597e0c7771df9179755d4ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 4 Oct 2018 09:06:38 +0200 Subject: [PATCH] Manage move from supplier to customer The stock move creates two invoices in this case. And it uses the origin of the invoice lines to manage them from the move workflow. issue7523 review70381002 --- CHANGELOG | 2 + stock.py | 39 ++-- ...io_stock_consignment_supplier_customer.rst | 175 ++++++++++++++++++ tests/test_stock_consignment.py | 5 + 4 files changed, 205 insertions(+), 16 deletions(-) create mode 100644 tests/scenario_stock_consignment_supplier_customer.rst diff --git a/CHANGELOG b/CHANGELOG index a377806a87..ade298a0bc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Manage move from supplier to customer + Version 5.0.0 - 2018-10-01 * Bug fixes (see mercurial logs for details) * Remove support for Python 2.7 diff --git a/stock.py b/stock.py index a47fc10d21..f5e5809cc6 100644 --- a/stock.py +++ b/stock.py @@ -48,17 +48,19 @@ def set_origin_consignment(func): def wrapper(cls, moves): pool = Pool() InvoiceLine = pool.get('account.invoice.line') - lines = {} + to_save = [] + move2line = {} for move in moves: if not move.origin: - line = move.get_invoice_line_consignment() - if line: - lines[move] = line - if lines: - InvoiceLine.save(list(lines.values())) - for move, line in lines.items(): + lines = move.get_invoice_lines_consignment() + if lines: + to_save.extend(lines) + move2line[move] = lines[0] + if to_save: + InvoiceLine.save(to_save) + for move, line in move2line.items(): move.origin = line - cls.save(list(lines.keys())) + cls.save(list(move2line.keys())) return func(cls, moves) return wrapper @@ -70,9 +72,8 @@ def unset_origin_consignment(func): InvoiceLine = pool.get('account.invoice.line') lines, to_save = [], [] for move in moves: - if (isinstance(move.origin, InvoiceLine) - and move.origin.origin == move): - lines.append(move.origin) + if isinstance(move.origin, InvoiceLine): + lines.extend(move.invoice_lines) move.origin = None to_save.append(move) if lines: @@ -85,6 +86,9 @@ def unset_origin_consignment(func): class Move(metaclass=PoolMeta): __name__ = 'stock.move' + invoice_lines = fields.One2Many( + 'account.invoice.line', 'origin', "Invoice Lines", readonly=True) + @classmethod def _get_origin(cls): return super(Move, cls)._get_origin() + ['account.invoice.line'] @@ -102,15 +106,18 @@ class Move(metaclass=PoolMeta): def _get_tax_rule_pattern(self): return {} - def get_invoice_line_consignment(self): + def get_invoice_lines_consignment(self): + lines = [] if (self.from_location.type == 'supplier' - and self.to_location.type in {'storage', 'production'} + and self.to_location.type in { + 'storage', 'production', 'customer'} and self.from_location.consignment_party): - return self._get_supplier_invoice_line_consignment() - elif (self.from_location.type in {'storage', 'production'} + lines.append(self._get_supplier_invoice_line_consignment()) + if (self.from_location.type in {'storage', 'production', 'supplier'} and self.to_location.type == 'customer' and self.from_location.consignment_party): - return self._get_customer_invoice_line_consignment() + lines.append(self._get_customer_invoice_line_consignment()) + return lines def _get_supplier_invoice_line_consignment(self): pool = Pool() diff --git a/tests/scenario_stock_consignment_supplier_customer.rst b/tests/scenario_stock_consignment_supplier_customer.rst new file mode 100644 index 0000000000..8c931f8ea3 --- /dev/null +++ b/tests/scenario_stock_consignment_supplier_customer.rst @@ -0,0 +1,175 @@ +=============================================== +Stock Consignment Supplier to Customer Scenario +=============================================== + +Imports:: + + >>> from decimal import Decimal + + >>> from proteus import Model, Wizard + >>> from trytond.tests.tools import activate_modules + >>> from trytond.modules.company.tests.tools import create_company, \ + ... get_company + >>> from trytond.modules.account.tests.tools import create_chart, \ + ... get_accounts, create_tax + +Install stock_consignment:: + + >>> config = activate_modules('stock_consignment') + +Create company:: + + >>> _ = create_company() + >>> company = get_company() + +Create chart of accounts:: + + >>> _ = create_chart(company) + >>> accounts = get_accounts(company) + >>> revenue = accounts['revenue'] + >>> expense = accounts['expense'] + +Create parties:: + + >>> Party = Model.get('party.party') + >>> supplier = Party(name='Supplier') + >>> supplier.save() + >>> customer = Party(name='Customer') + >>> customer.save() + +Get stock locations:: + + >>> Location = Model.get('stock.location') + >>> warehouse_loc, = Location.find([('code', '=', 'WH')]) + >>> supplier_loc, = Location.find([('code', '=', 'SUP')]) + >>> storage_loc, = Location.find([('code', '=', 'STO')]) + >>> customer_loc, = Location.find([('code', '=', 'CUS')]) + >>> output_loc, = Location.find([('code', '=', 'OUT')]) + +Create customer consignment location:: + + >>> customer_consignment_loc = Location() + >>> customer_consignment_loc.name = "Customer Consignment" + >>> customer_consignment_loc.type = 'storage' + >>> customer_consignment_loc.parent = customer_loc + >>> customer_consignment_loc.consignment_party = customer + >>> customer_consignment_loc.save() + +Create supplier consignment location under customer consignment location:: + + >>> supplier_consignment_loc = Location() + >>> supplier_consignment_loc.name = "Supplier Consignment" + >>> supplier_consignment_loc.type = 'supplier' + >>> supplier_consignment_loc.parent = customer_consignment_loc + >>> supplier_consignment_loc.consignment_party = supplier + >>> supplier_consignment_loc.save() + +Create account category:: + + >>> ProductCategory = Model.get('product.category') + >>> account_category = ProductCategory(name="Account Category") + >>> account_category.accounting = True + >>> account_category.account_expense = expense + >>> account_category.account_revenue = revenue + >>> account_category.save() + +Create product:: + + >>> ProductUom = Model.get('product.uom') + >>> unit, = ProductUom.find([('name', '=', 'Unit')]) + >>> ProductTemplate = Model.get('product.template') + >>> Product = Model.get('product.product') + >>> product = Product() + >>> template = ProductTemplate() + >>> template.name = 'product' + >>> template.default_uom = unit + >>> template.type = 'goods' + >>> template.purchasable = True + >>> template.salable = True + >>> template.list_price = Decimal('10') + >>> template.account_category = account_category + >>> product_supplier = template.product_suppliers.new() + >>> product_supplier.party = supplier + >>> price = product_supplier.prices.new() + >>> price.quantity = 1 + >>> price.unit_price = Decimal('4') + >>> template.save() + >>> product.template = template + >>> product.save() + +Fill supplier consignment location:: + + >>> Shipment = Model.get('stock.shipment.internal') + >>> shipment = Shipment() + >>> shipment.from_location = supplier_loc + >>> shipment.to_location = supplier_consignment_loc + >>> move = shipment.moves.new() + >>> move.product = product + >>> move.quantity = 10 + >>> move.from_location = supplier_loc + >>> move.to_location = supplier_consignment_loc + >>> shipment.click('wait') + >>> shipment.state + 'waiting' + >>> shipment.click('assign_try') + True + >>> shipment.state + 'assigned' + >>> shipment.click('done') + >>> shipment.state + 'done' + +Use supplier consignment stock by customer:: + + >>> shipment = Shipment() + >>> shipment.from_location = supplier_consignment_loc + >>> shipment.to_location = customer_loc + >>> move = shipment.moves.new() + >>> move.product = product + >>> move.quantity = 4 + >>> move.from_location = supplier_consignment_loc + >>> move.to_location = customer_loc + >>> shipment.click('wait') + >>> shipment.state + 'waiting' + >>> shipment.click('assign_try') + True + >>> shipment.state + 'assigned' + >>> shipment.click('done') + >>> shipment.state + 'done' + >>> move, = shipment.moves + >>> len(move.invoice_lines) + 2 + >>> move.origin in move.invoice_lines + True + +Check supplier invoice line:: + + >>> InvoiceLine = Model.get('account.invoice.line') + >>> invoice_line, = InvoiceLine.find([('invoice_type', '=', 'in')]) + >>> invoice_line.product == product + True + >>> invoice_line.quantity + 4.0 + >>> invoice_line.unit == unit + True + >>> invoice_line.unit_price + Decimal('4.0000') + >>> invoice_line.origin == move + True + +Check customer invoice line:: + + >>> invoice_line, = InvoiceLine.find([('invoice_type', '=', 'out')]) + >>> invoice_line.product == product + True + >>> invoice_line.quantity + 4.0 + >>> invoice_line.unit == unit + True + >>> invoice_line.unit_price + Decimal('10.0000') + >>> invoice_line.origin == move + True diff --git a/tests/test_stock_consignment.py b/tests/test_stock_consignment.py index 8595a410b2..bbb0fe81de 100644 --- a/tests/test_stock_consignment.py +++ b/tests/test_stock_consignment.py @@ -25,4 +25,9 @@ def suite(): tearDown=doctest_teardown, encoding='utf-8', checker=doctest_checker, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) + suite.addTests(doctest.DocFileSuite( + 'scenario_stock_consignment_supplier_customer.rst', + tearDown=doctest_teardown, encoding='utf-8', + checker=doctest_checker, + optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) return suite