changeset 6cbeb76e5c32 in modules/sale:default
details: https://hg.tryton.org/modules/sale?cmd=changeset;node=6cbeb76e5c32
description:
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
diffstat:
CHANGELOG | 1 +
sale.py | 35 +++++++++++++++++++++++++++++++++++
sale_reporting.py | 4 +++-
tests/scenario_sale.rst | 5 +++++
4 files changed, 44 insertions(+), 1 deletions(-)
diffs (118 lines):
diff -r 8530391cc764 -r 6cbeb76e5c32 CHANGELOG
--- a/CHANGELOG Mon Apr 13 17:25:02 2020 +0200
+++ b/CHANGELOG Tue Apr 14 09:27:53 2020 +0200
@@ -1,3 +1,4 @@
+* Add actual quantity on line
* Add employee on sales for some states
* Add contact and invoice party on sale
* Add salable products menu entry
diff -r 8530391cc764 -r 6cbeb76e5c32 sale.py
--- a/sale.py Mon Apr 13 17:25:02 2020 +0200
+++ b/sale.py Tue Apr 14 09:27:53 2020 +0200
@@ -946,6 +946,9 @@
@classmethod
@ModelView.button
def process(cls, sales):
+ pool = Pool()
+ Line = pool.get('sale.line')
+ lines = []
done = []
process = []
cls.lock(sales)
@@ -957,6 +960,11 @@
sale.create_shipment('out')
sale.create_shipment('return')
sale.set_shipment_state()
+
+ for line in sale.lines:
+ line.set_actual_quantity()
+ lines.append(line)
+
if sale.is_done():
if sale.state != 'done':
if sale.state == 'confirmed':
@@ -964,6 +972,7 @@
done.append(sale)
elif sale.state != 'processing':
process.append(sale)
+ Line.save(lines)
if process:
cls.proceed(process)
if done:
@@ -1023,6 +1032,12 @@
'readonly': Eval('sale_state') != 'draft',
},
depends=['type', 'unit_digits', 'sale_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={
'required': Bool(Eval('product')),
@@ -1563,6 +1578,26 @@
invoice_lines.append(invoice_line)
return invoice_lines
+ 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 -r 8530391cc764 -r 6cbeb76e5c32 sale_reporting.py
--- a/sale_reporting.py Mon Apr 13 17:25:02 2020 +0200
+++ b/sale_reporting.py Tue Apr 14 09:27:53 2020 +0200
@@ -10,6 +10,7 @@
from dateutil.relativedelta import relativedelta
from sql import Null, Literal, Column
from sql.aggregate import Sum, Max, Min, Count
+from sql.conditionals import Coalesce
from sql.functions import CurrentTimestamp, DateTrunc, Power, Ceil, Log
from trytond.pool import Pool
@@ -95,8 +96,9 @@
currency_company = tables['currency_company']
currency_sale = tables['currency_sale']
+ quantity = Coalesce(line.actual_quantity, line.quantity)
revenue = cls.revenue.sql_cast(
- Sum(line.quantity * line.unit_price
+ Sum(quantity * line.unit_price
* currency_company.rate / currency_sale.rate))
return [
cls._column_id(tables).as_('id'),
diff -r 8530391cc764 -r 6cbeb76e5c32 tests/scenario_sale.rst
--- a/tests/scenario_sale.rst Mon Apr 13 17:25:02 2020 +0200
+++ b/tests/scenario_sale.rst Tue Apr 14 09:27:53 2020 +0200
@@ -200,6 +200,11 @@
>>> stock_move2.invoice_lines == [invoice_line2]
True
+Check actual quantity::
+
+ >>> all(l.quantity == l.actual_quantity for l in sale.lines)
+ True
+
Post invoice and check no new invoices::