QuantityDivergenceTester.py 6.23 KB
Newer Older
1
# -*- coding: utf-8 -*-
2 3 4 5 6 7
##############################################################################
#
# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
#                    Rafael M. Monnerat <rafael@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
8
# programmers who take the whole responsibility of assessing all potential
9 10
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
11
# guarantees and support are strongly adviced to contract a Free Software
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
# 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
import zope.interface
31 32
from AccessControl import ClassSecurityInfo

33
from Products.ERP5Type.DivergenceMessage import DivergenceMessage
34
from Products.ERP5Type import Permissions, PropertySheet, Constraint, interfaces
35
from Products.ERP5Legacy.Document.PropertyDivergenceTester import \
36 37 38 39
                                              PropertyDivergenceTester

class QuantityDivergenceTester(PropertyDivergenceTester):
  """
40
  The purpose of this divergence tester is to check the
41 42 43
  consistency between delivery movement and simulation movement
  for the property quantity.
  """
Yusei Tahara's avatar
Yusei Tahara committed
44
  meta_type = 'ERP5 Quantity Divergence Tester'
45 46
  portal_type = 'Quantity Divergence Tester'
  add_permission = Permissions.AddPortalContent
47

48 49 50 51
  # Declarative security
  security = ClassSecurityInfo()
  security.declareObjectProtected(Permissions.AccessContentsInformation)

52
  # Declarative interfaces
53
  zope.interface.implements( interfaces.IDivergenceTester, )
54

55 56

  # Declarative properties
57 58
  property_sheets = PropertyDivergenceTester.property_sheets + (
                      PropertySheet.DecimalOption,
59 60 61 62 63 64 65 66 67 68 69
                     )

  def explain(self, simulation_movement):
    """
    This method returns a list of messages that contains
    the divergence of the Delivery Line.
    """
    delivery = simulation_movement.getDeliveryValue()

    d_quantity = delivery.getQuantity()
    quantity = simulation_movement.getCorrectedQuantity()
70

71 72 73 74 75 76 77 78
    extra_parameters = dict()
    if abs(quantity - d_quantity) < 1:
     # if the difference between quantities are small, use repr to have more
     # precise float display in the divergence message.
     extra_parameters = dict(
         decision_title=repr(d_quantity),
         prevision_title=repr(quantity),)

79 80
    # XXX - JPS - it would be much better not to create the message
    #             if we do not later use it (by isDivergent)
81 82
    message = DivergenceMessage(object_relative_url= delivery.getRelativeUrl(),
                 divergence_scope='quantity',
83 84 85 86 87
                 simulation_movement = simulation_movement,
                 decision_value = d_quantity ,
                 prevision_value = quantity,
                 tested_property='quantity',
                 message='Quantity',
88
                 **extra_parameters
89
                 )
90 91


92 93 94 95 96 97 98 99 100 101 102
    if quantity is None:
      if d_quantity is None:
        return []
      return [message]
    if d_quantity is None:
      d_quantity = 0
    delivery_ratio = simulation_movement.getDeliveryRatio()
    # if the delivery_ratio is None, make sure that we are
    # divergent even if the delivery quantity is 0
    if delivery_ratio is not None:
      d_quantity *= delivery_ratio
103
      message.decision_value = d_quantity
104
      message.decision_title = repr(d_quantity)
105 106
      if delivery_ratio == 0 and quantity > 0:
        return [message]
107

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    if d_quantity != 0.0:
      if not self.compare(d_quantity, quantity):
        return [message]
    else:
      # A delivery quantity of 0 is an exceptional case that we cannot really
      # handle with the current approach of delivery ratio.
      d_quantity = delivery.getQuantity()
      quantity = sum([m.getCorrectedQuantity() for m in
        delivery.getDeliveryRelatedValueList(
          portal_type='Simulation Movement')])

      if not self.compare(d_quantity, quantity):
        return [DivergenceMessage(
                     object_relative_url= delivery.getRelativeUrl(),
                     divergence_scope='quantity',
                     simulation_movement = simulation_movement,
                     decision_value = d_quantity ,
                     prevision_value = quantity,
                     tested_property='quantity',
                     message='Quantity',
                     **extra_parameters)]
      
130
    return []
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154

  def compare(self, x, y):
    if self.isDecimalAlignmentEnabled():
      from decimal import (Decimal, ROUND_DOWN, ROUND_UP, ROUND_CEILING,
                           ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN,
                           ROUND_HALF_UP)
      # Python2.4 did not support ROUND_05UP yet.
      rounding_option_dict = {'ROUND_DOWN':ROUND_DOWN,
                              'ROUND_UP':ROUND_UP,
                              'ROUND_CEILING':ROUND_CEILING,
                              'ROUND_FLOOR':ROUND_FLOOR,
                              'ROUND_HALF_DOWN':ROUND_HALF_DOWN,
                              'ROUND_HALF_EVEN':ROUND_HALF_EVEN,
                              'ROUND_HALF_UP':ROUND_HALF_UP}
      rounding_option = rounding_option_dict.get(self.getDecimalRoundingOption(),
                                                 ROUND_DOWN)
      
      return (Decimal(str(x)).quantize(Decimal(self.getDecimalExponent()),
                                       rounding=rounding_option)
              ==
              Decimal(str(y)).quantize(Decimal(self.getDecimalExponent()),
                                       rounding=rounding_option))
    else:
      return x==y