示例#1
0
def assertPRoles(ob, permission, expect):
    """
    Asserts that in the context of ob, the given permission maps to
    the given roles.
    """
    pr = PermissionRole(permission)
    roles = pr.__of__(ob)
    roles2 = aq_base(pr).__of__(ob)
    assert roles == roles2 or tuple(roles) == tuple(roles2), (
        'Different methods of checking roles computed unequal results')
    same = 0
    if roles:
        # When verbose security is enabled, permission names are
        # embedded in the computed roles.  Remove the permission
        # names.
        roles = [r for r in roles if not r.endswith('_Permission')]
    if roles is None or expect is None:
        if (roles is None or tuple(roles) == ('Anonymous',)) and (
            expect is None or tuple(expect) == ('Anonymous',)):
            same = 1
    else:
        got = {}
        for r in roles: got[r] = 1
        expected = {}
        for r in expect: expected[r] = 1
        if got == expected:  # Dict compare does the Right Thing.
            same = 1
    assert same, 'Expected roles: %s, got: %s' % (`expect`, `roles`)
def assertPRoles(ob, permission, expect):
    """
    Asserts that in the context of ob, the given permission maps to
    the given roles.
    """
    pr = PermissionRole(permission)
    roles = pr.__of__(ob)
    roles2 = aq_base(pr).__of__(ob)
    assert roles == roles2 or tuple(roles) == tuple(roles2), (
        'Different methods of checking roles computed unequal results')
    same = 0
    if roles:
        # When verbose security is enabled, permission names are
        # embedded in the computed roles.  Remove the permission
        # names.
        roles = [r for r in roles if not r.endswith('_Permission')]

    if roles is None or expect is None:
        if (roles is None or tuple(roles) == ('Anonymous', )) and \
                (expect is None or tuple(expect) == ('Anonymous', )):
            same = 1
    else:
        got = {}
        for r in roles:
            got[r] = 1
        expected = {}
        for r in expect:
            expected[r] = 1
        if got == expected:  # Dict compare does the Right Thing.
            same = 1
    assert same, 'Expected roles: %r, got: %r' % (expect, roles)
示例#3
0
文件: utils.py 项目: bendavis78/zope
    def register_constructor_permission(self, permission, meta_type,
                                        instance_class):
        if permission is None:
            permission = "Add %ss" % (meta_type or instance_class.meta_type)

        if isinstance(permission, tuple):
            permission, default = permission
        else:
            default = ('Manager', )

        pr = PermissionRole(permission, default)
        registerPermissions(((permission, (), default), ))
        return pr, permission
def assertPRoles(ob, permission, expect):
    """
    Asserts that in the context of ob, the given permission maps to
    the given roles.
    """
    pr = PermissionRole(permission)
    roles = pr.__of__(ob)
    roles2 = aq_base(pr).__of__(ob)
    assert roles == roles2 or tuple(roles) == tuple(roles2), (
        'Different methods of checking roles computed unequal results')
    same = 0
    if roles is None or expect is None:
        if (roles is None or tuple(roles) == ('Anonymous',)) and (
            expect is None or tuple(expect) == ('Anonymous',)):
            same = 1
    else:
        got = {}
        for r in roles: got[r] = 1
        expected = {}
        for r in expect: expected[r] = 1
        if got == expected:  # Dict compare does the Right Thing.
            same = 1
    assert same, 'Expected roles: %s, got: %s' % (`expect`, `roles`)
示例#5
0
def _update_MembershipTool_searchMembers_permission():
    """ Repair Collector #189 by careful surgery on the class dictionary
        of MembershipTool.
    """

    new_permissions = []
    for k, v in MTool.__ac_permissions__:

        if k == View:
            new_v = [x for x in v if x != 'searchMembers']
            v = tuple(new_v)

        new_permissions.append((k, v))

    new_permissions.append((ListPortalMembers, ('searchMembers', )))

    MTool.__ac_permissions__ = tuple(new_permissions)
    MTool.searchMembers__roles__ = PermissionRole(ListPortalMembers,
                                                  ('Manager', ))

    LOG(
        "CMFHotfix_20031026", INFO, "Updated permission on " +
        "CMFCore.MembershipTool.MembershipTool.searchMembers" +
        " from 'View' to 'List portal members'")
示例#6
0
    def registerClass(self,
                      instance_class=None,
                      meta_type='',
                      permission=None,
                      constructors=(),
                      icon=None,
                      permissions=None,
                      legacy=(),
                      visibility="Global",
                      interfaces=_marker,
                      container_filter=None):
        """Register a constructor

        Keyword arguments are used to provide meta data:

        instance_class -- The class of the object that will be created.

          This is not currently used, but may be used in the future to
          increase object mobility.

        meta_type -- The kind of object being created
           This appears in add lists.  If not specified, then the class
           meta_type will be used.

        permission -- The permission name for the constructors.
           If not specified, then a permission name based on the
           meta type will be used.

        constructors -- A list of constructor methods
          A method can be a callable object with a __name__
          attribute giving the name the method should have in the
          product, or the method may be a tuple consisting of a
          name and a callable object.  The method must be picklable.

          The first method will be used as the initial method called
          when creating an object.

        icon -- No longer used.

        permissions -- Additional permissions to be registered
           If not provided, then permissions defined in the
           class will be registered.

        legacy -- A list of legacy methods to be added to ObjectManager
                  for backward compatibility

        visibility -- "Global" if the object is globally visible, None else

        interfaces -- a list of the interfaces the object supports

        container_filter -- function that is called with an ObjectManager
           object as the only parameter, which should return a true object
           if the object is happy to be created in that container. The
           filter is called before showing ObjectManager's Add list,
           and before pasting (after object copy or cut), but not
           before calling an object's constructor.

        """
        pack = self.__pack
        initial = constructors[0]
        productObject = self.__prod
        pid = productObject.id

        if permissions:
            if isinstance(permissions, str):  # You goofed it!
                raise TypeError(
                    'Product context permissions should be a '
                    'list of permissions not a string', permissions)
            for p in permissions:
                if isinstance(p, tuple):
                    p, default = p
                    registerPermissions(((p, (), default), ))
                else:
                    registerPermissions(((p, ()), ))

        ############################################################
        # Constructor permission setup
        if permission is None:
            permission = "Add %ss" % (meta_type or instance_class.meta_type)

        if isinstance(permission, tuple):
            permission, default = permission
        else:
            default = ('Manager', )

        pr = PermissionRole(permission, default)
        registerPermissions(((permission, (), default), ))
        ############################################################

        OM = ObjectManager

        for method in legacy:
            if isinstance(method, tuple):
                name, method = method
                aliased = 1
            else:
                name = method.__name__
                aliased = 0
            if name not in OM.__dict__:
                setattr(OM, name, method)
                setattr(OM, name + '__roles__', pr)
                if aliased:
                    # Set the unaliased method name and its roles
                    # to avoid security holes.  XXX: All "legacy"
                    # methods need to be eliminated.
                    setattr(OM, method.__name__, method)
                    setattr(OM, method.__name__ + '__roles__', pr)

        if isinstance(initial, tuple):
            name, initial = initial
        else:
            name = initial.__name__

        fd = getattr(pack, '__FactoryDispatcher__', None)
        if fd is None:

            class __FactoryDispatcher__(FactoryDispatcher):
                "Factory Dispatcher for a Specific Product"

            fd = pack.__FactoryDispatcher__ = __FactoryDispatcher__

        if not hasattr(pack, '_m'):
            pack._m = AttrDict(fd)

        m = pack._m

        if interfaces is _marker:
            if instance_class is None:
                interfaces = ()
            else:
                interfaces = tuple(implementedBy(instance_class))

        Products.meta_types = Products.meta_types + (
            {
                'name': meta_type or instance_class.meta_type,
                # 'action': The action in the add drop down in the ZMI. This is
                #           currently also required by the _verifyObjectPaste
                #           method of CopyContainers like Folders.
                'action': ('manage_addProduct/%s/%s' % (pid, name)),
                # 'product': product id
                'product': pid,
                # 'permission': Guards the add action.
                'permission': permission,
                # 'visibility': A silly name. Doesn't have much to do with
                #               visibility. Allowed values: 'Global', None
                'visibility': visibility,
                # 'interfaces': A tuple of oldstyle and/or newstyle interfaces.
                'interfaces': interfaces,
                'instance': instance_class,
                'container_filter': container_filter
            }, )

        m[name] = initial
        m[name + '__roles__'] = pr

        for method in constructors[1:]:
            if isinstance(method, tuple):
                name, method = method
            else:
                name = os.path.split(method.__name__)[-1]
            if name not in productObject.__dict__:
                m[name] = method
                m[name + '__roles__'] = pr
 def testPermissionRoleSupportsGetattr(self):
     a = PermissionRole('a')
     self.assertTrue(getattr(a, '__roles__') == ('Manager', ))
     self.assertTrue(getattr(a, '_d') == ('Manager', ))
     self.assertTrue(getattr(a, '__name__') == 'a')
     self.assertTrue(getattr(a, '_p') == '_a_Permission')
示例#8
0
def patch_pas():
    # sort alphabetically by patched/added method name
    wrap_method(PluggableAuthService, '_delOb', _delOb)
    wrap_method(
        PluggableAuthService,
        '_getAllLocalRoles',
        _getAllLocalRoles,
        add=True,
    )
    wrap_method(PluggableAuthService, '_doAddGroup', _doAddGroup, add=True)
    wrap_method(PluggableAuthService, '_doAddUser', _doAddUser)
    wrap_method(PluggableAuthService,
                '_doChangeGroup',
                _doChangeGroup,
                add=True)
    wrap_method(PluggableAuthService, '_doChangeUser', _doChangeUser, add=True)
    wrap_method(PluggableAuthService, '_doDelGroups', _doDelGroups, add=True)
    wrap_method(PluggableAuthService, '_doDelUser', _doDelUser, add=True)
    wrap_method(PluggableAuthService,
                '_doDelUsers',
                _doDelUsers,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                '_getLocalRolesForDisplay',
                _getLocalRolesForDisplay,
                add=True)
    wrap_method(PluggableAuthService, '_updateGroup', _updateGroup, add=True)
    wrap_method(PluggableAuthService,
                'addRole',
                addRole,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(
        PluggableAuthService,
        'authenticate',
        authenticate,
        add=True,
        roles=(),
    )
    wrap_method(PluggableAuthService,
                'canListAllGroups',
                canListAllGroups,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'canListAllUsers',
                canListAllUsers,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'credentialsChanged',
                credentialsChanged,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(
        PluggableAuthService,
        'getAllLocalRoles',
        getAllLocalRoles,
        add=True,
    )
    wrap_method(PluggableAuthService,
                'getGroup',
                getGroup,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'getGroupById',
                getGroupById,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'getGroupByName',
                getGroupByName,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'getGroupIds',
                getGroupIds,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'getGroupNames',
                getGroupNames,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'getGroups',
                getGroups,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(
        PluggableAuthService,
        'getLocalRolesForDisplay',
        getLocalRolesForDisplay,
        add=True,
    )
    wrap_method(
        PluggableAuthService,
        'getUserIds',
        getUserIds,
        add=True,
        deprecated="Inefficient GRUF wrapper, use IUserIntrospection instead.")
    wrap_method(
        PluggableAuthService,
        'getUserNames',
        getUserNames,
        add=True,
        deprecated="Inefficient GRUF wrapper, use IUserIntrospection instead.")
    wrap_method(PluggableAuthService,
                'getUsers',
                getUsers,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'getPureUsers',
                getUsers,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'userFolderAddUser',
                postonly(userFolderAddUser),
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'userFolderDelUsers',
                postonly(_doDelUsers),
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'userFolderEditGroup',
                postonly(_doChangeGroup),
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'userFolderEditUser',
                postonly(_doChangeUser),
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'userFolderDelGroups',
                postonly(_doDelGroups),
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
    wrap_method(PluggableAuthService,
                'userSetGroups',
                _userSetGroups,
                add=True,
                deprecated="Method from GRUF was removed.")
    wrap_method(PluggableAuthService,
                'userSetPassword',
                userSetPassword,
                add=True,
                roles=PermissionRole(ManageUsers, ('Manager', )))
示例#9
0
def InitializeClass(self):
    from AccessControl.Permission import registerPermissions
    from AccessControl.PermissionRole import PermissionRole
    dict=self.__dict__
    have=dict.has_key
    ft=type(InitializeClass)
    dict_items=dict.items()

    for name, v in dict_items:
        if getattr(v, '_need__name__', 0):
            d = v.__dict__
            oldname = d.get('__name__', '')
            if d.get('_implicit__name__', 0):
                # Already supplied a name.
                if name != oldname:
                    # Tried to implicitly assign a different name!
                    try: classname = '%s.%s' % (
                        self.__module__, self.__name__)
                    except AttributeError: classname = `self`
                    logging.getLogger("Init").warning(
                        'Ambiguous name for method of %s: %r != %r',
                        classname, d['__name__'], name)
            else:
                # Supply a name implicitly so that the method can
                # find the security assertions on its container.
                v._implicit__name__ = 1
                v.__name__ = name
            if name=='manage' or name[:7]=='manage_':
                name=name+'__roles__'
                if not have(name):
                    setattr(self, name, ('Manager',))
        elif name=='manage' or name[:7]=='manage_' and type(v) is ft:
            name=name+'__roles__'
            if not have(name):
                setattr(self, name, ('Manager',))
                
    # Look for a SecurityInfo object on the class. If found, call its
    # apply() method to generate __ac_permissions__ for the class. We
    # delete the SecurityInfo from the class dict after it has been
    # applied out of paranoia.
    for key, value in dict_items:
        if hasattr(value, '__security_info__'):
            security_info=value
            security_info.apply(self)
            delattr(self, key)
            break

    if self.__dict__.has_key('__ac_permissions__'):
        registerPermissions(self.__ac_permissions__)
        for acp in self.__ac_permissions__:
            pname, mnames = acp[:2]
            if len(acp) > 2:
                roles = acp[2]
                pr = PermissionRole(pname, roles)
            else:
                pr = PermissionRole(pname)
            for mname in mnames:
                setattr(self, mname+'__roles__', pr)
                if (mname and mname not in ('context', 'request') and
                    not hasattr(self, mname)):
                    # don't complain about context or request, as they are
                    # frequently not available as class attributes
                    logging.getLogger("Init").warning(
                        "Class %s.%s has a security declaration for "
                        "nonexistent method %r", self.__module__,
                        self.__name__, mname)
示例#10
0
class Movement(XMLObject, Amount, CompositionMixin, AmountGeneratorMixin):
    """
    The Movement class allows to implement ERP5 universal accounting model.

    Movement instances are used in different situations:

    - Orders: Movement instances are use as a documentary object
      to define quantities in orders

    - Deliveries: movements track the actual transfer of resources
      in the past (accounting) or in the future (planning / budgetting)

    For example, the following objects are Orders:

    - a purchase order (the document we send to a supplier
      when we need some goods)

    - a sales order (the document we ask our customer
      to sign to confirm a sale)

    - a production order (the document we send to the workshop
      to confirm we need some operation / service to be achieved)

    Orders allow to describe a target, but can not be used to account
    the reality of actual transfered quantities.

    This is not the case for Deliveries:

    - an invoice (a delivery of money between abstract accounts)

    - a packing list (ie. a delivery of goods shipped)

    - a delivery report (ie. a delivery of goods received)

    - a production report (ie. a delivery of service)

    - a T/T report (a delivery of money between reals accounts)


    For planning, the following approach is used:

    1- Movements from an order are never modified once the order
       is confirmed. This is a rule. An Order is like a contract.
       It can only be amended, if all parties agree.

    2- Movements in a delivery may exist on their own
       (ex. an accounting transaction). Past movements
       can not be modified. Future movements may or may
       not be modified

    When an order is confirmed, the list of "order" movements
    it contains is copied into "delivery" movements. Each delivery
    movement contains a "causality" reference to the order
    it. This allows delivery to be completely different from order
    (ex. different resource, different date, different quantity)
    and allows to keep track of the causal relation between
    a delivery and an order.

    A delivery document (actually a delivery line) then points to one or more of
    the "delivery" movements in the simulation. It is possible to know
    which items have been delivered by making sure each movement in the simulation
    is associated to a "delivery document".

    By looking at all "simulation"

    Delivery movements can be applied the following transformations:

    - split : one movement is cut into 2 or more movements

    - submovements : one movement "generates" many other movements.
      For example, a delivery of goods from the workshop to the stock,
      will result in a "pull" calculation which generates operations
      and sourcing based on a BOM document. The causality of each
      delivery is the "applied rule" which was used to generate submovements

    One should note that

    - movements are never joined (because it would break causality and
      tracability)

    - movements acquire some context information from their causality

    Some submovements need no order to be "confirmed". Some submovements
    need an order to be "confirmed". For example

    - a submovement which allows to compute the CO2 emissions
      of a production facility needs no order confirmation
      (this kind of movement is mostly ised for reporting)

    - a submovement which takes some goods in a stock and
      brings them to a workshop needs some "stock movement"
      order to be "confirmed"

    - a submovement which requires someone to take his time
      for some operation nees a "service order" to be confirmed

    This means that the simulation process must make a distinction
    between different workflows applicable to a movement. For
    movements which require an order to be confirmed, the workflow
    involves the following steps:

    - an order is automaticaly generated, with "order movements"
      which become "causalities" for delivery movements (XXX
      this sound strange...)

    - each order movement is associated to one of the delivery

    As a result, a delivery movement which requires an order may
    have 2 causalities

    - one causality (ie. application of a rule)

    - another causality (ie. confirmation in an order)

    Each causality may define its own context. One context
    may be related for example to a customer request, another context
    may be related to an "internal customer" request (eg. the production manager).
    Context may eventually conflict each other.

    In a customer oriented company, movements should probably
    always be stored within the customer order and acquire
    from the order all context information.

    In a mass production facility, context may be defined in more
    than one location. This is an incentive for putting
    all movements in a simulation "box".

    The second approach is chosen for documentary consistency approach :
      in ERP5, documents rules, can be synchronized. Simulation can not be
      synchronized

    TODO:
    - consider creating a class GeneratedMovement
      and move some superfluous code to it
  """
    meta_type = 'ERP5 Movement'
    portal_type = 'Movement'
    add_permission = Permissions.AddPortalContent

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

    # Declarative interfaces
    zope.interface.implements(interfaces.IAmountGenerator,
                              interfaces.IVariated, interfaces.IMovement)

    # Declarative properties
    property_sheets = (
        PropertySheet.Base,
        PropertySheet.SimpleItem,
        PropertySheet.CategoryCore,
        PropertySheet.Amount,
        PropertySheet.Reference,
        PropertySheet.Task,
        PropertySheet.Arrow,
        PropertySheet.Movement,
        PropertySheet.Price,
        PropertySheet.
        Simulation  # XXX-JPS property should be moved to GeneratedMovement class
    )

    def isPropertyRecorded(
            self,
            k):  # XXX-JPS method should be moved to GeneratedMovement class
        return False

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isMovement')

    def isMovement(self):
        return 1

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isAccountable')

    def isAccountable(self):
        return True

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isMovingItem')

    def isMovingItem(self, item):
        type_based_script = self._getTypeBasedMethod('isMovingItem')
        if type_based_script:
            return type_based_script(item)
        return False

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getMovedItemUidList')

    def getMovedItemUidList(self):
        """This method returns an uid list of items
    """
        return [item.getUid() for item in self.getAggregateValueList() \
          if self.isMovingItem(item)]

    # Pricing methods
    def _getPrice(self, context):
        context = self.asContext(context=context,
                                 quantity=self.getConvertedQuantity())
        operand_dict = self.getPriceCalculationOperandDict(context=context)
        if operand_dict is not None:
            price = operand_dict['price']
            resource = self.getResourceValue()
            quantity_unit = self.getQuantityUnit()
            if price is not None and quantity_unit and resource is not None:
                return resource.convertQuantity(
                    price, quantity_unit, resource.getDefaultQuantityUnit(),
                    self.getVariationCategoryList())
            return price

    def _getTotalPrice(self, default=None, context=None, fast=0, **kw):
        price = self.getPrice(context=context)
        quantity = self.getQuantity()
        if isinstance(price, (int, float)) and \
          isinstance(quantity, (int, float)):
            return quantity * price
        else:
            return default

    def _getBaseUnitPrice(self, context):
        # Override Amount._getBaseUnitPrice to use Movement's
        # getPriceCalculationOperandDict instead of Resource's.
        operand_dict = context.getPriceCalculationOperandDict(context=context)
        if operand_dict is not None:
            base_unit_price = operand_dict.get('base_unit_price', None)
            return base_unit_price

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getPriceCalculationOperandDict')

    def getPriceCalculationOperandDict(self, default=None, context=None, **kw):
        """Return a dict object which contains operands used for price
    calculation. The returned items depend on a site configuration,
    because this will invoke a custom script at the end. The only
    assumption is that the dict must contain a key 'price'
    which represents the final result of the price calculation.

    The purpose is to obtain descriptive information to notify the user
    of how a price is calculated in details, in particular, for invoices
    and quotations. So a script which is eventually called should provide
    all values required for generating such reports (e.g. a price,
    a price without a discount, and a discount).
    """
        # First, try a type-based method, and if not present, use
        # the good-old-days way (which only returns a final result).
        if context is None:
            context = self
        method = context._getTypeBasedMethod('getPriceCalculationOperandDict')
        if method is None:
            # Try this, because when the context is an instance of a derived
            # class of Movement, Movement_getPriceCalculationOperandDict is
            # not searched.
            method = getattr(context,
                             'Movement_getPriceCalculationOperandDict', None)
        if method is not None:
            operand_dict = unrestricted_apply(method, kw=kw)
            if operand_dict is None:
                return default
            assert 'price' in operand_dict
            return operand_dict
        return {'price': context.Movement_lookupPrice()}

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getPrice')

    def getPrice(self, default=None, context=None, evaluate=1, **kw):
        """
      Get the Price in the context.

      If price is not stored locally, lookup a price and store it.

      FIXME: Don't trust this docstring, this method is not at all using the
      passed context, but uses this movement as context.
    """
        # XXX As all accessors can recieve the default value as first positional
        # argument, so we changed the first positional argument from context to
        # default. Here we try to provide backward compatibility for scripts
        # passing the context as first positional argument, and advice them to use:
        #   context.getPrice(context=context)
        # instead of:
        #   context.getPrice(context)
        if isinstance(default, Base):
            msg = 'getPrice first argument is supposed to be the default value'\
                  ' accessor, the context should be passed as with the context='\
                  ' keyword argument'
            warn(msg, DeprecationWarning)
            LOG('ERP5', WARNING, msg)
            context = default
            default = None

        if len(kw):
            warn(
                'Passing keyword arguments to Movement.getPrice has no effect',
                DeprecationWarning)

        local_price = self._baseGetPrice()
        if local_price is None and evaluate:
            # We must find a price for this movement
            local_price = self._getPrice(context=self)
        return local_price

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getTotalPrice')

    def getTotalPrice(self,
                      default=0.0,
                      context=None,
                      REQUEST=None,
                      fast=None,
                      **kw):
        """Return the total price in the context.

    The optional parameter "fast" is for compatibility, and will be ignored.
    """
        # see getPrice
        if isinstance(default, Base) and context is None:
            msg = 'getTotalPrice first argument is supposed to be the default value'\
                  ' accessor, the context should be passed as with the context='\
                  ' keyword argument'
            warn(msg, DeprecationWarning)
            LOG('ERP5', WARNING, msg)
            context = default
            default = None

        tmp_context = self.asContext(context=context, REQUEST=REQUEST, **kw)
        result = self._getTotalPrice(default=default,
                                     context=tmp_context,
                                     fast=fast,
                                     **kw)
        method = self._getTypeBasedMethod('convertTotalPrice')
        if method is None:
            return result
        return method(result)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getTotalQuantity')

    def getTotalQuantity(self, default=0.0):
        """
      Returns the quantity if no cell or the total quantity if cells
    """
        return self.getQuantity(default=default)

    # Industrial price API
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getIndustrialPrice')

    def getIndustrialPrice(self):
        """
      Calculates industrial price in context of this movement
    """
        resource = self.getResourceValue()
        if resource is not None:
            return resource.getIndustrialPrice(context=self)
        return None

    # Asset price calculation
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSourceInventoriatedTotalAssetPrice')

    def getSourceInventoriatedTotalAssetPrice(self):
        """
      Returns a price which can be used to calculate stock value (asset)

      Asset price is used for calculation of inventory asset value
      and for accounting

      If the asset price is specified (as in accounting for multi-currency),
      then it is returned. If no asset price is specified, then we use
      the price as defined on the line, but only for incoming quantities
      (purchase price, industrial price, etc.).

      For outgoing quantities, it is the responsability of database
      to calculate asset prices based on calculation rules (FIFO,
      LIFO, AVERAGE, etc.).
    """
        # This is what we use for accounting
        result = self.getSourceTotalAssetPrice()
        if result is not None:
            return result
        quantity = self.getQuantity()
        if quantity:
            source_asset_price = self.getSourceAssetPrice()
            if source_asset_price:
                return source_asset_price * -quantity
        return None

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSourceInventoriatedTotalAssetDebit')

    def getSourceInventoriatedTotalAssetDebit(self):
        """
      Returns the debit part of inventoriated source total asset price.
    """
        result = self.getSourceInventoriatedTotalAssetPrice()
        if result is not None:
            if (result > 0) ^ bool(self.isCancellationAmount()):
                return result
        return 0.0

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSourceInventoriatedTotalAssetCredit')

    def getSourceInventoriatedTotalAssetCredit(self):
        """
      Returns the credit part of inventoriated source total asset price.
    """
        result = self.getSourceInventoriatedTotalAssetPrice()
        if result is not None:
            if (result < 0) ^ bool(self.isCancellationAmount()):
                return -result
        return 0.0

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDestinationInventoriatedTotalAssetPrice')

    def getDestinationInventoriatedTotalAssetPrice(self):
        """
      Returns a price which can be used to calculate stock value (asset)

      Asset price is used for calculation of inventory asset value
      and for accounting
    """
        # This is what we use for accounting
        result = self.getDestinationTotalAssetPrice()
        if result is not None:
            return result
        quantity = self.getQuantity()
        if quantity:
            destination_asset_price = self.getDestinationAssetPrice()
            if destination_asset_price:
                return destination_asset_price * quantity
        return None

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDestinationInventoriatedTotalAssetDebit')

    def getDestinationInventoriatedTotalAssetDebit(self):
        """
      Returns the debit part of inventoriated destination total asset price.
    """
        result = self.getDestinationInventoriatedTotalAssetPrice()
        if result is not None:
            if (result > 0) ^ bool(self.isCancellationAmount()):
                return result
        return 0.0

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDestinationInventoriatedTotalAssetCredit')

    def getDestinationInventoriatedTotalAssetCredit(self):
        """
      Returns the credit part of inventoriated destination total asset price.
    """
        result = self.getDestinationInventoriatedTotalAssetPrice()
        if result is not None:
            if (result < 0) ^ bool(self.isCancellationAmount()):
                return -result
        return 0.0

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSourceAssetPrice')

    def getSourceAssetPrice(self):
        """
      Returns the price converted to the currency of the source section

      This will be implemeted by calling currency conversion on currency resources
    """
        type_based_script = self._getTypeBasedMethod('getSourceAssetPrice')
        if type_based_script:
            return type_based_script()
        return self._getAssetPrice(section=self.getSourceSectionValue(),
                                   date=self.getStartDate())

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDestinationAssetPrice')

    def getDestinationAssetPrice(self):
        """
      Returns the price converted to the currency of the destination section
    """
        type_based_script = self._getTypeBasedMethod(
            'getDestinationAssetPrice')
        if type_based_script:
            return type_based_script()
        return self._getAssetPrice(section=self.getDestinationSectionValue(),
                                   date=self.getStopDate())

    def _getAssetPrice(self, section, date):
        price = self.getPrice()
        if section is None or not price or getattr(
                section.aq_base, 'getPriceCurrencyValue', None) is None:
            return price
        currency_value = self.getPriceCurrencyValue()
        if currency_value:
            section_currency = section.getPriceCurrency()
            if section_currency:
                exchange_rate = getExchangeRate(currency_value,
                                                section_currency, date)
                if exchange_rate:
                    return exchange_rate * price
        return price

    # Causality computation
    security.declareProtected(Permissions.AccessContentsInformation,
                              'isConvergent')

    def isConvergent(self):
        """
      Returns true if movement is not divergent
    """
        return bool(not self.isDivergent())

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isDivergent')

    def isDivergent(self):
        """Return True if this movement diverges from the its simulation.
    """
        for simulation_movement in self.getDeliveryRelatedValueList():
            if simulation_movement.isDivergent():
                return True
        return False

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDivergenceList')

    def getDivergenceList(self):
        """
    Return a list of messages that contains the divergences
    """
        divergence_list = []
        for simulation_movement in self.getDeliveryRelatedValueList():
            divergence_list.extend(simulation_movement.getDivergenceList())

        return divergence_list

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getExplanation')

    def getExplanation(self):
        """
      Returns the relative_url of the explanation of this movement.
    """
        explanation = self.getExplanationValue()
        if explanation is not None:
            return explanation.getRelativeUrl()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getExplanationUid')

    def getExplanationUid(self):
        """
      Returns the uid of the explanation of this movement.
    """
        explanation = self.getExplanationValue()
        if explanation is not None:
            return explanation.getUid()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getExplanationValue')

    def getExplanationValue(self):
        """
      Returns the object explanation of this movement.
    """
        try:
            return self.getRootDeliveryValue()
        except AttributeError:
            return None

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getExplanationTitle')

    def getExplanationTitle(self, default=''):
        """
      Returns the title of the explanation of this movement.
    """
        explanation_value = self.getExplanationValue()
        if explanation_value is not None:
            return explanation_value.getTitle()
        return default

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getExplanationReference')

    def getExplanationReference(self, default=''):
        """
      Returns the reference of the explanation of this movement.
    """
        explanation_value = self.getExplanationValue()
        if explanation_value is not None:
            return explanation_value.getReference()
        return default

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getRootCausalityValueList')

    def getRootCausalityValueList(self):
        """
      Returns the initial causality value for this movement.
      This method will look at the causality and check if the
      causality has already a causality
    """
        return self.getExplanationValue().getRootCausalityValueList()

    # Simulation
    security.declareProtected(Permissions.AccessContentsInformation,
                              'isSimulated')

    def isSimulated(self):
        # 'order' category is deprecated. it is kept for compatibility.
        return (len(self.getDeliveryRelatedValueList()) > 0) or\
               (len(self.getOrderRelatedValueList()) > 0)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isGeneratedBySimulation')

    def isGeneratedBySimulation(self):
        """
      Returns true if the movement is linked to a simulation movement whose
      parent is not a root applied rule, even if the movement is being built.

      Otherwise, this means the movement is or should be linked to a root
      simulation movement.
    """
        simulation_movement = self.getDeliveryRelatedValue()
        return simulation_movement is not None and \
           not simulation_movement.getParentValue().isRootAppliedRule()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSimulationQuantity')

    def getSimulationQuantity(self):
        """Computes the quantities in the simulation.
    """
        return sum(m.getQuantity() for m in self.getDeliveryRelatedValueList())

    # Debit and credit methods
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSourceDebit')

    def getSourceDebit(self):
        """
      Return the quantity
    """
        quantity = self.getQuantity()
        try:
            quantity = float(quantity)
        except TypeError:
            quantity = 0.0
        if (quantity < 0) ^ bool(self.isCancellationAmount()):
            return -quantity
        return 0.0

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSourceCredit')

    def getSourceCredit(self):
        """
      Return the quantity
    """
        quantity = self.getQuantity()
        try:
            quantity = float(quantity)
        except TypeError:
            quantity = 0.0
        if (quantity < 0) ^ bool(self.isCancellationAmount()):
            return 0.0
        return quantity

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDestinationDebit', 'getDestinationCredit')
    getDestinationDebit = getSourceCredit
    getDestinationCredit = getSourceDebit

    security.declareProtected(Permissions.ModifyPortalContent,
                              'setSourceDebit')

    def setSourceDebit(self, source_debit):
        """
      Set the quantity
    """
        if source_debit in (None, ''):
            return
        try:
            source_debit = float(source_debit)
        except TypeError:
            source_debit = 0.0
        self.setCancellationAmount(source_debit < 0)
        self.setQuantity(-source_debit)

    security.declareProtected(Permissions.ModifyPortalContent,
                              'setSourceCredit')

    def setSourceCredit(self, source_credit):
        """
      Set the quantity
    """
        if source_credit in (None, ''):
            return
        try:
            source_credit = float(source_credit)
        except TypeError:
            source_credit = 0.0
        self.setCancellationAmount(source_credit < 0)
        self.setQuantity(source_credit)

    security.declareProtected(Permissions.ModifyPortalContent,
                              'setDestinationDebit', 'setDestinationCredit')
    setDestinationDebit = setSourceCredit
    setDestinationCredit = setSourceDebit

    security.declarePrivate('_edit')

    def _edit(self, edit_order=(), **kw):
        """Overloaded _edit to support setting debit and credit at the same time,
    which is required for the GUI.
    Also sets the variation category list and property dict at the end, because
    _setVariationCategoryList and _setVariationPropertyDict needs the resource
    to be set.
    """
        quantity = 0
        if 'source_debit' in kw and 'source_credit' in kw:
            source_credit = kw.pop('source_credit') or 0
            source_debit = kw.pop('source_debit') or 0
            quantity += (source_credit - source_debit)
            kw['quantity'] = quantity
            kw['cancellation_amount'] = (source_credit < 0 or source_debit < 0)
        if 'destination_debit' in kw and 'destination_credit' in kw:
            destination_credit = kw.pop('destination_credit') or 0
            destination_debit = kw.pop('destination_debit') or 0
            quantity += (destination_debit - destination_credit)
            kw['quantity'] = quantity
            kw['cancellation_amount'] = (destination_credit < 0
                                         or destination_debit < 0)

        # If both asset debit and asset credit are passed, we have to take care not
        # to erase the asset price if one of them is unset.
        if kw.get('source_asset_debit') or kw.get('source_asset_credit'):
            if kw.get('source_asset_debit') in (None, ''):
                kw.pop('source_asset_debit', None)
            if kw.get('source_asset_credit') in (None, ''):
                kw.pop('source_asset_credit', None)
        if kw.get('destination_asset_debit') or kw.get(
                'destination_asset_credit'):
            if kw.get('destination_asset_debit') in (None, ''):
                kw.pop('destination_asset_debit', None)
            if kw.get('destination_asset_credit') in (None, ''):
                kw.pop('destination_asset_credit', None)

        if not edit_order:
            edit_order = (
                'variation_category_list',
                'variation_property_dict',
            )
        return XMLObject._edit(self, edit_order=edit_order, **kw)

    # Debit and credit methods for asset
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSourceAssetDebit')

    def getSourceAssetDebit(self):
        """
      Return the debit part of the source total asset price.

      This is the same as getSourceDebit where quantity is replaced
      by source_total_asset_price.
      This method returns 0 if the total asset price is not set.
    """
        quantity = self.getSourceTotalAssetPrice()
        try:
            quantity = float(quantity)
        except TypeError:
            quantity = 0.0
        if (quantity < 0) ^ bool(self.isCancellationAmount()):
            return 0.0
        return quantity

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSourceAssetCredit')

    def getSourceAssetCredit(self):
        """
      Return the credit part of the source total asset price.

      This is the same as getSourceCredit where quantity is replaced
      by source_total_asset_price.
      This method returns 0 if the total asset price is not set.
    """
        quantity = self.getSourceTotalAssetPrice()
        try:
            quantity = float(quantity)
        except TypeError:
            quantity = 0.0
        if (quantity < 0) ^ bool(self.isCancellationAmount()):
            return -quantity
        return 0.0

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDestinationAssetDebit')

    def getDestinationAssetDebit(self):
        """
      Return the debit part of the destination total asset price.

      This is the same as getDestinationDebit where quantity is replaced
      by destination_total_asset_price.
      This method returns 0 if the total asset price is not set.
    """
        quantity = self.getDestinationTotalAssetPrice()
        try:
            quantity = float(quantity)
        except TypeError:
            quantity = 0.0
        if (quantity < 0) ^ bool(self.isCancellationAmount()):
            return 0.0
        return quantity

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDestinationAssetCredit')

    def getDestinationAssetCredit(self):
        """
      Return the credit part of the destination total asset price.

      This is the same as getDestinationCredit where quantity is replaced
      by destination_total_asset_price.
      This method returns 0 if the total asset price is not set.
    """
        quantity = self.getDestinationTotalAssetPrice()
        try:
            quantity = float(quantity)
        except TypeError:
            quantity = 0.0
        if (quantity < 0) ^ bool(self.isCancellationAmount()):
            return -quantity
        return 0.0

    security.declareProtected(Permissions.ModifyPortalContent,
                              'setSourceAssetDebit')

    def setSourceAssetDebit(self, source_debit):
        """
      Set the source total asset price
    """
        if source_debit in (None, ''):
            self.setSourceTotalAssetPrice(None)
            return
        try:
            source_debit = float(source_debit)
        except TypeError:
            source_debit = 0.0
        self.setCancellationAmount(source_debit < 0)
        self.setSourceTotalAssetPrice(source_debit)

    security.declareProtected(Permissions.ModifyPortalContent,
                              'setSourceAssetCredit')

    def setSourceAssetCredit(self, source_credit):
        """
      Set the source total asset price
    """
        if source_credit in (None, ''):
            self.setSourceTotalAssetPrice(None)
            return
        try:
            source_credit = float(source_credit)
        except TypeError:
            source_credit = 0.0
        self.setCancellationAmount(source_credit < 0)
        self.setSourceTotalAssetPrice(-source_credit)

    security.declareProtected(Permissions.ModifyPortalContent,
                              'setDestinationAssetDebit')

    def setDestinationAssetDebit(self, destination_debit):
        """
      Set the destination total asset price
    """
        if destination_debit in (None, ''):
            self.setDestinationTotalAssetPrice(None)
            return
        try:
            destination_debit = float(destination_debit)
        except TypeError:
            destination_debit = 0.0
        self.setCancellationAmount(destination_debit < 0)
        self.setDestinationTotalAssetPrice(destination_debit)

    security.declareProtected(Permissions.ModifyPortalContent,
                              'setDestinationAssetCredit')

    def setDestinationAssetCredit(self, destination_credit):
        """
      Set the destination total asset price
    """
        if destination_credit in (None, ''):
            self.setDestinationTotalAssetPrice(None)
            return
        try:
            destination_credit = float(destination_credit)
        except TypeError:
            destination_credit = 0.0
        self.setCancellationAmount(destination_credit < 0)
        self.setDestinationTotalAssetPrice(-destination_credit)

    # Item Access (tracking)
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getTrackedItemUidList')

    def getTrackedItemUidList(self):
        """
      Return a list of uid for related items
    """
        ### XXX We should filter by portal type here
        return self.getAggregateUidList()

    # Helper methods to display total quantities as produced / consumed
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getProductionTotalQuantity')

    def getProductionTotalQuantity(self):
        """
      Return the produced quantity
    """
        quantity = self.getTotalQuantity()
        return self.getProductionQuantity(quantity=quantity)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getConsumptionTotalQuantity')

    def getConsumptionTotalQuantity(self):
        """
      Return the produced quantity
    """
        quantity = self.getTotalQuantity()
        return self.getConsumptionQuantity(quantity=quantity)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSubVariationText')

    def getSubVariationText(self, **kw):
        """
    Provide a string representation of XXX
    """
        base_category_list = self.getPortalSubVariationBaseCategoryList()
        portal_type_list = self.getPortalSubVariationTypeList()
        return_list = []
        for base_category in base_category_list:
            variation_list = self.getAcquiredCategoryMembershipList(
                base_category, portal_type=portal_type_list, base=1)
            return_list.extend(variation_list)
        return "\n".join(return_list)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getParentExplanationValue')

    def getParentExplanationValue(self):
        """
      This method should be removed as soon as movement groups
      will be rewritten. It is a temp hack
    """
        return self.getParentValue().getExplanationValue()

    # SKU vs. CU


#   security.declareProtected(Permissions.AccessContentsInformation, 'getSourceStandardInventoriatedQuantity')
#   def getSourceStandardInventoriatedQuantity(self):
#     """
#       The inventoriated quantity converted in a default unit
#
#       For assortments, returns the inventoriated quantity in terms of number of items
#       in the assortemnt.
#
#       For accounting, returns the quantity converted in a default unit
#     """
#     return self.getStandardInventoriatedQuantity()

#   security.declareProtected(Permissions.AccessContentsInformation, 'getDestinationStandardInventoriatedQuantity')
#   def getDestinationStandardInventoriatedQuantity(self):
#     """
#       The inventoriated quantity converted in a default unit
#
#       For assortments, returns the inventoriated quantity in terms of number of items
#       in the assortemnt.
#
#       For accounting, returns the quantity converted in a default unit
#     """
#     return self.getStandardInventoriatedQuantity()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'asMovementList')

    def asMovementList(self):
        """
    Placeholder method called when indexing a movement.

    It can be overloaded to generate multiple movements
    from a single one.
    It is used for cataloging a movement multiple time in
    the movement/stock tables.

    Ex: a movement have multiple destinations.
    asMovementList returns a list a movement context with different
    single destination.
    """
        return (self, )

    # XXX: Dirty but required for erp5_banking_core
    getBaobabSourceUid = lambda x: x.getSourceUid()
    getBaobabSourceUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationUid = lambda x: x.getDestinationUid()
    getBaobabDestinationUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceSectionUid = lambda x: x.getSourceSectionUid()
    getBaobabSourceSectionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationSectionUid = lambda x: x.getDestinationSectionUid()
    getBaobabDestinationSectionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourcePaymentUid = lambda x: x.getSourcePaymentUid()
    getBaobabSourcePaymentUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationPaymentUid = lambda x: x.getDestinationPaymentUid()
    getBaobabDestinationPaymentUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceFunctionUid = lambda x: x.getSourceFunctionUid()
    getBaobabSourceFunctionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationFunctionUid = lambda x: x.getDestinationFunctionUid()
    getBaobabDestinationFunctionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceProjectUid = lambda x: x.getSourceProjectUid()
    getBaobabSourceProjectUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationProjectUid = lambda x: x.getDestinationProjectUid()
    getBaobabDestinationProjectUid__roles__ = PermissionRole(Permissions.View)
示例#11
0
class InventoryLine(DeliveryLine):
    """
  An Inventory Line describe the inventory of a resource, by variations.
  """
    meta_type = 'ERP5 Inventory Line'
    portal_type = 'Inventory Line'
    add_permission = Permissions.AddPortalContent
    isInventoryMovement = ConstantGetter('isInventoryMovement', value=True)

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

    # Declarative properties
    property_sheets = (PropertySheet.Base, PropertySheet.XMLObject,
                       PropertySheet.CategoryCore, PropertySheet.Amount,
                       PropertySheet.InventoryMovement, PropertySheet.Task,
                       PropertySheet.Arrow, PropertySheet.Movement,
                       PropertySheet.VariationRange,
                       PropertySheet.ItemAggregation)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getTotalInventory')

    def getTotalInventory(self):
        """
    Returns the inventory if no cell or the total inventory if cells
    """
        if not self.hasCellContent():
            return self.getInventory()
        else:
            total_quantity = 0.0
            for cell in self.getCellValueList(base_id='movement'):
                if cell.getInventory() is not None:
                    total_quantity += cell.getInventory()
            return total_quantity

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getQuantity')

    def getQuantity(self):
        """
    Computes a quantity which allows to reach inventory
    """
        if not self.hasCellContent():
            # First check if quantity already exists
            quantity = self._baseGetQuantity()
            if quantity not in (0.0, 0, None):
                return quantity
            # Make sure inventory is defined somewhere (here or parent)
            inventory = getattr(aq_base(self), 'inventory', None)
            if inventory is not None:
                return inventory
            return quantity
        else:
            return None

    # Inventory cataloging
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getConvertedInventory')

    def getConvertedInventory(self):
        """
    provides a default inventory value - None since
    no inventory was defined.
    """
        return self.getInventory()  # XXX quantity unit is missing

    # Required for indexing
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getInventoriatedQuantity')

    def getInventoriatedQuantity(self):
        """
    Take into account efficiency in converted target quantity
    """
        return Movement.getInventoriatedQuantity(self)

    # XXX: Dirty but required for erp5_banking_core
    getBaobabSourceUid = lambda x: x.getSourceUid()
    getBaobabSourceUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationUid = lambda x: x.getDestinationUid()
    getBaobabDestinationUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceSectionUid = lambda x: x.getSourceSectionUid()
    getBaobabSourceSectionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationSectionUid = lambda x: x.getDestinationSectionUid()
    getBaobabDestinationSectionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourcePaymentUid = lambda x: x.getSourcePaymentUid()
    getBaobabSourcePaymentUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationPaymentUid = lambda x: x.getDestinationPaymentUid()
    getBaobabDestinationPaymentUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceFunctionUid = lambda x: x.getSourceFunctionUid()
    getBaobabSourceFunctionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationFunctionUid = lambda x: x.getDestinationFunctionUid()
    getBaobabDestinationFunctionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceProjectUid = lambda x: x.getSourceProjectUid()
    getBaobabSourceProjectUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationProjectUid = lambda x: x.getDestinationProjectUid()
    getBaobabDestinationProjectUid__roles__ = PermissionRole(Permissions.View)
示例#12
0
            tabs.append({
                'label': 'Doc',
                'action': 'showDocumentation',
                'help': ('DocFinderTab', 'README.stx')
            })
        return tabs

    if not hasattr(Tabs, '_old_filtered_manage_options'):
        Tabs._old_filtered_manage_options = Tabs.filtered_manage_options
        Tabs.filtered_manage_options = filtered_manage_options

    showDocumentation = HTMLFile('dtml/showDocumentation', globals())

    def analyseDocumentation(self, object, type='scripter', filter=''):
        return DocFinder(object, type, filter)

    ViewDocRoles = PermissionRole(ViewDocPermission, ViewDocDefaultRoles)

    Item.showDocumentation = showDocumentation
    Item.showDocumentation__roles__ = ViewDocRoles
    Item.analyseDocumentation = analyseDocumentation
    Item.analyseDocumentation__roles__ = ViewDocRoles

    logger = getLogger('DocFinderTab')
    logger.info('Applied patch version %s.', __version__)

except:

    import traceback
    traceback.print_exc()
示例#13
0
class DeliveryCell(MappedValue, Movement, ImmobilisationMovement):
    """
      A DeliveryCell allows to define specific quantities
      for each variation of a resource in a delivery line.
    """

    meta_type = 'ERP5 Delivery Cell'
    portal_type = 'Delivery Cell'
    isCell = 1

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

    # Declarative properties
    property_sheets = (PropertySheet.Base, PropertySheet.CategoryCore,
                       PropertySheet.Arrow, PropertySheet.Amount,
                       PropertySheet.Task, PropertySheet.Movement,
                       PropertySheet.Price, PropertySheet.Predicate,
                       PropertySheet.MappedValue,
                       PropertySheet.ItemAggregation)

    # Declarative interfaces
    zope.interface.implements(interfaces.IDivergenceController, )

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isPredicate')

    def isPredicate(self):
        """Movements are not predicates.
      """
        return False

    # MatrixBox methods
    security.declareProtected(Permissions.AccessContentsInformation,
                              'hasCellContent')

    def hasCellContent(self, base_id='movement'):
        """A cell cannot have cell content itself.
      """
        return 0

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isAccountable')

    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
      """
        return self.getParentValue().getParentValue().isAccountable()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getPrice')

    def getPrice(self, *args, **kw):
        """
      call Movement.getPrice
      """
        return Movement.getPrice(self, *args, **kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getTotalPrice')

    def getTotalPrice(self, default=0.0, *args, **kw):
        """
      call Movement.getTotalPrice
      """
        return Movement.getTotalPrice(self, default=default, *args, **kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getRootDeliveryValue')

    def getRootDeliveryValue(self):
        """
      Returns the root delivery responsible of this cell
      """
        return self.getParentValue().getRootDeliveryValue()

    security.declareProtected(Permissions.ModifyPortalContent,
                              'notifyAfterUpdateRelatedContent')

    def notifyAfterUpdateRelatedContent(self, previous_category_url,
                                        new_category_url):
        """
        Membership Crirerions and Category List are same in DeliveryCell
        Must update it (or change implementation to remove data duplication)
      """
        update_method = self.portal_categories.updateRelatedCategory
        predicate_value = self.getPredicateValueList()
        new_predicate_value = map(
            lambda c: update_method(c, previous_category_url, new_category_url
                                    ), predicate_value)
        self._setPredicateValueList(new_predicate_value)
        # No reindex needed since uid stable

    # XXX FIXME: option variation are today not well implemented
    # This little hack is needed to make the matrixbox working
    # in DeliveryLine_viewIndustrialPhase
    # Generic form (DeliveryLine_viewOption) is required
    def _edit(self, **kw):
        """
        Store variation_category_list, in order to store new value of
        industrial_phase after.
      """
        edit_order = [
            'variation_category_list',  # edit this one first
            'item_id_list'
        ]  # this one must be the last
        edit_order[1:1] = [
            x for x in kw.pop('edit_order', ()) if x not in edit_order
        ]
        # Base._edit updates unordered properties first
        edit_order[1:1] = [x for x in kw if x not in edit_order]
        MappedValue._edit(self, edit_order=edit_order, **kw)
#       if self.isSimulated():
#         self.getRootDeliveryValue().activate().propagateResourceToSimulation()

    security.declareProtected(Permissions.ModifyPortalContent,
                              'updateSimulationDeliveryProperties')

    def updateSimulationDeliveryProperties(self, movement_list=None):
        """
      Set properties delivery_ratio and delivery_error for each
      simulation movement in movement_list (all movements by default),
      according to this delivery calculated quantity
      """
        parent = self.getParentValue()
        if parent is not None:
            parent = parent.getParentValue()
            if parent is not None:
                parent.updateSimulationDeliveryProperties(movement_list, self)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isMovement')

    def isMovement(self):
        return 1

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isMovingItem')

    def isMovingItem(self, item):
        type_based_script = self._getTypeBasedMethod('isMovingItem')
        if type_based_script:
            return type_based_script(item)
        return self.isAccountable()

    # Override getQuantityUnitXXX to negate same methods defined in
    # Amount class. Because cell must acquire quantity unit from line
    # not from resource.
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getQuantityUnitValue')

    def getQuantityUnitValue(self):
        return self.getParentValue().getQuantityUnitValue()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getQuantityUnit')

    def getQuantityUnit(self, checked_permission=None):
        return self.getParentValue().getQuantityUnit(
            checked_permission=checked_permission)

    # XXX: Dirty but required for erp5_banking_core
    ### Acquire Baobab source / destination uids from parent line
    getBaobabSourceUid = lambda x: x.getSourceUid()
    getBaobabSourceUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationUid = lambda x: x.getDestinationUid()
    getBaobabDestinationUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceSectionUid = lambda x: x.getSourceSectionUid()
    getBaobabSourceSectionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationSectionUid = lambda x: x.getDestinationSectionUid()
    getBaobabDestinationSectionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourcePaymentUid = lambda x: x.getSourcePaymentUid()
    getBaobabSourcePaymentUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationPaymentUid = lambda x: x.getDestinationPaymentUid()
    getBaobabDestinationPaymentUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceFunctionUid = lambda x: x.getSourceFunctionUid()
    getBaobabSourceFunctionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationFunctionUid = lambda x: x.getDestinationFunctionUid()
    getBaobabDestinationFunctionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceProjectUid = lambda x: x.getSourceProjectUid()
    getBaobabSourceProjectUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationProjectUid = lambda x: x.getDestinationProjectUid()
    getBaobabDestinationProjectUid__roles__ = PermissionRole(Permissions.View)
def apply_delete_objects_permission_role(klass, name, replacement):
    setattr(klass, name, PermissionRole('Delete objects', None))
示例#15
0
class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin,
               CompositionMixin, AmountGeneratorMixin):
    """
        Each time delivery is modified, it MUST launch a reindexing of
        inventories which are related to the resources contained in the Delivery
    """
    # CMF Type Definition
    meta_type = 'ERP5 Delivery'
    portal_type = 'Delivery'
    isDelivery = ConstantGetter('isDelivery', value=True)

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

    # Default Properties
    property_sheets = ( PropertySheet.Base
                      , PropertySheet.XMLObject
                      , PropertySheet.CategoryCore
                      , PropertySheet.DublinCore
                      , PropertySheet.Task
                      , PropertySheet.Arrow
                      , PropertySheet.Movement
                      , PropertySheet.Delivery
                      , PropertySheet.Reference
                      , PropertySheet.Price
                      )

    # Declarative interfaces
    zope.interface.implements(interfaces.IAmountGenerator,
                              interfaces.IDivergenceController,
                              interfaces.IMovementCollection)

    security.declareProtected(Permissions.AccessContentsInformation, 'isAccountable')
    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
      """
      return 1

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getTotalPrice')
    def getTotalPrice(self, fast=0, src__=0, base_contribution=None, rounding=False, **kw):
      """ Returns the total price for this order

        if the `fast` argument is set to a true value, then it use
        SQLCatalog to compute the price, otherwise it sums the total
        price of objects one by one.

        So if the order is not in the catalog, getTotalPrice(fast=1)
        will return 0, this is not a bug.

        base_contribution must be a relative url of a category. If passed, then
        fast parameter is ignored.
      """
      if 'portal_type' not in kw:
        kw['portal_type'] = self.getPortalObject() \
          .getPortalDeliveryMovementTypeList()
      if base_contribution is None:
        if fast:
          # XXX fast ignores base_contribution for now, but it should be possible
          # to use a related key
          kw['section_uid'] = self.getDestinationSectionUid()
          kw['stock.explanation_uid'] = self.getUid()
          return self.getPortalObject()\
            .portal_simulation.getInventoryAssetPrice(**kw)

        result = sum([ line.getTotalPrice(fast=0) for line in
                       self.objectValues(**kw) ])
      else:
        # Find amounts from movements in the delivery.
        if isinstance(base_contribution, (tuple, list)):
          base_contribution_list = base_contribution
        else:
          base_contribution_list = (base_contribution,)
        base_contribution_value_list = []
        portal_categories = self.portal_categories
        for relative_url in base_contribution_list:
          base_contribution_value = portal_categories.getCategoryValue(relative_url)
          if base_contribution_value is not None:
            base_contribution_value_list.append(base_contribution_value)
        if not base_contribution_value_list:
          # We cannot find any amount so that the result is 0.
          result = 0
        else:
          matched_movement_list = [
              movement
              for movement in self.getMovementList()
              if set(movement.getBaseContributionValueList()).intersection(base_contribution_value_list)]
          if rounding:
            portal_roundings = self.portal_roundings
            matched_movement_list = [
                portal_roundings.getRoundingProxy(movement)
                for movement in matched_movement_list]
          result = sum([movement.getTotalPrice()
                        for movement in matched_movement_list])

      method = self._getTypeBasedMethod('convertTotalPrice')
      if method is not None:
        return method(result)
      return result

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getTotalNetPrice')
    def getTotalNetPrice(self, fast=0, src__=0, **kw):
      """
        Same as getTotalPrice, but including Tax and Discount (from legacy
        simulation).

        This method is deprecated because it uses deprecated Tax & Discount
        portal types. You should use getTotalPrice(base_contribution=) instead.
      """
      total_price = self.getTotalPrice(fast=fast, src__=src__, **kw)
      kw['portal_type'] = self.getPortalObject().getPortalTaxMovementTypeList()
      return total_price + self.getTotalPrice(fast=fast, src__=src__, **kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getTotalQuantity')
    def getTotalQuantity(self, fast=0, src__=0, **kw):
      """ Returns the total quantity of this order.

        if the `fast` argument is set to a true value, then it use
        SQLCatalog to compute the quantity, otherwise it sums the total
        quantity of objects one by one.

        So if the order is not in the catalog, getTotalQuantity(fast=1)
        will return 0, this is not a bug.
      """
      if 'portal_type' not in kw:
        kw['portal_type'] = self.getPortalObject() \
          .getPortalDeliveryMovementTypeList()
      if fast:
        kw['section_uid'] = self.getDestinationSectionUid()
        kw['stock.explanation_uid'] = self.getUid()
        return self.getPortalObject().portal_simulation.getInventory(**kw)
      return sum([ line.getTotalQuantity(fast=0) for line in
                      self.objectValues(**kw) ])

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDeliveryUid')
    def getDeliveryUid(self):
      return self.getUid()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDeliveryValue')
    def getDeliveryValue(self):
      """
      Deprecated, we should use getRootDeliveryValue instead
      """
      return self

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getRootDeliveryValue')
    def getRootDeliveryValue(self):
      """
      This method returns the delivery, it is usefull to retrieve the delivery
      from a line or a cell
      """
      return self

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDelivery')
    def getDelivery(self):
      return self.getRelativeUrl()

    security.declareProtected(Permissions.AccessContentsInformation,
                             '_getMovementList')
    def _getMovementList(self, portal_type=None, **kw):
      """
      Return a list of movements
      """
      movement_portal_type_set = set(
        self.getPortalObject().getPortalMovementTypeList())
      movement_list = self.objectValues(
        portal_type=movement_portal_type_set, **kw)
      if movement_list:

        if isinstance(portal_type, str):
          portal_type = portal_type,
        elif isinstance(portal_type, (list, tuple)):
          portal_type = set(portal_type)

        # Browse lines recursively and collect leafs.
        stack = [iter(movement_list)]
        movement_list = []
        while stack:
          for sub_object in stack[-1]:
            content_list = sub_object.objectValues(
              portal_type=movement_portal_type_set, **kw)
            if sub_object.hasCellContent():
              cell_list = sub_object.getCellValueList()
              if len(cell_list) != len(content_list):
                content_list = set(content_list).difference(cell_list)
                if content_list:
                  stack.append(iter(content_list))
                  break
              else:
                movement_list.extend(x for x in content_list
                  if portal_type is None or x.getPortalType() in portal_type)
            elif content_list:
              stack.append(iter(content_list))
              break
            elif portal_type is None or \
                 sub_object.getPortalType() in portal_type:
              movement_list.append(sub_object)
          else:
            del stack[-1]

      return movement_list

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getMovementList')
    def getMovementList(self, portal_type=None, **kw):
      """
       Return a list of movements.
      """
      return self._getMovementList(portal_type=portal_type, **kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getSimulatedMovementList')
    def getSimulatedMovementList(self):
      """
        Return a list of simulated movements.
        This does not contain Container Line or Container Cell.
      """
      return self.getMovementList(portal_type=
        self.getPortalObject().getPortalSimulatedMovementTypeList())

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getInvoiceMovementList')
    def getInvoiceMovementList(self):
      """
        Return a list of simulated movements.
        This does not contain Container Line or Container Cell.
      """
      return self.getMovementList(portal_type=
        self.getPortalObject().getPortalInvoiceMovementTypeList())

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainerList')
    def getContainerList(self):
      """
        Return a list of root containers.
        This does not contain sub-containers.
      """
      return self.objectValues(portal_type=
        self.getPortalObject().getPortalContainerTypeList())

    #######################################################
    # Causality computation
    security.declareProtected(Permissions.AccessContentsInformation, 'isConvergent')
    def isConvergent(self,**kw):
      """
        Returns 0 if the target is not met
      """
      return bool(not self.isDivergent(**kw))

    security.declareProtected(Permissions.AccessContentsInformation, 'isSimulated')
    def isSimulated(self):
      """
        Returns 1 if all non-null movements have a delivery counterpart
        in the simulation
      """
      for m in self.getMovementList():
        if m.getQuantity() and not m.isSimulated():
          return 0
      return 1

    security.declareProtected(Permissions.AccessContentsInformation, 'isDivergent')
    def isDivergent(self, fast=0, **kw):
      """Return True if this movement diverges from the its simulation.
      """
      ## Note that fast option was removed. Now, fast=1 is ignored.

      # Check if the total quantity equals the total of each simulation movement quantity
      for simulation_movement in self._getAllRelatedSimulationMovementList():
        if simulation_movement.isDivergent():
          return True
      return False

    security.declareProtected(Permissions.AccessContentsInformation, 'getDivergenceList')
    def getDivergenceList(self, **kw):
      """
      Return a list of messages that contains the divergences
      """
      divergence_list = []
      for simulation_movement in self._getAllRelatedSimulationMovementList():
         divergence_list.extend(simulation_movement.getDivergenceList())
      return divergence_list

    security.declareProtected(Permissions.AccessContentsInformation, 'updateCausalityState')
    @UnrestrictedMethod
    def updateCausalityState(self, solve_automatically=True, **kw):
      """
      This is often called as an activity, it will check if the
      deliver is convergent, and if so it will put the delivery
      in a solved state, if not convergent in a diverged state
      """
      isTransitionPossible = \
          self.getPortalObject().portal_workflow.isTransitionPossible
      if isTransitionPossible(self, 'diverge') and \
          isTransitionPossible(self, 'converge'):
        if self.isDivergent(**kw):
          if solve_automatically and \
              isTransitionPossible(self, 'solve_automatically'):
            self.solveAutomatically()
          else:
            self.diverge()
        else:
          self.converge()

    def updateSimulation(self, calculate=False, **kw):
      if calculate:
        path = self.getPath()
        self.activate(
          after_tag='build:'+path,
          after_path_and_method_id=(path, '_localBuild'),
          ).updateCausalityState()
      if kw:
        super(Delivery, self).updateSimulation(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'splitAndDeferMovementList')
    def splitAndDeferMovementList(self, start_date=None, stop_date=None,
        movement_uid_list=[], delivery_solver=None,
        target_solver='CopyToTarget', delivery_builder=None):
      """
      this method will unlink and delete movements in movement_uid_list and
      rebuild a new Packing List with them.
      1/ change date in simulation, call TargetSolver and expand
      2/ detach simulation movements from to-be-deleted movements
      3/ delete movements
        XXX make sure that all detached movements are deleted at the same
        time, else the interaction workflow would reattach them to a delivery
        rule.
      4/ call builder
      """
      tag_list = []
      movement_list = [x for x in self.getMovementList() if x.getUid() in
          movement_uid_list]
      if not movement_list: return

      deferred_simulation_movement_list = []
      # defer simulation movements
      if start_date is not None or stop_date is not None:
        for movement in movement_list:
          start_date = start_date or movement.getStartDate()
          stop_date = stop_date or movement.getStopDate()
          for s_m in movement.getDeliveryRelatedValueList():
            if s_m.getStartDate() != start_date or \
                s_m.getStopDate() != stop_date:
              s_m.edit(start_date=start_date, stop_date=stop_date)
              deferred_simulation_movement_list.append(s_m)

      solver_tag = '%s_splitAndDefer_solver' % self.getRelativeUrl()
      expand_tag = '%s_splitAndDefer_expand' % self.getRelativeUrl()
      detach_tag = '%s_splitAndDefer_detach' % self.getRelativeUrl()
      build_tag = '%s_splitAndDefer_build' % self.getRelativeUrl()
      # call solver and expand on deferrd movements
      for movement in movement_list:
        movement.activate(tag=solver_tag).Movement_solveMovement(
            delivery_solver, target_solver)
      tag_list.append(solver_tag)
      kw = {'after_tag': tag_list[:], 'tag': expand_tag}
      for s_m in deferred_simulation_movement_list:
        s_m.expand('deferred', activate_kw=kw)
      tag_list.append(expand_tag)

      detached_movement_url_list = []
      deleted_movement_uid_list = []
      #detach simulation movements
      for movement in movement_list:
        movement_url = movement.getRelativeUrl()
        movement_uid = getattr(movement,'uid',None)
        if movement_uid: deleted_movement_uid_list.append(movement_uid)
        for s_m in movement.getDeliveryRelatedValueList():
          delivery_list = \
              [x for x in s_m.getDeliveryList() if x != movement_url]
          s_m.activate(after_tag=tag_list[:], tag=detach_tag).setDeliveryList(
              delivery_list)
          detached_movement_url_list.append(s_m.getRelativeUrl())
      tag_list.append(detach_tag)

      #delete delivery movements
      # deleteContent uses the uid as a activity tag
      self.activate(after_tag=tag_list[:]).deleteContent([movement.getId() for
          movement in movement_list])
      tag_list.extend(deleted_movement_uid_list)

      # update causality state on self, after deletion
      self.activate(after_tag=tag_list[:],
          activity='SQLQueue').updateCausalityState()

      # call builder on detached movements
      builder = getattr(self.portal_deliveries, delivery_builder)
      builder.activate(after_tag=tag_list[:], tag=build_tag).build(
          movement_relative_url_list=detached_movement_url_list)


    #######################################################
    # Defer indexing process
    def reindexObject(self, *k, **kw):
      """
        Reindex children and simulation
      """
      self.recursiveReindexObject(*k, **kw)
      # do not reexpand simulation: this is a task for DSolver / TSolver

    #######################################################
    # Stock Management
    def _getMovementResourceList(self):
      resource_set = {m.getResource()
        for m in self.objectValues(portal_type=
          self.getPortalObject().getPortalMovementTypeList())}
      resource_set.discard(None)
      return list(resource_set)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getInventory')
    def getInventory(self, **kw):
      """
      Returns inventory
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getInventory(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getCurrentInventory')
    def getCurrentInventory(self, **kw):
      """
      Returns current inventory
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getCurrentInventory(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getAvailableInventory')
    def getAvailableInventory(self, **kw):
      """
      Returns available inventory
      (current inventory - deliverable)
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getAvailableInventory(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getFutureInventory')
    def getFutureInventory(self, **kw):
      """
      Returns inventory at infinite
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getFutureInventory(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getInventoryList')
    def getInventoryList(self, **kw):
      """
      Returns list of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getInventoryList(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getCurrentInventoryList')
    def getCurrentInventoryList(self, **kw):
      """
      Returns list of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getCurrentInventoryList(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getFutureInventoryList')
    def getFutureInventoryList(self, **kw):
      """
      Returns list of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getFutureInventoryList(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getInventoryStat')
    def getInventoryStat(self, **kw):
      """
      Returns statistics of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getInventoryStat(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getCurrentInventoryStat')
    def getCurrentInventoryStat(self, **kw):
      """
      Returns statistics of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getCurrentInventoryStat(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getFutureInventoryStat')
    def getFutureInventoryStat(self, **kw):
      """
      Returns statistics of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getFutureInventoryStat(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getInventoryChart')
    def getInventoryChart(self, **kw):
      """
      Returns list of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getInventoryChart(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getCurrentInventoryChart')
    def getCurrentInventoryChart(self, **kw):
      """
      Returns list of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getCurrentInventoryChart(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getFutureInventoryChart')
    def getFutureInventoryChart(self, **kw):
      """
      Returns list of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getFutureInventoryChart(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getInventoryHistoryList')
    def getInventoryHistoryList(self, **kw):
      """
      Returns list of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getInventoryHistoryList(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getInventoryHistoryChart')
    def getInventoryHistoryChart(self, **kw):
      """
      Returns list of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getInventoryHistoryChart(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getMovementHistoryList')
    def getMovementHistoryList(self, **kw):
      """
      Returns list of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getMovementHistoryList(**kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getMovementHistoryStat')
    def getMovementHistoryStat(self, **kw):
      """
      Returns list of inventory grouped by section or site
      """
      kw['resource'] = self._getMovementResourceList()
      return self.portal_simulation.getMovementHistoryStat(**kw)





# JPS: We must still decide if getInventoryAssetPrice is part of the Delivery API

#     security.declareProtected(Permissions.AccessContentsInformation, 'getInventoryAssetPrice')
#     def getInventoryAssetPrice(self, **kw):
#       """
#         Returns asset at infinite
#       """
#       kw['category'] = self._getMovementResourceList()
#       return self.portal_simulation.getInventoryAssetPrice(**kw)
#
#     security.declareProtected(Permissions.AccessContentsInformation, 'getFutureInventoryAssetPrice')
#     def getFutureInventoryAssetPrice(self, **kw):
#       """
#         Returns asset at infinite
#       """
#       kw['category'] = self._getMovementResourceList()
#       return self.portal_simulation.getFutureInventoryAssetPrice(**kw)
#
#     security.declareProtected(Permissions.AccessContentsInformation, 'getCurrentInventoryAssetPrice')
#     def getCurrentInventoryAssetPrice(self, **kw):
#       """
#         Returns asset at infinite
#       """
#       kw['category'] = self._getMovementResourceList()
#       return self.portal_simulation.getCurrentInventoryAssetPrice(**kw)
#
#     security.declareProtected(Permissions.AccessContentsInformation, 'getAvailableInventoryAssetPrice')
#     def getAvailableInventoryAssetPrice(self, **kw):
#       """
#         Returns asset at infinite
#       """
#       kw['category'] = self._getMovementResourceList()
#       return self.portal_simulation.getAvailableInventoryAssetPrice(**kw)

    ##########################################################################
    # Applied Rule stuff

    security.declareProtected(Permissions.AccessContentsInformation,
                              'localBuild')
    def localBuild(self, activity_kw=()):
      """Activate builders for this delivery

      The generated activity will find all buildable business links for this
      delivery, and call related builders, which will select all simulation
      movements part of the same explanation(s) as the delivery.

      XXX: Consider moving it to SimulableMixin if it's useful for
           Subscription Items.
      """
      # XXX: Previous implementation waited for expand activities of related
      #      documents and even suggested to look at explanation tree,
      #      instead of causalities. Is it required ?
      kw = {'priority': 3}
      kw.update(activity_kw)
      after_tag = kw.pop('after_tag', None)
      if isinstance(after_tag, basestring):
        after_tag = [after_tag]
      else:
        after_tag = list(after_tag) if after_tag else []
      after_tag.append('build:' + self.getPath())
      sm = getSecurityManager()
      newSecurityManager(None, nobody)
      try:
        unrestricted_apply(self.activate(after_tag=after_tag, **kw)._localBuild)
      finally:
        setSecurityManager(sm)

    def _localBuild(self):
      """Do an immediate local build for this delivery"""
      return self.asComposedDocument().build(explanation=self)

    def _createRootAppliedRule(self):
      # Only create RAR if we are not in a "too early" or "too late" state.
      state = self.getSimulationState()
      if (state != 'deleted' and
          state not in self.getPortalObject().getPortalDraftOrderStateList()):
        return super(Delivery, self)._createRootAppliedRule()

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getRootCausalityValueList')
    def getRootCausalityValueList(self):
      """
        Returns the initial causality value for this movement.
        This method will look at the causality and check if the
        causality has already a causality
      """
      seen_set = set()
      def recursive(self):
        if self in seen_set:
          return []

        seen_set.add(self)
        causality_value_list = self.getCausalityValueList()
        if causality_value_list:
          initial_list = []
          for causality in causality_value_list:
            # The causality may be something which has not this method
            # (e.g. item)
            if getattr(causality, 'getRootCausalityValueList', None) is None:
                continue

            assert causality != self
            initial_list += [x for x in recursive(causality)
                             if x not in initial_list]
          return initial_list
        return [self]

      return recursive(self)

    # XXX Temp hack, should be removed has soon as the structure of
    # the order/delivery builder will be reviewed. It might
    # be reviewed if we plan to configure movement groups in the zmi
    security.declareProtected( Permissions.ModifyPortalContent,
                               'setRootCausalityValueList')
    def setRootCausalityValueList(self,value):
      """
      This is a hack
      """
      pass

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getParentExplanationValue')
    def getParentExplanationValue(self):
      """
        This method should be removed as soon as movement groups
        will be rewritten. It is a temp hack
      """
      return self

    # XXX Temp hack, should be removed has soon as the structure of
    # the order/delivery builder will be reviewed. It might
    # be reviewed if we plan to configure movement groups in the zmi
    security.declareProtected( Permissions.ModifyPortalContent,
                               'setParentExplanationValue')
    def setParentExplanationValue(self,value):
      """
      This is a hack
      """
      pass

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getBuilderList')
    def getBuilderList(self):
      """Returns appropriate builder list."""
      return self._getTypeBasedMethod('getBuilderList')()
      # XXX - quite a hack, since no way to know...
      #       propper implementation should use business path definition
      #       however, the real question is "is this really necessary"
      #       since the main purpose of this method is superceded
      #       by IDivergenceController

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getRootSpecialiseValue')
    def getRootSpecialiseValue(self, portal_type_list):
      """Returns first specialise value matching portal type"""
      def findSpecialiseValue(context):
        if context.getPortalType() in portal_type_list:
          return context
        if getattr(context, 'getSpecialiseValueList', None) is not None:
          for specialise in context.getSpecialiseValueList():
            specialise_value = findSpecialiseValue(specialise)
            if specialise_value is not None:
              return specialise_value
        return None
      return findSpecialiseValue(self)

    security.declareProtected( Permissions.ModifyPortalContent,
                               'disconnectSimulationMovementList')
    def disconnectSimulationMovementList(self, movement_list=None):
      """Disconnects simulation movements from delivery's lines

      If movement_list is passed only those movements will be disconnected
      from simulation.

      If movements in movement_list do not belong to current
      delivery they are silently ignored.

      Returns list of disconnected Simulation Movements.

      Known issues and open questions:
       * how to protect disconnection from completed delivery?
       * what to do if movements from movement_list do not belong to delivery?
       * it is required to remove causality relation from delivery or delivery
         lines??
      """
      delivery_movement_value_list = self.getMovementList()
      if movement_list is not None:
        movement_value_list = [self.restrictedTraverse(movement) for movement
            in movement_list]
        # only those how are in this delivery
        movement_value_list = [movement_value for movement_value in
            movement_value_list if movement_value
            in delivery_movement_value_list]
      else:
        movement_value_list = delivery_movement_value_list

      disconnected_simulation_movement_list = []
      for movement_value in movement_value_list:
        # Note: Relies on fact that is invoked, when simulation movements are
        # indexed properly
        for simulation_movement in movement_value \
            .getDeliveryRelatedValueList(portal_type='Simulation Movement'):
          simulation_movement.edit(
            delivery = None,
            delivery_ratio = None
          )
          disconnected_simulation_movement_list.append(
              simulation_movement.getRelativeUrl())

      return disconnected_simulation_movement_list

    def _getAllRelatedSimulationMovementList(self):
      result = []
      for movement in self.getMovementList():
        result += movement.getDeliveryRelatedValueList()
      return result

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getDivergentTesterAndSimulationMovementList')
    def getDivergentTesterAndSimulationMovementList(self):
      """
      This method returns a list of (tester, simulation_movement) for each divergence.
      """
      divergent_tester_list = []
      for simulation_movement in self._getAllRelatedSimulationMovementList():
        simulation_movement = simulation_movement.getObject()
        rule = simulation_movement.getParentValue().getSpecialiseValue()
        for tester in rule._getDivergenceTesterList(exclude_quantity=False):
          if tester.explain(simulation_movement) not in (None, []):
            divergent_tester_list.append((tester, simulation_movement))
      return divergent_tester_list

    # XXX: Dirty but required for erp5_banking_core
    getBaobabSourceUid = lambda x: x.getSourceUid()
    getBaobabSourceUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationUid = lambda x: x.getDestinationUid()
    getBaobabDestinationUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceSectionUid = lambda x: x.getSourceSectionUid()
    getBaobabSourceSectionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationSectionUid = lambda x: x.getDestinationSectionUid()
    getBaobabDestinationSectionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourcePaymentUid = lambda x: x.getSourcePaymentUid()
    getBaobabSourcePaymentUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationPaymentUid = lambda x: x.getDestinationPaymentUid()
    getBaobabDestinationPaymentUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceFunctionUid = lambda x: x.getSourceFunctionUid()
    getBaobabSourceFunctionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationFunctionUid = lambda x: x.getDestinationFunctionUid()
    getBaobabDestinationFunctionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceProjectUid = lambda x: x.getSourceProjectUid()
    getBaobabSourceProjectUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationProjectUid = lambda x: x.getDestinationProjectUid()
    getBaobabDestinationProjectUid__roles__ = PermissionRole(Permissions.View)
示例#16
0
    security.declareProtected(Permissions.View, 'setPosted')

    def setPosted(self, value):
        """
    Custom method that's automatically sets the reference
    of the account transfer
    """
        if self.getPortalType() == "Account Transfer":
            self.setReference("posted")
        return self._setPosted(value)


### Dynamic patch
Delivery.getBaobabSourceUid = lambda x: x.getSourceUid()
Delivery.getBaobabSourceUid__roles__ = PermissionRole(Permissions.View)

Delivery.getBaobabDestinationUid = lambda x: x.getDestinationUid()
Delivery.getBaobabDestinationUid__roles__ = PermissionRole(Permissions.View)

Delivery.getBaobabSourceSectionUid = lambda x: x.getSourceSectionUid()
Delivery.getBaobabSourceSectionUid__roles__ = PermissionRole(Permissions.View)

Delivery.getBaobabDestinationSectionUid = lambda x: x.getDestinationSectionUid(
)
Delivery.getBaobabDestinationSectionUid__roles__ = PermissionRole(
    Permissions.View)

Delivery.getBaobabSourcePaymentUid = lambda x: x.getSourcePaymentUid()
Delivery.getBaobabSourcePaymentUid__roles__ = PermissionRole(Permissions.View)
示例#17
0
    action is the relative URL of the add form or wizard

    permission is the name of the permission required to instantiate
    this class

    icon is the name of an image file to use as the document's icon
    """
    return


if 0:
    meta_type = meta_type or getattr(instance_class, 'meta_type', '')

    if constructors:
        pr = PermissionRole(permission)
        for c in constructors:
            name = c.__name__
            setattr(PortalFolder, name, c)
            setattr(PortalFolder, '%s__roles__' % name, pr)

    PortalFolder.content_meta_types = PortalFolder.content_meta_types + (
        {
            'name': meta_type,
            'action': action,
            'permission': permission
        }, )

    if icon:
        path = 'misc_/CMF/%s' % urllib.quote(meta_type)
        instance_class.icon = path
示例#18
0
class Container(Movement, XMLObject):
    """
      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...

      A Container which does not point to an Item can act itself as an Item
      for traceability.

      Container Line / Container Cell is used to store quantities (never
      accounted)
      Container Line / Countainer Cell may point to Item
    """

    meta_type = 'ERP5 Container'
    portal_type = 'Container'

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

    # 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)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getQuantity')

    def getQuantity(self, default=1.0):
        """
        Returns 1 because only one container is shipped
      """
        return 1.0

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isAccountable')

    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

    security.declareProtected(Permissions.ModifyPortalContent,
                              'hasCellContent')

    def hasCellContent(self, base_id='movement'):
        """
          This method can be overriden
      """
        return 0

    security.declareProtected(Permissions.AccessContentsInformation,
                              'isDivergent')

    def isDivergent(self):
        """Return True if this movement diverges from the its simulation.
      Containers are never divergent.
      """
        return False

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainerText')

    def getContainerText(self):
        """
        Creates a unique string which allows to compare/hash two containers
      """
        result = ""
        container_line_list = list(self.objectValues())
        container_line_list.sort(key=lambda x: x.getResource())
        for container_line in container_line_list:
            if container_line.hasCellContent():
                container_cell_list = list(container_line.objectValues())
                container_cell_list.sort(key=lambda x: x.getVariationText())
                for container_cell in container_cell_list:
                    result += "%s %s %s\n" % (
                        container_cell.getResource(),
                        container_cell.getQuantity(), '|'.join(
                            container_cell.getVariationText().split('\n')))
            else:
                result += "%s %s\n" % (container_line.getResource(),
                                       container_line.getQuantity())
        container_list = list(self.objectValues(spec=self.meta_type))
        container_list.sort(key=lambda x: x.getContainerText())
        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

    # Used for optimization - requires reindexing using container_uid
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainerUid')

    def getContainerUid(self):
        return self.getUid()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainerValue')

    def getContainerValue(self):
        return self

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainer')

    def getContainer(self):
        return self.getRelativeUrl()

    # Quantity methods
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainedTotalQuantity')

    def getContainedTotalQuantity(self, recursive=0):
        """
        The sum of quantities of contained lines
      """
        result = 0.0
        for o in self.contentValues(
                filter={'portal_type': self.getPortalContainerLineTypeList()}):
            result += o.getTotalQuantity()
        if recursive:
            for o in self.contentValues(
                    filter={'portal_type': self.getPortalContainerTypeList()}):
                result += o.getContainedTotalQuantity()
        return result

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getContainedTotalPrice')

    def getContainedTotalPrice(self, recursive=0):
        """
        The sum of price of contained lines
      """
        result = 0.0
        for o in self.contentValues(
                filter={'portal_type': self.getPortalContainerLineTypeList()}):
            result += o.getTotalPrice()
        if recursive:
            for o in self.contentValues(
                    filter={'portal_type': self.getPortalContainerTypeList()}):
                result += o.getContainedTotalPrice()
        return result

    # Item Access
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getTrackedItemUidList')

    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(), )

    # XXX: Dirty but required for erp5_banking_core
    getBaobabSourceUid = lambda x: x.getSourceUid()
    getBaobabSourceUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationUid = lambda x: x.getDestinationUid()
    getBaobabDestinationUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceSectionUid = lambda x: x.getSourceSectionUid()
    getBaobabSourceSectionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationSectionUid = lambda x: x.getDestinationSectionUid()
    getBaobabDestinationSectionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourcePaymentUid = lambda x: x.getSourcePaymentUid()
    getBaobabSourcePaymentUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationPaymentUid = lambda x: x.getDestinationPaymentUid()
    getBaobabDestinationPaymentUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceFunctionUid = lambda x: x.getSourceFunctionUid()
    getBaobabSourceFunctionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationFunctionUid = lambda x: x.getDestinationFunctionUid()
    getBaobabDestinationFunctionUid__roles__ = PermissionRole(Permissions.View)

    getBaobabSourceProjectUid = lambda x: x.getSourceProjectUid()
    getBaobabSourceProjectUid__roles__ = PermissionRole(Permissions.View)

    getBaobabDestinationProjectUid = lambda x: x.getDestinationProjectUid()
    getBaobabDestinationProjectUid__roles__ = PermissionRole(Permissions.View)