Commit abe20f8c authored by Jérome Perrin's avatar Jérome Perrin

trade: support Inventory Valuation in Stock Report

Using default {purchase,internal,sale} price
parent e6f788f7
......@@ -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>
......@@ -102,6 +102,14 @@ class TestTradeReports(ERP5ReportTestCase):
title=group_id,
reference=group_id,
id=group_id)
# currencies
if not self.portal.currency_module.has_key('EUR'):
self.portal.currency_module.newContent(
portal_type='Currency',
id='EUR',
base_unit_quantity=0.01,
).validate()
# create organisations (with no organisation member of g3)
if not self.organisation_module.has_key('Organisation_1'):
self.portal.organisation_module.newContent(
......@@ -111,6 +119,7 @@ class TestTradeReports(ERP5ReportTestCase):
id='Organisation_1',
group='g1',
site='demo_site_A',
price_currency_value=self.portal.currency_module.EUR,
default_email_coordinate_text='organisation1@example.com',
default_telephone_coordinate_text='11111',
default_address_street_address='1 Organisation Street',
......@@ -157,7 +166,10 @@ class TestTradeReports(ERP5ReportTestCase):
id='product_A',
title='product_A',
reference='ref 2',
quantity_unit='g'
quantity_unit='g',
default_purchase_supply_line_base_price=3,
default_internal_supply_line_base_price=5,
default_sale_supply_line_base_price=7,
)
if not module.has_key('product_C'):
module.newContent(
......@@ -1400,6 +1412,94 @@ class TestTradeReports(ERP5ReportTestCase):
self.assertEqual(0, len(data_line_list))
def testStockReport_valuation_method_default_default_purchase_price(self):
self._createConfirmedSalePackingListForStockReportTest()
request = self.portal.REQUEST
request.form['at_date'] = DateTime(2007, 3, 3)
request.form['node_category'] = 'site/demo_site_A'
request.form['simulation_period'] = 'future'
request.form['inventory_valuation_method'] = 'default_purchase_price'
line_list = self.portal.inventory_module.Base_viewStockReportBySite.listbox.\
get_value('default',
render_format='list', REQUEST=self.portal.REQUEST)
data_line_list = [l for l in line_list if l.isDataLine()]
self.assertEqual(1, len(data_line_list))
data_line = data_line_list[0]
self.assertEqual(
data_line.column_id_list,
['resource_title', 'resource_reference', 'variation_category_item_list', 'inventory', 'quantity_unit', 'total_price'])
self.checkLineProperties(
data_line_list[0],
resource_title='product_A',
resource_reference='ref 2',
variation_category_item_list=[],
inventory=1,
quantity_unit='G',
total_price=3,
)
# listbox_total_price is an editable field using this for precision
self.assertEqual(self.portal.REQUEST.get('precision'), 2)
def testStockReport_valuation_method_default_default_internal_price(self):
self._createConfirmedSalePackingListForStockReportTest()
request = self.portal.REQUEST
request.form['at_date'] = DateTime(2007, 3, 3)
request.form['node_category'] = 'site/demo_site_A'
request.form['simulation_period'] = 'future'
request.form['inventory_valuation_method'] = 'default_internal_price'
line_list = self.portal.inventory_module.Base_viewStockReportBySite.listbox.\
get_value('default',
render_format='list', REQUEST=self.portal.REQUEST)
data_line_list = [l for l in line_list if l.isDataLine()]
self.assertEqual(1, len(data_line_list))
data_line = data_line_list[0]
self.assertEqual(
data_line.column_id_list,
['resource_title', 'resource_reference', 'variation_category_item_list', 'inventory', 'quantity_unit', 'total_price'])
self.checkLineProperties(
data_line_list[0],
resource_title='product_A',
resource_reference='ref 2',
variation_category_item_list=[],
inventory=1,
quantity_unit='G',
total_price=5,
)
def testStockReport_valuation_method_default_default_sales_price(self):
self._createConfirmedSalePackingListForStockReportTest()
request = self.portal.REQUEST
request.form['at_date'] = DateTime(2007, 3, 3)
request.form['node_category'] = 'site/demo_site_A'
request.form['simulation_period'] = 'future'
request.form['inventory_valuation_method'] = 'default_sale_price'
line_list = self.portal.inventory_module.Base_viewStockReportBySite.listbox.\
get_value('default',
render_format='list', REQUEST=self.portal.REQUEST)
data_line_list = [l for l in line_list if l.isDataLine()]
self.assertEqual(1, len(data_line_list))
data_line = data_line_list[0]
self.assertEqual(
data_line.column_id_list,
['resource_title', 'resource_reference', 'variation_category_item_list', 'inventory', 'quantity_unit', 'total_price'])
self.checkLineProperties(
data_line_list[0],
resource_title='product_A',
resource_reference='ref 2',
variation_category_item_list=[],
inventory=1,
quantity_unit='G',
total_price=7,
)
def test_Folder_generateWorkflowReport(self):
# Create sales orders
......
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