Fix corruption of outgoing_moves in packed state of stock.shipment.out

The `move_obj.write` changes the value of `quantity` in the move, but the
removed_qty computation used `move.quantity` from the browse record. The
browse record gets the updated value and not the old value from
browse_record cache. This fix just moves the computation before the
move_obj.write and includes a scenario to confirm the behavior.

issue2426
review254001
(transplanted from eedeb7ba47b1363919364b40a07862ccf012db97)
This commit is contained in:
Sharoon Thomas committed 2012-03-02 10:46:08 -05:00
1 parent 90a0929cdd
commit 4c26686ad8
4 files changed
+220 -3

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
Copyright (C) 2012 Openlabs Technologies & Consulting (P) LTD.
Copyright (C) 2008-2011 Cédric Krier.
Copyright (C) 2008-2011 Bertrand Chenal.
Copyright (C) 2008-2011 B2CK SPRL.
+3 -3
View File
@@ -1018,12 +1018,12 @@ class ShipmentOut(ModelWorkflow, ModelSQL, ModelView):
if outgoing_qty.get(move.product.id, 0.0) > 0.0:
exc_qty = uom_obj.compute_qty(move.product.default_uom,
outgoing_qty[move.product.id], move.uom)
move_obj.write(move.id,{
'quantity': max(0.0, move.quantity-exc_qty),
})
removed_qty = uom_obj.compute_qty(move.uom,
min(exc_qty, move.quantity), move.product.default_uom,
round=False)
move_obj.write(move.id,{
'quantity': max(0.0, move.quantity-exc_qty),
})
outgoing_qty[move.product.id] -= removed_qty
move_obj.write([x.id for x in shipment.outgoing_moves
+197
View File
@@ -0,0 +1,197 @@
===========================
Stock Shipment Out Scenario
===========================
=============
General Setup
=============
Imports::
>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> from decimal import Decimal
>>> from proteus import config, Model, Wizard
>>> today = datetime.date.today()
Create database::
>>> config = config.set_trytond()
>>> config.pool.test = True
Install stock Module::
>>> Module = Model.get('ir.module.module')
>>> modules = Module.find([('name', '=', 'stock')])
>>> Module.button_install([x.id for x in modules], config.context)
>>> Wizard('ir.module.module.install_upgrade').execute('upgrade')
Create company::
>>> Currency = Model.get('currency.currency')
>>> CurrencyRate = Model.get('currency.currency.rate')
>>> Company = Model.get('company.company')
>>> company_config = Wizard('company.company.config')
>>> company_config.execute('company')
>>> company = company_config.form
>>> company.name = 'OPENLABS'
>>> currencies = Currency.find([('code', '=', 'EUR')])
>>> if not currencies:
... currency = Currency(name='Euro', symbol=u'€', code='EUR',
... rounding=Decimal('0.01'), mon_grouping='[3, 3, 0]',
... mon_decimal_point=',')
... currency.save()
... CurrencyRate(date=today + relativedelta(month=1, day=1),
... rate=Decimal('1.0'), currency=currency).save()
... else:
... currency, = currencies
>>> company.currency = currency
>>> company_config.execute('add')
>>> company, = Company.find()
Reload the context::
>>> User = Model.get('res.user')
>>> config._context = User.get_preferences(True, config.context)
Create customer::
>>> Party = Model.get('party.party')
>>> customer = Party(name='Customer')
>>> customer.save()
Create category::
>>> ProductCategory = Model.get('product.category')
>>> category = ProductCategory(name='Category')
>>> category.save()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> Product = Model.get('product.product')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> product = Product()
>>> product.name = 'Product'
>>> product.category = category
>>> product.default_uom = unit
>>> product.type = 'stockable'
>>> product.list_price = Decimal('20')
>>> product.cost_price = Decimal('8')
>>> product.save()
Get stock locations::
>>> Location = Model.get('stock.location')
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
>>> output_loc, = Location.find([('code', '=', 'OUT')])
>>> storage_loc, = Location.find([('code', '=', 'STO')])
Create Shipment Out::
>>> ShipmentOut = Model.get('stock.shipment.out')
>>> shipment_out = ShipmentOut()
>>> shipment_out.planned_date = today
>>> shipment_out.customer = customer
>>> shipment_out.warehouse = warehouse_loc
>>> shipment_out.company = company
Add two shipment lines of same product::
>>> StockMove = Model.get('stock.move')
>>> shipment_out.outgoing_moves.extend([StockMove(), StockMove()])
>>> for move in shipment_out.outgoing_moves:
... move.product = product
... move.uom =unit
... move.quantity = 1
... move.from_location = output_loc
... move.to_location = customer_loc
... move.company = company
... move.unit_price = Decimal('1')
... move.currency = currency
>>> shipment_out.save()
Set the shipment state to waiting::
>>> ShipmentOut.workflow_trigger_validate(
... shipment_out.id, 'waiting', config.context)
>>> shipment_out.reload()
>>> len(shipment_out.outgoing_moves)
2
>>> len(shipment_out.inventory_moves)
2
Make 1 unit of the product available::
>>> incoming_move = StockMove()
>>> incoming_move.product = product
>>> incoming_move.uom = unit
>>> incoming_move.quantity = 1
>>> incoming_move.from_location = supplier_loc
>>> incoming_move.to_location = storage_loc
>>> incoming_move.planned_date = today
>>> incoming_move.effective_date = today
>>> incoming_move.state = 'done'
>>> incoming_move.company = company
>>> incoming_move.unit_price = Decimal('1')
>>> incoming_move.currency = currency
>>> incoming_move.save()
Assign the shipment now::
>>> ShipmentOut.workflow_trigger_validate(
... shipment_out.id, 'assign', config.context)
>>> shipment_out.reload()
>>> len(shipment_out.outgoing_moves)
2
>>> len(shipment_out.inventory_moves)
2
>>> states = [m.state for m in shipment_out.inventory_moves]
>>> states.sort()
>>> states
[u'assigned', u'draft']
Delete the draft move, assign and pack shipment::
>>> for move in shipment_out.inventory_moves:
... if move.state == 'draft':
... break
>>> shipment_out.inventory_moves.remove(move)
>>> shipment_out.save()
>>> ShipmentOut.workflow_trigger_validate(
... shipment_out.id, 'assign', config.context)
>>> ShipmentOut.workflow_trigger_validate(
... shipment_out.id, 'packed', config.context)
>>> shipment_out.reload()
>>> set([m.state for m in shipment_out.outgoing_moves])
set([u'assigned'])
>>> len(shipment_out.outgoing_moves)
2
>>> len(shipment_out.inventory_moves)
1
>>> shipment_out.inventory_moves[0].state
u'done'
>>> sum([m.quantity for m in shipment_out.inventory_moves]) == \
... sum([m.quantity for m in shipment_out.outgoing_moves])
True
Set the state as Done::
>>> ShipmentOut.workflow_trigger_validate(
... shipment_out.id, 'done', config.context)
>>> shipment_out.reload()
>>> set([m.state for m in shipment_out.outgoing_moves])
set([u'done'])
>>> len(shipment_out.outgoing_moves)
2
>>> len(shipment_out.inventory_moves)
1
>>> shipment_out.inventory_moves[0].state
u'done'
>>> sum([m.quantity for m in shipment_out.inventory_moves]) == \
... sum([m.quantity for m in shipment_out.outgoing_moves])
True
+19
View File
@@ -9,6 +9,7 @@ if os.path.isdir(DIR):
sys.path.insert(0, os.path.dirname(DIR))
import unittest
import doctest
import datetime
from decimal import Decimal
from dateutil.relativedelta import relativedelta
@@ -16,6 +17,7 @@ from functools import partial
import trytond.tests.test_tryton
from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, test_view,\
test_depends
from trytond.backend.sqlite.database import Database as SQLiteDatabase
from trytond.transaction import Transaction
@@ -459,6 +461,19 @@ class StockTestCase(unittest.TestCase):
self.assertRaises(Exception, self.period.button_close, [period_id])
def doctest_dropdb(test):
'''
Remove sqlite memory database
'''
database = SQLiteDatabase().connect()
cursor = database.cursor(autocommit=True)
try:
database.drop(cursor, ':memory:')
cursor.commit()
finally:
cursor.close()
def suite():
suite = trytond.tests.test_tryton.suite()
from trytond.modules.company.tests import test_company
@@ -466,6 +481,10 @@ def suite():
if test not in suite:
suite.addTest(test)
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(StockTestCase))
suite.addTests(doctest.DocFileSuite('scenario_stock_shipment_out.rst',
setUp=doctest_dropdb, tearDown=doctest_dropdb, encoding='utf-8',
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
return suite
if __name__ == '__main__':