InvoicingRule.py 6.55 KB
Newer Older
Sebastien Robin's avatar
Sebastien Robin committed
1 2
##############################################################################
#
3
# Copyright (c) 2002-2005 Nexedi SARL and Contributors. All Rights Reserved.
Sebastien Robin's avatar
Sebastien Robin committed
4
#                    Sebastien Robin <seb@nexedi.com>
5
#                    Romain Courteaud <romain@nexedi.com>
Sebastien Robin's avatar
Sebastien Robin committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
#
# 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
36
#from Products.ERP5Type.Base import TempBase
Sebastien Robin's avatar
Sebastien Robin committed
37 38 39 40

from zLOG import LOG

class InvoicingRule(Rule):
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
  """
    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
64
                    , PropertySheet.Task
65 66 67 68 69
                    )

  security.declareProtected(Permissions.AccessContentsInformation,
                            'isAccountable')
  def isAccountable(self, movement):
Sebastien Robin's avatar
Sebastien Robin committed
70
    """
71 72 73 74
    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.
Sebastien Robin's avatar
Sebastien Robin committed
75
    """
76
    return 0
Sebastien Robin's avatar
Sebastien Robin committed
77

78 79 80 81 82 83 84 85 86 87 88
#### 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()

89
    # XXX Harcoded list
90 91
    invoice_line = {
        'source': context_movement.getSource(),
92
        'source_section': context_movement.getSourceSection(),
93
        'source_decision': context_movement.getSourceDecision(),
Jérome Perrin's avatar
Jérome Perrin committed
94
        'source_administration': context_movement.getSourceAdministration(),
95 96 97
        'source_project': context_movement.getSourceProject(),
        'source_function': context_movement.getSourceFunction(),
        'source_payment': context_movement.getSourcePayment(),
Jérome Perrin's avatar
Jérome Perrin committed
98
        'destination': context_movement.getDestination(),
99
        'destination_section': context_movement.getDestinationSection(),
100
        'destination_decision': context_movement.getDestinationDecision(),
Jérome Perrin's avatar
Jérome Perrin committed
101
        'destination_administration': context_movement.getDestinationAdministration(),
102 103 104
        'destination_project': context_movement.getDestinationProject(),
        'destination_function': context_movement.getDestinationFunction(),
        'destination_payment': context_movement.getDestinationPayment(),
Jérome Perrin's avatar
Jérome Perrin committed
105
        'start_date': context_movement.getStartDate(),
106 107 108 109
        'stop_date': context_movement.getStopDate(),
        'resource': context_movement.getResource(),
        'variation_category_list': context_movement.getVariationCategoryList(),
        'variation_property_dict': context_movement.getVariationPropertyDict(),
110
        'base_contribution_list': context_movement.getBaseContributionList(),
111 112 113 114 115 116
        '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(),
117
        # We do need to collect invoice lines to build invoices
118 119
        'deliverable': 1
        }
120 121 122 123 124 125 126 127 128 129 130 131 132
    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
    """
133 134 135 136 137 138 139 140
    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)
141
      
142 143 144 145 146 147 148 149 150 151 152
        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)
Sebastien Robin's avatar
Sebastien Robin committed
153

154 155 156 157 158 159
    # Pass to base class
    Rule.expand(self, applied_rule, force=force, **kw)

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

160