FIFODeliverySolver.py 4.4 KB
Newer Older
1 2 3
# -*- coding: utf-8 -*-
##############################################################################
#
4
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility 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
# guarantees 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
25
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 27 28
#
##############################################################################

29
import zope.interface
30 31 32
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.XMLObject import XMLObject
33

34
class FIFODeliverySolver(XMLObject):
35
  """
Leonardo Rochael Almeida's avatar
typo  
Leonardo Rochael Almeida committed
36
  The FIFO solver reduces delivered quantity by reducing the quantity of
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
37
  simulation movements from the last order.
38
  """
39 40 41 42
  meta_type = 'ERP5 FIFO Delivery Solver'
  portal_type = 'FIFO Delivery Solver'
  add_permission = Permissions.AddPortalContent
  isIndexable = 0 # We do not want to fill the catalog with objects on which we need no reporting
Jean-Paul Smets's avatar
Jean-Paul Smets committed
43

44 45 46
  # Declarative security
  security = ClassSecurityInfo()
  security.declareObjectProtected(Permissions.AccessContentsInformation)
47

48 49 50 51 52 53 54
  # Default Properties
  property_sheets = ( PropertySheet.Base
                    , PropertySheet.XMLObject
                    , PropertySheet.CategoryCore
                    , PropertySheet.DublinCore
                    , PropertySheet.DeliverySolver
                    )
55

56 57
  # Declarative interfaces
  zope.interface.implements(interfaces.IDeliverySolver,)
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
58

59
  # IDeliverySolver Implementation
60
  security.declareProtected(Permissions.AccessContentsInformation, 'getTotalQuantity')
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
61
  def getTotalQuantity(self):
62 63 64 65
    """
      Move this to mixin
    """
    total_quantity = 0
66
    for movement in self.getDeliveryValueList():
67 68 69
      total_quantity += movement.getQuantity()
    return total_quantity

70
  security.declareProtected(Permissions.ModifyPortalContent, 'setTotalQuantity')
71
  def setTotalQuantity(self, new_quantity, activate_kw=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
72 73 74
    """
    """
    result = []
75
    remaining_quantity = self.getTotalQuantity() - new_quantity
76 77 78
    if remaining_quantity < 0:
      return result
    simulation_movement_list = self._getSimulationMovementList()
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
79
    for movement in simulation_movement_list:
80 81 82 83 84 85 86 87 88 89
      quantity = movement.getQuantity()
      if quantity < remaining_quantity:
        result.append((movement, quantity))
        remaining_quantity -= quantity
        movement.edit(quantity=0, delivery_ratio=0, activate_kw=activate_kw)
      else:
        # only append movement if we decrease the quantity, which means we
        # would surely split it. If remaining quantity is 0, the code is
        # just used to update delivery ratio
        if remaining_quantity:
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
90
          result.append((movement, remaining_quantity))
91 92 93 94 95 96 97 98
        movement_quantity = quantity - remaining_quantity
        delivery_ratio = 1.
        if new_quantity:
          delivery_ratio = movement_quantity / new_quantity
        movement.edit(quantity=movement_quantity,
                      delivery_ratio=delivery_ratio,
                      activate_kw=activate_kw)
        remaining_quantity = 0
Jean-Paul Smets's avatar
Jean-Paul Smets committed
99
    # Return movement, split_quantity tuples
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
100 101 102 103 104 105
    return result

  def _getSimulationMovementList(self):
    """
    Returns a list of simulation movement sorted from the last order.
    """
106
    simulation_movement_list = self.getDeliveryValueList()
107
    if len(simulation_movement_list) > 1:
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
108
      return sorted(simulation_movement_list,
109
        key=lambda x:x.getExplanationValue().getStartDate(), reverse=True)
Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
110 111
    else:
      return simulation_movement_list