##############################################################################
#
# 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 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
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# 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.
#
##############################################################################

from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from Products.ERP5.Document.Rule import Rule
from Products.ERP5.Document.PredicateMatrix import PredicateMatrix

class PaymentSimulationRule(Rule, PredicateMatrix):
  """
  Payment Simulation Rule generates payment simulation movements from
  accounting / 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):
    """
    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.
    """
    # 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_conditon_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
    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)

    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()

        # 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(),
          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(),
          quantity=quantity
          )
        prevision_list.append(prevision_line)
    return prevision_list

  security.declareProtected(Permissions.ModifyPortalContent, 'expand')
  def expand(self, applied_rule, **kw):
    """Expands the current movement downward.
    """
    return Rule._expand(self, applied_rule, **kw)

  # Matrix related
  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)