Commit a0f7e169 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 5b279571
...@@ -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>
2013-05-30 yusei
* Fix an inventory bug. When there are more than 300 inventories which are cancelled by inventory document, inventory document creates wrong stock records.
2013-05-08 arnaud.fontaine 2013-05-08 arnaud.fontaine
* Prevent order_id column value to overflow following changes in Inventory (cf53db4). * Prevent order_id column value to overflow following changes in Inventory (cf53db4).
......
260 261
\ No newline at end of file \ No newline at end of file
...@@ -42,6 +42,7 @@ erp5_mysql_innodb/z_catalog_predicate_list ...@@ -42,6 +42,7 @@ erp5_mysql_innodb/z_catalog_predicate_list
erp5_mysql_innodb/z_catalog_quantity_unit_conversion_list erp5_mysql_innodb/z_catalog_quantity_unit_conversion_list
erp5_mysql_innodb/z_catalog_roles_and_users_list erp5_mysql_innodb/z_catalog_roles_and_users_list
erp5_mysql_innodb/z_catalog_stock_list erp5_mysql_innodb/z_catalog_stock_list
erp5_mysql_innodb/z_catalog_stock_list_without_delete_for_inventory_virtual_movement
erp5_mysql_innodb/z_catalog_subject_list erp5_mysql_innodb/z_catalog_subject_list
erp5_mysql_innodb/z_catalog_transformation_list erp5_mysql_innodb/z_catalog_transformation_list
erp5_mysql_innodb/z_catalog_translation_list erp5_mysql_innodb/z_catalog_translation_list
......
...@@ -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