Commit 3530f7c6 authored by Jérome Perrin's avatar Jérome Perrin

Stock report valuation

Extend stock report dialog to allow choosing a (simple) valuation method

![stock report dialog screenshot](/uploads/2e0fa7c420954c3f84260605b3a6930e/image.png)

which be displayed in a new column, showing the inventory value for each line

See merge request nexedi/erp5!1203
parents ae3d5330 85350657
Pipeline #12146 failed with stage
in 0 seconds
......@@ -92,6 +92,7 @@
<string>listbox_quantity_unit</string>
<string>listbox_variation_category_item_list</string>
<string>listbox_aggregate_title_list</string>
<string>listbox_total_price</string>
</list>
</value>
</item>
......@@ -103,6 +104,7 @@
<string>your_section_category</string>
<string>your_at_date</string>
<string>your_simulation_period</string>
<string>your_currency</string>
</list>
</value>
</item>
......
......@@ -145,6 +145,10 @@
<string>aggregate_quantity_list</string>
<string>Item Quantity List</string>
</tuple>
<tuple>
<string>total_price</string>
<string>Inventory Value</string>
</tuple>
</list>
</value>
</item>
......@@ -199,7 +203,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: field.get_orig_value(\'columns\') + (context.REQUEST.form.get("item_stock") == 1 and [(\'aggregate_title_list\', \'Aggregated Items\')] or [])</string> </value>
<value> <string>python: field.get_orig_value(\'columns\') + (context.REQUEST.form.get("item_stock") == 1 and [(\'aggregate_title_list\', \'Aggregated Items\')] or []) + (context.REQUEST.form.get("inventory_valuation_method") and [(\'total_price\', \'Inventory Value\')] or [])</string> </value>
</item>
</dictionary>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>default</string>
<string>precision</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>listbox_total_price</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>precision</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_total_price</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewTradeFieldLibrary</string> </value>
</item>
<item>
<key> <string>precision</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Inventory Value</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: cell.InventoryListBrain_getInventoryValuatedTotalPrice(request[\'inventory_valuation_method\'])</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>request/precision | python: 2</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_currency</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_report_mode_currency</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewTradeFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -111,6 +111,7 @@
<string>your_negative_stock</string>
<string>your_zero_stock</string>
<string>your_item_stock</string>
<string>your_inventory_valuation_method</string>
</list>
</value>
</item>
......
......@@ -304,6 +304,7 @@
<string>my_view_mode_preferred_tax_use_list</string>
<string>my_report_mode_node_category</string>
<string>my_view_mode_ledger</string>
<string>my_report_mode_currency</string>
</list>
</value>
</item>
......
request = container.REQUEST
portal = context.getPortalObject()
# set request `precision` for listbox's total price field if not already set
if request.get('precision') is None:
request.set('precision', 3) # fallback value to search only once if nothing is defined
# Search an organisation's accounting currency to display currencies in this precision.
organisation_search_kw = {
'portal_type': 'Organisation',
}
if request.get('section_category'):
organisation_search_kw['uid'] = portal.Base_getSectionUidListForSectionCategory(request['section_category'])
else:
organisation_search_kw['site_uid'] = portal.portal_categories.restrictedTraverse(request['node_category']).getUid()
for brain in portal.portal_catalog(**organisation_search_kw):
currency_relative_url = brain.getObject().getPriceCurrency()
if currency_relative_url:
request.set('precision', context.getQuantityPrecisionFromResource(currency_relative_url))
break
def getPriceFromDefaultSupplyLine(brain, supply_line_id):
# TODO: support variations ? (at same time this approach is intentionally super simple)
# XXX what if this supply line's currency does not match the default accounting currency ?
resource = brain.getResourceValue()
base_price = None
supply_line = getattr(resource, supply_line_id, None)
if supply_line is not None:
base_price = supply_line.getBasePrice()
priced_quantity = supply_line.getPricedQuantity()
if priced_quantity and supply_line.getQuantityUnit():
priced_quantity = resource.convertQuantity(
priced_quantity,
supply_line.getQuantityUnit(),
resource.getQuantityUnit())
if base_price is not None:
base_price /= priced_quantity
if base_price is None:
return None
return brain.inventory * base_price
if inventory_valuation_method:
supply_line_id_mapping = {
'default_purchase_price': 'default_psl',
'default_internal_price': 'default_isl',
'default_sale_price': 'default_ssl',
}
return getPriceFromDefaultSupplyLine(
context,
supply_line_id_mapping[inventory_valuation_method],
)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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>inventory_valuation_method=\'\', **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>InventoryListBrain_getInventoryValuatedTotalPrice</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