PaySheetTransaction.py 9.96 KB
Newer Older
1
# -*- coding: utf-8 -*-
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
#                    Jean-Paul Smets-Solanes <jp@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.
#
##############################################################################

30

Yoshinori Okuji's avatar
Yoshinori Okuji committed
31
from AccessControl import ClassSecurityInfo
32
from Products.ERP5Type import Permissions, PropertySheet
33
from Products.ERP5.Document.Invoice import Invoice
Yoshinori Okuji's avatar
Yoshinori Okuji committed
34

Fabien Morin's avatar
Fabien Morin committed
35 36
#XXX TODO: review naming of new methods

37
class PaySheetTransaction(Invoice):
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
  """
  A paysheet will store data about the salary of an employee
  """
  meta_type = 'ERP5 Pay Sheet Transaction'
  portal_type = 'Pay Sheet Transaction'
  add_permission = Permissions.AddPortalContent

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

  # Default Properties
  property_sheets = ( PropertySheet.Base
                    , PropertySheet.SimpleItem
                    , PropertySheet.CategoryCore
                    , PropertySheet.Task
                    , PropertySheet.Arrow
                    , PropertySheet.Delivery
                    , PropertySheet.Movement
                    , PropertySheet.Amount
                    , PropertySheet.XMLObject
                    , PropertySheet.TradeCondition
                    , PropertySheet.DefaultAnnotationLine
                    )


64
  security.declareProtected(Permissions.AccessContentsInformation,
Fabien Morin's avatar
Fabien Morin committed
65
                            'getRatioQuantityFromReference')
66 67 68 69 70
  def getRatioQuantityFromReference(self, ratio_reference=None):
    """
    return the ratio value correponding to the ratio_reference,
    None if ratio_reference not found
    """
71 72
    # get ratio lines
    portal_type_list = ['Pay Sheet Model Ratio Line']
Fabien Morin's avatar
Fabien Morin committed
73 74 75
    object_ratio_list = self.contentValues(portal_type=portal_type_list)
    # look for ratio lines on the paysheet
    if object_ratio_list:
76 77 78
      for obj in object_ratio_list:
        if obj.getReference() == ratio_reference:
          return obj.getQuantity()
Fabien Morin's avatar
Fabien Morin committed
79
    # if not find in the paysheet, look on dependence tree
80
    sub_object_list = self.getInheritedObjectValueList(portal_type_list)
81
    object_ratio_list = sub_object_list
82 83 84
    for document in object_ratio_list:
      if document.getReference() == ratio_reference:
        return document.getQuantity()
Fabien Morin's avatar
typo  
Fabien Morin committed
85
    return None
86 87

  security.declareProtected(Permissions.AccessContentsInformation,
Fabien Morin's avatar
Fabien Morin committed
88
                            'getRatioQuantityList')
89 90 91 92 93 94
  def getRatioQuantityList(self, ratio_reference_list):
    """
    Return a list of reference_ratio_list correponding values.
    reference_ratio_list is a list of references to the ratio lines
    we want to get.
    """
95
    if not isinstance(ratio_reference_list, (list, tuple)):
96 97 98 99
      return [self.getRatioQuantityFromReference(ratio_reference_list)]
    return [self.getRatioQuantityFromReference(reference) \
        for reference in ratio_reference_list]

100
  security.declareProtected(Permissions.AccessContentsInformation,
Fabien Morin's avatar
Fabien Morin committed
101
                            'getAnnotationLineFromReference')
102
  def getAnnotationLineFromReference(self, reference=None):
103 104
    """Return the annotation line corresponding to the reference.
    Returns None if reference not found
105
    """
Fabien Morin's avatar
Fabien Morin committed
106
    # look for annotation lines on the paysheet
107
    annotation_line_list = self.contentValues(portal_type=['Annotation Line'])
Fabien Morin's avatar
Fabien Morin committed
108 109
    if annotation_line_list:
      for annotation_line in annotation_line_list:
110
        if (annotation_line.getReference() or annotation_line.getId()) == reference :
Fabien Morin's avatar
Fabien Morin committed
111 112
          return annotation_line
    # if not find in the paysheet, look on dependence tree
113
    for annotation_line in self.getInheritedObjectValueList(['Annotation Line']):
114
      if (annotation_line.getReference() or annotation_line.getId()) == reference:
115
        return annotation_line
Fabien Morin's avatar
typo  
Fabien Morin committed
116
    return None
117 118

  security.declareProtected(Permissions.AccessContentsInformation,
Fabien Morin's avatar
Fabien Morin committed
119
                            'getAnnotationLineListList')
120
  def getAnnotationLineListList(self, reference_list):
121
    """Return a list of annotation lines corresponding to the reference_list
Fabien Morin's avatar
Fabien Morin committed
122
    reference_list is a list of references to the Annotation Line we want
123 124
    to get.
    """
125
    if not isinstance(reference_list, (list, tuple)):
126 127 128
      return [self.getAnnotationLineFromReference(reference_list)]
    return [self.getAnnotationLineFromReference(reference) \
        for reference in reference_list]
129

Fabien Morin's avatar
Fabien Morin committed
130 131
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getInheritedObjectValueList')
132
  def getInheritedObjectValueList(self, portal_type_list, property_list=()):
133
    '''Return a list of all subobjects of the herited model (incuding the
134 135
      dependencies).
      If property_list is provided, only subobjects with at least one of those
136
      properties will be taken into account
137
    '''
138
    model = self.getSpecialiseValue()
139
    sub_object_list = []
140 141
    if model is not None:
      # if there is an effective model
142
      model_reference_dict = model.getInheritanceReferenceDict(self,
143 144 145 146 147 148
                                     portal_type_list=portal_type_list,
                                     property_list=property_list)
      traverse = self.getPortalObject().unrestrictedTraverse
      for model_url, id_list in model_reference_dict.items():
        model = traverse(model_url)
        sub_object_list.extend([model._getOb(x) for x in id_list])
149 150
    return sub_object_list

151 152
  security.declarePrivate('updateAggregatedAmountList')
  def updateAggregatedAmountList(self, *args, **kw):
153 154
    amount_dict = dict(((x.getReference(),
                         tuple(x.getVariationCategoryList())), x)
155 156
                       for x in self.getAggregatedAmountList(*args, **kw)
                       if x.getResource())
157 158 159
    movement_to_delete_list = []
    for movement in self.getMovementList():
      if movement.getBaseApplication():
160
        amount = amount_dict.pop((movement.getReference(),
161 162 163 164 165 166 167 168 169 170 171 172
                                  tuple(movement.getVariationCategoryList())),
                                 None)
        if amount is None:
          movement_to_delete_list.append(movement)
        else:
          movement.edit(**dict((x, amount.getProperty(x))
              for x in ('price', 'resource', 'quantity',
                        'base_application_list', 'base_contribution_list')))

    return {'movement_to_delete_list': movement_to_delete_list,
            'movement_to_add_list': amount_dict.values()}

Fabien Morin's avatar
Fabien Morin committed
173 174
  security.declareProtected(Permissions.ModifyPortalContent,
                            'applyTransformation')
175
  def applyTransformation(self):
Fabien Morin's avatar
Fabien Morin committed
176
    '''use a delivery builder to create all the paysheet lines using
177 178 179 180
      movements return by updateAggregatedAmountList
    '''
    portal = self.getPortalObject()
    paysheet_model = self.getSpecialiseValue()
181
    movement_dict = self.updateAggregatedAmountList()
182 183
    for movement in movement_dict['movement_to_delete_list']:
      parent = movement.getParentValue()
184
      if parent.getPortalType() in ['Pay Sheet Line', 'Pay Sheet Transaction']:
185
        parent.manage_delObjects(movement.getId())
186 187
      if parent.getPortalType() == 'Pay Sheet Line' and \
             len(parent.contentValues(portal_type='Pay Sheet Cell')) == 0:
Fabien Morin's avatar
Fabien Morin committed
188 189
        # the line contain no movements, remove it
        self.manage_delObjects(parent.getId())
190 191
    business_process_list = paysheet_model.findEffectiveSpecialiseValueList(
        self, portal_type_list=['Business Process'])
192 193 194 195 196 197 198
    if len(business_process_list):
      # XXX currently, we consider that is to complicated to use more than one
      # Business Process, so we take the first (wich is the nearest from
      # the paysheet)
      business_process = business_process_list[0]
      movement_list_trade_phase_dic = {}
      for movement in movement_dict['movement_to_add_list']:
199 200 201 202 203 204
        if movement.getTotalPrice() != 0:
          # remove movement with 0 total_price
          trade_phase = movement.getTradePhase()
          if not movement_list_trade_phase_dic.has_key(trade_phase):
            movement_list_trade_phase_dic[trade_phase] = []
          movement_list_trade_phase_dic[trade_phase].append(movement)
205
      for trade_phase in movement_list_trade_phase_dic.keys():
Aurel's avatar
Aurel committed
206
        business_link_list = business_process.getBusinessLinkValueList(trade_phase=\
207
            trade_phase)
208 209 210 211 212 213 214

        movement_list = []
        for amount in movement_list_trade_phase_dic[trade_phase]:
          variation_dict = dict([tuple(x.split('/',1)) for x in amount.getVariationCategoryList()])
          movement_list.extend(
            business_process.getTradePhaseMovementList(
              self, amount, trade_phase, update_property_dict=variation_dict))
215
        for business_link in business_link_list:
216
          builder_list = [portal.restrictedTraverse(url) for url in\
217
                          business_link.getDeliveryBuilderList()]
218 219
          for builder in builder_list:
            builder.build(delivery_relative_url_list=[self.getRelativeUrl(),],
220
                          movement_list = movement_list)