Container.py 8.11 KB
Newer Older
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1 2
##############################################################################
#
Romain Courteaud's avatar
Romain Courteaud committed
3
# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved.
4
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
Romain Courteaud's avatar
Romain Courteaud committed
5
#                    Romain Courteaud <romain@nexedi.com>
Jean-Paul Smets's avatar
Jean-Paul Smets 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 36 37 38 39 40 41 42
#
# 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 Globals import InitializeClass, PersistentMapping
from AccessControl import ClassSecurityInfo

from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5Type.XMLMatrix import XMLMatrix
from Products.ERP5Type.XMLObject import XMLObject

from Products.ERP5.Document.Movement import Movement

from zLOG import LOG

class Container(Movement, XMLObject):
    """
Jérome Perrin's avatar
Jérome Perrin committed
43 44 45 46 47 48
      Container is equivalent to a movement with qty 1.0 and resource =
      to the kind of packaging Container may point to item (ex.
      Container serial No or Parcel Serial No if tracing required)
      Container may eventually usa optional property sheet to store
      parcel No information (we use Item property sheet for that). Some
      acquisition may be required...
Jean-Paul Smets's avatar
Jean-Paul Smets committed
49 50 51
      
      A Container which does not point to an Item can act itself as an Item
      for traceability. 
Jean-Paul Smets's avatar
Jean-Paul Smets committed
52

Jérome Perrin's avatar
Jérome Perrin committed
53 54
      Container Line / Container Cell is used to store quantities (never
      accounted)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
55 56 57 58 59 60 61 62
      Container Line / Countainer Cell may point to Item
    """

    meta_type = 'ERP5 Container'
    portal_type = 'Container'

    # Declarative security
    security = ClassSecurityInfo()
63
    security.declareObjectProtected(Permissions.AccessContentsInformation)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

    # Declarative interfaces
    __implements__ = ( Interface.Variated, )

    # Declarative properties
    property_sheets = ( PropertySheet.Base
                      , PropertySheet.XMLObject
                      , PropertySheet.CategoryCore
                      , PropertySheet.Amount
                      , PropertySheet.Task
                      , PropertySheet.Arrow
                      , PropertySheet.Movement
                      , PropertySheet.Price
                      , PropertySheet.VariationRange
                      , PropertySheet.ItemAggregation
                      , PropertySheet.Item
                      , PropertySheet.Container
                      , PropertySheet.SortIndex
                      )

Jérome Perrin's avatar
Jérome Perrin committed
84 85
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getQuantity')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
86 87 88 89 90 91
    def getQuantity(self):
      """
        Returns 1 because only one container is shipped
      """
      return 1.0

Jérome Perrin's avatar
Jérome Perrin committed
92 93
    security.declareProtected(Permissions.AccessContentsInformation,
                              'isAccountable')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
94 95 96 97 98 99 100 101 102
    def isAccountable(self):
      """
        Returns 1 if this needs to be accounted
        Only account movements which are not associated to a delivery
        Whenever delivery is there, delivery has priority
      """
      # Always accountable - to account the containers which we use
      return 1

Jérome Perrin's avatar
Jérome Perrin committed
103 104
    security.declareProtected( Permissions.ModifyPortalContent,
                              'hasCellContent' )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    def hasCellContent(self, base_id='movement'):
      """
          This method can be overriden
      """
      return 0

    security.declareProtected(Permissions.View, 'isDivergent')
    def isDivergent(self):
      """
        Returns 1 if the target is not met according to the current information
        After and edit, the isOutOfTarget will be checked. If it is 1,
        a message is emitted

        emit targetUnreachable !
      """
      return 0

    def getContainerText(self):
      """
        Creates a unique string which allows to compare/hash two containers
      """
      result = ""
      container_line_list = list(self.objectValues())
128
      container_line_list.sort(key=lambda x: x.getResource())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
129 130 131
      for container_line in container_line_list:
        if container_line.hasCellContent():
          container_cell_list = list(container_line.objectValues())
132
          container_cell_list.sort(key=lambda x: x.getVariationText())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
133
          for container_cell in container_cell_list:
Romain Courteaud's avatar
Romain Courteaud committed
134 135 136
            result += "%s %s %s\n" % (container_cell.getResource(), 
                                      container_cell.getQuantity(), 
                                      '|'.join(container_cell.getVariationText().split('\n')))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
137
        else:
Romain Courteaud's avatar
Romain Courteaud committed
138
          result += "%s %s\n" % (container_line.getResource(), container_line.getQuantity())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
139
      container_list = list(self.objectValues(spec = self.meta_type))
140
      container_list.sort(key=lambda x: x.getContainerText())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
141 142 143 144 145
      more_result = ""
      for container in container_list:
        more_result += container.getContainerText()
      result = result + '\n'.join(map(lambda x: " %s" % x, more_result.split('\n')))
      return result
146 147

    # Used for optimization - requires reindexing using container_uid 
Jérome Perrin's avatar
Jérome Perrin committed
148 149
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainerUid')
150 151 152
    def getContainerUid(self):
      return self.getUid()

Jérome Perrin's avatar
Jérome Perrin committed
153 154
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainerValue')
155 156 157
    def getContainerValue(self):
      return self

Jérome Perrin's avatar
Jérome Perrin committed
158 159
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainer')
160 161 162
    def getContainer(self):
      return self.getRelativeUrl()

163
    # Quantity methods
Jérome Perrin's avatar
Jérome Perrin committed
164 165
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainedTotalQuantity')
166 167 168 169 170
    def getContainedTotalQuantity(self, recursive = 0):
      """
        The sum of quantities of contained lines
      """
      result = 0.0
Jérome Perrin's avatar
Jérome Perrin committed
171 172
      for o in self.contentValues(filter =
                    {'portal_type': self.getPortalContainerLineTypeList()}):
173 174
        result += o.getTotalQuantity()
      if recursive:
Jérome Perrin's avatar
Jérome Perrin committed
175 176 177 178
        for o in self.contentValues(filter =
                    {'portal_type': self.getPortalContainerTypeList()}):
          result += o.getContainedTotalQuantity()
      return result
179
    
Jérome Perrin's avatar
Jérome Perrin committed
180 181
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainedTotalPrice')
182 183 184 185 186
    def getContainedTotalPrice(self, recursive = 0):
      """
        The sum of price of contained lines
      """
      result = 0.0
Jérome Perrin's avatar
Jérome Perrin committed
187 188
      for o in self.contentValues(filter =
                    {'portal_type': self.getPortalContainerLineTypeList()}):
189 190
        result += o.getTotalPrice()
      if recursive:
Jérome Perrin's avatar
Jérome Perrin committed
191 192 193 194
        for o in self.contentValues(filter =
                    {'portal_type': self.getPortalContainerTypeList()}):
          result += o.getContainedTotalPrice()
      return result
195
      
Jean-Paul Smets's avatar
Jean-Paul Smets committed
196
    # Item Access
Jérome Perrin's avatar
Jérome Perrin committed
197 198
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getTrackedItemUidList')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
199 200 201 202 203 204 205 206 207 208
    def getTrackedItemUidList(self):
      """
        Return a list of uid for related items.
        If this container is related to no item, it is treated as an Item
      """
      ### XXX We should filter by portal type here
      item_uid_list = self.getAggregateUidList()
      if len(item_uid_list): return item_uid_list
      return (self.getUid(),)