##############################################################################
#
# Copyright (c) 2002-2005 Nexedi SARL and Contributors. All Rights Reserved.
#                    Sebastien Robin <seb@nexedi.com>
#                    Romain Courteaud <romain@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability 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
# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

from AccessControl import ClassSecurityInfo
from Acquisition import aq_base, aq_parent, aq_inner, aq_acquire
from Products.CMFCore.utils import getToolByName

from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5.Document.Rule import Rule
#from Products.ERP5Type.Base import TempBase

from zLOG import LOG

class InvoicingRule(Rule):
  """
    Invoicing Rule expand simulation created by a order or delivery rule.
  """

  # CMF Type Definition
  meta_type = 'ERP5 Invoicing Rule'
  portal_type = 'Invoicing Rule'
  add_permission = Permissions.AddPortalContent
  isPortalContent = 1
  isRADContent = 1

  # Declarative security
  security = ClassSecurityInfo()
  security.declareObjectProtected(Permissions.AccessContentsInformation)

  __implements__ = ( Interface.Predicate,
                     Interface.Rule )

  # Default Properties
  property_sheets = ( PropertySheet.Base
                    , PropertySheet.XMLObject
                    , PropertySheet.CategoryCore
                    , PropertySheet.DublinCore
                    , PropertySheet.Task
                    )

  security.declareProtected(Permissions.AccessContentsInformation,
                            'isAccountable')
  def isAccountable(self, movement):
    """
    Tells wether generated movement needs to be accounted or not.

    Invoice movement are never accountable, so simulation movement for
    invoice movements should not be accountable either.
    """
    return 0

#### Helper method for expand
  def _generatePrevisionList(self, applied_rule, **kw):
    """
    Generate a list of movements, that should be children of this rule,
    based on its context (parent movement, delivery, configuration ...)

    These previsions are acrually returned as dictionaries.
    """
    # XXX Isn't it better to share the code with expand method
    context_movement = applied_rule.getParentValue()

    # XXX Harcoded list
    invoice_line = {
        'source': context_movement.getSource(),
        'source_section': context_movement.getSourceSection(),
        'source_decision': context_movement.getSourceDecision(),
        'source_administration': context_movement.getSourceAdministration(),
        'source_project': context_movement.getSourceProject(),
        'source_function': context_movement.getSourceFunction(),
        'source_payment': context_movement.getSourcePayment(),
        'destination': context_movement.getDestination(),
        'destination_section': context_movement.getDestinationSection(),
        'destination_decision': context_movement.getDestinationDecision(),
        'destination_administration': context_movement.getDestinationAdministration(),
        'destination_project': context_movement.getDestinationProject(),
        'destination_function': context_movement.getDestinationFunction(),
        'destination_payment': context_movement.getDestinationPayment(),
        'start_date': context_movement.getStartDate(),
        'stop_date': context_movement.getStopDate(),
        'resource': context_movement.getResource(),
        'variation_category_list': context_movement.getVariationCategoryList(),
        'variation_property_dict': context_movement.getVariationPropertyDict(),
        'base_contribution_list': context_movement.getBaseContributionList(),
        'aggregate_list': context_movement.getAggregateList(),
        'quantity': context_movement.getCorrectedQuantity(),
        'quantity_unit': context_movement.getQuantityUnit(),
        'price': context_movement.getPrice(),
        'price_currency': context_movement.getPriceCurrency(),
        'efficiency': context_movement.getEfficiency(),
        # We do need to collect invoice lines to build invoices
        'deliverable': 1
        }
    return [invoice_line]

  security.declareProtected(Permissions.ModifyPortalContent, 'expand')
  def expand(self, applied_rule, force=0, **kw):
    """
    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
    """
    parent_movement = applied_rule.getParentValue()
    if parent_movement is not None: 
      if not parent_movement.isFrozen():
        add_list, modify_dict, \
          delete_list = self._getCompensatedMovementList(applied_rule, **kw)

        for movement_id in delete_list:
          applied_rule._delObject(movement_id)
      
        for movement, prop_dict in modify_dict.items():
          applied_rule[movement].edit(**prop_dict)

        for movement_dict in add_list:
          if 'id' in movement_dict.keys():
            mvmt_id = applied_rule._get_id(movement_dict.pop('id'))
            new_mvmt = applied_rule.newContent(id=mvmt_id,
                portal_type=self.movement_type)
          else:
            new_mvmt = applied_rule.newContent(portal_type=self.movement_type)
          new_mvmt.edit(**movement_dict)

    # Pass to base class
    Rule.expand(self, applied_rule, force=force, **kw)

  def isDeliverable(self, movement):
    return movement.getResource() is not None