SupplyLink.py 7.02 KB
Newer Older
Romain Courteaud's avatar
Romain Courteaud committed
1 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 30
##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
#                    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

31
from Products.ERP5Type import Permissions, PropertySheet
Romain Courteaud's avatar
Romain Courteaud committed
32 33 34

from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5.Document.Path import Path
35
from Products.ERP5.Document.SupplyChain import SupplyChainError
Romain Courteaud's avatar
Romain Courteaud committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

class SupplyLink(Path, XMLObject):
    """
      A DeliveryLine object allows to implement lines in
      Deliveries (packing list, order, invoice, etc.)

      It may include a price (for insurance, for customs, for invoices,
      for orders)
    """

    meta_type = 'ERP5 Supply Link'
    portal_type = 'Supply Link'

    # Declarative security
    security = ClassSecurityInfo()
51
    security.declareObjectProtected(Permissions.AccessContentsInformation)
Romain Courteaud's avatar
Romain Courteaud committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65

    # Declarative properties
    property_sheets = ( PropertySheet.Base
                      , PropertySheet.XMLObject
                      , PropertySheet.CategoryCore
                      , PropertySheet.Amount
                      , PropertySheet.Arrow
                      , PropertySheet.Movement
                      , PropertySheet.Price
                      , PropertySheet.VariationRange
                      , PropertySheet.Path
                      , PropertySheet.FlowCapacity
                      , PropertySheet.TransformedResource
                      , PropertySheet.Delivery
66
                      , PropertySheet.SupplyLink
67
                      , PropertySheet.Reference
Romain Courteaud's avatar
Romain Courteaud committed
68 69
                      )

70 71
    security.declareProtected(Permissions.AccessContentsInformation,
                              'isProductionSupplyLink')
Romain Courteaud's avatar
Romain Courteaud committed
72 73 74 75 76 77
    def isProductionSupplyLink(self):
      """
        Return 1 if the SupplyLink represents a production.
      """
      return (self.getSourceValue() is None)

78 79
    security.declareProtected(Permissions.AccessContentsInformation,
                              'isPackingListSupplyLink')
Romain Courteaud's avatar
Romain Courteaud committed
80 81 82 83 84 85
    def isPackingListSupplyLink(self):
      """
        Return 1 if the SupplyLink represents a packing list.
      """
      return not(self.isProductionSupplyLink())

86 87
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getCurrentNodeValue')
Romain Courteaud's avatar
Romain Courteaud committed
88 89 90 91 92 93 94 95 96 97
    def getCurrentNodeValue(self):
      """
        Return the node used to find the previous SupplyLink
      """
      if self.isProductionSupplyLink():
        node = self.getDestinationValue()
      else:
        node = self.getSourceValue()
      return node

98 99
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getNextNodeValue')
100 101 102 103 104 105
    def getNextNodeValue(self):
      """
        Return the node used to find the next SupplyLink
      """
      return self.getDestinationValue()

106 107
    security.declareProtected(Permissions.AccessContentsInformation,
                              'test')
Romain Courteaud's avatar
Romain Courteaud committed
108 109 110 111 112
    def test(self, movement, concurrent_supply_link_list):
      """
        Test if the current link can expand this movement.
        Futur implementation have to return properties value
        (like quantity) calculated.
113
        This method is called only on packing list supply link.
Romain Courteaud's avatar
Romain Courteaud committed
114 115
      """
      result = 0
116 117 118 119
      # Test if the movement correspond to the resource to produced
      ind_phase_list = movement.getIndustrialPhaseValueList()
      if ind_phase_list != []:
        # Is this SupplyLink in the route to the previous production node ?
120
        supply_chain = self.getParentValue()
121 122 123 124 125 126 127 128 129 130 131
        previous_ind_phase_list =\
              supply_chain.getPreviousProductionIndustrialPhaseList(self)
        for ind_phase in ind_phase_list:
          if ind_phase in previous_ind_phase_list:
            result = 1
            break
      else:
        # How to delivered raw materials ?
        # XXX This method has to be rewritten.
        # Predicate must be used.
        if len(concurrent_supply_link_list) > 1:
132
          raise SupplyChainError,\
133
                "SupplyChain unable to find route."
Romain Courteaud's avatar
Romain Courteaud committed
134
        else:
135 136
          # Check if raw material is create by a production link or a packing
          # list link.
137
          supply_chain = self.getParentValue()
138 139
          next_industrial_phase_list = \
              supply_chain.getNextProductionIndustrialPhaseList(self)
140 141
          # XXX GetRelativeUrl copy/paste from transformation
          # Code duplication
142
          ind_phase_url_list = [x.getCategoryRelativeUrl()
143
                               for x in next_industrial_phase_list]
144 145

          # Get the transformation to use
146
          applied_rule = movement.getParentValue()
147
          rule = applied_rule.getSpecialiseValue()
148
          transformation = rule.getTransformation(movement)
149
          # Call getAggregatedAmountList
150
          input_amount = applied_rule.getParentValue()
151
          resource_list = [x.getResourceValue()
152
            for x in transformation.getAggregatedAmountList((input_amount,))
153
            if x.getCausalityValue().getIndustrialPhase() in ind_phase_url_list]
154 155 156
          current_resource = movement.getResourceValue()
          if current_resource not in resource_list:
            # We can delivered this resource
157
            supply_chain = self.getParentValue()
Romain Courteaud's avatar
Romain Courteaud committed
158 159
            previous_ind_phase_list =\
                  supply_chain.getPreviousProductionIndustrialPhaseList(self)
160 161
            if len(previous_ind_phase_list) == 0:
              result = 1
Romain Courteaud's avatar
Romain Courteaud committed
162
      return result
Romain Courteaud's avatar
Romain Courteaud committed
163

164
    security.declareProtected(Permissions.AccessContentsInformation,
165 166
                              'calculateStartDate')
    def calculateStartDate(self, stop_date):
Romain Courteaud's avatar
Romain Courteaud committed
167 168 169 170 171 172 173 174 175 176
      """
        Calculate the start date, depending on the delay.
      """
      max_delay = self.getMaxDelay()
      min_delay = self.getMinDelay()
      for delay in [max_delay, min_delay, 0]:
        if type(delay) in (type(1), type(1.0)):
          start_date = stop_date - delay
          break
      return start_date
177