changeset 53918955ac8b in modules/account_invoice_stock:default
details: 
https://hg.tryton.org/modules/account_invoice_stock?cmd=changeset&node=53918955ac8b
description:
        Manage price correction from supplier invoice

        issue10191
        review332231002
diffstat:

 CHANGELOG                                           |    1 +
 account.py                                          |   13 +
 stock.py                                            |    5 +-
 tests/scenario_account_invoice_stock_correction.rst |  133 ++++++++++++++++++++
 tests/test_account_invoice_stock.py                 |    5 +
 view/invoice_line_form.xml                          |    4 +
 6 files changed, 159 insertions(+), 2 deletions(-)

diffs (223 lines):

diff -r af0e374fc258 -r 53918955ac8b CHANGELOG
--- a/CHANGELOG Sat Feb 27 15:23:46 2021 +0100
+++ b/CHANGELOG Sun Mar 21 16:07:45 2021 +0100
@@ -1,3 +1,4 @@
+* Manage price correction from supplier invoice
 * Update unit price after invoice posted and move done
 
 Version 5.8.0 - 2020-11-02
diff -r af0e374fc258 -r 53918955ac8b account.py
--- a/account.py        Sat Feb 27 15:23:46 2021 +0100
+++ b/account.py        Sun Mar 21 16:07:45 2021 +0100
@@ -54,12 +54,24 @@
                 | ~Eval('product')),
             },
         depends=['type', 'product_uom_category', 'invoice', 'invoice_type'])
+    correction = fields.Boolean(
+        "Correction",
+        states={
+            'invisible': ((Eval('_parent_invoice', {}).get('type') == 'out')
+                | (Eval('invoice_type') == 'out')),
+            },
+        depends=['invoice_type'],
+        help="Check to correct price of already posted invoice.")
 
     @classmethod
     def __setup__(cls):
         super().__setup__()
         cls._check_modify_exclude.add('stock_moves')
 
+    @classmethod
+    def default_correction(cls):
+        return False
+
     @property
     def moved_quantity(self):
         'The quantity from linked stock moves in line unit'
@@ -88,6 +100,7 @@
             default = {}
         else:
             default = default.copy()
+        default.setdefault('correction', False)
         if not Transaction().context.get('_account_invoice_correction'):
             default.setdefault('stock_moves', None)
         return super(InvoiceLine, cls).copy(lines, default=default)
diff -r af0e374fc258 -r 53918955ac8b stock.py
--- a/stock.py  Sat Feb 27 15:23:46 2021 +0100
+++ b/stock.py  Sun Mar 21 16:07:45 2021 +0100
@@ -108,8 +108,9 @@
                 with Transaction().set_context(date=self.effective_date):
                     amount += Currency.compute(
                         line.invoice.currency, line.amount, self.currency)
-                quantity += UoM.compute_qty(
-                    line.unit, line.quantity, self.uom)
+                if line.invoice.type == 'out' or not line.correction:
+                    quantity += UoM.compute_qty(
+                        line.unit, line.quantity, self.uom)
         if not quantity:
             unit_price = self.unit_price
         else:
diff -r af0e374fc258 -r 53918955ac8b 
tests/scenario_account_invoice_stock_correction.rst
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/scenario_account_invoice_stock_correction.rst       Sun Mar 21 
16:07:45 2021 +0100
@@ -0,0 +1,133 @@
+===================================
+Invoice - Stock Correction Scenario
+===================================
+
+Imports::
+
+    >>> import datetime as dt
+    >>> 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_fiscalyear, create_chart, get_accounts)
+    >>> from trytond.modules.account_invoice.tests.tools import (
+    ...     set_fiscalyear_invoice_sequences)
+
+    >>> today = dt.date.today()
+
+Activate modules::
+
+    >>> config = activate_modules('account_invoice_stock')
+
+    >>> Invoice = Model.get('account.invoice')
+    >>> Location = Model.get('stock.location')
+    >>> Move = Model.get('stock.move')
+    >>> Party = Model.get('party.party')
+    >>> ProductCategory = Model.get('product.category')
+    >>> ProductTemplate = Model.get('product.template')
+    >>> ProductUom = Model.get('product.uom')
+    >>> Shipment = Model.get('stock.shipment.in')
+
+Create company::
+
+    >>> _ = create_company()
+    >>> company = get_company()
+
+Create fiscal year::
+
+    >>> fiscalyear = set_fiscalyear_invoice_sequences(
+    ...     create_fiscalyear(company))
+    >>> fiscalyear.click('create_period')
+
+Create chart of accounts::
+
+    >>> _ = create_chart(company)
+    >>> accounts = get_accounts(company)
+
+Create a party::
+
+    >>> party = Party(name="Party")
+    >>> party.save()
+
+Create an account category::
+
+    >>> account_category = ProductCategory(name="Account Category")
+    >>> account_category.accounting = True
+    >>> account_category.account_expense = accounts['expense']
+    >>> account_category.account_revenue = accounts['revenue']
+    >>> account_category.save()
+
+Create a product::
+
+    >>> unit, = ProductUom.find([('name', '=', 'Unit')])
+    >>> template = ProductTemplate()
+    >>> template.name = 'product'
+    >>> template.default_uom = unit
+    >>> template.type = 'goods'
+    >>> template.list_price = Decimal('40')
+    >>> template.account_category = account_category
+    >>> template.save()
+    >>> product, = template.products
+
+Get stock locations::
+
+    >>> input_loc, = Location.find([('code', '=', 'IN')])
+    >>> supplier_loc, = Location.find([('code', '=', 'SUP')])
+
+Create a shipment::
+
+    >>> shipment = Shipment()
+    >>> shipment.supplier = party
+    >>> move = shipment.incoming_moves.new()
+    >>> move.product = product
+    >>> move.quantity = 10
+    >>> move.from_location = supplier_loc
+    >>> move.to_location = input_loc
+    >>> move.unit_price = Decimal('40.0000')
+    >>> shipment.click('receive')
+    >>> shipment.state
+    'received'
+    >>> move, = shipment.incoming_moves
+
+Create an invoice::
+
+    >>> invoice = Invoice(type='in')
+    >>> invoice.party = party
+    >>> invoice.invoice_date = today
+    >>> line = invoice.lines.new()
+    >>> line.product = product
+    >>> line.quantity = 10
+    >>> line.unit_price = Decimal('40.0000')
+    >>> line.stock_moves.append(Move(move.id))
+    >>> invoice.click('post')
+    >>> invoice.state
+    'posted'
+
+Check move unit price::
+
+    >>> move.reload()
+    >>> move.unit_price
+    Decimal('40.0000')
+
+Post a price correction::
+
+    >>> invoice = Invoice(type='in')
+    >>> invoice.party = party
+    >>> invoice.invoice_date = today
+    >>> line = invoice.lines.new()
+    >>> line.product = product
+    >>> line.quantity = 1
+    >>> line.unit_price = Decimal('-10.0000')
+    >>> line.correction = True
+    >>> line.stock_moves.append(Move(move.id))
+    >>> invoice.click('post')
+    >>> invoice.state
+    'posted'
+
+Check move unit price::
+
+    >>> move.reload()
+    >>> move.unit_price
+    Decimal('39.0000')
diff -r af0e374fc258 -r 53918955ac8b tests/test_account_invoice_stock.py
--- a/tests/test_account_invoice_stock.py       Sat Feb 27 15:23:46 2021 +0100
+++ b/tests/test_account_invoice_stock.py       Sun Mar 21 16:07:45 2021 +0100
@@ -22,4 +22,9 @@
             tearDown=doctest_teardown, encoding='utf-8',
             checker=doctest_checker,
             optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
+    suite.addTests(doctest.DocFileSuite(
+            'scenario_account_invoice_stock_correction.rst',
+            tearDown=doctest_teardown, encoding='utf-8',
+            checker=doctest_checker,
+            optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
     return suite
diff -r af0e374fc258 -r 53918955ac8b view/invoice_line_form.xml
--- a/view/invoice_line_form.xml        Sat Feb 27 15:23:46 2021 +0100
+++ b/view/invoice_line_form.xml        Sun Mar 21 16:07:45 2021 +0100
@@ -2,6 +2,10 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <data>
+    <xpath expr="//field[@name='amount']" position="after">
+        <label name="correction"/>
+        <field name="correction"/>
+    </xpath>
     <xpath expr="/form/notebook/page[@id='general']" position="after">
         <page string="Stock" name="stock_moves">
             <field name="stock_moves" colspan="4"/>

Reply via email to