changeset f5ae6da828ea in modules/sale_shipment_cost:default
details: 
https://hg.tryton.org/modules/sale_shipment_cost?cmd=changeset&node=f5ae6da828ea
description:
        Add unsold shipment cost to the outgoing moves

        Even there is a shipment cost method if the cost sold is lower than the 
actual
        shipment cost, the difference must be added to the outgoing moves.

        issue10149
        review335731002
diffstat:

 sale.py                                     |   10 +-
 setup.py                                    |    5 +-
 stock.py                                    |   31 ++++-
 tests/scenario_sale_shipment_cost_stock.rst |  190 ++++++++++++++++++++++++++++
 tests/test_sale_shipment_cost.py            |    5 +
 5 files changed, 237 insertions(+), 4 deletions(-)

diffs (291 lines):

diff -r ea4abbc22660 -r f5ae6da828ea sale.py
--- a/sale.py   Sat Mar 13 23:43:51 2021 +0100
+++ b/sale.py   Tue Mar 23 15:33:02 2021 +0100
@@ -224,7 +224,7 @@
         unit_price = None
         lines = list(self.lines or [])
         for line in self.lines:
-            if line.type == 'line' and line.shipment_cost:
+            if line.type == 'line' and line.shipment_cost is not None:
                 if line.shipment_cost == cost:
                     unit_price = line.unit_price * Decimal(str(line.quantity))
                 lines.remove(line)
@@ -265,6 +265,14 @@
         cost_line.amount = cost_line.on_change_with_amount()
         return cost_line
 
+    @property
+    def shipment_cost_amount(self):
+        cost = Decimal(0)
+        for line in self.lines:
+            if line.type == 'line' and line.shipment_cost is not None:
+                cost += line.amount
+        return cost
+
     def create_shipment(self, shipment_type):
         pool = Pool()
         Shipment = pool.get('stock.shipment.out')
diff -r ea4abbc22660 -r f5ae6da828ea setup.py
--- a/setup.py  Sat Mar 13 23:43:51 2021 +0100
+++ b/setup.py  Tue Mar 23 15:33:02 2021 +0100
@@ -63,7 +63,10 @@
         requires.append(get_require_version('trytond_%s' % dep))
 requires.append(get_require_version('trytond'))
 
-tests_require = [get_require_version('proteus')]
+tests_require = [
+    get_require_version('proteus'),
+    get_require_version('trytond_stock_shipment_cost'),
+    ]
 for dep in ['account', 'party', 'stock', 'sale_promotion']:
     tests_require.append(get_require_version('trytond_%s' % dep))
 dependency_links = []
diff -r ea4abbc22660 -r f5ae6da828ea stock.py
--- a/stock.py  Sat Mar 13 23:43:51 2021 +0100
+++ b/stock.py  Tue Mar 23 15:33:02 2021 +0100
@@ -209,9 +209,36 @@
         return invoice_line
 
     def _get_shipment_cost(self):
+        pool = Pool()
+        Currency = pool.get('currency.currency')
         cost = super()._get_shipment_cost()
-        if all(not m.sale.shipment_cost_method for m in self.outgoing_moves):
-            cost += self.cost_used
+        methods = {
+            m.sale.shipment_cost_method for m in self.outgoing_moves if m.sale}
+        if methods == {None}:
+            cost += self.cost_used or 0
+        if 'shipment' in methods:
+            if self.cost_sale:
+                cost_sale = Currency.compute(
+                    self.cost_sale_currency, self.cost_sale,
+                    self.company.currency, round=False)
+            else:
+                cost_sale = 0
+            delta = cost_sale - (self.cost_used or 0)
+            if delta < 0:
+                cost -= delta
+        if 'order' in methods and self.cost_used:
+            sales = {
+                m.sale for m in self.outgoing_moves
+                if m.sale and m.sale.shipment_cost_method == 'order'}
+            for sale in sales:
+                shipment_cost = sum(
+                    (s.cost_used or 0) for s in sale.shipments
+                    if s.state == 'done' and s != self)
+                cost_sale = sale.shipment_cost_amount
+                if cost_sale < shipment_cost:
+                    cost += self.cost_used or 0
+                elif cost_sale < shipment_cost + (self.cost_used or 0):
+                    cost += shipment_cost + self.cost_used - cost_sale
         return cost
 
     @classmethod
diff -r ea4abbc22660 -r f5ae6da828ea tests/scenario_sale_shipment_cost_stock.rst
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/scenario_sale_shipment_cost_stock.rst       Tue Mar 23 15:33:02 
2021 +0100
@@ -0,0 +1,190 @@
+=================================
+Sale Shipment Cost Stock 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, get_company)
+    >>> from trytond.modules.account.tests.tools import (
+    ...     create_chart, get_accounts)
+
+Activate modules::
+
+    >>> config = activate_modules([
+    ...         'sale_shipment_cost',
+    ...         'sale',
+    ...         'stock_shipment_cost',
+    ...         ])
+
+    >>> Carrier = Model.get('carrier')
+    >>> CarrierSelection = Model.get('carrier.selection')
+    >>> 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')
+    >>> ShipmentOut = Model.get('stock.shipment.out')
+
+Create company::
+
+    >>> _ = create_company()
+    >>> company = get_company()
+
+Create chart of accounts::
+
+    >>> _ = create_chart(company)
+    >>> accounts = get_accounts(company)
+
+Create customer::
+
+    >>> customer = Party(name='Customer')
+    >>> customer.save()
+
+Create account category::
+
+    >>> account_category = ProductCategory(name="Account Category")
+    >>> account_category.accounting = True
+    >>> account_category.account_revenue = accounts['revenue']
+    >>> account_category.save()
+
+Create product::
+
+    >>> unit, = ProductUom.find([('name', '=', 'Unit')])
+
+    >>> template = ProductTemplate()
+    >>> template.name = 'Product'
+    >>> template.default_uom = unit
+    >>> template.type = 'goods'
+    >>> template.salable = True
+    >>> template.list_price = Decimal('20')
+    >>> template.account_category = account_category
+    >>> template.save()
+    >>> product, = template.products
+
+    >>> carrier_template = ProductTemplate()
+    >>> carrier_template.name = 'Carrier Product'
+    >>> carrier_template.default_uom = unit
+    >>> carrier_template.type = 'service'
+    >>> carrier_template.salable = True
+    >>> carrier_template.list_price = Decimal('2')
+    >>> carrier_template.account_category = account_category
+    >>> carrier_template.save()
+    >>> carrier_product, = carrier_template.products
+    >>> carrier_product.cost_price = Decimal('5')
+    >>> carrier_product.save()
+
+Create carrier::
+
+    >>> carrier = Carrier()
+    >>> party = Party(name='Carrier')
+    >>> party.save()
+    >>> carrier.party = party
+    >>> carrier.carrier_product = carrier_product
+    >>> carrier.save()
+
+Use it as the default carrier::
+
+    >>> csc = CarrierSelection(carrier=carrier)
+    >>> csc.save()
+
+
+Sell product with no shipment cost::
+
+    >>> sale = Sale()
+    >>> sale.party = customer
+    >>> sale.carrier = carrier
+    >>> sale.shipment_cost_method = None
+    >>> line = sale.lines.new()
+    >>> line.product = product
+    >>> line.quantity = 1
+    >>> sale.click('quote')
+    >>> len(sale.lines)
+    1
+    >>> sale.click('confirm')
+    >>> sale.click('process')
+    >>> sale.state
+    'processing'
+
+    >>> shipment, = sale.shipments
+    >>> shipment.cost_used
+    Decimal('5.0000')
+    >>> shipment.click('assign_force')
+    >>> shipment.click('pick')
+    >>> shipment.click('pack')
+    >>> shipment.click('done')
+    >>> shipment.state
+    'done'
+    >>> move, = shipment.outgoing_moves
+    >>> move.shipment_out_cost_price
+    Decimal('5.0000')
+
+Sell product with cost on shipment::
+
+    >>> sale = Sale()
+    >>> sale.party = customer
+    >>> sale.carrier = carrier
+    >>> sale.shipment_cost_method = 'shipment'
+    >>> line = sale.lines.new()
+    >>> line.product = product
+    >>> line.quantity = 1
+    >>> sale.click('quote')
+    >>> len(sale.lines)
+    2
+    >>> sale.click('confirm')
+    >>> sale.click('process')
+    >>> sale.state
+    'processing'
+
+    >>> shipment, = sale.shipments
+    >>> shipment.cost_used
+    Decimal('5.0000')
+    >>> shipment.cost_sale_used
+    Decimal('2.0000')
+    >>> shipment.click('assign_force')
+    >>> shipment.click('pick')
+    >>> shipment.click('pack')
+    >>> shipment.click('done')
+    >>> shipment.state
+    'done'
+    >>> move, = shipment.outgoing_moves
+    >>> move.shipment_out_cost_price
+    Decimal('3.0000')
+
+Sell product with cost on order::
+
+    >>> sale = Sale()
+    >>> sale.party = customer
+    >>> sale.carrier = carrier
+    >>> sale.shipment_cost_method = 'order'
+    >>> line = sale.lines.new()
+    >>> line.product = product
+    >>> line.quantity = 1
+    >>> sale.click('quote')
+    >>> len(sale.lines)
+    2
+    >>> sale.click('draft')
+    >>> sale.lines[-1].unit_price = Decimal('3.0000')
+    >>> sale.click('quote')
+    >>> sale.lines[-1].unit_price
+    Decimal('3.0000')
+    >>> sale.click('confirm')
+    >>> sale.click('process')
+    >>> sale.state
+    'processing'
+
+    >>> shipment, = sale.shipments
+    >>> shipment.cost_used
+    Decimal('5.0000')
+    >>> shipment.click('assign_force')
+    >>> shipment.click('pick')
+    >>> shipment.click('pack')
+    >>> shipment.click('done')
+    >>> shipment.state
+    'done'
+    >>> move, = shipment.outgoing_moves
+    >>> move.shipment_out_cost_price
+    Decimal('2.0000')
diff -r ea4abbc22660 -r f5ae6da828ea tests/test_sale_shipment_cost.py
--- a/tests/test_sale_shipment_cost.py  Sat Mar 13 23:43:51 2021 +0100
+++ b/tests/test_sale_shipment_cost.py  Tue Mar 23 15:33:02 2021 +0100
@@ -32,4 +32,9 @@
             tearDown=doctest_teardown, encoding='utf-8',
             checker=doctest_checker,
             optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
+    suite.addTests(doctest.DocFileSuite(
+            'scenario_sale_shipment_cost_stock.rst',
+            tearDown=doctest_teardown, encoding='utf-8',
+            checker=doctest_checker,
+            optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
     return suite

Reply via email to