############################################################################## # # Copyright (c) 2002-2005 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> # Romain Courteaud <romain@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## from AccessControl import ClassSecurityInfo from Acquisition import aq_base, aq_parent, aq_inner, aq_acquire from Products.CMFCore.utils import getToolByName from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface from Products.ERP5.Document.Rule import Rule #from Products.ERP5Type.Base import TempBase from zLOG import LOG class InvoicingRule(Rule): """ Invoicing Rule expand simulation created by a order or delivery rule. """ # CMF Type Definition meta_type = 'ERP5 Invoicing Rule' portal_type = 'Invoicing Rule' add_permission = Permissions.AddPortalContent isPortalContent = 1 isRADContent = 1 # Declarative security security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) __implements__ = ( Interface.Predicate, Interface.Rule ) # Default Properties property_sheets = ( PropertySheet.Base , PropertySheet.XMLObject , PropertySheet.CategoryCore , PropertySheet.DublinCore ) security.declareProtected(Permissions.AccessContentsInformation, 'isAccountable') def isAccountable(self, movement): """ Tells wether generated movement needs to be accounted or not. Invoice movement are never accountable, so simulation movement for invoice movements should not be accountable either. """ return 0 def _test(self, movement): """ Tests if the rule (still) applies """ parent = movement.getParentValue() result = 0 if (parent.getPortalType() == 'Applied Rule') and \ (parent.getSpecialiseId() in ('default_order_rule', 'default_delivery_rule' )): result = 1 return result #### Helper method for expand def _generatePrevisionList(self, applied_rule, **kw): """ Generate a list of movements, that should be children of this rule, based on its context (parent movement, delivery, configuration ...) These previsions are acrually returned as dictionaries. """ # XXX Isn't it better to share the code with expand method context_movement = applied_rule.getParentValue() # Do not invoice within the same entity or whenever entities are not all # defined. # It could be OK to invoice within different entities of the same # company if we wish to get some internal analytical accounting but that # requires some processing to produce a balance sheet. source_section = context_movement.getSourceSection() destination_section = context_movement.getDestinationSection() if source_section == destination_section or source_section is None \ or destination_section is None: return [] invoice_line = {} invoice_line.update( price=context_movement.getPrice(), quantity=context_movement.getCorrectedQuantity(), quantity_unit=context_movement.getQuantityUnit(), efficiency=context_movement.getEfficiency(), resource=context_movement.getResource(), variation_category_list=context_movement.getVariationCategoryList(), variation_property_dict=context_movement.getVariationPropertyDict(), start_date=context_movement.getStartDate(), stop_date=context_movement.getStopDate(), source=context_movement.getSource(), source_section=source_section, destination=context_movement.getDestination(), destination_section=destination_section, # We do need to collect invoice lines to build invoices deliverable=1 ) return [invoice_line] security.declareProtected(Permissions.ModifyPortalContent, 'expand') def expand(self, applied_rule, force=0, **kw): """ Expands the rule: - generate a list of previsions - compare the prevision with existing children - get the list of existing movements (immutable, mutable, deletable) - compute the difference between prevision and existing (add, modify, remove) - add/modify/remove child movements to match prevision """ parent_movement = applied_rule.getParentValue() if parent_movement is not None: if not parent_movement.isFrozen(): add_list, modify_dict, \ delete_list = self._getCompensatedMovementList(applied_rule, **kw) for movement_id in delete_list: applied_rule._delObject(movement_id) for movement, prop_dict in modify_dict.items(): #XXX ignore start_date and stop_date if the difference is smaller than a # rule defined value for prop in ('start_date', 'stop_date'): if prop in prop_dict.keys(): prop_dict.pop(prop) applied_rule[movement].edit(**prop_dict) for movement_dict in add_list: if 'id' in movement_dict.keys(): mvmt_id = applied_rule._get_id(movement_dict.pop('id')) new_mvmt = applied_rule.newContent(id=mvmt_id, portal_type=self.movement_type) else: new_mvmt = applied_rule.newContent(portal_type=self.movement_type) new_mvmt.edit(**movement_dict) # Pass to base class Rule.expand(self, applied_rule, force=force, **kw) def isDeliverable(self, movement): return movement.getResource() is not None