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
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
Romain Courteaud
erp5
Commits
4ee585d8
Commit
4ee585d8
authored
Apr 08, 2020
by
Arnaud Fontaine
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ERP5/Document: Fix pylint warnings.
parent
d672defe
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
792 additions
and
808 deletions
+792
-808
product/ERP5/Document/DeliveryCell.py
product/ERP5/Document/DeliveryCell.py
+170
-174
product/ERP5/Document/DeliveryLine.py
product/ERP5/Document/DeliveryLine.py
+398
-403
product/ERP5/Document/InventoryCell.py
product/ERP5/Document/InventoryCell.py
+52
-54
product/ERP5/Document/InventoryLine.py
product/ERP5/Document/InventoryLine.py
+104
-106
product/ERP5/Document/OrderCell.py
product/ERP5/Document/OrderCell.py
+47
-49
product/ERP5/Document/OrderLine.py
product/ERP5/Document/OrderLine.py
+21
-22
No files found.
product/ERP5/Document/DeliveryCell.py
View file @
4ee585d8
...
...
@@ -35,187 +35,183 @@ from AccessControl.PermissionRole import PermissionRole
from
Products.ERP5Type
import
Permissions
,
PropertySheet
,
interfaces
from
Products.ERP5.Document.OrderLine
import
OrderLine
from
Products.ERP5.Document.Movement
import
Movement
from
Products.ERP5.Document.MappedValue
import
MappedValue
from
Products.ERP5.Document.ImmobilisationMovement
import
ImmobilisationMovement
class
DeliveryCell
(
MappedValue
,
Movement
,
ImmobilisationMovement
):
"""
A DeliveryCell allows to define specific quantities
for each variation of a resource in a delivery line.
"""
meta_type
=
'ERP5 Delivery Cell'
portal_type
=
'Delivery Cell'
isCell
=
1
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Arrow
,
PropertySheet
.
Amount
,
PropertySheet
.
Task
,
PropertySheet
.
Movement
,
PropertySheet
.
Price
,
PropertySheet
.
Predicate
,
PropertySheet
.
MappedValue
,
PropertySheet
.
ItemAggregation
)
# Declarative interfaces
zope
.
interface
.
implements
(
interfaces
.
IDivergenceController
,)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isPredicate'
)
def
isPredicate
(
self
):
"""Movements are not predicates.
"""
A DeliveryCell allows to define specific quantities
for each variation of a resource in a delivery line.
"""
meta_type
=
'ERP5 Delivery Cell'
portal_type
=
'Delivery Cell'
isCell
=
1
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Arrow
,
PropertySheet
.
Amount
,
PropertySheet
.
Task
,
PropertySheet
.
Movement
,
PropertySheet
.
Price
,
PropertySheet
.
Predicate
,
PropertySheet
.
MappedValue
,
PropertySheet
.
ItemAggregation
)
# Declarative interfaces
zope
.
interface
.
implements
(
interfaces
.
IDivergenceController
,)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isPredicate'
)
def
isPredicate
(
self
):
"""Movements are not predicates.
"""
return
False
# MatrixBox methods
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'hasCellContent'
)
def
hasCellContent
(
self
,
base_id
=
'movement'
):
"""A cell cannot have cell content itself.
"""
return
0
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isAccountable'
)
def
isAccountable
(
self
):
"""
Returns 1 if this needs to be accounted
Only account movements which are not associated to a delivery
Whenever delivery is there, delivery has priority
"""
return
self
.
getParentValue
().
getParentValue
().
isAccountable
()
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getPrice'
)
def
getPrice
(
self
,
*
args
,
**
kw
):
"""
call Movement.getPrice
"""
return
Movement
.
getPrice
(
self
,
*
args
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalPrice'
)
def
getTotalPrice
(
self
,
default
=
0.0
,
*
args
,
**
kw
):
"""
call Movement.getTotalPrice
"""
return
Movement
.
getTotalPrice
(
self
,
default
=
default
,
*
args
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getRootDeliveryValue'
)
def
getRootDeliveryValue
(
self
):
"""
Returns the root delivery responsible of this cell
"""
return
self
.
getParentValue
().
getRootDeliveryValue
()
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'notifyAfterUpdateRelatedContent'
)
def
notifyAfterUpdateRelatedContent
(
self
,
previous_category_url
,
new_category_url
):
"""
Membership Crirerions and Category List are same in DeliveryCell
Must update it (or change implementation to remove data duplication)
"""
update_method
=
self
.
portal_categories
.
updateRelatedCategory
predicate_value
=
self
.
getPredicateValueList
()
new_predicate_value
=
map
(
lambda
c
:
update_method
(
c
,
previous_category_url
,
new_category_url
),
predicate_value
)
self
.
_setPredicateValueList
(
new_predicate_value
)
# No reindex needed since uid stable
# XXX FIXME: option variation are today not well implemented
# This little hack is needed to make the matrixbox working
# in DeliveryLine_viewIndustrialPhase
# Generic form (DeliveryLine_viewOption) is required
def
_edit
(
self
,
**
kw
):
"""
Store variation_category_list, in order to store new value of
industrial_phase after.
"""
edit_order
=
[
'variation_category_list'
,
# edit this one first
'item_id_list'
]
# this one must be the last
edit_order
[
1
:
1
]
=
[
x
for
x
in
kw
.
pop
(
'edit_order'
,
())
if
x
not
in
edit_order
]
# Base._edit updates unordered properties first
edit_order
[
1
:
1
]
=
[
x
for
x
in
kw
if
x
not
in
edit_order
]
MappedValue
.
_edit
(
self
,
edit_order
=
edit_order
,
**
kw
)
return
False
# MatrixBox methods
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'hasCellContent'
)
def
hasCellContent
(
self
,
base_id
=
'movement'
):
"""A cell cannot have cell content itself.
"""
return
0
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isAccountable'
)
def
isAccountable
(
self
):
"""
Returns 1 if this needs to be accounted
Only account movements which are not associated to a delivery
Whenever delivery is there, delivery has priority
"""
return
self
.
getParentValue
().
getParentValue
().
isAccountable
()
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getPrice'
)
def
getPrice
(
self
,
*
args
,
**
kw
):
"""
call Movement.getPrice
"""
return
Movement
.
getPrice
(
self
,
*
args
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalPrice'
)
def
getTotalPrice
(
self
,
default
=
0.0
,
*
args
,
**
kw
):
"""
call Movement.getTotalPrice
"""
return
Movement
.
getTotalPrice
(
self
,
default
=
default
,
*
args
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getRootDeliveryValue'
)
def
getRootDeliveryValue
(
self
):
"""
Returns the root delivery responsible of this cell
"""
return
self
.
getParentValue
().
getRootDeliveryValue
()
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'notifyAfterUpdateRelatedContent'
)
def
notifyAfterUpdateRelatedContent
(
self
,
previous_category_url
,
new_category_url
):
"""
Membership Crirerions and Category List are same in DeliveryCell
Must update it (or change implementation to remove data duplication)
"""
update_method
=
self
.
portal_categories
.
updateRelatedCategory
new_predicate_value
=
[
update_method
(
c
,
previous_category_url
,
new_category_url
)
for
c
in
self
.
getPredicateValueList
()]
self
.
_setPredicateValueList
(
new_predicate_value
)
# No reindex needed since uid stable
# XXX FIXME: option variation are today not well implemented
# This little hack is needed to make the matrixbox working
# in DeliveryLine_viewIndustrialPhase
# Generic form (DeliveryLine_viewOption) is required
def
_edit
(
self
,
**
kw
):
"""
Store variation_category_list, in order to store new value of
industrial_phase after.
"""
edit_order
=
[
'variation_category_list'
,
# edit this one first
'item_id_list'
]
# this one must be the last
edit_order
[
1
:
1
]
=
[
x
for
x
in
kw
.
pop
(
'edit_order'
,
())
if
x
not
in
edit_order
]
# Base._edit updates unordered properties first
edit_order
[
1
:
1
]
=
[
x
for
x
in
kw
if
x
not
in
edit_order
]
MappedValue
.
_edit
(
self
,
edit_order
=
edit_order
,
**
kw
)
# if self.isSimulated():
# self.getRootDeliveryValue().activate().propagateResourceToSimulation()
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'updateSimulationDeliveryProperties'
)
def
updateSimulationDeliveryProperties
(
self
,
movement_list
=
None
):
"""
Set properties delivery_ratio and delivery_error for each
simulation movement in movement_list (all movements by default),
according to this delivery calculated quantity
"""
parent
=
self
.
getParentValue
()
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'updateSimulationDeliveryProperties'
)
def
updateSimulationDeliveryProperties
(
self
,
movement_list
=
None
):
"""
Set properties delivery_ratio and delivery_error for each
simulation movement in movement_list (all movements by default),
according to this delivery calculated quantity
"""
parent
=
self
.
getParentValue
()
if
parent
is
not
None
:
parent
=
parent
.
getParentValue
()
if
parent
is
not
None
:
parent
=
parent
.
getParentValue
()
if
parent
is
not
None
:
parent
.
updateSimulationDeliveryProperties
(
movement_list
,
self
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isMovement'
)
def
isMovement
(
self
):
return
1
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isMovingItem'
)
def
isMovingItem
(
self
,
item
):
type_based_script
=
self
.
_getTypeBasedMethod
(
'isMovingItem'
)
if
type_based_script
:
return
type_based_script
(
item
)
return
self
.
isAccountable
()
# Override getQuantityUnitXXX to negate same methods defined in
# Amount class. Because cell must acquire quantity unit from line
# not from resource.
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getQuantityUnitValue'
)
def
getQuantityUnitValue
(
self
):
return
self
.
getParentValue
().
getQuantityUnitValue
()
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getQuantityUnit'
)
def
getQuantityUnit
(
self
,
checked_permission
=
None
):
return
self
.
getParentValue
().
getQuantityUnit
(
checked_permission
=
checked_permission
)
# XXX: Dirty but required for erp5_banking_core
### Acquire Baobab source / destination uids from parent line
getBaobabSourceUid
=
lambda
x
:
x
.
getSourceUid
()
getBaobabSourceUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationUid
=
lambda
x
:
x
.
getDestinationUid
()
getBaobabDestinationUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceSectionUid
=
lambda
x
:
x
.
getSourceSectionUid
()
getBaobabSourceSectionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationSectionUid
=
lambda
x
:
x
.
getDestinationSectionUid
()
getBaobabDestinationSectionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourcePaymentUid
=
lambda
x
:
x
.
getSourcePaymentUid
()
getBaobabSourcePaymentUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationPaymentUid
=
lambda
x
:
x
.
getDestinationPaymentUid
()
getBaobabDestinationPaymentUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceFunctionUid
=
lambda
x
:
x
.
getSourceFunctionUid
()
getBaobabSourceFunctionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationFunctionUid
=
lambda
x
:
x
.
getDestinationFunctionUid
()
getBaobabDestinationFunctionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceProjectUid
=
lambda
x
:
x
.
getSourceProjectUid
()
getBaobabSourceProjectUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationProjectUid
=
lambda
x
:
x
.
getDestinationProjectUid
()
getBaobabDestinationProjectUid__roles__
=
PermissionRole
(
Permissions
.
View
)
parent
.
updateSimulationDeliveryProperties
(
movement_list
,
self
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isMovement'
)
def
isMovement
(
self
):
return
1
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isMovingItem'
)
def
isMovingItem
(
self
,
item
):
type_based_script
=
self
.
_getTypeBasedMethod
(
'isMovingItem'
)
if
type_based_script
:
return
type_based_script
(
item
)
return
self
.
isAccountable
()
# Override getQuantityUnitXXX to negate same methods defined in
# Amount class. Because cell must acquire quantity unit from line
# not from resource.
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getQuantityUnitValue'
)
def
getQuantityUnitValue
(
self
):
return
self
.
getParentValue
().
getQuantityUnitValue
()
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getQuantityUnit'
)
def
getQuantityUnit
(
self
,
checked_permission
=
None
):
return
self
.
getParentValue
().
getQuantityUnit
(
checked_permission
=
checked_permission
)
# XXX: Dirty but required for erp5_banking_core
### Acquire Baobab source / destination uids from parent line
getBaobabSourceUid
=
lambda
x
:
x
.
getSourceUid
()
getBaobabSourceUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationUid
=
lambda
x
:
x
.
getDestinationUid
()
getBaobabDestinationUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceSectionUid
=
lambda
x
:
x
.
getSourceSectionUid
()
getBaobabSourceSectionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationSectionUid
=
lambda
x
:
x
.
getDestinationSectionUid
()
getBaobabDestinationSectionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourcePaymentUid
=
lambda
x
:
x
.
getSourcePaymentUid
()
getBaobabSourcePaymentUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationPaymentUid
=
lambda
x
:
x
.
getDestinationPaymentUid
()
getBaobabDestinationPaymentUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceFunctionUid
=
lambda
x
:
x
.
getSourceFunctionUid
()
getBaobabSourceFunctionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationFunctionUid
=
lambda
x
:
x
.
getDestinationFunctionUid
()
getBaobabDestinationFunctionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceProjectUid
=
lambda
x
:
x
.
getSourceProjectUid
()
getBaobabSourceProjectUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationProjectUid
=
lambda
x
:
x
.
getDestinationProjectUid
()
getBaobabDestinationProjectUid__roles__
=
PermissionRole
(
Permissions
.
View
)
\ No newline at end of file
product/ERP5/Document/DeliveryLine.py
View file @
4ee585d8
...
...
@@ -41,240 +41,238 @@ from Products.ERP5Type.Base import Base
edit_args_list
=
getargspec
(
Base
.
_edit
).
args
class
DeliveryLine
(
Movement
,
XMLMatrix
,
ImmobilisationMovement
):
"""
A DeliveryLine object allows to implement lines in
Deliveries (packing list, order, invoice, etc.)
It may include a price (for insurance, for customs, for invoices,
for orders)
"""
meta_type
=
'ERP5 Delivery Line'
portal_type
=
'Delivery Line'
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
XMLObject
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Amount
,
PropertySheet
.
Task
,
PropertySheet
.
Arrow
,
PropertySheet
.
Movement
,
PropertySheet
.
Price
,
PropertySheet
.
VariationRange
,
PropertySheet
.
ItemAggregation
,
PropertySheet
.
SortIndex
)
# Declarative interfaces
zope
.
interface
.
implements
(
interfaces
.
IDivergenceController
,)
# Multiple inheritance definition
updateRelatedContent
=
XMLMatrix
.
updateRelatedContent
# Force in _edit to modify variation_base_category_list first
def
_edit
(
self
,
edit_order
=
(),
**
kw
):
# XXX FIXME For now, special cases are handled in _edit methods in many
# documents : DeliveryLine, DeliveryCell ... Ideally, to prevent code
# duplication, it should be handled in a _edit method present only in
# Amount.py
# If variations and resources are set at the same time, resource must be
# set before any variation.
before_order
=
(
'resource'
,
'resource_value'
,
'variation_base_category_list'
,
'variation_category_list'
)
before_kw
=
{
k
:
kw
.
pop
(
k
)
for
k
in
before_order
if
k
in
kw
}
if
before_kw
:
before_kw
.
update
((
k
,
kw
[
k
])
for
k
in
edit_args_list
if
k
in
kw
)
Base
.
_edit
(
self
,
edit_order
=
before_order
,
**
before_kw
)
if
kw
:
Movement
.
_edit
(
self
,
edit_order
=
edit_order
,
**
kw
)
# We must check if the user has changed the resource of particular line
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'edit'
)
def
edit
(
self
,
REQUEST
=
None
,
force_update
=
0
,
reindex_object
=
1
,
**
kw
):
return
self
.
_edit
(
REQUEST
=
REQUEST
,
force_update
=
force_update
,
reindex_object
=
reindex_object
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isAccountable'
)
def
isAccountable
(
self
):
"""To avoid duplicate docstring. Please read movement interface."""
return
self
.
getParentValue
().
isAccountable
()
and
(
not
self
.
hasCellContent
())
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isMovingItem'
)
def
isMovingItem
(
self
,
item
):
type_based_script
=
self
.
_getTypeBasedMethod
(
'isMovingItem'
)
if
type_based_script
:
return
type_based_script
(
item
)
return
self
.
isAccountable
()
def
_getTotalPrice
(
self
,
default
=
0.0
,
context
=
None
,
fast
=
0
):
"""
A DeliveryLine object allows to implement lines in
Deliveries (packing list, order, invoice, etc.)
Returns the total price for this line, this line contains, or the cells it contains.
It may include a price (for insurance, for customs, for invoices,
for orders)
if hasLineContent: return sum of lines total price
if hasCellContent: return sum of cells total price
else: return quantity * price
if fast argument is true, inventory API will be used.
"""
if
fast
:
kw
=
{}
kw
[
'section_uid'
]
=
self
.
getDestinationSectionUid
()
kw
[
'stock.explanation_uid'
]
=
self
.
getExplanationUid
()
kw
[
'relative_url'
]
=
(
'%s/%%'
%
(
self
.
getRelativeUrl
().
replace
(
'_'
,
'
\
\
_'
)),
self
.
getRelativeUrl
()
)
kw
[
'only_accountable'
]
=
False
return
self
.
getPortalObject
().
portal_simulation
.
getInventoryAssetPrice
(
**
kw
)
if
self
.
hasLineContent
():
meta_type
=
self
.
meta_type
return
sum
(
l
.
getTotalPrice
(
context
=
context
)
for
l
in
self
.
objectValues
()
if
l
.
meta_type
==
meta_type
)
elif
not
self
.
hasCellContent
(
base_id
=
'movement'
):
return
Movement
.
_getTotalPrice
(
self
,
default
=
default
,
context
=
context
)
return
sum
(
cell
.
getTotalPrice
(
default
=
0.0
,
context
=
context
)
for
cell
in
self
.
getCellValueList
())
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalQuantity'
)
def
getTotalQuantity
(
self
,
fast
=
0
):
"""
Returns the quantity if no cell or the total quantity if cells
meta_type
=
'ERP5 Delivery Line'
portal_type
=
'Delivery Line'
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
XMLObject
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Amount
,
PropertySheet
.
Task
,
PropertySheet
.
Arrow
,
PropertySheet
.
Movement
,
PropertySheet
.
Price
,
PropertySheet
.
VariationRange
,
PropertySheet
.
ItemAggregation
,
PropertySheet
.
SortIndex
)
# Declarative interfaces
zope
.
interface
.
implements
(
interfaces
.
IDivergenceController
,)
# Multiple inheritance definition
updateRelatedContent
=
XMLMatrix
.
updateRelatedContent
# Force in _edit to modify variation_base_category_list first
def
_edit
(
self
,
edit_order
=
(),
**
kw
):
# XXX FIXME For now, special cases are handled in _edit methods in many
# documents : DeliveryLine, DeliveryCell ... Ideally, to prevent code
# duplication, it should be handled in a _edit method present only in
# Amount.py
# If variations and resources are set at the same time, resource must be
# set before any variation.
before_order
=
(
'resource'
,
'resource_value'
,
'variation_base_category_list'
,
'variation_category_list'
)
before_kw
=
{
k
:
kw
.
pop
(
k
)
for
k
in
before_order
if
k
in
kw
}
if
before_kw
:
before_kw
.
update
((
k
,
kw
[
k
])
for
k
in
edit_args_list
if
k
in
kw
)
Base
.
_edit
(
self
,
edit_order
=
before_order
,
**
before_kw
)
if
kw
:
Movement
.
_edit
(
self
,
edit_order
=
edit_order
,
**
kw
)
# We must check if the user has changed the resource of particular line
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'edit'
)
def
edit
(
self
,
REQUEST
=
None
,
force_update
=
0
,
reindex_object
=
1
,
**
kw
):
return
self
.
_edit
(
REQUEST
=
REQUEST
,
force_update
=
force_update
,
reindex_object
=
reindex_object
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isAccountable'
)
def
isAccountable
(
self
):
"""To avoid duplicate docstring. Please read movement interface."""
return
self
.
getParentValue
().
isAccountable
()
and
(
not
self
.
hasCellContent
())
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isMovingItem'
)
def
isMovingItem
(
self
,
item
):
type_based_script
=
self
.
_getTypeBasedMethod
(
'isMovingItem'
)
if
type_based_script
:
return
type_based_script
(
item
)
return
self
.
isAccountable
()
def
_getTotalPrice
(
self
,
default
=
0.0
,
context
=
None
,
fast
=
0
):
"""
Returns the total price for this line, this line contains, or the cells it contains.
if hasLineContent: return sum of lines total price
if hasCellContent: return sum of cells total price
else: return quantity * price
if fast argument is true, inventory API will be used.
"""
if
fast
:
kw
=
{}
kw
[
'section_uid'
]
=
self
.
getDestinationSectionUid
()
kw
[
'stock.explanation_uid'
]
=
self
.
getExplanationUid
()
kw
[
'relative_url'
]
=
(
'%s/%%'
%
(
self
.
getRelativeUrl
().
replace
(
'_'
,
'
\
\
_'
)),
self
.
getRelativeUrl
()
)
kw
[
'only_accountable'
]
=
False
return
self
.
getPortalObject
().
portal_simulation
.
getInventoryAssetPrice
(
**
kw
)
if
self
.
hasLineContent
():
meta_type
=
self
.
meta_type
return
sum
(
l
.
getTotalPrice
(
context
=
context
)
for
l
in
self
.
objectValues
()
if
l
.
meta_type
==
meta_type
)
elif
not
self
.
hasCellContent
(
base_id
=
'movement'
):
return
Movement
.
_getTotalPrice
(
self
,
default
=
default
,
context
=
context
)
return
sum
(
cell
.
getTotalPrice
(
default
=
0.0
,
context
=
context
)
for
cell
in
self
.
getCellValueList
())
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalQuantity'
)
def
getTotalQuantity
(
self
,
fast
=
0
):
"""
Returns the quantity if no cell or the total quantity if cells
if hasLineContent: return sum of lines total quantity
if hasCellContent: return sum of cells total quantity
else: return quantity
if fast argument is true, inventory API will be used.
"""
if
fast
:
kw
=
{}
kw
[
'section_uid'
]
=
self
.
getDestinationSectionUid
()
kw
[
'stock.explanation_uid'
]
=
self
.
getExplanationUid
()
kw
[
'relative_url'
]
=
(
'%s/%%'
%
(
self
.
getRelativeUrl
().
replace
(
'_'
,
'
\
\
_'
)),
self
.
getRelativeUrl
()
)
kw
[
'only_accountable'
]
=
False
return
self
.
getPortalObject
().
portal_simulation
.
getInventory
(
**
kw
)
base_id
=
'movement'
if
self
.
hasLineContent
():
meta_type
=
self
.
meta_type
return
sum
(
l
.
getTotalQuantity
()
for
l
in
self
.
objectValues
()
if
l
.
meta_type
==
meta_type
)
elif
self
.
hasCellContent
(
base_id
=
base_id
):
return
sum
([
cell
.
getQuantity
()
for
cell
in
self
.
getCellValueList
()])
return
self
.
getQuantity
()
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'hasLineContent'
)
def
hasLineContent
(
self
):
"""Return true if the object contains lines.
if hasLineContent: return sum of lines total quantity
if hasCellContent: return sum of cells total quantity
else: return quantity
if fast argument is true, inventory API will be used.
"""
if
fast
:
kw
=
{}
kw
[
'section_uid'
]
=
self
.
getDestinationSectionUid
()
kw
[
'stock.explanation_uid'
]
=
self
.
getExplanationUid
()
kw
[
'relative_url'
]
=
(
'%s/%%'
%
(
self
.
getRelativeUrl
().
replace
(
'_'
,
'
\
\
_'
)),
self
.
getRelativeUrl
()
)
kw
[
'only_accountable'
]
=
False
return
self
.
getPortalObject
().
portal_simulation
.
getInventory
(
**
kw
)
base_id
=
'movement'
if
self
.
hasLineContent
():
meta_type
=
self
.
meta_type
return
sum
(
l
.
getTotalQuantity
()
for
l
in
self
.
objectValues
()
if
l
.
meta_type
==
meta_type
)
elif
self
.
hasCellContent
(
base_id
=
base_id
):
return
sum
([
cell
.
getQuantity
()
for
cell
in
self
.
getCellValueList
()])
return
self
.
getQuantity
()
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'hasLineContent'
)
def
hasLineContent
(
self
):
"""Return true if the object contains lines.
This method only checks the first sub line because all sub
lines should be same meta type in reality if we have line
inside line.
"""
return
len
(
self
)
!=
0
and
self
.
objectValues
()[
0
].
meta_type
==
self
.
meta_type
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'hasCellContent'
)
def
hasCellContent
(
self
,
base_id
=
'movement'
):
"""Return true if the object contains cells.
"""
# Do not use XMLMatrix.hasCellContent, because it can generate
# inconsistency in catalog
# Exemple: define a line and set the matrix cell range, but do not create
# cell.
# Line was in this case consider like a movement, and was catalogued.
# But, getVariationText of the line was not empty.
# So, in ZODB, resource as without variation, but in catalog, this was
# the contrary...
cell_range
=
XMLMatrix
.
getCellRange
(
self
,
base_id
=
base_id
)
return
(
cell_range
is
not
None
and
len
(
cell_range
)
>
0
)
# DeliveryLine can be a movement when it does not content any cell and
# matrix cell range is not empty.
# Better implementation is needed.
# We want to define a line without cell, defining a variated resource.
# If we modify the cell range, we need to move the quantity to a new
# cell, which define the same variated resource.
"""
return
len
(
self
)
!=
0
and
self
.
objectValues
()[
0
].
meta_type
==
self
.
meta_type
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'hasCellContent'
)
def
hasCellContent
(
self
,
base_id
=
'movement'
):
"""Return true if the object contains cells.
"""
# Do not use XMLMatrix.hasCellContent, because it can generate
# inconsistency in catalog
# Exemple: define a line and set the matrix cell range, but do not create
# cell.
# Line was in this case consider like a movement, and was catalogued.
# But, getVariationText of the line was not empty.
# So, in ZODB, resource as without variation, but in catalog, this was
# the contrary...
cell_range
=
XMLMatrix
.
getCellRange
(
self
,
base_id
=
base_id
)
return
(
cell_range
is
not
None
and
len
(
cell_range
)
>
0
)
# DeliveryLine can be a movement when it does not content any cell and
# matrix cell range is not empty.
# Better implementation is needed.
# We want to define a line without cell, defining a variated resource.
# If we modify the cell range, we need to move the quantity to a new
# cell, which define the same variated resource.
# return XMLMatrix.hasCellContent(self, base_id=base_id)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isMovement'
)
def
isMovement
(
self
):
"""
returns true is the object contains no submovement (line or cell)
"""
object_list
=
self
.
objectValues
()
if
object_list
:
portal_type
=
self
.
getPortalObject
().
getPortalMovementTypeList
()
for
ob
in
object_list
:
if
ob
.
getPortalType
()
in
portal_type
:
return
False
return
True
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getMovedItemUidList'
)
def
getMovedItemUidList
(
self
):
"""This method returns an uid list of items
"""
return
[
item
.
getUid
()
for
item
in
self
.
getAggregateValueList
()
\
if
self
.
isMovingItem
(
item
)]
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getCellValueList'
)
def
getCellValueList
(
self
,
base_id
=
'movement'
):
"""
This method can be overriden
"""
return
XMLMatrix
.
getCellValueList
(
self
,
base_id
=
base_id
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getCell'
)
def
getCell
(
self
,
*
kw
,
**
kwd
):
"""
This method can be overriden
"""
if
'base_id'
not
in
kwd
:
kwd
[
'base_id'
]
=
'movement'
return
XMLMatrix
.
getCell
(
self
,
*
kw
,
**
kwd
)
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'newCell'
)
def
newCell
(
self
,
*
kw
,
**
kwd
):
"""
This method creates a new cell
"""
if
'base_id'
not
in
kwd
:
kwd
[
'base_id'
]
=
'movement'
return
XMLMatrix
.
newCell
(
self
,
*
kw
,
**
kwd
)
def
applyToDeliveryLineRelatedMovement
(
self
,
portal_type
=
'Simulation Movement'
,
method_id
=
'expand'
):
# Find related in simulation
for
my_simulation_movement
in
self
.
getDeliveryRelatedValueList
(
portal_type
=
'Simulation Movement'
):
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isMovement'
)
def
isMovement
(
self
):
"""
returns true is the object contains no submovement (line or cell)
"""
object_list
=
self
.
objectValues
()
if
object_list
:
portal_type
=
self
.
getPortalObject
().
getPortalMovementTypeList
()
for
ob
in
object_list
:
if
ob
.
getPortalType
()
in
portal_type
:
return
False
return
True
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getMovedItemUidList'
)
def
getMovedItemUidList
(
self
):
"""This method returns an uid list of items
"""
return
[
item
.
getUid
()
for
item
in
self
.
getAggregateValueList
()
\
if
self
.
isMovingItem
(
item
)]
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getCellValueList'
)
def
getCellValueList
(
self
,
base_id
=
'movement'
):
"""
This method can be overriden
"""
return
XMLMatrix
.
getCellValueList
(
self
,
base_id
=
base_id
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getCell'
)
def
getCell
(
self
,
*
kw
,
**
kwd
):
"""
This method can be overriden
"""
if
'base_id'
not
in
kwd
:
kwd
[
'base_id'
]
=
'movement'
return
XMLMatrix
.
getCell
(
self
,
*
kw
,
**
kwd
)
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'newCell'
)
def
newCell
(
self
,
*
kw
,
**
kwd
):
"""
This method creates a new cell
"""
if
'base_id'
not
in
kwd
:
kwd
[
'base_id'
]
=
'movement'
return
XMLMatrix
.
newCell
(
self
,
*
kw
,
**
kwd
)
def
applyToDeliveryLineRelatedMovement
(
self
,
portal_type
=
'Simulation Movement'
,
method_id
=
'expand'
):
# Find related in simulation
for
my_simulation_movement
in
self
.
getDeliveryRelatedValueList
(
portal_type
=
'Simulation Movement'
):
# And apply
getattr
(
my_simulation_movement
.
getObject
(),
method_id
)()
for
c
in
self
.
objectValues
(
portal_type
=
'Delivery Cell'
):
for
my_simulation_movement
in
c
.
getDeliveryRelatedValueList
(
portal_type
=
'Simulation Movement'
):
# And apply
getattr
(
my_simulation_movement
.
getObject
(),
method_id
)()
for
c
in
self
.
objectValues
(
portal_type
=
'Delivery Cell'
):
for
my_simulation_movement
in
c
.
getDeliveryRelatedValueList
(
portal_type
=
'Simulation Movement'
):
# And apply
getattr
(
my_simulation_movement
.
getObject
(),
method_id
)()
def
reindexObject
(
self
,
*
k
,
**
kw
):
"""Reindex children"""
self
.
recursiveReindexObject
(
*
k
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getInventoriatedQuantity'
)
def
getInventoriatedQuantity
(
self
):
"""
"""
return
Movement
.
getInventoriatedQuantity
(
self
)
def
reindexObject
(
self
,
*
k
,
**
kw
):
"""Reindex children"""
self
.
recursiveReindexObject
(
*
k
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getInventoriatedQuantity'
)
def
getInventoriatedQuantity
(
self
):
"""
"""
return
Movement
.
getInventoriatedQuantity
(
self
)
# security.declarePrivate('_checkConsistency')
# def _checkConsistency(self, fixit=0, mapped_value_property_list = ('quantity', 'price')):
...
...
@@ -311,188 +309,185 @@ class DeliveryLine(Movement, XMLMatrix, ImmobilisationMovement):
#
# return error_list
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getRootDeliveryValue'
)
def
getRootDeliveryValue
(
self
):
"""
Returns the root delivery responsible of this line
"""
return
self
.
getParentValue
().
getRootDeliveryValue
()
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'updateSimulationDeliveryProperties'
)
def
updateSimulationDeliveryProperties
(
self
,
movement_list
=
None
):
"""
Set properties delivery_ratio and delivery_error for each
simulation movement in movement_list (all movements by default),
according to this delivery calculated quantity
"""
parent
=
self
.
getParentValue
()
if
parent
is
not
None
:
parent
.
updateSimulationDeliveryProperties
(
movement_list
,
self
)
security
.
declarePrivate
(
'manage_afterAdd'
)
def
manage_afterAdd
(
self
,
item
,
container
):
"if the container is a line too, reindex it"
if
self
.
meta_type
==
container
.
meta_type
:
container
.
reindexObject
()
return
Movement
.
manage_afterAdd
(
self
,
item
,
container
)
security
.
declarePrivate
(
'manage_beforeDelete'
)
def
manage_beforeDelete
(
self
,
item
,
container
):
"if the container is a line too, reindex it"
if
self
.
meta_type
==
container
.
meta_type
:
container
.
reindexObject
()
return
Movement
.
manage_beforeDelete
(
self
,
item
,
container
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getRootDeliveryValue'
)
def
getRootDeliveryValue
(
self
):
"""
Returns the root delivery responsible of this line
"""
return
self
.
getParentValue
().
getRootDeliveryValue
()
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'updateSimulationDeliveryProperties'
)
def
updateSimulationDeliveryProperties
(
self
,
movement_list
=
None
):
"""
Set properties delivery_ratio and delivery_error for each
simulation movement in movement_list (all movements by default),
according to this delivery calculated quantity
"""
parent
=
self
.
getParentValue
()
if
parent
is
not
None
:
parent
.
updateSimulationDeliveryProperties
(
movement_list
,
self
)
security
.
declarePrivate
(
'manage_afterAdd'
)
def
manage_afterAdd
(
self
,
item
,
container
):
"if the container is a line too, reindex it"
if
self
.
meta_type
==
container
.
meta_type
:
container
.
reindexObject
()
return
Movement
.
manage_afterAdd
(
self
,
item
,
container
)
security
.
declarePrivate
(
'manage_beforeDelete'
)
def
manage_beforeDelete
(
self
,
item
,
container
):
"if the container is a line too, reindex it"
if
self
.
meta_type
==
container
.
meta_type
:
container
.
reindexObject
()
return
Movement
.
manage_beforeDelete
(
self
,
item
,
container
)
# divergence support with solving
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isDivergent'
)
def
isDivergent
(
self
):
"""Returns true if the delivery line is divergent, or if any contained
cell is divergent.
"""
return
bool
(
self
.
getDivergenceList
())
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getDivergenceList'
)
def
getDivergenceList
(
self
):
"""Returns a list of messages that contains the divergences for that line
and the cells it may contain.
"""
if
self
.
hasCellContent
():
divergence_list
=
[]
for
cell
in
self
.
objectValues
(
portal_type
=
self
.
getPortalObject
()
.
getPortalDeliveryMovementTypeList
()):
divergence_list
+=
cell
.
getDivergenceList
()
return
divergence_list
else
:
return
Movement
.
getDivergenceList
(
self
)
def
_distributePropertyToSimulation
(
self
,
decision
):
"""Distributes property from self to all related simulation movements
AKA - accept decision"""
for
simulation_movement
in
self
.
getDeliveryRelatedValueList
(
portal_type
=
'Simulation Movement'
):
simulation_movement
.
edit
(
**
{
decision
.
divergence
.
tested_property
:
self
.
getProperty
(
decision
.
divergence
.
tested_property
)
})
def
_updatePropertyFromSimulation
(
self
,
decision_list
):
"""Update property from simulation
'Stolen' from Products.ERP5.Document.DeliveryBuilder._solveDivergence
Another possibility is to just simply copy properties or, in case of
quantity, add from all simulation movements.
"""
simulation_movement_list
=
self
.
getDeliveryRelatedValueList
(
portal_type
=
"Simulation Movement"
)
business_link
=
simulation_movement_list
[
0
].
getCausalityValue
()
delivery
=
self
.
getExplanationValue
()
delivery_portal_type
=
delivery
.
getPortalType
()
delivery_line_portal_type
=
self
.
getPortalType
()
# we need to find only one matching delivery builder
for
delivery_builder
in
business_link
.
getDeliveryBuilderValueList
():
if
delivery_builder
.
getDeliveryPortalType
()
==
\
delivery_portal_type
and
\
delivery_builder
.
getDeliveryLinePortalType
()
==
\
delivery_line_portal_type
:
break
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isDivergent'
)
def
isDivergent
(
self
):
"""Returns true if the delivery line is divergent, or if any contained
cell is divergent.
"""
return
bool
(
self
.
getDivergenceList
())
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getDivergenceList'
)
def
getDivergenceList
(
self
):
"""Returns a list of messages that contains the divergences for that line
and the cells it may contain.
"""
if
self
.
hasCellContent
():
divergence_list
=
[]
for
cell
in
self
.
objectValues
(
portal_type
=
self
.
getPortalObject
()
.
getPortalDeliveryMovementTypeList
()):
divergence_list
+=
cell
.
getDivergenceList
()
return
divergence_list
else
:
return
Movement
.
getDivergenceList
(
self
)
def
_distributePropertyToSimulation
(
self
,
decision
):
"""Distributes property from self to all related simulation movements
AKA - accept decision"""
for
simulation_movement
in
self
.
getDeliveryRelatedValueList
(
portal_type
=
'Simulation Movement'
):
simulation_movement
.
edit
(
**
{
decision
.
divergence
.
tested_property
:
self
.
getProperty
(
decision
.
divergence
.
tested_property
)
})
def
_updatePropertyFromSimulation
(
self
,
decision_list
):
"""Update property from simulation
'Stolen' from Products.ERP5.Document.DeliveryBuilder._solveDivergence
Another possibility is to just simply copy properties or, in case of
quantity, add from all simulation movements.
"""
simulation_movement_list
=
self
.
getDeliveryRelatedValueList
(
portal_type
=
"Simulation Movement"
)
business_link
=
simulation_movement_list
[
0
].
getCausalityValue
()
delivery
=
self
.
getExplanationValue
()
delivery_portal_type
=
delivery
.
getPortalType
()
delivery_line_portal_type
=
self
.
getPortalType
()
# we need to find only one matching delivery builder
for
delivery_builder
in
business_link
.
getDeliveryBuilderValueList
():
if
delivery_builder
.
getDeliveryPortalType
()
==
\
delivery_portal_type
and
\
delivery_builder
.
getDeliveryLinePortalType
()
==
\
delivery_line_portal_type
:
break
else
:
raise
ValueError
(
'No builder found'
)
self
.
edit
(
quantity
=
0
)
# adoption have to 'rebuild' delivery line
# Collect
root_group_node
=
delivery_builder
.
collectMovement
(
simulation_movement_list
)
divergence_list
=
[
decision
.
divergence
for
decision
in
decision_list
]
# Build
portal
=
self
.
getPortalObject
()
delivery_module
=
getattr
(
portal
,
delivery_builder
.
getDeliveryModule
())
delivery_to_update_list
=
[
delivery
]
delivery_list
=
delivery_builder
.
_processDeliveryGroup
(
delivery_module
,
root_group_node
,
delivery_builder
.
getDeliveryMovementGroupList
(),
delivery_to_update_list
=
delivery_to_update_list
,
divergence_list
=
divergence_list
,
force_update
=
1
)
new_delivery_list
=
[
x
for
x
in
delivery_list
if
x
!=
delivery
]
if
new_delivery_list
:
raise
ValueError
(
'No new deliveries shall be created'
)
# Then, we should re-apply quantity divergence according to 'Do
# nothing' quantity divergence list because all quantity are already
# calculated in adopt prevision phase.
quantity_dict
=
{}
for
divergence
in
self
.
getDivergenceList
():
if
divergence
.
getProperty
(
'divergence_scope'
)
!=
'quantity'
or
\
divergence
in
divergence_list
:
continue
s_m
=
divergence
.
getProperty
(
'simulation_movement'
)
delivery_movement
=
s_m
.
getDeliveryValue
()
assert
delivery_movement
==
self
quantity_gap
=
divergence
.
getProperty
(
'decision_value'
)
-
\
divergence
.
getProperty
(
'prevision_value'
)
delivery_movement
.
setQuantity
(
delivery_movement
.
getQuantity
()
+
\
quantity_gap
)
quantity_dict
[
s_m
]
=
\
divergence
.
getProperty
(
'decision_value'
)
# Finally, recalculate delivery_ratio
#
# Here, created/updated movements are not indexed yet. So we try to
# gather delivery relations from simulation movements.
delivery_dict
=
{}
for
s_m
in
simulation_movement_list
:
delivery_path
=
s_m
.
getDelivery
()
delivery_dict
[
delivery_path
]
=
\
delivery_dict
.
get
(
delivery_path
,
[])
+
\
[
s_m
]
for
s_m_list_per_movement
in
delivery_dict
.
values
():
total_quantity
=
sum
([
quantity_dict
.
get
(
s_m
,
s_m
.
getQuantity
())
\
for
s_m
in
s_m_list_per_movement
])
if
total_quantity
!=
0.0
:
for
s_m
in
s_m_list_per_movement
:
delivery_ratio
=
quantity_dict
.
get
(
s_m
,
s_m
.
getQuantity
())
\
/
total_quantity
s_m
.
edit
(
delivery_ratio
=
delivery_ratio
)
else
:
raise
ValueError
(
'No builder found'
)
self
.
edit
(
quantity
=
0
)
# adoption have to 'rebuild' delivery line
movement_type_list
=
(
delivery_builder
.
getDeliveryLinePortalType
(),
delivery_builder
.
getDeliveryCellPortalType
())
# Collect
root_group_node
=
delivery_builder
.
collectMovement
(
simulation_movement_list
)
divergence_list
=
[
decision
.
divergence
for
decision
in
decision_list
]
# Build
portal
=
self
.
getPortalObject
()
delivery_module
=
getattr
(
portal
,
delivery_builder
.
getDeliveryModule
())
delivery_to_update_list
=
[
delivery
]
delivery_list
=
delivery_builder
.
_processDeliveryGroup
(
delivery_module
,
root_group_node
,
delivery_builder
.
getDeliveryMovementGroupList
(),
delivery_to_update_list
=
delivery_to_update_list
,
divergence_list
=
divergence_list
,
force_update
=
1
)
new_delivery_list
=
[
x
for
x
in
delivery_list
if
x
!=
delivery
]
if
new_delivery_list
:
raise
ValueError
(
'No new deliveries shall be created'
)
# Then, we should re-apply quantity divergence according to 'Do
# nothing' quantity divergence list because all quantity are already
# calculated in adopt prevision phase.
quantity_dict
=
{}
for
divergence
in
self
.
getDivergenceList
():
if
divergence
.
getProperty
(
'divergence_scope'
)
!=
'quantity'
or
\
divergence
in
divergence_list
:
continue
s_m
=
divergence
.
getProperty
(
'simulation_movement'
)
delivery_movement
=
s_m
.
getDeliveryValue
()
assert
delivery_movement
==
self
quantity_gap
=
divergence
.
getProperty
(
'decision_value'
)
-
\
divergence
.
getProperty
(
'prevision_value'
)
delivery_movement
.
setQuantity
(
delivery_movement
.
getQuantity
()
+
\
quantity_gap
)
quantity_dict
[
s_m
]
=
\
divergence
.
getProperty
(
'decision_value'
)
# Finally, recalculate delivery_ratio
#
# Here, created/updated movements are not indexed yet. So we try to
# gather delivery relations from simulation movements.
delivery_dict
=
{}
for
s_m
in
simulation_movement_list
:
delivery_path
=
s_m
.
getDelivery
()
delivery_dict
[
delivery_path
]
=
\
delivery_dict
.
get
(
delivery_path
,
[])
+
\
[
s_m
]
for
s_m_list_per_movement
in
delivery_dict
.
values
():
total_quantity
=
sum
([
quantity_dict
.
get
(
s_m
,
s_m
.
getQuantity
())
\
for
s_m
in
s_m_list_per_movement
])
if
total_quantity
!=
0.0
:
for
s_m
in
s_m_list_per_movement
:
delivery_ratio
=
quantity_dict
.
get
(
s_m
,
s_m
.
getQuantity
())
\
/
total_quantity
s_m
.
edit
(
delivery_ratio
=
delivery_ratio
)
else
:
for
s_m
in
s_m_list_per_movement
:
delivery_ratio
=
1.0
/
len
(
s_m_list_per_movement
)
s_m
.
edit
(
delivery_ratio
=
delivery_ratio
)
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'solve'
)
def
solve
(
self
,
decision_list
):
"""Solves line according to decision list
"""
simulation_tool
=
self
.
getPortalObject
().
portal_simulation
solveMovement
=
simulation_tool
.
solveMovement
solve_result_list
=
[]
# accept + split
for
decision
in
[
q
for
q
in
decision_list
if
q
.
decision
!=
'adopt'
]:
if
decision
.
decision
==
'accept'
:
# accepting - in case of passed DeliverySolver use it, otherwise
# simply copy values to simulation
if
not
decision
.
delivery_solver_name
:
self
.
_distributePropertyToSimulation
(
decision
)
solveMovement
(
self
,
decision
.
delivery_solver_name
,
decision
.
target_solver_name
,
divergence_list
=
[
decision
.
divergence
])
elif
decision
.
decision
==
'split'
:
solveMovement
(
self
,
decision
.
delivery_solver_name
,
decision
.
target_solver_name
,
**
decision
.
split_kw
)
else
:
# aka - do nothing
pass
# adopt
adopt_decision_list
=
[
q
for
q
in
decision_list
\
if
q
.
decision
==
'adopt'
]
if
adopt_decision_list
:
self
.
_updatePropertyFromSimulation
(
adopt_decision_list
)
for
s_m
in
s_m_list_per_movement
:
delivery_ratio
=
1.0
/
len
(
s_m_list_per_movement
)
s_m
.
edit
(
delivery_ratio
=
delivery_ratio
)
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'solve'
)
def
solve
(
self
,
decision_list
):
"""Solves line according to decision list
"""
simulation_tool
=
self
.
getPortalObject
().
portal_simulation
solveMovement
=
simulation_tool
.
solveMovement
# accept + split
for
decision
in
[
q
for
q
in
decision_list
if
q
.
decision
!=
'adopt'
]:
if
decision
.
decision
==
'accept'
:
# accepting - in case of passed DeliverySolver use it, otherwise
# simply copy values to simulation
if
not
decision
.
delivery_solver_name
:
self
.
_distributePropertyToSimulation
(
decision
)
solveMovement
(
self
,
decision
.
delivery_solver_name
,
decision
.
target_solver_name
,
divergence_list
=
[
decision
.
divergence
])
elif
decision
.
decision
==
'split'
:
solveMovement
(
self
,
decision
.
delivery_solver_name
,
decision
.
target_solver_name
,
**
decision
.
split_kw
)
else
:
# aka - do nothing
pass
# adopt
adopt_decision_list
=
[
q
for
q
in
decision_list
\
if
q
.
decision
==
'adopt'
]
if
adopt_decision_list
:
self
.
_updatePropertyFromSimulation
(
adopt_decision_list
)
\ No newline at end of file
product/ERP5/Document/InventoryCell.py
View file @
4ee585d8
...
...
@@ -34,64 +34,62 @@ from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
from
Products.ERP5.Document.DeliveryCell
import
DeliveryCell
class
InventoryCell
(
DeliveryCell
):
"""
An InventoryCell allows to define specific inventory
for each variation of a resource in an inventory line.
"""
"""
An InventoryCell allows to define specific inventory
for each variation of a resource in an inventory line.
"""
meta_type
=
'ERP5 Inventory Cell'
portal_type
=
'Inventory Cell'
add_permission
=
Permissions
.
AddPortalContent
isInventoryMovement
=
ConstantGetter
(
'isInventoryMovement'
,
value
=
True
)
meta_type
=
'ERP5 Inventory Cell'
portal_type
=
'Inventory Cell'
add_permission
=
Permissions
.
AddPortalContent
isInventoryMovement
=
ConstantGetter
(
'isInventoryMovement'
,
value
=
True
)
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Amount
,
PropertySheet
.
InventoryMovement
,
PropertySheet
.
Task
,
PropertySheet
.
Movement
,
PropertySheet
.
Price
,
PropertySheet
.
Predicate
,
PropertySheet
.
MappedValue
,
PropertySheet
.
ItemAggregation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Amount
,
PropertySheet
.
InventoryMovement
,
PropertySheet
.
Task
,
PropertySheet
.
Movement
,
PropertySheet
.
Price
,
PropertySheet
.
Predicate
,
PropertySheet
.
MappedValue
,
PropertySheet
.
ItemAggregation
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalInventory'
)
def
getTotalInventory
(
self
):
"""
Returns the inventory, as cells are not supposed to contain more cells.
"""
return
self
.
getInventory
()
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalInventory'
)
def
getTotalInventory
(
self
):
"""
Returns the inventory, as cells are not supposed to contain more cells.
"""
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getQuantity'
)
def
getQuantity
(
self
):
"""
Computes a quantity which allows to reach inventory
"""
if
not
self
.
hasCellContent
():
# First check if quantity already exists
quantity
=
self
.
_baseGetQuantity
()
if
quantity
not
in
(
0.0
,
0
,
None
):
return
quantity
# Make sure inventory is defined somewhere (here or parent)
if
getattr
(
aq_base
(
self
),
'inventory'
,
None
)
is
None
:
return
0.0
# No inventory defined, so no quantity
return
self
.
getInventory
()
else
:
return
None
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getQuantity'
)
def
getQuantity
(
self
):
"""
Computes a quantity which allows to reach inventory
"""
if
not
self
.
hasCellContent
():
# First check if quantity already exists
quantity
=
self
.
_baseGetQuantity
()
if
quantity
not
in
(
0.0
,
0
,
None
):
return
quantity
# Make sure inventory is defined somewhere (here or parent)
if
getattr
(
aq_base
(
self
),
'inventory'
,
None
)
is
None
:
return
0.0
# No inventory defined, so no quantity
return
self
.
getInventory
()
else
:
return
None
# Inventory cataloging
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getConvertedInventory'
)
def
getConvertedInventory
(
self
):
"""
provides a default inventory value - None since
no inventory was defined.
"""
return
self
.
getInventory
()
# XXX quantity unit is missing
# Inventory cataloging
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getConvertedInventory'
)
def
getConvertedInventory
(
self
):
"""
provides a default inventory value - None since
no inventory was defined.
"""
return
self
.
getInventory
()
# XXX quantity unit is missing
\ No newline at end of file
product/ERP5/Document/InventoryLine.py
View file @
4ee585d8
...
...
@@ -36,112 +36,110 @@ from Products.ERP5.Document.Movement import Movement
from
Products.ERP5Type.Accessor.Constant
import
PropertyGetter
as
ConstantGetter
class
InventoryLine
(
DeliveryLine
):
"""
An Inventory Line describe the inventory of a resource, by variations.
"""
meta_type
=
'ERP5 Inventory Line'
portal_type
=
'Inventory Line'
add_permission
=
Permissions
.
AddPortalContent
isInventoryMovement
=
ConstantGetter
(
'isInventoryMovement'
,
value
=
True
)
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
XMLObject
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Amount
,
PropertySheet
.
InventoryMovement
,
PropertySheet
.
Task
,
PropertySheet
.
Arrow
,
PropertySheet
.
Movement
,
PropertySheet
.
VariationRange
,
PropertySheet
.
ItemAggregation
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalInventory'
)
def
getTotalInventory
(
self
):
"""
An Inventory Line describe the inventory of a resource, by variations.
Returns the inventory if no cell or the total inventory if cells
"""
meta_type
=
'ERP5 Inventory Line'
portal_type
=
'Inventory Line'
add_permission
=
Permissions
.
AddPortalContent
isInventoryMovement
=
ConstantGetter
(
'isInventoryMovement'
,
value
=
True
)
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
XMLObject
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Amount
,
PropertySheet
.
InventoryMovement
,
PropertySheet
.
Task
,
PropertySheet
.
Arrow
,
PropertySheet
.
Movement
,
PropertySheet
.
VariationRange
,
PropertySheet
.
ItemAggregation
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalInventory'
)
def
getTotalInventory
(
self
):
"""
Returns the inventory if no cell or the total inventory if cells
"""
if
not
self
.
hasCellContent
():
return
self
.
getInventory
()
else
:
total_quantity
=
0.0
for
cell
in
self
.
getCellValueList
(
base_id
=
'movement'
):
if
cell
.
getInventory
()
is
not
None
:
total_quantity
+=
cell
.
getInventory
()
return
total_quantity
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getQuantity'
)
def
getQuantity
(
self
):
"""
Computes a quantity which allows to reach inventory
"""
if
not
self
.
hasCellContent
():
# First check if quantity already exists
quantity
=
self
.
_baseGetQuantity
()
if
quantity
not
in
(
0.0
,
0
,
None
):
return
quantity
# Make sure inventory is defined somewhere (here or parent)
inventory
=
getattr
(
aq_base
(
self
),
'inventory'
,
None
)
if
inventory
is
not
None
:
return
inventory
if
not
self
.
hasCellContent
():
return
self
.
getInventory
()
else
:
total_quantity
=
0.0
for
cell
in
self
.
getCellValueList
(
base_id
=
'movement'
):
if
cell
.
getInventory
()
is
not
None
:
total_quantity
+=
cell
.
getInventory
()
return
total_quantity
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getQuantity'
)
def
getQuantity
(
self
):
"""
Computes a quantity which allows to reach inventory
"""
if
not
self
.
hasCellContent
():
# First check if quantity already exists
quantity
=
self
.
_baseGetQuantity
()
if
quantity
not
in
(
0.0
,
0
,
None
):
return
quantity
else
:
return
None
# Inventory cataloging
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getConvertedInventory'
)
def
getConvertedInventory
(
self
):
"""
provides a default inventory value - None since
no inventory was defined.
"""
return
self
.
getInventory
()
# XXX quantity unit is missing
# Required for indexing
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getInventoriatedQuantity'
)
def
getInventoriatedQuantity
(
self
):
"""
Take into account efficiency in converted target quantity
"""
return
Movement
.
getInventoriatedQuantity
(
self
)
# XXX: Dirty but required for erp5_banking_core
getBaobabSourceUid
=
lambda
x
:
x
.
getSourceUid
()
getBaobabSourceUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationUid
=
lambda
x
:
x
.
getDestinationUid
()
getBaobabDestinationUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceSectionUid
=
lambda
x
:
x
.
getSourceSectionUid
()
getBaobabSourceSectionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationSectionUid
=
lambda
x
:
x
.
getDestinationSectionUid
()
getBaobabDestinationSectionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourcePaymentUid
=
lambda
x
:
x
.
getSourcePaymentUid
()
getBaobabSourcePaymentUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationPaymentUid
=
lambda
x
:
x
.
getDestinationPaymentUid
()
getBaobabDestinationPaymentUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceFunctionUid
=
lambda
x
:
x
.
getSourceFunctionUid
()
getBaobabSourceFunctionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationFunctionUid
=
lambda
x
:
x
.
getDestinationFunctionUid
()
getBaobabDestinationFunctionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceProjectUid
=
lambda
x
:
x
.
getSourceProjectUid
()
getBaobabSourceProjectUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationProjectUid
=
lambda
x
:
x
.
getDestinationProjectUid
()
getBaobabDestinationProjectUid__roles__
=
PermissionRole
(
Permissions
.
View
)
# Make sure inventory is defined somewhere (here or parent)
inventory
=
getattr
(
aq_base
(
self
),
'inventory'
,
None
)
if
inventory
is
not
None
:
return
inventory
return
quantity
else
:
return
None
# Inventory cataloging
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getConvertedInventory'
)
def
getConvertedInventory
(
self
):
"""
provides a default inventory value - None since
no inventory was defined.
"""
return
self
.
getInventory
()
# XXX quantity unit is missing
# Required for indexing
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getInventoriatedQuantity'
)
def
getInventoriatedQuantity
(
self
):
"""
Take into account efficiency in converted target quantity
"""
return
Movement
.
getInventoriatedQuantity
(
self
)
# XXX: Dirty but required for erp5_banking_core
getBaobabSourceUid
=
lambda
x
:
x
.
getSourceUid
()
getBaobabSourceUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationUid
=
lambda
x
:
x
.
getDestinationUid
()
getBaobabDestinationUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceSectionUid
=
lambda
x
:
x
.
getSourceSectionUid
()
getBaobabSourceSectionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationSectionUid
=
lambda
x
:
x
.
getDestinationSectionUid
()
getBaobabDestinationSectionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourcePaymentUid
=
lambda
x
:
x
.
getSourcePaymentUid
()
getBaobabSourcePaymentUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationPaymentUid
=
lambda
x
:
x
.
getDestinationPaymentUid
()
getBaobabDestinationPaymentUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceFunctionUid
=
lambda
x
:
x
.
getSourceFunctionUid
()
getBaobabSourceFunctionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationFunctionUid
=
lambda
x
:
x
.
getDestinationFunctionUid
()
getBaobabDestinationFunctionUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabSourceProjectUid
=
lambda
x
:
x
.
getSourceProjectUid
()
getBaobabSourceProjectUid__roles__
=
PermissionRole
(
Permissions
.
View
)
getBaobabDestinationProjectUid
=
lambda
x
:
x
.
getDestinationProjectUid
()
getBaobabDestinationProjectUid__roles__
=
PermissionRole
(
Permissions
.
View
)
\ No newline at end of file
product/ERP5/Document/OrderCell.py
View file @
4ee585d8
...
...
@@ -33,59 +33,57 @@ from Products.ERP5Type import Permissions, PropertySheet
from
Products.ERP5.Document.DeliveryCell
import
DeliveryCell
class
OrderCell
(
DeliveryCell
):
"""
A OrderCell allows to define specific quantities
for each variation of a resource in a delivery line.
"""
"""
A OrderCell allows to define specific quantities
for each variation of a resource in a delivery line.
"""
meta_type
=
'ERP5 Order Cell'
portal_type
=
'Order Cell'
isCell
=
1
meta_type
=
'ERP5 Order Cell'
portal_type
=
'Order Cell'
isCell
=
1
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Arrow
,
PropertySheet
.
Amount
,
PropertySheet
.
Task
,
PropertySheet
.
Movement
,
PropertySheet
.
Price
,
PropertySheet
.
Predicate
,
PropertySheet
.
MappedValue
,
PropertySheet
.
ItemAggregation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Arrow
,
PropertySheet
.
Amount
,
PropertySheet
.
Task
,
PropertySheet
.
Movement
,
PropertySheet
.
Price
,
PropertySheet
.
Predicate
,
PropertySheet
.
MappedValue
,
PropertySheet
.
ItemAggregation
)
def
reindexObject
(
self
,
*
k
,
**
kw
):
"""
Reindex children and simulation
"""
self
.
recursiveReindexObject
(
*
k
,
**
kw
)
def
reindexObject
(
self
,
*
k
,
**
kw
):
"""
Reindex children and simulation
"""
self
.
recursiveReindexObject
(
*
k
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isMovement'
)
def
isMovement
(
self
):
"""
should be considered as a movement if the parent does not have sub lines
"""
return
not
self
.
getParentValue
().
hasLineContent
()
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'isMovement'
)
def
isMovement
(
self
):
"""
should be considered as a movement if the parent does not have sub lines
"""
return
not
self
.
getParentValue
().
hasLineContent
()
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalPrice'
)
def
getTotalPrice
(
self
,
default
=
0.0
,
*
args
,
**
kw
):
"only return a value if self is a movement"
if
not
self
.
isMovement
():
return
default
return
DeliveryCell
.
getTotalPrice
(
self
,
default
=
default
,
*
args
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalQuantity'
)
def
getTotalQuantity
(
self
,
default
=
0.0
,
*
args
,
**
kw
):
"only return a value if self is a movement"
if
not
self
.
isMovement
():
return
default
return
DeliveryCell
.
getTotalQuantity
(
self
,
default
=
default
,
*
args
,
**
kw
)
def
getTotalPrice
(
self
,
default
=
0.0
,
*
args
,
**
kw
):
"only return a value if self is a movement"
if
not
self
.
isMovement
():
return
default
return
DeliveryCell
.
getTotalPrice
(
self
,
default
=
default
,
*
args
,
**
kw
)
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
'getTotalQuantity'
)
def
getTotalQuantity
(
self
,
default
=
0.0
,
*
args
,
**
kw
):
"only return a value if self is a movement"
if
not
self
.
isMovement
():
return
default
return
DeliveryCell
.
getTotalQuantity
(
self
,
default
=
default
,
*
args
,
**
kw
)
\ No newline at end of file
product/ERP5/Document/OrderLine.py
View file @
4ee585d8
...
...
@@ -32,27 +32,26 @@ from Products.ERP5Type import Permissions, PropertySheet
from
Products.ERP5.Document.DeliveryLine
import
DeliveryLine
class
OrderLine
(
DeliveryLine
):
"""
A order line defines quantity and price
"""
"""
A order line defines quantity and price
"""
meta_type
=
'ERP5 Order Line'
portal_type
=
'Order Line'
meta_type
=
'ERP5 Order Line'
portal_type
=
'Order Line'
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative security
security
=
ClassSecurityInfo
()
security
.
declareObjectProtected
(
Permissions
.
AccessContentsInformation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
XMLObject
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Amount
,
PropertySheet
.
Task
,
PropertySheet
.
DublinCore
,
PropertySheet
.
Arrow
,
PropertySheet
.
Movement
,
PropertySheet
.
Price
,
PropertySheet
.
VariationRange
,
PropertySheet
.
ItemAggregation
)
# Declarative properties
property_sheets
=
(
PropertySheet
.
Base
,
PropertySheet
.
XMLObject
,
PropertySheet
.
CategoryCore
,
PropertySheet
.
Amount
,
PropertySheet
.
Task
,
PropertySheet
.
DublinCore
,
PropertySheet
.
Arrow
,
PropertySheet
.
Movement
,
PropertySheet
.
Price
,
PropertySheet
.
VariationRange
,
PropertySheet
.
ItemAggregation
)
\ No newline at end of file
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