Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Léo-Paul Géneau
erp5
Commits
d496e223
Commit
d496e223
authored
Apr 28, 2023
by
Sebastien Robin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Inventory: add lowest_value_test for getInventoryAssetPrice
parent
169711ad
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
110 additions
and
28 deletions
+110
-28
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetAssetPrice.sql
...teItem/portal_skins/erp5_core/Resource_zGetAssetPrice.sql
+49
-15
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetAssetPrice.xml
...teItem/portal_skins/erp5_core/Resource_zGetAssetPrice.xml
+3
-1
product/ERP5/bootstrap/erp5_core/ToolComponentTemplateItem/portal_components/tool.erp5.SimulationTool.py
...emplateItem/portal_components/tool.erp5.SimulationTool.py
+13
-1
product/ERP5/tests/testInventoryAPI.py
product/ERP5/tests/testInventoryAPI.py
+45
-11
No files found.
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetAssetPrice.sql
View file @
d496e223
...
...
@@ -9,7 +9,7 @@ during each period.
Then perform a weighted average over all periods.
*/
set
@
total_asset_price
=
0
,
@
total_quantity
=
0
set
@
total_asset_price
=
0
.
0
,
@
total_quantity
=
0
.
0
<
dtml
-
var
sql_delimiter
>
select
byperiod
.
*
,
...
...
@@ -45,19 +45,28 @@ order by d_year<dtml-if "'Monthly' in valuation_method">, d_month</dtml-if>
Very similar to (Monthly)WeightedAverage except that we do not have to
split the timeframe / fold movements and simply perform a weighted average
on all single movements.
Parameter lowest_value_test compares latest price and Moving average price and takes the
smallest one.
*/
set
@
total_asset_price
=
0
,
@
total_quantity
=
0
SET
@
total_asset_price
=
0
,
@
total_quantity
=
0
,
@
latest_price
=
null
,
@
running_total_asset_price
=
0
<
dtml
-
var
sql_delimiter
>
select
(
@
incoming_total_price
:
=
IF
(
quantity
>
0
,
total_price
,
0
))
as
incoming_total_price
,
@
latest_price
:
=
IF
(
quantity
>
0
,
total_price
/
quantity
,
@
latest_price
)
as
latest_price
,
@
unit_price
:
=
((
@
total_asset_price
+@
incoming_total_price
)
/
(
@
total_quantity
+
GREATEST
(
0
,
quantity
)))
as
unit_price
,
(
@
total_asset_price
:
=
@
total_asset_price
+
(
@
running_
total_asset_price
:
=
@
running_
total_asset_price
+
@
incoming_total_price
+
LEAST
(
0
,
quantity
)
*
@
unit_price
)
as
total_asset_price
,
(
@
total_quantity
:
=@
total_quantity
+
quantity
)
as
dummy
LEAST
(
0
,
quantity
)
*
@
unit_price
)
as
running_total_asset_price
,
(
@
total_quantity
:
=@
total_quantity
+
quantity
)
as
dummy
,
<
dtml
-
if
"lowest_value_test"
>
(
@
total_asset_price
:
=
LEAST
(
@
running_total_asset_price
,
@
total_quantity
*@
latest_price
))
as
total_asset_price
<
dtml
-
else
>
(
@
total_asset_price
:
=
@
running_total_asset_price
)
as
total_asset_price
</
dtml
-
if
>
from
stock
,
catalog
where
...
...
@@ -93,6 +102,9 @@ Thus, each movement has a value of:
if @unbalanced_output is initialized to @total_output_quantity and reduced by
quantity at each step:
unbalanced_output=max(0, unbalanced_output-quantity)
Parameter lowest_value_test compares latest price and FIFO price and takes the
smallest one.
*/
SET
@
unbalanced_output
:
=
...
...
@@ -105,16 +117,27 @@ SET
AND
<
dtml
-
var
where_expression
>
),
0
),
@
total_asset_price
=
0
@
total_asset_price
=
0
.
0
,
@
running_total_asset_price
=
0
.
0
,
@
running_quantity
=
0
.
0
<
dtml
-
var
sql_delimiter
>
SELECT
(
@
total_asset_price
:
=@
total_asset_price
+
(
@
running_quantity
:
=@
running_quantity
+
GREATEST
(
0
,
quantity
-@
unbalanced_output
)
)
AS
running_quantity
,
(
@
running_total_asset_price
:
=@
running_total_asset_price
+
GREATEST
(
0
,
(
quantity
-@
unbalanced_output
)
*
total_price
/
quantity
)
)
AS
total_asset_price
,
(
@
unbalanced_output
:
=
GREATEST
(
0
,
@
unbalanced_output
-
quantity
))
as
dummy
)
AS
running_total_asset_price
,
(
@
unbalanced_output
:
=
GREATEST
(
0
,
@
unbalanced_output
-
quantity
))
as
dummy
,
<
dtml
-
if
"lowest_value_test"
>
(
@
total_asset_price
:
=
LEAST
(
@
running_total_asset_price
,
@
running_quantity
*
total_price
/
quantity
))
as
total_asset_price
<
dtml
-
else
>
(
@
total_asset_price
:
=
@
running_total_asset_price
)
as
total_asset_price
</
dtml
-
if
>
FROM
stock
,
catalog
WHERE
...
...
@@ -148,15 +171,26 @@ until we reach an incoming movement. Then:
movement got out of inventory between t=current and T=END. These items are not
present in the final inventory and can be discarded.
@unbalanced_inventory=@unbalanced_inventory - quantity
Parameter lowest_value_test compares latest price and FILO price and takes the
smallest one.
*/
SET
@
unbalanced_output
=
0
,
@
total_asset_price
=
0
SET
@
unbalanced_output
=
0
.
0
,
@
total_asset_price
=
0
.
0
,
@
running_total_asset_price
=
0
.
0
,
@
latest_price
=
null
,
@
running_quantity
=
0
.
0
<
dtml
-
var
sql_delimiter
>
SELECT
(
@
total_asset_price
:
=@
total_asset_price
+
(
IF
(
quantity
<=
0
,
@
latest_price
,
@
latest_price
:
=
IFNULL
(
@
latest_price
,
total_price
/
quantity
)))
as
dummy_latest_price
,
(
@
running_total_asset_price
:
=@
running_total_asset_price
+
IF
(
quantity
<=
0
,
0
,
total_price
/
quantity
*
GREATEST
(
0
,
quantity
-@
unbalanced_output
)))
as
total_asset_price
,
(
@
unbalanced_output
:
=
GREATEST
(
0
,
@
unbalanced_output
-
quantity
))
as
dummy
total_price
/
quantity
*
GREATEST
(
0
,
quantity
-@
unbalanced_output
)))
as
running_total_asset_price
,
(
@
unbalanced_output
:
=
GREATEST
(
0
,
@
unbalanced_output
-
quantity
))
as
dummy
,
(
@
running_quantity
:
=@
running_quantity
+
quantity
)
as
running_quantity
,
<
dtml
-
if
"lowest_value_test"
>
(
@
total_asset_price
:
=
LEAST
(
@
running_total_asset_price
,
@
running_quantity
*@
latest_price
))
as
total_asset_price
<
dtml
-
else
>
(
@
total_asset_price
:
=
@
running_total_asset_price
)
as
total_asset_price
</
dtml
-
if
>
FROM
stock
,
catalog
WHERE
...
...
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetAssetPrice.xml
View file @
d496e223
...
...
@@ -9,7 +9,9 @@
<item>
<key>
<string>
arguments_src
</string>
</key>
<value>
<string>
where_expression\n
valuation_method
</string>
</value>
valuation_method\n
lowest_value_test\n
</string>
</value>
</item>
<item>
<key>
<string>
connection_id
</string>
</key>
...
...
product/ERP5/bootstrap/erp5_core/ToolComponentTemplateItem/portal_components/tool.erp5.SimulationTool.py
View file @
d496e223
...
...
@@ -1821,6 +1821,7 @@ class SimulationTool(BaseTool):
def
getInventoryAssetPrice
(
self
,
src__
=
0
,
simulation_period
=
''
,
valuation_method
=
None
,
lowest_value_test
=
False
,
**
kw
):
"""
Same thing as getInventory but returns an asset
...
...
@@ -1836,6 +1837,10 @@ class SimulationTool(BaseTool):
MovingAverage
When using a specific valuation method, a resource_uid is expected
as well as one of (section_uid or node_uid).
Parameter lowest_value_price compares latest price with price
calculated with valuation_method and takes the smallest one. This
is useful for accountants to avoid overestimating the price of a stock.
"""
if
valuation_method
is
None
:
method
=
getattr
(
self
,
'get%sInventoryList'
%
simulation_period
)
...
...
@@ -1857,6 +1862,9 @@ class SimulationTool(BaseTool):
if
valuation_method
not
in
(
'Fifo'
,
'Filo'
,
'WeightedAverage'
,
'MonthlyWeightedAverage'
,
'MovingAverage'
):
raise
ValueError
(
"Invalid valuation method: %s"
%
valuation_method
)
if
lowest_value_test
and
valuation_method
not
in
(
'Fifo'
,
'Filo'
,
'MovingAverage'
):
raise
NotImplementedError
(
'lowest_value_test not implemented'
)
assert
'node_uid'
in
kw
or
'section_uid'
in
kw
sql_kw
=
self
.
_generateSQLKeywordDict
(
**
kw
)
...
...
@@ -1868,6 +1876,7 @@ class SimulationTool(BaseTool):
result
=
self
.
Resource_zGetAssetPrice
(
valuation_method
=
valuation_method
,
lowest_value_test
=
lowest_value_test
,
src__
=
src__
,
**
sql_kw
)
...
...
@@ -1875,7 +1884,10 @@ class SimulationTool(BaseTool):
return
result
if
len
(
result
)
>
0
:
return
result
[
-
1
].
total_asset_price
asset_price
=
result
[
-
1
].
total_asset_price
if
asset_price
:
asset_price
=
float
(
asset_price
)
return
asset_price
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getCurrentInventoryAssetPrice'
)
...
...
product/ERP5/tests/testInventoryAPI.py
View file @
d496e223
...
...
@@ -1516,20 +1516,22 @@ class TestInventoryList(InventoryAPITestCase):
def
test_inventory_asset_price
(
self
):
# examples from http://accountinginfo.com/study/inventory/inventory-120.htm
# # total quantity
movement_list
=
[
(
1
,
"Beginning Inventory"
,
-
700
,
10
),
(
3
,
"Purchase"
,
-
100
,
12
),
(
8
,
"Sale"
,
500
,
None
),
(
15
,
"Purchase"
,
-
600
,
14
),
(
19
,
"Purchase"
,
-
200
,
15
),
(
25
,
"Sale"
,
400
,
None
),
(
27
,
"Sale"
,
100
,
None
),
(
1
,
"Beginning Inventory"
,
-
700
,
10
),
# 700
(
3
,
"Purchase"
,
-
100
,
12
),
# 800
(
8
,
"Sale"
,
500
,
None
),
# 300
(
15
,
"Purchase"
,
-
600
,
14
),
# 900
(
19
,
"Purchase"
,
-
200
,
15
),
# 1100
(
25
,
"Sale"
,
400
,
None
),
# 700
(
27
,
"Sale"
,
100
,
None
),
# 600
]
resource
=
self
.
getProductModule
().
newContent
(
title
=
'My resource'
,
portal_type
=
'Product'
)
for
m
in
movement_list
:
self
.
_makeMovement
(
resource_value
=
resource
,
def
makeMovementList
(
movement_list
):
for
m
in
movement_list
:
self
.
_makeMovement
(
resource_value
=
resource
,
source_value
=
self
.
node
,
destination_value
=
self
.
mirror_node
,
start_date
=
DateTime
(
'2000/1/%d 12:00 UTC'
%
m
[
0
]),
...
...
@@ -1537,19 +1539,51 @@ class TestInventoryList(InventoryAPITestCase):
quantity
=
m
[
2
],
price
=
m
[
3
],
)
makeMovementList
(
movement_list
)
simulation_tool
=
self
.
getSimulationTool
()
def
valuate
(
method
):
def
valuate
(
method
,
lowest_value_test
=
False
):
self
.
portal
.
person_module
.
log
(
simulation_tool
.
getInventoryAssetPrice
(
src__
=
1
,
valuation_method
=
method
,
resource_uid
=
resource
.
getUid
(),
node_uid
=
self
.
node
.
getUid
(),
lowest_value_test
=
lowest_value_test
))
r
=
simulation_tool
.
getInventoryAssetPrice
(
valuation_method
=
method
,
resource_uid
=
resource
.
getUid
(),
node_uid
=
self
.
node
.
getUid
())
node_uid
=
self
.
node
.
getUid
(),
lowest_value_test
=
lowest_value_test
)
return
round
(
r
)
self
.
assertEqual
(
7895
,
valuate
(
"MovingAverage"
))
self
.
assertEqual
(
7200
,
valuate
(
"Filo"
))
self
.
assertEqual
(
8600
,
valuate
(
"Fifo"
))
# latest purchase price is 15, total quantity is 600
# average price of 13.15, thus lowest value test change nothing
self
.
assertEqual
(
7895
,
valuate
(
"MovingAverage"
,
lowest_value_test
=
True
))
# average price of 12.15, thus lowest value test change nothing
self
.
assertEqual
(
7200
,
valuate
(
"Filo"
,
lowest_value_test
=
True
))
# average price of 14.33, thus lowest value test change nothing
self
.
assertEqual
(
8600
,
valuate
(
"Fifo"
,
lowest_value_test
=
True
))
movement_list
=
[
(
28
,
"Purchase"
,
-
100
,
11
),
# 700
(
29
,
"Sale"
,
100
,
None
),
# 600
]
makeMovementList
(
movement_list
)
self
.
assertEqual
(
7710
,
valuate
(
"MovingAverage"
))
self
.
assertEqual
(
7200
,
valuate
(
"Filo"
))
self
.
assertEqual
(
8300
,
valuate
(
"Fifo"
))
self
.
assertEqual
(
7710
,
valuate
(
"MovingAverage"
))
self
.
assertEqual
(
7200
,
valuate
(
"Filo"
))
# latest purchase price is 11, total quantity is 600
# average price of 12.85, thus lowest value test change value
self
.
assertEqual
(
6600
,
valuate
(
"MovingAverage"
,
lowest_value_test
=
True
))
# average price of 12, thus lowest value test change value
self
.
assertEqual
(
6600
,
valuate
(
"Filo"
,
lowest_value_test
=
True
))
# average price of 13.83, thus lowest value test change value
self
.
assertEqual
(
6600
,
valuate
(
"Fifo"
,
lowest_value_test
=
True
))
def
test_weighted_average_asset_price
(
self
):
def
h
(
quantity
,
total_price
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment