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
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Nicolas Wavrant
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 | quantity
Invoice Line | view
...
...
bt5/erp5_simulation/SkinTemplateItem/portal_skins/erp5_simulation/DeliveryModule_mergeDeliveryList.py
View file @
7505e054
...
...
@@ -5,12 +5,15 @@ delivery_list = []
for
o
in
object_list
:
delivery_list
.
append
(
o
)
Base_translateString
=
context
.
Base_translateString
if
len
(
delivery_list
)
<
2
:
ret_url
=
context
.
absolute_url
()
+
'/'
+
form_id
qs
=
'?portal_status_message=Please+select+more+than+one+items.'
message
=
Base_translateString
(
'Please select more than one items.'
)
else
:
ret_url
=
context
.
absolute_url
()
+
'/'
+
form_id
qs
=
'?portal_status_message=Merged.'
context
.
portal_simulation
.
mergeDeliveryList
(
delivery_list
)
error_list
=
context
.
portal_simulation
.
mergeDeliveryList
(
delivery_list
)
if
not
error_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):
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
"""
...
...
@@ -2655,7 +2655,8 @@ class SimulationTool(BaseTool):
portal_type
=
delivery
.
getPortalType
()
for
business_link
in
delivery
.
asComposedDocument
().
objectValues
(
portal_type
=
"Business Link"
):
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
break
if
builder
is
not
None
:
...
...
@@ -2681,87 +2682,93 @@ class SimulationTool(BaseTool):
if
len
(
portal_type_set
)
!=
1
:
error_list
.
append
(
translateString
(
"Please select only deliveries of same type"
))
else
:
portal_type
,
=
portal_type_set
allowed_state_set
=
set
(
portal
.
getPortalReservedInventoryStateList
()
+
\
portal
.
getPortalFutureInventoryStateList
())
found_state_set
=
set
([
x
.
getSimulationState
()
for
x
in
delivery_list
])
if
found_state_set
.
difference
(
allowed_state_set
):
error_list
.
append
(
translateString
(
"Found delivery having unexpected status for merge"
))
else
:
# Allow to call a script to do custom checking conditions before merge
main_delivery
=
delivery_list
[
0
]
check_merge_condition_method
=
main_delivery
.
_getTypeBasedMethod
(
"checkMergeConditionOnDeliveryList"
)
if
check_merge_condition_method
is
not
None
:
error_list
.
extend
(
check_merge_condition_method
(
delivery_list
=
delivery_list
))
if
len
(
error_list
)
==
0
:
# so far so good
# in delivery_list we have list of delivery to merge
simulation_movement_list
=
[]
to_copy_delivery_line_list
=
[]
# for lines not coming from upper simulation, thus
# created by hand should be manually added to main
# delivery since they are not coming from builder
for
delivery
in
delivery_list
:
line_id_to_delete_list
=
[]
for
movement
in
delivery
.
getMovementList
():
related_simulation_movement_list
=
movement
.
getDeliveryRelatedValueList
()
for
simulation_movement
in
related_simulation_movement_list
:
# if we are on a root applied rule directly, so in the case of
# a manually added line, we have to copy
# the simulation movement into to main delivery
if
simulation_movement
.
getParentValue
().
getParentValue
().
getId
()
==
"portal_simulation"
:
# For manually added lines, make sure we have only one simulation movement
assert
len
(
related_simulation_movement_list
)
==
1
if
not
(
delivery
is
main_delivery
):
to_copy_delivery_line_list
.
append
(
movement
)
else
:
simulation_movement
.
setDeliveryValue
(
None
)
simulation_movement_list
.
append
(
simulation_movement
)
# Since we keep the main delivery, we remove existing lines already
# coming from builder to let builder recreate them in the same time
# as other ones (to possibly merge lines also)
movement_id
=
movement
.
getId
()
if
delivery
is
main_delivery
and
not
(
movement_id
in
line_id_to_delete_list
):
line_id_to_delete_list
.
append
(
movement
.
getId
())
if
line_id_to_delete_list
:
delivery
.
manage_delObjects
(
ids
=
line_id_to_delete_list
)
# It is required to expand again simulation movement, because
# we unlinked them from delivery, so it is possible that some
# properties will change on simulation movement (mostly categories).
# By expanding again, we will avoid having many deliveries instead
# of one when doing "merge"
for
simulation_movement
in
simulation_movement_list
:
simulation_movement
.
expand
(
expand_policy
=
'immediate'
)
# activate builder
merged_builder
=
self
.
_findBuilderForDelivery
(
main_delivery
)
if
merged_builder
is
None
:
error_list
.
append
(
translateString
(
"Unable to find builder"
))
else
:
merged_builder
.
build
(
movement_relative_url_list
=
[
q
.
getRelativeUrl
()
for
q
in
\
simulation_movement_list
],
merge_delivery
=
True
,
delivery_relative_url_list
=
[
main_delivery
.
getRelativeUrl
()])
# Finally, copy all lines that were created manually on all deliveries except
# the main one
@
UnrestrictedMethod
def
setMainDeliveryModifiable
(
delivery
):
# set causality state in such way we can modify delivery
delivery
.
diverge
()
setMainDeliveryModifiable
(
main_delivery
)
delivery_type_list
=
portal
.
getPortalDeliveryTypeList
()
for
delivery_line
in
to_copy_delivery_line_list
:
delivery
=
delivery_line
.
getParentValue
()
if
not
(
delivery
.
getPortalType
()
in
delivery_type_list
):
raise
NotImplementedError
(
"Merge of deliveries doe not yet handle case of cells"
)
copy_data
=
delivery
.
manage_copyObjects
(
ids
=
[
delivery_line
.
getId
()])
main_delivery
.
manage_pasteObjects
(
copy_data
)
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
()
movement_portal_type_set
=
set
()
for
delivery
in
delivery_list
:
movement_portal_type_set
.
update
([
x
.
getPortalType
()
for
x
in
delivery
.
getMovementList
()])
if
len
(
movement_portal_type_set
)
!=
1
:
error_list
.
append
(
translateString
(
"Please Select only movement of same type"
))
else
:
# Allow to call a script to do custom checking conditions before merge
main_delivery
=
delivery_list
[
0
]
check_merge_condition_method
=
main_delivery
.
_getTypeBasedMethod
(
"checkMergeConditionOnDeliveryList"
)
if
check_merge_condition_method
is
not
None
:
error_list
.
extend
(
check_merge_condition_method
(
delivery_list
=
delivery_list
))
if
len
(
error_list
)
==
0
:
# so far so good
# in delivery_list we have list of delivery to merge
simulation_movement_list
=
[]
to_copy_delivery_line_list
=
[]
# for lines not coming from upper simulation, thus
# created by hand should be manually added to main
# delivery since they are not coming from builder
for
delivery
in
delivery_list
:
line_id_to_delete_list
=
[]
for
movement
in
delivery
.
getMovementList
():
related_simulation_movement_list
=
movement
.
getDeliveryRelatedValueList
()
for
simulation_movement
in
related_simulation_movement_list
:
# if we are on a root applied rule directly, so in the case of
# a manually added line, we have to copy
# the simulation movement into to main delivery
if
simulation_movement
.
getParentValue
().
getParentValue
().
getId
()
==
"portal_simulation"
:
# For manually added lines, make sure we have only one simulation movement
assert
len
(
related_simulation_movement_list
)
==
1
if
not
(
delivery
is
main_delivery
):
to_copy_delivery_line_list
.
append
(
movement
)
else
:
simulation_movement
.
setDeliveryValue
(
None
)
simulation_movement_list
.
append
(
simulation_movement
)
# Since we keep the main delivery, we remove existing lines already
# coming from builder to let builder recreate them in the same time
# as other ones (to possibly merge lines also)
movement_id
=
movement
.
getId
()
if
delivery
is
main_delivery
and
not
(
movement_id
in
line_id_to_delete_list
):
line_id_to_delete_list
.
append
(
movement
.
getId
())
if
line_id_to_delete_list
:
delivery
.
manage_delObjects
(
ids
=
line_id_to_delete_list
)
# It is required to expand again simulation movement, because
# we unlinked them from delivery, so it is possible that some
# properties will change on simulation movement (mostly categories).
# By expanding again, we will avoid having many deliveries instead
# of one when doing "merge"
for
simulation_movement
in
simulation_movement_list
:
simulation_movement
.
expand
(
expand_policy
=
'immediate'
)
# activate builder
movement_portal_type
,
=
movement_portal_type_set
merged_builder
=
self
.
_findBuilderForDelivery
(
main_delivery
,
movement_portal_type
)
if
merged_builder
is
None
:
error_list
.
append
(
translateString
(
"Unable to find builder"
))
else
:
merged_builder
.
build
(
movement_relative_url_list
=
[
q
.
getRelativeUrl
()
for
q
in
\
simulation_movement_list
],
merge_delivery
=
True
,
delivery_relative_url_list
=
[
main_delivery
.
getRelativeUrl
()])
# Finally, copy all lines that were created manually on all deliveries except
# the main one
@
UnrestrictedMethod
def
setMainDeliveryModifiable
(
delivery
):
# set causality state in such way we can modify delivery
delivery
.
diverge
()
setMainDeliveryModifiable
(
main_delivery
)
delivery_type_list
=
portal
.
getPortalDeliveryTypeList
()
for
delivery_line
in
to_copy_delivery_line_list
:
delivery
=
delivery_line
.
getParentValue
()
if
not
(
delivery
.
getPortalType
()
in
delivery_type_list
):
raise
NotImplementedError
(
"Merge of deliveries doe not yet handle case of cells"
)
copy_data
=
delivery
.
manage_copyObjects
(
ids
=
[
delivery_line
.
getId
()])
main_delivery
.
manage_pasteObjects
(
copy_data
)
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
:
error_list
.
append
(
translateString
(
"Please select at least two deliveries"
))
return
error_list
...
...
product/ERP5/tests/testInvoice.py
View file @
7505e054
...
...
@@ -2471,6 +2471,63 @@ self.portal.getDefaultModule(self.packing_list_portal_type).newContent(
self
.
assertEqual
([],
packing_list
.
getDivergenceList
())
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
):
"""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