Commit 83d055b1 authored by Jean-Paul Smets's avatar Jean-Paul Smets

copied from erp5_simulation

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/amount_generator@37494 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 71bea97b
...@@ -25,28 +25,113 @@ ...@@ -25,28 +25,113 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# #
############################################################################## ##############################################################################
"""
XXX This file is experimental for new simulation implementation, and
will replace DeliveryRule.
"""
import zope.interface
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Legacy.Document.DeliveryRule import DeliveryRule from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
from Products.ERP5.mixin.movement_collection_updater import \
MovementCollectionUpdaterMixin
class DeliveryRootSimulationRule(DeliveryRule): class DeliveryRootSimulationRule(RuleMixin, MovementCollectionUpdaterMixin, Predicate):
"""
Delivery Root Simulation Rule is a root level rule for Deliveries.
""" """
Delivery Rule object make sure an Delivery in the simulation
is consistent with the real delivery
WARNING: what to do with movement split ?
"""
# CMF Type Definition # CMF Type Definition
meta_type = 'ERP5 Delivery Root Simulation Rule' meta_type = 'ERP5 Delivery Root Simulation Rule'
portal_type = 'Delivery Root Simulation Rule' portal_type = 'Delivery Root Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
def _getExpandablePropertyUpdateDict(self, applied_rule, movement, # Declarative interfaces
business_link, current_property_dict): zope.interface.implements(interfaces.IRule,
"""Order rule specific update dictionary""" interfaces.IDivergenceController,
return { interfaces.IMovementCollectionUpdater,)
'delivery': movement.getRelativeUrl(),
} # Default Properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Task,
PropertySheet.Predicate,
PropertySheet.Reference,
PropertySheet.Version,
PropertySheet.Rule
)
def _getMovementGenerator(self, context):
"""
Return the movement generator to use in the expand process
"""
return DeliveryRuleMovementGenerator(applied_rule=context, rule=self)
def _getMovementGeneratorContext(self, context):
"""
Return the movement generator context to use for expand
"""
return context
def _getMovementGeneratorMovementList(self, context):
"""
Return the movement lists to provide to the movement generator
"""
return []
def _isProfitAndLossMovement(self, movement):
# For a kind of trade rule, a profit and loss movement lacks source
# or destination.
return (movement.getSource() is None or movement.getDestination() is None)
class DeliveryRuleMovementGenerator(MovementGeneratorMixin):
def _getInputMovementList(self, movement_list=None, rounding=None):
"""Input movement list comes from delivery"""
delivery = self._applied_rule.getDefaultCausalityValue()
if delivery is None:
return []
else:
result = []
existing_movement_list = self._applied_rule.objectValues()
for movement in delivery.getMovementList(
portal_type=delivery.getPortalDeliveryMovementTypeList()):
simulation_movement = self._getDeliveryRelatedSimulationMovement(movement)
if simulation_movement is None or \
simulation_movement in existing_movement_list:
result.append(movement)
return result
def _getDeliveryRelatedSimulationMovement(self, delivery_movement):
"""Helper method to get the delivery related simulation movement.
This method is more robust than simply calling getDeliveryRelatedValue
which will not work if simulation movements are not indexed.
"""
simulation_movement = delivery_movement.getDeliveryRelatedValue()
if simulation_movement is not None:
return simulation_movement
# simulation movement was not found, maybe simply because it's not indexed
# yet. We'll look in the simulation tree and try to find it anyway before
# creating another simulation movement.
# Try to find the one from trade model rule, which is the most common case
# where we may expand again before indexation of simulation movements is
# finished.
delivery = delivery_movement.getExplanationValue()
for movement in delivery.getMovementList():
related_simulation_movement = movement.getDeliveryRelatedValue()
if related_simulation_movement is not None:
for applied_rule in related_simulation_movement.contentValues():
for simulation_movement in applied_rule.contentValues():
if simulation_movement.getDeliveryValue() == delivery_movement:
return simulation_movement
return None
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved. # Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
...@@ -24,38 +25,81 @@ ...@@ -24,38 +25,81 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# #
############################################################################## ##############################################################################
"""
XXX This file is experimental for new simulation implementation, and
will replace DeliveryRule.
"""
import zope.interface
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Legacy.Document.Rule import Rule from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
from Products.ERP5.mixin.movement_collection_updater import \
MovementCollectionUpdaterMixin
class DeliverySimulationRule(Rule): class DeliverySimulationRule(RuleMixin, MovementCollectionUpdaterMixin, Predicate):
"""
Delivery Simulation Rule expand simulation created by a order root
simulation rule or delivery root simulation rule.
""" """
Delivery Rule object make sure an Delivery in the simulation
is consistent with the real delivery
WARNING: what to do with movement split ?
"""
# CMF Type Definition # CMF Type Definition
meta_type = 'ERP5 Delivery Simulation Rule' meta_type = 'ERP5 Delivery Simulation Rule'
portal_type = 'Delivery Simulation Rule' portal_type = 'Delivery Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
security.declareProtected(Permissions.ModifyPortalContent, 'expand') # Declarative interfaces
def expand(self, applied_rule, force=0, **kw): zope.interface.implements(interfaces.IRule,
interfaces.IDivergenceController,
interfaces.IMovementCollectionUpdater,)
# Default Properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Task,
PropertySheet.Predicate,
PropertySheet.Reference,
PropertySheet.Version,
PropertySheet.Rule
)
def _getMovementGenerator(self, context):
"""
Return the movement generator to use in the expand process
"""
return DeliveryRuleMovementGenerator(applied_rule=context, rule=self)
def _getMovementGeneratorContext(self, context):
""" """
Expands the rule: Return the movement generator context to use for expand
- generate a list of previsions
- compare the prevision with existing children
- get the list of existing movements (immutable, mutable, deletable)
- compute the difference between prevision and existing (add,
modify, remove)
- add/modify/remove child movements to match prevision
""" """
return Rule._expand(self, applied_rule, force=force, **kw) return context
def _getMovementGeneratorMovementList(self, context):
"""
Return the movement lists to provide to the movement generator
"""
return []
def _isProfitAndLossMovement(self, movement):
# For a kind of trade rule, a profit and loss movement lacks source
# or destination.
return (movement.getSource() is None or movement.getDestination() is None)
class DeliveryRuleMovementGenerator(MovementGeneratorMixin):
def _getUpdatePropertyDict(self, input_movement):
# Override default mixin implementation
return {'order': None,
'delivery': None,}
def isDeliverable(self, movement): def _getInputMovementList(self, movement_list=None, rounding=None):
return movement.getResource() is not None return [self._applied_rule.getParentValue(),]
...@@ -25,28 +25,112 @@ ...@@ -25,28 +25,112 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# #
############################################################################## ##############################################################################
"""
XXX This file is experimental for new simulation implementation, and
will replace InvoiceRule.
"""
import zope.interface
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Legacy.Document.InvoiceRule import InvoiceRule from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
from Products.ERP5.mixin.movement_collection_updater import \
MovementCollectionUpdaterMixin
class InvoiceRootSimulationRule(InvoiceRule): class InvoiceRootSimulationRule(RuleMixin, MovementCollectionUpdaterMixin, Predicate):
""" """
Invoice Root Simulation Rule is a root level rule for Deliveries. InvoiceRule and DeliveryRule seems to be identical. Keep it for
compatibility only.
""" """
# CMF Type Definition # CMF Type Definition
meta_type = 'ERP5 Invoice Root Simulation Rule' meta_type = 'ERP5 Invoice Root Simulation Rule'
portal_type = 'Invoice Root Simulation Rule' portal_type = 'Invoice Root Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
def _getExpandablePropertyUpdateDict(self, applied_rule, movement, # Declarative interfaces
business_link, current_property_dict): zope.interface.implements(interfaces.IRule,
"""Order rule specific update dictionary""" interfaces.IDivergenceController,
return { interfaces.IMovementCollectionUpdater,)
'delivery': movement.getRelativeUrl(),
} # Default Properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Task,
PropertySheet.Predicate,
PropertySheet.Reference,
PropertySheet.Version,
PropertySheet.Rule
)
def _getMovementGenerator(self, context):
"""
Return the movement generator to use in the expand process
"""
return InvoiceRuleMovementGenerator(applied_rule=context, rule=self)
def _getMovementGeneratorContext(self, context):
"""
Return the movement generator context to use for expand
"""
return context
def _getMovementGeneratorMovementList(self, context):
"""
Return the movement lists to provide to the movement generator
"""
return []
def _isProfitAndLossMovement(self, movement):
# For a kind of trade rule, a profit and loss movement lacks source
# or destination.
return (movement.getSource() is None or movement.getDestination() is None)
class InvoiceRuleMovementGenerator(MovementGeneratorMixin):
def _getInputMovementList(self, movement_list=None, rounding=None):
"""Input movement list comes from delivery"""
delivery = self._applied_rule.getDefaultCausalityValue()
if delivery is None:
return []
else:
ret = []
existing_movement_list = self._applied_rule.objectValues()
for movement in delivery.getMovementList(
portal_type=(delivery.getPortalInvoiceMovementTypeList() + \
delivery.getPortalTaxMovementTypeList())): # This is bad XXX-JPS - use use
simulation_movement = self._getDeliveryRelatedSimulationMovement(movement)
if simulation_movement is None or \
simulation_movement in existing_movement_list:
ret.append(movement)
return ret
def _getDeliveryRelatedSimulationMovement(self, delivery_movement):
"""Helper method to get the delivery related simulation movement.
This method is more robust than simply calling getDeliveryRelatedValue
which will not work if simulation movements are not indexed.
"""
simulation_movement = delivery_movement.getDeliveryRelatedValue()
if simulation_movement is not None:
return simulation_movement
# simulation movement was not found, maybe simply because it's not indexed
# yet. We'll look in the simulation tree and try to find it anyway before
# creating another simulation movement.
# Try to find the one from trade model rule, which is the most common case
# where we may expand again before indexation of simulation movements is
# finished.
delivery = delivery_movement.getExplanationValue()
for movement in delivery.getMovementList():
related_simulation_movement = movement.getDeliveryRelatedValue()
if related_simulation_movement is not None:
for applied_rule in related_simulation_movement.contentValues():
for simulation_movement in applied_rule.contentValues():
if simulation_movement.getDeliveryValue() == delivery_movement:
return simulation_movement
return None
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved. # Copyright (c) 2009 Nexedi SARL and Contributors. All Rights Reserved.
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential # programmers who take the whole responsibility of assessing all potential
...@@ -21,24 +22,76 @@ ...@@ -21,24 +22,76 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# #
############################################################################## ##############################################################################
"""
XXX This file is experimental for new simulation implementation, and
will replace InvoicingRule.
"""
import zope.interface
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Legacy.Document.InvoicingRule import InvoicingRule from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
from Products.ERP5.mixin.movement_collection_updater import \
MovementCollectionUpdaterMixin
class InvoiceSimulationRule(InvoicingRule): class InvoiceSimulationRule(RuleMixin, MovementCollectionUpdaterMixin, Predicate):
""" """
Invoice Simulation Rule expand simulation created by a order or delivery rule. Invoicing Rule expand simulation created by a order or delivery rule.
""" """
# CMF Type Definition # CMF Type Definition
meta_type = 'ERP5 Invoice Simulation Rule' meta_type = 'ERP5 Invoice Simulation Rule'
portal_type = 'Invoice Simulation Rule' portal_type = 'Invoice Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative interfaces
zope.interface.implements(interfaces.IRule,
interfaces.IDivergenceController,
interfaces.IMovementCollectionUpdater,)
# Default Properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Task,
PropertySheet.Predicate,
PropertySheet.Reference,
PropertySheet.Version,
PropertySheet.Rule
)
def _getMovementGenerator(self, context):
"""
Return the movement generator to use in the expand process
"""
return InvoicingRuleMovementGenerator(applied_rule=context, rule=self)
def _getMovementGeneratorContext(self, context):
"""
Return the movement generator context to use for expand
"""
return context
def _getMovementGeneratorMovementList(self, context, movement_list=None, rounding=None):
"""
Return the movement lists to provide to the movement generator
"""
return []
def _isProfitAndLossMovement(self, movement):
# For a kind of trade rule, a profit and loss movement lacks source
# or destination.
return (movement.getSource() is None or movement.getDestination() is None)
class InvoicingRuleMovementGenerator(MovementGeneratorMixin):
def _getInputMovementList(self, movement_list=None, rounding=None):
return [self._applied_rule.getParentValue(),]
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved. # Copyright (c) 2009 Nexedi SARL and Contributors. All Rights Reserved.
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential # programmers who take the whole responsibility of assessing all potential
...@@ -21,27 +22,207 @@ ...@@ -21,27 +22,207 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# #
############################################################################## ##############################################################################
"""
XXX This file is experimental for new simulation implementation, and
will replace InvoicingRule.
"""
import zope.interface
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Legacy.Document.InvoiceTransactionRule import InvoiceTransactionRule from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
from Products.ERP5.mixin.movement_collection_updater import \
MovementCollectionUpdaterMixin
from Products.ERP5.Document.PredicateMatrix import PredicateMatrix
class InvoiceTransactionSimulationRule(InvoiceTransactionRule):
class InvoiceTransactionSimulationRule(RuleMixin, MovementCollectionUpdaterMixin, Predicate, PredicateMatrix):
""" """
Invoice Transaction Simulation Rule object generates accounting movements for Invoice Transaction Rule object generates accounting movements for
each invoice movement based on category membership and other each invoice movement based on category membership and other
predicated. Template accounting movements are stored in cells inside predicated. Template accounting movements are stored in cells inside
an instance of the InvoiceTransactionRule. an instance of the InvoiceTransactionRule.
""" """
# CMF Type Definition # CMF Type Definition
meta_type = 'ERP5 Invoice Transaction Simulation Rule' meta_type = 'ERP5 Invoice Transaction Simulation Rule'
portal_type = 'Invoice Transaction Simulation Rule' portal_type = 'Invoice Transaction Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative interfaces
zope.interface.implements(interfaces.IRule,
interfaces.IDivergenceController,
interfaces.IMovementCollectionUpdater,)
# Default Properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Task,
PropertySheet.Predicate,
PropertySheet.Reference,
PropertySheet.Version,
PropertySheet.Rule
)
def _getMovementGenerator(self, context):
"""
Return the movement generator to use in the expand process
"""
return InvoiceTransactionRuleMovementGenerator(applied_rule=context, rule=self)
def _getMovementGeneratorContext(self, context):
"""
Return the movement generator context to use for expand
"""
return context
def _getMovementGeneratorMovementList(self, context):
"""
Return the movement lists to provide to the movement generator
"""
return []
def _isProfitAndLossMovement(self, movement):
# For a kind of trade rule, a profit and loss movement lacks source
# or destination.
return (movement.getSource() is None or movement.getDestination() is None)
class InvoiceTransactionRuleMovementGenerator(MovementGeneratorMixin):
def getGeneratedMovementList(self, movement_list=None, rounding=False):
"""
Input movement list comes from order
XXX This implementation is very primitive, and does not support BPM,
i.e. business paths are not taken into account.
"""
ret = []
rule = self._rule
# input_movement, business_path = rule._getInputMovementAndPathTupleList(
# applied_rule)[0]
input_movement = self._applied_rule.getParentValue()
parent_movement = self._applied_rule.getParentValue()
# Find a matching cell
cell = rule._getMatchingCell(input_movement)
if cell is not None:
for accounting_rule_cell_line in cell.objectValues():
# get the resource (in that order):
# * resource from the invoice (using deliveryValue)
# * price_currency from the invoice
# * price_currency from the parents simulation movement's
# deliveryValue
# * price_currency from the top level simulation movement's
# orderValue
resource = None
invoice_line = input_movement.getDeliveryValue()
if invoice_line is not None :
invoice = invoice_line.getExplanationValue()
resource = invoice.getProperty('resource',
invoice.getProperty('price_currency', None))
if resource is None :
# search the resource on parents simulation movement's deliveries
simulation_movement = parent_movement
portal_simulation = self._applied_rule.getPortalObject().portal_simulation
while resource is None and \
simulation_movement != portal_simulation :
delivery = simulation_movement.getDeliveryValue()
if delivery is not None:
resource = delivery.getProperty('price_currency', None)
if (resource is None) and \
(simulation_movement.getParentValue().getParentValue() \
== portal_simulation) :
# we are on the first simulation movement, we'll try
# to get the resource from it's order price currency.
order = simulation_movement.getOrderValue()
if order is not None:
resource = order.getProperty('price_currency', None)
simulation_movement = simulation_movement\
.getParentValue().getParentValue()
if resource is None :
# last resort : get the resource from the rule
resource = accounting_rule_cell_line.getResource() \
or cell.getResource()
# XXX we need business path here?
kw = self._getPropertyAndCategoryList(input_movement, None, rule)
kw.update(
delivery=None,
source=[accounting_rule_cell_line.getSource()],
destination=[accounting_rule_cell_line.getDestination()],
quantity=(input_movement.getCorrectedQuantity() *
input_movement.getPrice(0.0)) *
accounting_rule_cell_line.getQuantity(),
resource=[resource],
price=1,
)
if resource is not None:
#set asset_price on movement when resource is different from price
#currency of the source/destination section
destination_exchange_ratio, precision = self \
._getCurrencyRatioAndPrecisionByArrow(
rule, 'destination_section', kw)
if destination_exchange_ratio is not None:
kw.update(destination_total_asset_price=round(
(destination_exchange_ratio*
parent_movement.getTotalPrice()),precision))
source_exchange_ratio, precision = self \
._getCurrencyRatioAndPrecisionByArrow(
rule, 'source_section', kw)
if source_exchange_ratio is not None:
kw.update(source_total_asset_price=round(
(source_exchange_ratio*
parent_movement.getTotalPrice()),precision))
if accounting_rule_cell_line.hasProperty(
'generate_prevision_script_id'):
generate_prevision_script_id = \
accounting_rule_cell_line.getGeneratePrevisionScriptId()
kw.update(getattr(input_movement,
generate_prevision_script_id)(kw))
simulation_movement = self._applied_rule.newContent(
portal_type=RuleMixin.movement_type,
temp_object=True,
**kw)
ret.append(simulation_movement)
return ret
def _getCurrencyRatioAndPrecisionByArrow(self, rule, arrow, prevision_line):
from Products.ERP5Type.Document import newTempSimulationMovement
try:
prevision_currency = prevision_line['resource'][0]
except IndexError:
prevision_currency = None
exchange_ratio = None
precision = None
try:
section = prevision_line.get(arrow, [])[0]
except IndexError:
section = None
if section is not None:
currency_url = rule.restrictedTraverse(section).getProperty(
'price_currency', None)
else:
currency_url = None
if currency_url is not None and prevision_currency != currency_url:
precision = section.getPriceCurrencyValue() \
.getQuantityPrecision()
temporary_movement = newTempSimulationMovement(rule.getPortalObject(),
'1', **prevision_line)
exchange_ratio = rule.restrictedTraverse(currency_url).getPrice(
context=temporary_movement.asContext(
categories=['price_currency/%s' % currency_url,
'resource/%s' % prevision_currency],
start_date=temporary_movement.getStartDate()))
return exchange_ratio, precision
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved. # Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solanes <jp@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs # consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial # End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software # guarantees and support are strongly adviced to contract a Free Software
# Service Company # Service Company
# #
# This program is Free Software; you can redistribute it and/or # This program is Free Software; you can redistribute it and/or
...@@ -24,15 +22,23 @@ ...@@ -24,15 +22,23 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# #
############################################################################## ##############################################################################
"""
XXX This file is experimental for new simulation implementation, and
will replace OrderRule.
"""
import zope.interface
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Legacy.Document.OrderRule import OrderRule from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
from Products.ERP5.mixin.movement_collection_updater import \
MovementCollectionUpdaterMixin
class OrderRootSimulationRule(OrderRule): class OrderRootSimulationRule(RuleMixin, MovementCollectionUpdaterMixin, Predicate):
""" """
Order Rule object make sure an Order in the simulation Order Rule object make sure an Order in the simulation
is consistent with the real order is consistent with the real order
...@@ -47,9 +53,54 @@ class OrderRootSimulationRule(OrderRule): ...@@ -47,9 +53,54 @@ class OrderRootSimulationRule(OrderRule):
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
def _getExpandablePropertyUpdateDict(self, applied_rule, movement, # Declarative interfaces
business_link, current_property_dict): zope.interface.implements(interfaces.IRule,
"""Order rule specific update dictionary""" interfaces.IDivergenceController,
return { interfaces.IMovementCollectionUpdater,)
'delivery': movement.getRelativeUrl(),
} # Default Properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Task,
PropertySheet.Predicate,
PropertySheet.Reference,
PropertySheet.Version,
PropertySheet.Rule
)
def _getMovementGenerator(self, context):
"""
Return the movement generator to use in the expand process
"""
return OrderRuleMovementGenerator(applied_rule=context, rule=self)
def _getMovementGeneratorContext(self, context):
"""
Return the movement generator context to use for expand
"""
return context
def _getMovementGeneratorMovementList(self, context):
"""
Return the movement lists to provide to the movement generator
"""
return []
def _isProfitAndLossMovement(self, movement):
# For a kind of trade rule, a profit and loss movement lacks source
# or destination.
return (movement.getSource() is None or movement.getDestination() is None)
class OrderRuleMovementGenerator(MovementGeneratorMixin):
def _getInputMovementList(self, movement_list=None, rounding=None):
"""Input movement list comes from order"""
order = self._applied_rule.getDefaultCausalityValue()
if order is None:
return []
else:
return order.getMovementList(
portal_type=order.getPortalOrderMovementTypeList())
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved. # Copyright (c) 2010 Nexedi SARL and Contributors. All Rights Reserved.
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential # programmers who take the whole responsibility of assessing all potential
...@@ -24,125 +25,118 @@ ...@@ -24,125 +25,118 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# #
############################################################################## ##############################################################################
"""
XXX This file is experimental for new simulation implementation, and
will replace PaymentRule.
"""
import zope.interface
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Legacy.Document.Rule import Rule from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.Document.PredicateMatrix import PredicateMatrix from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
from Products.ERP5.mixin.movement_collection_updater import \
MovementCollectionUpdaterMixin
class PaymentSimulationRule(Rule, PredicateMatrix): class PaymentSimulationRule(RuleMixin, MovementCollectionUpdaterMixin, Predicate):
""" """
Payment Simulation Rule generates payment simulation movements from Payment Rule generates payment simulation movement from invoice
accounting / invoice transaction simulation movements. transaction simulation movements.
""" """
# CMF Type Definition # CMF Type Definition
meta_type = 'ERP5 Payment Simulation Rule' meta_type = 'ERP5 Payment Simulation Rule'
portal_type = 'Payment Simulation Rule' portal_type = 'Payment Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
def _generatePrevisionList(self, applied_rule, **kw): # Declarative interfaces
zope.interface.implements(interfaces.IRule,
interfaces.IDivergenceController,
interfaces.IMovementCollectionUpdater,)
# Default Properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Task,
PropertySheet.Predicate,
PropertySheet.Reference,
PropertySheet.Version,
PropertySheet.Rule
)
def _getMovementGenerator(self, context):
""" """
Generate a list of dictionaries, that contain calculated content of Return the movement generator to use in the expand process
current Simulation Movements in applied rule.
based on its context (parent movement, delivery, configuration ...)
These previsions are returned as dictionaries.
* source and destination (i.e. account) are provided by rule cells.
* start_date, stop_date and quantity are calculated according to
payment conditions.
""" """
# Find an input movement and using Payment Conditions. return PaymentRuleMovementGenerator(applied_rule=context, rule=self)
# XXX we also need to support local Payment Conditions, that are not
# provided by BPM.
movement_and_tuple_list = self._getInputMovementAndPathTupleList(
applied_rule)
input_movement = movement_and_tuple_list[0][0]
payment_condition_list = []
# try to find local payment conditions from the upper level delivery
rule = applied_rule
movement = input_movement
delivery = movement.getDeliveryValue()
while delivery is None and not(rule.isRootAppliedRule()):
rule = movement.getParentValue()
movement = rule.getParentValue()
delivery = movement.getDeliveryValue()
if delivery is not None:
payment_condition_list = delivery.getPaymentConditionValueList()
# try to find payment conditions in specialised trade conditions
if len(payment_condition_list) == 0:
specialise = input_movement.getSpecialiseValue()
if specialise is None and delivery is not None:
specialise = delivery.getSpecialiseValue()
if specialise is not None:
payment_condition_list = specialise.getPaymentConditionValueList()
# try to use payment conditions in BPM configuration def _getMovementGeneratorContext(self, context):
if len(payment_condition_list) == 0: """
payment_condition_list = [x[1] for x in movement_and_tuple_list if x[1] is not None] Return the movement generator context to use for expand
"""
kw = self._getExpandablePropertyDict(applied_rule, input_movement, None) return context
prevision_list = []
# Find a matching cell
cell = self._getMatchingCell(input_movement)
if cell is not None : # else, we do nothing def _getMovementGeneratorMovementList(self, context):
for payment_condition in payment_condition_list: """
aggregated_ammount_list = payment_condition.getAggregatedAmountList( Return the movement lists to provide to the movement generator
input_movement, movement_list=[input_movement]) """
assert len(aggregated_ammount_list) == 1 return []
aggregated_ammount = aggregated_ammount_list[0]
start_date = aggregated_ammount.getStartDate()
stop_date = aggregated_ammount.getStopDate()
quantity = aggregated_ammount.getQuantity()
payment_mode = payment_condition.getPaymentMode()
# one for payable def _isProfitAndLossMovement(self, movement):
prevision_line = kw.copy() # For a kind of trade rule, a profit and loss movement lacks source
prevision_line.update( # or destination.
start_date=start_date, return (movement.getSource() is None or movement.getDestination() is None)
stop_date=stop_date,
source=input_movement.getSource(),
destination=input_movement.getDestination(),
payment_mode=payment_mode,
quantity=-quantity
)
prevision_list.append(prevision_line)
# one for cash, bank etc.
payment_rule_cell_line_list = cell.objectValues()
assert len(payment_rule_cell_line_list) == 1
payment_rule_cell_line = payment_rule_cell_line_list[0]
prevision_line = kw.copy()
prevision_line.update(
start_date=start_date,
stop_date=stop_date,
source=payment_rule_cell_line.getSource(),
destination=payment_rule_cell_line.getDestination(),
payment_mode=payment_mode,
quantity=quantity
)
prevision_list.append(prevision_line)
return prevision_list
security.declareProtected(Permissions.ModifyPortalContent, 'expand') class PaymentRuleMovementGenerator(MovementGeneratorMixin):
def expand(self, applied_rule, **kw): def getGeneratedMovementList(self, movement_list=None, rounding=False):
"""Expands the current movement downward.
""" """
return Rule._expand(self, applied_rule, **kw) Input movement list comes from parent.
# Matrix related XXX This implementation using Business Path, not Payment Condition.
security.declareProtected( Permissions.ModifyPortalContent,
'newCellContent' )
def newCellContent(self, id, portal_type='Accounting Rule Cell', **kw):
"""Overriden to specify default portal type
""" """
return self.newContent(id=id, portal_type=portal_type, **kw) ret = []
rule = self._rule
for input_movement, business_path in self \
._getInputMovementAndPathTupleList(movement_list=movement_list, rounding=rounding):
# Payment Rule does not work with Business Path
if business_path is None:
continue
# Since we need to consider business_path only for bank movement,
# not for payable movement, we pass None as business_path here.
kw = self._getPropertyAndCategoryList(input_movement, None, rule)
kw.update({'order':None, 'delivery':None})
quantity = kw.pop('quantity', 0)
efficiency = business_path.getEfficiency()
if efficiency:
quantity *= efficiency
start_date = business_path.getExpectedStartDate(input_movement)
if start_date is not None:
kw.update({'start_date':start_date})
stop_date = business_path.getExpectedStopDate(input_movement)
if stop_date is not None:
kw.update({'stop_date':stop_date})
# one for payable
simulation_movement = self._applied_rule.newContent(
portal_type=RuleMixin.movement_type,
temp_object=True,
quantity=-quantity,
**kw)
ret.append(simulation_movement)
# one for bank
kw.update({'source':business_path.getSource(),
'destination':business_path.getDestination(),})
simulation_movement = self._applied_rule.newContent(
portal_type=RuleMixin.movement_type,
temp_object=True,
quantity=quantity,
**kw)
ret.append(simulation_movement)
return ret
def _getInputMovementList(self, movement_list=None, rounding=None):
return [self._applied_rule.getParentValue(),]
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment