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 @@
# 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 Products.ERP5Type import Permissions
from Products.ERP5Legacy.Document.DeliveryRule import DeliveryRule
from Products.ERP5Type import Permissions, PropertySheet, interfaces
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):
"""
Delivery Root Simulation Rule is a root level rule for Deliveries.
class DeliveryRootSimulationRule(RuleMixin, MovementCollectionUpdaterMixin, Predicate):
"""
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
meta_type = 'ERP5 Delivery Root Simulation Rule'
portal_type = 'Delivery Root Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
def _getExpandablePropertyUpdateDict(self, applied_rule, movement,
business_link, current_property_dict):
"""Order rule specific update dictionary"""
return {
'delivery': movement.getRelativeUrl(),
}
# 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 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.
......@@ -24,38 +25,81 @@
# 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 Products.ERP5Type import Permissions
from Products.ERP5Legacy.Document.Rule import Rule
from Products.ERP5Type import Permissions, PropertySheet, interfaces
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):
"""
Delivery Simulation Rule expand simulation created by a order root
simulation rule or delivery root simulation rule.
class DeliverySimulationRule(RuleMixin, MovementCollectionUpdaterMixin, Predicate):
"""
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
meta_type = 'ERP5 Delivery Simulation Rule'
portal_type = 'Delivery Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
security.declareProtected(Permissions.ModifyPortalContent, 'expand')
def expand(self, applied_rule, force=0, **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):
"""
Return the movement generator to use in the expand process
"""
return DeliveryRuleMovementGenerator(applied_rule=context, rule=self)
def _getMovementGeneratorContext(self, context):
"""
Expands the rule:
- 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 the movement generator context to use for expand
"""
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):
return movement.getResource() is not None
def _getInputMovementList(self, movement_list=None, rounding=None):
return [self._applied_rule.getParentValue(),]
......@@ -25,28 +25,112 @@
# 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 Products.ERP5Type import Permissions
from Products.ERP5Legacy.Document.InvoiceRule import InvoiceRule
from Products.ERP5Type import Permissions, PropertySheet, interfaces
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
meta_type = 'ERP5 Invoice Root Simulation Rule'
portal_type = 'Invoice Root Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
def _getExpandablePropertyUpdateDict(self, applied_rule, movement,
business_link, current_property_dict):
"""Order rule specific update dictionary"""
return {
'delivery': movement.getRelativeUrl(),
}
# 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 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
# programmers who take the whole responsibility of assessing all potential
......@@ -21,24 +22,76 @@
#
# You should have received a copy of the GNU General Public License
# 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 Products.ERP5Type import Permissions
from Products.ERP5Legacy.Document.InvoicingRule import InvoicingRule
from Products.ERP5Type import Permissions, PropertySheet, interfaces
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
meta_type = 'ERP5 Invoice Simulation Rule'
portal_type = 'Invoice Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
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
# programmers who take the whole responsibility of assessing all potential
......@@ -21,27 +22,207 @@
#
# You should have received a copy of the GNU General Public License
# 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 Products.ERP5Type import Permissions
from Products.ERP5Legacy.Document.InvoiceTransactionRule import InvoiceTransactionRule
from Products.ERP5Type import Permissions, PropertySheet, interfaces
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
predicated. Template accounting movements are stored in cells inside
an instance of the InvoiceTransactionRule.
"""
# CMF Type Definition
meta_type = 'ERP5 Invoice Transaction Simulation Rule'
portal_type = 'Invoice Transaction Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
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 -*-
##############################################################################
#
# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solanes <jp@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
#
# 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
# 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
#
# This program is Free Software; you can redistribute it and/or
......@@ -24,15 +22,23 @@
#
# You should have received a copy of the GNU General Public License
# 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 Products.ERP5Type import Permissions
from Products.ERP5Legacy.Document.OrderRule import OrderRule
from Products.ERP5Type import Permissions, PropertySheet, interfaces
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
is consistent with the real order
......@@ -47,9 +53,54 @@ class OrderRootSimulationRule(OrderRule):
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
def _getExpandablePropertyUpdateDict(self, applied_rule, movement,
business_link, current_property_dict):
"""Order rule specific update dictionary"""
return {
'delivery': movement.getRelativeUrl(),
}
# 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 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
# programmers who take the whole responsibility of assessing all potential
......@@ -24,125 +25,118 @@
# 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 Products.ERP5Type import Permissions
from Products.ERP5Legacy.Document.Rule import Rule
from Products.ERP5.Document.PredicateMatrix import PredicateMatrix
from Products.ERP5Type import Permissions, PropertySheet, interfaces
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 PaymentSimulationRule(Rule, PredicateMatrix):
class PaymentSimulationRule(RuleMixin, MovementCollectionUpdaterMixin, Predicate):
"""
Payment Simulation Rule generates payment simulation movements from
accounting / invoice transaction simulation movements.
Payment Rule generates payment simulation movement from invoice
transaction simulation movements.
"""
# CMF Type Definition
meta_type = 'ERP5 Payment Simulation Rule'
portal_type = 'Payment Simulation Rule'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
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
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.
Return the movement generator to use in the expand process
"""
# Find an input movement and using Payment Conditions.
# 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()
return PaymentRuleMovementGenerator(applied_rule=context, rule=self)
# try to use payment conditions in BPM configuration
if len(payment_condition_list) == 0:
payment_condition_list = [x[1] for x in movement_and_tuple_list if x[1] is not None]
kw = self._getExpandablePropertyDict(applied_rule, input_movement, None)
prevision_list = []
# Find a matching cell
cell = self._getMatchingCell(input_movement)
def _getMovementGeneratorContext(self, context):
"""
Return the movement generator context to use for expand
"""
return context
if cell is not None : # else, we do nothing
for payment_condition in payment_condition_list:
aggregated_ammount_list = payment_condition.getAggregatedAmountList(
input_movement, movement_list=[input_movement])
assert len(aggregated_ammount_list) == 1
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()
def _getMovementGeneratorMovementList(self, context):
"""
Return the movement lists to provide to the movement generator
"""
return []
# one for payable
prevision_line = kw.copy()
prevision_line.update(
start_date=start_date,
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
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)
security.declareProtected(Permissions.ModifyPortalContent, 'expand')
def expand(self, applied_rule, **kw):
"""Expands the current movement downward.
class PaymentRuleMovementGenerator(MovementGeneratorMixin):
def getGeneratedMovementList(self, movement_list=None, rounding=False):
"""
return Rule._expand(self, applied_rule, **kw)
Input movement list comes from parent.
# Matrix related
security.declareProtected( Permissions.ModifyPortalContent,
'newCellContent' )
def newCellContent(self, id, portal_type='Accounting Rule Cell', **kw):
"""Overriden to specify default portal type
XXX This implementation using Business Path, not Payment Condition.
"""
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