InvoicingRule.py 6.7 KB
Newer Older
Sebastien Robin's avatar
Sebastien Robin committed
1 2
##############################################################################
#
Fabien Morin's avatar
Fabien Morin committed
3
# Copyright (c) 2002-2005 Nexedi SA 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
#
# WARNING: This program as such is intended to be used by professional
Fabien Morin's avatar
Fabien Morin committed
8
# programmers who take the whole responsibility of assessing all potential
Sebastien Robin's avatar
Sebastien Robin committed
9 10
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
Fabien Morin's avatar
Fabien Morin committed
11
# guarantees and support are strongly adviced to contract a Free Software
Sebastien Robin's avatar
Sebastien Robin committed
12 13 14 15 16 17 18 19 20 21 22 23 24 25
# 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
Fabien Morin's avatar
Fabien Morin committed
26 27
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
# USA.
Sebastien Robin's avatar
Sebastien Robin committed
28 29 30
#
##############################################################################

31
import zope.interface
Sebastien Robin's avatar
Sebastien Robin committed
32
from AccessControl import ClassSecurityInfo
33
from Products.ERP5Type import Permissions, PropertySheet, interfaces
Sebastien Robin's avatar
Sebastien Robin committed
34 35 36 37
from Products.ERP5.Document.Rule import Rule


class InvoicingRule(Rule):
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
  """
    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)

53
  zope.interface.implements( interfaces.IPredicate,
54
                     interfaces.IRule )
55 56 57 58 59 60

  # Default Properties
  property_sheets = ( PropertySheet.Base
                    , PropertySheet.XMLObject
                    , PropertySheet.CategoryCore
                    , PropertySheet.DublinCore
61
                    , PropertySheet.Task
62 63 64 65 66
                    )

  security.declareProtected(Permissions.AccessContentsInformation,
                            'isAccountable')
  def isAccountable(self, movement):
Sebastien Robin's avatar
Sebastien Robin committed
67
    """
Jérome Perrin's avatar
typos  
Jérome Perrin committed
68
    Tells whether generated movement needs to be accounted or not.
69 70 71

    Invoice movement are never accountable, so simulation movement for
    invoice movements should not be accountable either.
Sebastien Robin's avatar
Sebastien Robin committed
72
    """
73
    return 0
Sebastien Robin's avatar
Sebastien Robin committed
74

75 76 77 78 79 80
#### 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 ...)

Jérome Perrin's avatar
typos  
Jérome Perrin committed
81
    These previsions are returned as dictionaries.
82 83 84 85
    """
    # XXX Isn't it better to share the code with expand method
    context_movement = applied_rule.getParentValue()

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

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

  def isDeliverable(self, movement):
    return movement.getResource() is not None
Jérome Perrin's avatar
typos  
Jérome Perrin committed
161