Commit ea6006d8 authored by Yusei Tahara's avatar Yusei Tahara Committed by Arnaud Fontaine

Fix an inventory bug. When there are more than 300 inventories which are...

Fix an inventory bug. When there are more than 300 inventories which are cancelled by inventory document, inventory document creates wrong stock records.
parent 18a02672
...@@ -198,6 +198,13 @@ class Inventory(Delivery): ...@@ -198,6 +198,13 @@ class Inventory(Delivery):
inventory_id = self.getId() inventory_id = self.getId()
list_method = inventory_calculation_dict['list_method'] list_method = inventory_calculation_dict['list_method']
method = getattr(self, list_method) method = getattr(self, list_method)
__order_id_counter_list = [0]
def getOrderIdCounter():
value = __order_id_counter_list[0]
__order_id_counter_list[0] = value + 1
return value
for movement in method(): for movement in method():
if movement.getResourceValue() is not None and \ if movement.getResourceValue() is not None and \
movement.getInventoriatedQuantity() not in (None, ''): movement.getInventoriatedQuantity() not in (None, ''):
...@@ -240,7 +247,10 @@ class Inventory(Delivery): ...@@ -240,7 +247,10 @@ class Inventory(Delivery):
# Create tmp movement # Create tmp movement
kwd = {'uid': movement.getUid(), kwd = {'uid': movement.getUid(),
'start_date': stop_date} 'start_date': stop_date,
'order_id': getOrderIdCounter(),
'mirror_order_id':getOrderIdCounter()
}
temp_delivery_line = temp_constructor(self, temp_delivery_line = temp_constructor(self,
inventory_id) inventory_id)
...@@ -284,7 +294,10 @@ class Inventory(Delivery): ...@@ -284,7 +294,10 @@ class Inventory(Delivery):
diff_quantity = - inventory_value[tuple(second_level_key)] diff_quantity = - inventory_value[tuple(second_level_key)]
kwd = {'uid': inventory_uid, kwd = {'uid': inventory_uid,
'start_date': stop_date} 'start_date': stop_date,
'order_id': getOrderIdCounter(),
'mirror_order_id':getOrderIdCounter()
}
# create the tmp line and set category on it # create the tmp line and set category on it
temp_delivery_line = temp_constructor(self, temp_delivery_line = temp_constructor(self,
...@@ -320,8 +333,15 @@ class Inventory(Delivery): ...@@ -320,8 +333,15 @@ class Inventory(Delivery):
immediate_reindex_archive=immediate_reindex_archive) immediate_reindex_archive=immediate_reindex_archive)
if stock_object_list: if stock_object_list:
# Delete existing records first.
self.portal_catalog.catalogObjectList(
stock_object_list[:], method_id_list=('z0_uncatalog_stock', ),
sql_catalog_id = sql_catalog_id,
disable_cache=1, check_uid=0, disable_archive=disable_archive,
immediate_reindex_archive=immediate_reindex_archive)
# Then insert new records without delete.
self.portal_catalog.catalogObjectList( self.portal_catalog.catalogObjectList(
stock_object_list, method_id_list=('z_catalog_stock_list', ), stock_object_list[:], method_id_list=('z_catalog_stock_list_without_delete_for_inventory_virtual_movement', ),
sql_catalog_id = sql_catalog_id, sql_catalog_id = sql_catalog_id,
disable_cache=1, check_uid=0, disable_archive=disable_archive, disable_cache=1, check_uid=0, disable_archive=disable_archive,
immediate_reindex_archive=immediate_reindex_archive) immediate_reindex_archive=immediate_reindex_archive)
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
<key> <string>src</string> </key> <key> <string>src</string> </key>
<value> <string encoding="cdata"><![CDATA[ <value> <string encoding="cdata"><![CDATA[
DELETE FROM stock WHERE <dtml-sqltest uid op=eq type=int> DELETE FROM stock WHERE <dtml-sqltest uid op=eq type=int multiple>
]]></string> </value> ]]></string> </value>
</item> </item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="SQL" module="Products.ZSQLMethods.SQL"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>arguments_src</string> </key>
<value> <string>uid\r\n
order_id\r\n
mirror_order_id\r\n
getExplanationUid\r\n
getResourceUid\r\n
getInventoriatedQuantity\r\n
getSourceUid\r\n
getDestinationUid\r\n
getSourceSectionUid\r\n
getDestinationSectionUid\r\n
isMovement\r\n
isCancellationAmount\r\n
isInventoryMovement\r\n
getSourcePaymentUid\r\n
getDestinationPaymentUid\r\n
getSourceFunctionUid\r\n
getDestinationFunctionUid\r\n
getSourceProjectUid\r\n
getDestinationProjectUid\r\n
getSourceFundingUid\r\n
getDestinationFundingUid\r\n
getSourcePaymentRequestUid\r\n
getDestinationPaymentRequestUid\r\n
getSimulationState\r\n
getSourceInventoriatedTotalAssetPrice\r\n
getDestinationInventoriatedTotalAssetPrice\r\n
getStartDate\r\n
getStopDate\r\n
isAccountable\r\n
getPortalType\r\n
getVariationText\r\n
getSubVariationText</string> </value>
</item>
<item>
<key> <string>connection_id</string> </key>
<value> <string>erp5_sql_connection</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>z_catalog_stock_list_without_delete_for_inventory_virtual_movement</string> </value>
</item>
<item>
<key> <string>src</string> </key>
<value> <string encoding="cdata"><![CDATA[
<dtml-let row_list="[]">\n
<dtml-in prefix="loop" expr="_.range(_.len(uid))">\n
<dtml-if "not(isInventoryMovement[loop_item]) and isMovement[loop_item] and getResourceUid[loop_item]">\n
<dtml-if "getDestinationUid[loop_item]">\n
<dtml-call expr="row_list.append([\n
uid[loop_item], \n
order_id[loop_item],\n
getExplanationUid[loop_item],\n
getDestinationUid[loop_item],\n
getDestinationSectionUid[loop_item],\n
getDestinationPaymentUid[loop_item],\n
getDestinationFunctionUid[loop_item],\n
getDestinationProjectUid[loop_item], \n
getDestinationFundingUid[loop_item], \n
getDestinationPaymentRequestUid[loop_item], \n
getSourceSectionUid[loop_item], \n
getSourceUid[loop_item], \n
getResourceUid[loop_item],\n
getInventoriatedQuantity[loop_item],\n
isCancellationAmount[loop_item],\n
isAccountable[loop_item],\n
getStopDate[loop_item], \n
getStartDate[loop_item], \n
getDestinationInventoriatedTotalAssetPrice[loop_item], \n
getPortalType[loop_item], \n
getSimulationState[loop_item], \n
getVariationText[loop_item],\n
getSubVariationText[loop_item]])">\n
</dtml-if>\n
<dtml-if "getSourceUid[loop_item]">\n
<dtml-call expr="row_list.append([\n
uid[loop_item], \n
mirror_order_id[loop_item],\n
getExplanationUid[loop_item],\n
getSourceUid[loop_item],\n
getSourceSectionUid[loop_item],\n
getSourcePaymentUid[loop_item],\n
getSourceFunctionUid[loop_item],\n
getSourceProjectUid[loop_item], \n
getSourceFundingUid[loop_item], \n
getSourcePaymentRequestUid[loop_item], \n
getDestinationSectionUid[loop_item], \n
getDestinationUid[loop_item], \n
getResourceUid[loop_item],\n
-(getInventoriatedQuantity[loop_item] or 0), \n
isCancellationAmount[loop_item],\n
isAccountable[loop_item],\n
getStartDate[loop_item], \n
getStopDate[loop_item],\n
getSourceInventoriatedTotalAssetPrice[loop_item], \n
getPortalType[loop_item], \n
getSimulationState[loop_item], \n
getVariationText[loop_item],\n
getSubVariationText[loop_item]])">\n
</dtml-if>\n
</dtml-if>\n
</dtml-in> \n
<dtml-if "row_list">\n
INSERT INTO\n
stock\n
VALUES\n
<dtml-in prefix="row" expr="row_list">\n
(\n
<dtml-sqlvar expr="row_item[0]" type="int">,\n
<dtml-sqlvar expr="row_item[1]" type="int">,\n
<dtml-sqlvar expr="row_item[2]" type="int" optional>,\n
<dtml-sqlvar expr="row_item[3]" type="int">,\n
<dtml-sqlvar expr="row_item[4]" type="int" optional>, \n
<dtml-sqlvar expr="row_item[5]" type="int" optional>, \n
<dtml-sqlvar expr="row_item[6]" type="int" optional>,\n
<dtml-sqlvar expr="row_item[7]" type="int" optional>,\n
<dtml-sqlvar expr="row_item[8]" type="int" optional>,\n
<dtml-sqlvar expr="row_item[9]" type="int" optional>,\n
<dtml-sqlvar expr="row_item[10]" type="int" optional>,\n
<dtml-sqlvar expr="row_item[11]" type="int" optional>,\n
<dtml-sqlvar expr="row_item[12]" type="int">, \n
<dtml-sqlvar expr="row_item[13]" type="float" optional>,\n
<dtml-sqlvar expr="row_item[14]" type="int">, \n
<dtml-sqlvar expr="row_item[15]" type="int">,\n
<dtml-sqlvar expr="row_item[16]" type="datetime" optional>,\n
<dtml-sqlvar expr="row_item[17]" type="datetime" optional>,\n
<dtml-sqlvar expr="row_item[18]" type="float" optional>,\n
<dtml-sqlvar expr="row_item[19]" type="string" optional>,\n
<dtml-sqlvar expr="row_item[20]" type="string" optional>,\n
<dtml-sqlvar expr="row_item[21]" type="string" optional>,\n
<dtml-sqlvar expr="row_item[22]" type="string" optional>\n
)\n
<dtml-if sequence-end><dtml-else>,</dtml-if>\n
</dtml-in>\n
</dtml-if>\n
</dtml-let>\n
]]></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -2078,56 +2078,6 @@ class TestInventory(TestOrderMixin, ERP5TypeTestCase): ...@@ -2078,56 +2078,6 @@ class TestInventory(TestOrderMixin, ERP5TypeTestCase):
inventory_list.append(inventory) inventory_list.append(inventory)
sequence.edit(inventory_list=inventory_list) sequence.edit(inventory_list=inventory_list)
def stepCreateTwoResourceFullInventoryAtTheDate(self, sequence=None,
sequence_list=None, **kw):
""" Create Full Inventory at the date' """
inventory_list = sequence.get('inventory_list',[])
if kw.get('start_date', None) is not None:
start_date = kw['start_date']
else:
start_date = '2013/03/12 00:00:00 GMT+9'
if kw.get('inventory1', None) is not None:
inventory1 = kw['inventory1']
else:
inventory_1 = 10
if kw.get('inventory2', None) is not None:
inventory2 = kw['inventory2']
else:
inventory2 = 100
inventory = self.createInventory(sequence=sequence)
inventory_list = sequence.get('inventory_list',[])
inventory.edit(full_inventory=True,
start_date=start_date)
inventory_line = inventory.newContent(
portal_type = self.inventory_line_portal_type,
resource_value = sequence.get("resource"),
inventory = inventory1)
inventory_line = inventory.newContent(
portal_type = self.inventory_line_portal_type,
resource_value = sequence.get("second_resource"),
inventory = inventory2)
inventory.deliver()
inventory_list.append(inventory)
sequence.edit(inventory_list=inventory_list)
def stepCreateTwoResourceFullInventoryAtTheDate1(self, sequence=None,
sequence_list=None, **kw):
params = dict(start_date=self.two_resource_full_inventory1_start_date,
inventory1=self.two_resource_full_inventory1_inventory_1,
inventory2=self.two_resource_full_inventory1_inventory_2)
self.stepCreateTwoResourceFullInventoryAtTheDate(sequence, sequence_list,
**params)
def stepCreateTwoResourceFullInventoryAtTheDate2(self, sequence=None,
sequence_list=None, **kw):
params = dict(start_date=self.two_resource_full_inventory2_start_date,
inventory1=self.two_resource_full_inventory2_inventory_1,
inventory2=self.two_resource_full_inventory2_inventory_2)
self.stepCreateTwoResourceFullInventoryAtTheDate(sequence, sequence_list,
**params)
def stepTestFullInventoryWithResourceCategory(self, def stepTestFullInventoryWithResourceCategory(self,
sequence=None, sequence=None,
sequence_list=None, sequence_list=None,
...@@ -2580,20 +2530,6 @@ class TestInventory(TestOrderMixin, ERP5TypeTestCase): ...@@ -2580,20 +2530,6 @@ class TestInventory(TestOrderMixin, ERP5TypeTestCase):
node_uid=node_value.getUid(), node_uid=node_value.getUid(),
resource_uid=resource_value.getUid()) resource_uid=resource_value.getUid())
def stepCheckFullInventoryUpdateWithValidDateOrder(
self, sequence=None, sequence_list=None, **kw):
resource_value = sequence.get('resource')
second_resource_value = sequence.get('second_resource')
node_value = sequence.get('node')
section_value = sequence.get('section')
self._testGetInventory(expected=100,
section_uid=section_value.getUid(),
node_uid=node_value.getUid(),
resource_uid=resource_value.getUid())
self._testGetInventory(expected=0,
section_uid=section_value.getUid(),
node_uid=node_value.getUid(),
resource_uid=second_resource_value.getUid())
def test_01_getInventory(self, quiet=0, run=run_all_test): def test_01_getInventory(self, quiet=0, run=run_all_test):
""" """
...@@ -3261,46 +3197,69 @@ class TestInventory(TestOrderMixin, ERP5TypeTestCase): ...@@ -3261,46 +3197,69 @@ class TestInventory(TestOrderMixin, ERP5TypeTestCase):
sequence_list.addSequenceString(sequence_string) sequence_list.addSequenceString(sequence_string)
sequence_list.play(self) sequence_list.play(self)
def test_15_FullInventoryUpdateWithValidDateOrder( def test_15_FullInventoryCanCreatesManyVirtualCompensationMovement(self, quiet=0, run=run_all_test):
self, quiet=0, run=run_all_test): organisation = self.portal.organisation_module.newContent(portal_type='Organisation')
""" resource_value_list = []
Confirm Full inventory update with a valid start_date order for i in range(2000):
resource_value_list.append(self.portal.product_module.newContent(portal_type='Product'))
The case is: self.commit()
1) full inventory: 2013/02/01,section=A, node=B, resource=X, quantity=15 self.tic()
resource=Y, quantity=20
2) full inventory: 2013/02/02,section=A, node=B, resource=X, quantity=20
resource=Y, quantity=50
3) full inventory: 2013/02/10,section=A, node=B, resource=X, quantity=100
-> X:100
Y:0 # creates a dummy movement with quantity=-50
[test]
getInventory(resource=X, to_date=2013/02/15) should return 100
getInventory(resource=Y, to_date=2013/02/15) should return 0
"""
if not run: return
self.two_resource_full_inventory1_start_date = '2013/02/01 00:00:00 GMT+9' # Create initial inventory
self.two_resource_full_inventory1_inventory_1 = 15 date_1 = DateTime('2013/04/29 00:00:00 GMT+9')
self.two_resource_full_inventory1_inventory_2 = 20 result = self.portal.portal_simulation.getCurrentInventoryList(at_date=date_1,
self.two_resource_full_inventory2_start_date = '2013/02/02 00:00:00 GMT+9' section_uid=organisation.getUid(),
self.two_resource_full_inventory2_inventory_1 = 20 node_uid=organisation.getUid(),
self.two_resource_full_inventory2_inventory_2 = 50 group_by_resource=1)
self.full_inventory_start_date_1 = '2013/02/10 00:00:00 GMT+9' self.assertEqual(len(result), 0)
sequence_list = SequenceList()
sequence_string = 'CreateOrganisationsForModule \ full_inventory_1 = self.portal.inventory_module.newContent(portal_type='Inventory')
CreateNotVariatedResource \ full_inventory_1.edit(destination_section_value=organisation,
CreateNotVariatedSecondResource \ destination_value=organisation,
CreateTwoResourceFullInventoryAtTheDate1 \ start_date=date_1,
Tic \ full_inventory=True)
CreateTwoResourceFullInventoryAtTheDate2 \ for resource_value in resource_value_list:
Tic \ full_inventory_1.newContent(portal_type='Inventory Line',
CreateFullInventoryAtTheDate1 \ resource_value=resource_value,
Tic \ quantity=123)
CheckFullInventoryUpdateWithValidDateOrder \ full_inventory_1.deliver()
'
sequence_list.addSequenceString(sequence_string) self.commit()
sequence_list.play(self) self.tic()
result = self.portal.portal_simulation.getCurrentInventoryList(at_date=date_1,
section_uid=organisation.getUid(),
node_uid=organisation.getUid(),
group_by_resource=1)
self.assertEqual(sorted([(brain.resource_uid, brain.inventory)
for brain in result]),
sorted([(movement.getResourceUid(), movement.getQuantity())
for movement in full_inventory_1.getMovementList()]))
# Create second inventory which deletes inventories of many resources.
date_2 = DateTime('2013/05/03 00:00:00 GMT+9')
full_inventory_2 = self.portal.inventory_module.newContent(portal_type='Inventory')
full_inventory_2.edit(destination_section_value=organisation,
destination_value=organisation,
start_date=date_2,
full_inventory=True)
full_inventory_2.newContent(portal_type='Inventory Line',
resource_value=resource_value_list[0],
quantity=1)
full_inventory_2.deliver()
self.commit()
self.tic()
result = self.portal.portal_simulation.getCurrentInventoryList(at_date=date_2,
section_uid=organisation.getUid(),
node_uid=organisation.getUid(),
group_by_resource=1)
self.assertEqual(sorted([(brain.resource_uid, brain.inventory)
for brain in result if brain.inventory != 0]),
sorted([(movement.getResourceUid(), movement.getQuantity())
for movement in full_inventory_2.getMovementList()]))
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment