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
Levin Zimmermann
erp5
Commits
9f1aa3bb
Commit
9f1aa3bb
authored
May 11, 2016
by
Jérome Perrin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SimulationTool: implement group_by_time_interval_list
parent
5016f968
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
315 additions
and
3 deletions
+315
-3
product/ERP5/Tool/SimulationTool.py
product/ERP5/Tool/SimulationTool.py
+14
-2
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.sql
...tem/portal_skins/erp5_core/Resource_zGetInventoryList.sql
+73
-0
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.xml
...tem/portal_skins/erp5_core/Resource_zGetInventoryList.xml
+2
-1
product/ERP5/tests/testInventoryAPI.py
product/ERP5/tests/testInventoryAPI.py
+226
-0
No files found.
product/ERP5/Tool/SimulationTool.py
View file @
9f1aa3bb
...
@@ -599,6 +599,7 @@ class SimulationTool(BaseTool):
...
@@ -599,6 +599,7 @@ class SimulationTool(BaseTool):
group_by_function_category
=
0
,
group_by_function_category
=
0
,
group_by_function_category_strict_membership
=
0
,
group_by_function_category_strict_membership
=
0
,
group_by_date
=
0
,
group_by_date
=
0
,
group_by_time_interval_list
=
(),
# sort_on
# sort_on
sort_on
=
None
,
sort_on
=
None
,
group_by
=
None
,
group_by
=
None
,
...
@@ -980,6 +981,14 @@ class SimulationTool(BaseTool):
...
@@ -980,6 +981,14 @@ class SimulationTool(BaseTool):
new_kw
[
'related_key_select_expression_list'
]
=
\
new_kw
[
'related_key_select_expression_list'
]
=
\
related_key_select_expression_list
related_key_select_expression_list
for
slot_index
,
time_sequence
in
enumerate
(
group_by_time_interval_list
):
if
not
(
time_sequence
.
get
(
'from_date'
)
or
time_sequence
.
get
(
'at_date'
)
or
time_sequence
.
get
(
'to_date'
)):
raise
ValueError
(
"Invalid time sequence {slot_index}: {time_sequence!r}"
.
format
(
slot_index
=
slot_index
,
time_sequence
=
time_sequence
,
))
sql_kw
[
'group_by_time_interval_list'
]
=
group_by_time_interval_list
return
sql_kw
,
new_kw
return
sql_kw
,
new_kw
#######################################################
#######################################################
...
@@ -1189,6 +1198,7 @@ class SimulationTool(BaseTool):
...
@@ -1189,6 +1198,7 @@ class SimulationTool(BaseTool):
group_by_section_category
=
0
,
group_by_section_category
=
0
,
group_by_section_category_strict_membership
=
0
,
group_by_section_category_strict_membership
=
0
,
group_by_resource
=
None
,
group_by_resource
=
None
,
group_by_time_interval_list
=
(),
group_by
=
None
,
group_by
=
None
,
**
ignored
):
**
ignored
):
"""
"""
...
@@ -1208,7 +1218,8 @@ class SimulationTool(BaseTool):
...
@@ -1208,7 +1218,8 @@ class SimulationTool(BaseTool):
group_by_function
or
group_by_mirror_section
or
group_by_payment
or
\
group_by_function
or
group_by_mirror_section
or
group_by_payment
or
\
group_by_sub_variation
or
group_by_variation
or
\
group_by_sub_variation
or
group_by_variation
or
\
group_by_movement
or
group_by_date
or
group_by_section_category
or
\
group_by_movement
or
group_by_date
or
group_by_section_category
or
\
group_by_section_category_strict_membership
:
group_by_section_category_strict_membership
or
\
group_by_time_interval_list
:
if
group_by_resource
is
None
:
if
group_by_resource
is
None
:
group_by_resource
=
1
group_by_resource
=
1
new_group_by_dict
[
'group_by_resource'
]
=
group_by_resource
new_group_by_dict
[
'group_by_resource'
]
=
group_by_resource
...
@@ -1315,7 +1326,8 @@ class SimulationTool(BaseTool):
...
@@ -1315,7 +1326,8 @@ class SimulationTool(BaseTool):
# Get cached data
# Get cached data
if
getattr
(
self
,
"Resource_zGetInventoryCacheResult"
,
None
)
is
not
None
and
\
if
getattr
(
self
,
"Resource_zGetInventoryCacheResult"
,
None
)
is
not
None
and
\
optimisation__
and
(
not
kw
.
get
(
'from_date'
))
and
\
optimisation__
and
(
not
kw
.
get
(
'from_date'
))
and
\
'transformed_resource'
not
in
kw
:
'transformed_resource'
not
in
kw
\
and
"group_by_time_interval_list"
not
in
kw
:
# Here is the different kind of date
# Here is the different kind of date
# from_date : >=
# from_date : >=
# to_date : <
# to_date : <
...
...
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.sql
View file @
9f1aa3bb
<
dtml
-
if
group_by_time_interval_list
>
SELECT
slots
.
time_interval_index
,
q
.
*
FROM
(
</
dtml
-
if
>
SELECT
SELECT
<
dtml
-
if
expr
=
"precision is not None"
>
<
dtml
-
if
expr
=
"precision is not None"
>
SUM
(
ROUND
(
<
dtml
-
var
stock_table_id
>
.
quantity
SUM
(
ROUND
(
<
dtml
-
var
stock_table_id
>
.
quantity
...
@@ -56,6 +59,8 @@ SELECT
...
@@ -56,6 +59,8 @@ SELECT
COUNT
(
DISTINCT
<
dtml
-
var
stock_table_id
>
.
uid
)
AS
stock_uid
,
COUNT
(
DISTINCT
<
dtml
-
var
stock_table_id
>
.
uid
)
AS
stock_uid
,
MAX
(
<
dtml
-
var
stock_table_id
>
.
date
)
AS
date
MAX
(
<
dtml
-
var
stock_table_id
>
.
date
)
AS
date
</
dtml
-
if
>
</
dtml
-
if
>
<
dtml
-
if
group_by_time_interval_list
>
,
time_interval_index
as
_time_interval_index
</
dtml
-
if
>
<
dtml
-
if
select_expression
>
,
<
dtml
-
var
select_expression
></
dtml
-
if
>
<
dtml
-
if
select_expression
>
,
<
dtml
-
var
select_expression
></
dtml
-
if
>
FROM
FROM
...
@@ -69,6 +74,55 @@ FROM
...
@@ -69,6 +74,55 @@ FROM
</
dtml
-
if
>
</
dtml
-
if
>
</
dtml
-
in
>
</
dtml
-
in
>
,
<
dtml
-
var
stock_table_id
>
,
<
dtml
-
var
stock_table_id
>
<
dtml
-
if
group_by_time_interval_list
>
RIGHT
JOIN
(
<
dtml
-
in
prefix
=
"time_interval"
expr
=
"_.list(_.enumerate(group_by_time_interval_list))"
>
SELECT
<
dtml
-
sqlvar
expr
=
"time_interval_key"
type
=
"int"
>
time_interval_index
,
<
dtml
-
sqlvar
expr
=
"time_interval_item.get('from_date')"
type
=
"datetime"
optional
>
time_interval_from_date
,
<
dtml
-
sqlvar
expr
=
"time_interval_item.get('at_date')"
type
=
"datetime"
optional
>
time_interval_at_date
,
<
dtml
-
sqlvar
expr
=
"time_interval_item.get('to_date')"
type
=
"datetime"
optional
>
time_interval_to_date
<
dtml
-
unless
time_interval_end
>
UNION
ALL
</
dtml
-
unless
>
</
dtml
-
in
>
)
slots
ON
<
dtml
-
if
group_by_time_interval_list
>
(
(
time_interval_from_date
is
not
null
AND
(
time_interval_at_date
is
not
null
AND
GREATEST
(
`stock`
.
`date`
,
`stock`
.
`mirror_date`
)
>=
time_interval_from_date
AND
LEAST
(
`stock`
.
`date`
,
`stock`
.
`mirror_date`
)
<=
time_interval_at_date
)
OR
(
(
time_interval_to_date
is
not
null
AND
GREATEST
(
`stock`
.
`date`
,
`stock`
.
`mirror_date`
)
>=
time_interval_from_date
AND
LEAST
(
`stock`
.
`date`
,
`stock`
.
`mirror_date`
)
<
time_interval_to_date
)
OR
(
GREATEST
(
`stock`
.
`date`
,
`stock`
.
`mirror_date`
)
>=
time_interval_from_date
AND
time_interval_at_date
is
null
AND
time_interval_to_date
is
null
)
)
)
OR
(
time_interval_from_date
is
null
AND
(
(
time_interval_at_date
is
not
null
AND
(
LEAST
(
`stock`
.
`date`
,
`stock`
.
`mirror_date`
)
<=
time_interval_at_date
)
)
OR
LEAST
(
`stock`
.
`date`
,
`stock`
.
`mirror_date`
)
<
time_interval_to_date
)
)
)
<
dtml
-
else
>
(
(
time_interval_from_date
is
null
OR
stock
.
date
>=
time_interval_from_date
)
AND
(
time_interval_at_date
is
null
OR
stock
.
date
<=
time_interval_at_date
)
AND
(
time_interval_to_date
is
null
OR
stock
.
date
<
time_interval_to_date
)
)
</
dtml
-
if
>
</
dtml
-
if
>
</
dtml
-
if
>
</
dtml
-
if
>
<
dtml
-
if
quantity_unit_uid
>
<
dtml
-
comment
>
XXX
quantity
unit
conversion
will
not
work
when
using
implict_join
=
False
</
dtml
-
comment
>
<
dtml
-
if
quantity_unit_uid
>
<
dtml
-
comment
>
XXX
quantity
unit
conversion
will
not
work
when
using
implict_join
=
False
</
dtml
-
comment
>
LEFT
JOIN
quantity_unit_conversion
ON
LEFT
JOIN
quantity_unit_conversion
ON
...
@@ -116,9 +170,28 @@ WHERE
...
@@ -116,9 +170,28 @@ WHERE
<
dtml
-
if
group_by_expression
>
<
dtml
-
if
group_by_expression
>
GROUP
BY
GROUP
BY
<
dtml
-
if
transformed_uid
>
transformation
.
transformed_uid
,
</
dtml
-
if
>
<
dtml
-
if
transformed_uid
>
transformation
.
transformed_uid
,
</
dtml
-
if
>
<
dtml
-
if
group_by_time_interval_list
>
time_interval_index
,
</
dtml
-
if
>
<
dtml
-
var
group_by_expression
>
<
dtml
-
var
group_by_expression
>
</
dtml
-
if
>
</
dtml
-
if
>
<
dtml
-
if
order_by_expression
>
<
dtml
-
if
order_by_expression
>
ORDER
BY
ORDER
BY
<
dtml
-
var
order_by_expression
>
<
dtml
-
var
order_by_expression
>
<
dtml
-
else
>
<
dtml
-
if
group_by_time_interval_list
>
ORDER
BY
time_interval_index
</
dtml
-
if
>
</
dtml
-
if
>
<
dtml
-
if
group_by_time_interval_list
>
)
q
RIGHT
JOIN
(
<
dtml
-
in
prefix
=
"time_interval"
expr
=
"_.list(_.enumerate(group_by_time_interval_list))"
>
SELECT
<
dtml
-
sqlvar
expr
=
"time_interval_key"
type
=
"int"
>
time_interval_index
,
<
dtml
-
sqlvar
expr
=
"time_interval_item.get('from_date')"
type
=
"datetime"
optional
>
time_interval_from_date
,
<
dtml
-
sqlvar
expr
=
"time_interval_item.get('at_date')"
type
=
"datetime"
optional
>
time_interval_at_date
,
<
dtml
-
sqlvar
expr
=
"time_interval_item.get('to_date')"
type
=
"datetime"
optional
>
time_interval_to_date
<
dtml
-
unless
time_interval_end
>
UNION
ALL
</
dtml
-
unless
>
</
dtml
-
in
>
)
slots
ON
(
q
.
_time_interval_index
=
slots
.
time_interval_index
)
</
dtml
-
if
>
</
dtml
-
if
>
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.xml
View file @
9f1aa3bb
...
@@ -46,7 +46,8 @@ convert_quantity_result\n
...
@@ -46,7 +46,8 @@ convert_quantity_result\n
quantity_unit_uid\n
quantity_unit_uid\n
stock_table_id=stock\n
stock_table_id=stock\n
transformed_uid\n
transformed_uid\n
transformed_variation_text
</string>
</value>
transformed_variation_text\n
group_by_time_interval_list:list
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
cache_time_
</string>
</key>
<key>
<string>
cache_time_
</string>
</key>
...
...
product/ERP5/tests/testInventoryAPI.py
View file @
9f1aa3bb
...
@@ -1267,6 +1267,232 @@ class TestInventoryList(InventoryAPITestCase):
...
@@ -1267,6 +1267,232 @@ class TestInventoryList(InventoryAPITestCase):
self
.
assertEqual
([
r
.
inventory
for
r
in
inventory_list
self
.
assertEqual
([
r
.
inventory
for
r
in
inventory_list
if
r
.
strict_use_uid
==
use
.
use1
.
use12
.
getUid
()],
[
11
])
if
r
.
strict_use_uid
==
use
.
use1
.
use12
.
getUid
()],
[
11
])
def
test_group_by_time_interval
(
self
):
getInventoryList
=
self
.
getSimulationTool
().
getInventoryList
# Create 3 groups of movements:
self
.
_makeMovement
(
quantity
=
1
,
start_date
=
DateTime
(
'2016/01/01'
))
self
.
_makeMovement
(
quantity
=
3
,
start_date
=
DateTime
(
'2016/02/01'
))
self
.
_makeMovement
(
quantity
=
5
,
start_date
=
DateTime
(
'2016/02/02'
))
self
.
_makeMovement
(
quantity
=
7
,
start_date
=
DateTime
(
'2016/03/01'
))
# Create "noise" movement that we should not select
self
.
_makeMovement
(
quantity
=
10
,
start_date
=
DateTime
(
'2016/02/01'
),
destination_value
=
self
.
portal
.
organisation_module
.
newContent
())
inventory_list
=
getInventoryList
(
node_uid
=
self
.
node
.
getUid
(),
group_by_time_interval_list
=
[
{
'at_date'
:
DateTime
(
'2016/01/01'
).
latestTime
()
},
{
'from_date'
:
DateTime
(
'2016/02/01'
),
'to_date'
:
DateTime
(
'2016/03/01'
)
},
{
'from_date'
:
DateTime
(
'2016/03/01'
)
},
])
# by default, time sequence are returned sorted by keys.
self
.
assertEqual
(
3
,
len
(
inventory_list
))
self
.
assertEqual
(
1
,
inventory_list
[
0
].
total_quantity
)
self
.
assertEqual
(
0
,
inventory_list
[
0
].
time_interval_index
)
self
.
assertEqual
(
3
+
5
,
inventory_list
[
1
].
total_quantity
)
self
.
assertEqual
(
1
,
inventory_list
[
1
].
time_interval_index
)
self
.
assertEqual
(
7
,
inventory_list
[
2
].
total_quantity
)
self
.
assertEqual
(
2
,
inventory_list
[
2
].
time_interval_index
)
# now using all combinations of from_date, at_date & to_date
inventory_list
=
getInventoryList
(
node_uid
=
self
.
node
.
getUid
(),
group_by_time_interval_list
=
[
{
'at_date'
:
DateTime
(
'2016/01/01'
).
latestTime
()
},
{
'to_date'
:
DateTime
(
'2016/01/02'
)
},
# equivalent to above
{
'from_date'
:
DateTime
(
'2016/02/01'
),
'at_date'
:
DateTime
(
'2016/02/29'
).
latestTime
()
},
{
'from_date'
:
DateTime
(
'2016/02/01'
),
'to_date'
:
DateTime
(
'2016/03/01'
)
},
{
'from_date'
:
DateTime
(
'2016/03/01'
)
},
])
self
.
assertEqual
(
[
1
,
1
,
3
+
5
,
3
+
5
,
7
],
[
brain
.
inventory
for
brain
in
inventory_list
],
)
def
test_group_by_time_interval_empty_slots_are_returned
(
self
):
getInventoryList
=
self
.
getSimulationTool
().
getInventoryList
self
.
_makeMovement
(
title
=
"M1"
,
quantity
=
3
,
start_date
=
DateTime
(
'2016/01/01'
))
self
.
_makeMovement
(
title
=
"M2"
,
quantity
=
5
,
start_date
=
DateTime
(
'2016/02/01'
))
inventory_list
=
getInventoryList
(
node_uid
=
self
.
node
.
getUid
(),
group_by_time_interval_list
=
[
# before M1 -> empty
{
'at_date'
:
DateTime
(
'2001/01/01'
).
latestTime
()
},
{
'to_date'
:
DateTime
(
'2001/01/01'
)
},
{
'from_date'
:
DateTime
(
'1999/01/01'
),
'to_date'
:
DateTime
(
'2001/01/01'
)
},
{
'from_date'
:
DateTime
(
'1999/01/01'
),
'at_date'
:
DateTime
(
'2001/01/01'
)
},
# selecting M1
{
'from_date'
:
DateTime
(
'2016/01/01'
),
'to_date'
:
DateTime
(
'2016/01/02'
)
},
# between M1 & M2 -> empty
{
'from_date'
:
DateTime
(
'2016/01/02'
),
'at_date'
:
DateTime
(
'2001/01/03'
)
},
{
'from_date'
:
DateTime
(
'2016/01/02'
),
'to_date'
:
DateTime
(
'2001/01/03'
)
},
# selecting M2
{
'from_date'
:
DateTime
(
'2016/02/01'
),
'to_date'
:
DateTime
(
'2016/02/03'
)
},
# after M2 -> empty
{
'from_date'
:
DateTime
(
'2016/02/03'
),
'to_date'
:
DateTime
(
'2016/02/04'
)
},
{
'from_date'
:
DateTime
(
'2016/02/03'
),
'at_date'
:
DateTime
(
'2001/02/04'
)
},
{
'from_date'
:
DateTime
(
'2016/02/03'
)
},
])
self
.
assertEqual
(
[
None
,
None
,
None
,
None
,
3
,
None
,
None
,
5
,
None
,
None
,
None
,
],
[
x
.
inventory
for
x
in
inventory_list
],
)
def
test_group_by_time_interval_and_other_group_by
(
self
):
# group_by_time_interval_list can be used with other "group by" parameters
getInventoryList
=
self
.
getSimulationTool
().
getInventoryList
another_resource
=
self
.
_makeResource
()
self
.
_makeMovement
(
title
=
"M1"
,
quantity
=
5
,
start_date
=
DateTime
(
'2016/01/01'
))
self
.
_makeMovement
(
title
=
"M2"
,
quantity
=
7
,
start_date
=
DateTime
(
'2016/01/03'
))
self
.
_makeMovement
(
title
=
"M3"
,
quantity
=
11
,
resource_value
=
another_resource
,
start_date
=
DateTime
(
'2016/01/03'
))
self
.
assertEqual
(
{
(
0
,
self
.
resource
.
uid
):
5
,
(
1
,
self
.
resource
.
uid
):
7
,
(
1
,
another_resource
.
uid
):
11
,
},
{(
brain
.
time_interval_index
,
brain
.
resource_uid
):
brain
.
inventory
for
brain
in
getInventoryList
(
node_uid
=
self
.
node
.
getUid
(),
group_by_resource
=
True
,
group_by_time_interval_list
=
[
{
'at_date'
:
DateTime
(
'2016/01/02'
)
},
{
'from_date'
:
DateTime
(
'2016/01/02'
)
},
])},
)
def
test_group_by_time_interval_invalid_inputs
(
self
):
getInventoryList
=
self
.
getSimulationTool
().
getInventoryList
self
.
_makeMovement
(
title
=
"M1"
,
quantity
=
3
,
start_date
=
DateTime
(
'2016/01/02'
))
# no from_date, at_date or to_date on a slot raise a ValueError
with
self
.
assertRaises
(
ValueError
):
getInventoryList
(
node_uid
=
self
.
node
.
getUid
(),
group_by_time_interval_list
=
[{}],
)
# intervals where start_date > stop_date are valid, but select nothing
self
.
assertEqual
(
{
0
:
None
},
{
brain
.
time_interval_index
:
brain
.
inventory
for
brain
in
getInventoryList
(
node_uid
=
self
.
node
.
getUid
(),
group_by_time_interval_list
=
[{
'from_date'
:
DateTime
(
'2016/01/03'
),
'at_date'
:
DateTime
(
'2016/01/01'
)
}])
},
)
self
.
assertEqual
(
{
0
:
None
},
{
brain
.
time_interval_index
:
brain
.
inventory
for
brain
in
getInventoryList
(
node_uid
=
self
.
node
.
getUid
(),
group_by_time_interval_list
=
[{
'from_date'
:
DateTime
(
'2016/01/03'
),
'to_date'
:
DateTime
(
'2016/01/01'
)
}])
},
)
def
test_OmitInputOmitOutput
(
self
):
def
test_OmitInputOmitOutput
(
self
):
getInventoryList
=
self
.
getSimulationTool
().
getInventoryList
getInventoryList
=
self
.
getSimulationTool
().
getInventoryList
self
.
_makeMovement
(
quantity
=
1
,
price
=
1
)
self
.
_makeMovement
(
quantity
=
1
,
price
=
1
)
...
...
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