Commit 3c5dfa90 authored by Titouan Soulard's avatar Titouan Soulard

erp5_trade: add script to compute Inventory Offset

parent ad414193
portal = context.getPortalObject()
inventory_calculation_dict = {
"inventory_kw": {
"group_by_resource": 1,
"group_by_variation": 1,
#"group_by_sub_variation": 1,
"resourceType": portal.getPortalProductTypeList(),
},
"group_by": [
{
"key": "resource_relative_url",
"getter": "getResource",
"property": "resource",
},
{
"key": "variation_text",
"getter": "getVariationCategoryList",
# Without the lambda expression, one gets the following error:
# > You are not allowed to access 'join' in this context.
# pylint: disable=unnecessary-lambda
"post_getter": lambda variation_list: "\n".join(variation_list),
"property": "variation",
}
]
}
# Create first dictionary of what is currently in the stock
section_uid = context.getDestinationSectionUid()
node_uid = context.getDestinationUid()
inventory_list = portal.portal_simulation.getCurrentInventoryList(
section_uid=section_uid,
node_uid=node_uid,
at_date=context.getStartDate(),
**inventory_calculation_dict["inventory_kw"]
)
inventory_dict = {}
for inventory in inventory_list:
total_quantity = inventory.total_quantity
total_price = inventory.total_price
# Build a key, usually by going from most general (product) to most specific (sub-variation)
key = []
for group_dict in inventory_calculation_dict["group_by"]:
key_method = group_dict["key"]
partial_key = inventory[key_method]
if partial_key:
key.append(partial_key)
# Xavier's magic method: traverse dictionnary and assign to last
inventory_element = reduce(lambda t, x: t.setdefault(x, {}), key, inventory_dict)
inventory_element["quantity"] = total_quantity
inventory_element["price"] = total_price
# Create second dictionary of what is reported on inventory
movement_generator_list = []
for inventory_line in context.objectValues(portal_type="Inventory Line"):
if inventory_line.hasCellContent():
movement_generator_list.extend(inventory_line.objectValues(portal_type="Inventory Cell"))
else:
movement_generator_list.append(inventory_line)
report_dict = {}
for movement_generator in movement_generator_list:
# Browse dictionnary by creating a key for each pseudo-movement
key = []
for group_dict in inventory_calculation_dict["group_by"]:
getter_method = getattr(movement_generator, group_dict["getter"], None)
partial_key = getter_method() if getter_method else None
if partial_key:
if "post_getter" in group_dict:
partial_key = group_dict["post_getter"](partial_key)
key.append(partial_key)
report_element = reduce(lambda t, x: t.setdefault(x, {}), key, report_dict)
report_element["quantity"] = movement_generator.getInventory()
# XXX: is that expected? We have two diverging dictionaries: one with total prices and the other with unit prices.
report_element["price"] = movement_generator.getPrice()
# Normal case: iterate over report dictionary and compare with inventory
# When full inventory, take into account all objects in inventory and compare with report
base_dict = inventory_dict if context.isFullInventory() else report_dict
compare_dict = report_dict if context.isFullInventory() else inventory_dict
def compareDict(base, compare, path):
# Traverse dictionary to return item or None
base_element = reduce(lambda d, x: d[x] if d and x in d else None, path, base)
compare_element = reduce(lambda d, x: d[x] if d and x in d else None, path, compare)
if base_element is None:
raise AssertionError("Since iterating on `base_dict` gives the key, element is expected to exist in `base_dict`")
if compare_element is None:
# Case (normal): object not in inventory API but in Inventory
return base_element["quantity"]
# Case (normal): object in inventory API and in Inventory
return base_element["quantity"] - compare_element["quantity"]
for product_relative_url, base_product_content in base_dict.items():
if not "price" in base_product_content:
offset_line = None
variation_list = []
for variation_partial_text, base_variation_content in base_product_content.items():
missing_quantity = compareDict(base_dict, compare_dict, [product_relative_url, variation_partial_text])
if missing_quantity != 0.0:
if offset_line is None:
offset_line = context.newContent(
portal_type="Inventory Offset Line",
resource=product_relative_url,
)
variation = variation_partial_text.split("/", 1)[1]
offset_line.newContent(
portal_type="Inventory Offset Cell",
quantity=missing_quantity,
variation=variation,
price=base_variation_content["price"],
)
variation_list.append(variation)
if offset_line is not None:
offset_line.setVariationList(variation_list)
else:
missing_quantity = compareDict(base_dict, compare_dict, [product_relative_url])
if missing_quantity != 0.0:
context.newContent(
portal_type="Inventory Offset Line",
quantity=missing_quantity,
resource=product_relative_url,
price=base_product_content["price"],
)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Inventory_computeOffset</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
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