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