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
Xavier Thompson
erp5
Commits
7505e054
Commit
7505e054
authored
May 22, 2019
by
Xiaowu Zhang
Browse files
Options
Browse Files
Download
Plain Diff
erp5_invoicing: enable "Merge Deliveries" action on accounting module
/reviewed-on
nexedi/erp5!876
parents
17c0aa5a
d2f4dd44
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
236 additions
and
83 deletions
+236
-83
bt5/erp5_invoicing/ActionTemplateItem/portal_types/Accounting%20Transaction%20Module/merge_delivery_list.xml
...Accounting%20Transaction%20Module/merge_delivery_list.xml
+85
-0
bt5/erp5_invoicing/bt/template_action_path_list
bt5/erp5_invoicing/bt/template_action_path_list
+1
-0
bt5/erp5_simulation/SkinTemplateItem/portal_skins/erp5_simulation/DeliveryModule_mergeDeliveryList.py
...skins/erp5_simulation/DeliveryModule_mergeDeliveryList.py
+9
-6
product/ERP5/Tool/SimulationTool.py
product/ERP5/Tool/SimulationTool.py
+84
-77
product/ERP5/tests/testInvoice.py
product/ERP5/tests/testInvoice.py
+57
-0
No files found.
bt5/erp5_invoicing/ActionTemplateItem/portal_types/Accounting%20Transaction%20Module/merge_delivery_list.xml
0 → 100644
View file @
7505e054
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"ActionInformation"
module=
"Products.CMFCore.ActionInformation"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
action
</string>
</key>
<value>
<persistent>
<string
encoding=
"base64"
>
AAAAAAAAAAI=
</string>
</persistent>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
action_type/object_action
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
category
</string>
</key>
<value>
<string>
object_action
</string>
</value>
</item>
<item>
<key>
<string>
condition
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
icon
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
merge_delivery_list
</string>
</value>
</item>
<item>
<key>
<string>
permissions
</string>
</key>
<value>
<tuple>
<string>
Add portal content
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Action Information
</string>
</value>
</item>
<item>
<key>
<string>
priority
</string>
</key>
<value>
<float>
5.0
</float>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
Merge Deliveries
</string>
</value>
</item>
<item>
<key>
<string>
visible
</string>
</key>
<value>
<int>
1
</int>
</value>
</item>
</dictionary>
</pickle>
</record>
<record
id=
"2"
aka=
"AAAAAAAAAAI="
>
<pickle>
<global
name=
"Expression"
module=
"Products.CMFCore.Expression"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
text
</string>
</key>
<value>
<string>
string:${object_url}/DeliveryModule_mergeDeliveryList
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_invoicing/bt/template_action_path_list
View file @
7505e054
Accounting Transaction Module | merge_delivery_list
Invoice Line | price
Invoice Line | price
Invoice Line | quantity
Invoice Line | quantity
Invoice Line | view
Invoice Line | view
...
...
bt5/erp5_simulation/SkinTemplateItem/portal_skins/erp5_simulation/DeliveryModule_mergeDeliveryList.py
View file @
7505e054
...
@@ -5,12 +5,15 @@ delivery_list = []
...
@@ -5,12 +5,15 @@ delivery_list = []
for
o
in
object_list
:
for
o
in
object_list
:
delivery_list
.
append
(
o
)
delivery_list
.
append
(
o
)
Base_translateString
=
context
.
Base_translateString
if
len
(
delivery_list
)
<
2
:
if
len
(
delivery_list
)
<
2
:
ret_url
=
context
.
absolute_url
()
+
'/'
+
form_id
message
=
Base_translateString
(
'Please select more than one items.'
)
qs
=
'?portal_status_message=Please+select+more+than+one+items.'
else
:
else
:
ret_url
=
context
.
absolute_url
()
+
'/'
+
form_id
error_list
=
context
.
portal_simulation
.
mergeDeliveryList
(
delivery_list
)
qs
=
'?portal_status_message=Merged.'
if
not
error_list
:
context
.
portal_simulation
.
mergeDeliveryList
(
delivery_list
)
message
=
Base_translateString
(
'Merged.'
)
else
:
message
=
' '
.
join
([
str
(
x
)
for
x
in
error_list
])
return
REQUEST
.
RESPONSE
.
redirect
(
ret_url
+
qs
)
return
context
.
Base_redirect
(
'view'
,
keep_items
=
{
'portal_status_message'
:
message
}
)
product/ERP5/Tool/SimulationTool.py
View file @
7505e054
...
@@ -2647,7 +2647,7 @@ class SimulationTool(BaseTool):
...
@@ -2647,7 +2647,7 @@ class SimulationTool(BaseTool):
return
result
return
result
def
_findBuilderForDelivery
(
self
,
delivery
):
def
_findBuilderForDelivery
(
self
,
delivery
,
movement_portal_type
):
"""
"""
Find out the builder corresponding to a delivery by looking at the business process
Find out the builder corresponding to a delivery by looking at the business process
"""
"""
...
@@ -2655,7 +2655,8 @@ class SimulationTool(BaseTool):
...
@@ -2655,7 +2655,8 @@ class SimulationTool(BaseTool):
portal_type
=
delivery
.
getPortalType
()
portal_type
=
delivery
.
getPortalType
()
for
business_link
in
delivery
.
asComposedDocument
().
objectValues
(
portal_type
=
"Business Link"
):
for
business_link
in
delivery
.
asComposedDocument
().
objectValues
(
portal_type
=
"Business Link"
):
for
business_link_builder
in
business_link
.
getDeliveryBuilderValueList
():
for
business_link_builder
in
business_link
.
getDeliveryBuilderValueList
():
if
business_link_builder
.
getDeliveryPortalType
()
==
portal_type
:
if
business_link_builder
.
getDeliveryPortalType
()
==
portal_type
\
and
business_link_builder
.
getDeliveryLinePortalType
()
==
movement_portal_type
:
builder
=
business_link_builder
builder
=
business_link_builder
break
break
if
builder
is
not
None
:
if
builder
is
not
None
:
...
@@ -2681,87 +2682,93 @@ class SimulationTool(BaseTool):
...
@@ -2681,87 +2682,93 @@ class SimulationTool(BaseTool):
if
len
(
portal_type_set
)
!=
1
:
if
len
(
portal_type_set
)
!=
1
:
error_list
.
append
(
translateString
(
"Please select only deliveries of same type"
))
error_list
.
append
(
translateString
(
"Please select only deliveries of same type"
))
else
:
else
:
portal_type
,
=
portal_type_set
allowed_state_set
=
set
(
portal
.
getPortalReservedInventoryStateList
()
+
\
allowed_state_set
=
set
(
portal
.
getPortalReservedInventoryStateList
()
+
\
portal
.
getPortalFutureInventoryStateList
())
portal
.
getPortalFutureInventoryStateList
())
found_state_set
=
set
([
x
.
getSimulationState
()
for
x
in
delivery_list
])
found_state_set
=
set
([
x
.
getSimulationState
()
for
x
in
delivery_list
])
if
found_state_set
.
difference
(
allowed_state_set
):
if
found_state_set
.
difference
(
allowed_state_set
):
error_list
.
append
(
translateString
(
"Found delivery having unexpected status for merge"
))
error_list
.
append
(
translateString
(
"Found delivery having unexpected status for merge"
))
else
:
else
:
# Allow to call a script to do custom checking conditions before merge
movement_portal_type_set
=
set
()
main_delivery
=
delivery_list
[
0
]
for
delivery
in
delivery_list
:
check_merge_condition_method
=
main_delivery
.
_getTypeBasedMethod
(
"checkMergeConditionOnDeliveryList"
)
movement_portal_type_set
.
update
([
x
.
getPortalType
()
for
x
in
delivery
.
getMovementList
()])
if
check_merge_condition_method
is
not
None
:
if
len
(
movement_portal_type_set
)
!=
1
:
error_list
.
extend
(
check_merge_condition_method
(
delivery_list
=
delivery_list
))
error_list
.
append
(
translateString
(
"Please Select only movement of same type"
))
if
len
(
error_list
)
==
0
:
else
:
# so far so good
# Allow to call a script to do custom checking conditions before merge
# in delivery_list we have list of delivery to merge
main_delivery
=
delivery_list
[
0
]
simulation_movement_list
=
[]
check_merge_condition_method
=
main_delivery
.
_getTypeBasedMethod
(
"checkMergeConditionOnDeliveryList"
)
to_copy_delivery_line_list
=
[]
# for lines not coming from upper simulation, thus
if
check_merge_condition_method
is
not
None
:
# created by hand should be manually added to main
error_list
.
extend
(
check_merge_condition_method
(
delivery_list
=
delivery_list
))
# delivery since they are not coming from builder
if
len
(
error_list
)
==
0
:
for
delivery
in
delivery_list
:
# so far so good
line_id_to_delete_list
=
[]
# in delivery_list we have list of delivery to merge
for
movement
in
delivery
.
getMovementList
():
simulation_movement_list
=
[]
related_simulation_movement_list
=
movement
.
getDeliveryRelatedValueList
()
to_copy_delivery_line_list
=
[]
# for lines not coming from upper simulation, thus
for
simulation_movement
in
related_simulation_movement_list
:
# created by hand should be manually added to main
# if we are on a root applied rule directly, so in the case of
# delivery since they are not coming from builder
# a manually added line, we have to copy
for
delivery
in
delivery_list
:
# the simulation movement into to main delivery
line_id_to_delete_list
=
[]
if
simulation_movement
.
getParentValue
().
getParentValue
().
getId
()
==
"portal_simulation"
:
for
movement
in
delivery
.
getMovementList
():
# For manually added lines, make sure we have only one simulation movement
related_simulation_movement_list
=
movement
.
getDeliveryRelatedValueList
()
assert
len
(
related_simulation_movement_list
)
==
1
for
simulation_movement
in
related_simulation_movement_list
:
if
not
(
delivery
is
main_delivery
):
# if we are on a root applied rule directly, so in the case of
to_copy_delivery_line_list
.
append
(
movement
)
# a manually added line, we have to copy
else
:
# the simulation movement into to main delivery
simulation_movement
.
setDeliveryValue
(
None
)
if
simulation_movement
.
getParentValue
().
getParentValue
().
getId
()
==
"portal_simulation"
:
simulation_movement_list
.
append
(
simulation_movement
)
# For manually added lines, make sure we have only one simulation movement
# Since we keep the main delivery, we remove existing lines already
assert
len
(
related_simulation_movement_list
)
==
1
# coming from builder to let builder recreate them in the same time
if
not
(
delivery
is
main_delivery
):
# as other ones (to possibly merge lines also)
to_copy_delivery_line_list
.
append
(
movement
)
movement_id
=
movement
.
getId
()
else
:
if
delivery
is
main_delivery
and
not
(
movement_id
in
line_id_to_delete_list
):
simulation_movement
.
setDeliveryValue
(
None
)
line_id_to_delete_list
.
append
(
movement
.
getId
())
simulation_movement_list
.
append
(
simulation_movement
)
if
line_id_to_delete_list
:
# Since we keep the main delivery, we remove existing lines already
delivery
.
manage_delObjects
(
ids
=
line_id_to_delete_list
)
# coming from builder to let builder recreate them in the same time
# It is required to expand again simulation movement, because
# as other ones (to possibly merge lines also)
# we unlinked them from delivery, so it is possible that some
movement_id
=
movement
.
getId
()
# properties will change on simulation movement (mostly categories).
if
delivery
is
main_delivery
and
not
(
movement_id
in
line_id_to_delete_list
):
# By expanding again, we will avoid having many deliveries instead
line_id_to_delete_list
.
append
(
movement
.
getId
())
# of one when doing "merge"
if
line_id_to_delete_list
:
for
simulation_movement
in
simulation_movement_list
:
delivery
.
manage_delObjects
(
ids
=
line_id_to_delete_list
)
simulation_movement
.
expand
(
expand_policy
=
'immediate'
)
# It is required to expand again simulation movement, because
# we unlinked them from delivery, so it is possible that some
# activate builder
# properties will change on simulation movement (mostly categories).
merged_builder
=
self
.
_findBuilderForDelivery
(
main_delivery
)
# By expanding again, we will avoid having many deliveries instead
if
merged_builder
is
None
:
# of one when doing "merge"
error_list
.
append
(
translateString
(
"Unable to find builder"
))
for
simulation_movement
in
simulation_movement_list
:
else
:
simulation_movement
.
expand
(
expand_policy
=
'immediate'
)
merged_builder
.
build
(
movement_relative_url_list
=
[
q
.
getRelativeUrl
()
for
q
in
\
simulation_movement_list
],
merge_delivery
=
True
,
# activate builder
delivery_relative_url_list
=
[
main_delivery
.
getRelativeUrl
()])
movement_portal_type
,
=
movement_portal_type_set
# Finally, copy all lines that were created manually on all deliveries except
merged_builder
=
self
.
_findBuilderForDelivery
(
main_delivery
,
movement_portal_type
)
# the main one
if
merged_builder
is
None
:
@
UnrestrictedMethod
error_list
.
append
(
translateString
(
"Unable to find builder"
))
def
setMainDeliveryModifiable
(
delivery
):
else
:
# set causality state in such way we can modify delivery
merged_builder
.
build
(
movement_relative_url_list
=
[
q
.
getRelativeUrl
()
for
q
in
\
delivery
.
diverge
()
simulation_movement_list
],
merge_delivery
=
True
,
setMainDeliveryModifiable
(
main_delivery
)
delivery_relative_url_list
=
[
main_delivery
.
getRelativeUrl
()])
delivery_type_list
=
portal
.
getPortalDeliveryTypeList
()
# Finally, copy all lines that were created manually on all deliveries except
for
delivery_line
in
to_copy_delivery_line_list
:
# the main one
delivery
=
delivery_line
.
getParentValue
()
@
UnrestrictedMethod
if
not
(
delivery
.
getPortalType
()
in
delivery_type_list
):
def
setMainDeliveryModifiable
(
delivery
):
raise
NotImplementedError
(
"Merge of deliveries doe not yet handle case of cells"
)
# set causality state in such way we can modify delivery
copy_data
=
delivery
.
manage_copyObjects
(
ids
=
[
delivery_line
.
getId
()])
delivery
.
diverge
()
main_delivery
.
manage_pasteObjects
(
copy_data
)
setMainDeliveryModifiable
(
main_delivery
)
main_delivery
.
updateCausalityState
()
delivery_type_list
=
portal
.
getPortalDeliveryTypeList
()
for
delivery_line
in
to_copy_delivery_line_list
:
# Finally do cleanup
delivery
=
delivery_line
.
getParentValue
()
for
delivery
in
delivery_list
[
1
:]:
if
not
(
delivery
.
getPortalType
()
in
delivery_type_list
):
# cancel, delete - to disallow any user related operations on those deliveries
raise
NotImplementedError
(
"Merge of deliveries doe not yet handle case of cells"
)
after_merge_method
=
delivery
.
_getTypeBasedMethod
(
'cleanDeliveryAfterMerge'
)
copy_data
=
delivery
.
manage_copyObjects
(
ids
=
[
delivery_line
.
getId
()])
if
after_merge_method
is
not
None
:
main_delivery
.
manage_pasteObjects
(
copy_data
)
after_merge_method
()
main_delivery
.
updateCausalityState
()
# Finally do cleanup
for
delivery
in
delivery_list
[
1
:]:
# cancel, delete - to disallow any user related operations on those deliveries
after_merge_method
=
delivery
.
_getTypeBasedMethod
(
'cleanDeliveryAfterMerge'
)
if
after_merge_method
is
not
None
:
after_merge_method
()
else
:
else
:
error_list
.
append
(
translateString
(
"Please select at least two deliveries"
))
error_list
.
append
(
translateString
(
"Please select at least two deliveries"
))
return
error_list
return
error_list
...
...
product/ERP5/tests/testInvoice.py
View file @
7505e054
...
@@ -2471,6 +2471,63 @@ self.portal.getDefaultModule(self.packing_list_portal_type).newContent(
...
@@ -2471,6 +2471,63 @@ self.portal.getDefaultModule(self.packing_list_portal_type).newContent(
self
.
assertEqual
([],
packing_list
.
getDivergenceList
())
self
.
assertEqual
([],
packing_list
.
getDivergenceList
())
self
.
assertEqual
(
'solved'
,
packing_list
.
getCausalityState
())
self
.
assertEqual
(
'solved'
,
packing_list
.
getCausalityState
())
def
test_merge_accounting_invoice
(
self
,
quiet
=
quiet
):
sequence_list
=
SequenceList
()
sequence
=
sequence_list
.
addSequenceString
(
self
.
PACKING_LIST_DEFAULT_SEQUENCE
)
sequence_list
.
play
(
self
,
quiet
=
quiet
)
packing_list
=
sequence
.
get
(
'packing_list'
)
packing_list_line
=
packing_list
.
getMovementList
()[
0
]
quantity
=
packing_list_line
.
getQuantity
()
resource
=
packing_list_line
.
getResource
()
price
=
packing_list_line
.
getPrice
()
packing_list
.
setReady
()
packing_list
.
start
()
packing_list
.
stop
()
self
.
tic
()
self
.
default_quantity
=
self
.
default_quantity
+
10
self
.
default_price
=
self
.
default_price
+
10
self
.
tic
()
sequence_list
.
play
(
self
,
quiet
=
quiet
)
packing_list_2
=
sequence
.
get
(
'packing_list'
)
packing_list_line
=
packing_list_2
.
getMovementList
()[
0
]
quantity_2
=
packing_list_line
.
getQuantity
()
resource_2
=
packing_list_line
.
getResource
()
price_2
=
packing_list_line
.
getPrice
()
packing_list_2
.
setReady
()
packing_list_2
.
start
()
packing_list_2
.
stop
()
self
.
tic
()
self
.
stepInvoiceBuilderAlarm
()
self
.
tic
()
self
.
default_quantity
=
self
.
default_quantity
-
10
self
.
default_price
=
self
.
default_price
-
10
invoice
=
packing_list
.
getCausalityRelatedValue
(
portal_type
=
self
.
invoice_portal_type
)
invoice_2
=
packing_list_2
.
getCausalityRelatedValue
(
portal_type
=
self
.
invoice_portal_type
)
self
.
assertNotEquals
(
invoice
,
None
)
self
.
assertNotEquals
(
invoice_2
,
None
)
self
.
tic
()
error_list
=
self
.
portal
.
portal_simulation
.
mergeDeliveryList
([
invoice
,
invoice_2
])
self
.
tic
()
self
.
assertEqual
(
0
,
len
(
error_list
))
self
.
assertEqual
(
invoice
.
getSimulationState
(),
'confirmed'
)
# MergeDeliveryList change the first delivery to diverged
# Make sure it works as expected
self
.
assertEqual
(
invoice
.
getCausalityState
(),
'diverged'
)
self
.
assertEqual
(
invoice_2
.
getSimulationState
(),
'cancelled'
)
self
.
assertEqual
(
len
(
invoice
.
getMovementList
()),
2
)
expected_set
=
set
([
(
resource
,
quantity
,
price
),
(
resource_2
,
quantity_2
,
price_2
)])
result_set
=
set
(
sorted
(
[(
x
.
getResource
(),
x
.
getQuantity
(),
x
.
getPrice
())
for
x
in
invoice
.
getMovementList
()],
key
=
lambda
x
:
x
[
1
]))
self
.
assertEqual
(
expected_set
,
result_set
)
def
test_subcontent_reindexing
(
self
):
def
test_subcontent_reindexing
(
self
):
"""Tests, that modification on Order are propagated to lines and cells
"""Tests, that modification on Order are propagated to lines and cells
...
...
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