Compute remaining quantity of sale and purchase order following their method

Ignored quantities must be deduced from the remaining quantity.

Closes #12698
This commit is contained in:
Cédric Krier
2023-12-17 17:07:41 +01:00
parent d3264484c4
commit 61e1b18e76
6 changed files with 326 additions and 0 deletions
+17
View File
@@ -132,6 +132,23 @@ class LineComponent(
invoice_line.quantity, self.line.unit)
return self.quantity * quantity / self.line.quantity
@property
def _move_remaining_quantity(self):
pool = Pool()
UoM = pool.get('product.uom')
quantity = super()._move_remaining_quantity
if self.line.sale.shipment_method == 'invoice':
invoices_ignored = set(self.line.sale.invoices_ignored)
ratio = self.get_moved_ratio()
if ratio:
for invoice_line in self.line.invoice_lines:
if invoice_line.invoice in invoices_ignored:
quantity -= UoM.compute_qty(
invoice_line.unit * self.line.unit,
invoice_line.quantity / ratio,
self.line.unit)
return quantity
def check_move_quantity(self):
from trytond.modules.sale.exceptions import SaleMoveQuantity
pool = Pool()
+6
View File
@@ -1330,6 +1330,12 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
return
skips = set(self.purchase.invoices_ignored)
quantity = self.quantity
if self.purchase.invoice_method == 'shipment':
moves_ignored = set(self.moves_ignored)
for move in self.moves:
if move in moves_ignored:
quantity -= UoM.compute_qty(
move.unit, move.quantity, self.unit)
for invoice_line in self.invoice_lines:
if invoice_line.type != 'line':
continue
@@ -0,0 +1,96 @@
============================================
Purchase Line Cancelled On Shipment Scenario
============================================
Imports::
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.tests.tools import activate_modules
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.account.tests.tools import (
... create_chart, get_accounts)
Activate modules::
>>> config = activate_modules('purchase')
>>> Party = Model.get('party.party')
>>> ProductCategory = Model.get('product.category')
>>> ProductTemplate = Model.get('product.template')
>>> ProductUom = Model.get('product.uom')
>>> Purchase = Model.get('purchase.purchase')
Create company::
>>> _ = create_company()
Create chart of accounts::
>>> _ = create_chart()
>>> accounts = get_accounts()
Create party::
>>> supplier = Party(name="Supplier")
>>> supplier.save()
Create product::
>>> account_category = ProductCategory(name="Account Category")
>>> account_category.accounting = True
>>> account_category.account_expense = accounts['expense']
>>> account_category.save()
>>> template = ProductTemplate(name="Product")
>>> template.default_uom, = ProductUom.find([('name', '=', "Unit")])
>>> template.type = 'goods'
>>> template.purchasable = True
>>> template.account_category = account_category
>>> template.save()
>>> product, = template.products
Purchase product::
>>> purchase = Purchase(party=supplier)
>>> purchase.invoice_method = 'shipment'
>>> line = purchase.lines.new()
>>> line.product = product
>>> line.quantity = 1
>>> line.unit_price = Decimal('10.0000')
>>> purchase.click('quote')
>>> purchase.click('confirm')
>>> purchase.state
'processing'
>>> purchase.shipment_state
'waiting'
>>> purchase.invoice_state
'none'
Cancel stock move::
>>> move, = purchase.moves
>>> move.click('cancel')
>>> move.state
'cancelled'
>>> purchase.reload()
>>> purchase.state
'processing'
>>> purchase.shipment_state
'exception'
>>> purchase.invoice_state
'none'
Ignore exception::
>>> shipment_handle_exception = purchase.click('handle_shipment_exception')
>>> shipment_handle_exception.form.recreate_moves.clear()
>>> shipment_handle_exception.execute('handle')
>>> purchase.shipment_state
'none'
>>> purchase.state
'done'
+15
View File
@@ -1405,6 +1405,12 @@ class SaleLine(TaxableMixin, sequence_ordered(), ModelSQL, ModelView):
return
skips = set(self.sale.invoices_ignored)
quantity = self.quantity
if self.sale.invoice_method == 'shipment':
moves_ignored = set(self.moves_ignored)
for move in self.moves:
if move in moves_ignored:
quantity -= UoM.compute_qty(
move.unit, move.quantity, self.unit)
for invoice_line in self.invoice_lines:
if invoice_line.type != 'line':
continue
@@ -1434,6 +1440,15 @@ class SaleLine(TaxableMixin, sequence_ordered(), ModelSQL, ModelView):
return
skips = set(self.moves_ignored)
quantity = abs(self.quantity)
if self.sale.shipment_method == 'invoice':
invoices_ignored = set(self.sale.invoices_ignored)
for invoice_line in self.invoice_lines:
if invoice_line.type != 'line':
continue
if invoice_line.invoice in invoices_ignored:
quantity -= Uom.compute_qty(
invoice_line.unit or self.unit, invoice_line.quantity,
self.unit)
for move in self.moves:
if move.state == 'done' or move in skips:
quantity -= Uom.compute_qty(
@@ -0,0 +1,96 @@
=======================================
Sale Line Cancelled On Invoice Scenario
=======================================
Imports::
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.tests.tools import activate_modules
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.account.tests.tools import (
... create_chart, get_accounts)
Activate modules::
>>> config = activate_modules('sale')
>>> Party = Model.get('party.party')
>>> ProductCategory = Model.get('product.category')
>>> ProductTemplate = Model.get('product.template')
>>> ProductUom = Model.get('product.uom')
>>> Sale = Model.get('sale.sale')
Create company::
>>> _ = create_company()
Create chart of accounts::
>>> _ = create_chart()
>>> accounts = get_accounts()
Create party::
>>> customer = Party(name="Customer")
>>> customer.save()
Create product::
>>> account_category = ProductCategory(name="Account Category")
>>> account_category.accounting = True
>>> account_category.account_revenue = accounts['revenue']
>>> account_category.save()
>>> template = ProductTemplate(name="Product")
>>> template.default_uom, = ProductUom.find([('name', '=', "Unit")])
>>> template.type = 'goods'
>>> template.salable = True
>>> template.list_price = Decimal('10.0000')
>>> template.account_category = account_category
>>> template.save()
>>> product, = template.products
Sale product::
>>> sale = Sale(party=customer)
>>> sale.shipment_method = 'invoice'
>>> line = sale.lines.new()
>>> line.product = product
>>> line.quantity = 1
>>> sale.click('quote')
>>> sale.click('confirm')
>>> sale.state
'processing'
>>> sale.shipment_state
'none'
>>> sale.invoice_state
'pending'
Cancel invoice::
>>> invoice, = sale.invoices
>>> invoice.click('cancel')
>>> invoice.state
'cancelled'
>>> sale.reload()
>>> sale.state
'processing'
>>> sale.shipment_state
'none'
>>> sale.invoice_state
'exception'
Ignore exception::
>>> invoice_handle_exception = sale.click('handle_invoice_exception')
>>> invoice_handle_exception.form.recreate_invoices.clear()
>>> invoice_handle_exception.execute('handle')
>>> sale.shipment_state
'none'
>>> sale.state
'done'
@@ -0,0 +1,96 @@
========================================
Sale Line Cancelled On Shipment Scenario
========================================
Imports::
>>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.tests.tools import activate_modules
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.account.tests.tools import (
... create_chart, get_accounts)
Activate modules::
>>> config = activate_modules('sale')
>>> Party = Model.get('party.party')
>>> ProductCategory = Model.get('product.category')
>>> ProductTemplate = Model.get('product.template')
>>> ProductUom = Model.get('product.uom')
>>> Sale = Model.get('sale.sale')
Create company::
>>> _ = create_company()
Create chart of accounts::
>>> _ = create_chart()
>>> accounts = get_accounts()
Create party::
>>> customer = Party(name="Customer")
>>> customer.save()
Create product::
>>> account_category = ProductCategory(name="Account Category")
>>> account_category.accounting = True
>>> account_category.account_revenue = accounts['revenue']
>>> account_category.save()
>>> template = ProductTemplate(name="Product")
>>> template.default_uom, = ProductUom.find([('name', '=', "Unit")])
>>> template.type = 'goods'
>>> template.salable = True
>>> template.list_price = Decimal('10.0000')
>>> template.account_category = account_category
>>> template.save()
>>> product, = template.products
Sale product::
>>> sale = Sale(party=customer)
>>> sale.invoice_method = 'shipment'
>>> line = sale.lines.new()
>>> line.product = product
>>> line.quantity = 1
>>> sale.click('quote')
>>> sale.click('confirm')
>>> sale.state
'processing'
>>> sale.shipment_state
'waiting'
>>> sale.invoice_state
'none'
Cancel shipment::
>>> shipment, = sale.shipments
>>> shipment.click('cancel')
>>> shipment.state
'cancelled'
>>> sale.reload()
>>> sale.state
'processing'
>>> sale.shipment_state
'exception'
>>> sale.invoice_state
'none'
Ignore exception::
>>> shipment_handle_exception = sale.click('handle_shipment_exception')
>>> shipment_handle_exception.form.recreate_moves.clear()
>>> shipment_handle_exception.execute('handle')
>>> sale.shipment_state
'none'
>>> sale.state
'done'