From db5b6217343a2361fa285c8cd1eb0e6822d418e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 14 Apr 2020 09:27:53 +0200 Subject: [PATCH] Add actual quantity on line The actual quantity is computed once the order is processing. We take the biggest quantity between the shipped and invoiced and ignore the cancelled. issue9191 review319091002 --- CHANGELOG | 1 + purchase.py | 35 +++++++++++++++++++++++++++++++++++ tests/scenario_purchase.rst | 5 +++++ 3 files changed, 41 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index b0e7cfa676..a7e9735eb7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add actual quantity on line * Add return wizard * Add employee on purchases for some states * Add contact and invoice party on purchase diff --git a/purchase.py b/purchase.py index 77aed90d60..5f3d071945 100644 --- a/purchase.py +++ b/purchase.py @@ -909,6 +909,9 @@ class Purchase( @classmethod @ModelView.button def process(cls, purchases): + pool = Pool() + Line = pool.get('purchase.line') + lines = [] process, done = [], [] cls.lock(purchases) for purchase in purchases: @@ -921,6 +924,11 @@ class Purchase( if return_moves: purchase.create_return_shipment(return_moves) purchase.set_shipment_state() + + for line in purchase.lines: + line.set_actual_quantity() + lines.append(line) + if purchase.is_done(): if purchase.state != 'done': if purchase.state == 'confirmed': @@ -928,6 +936,7 @@ class Purchase( done.append(purchase) elif purchase.state != 'processing': process.append(purchase) + Line.save(lines) if process: cls.proceed(process) if done: @@ -987,6 +996,12 @@ class Line(sequence_ordered(), ModelSQL, ModelView): 'readonly': Eval('purchase_state') != 'draft', }, depends=['unit_digits', 'type', 'purchase_state']) + actual_quantity = fields.Float( + "Actual Quantity", digits=(16, Eval('unit_digits', 2)), readonly=True, + states={ + 'invisible': Eval('type') != 'line', + }, + depends=['unit_digits', 'type']) unit = fields.Many2One('product.uom', 'Unit', ondelete='RESTRICT', states={ @@ -1600,6 +1615,26 @@ class Line(sequence_ordered(), ModelSQL, ModelView): else: return [l for l in self.invoice_lines if not l.stock_moves] + def set_actual_quantity(self): + pool = Pool() + Uom = pool.get('product.uom') + if self.type != 'line': + return + moved_quantity = 0 + for move in self.moves: + if move.state != 'cancel': + moved_quantity += Uom.compute_qty( + move.uom, move.quantity, self.unit) + if self.quantity < 0: + moved_quantity *= -1 + invoiced_quantity = 0 + for invoice_line in self.invoice_lines: + if (not invoice_line.invoice + or invoice_line.invoice.state != 'cancel'): + invoiced_quantity += Uom.compute_qty( + invoice_line.unit, invoice_line.quantity, self.unit) + self.actual_quantity = max(moved_quantity, invoiced_quantity, key=abs) + def get_rec_name(self, name): pool = Pool() Lang = pool.get('ir.lang') diff --git a/tests/scenario_purchase.rst b/tests/scenario_purchase.rst index 576d326181..66921cf43a 100644 --- a/tests/scenario_purchase.rst +++ b/tests/scenario_purchase.rst @@ -204,6 +204,11 @@ Invoice line must be linked to stock move:: >>> stock_move2.invoice_lines == [invoice_line2] True +Check actual quantity:: + + >>> all(l.quantity == l.actual_quantity for l in purchase.lines) + True + Post invoice and check no new invoices:: >>> invoice.invoice_date = today