# -*- coding: utf-8 -*- ############################################################################## # # Copyright (c) 2002-2009 Nexedi SA and Contributors. All Rights Reserved. # Jean-Paul Smets-Solanes <jp@nexedi.com> # Ćukasz Nowak <luke@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 Products.ERP5Type import Permissions, PropertySheet from Products.ERP5.Document.BPMRule import BPMRule from Products.ERP5.Document.PredicateMatrix import PredicateMatrix class BPMInvoiceTransactionRule(BPMRule, PredicateMatrix): """ DISCLAIMER: Refer to BPMRule docstring disclaimer. This is BPM enabled Invoice Transaction Rule. """ # CMF Type Definition meta_type = 'ERP5 BPM Invoice Transaction Rule' portal_type = 'BPM Invoice Transaction Rule' add_permission = Permissions.AddPortalContent isPortalContent = 1 isRADContent = 1 # Declarative security security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) # Default Properties property_sheets = ( PropertySheet.Base , PropertySheet.XMLObject , PropertySheet.CategoryCore , PropertySheet.DublinCore , PropertySheet.Task , PropertySheet.AppliedRule ) #### 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 actually returned as dictionaries. """ prevision_list = [] context_movement = applied_rule.getParentValue() business_process = applied_rule.getBusinessProcessValue() movement_and_path_list = [] for business_path in business_process.getPathValueList( self.getProperty('trade_phase_list'), context_movement): movement_and_path_list.append((context_movement, business_path)) if len(movement_and_path_list) > 1: raise NotImplementedError # Find a matching cell cell = self._getMatchingCell(context_movement) if cell is not None : # else, we do nothing for accounting_rule_cell_line in cell.objectValues() : # get the resource (in that order): # * resource from the invoice (using deliveryValue) # * price_currency from the invoice # * price_currency from the parents simulation movement's # deliveryValue # * price_currency from the top level simulation movement's # orderValue resource = None invoice_line = context_movement.getDeliveryValue() if invoice_line is not None : invoice = invoice_line.getExplanationValue() resource = invoice.getProperty('resource', invoice.getProperty('price_currency', None)) if resource is None : # search the resource on parents simulation movement's deliveries simulation_movement = applied_rule.getParentValue() portal_simulation = self.getPortalObject().portal_simulation while resource is None and \ simulation_movement != portal_simulation : delivery = simulation_movement.getDeliveryValue() if delivery is not None: resource = delivery.getProperty('price_currency', None) if (resource is None) and \ (simulation_movement.getParentValue().getParentValue() \ == portal_simulation) : # we are on the first simulation movement, we'll try # to get the resource from it's order price currency. order = simulation_movement.getOrderValue() if order is not None: resource = order.getProperty('price_currency', None) simulation_movement = simulation_movement\ .getParentValue().getParentValue() if resource is None : # last resort : get the resource from the rule resource = accounting_rule_cell_line.getResource() \ or cell.getResource() # XXX Harcoded list prevision_line = { 'source': accounting_rule_cell_line.getSource(), 'source_section': context_movement.getSourceSection(), 'source_decision': context_movement.getSourceDecision(), 'source_administration': context_movement \ .getSourceAdministration(), 'source_project': context_movement.getSourceProject(), 'source_function': context_movement.getSourceFunction(), 'source_payment': context_movement.getSourcePayment(), 'destination': accounting_rule_cell_line.getDestination(), 'destination_section': context_movement.getDestinationSection(), 'destination_decision': context_movement.getDestinationDecision(), 'destination_administration': context_movement \ .getDestinationAdministration(), 'destination_project': context_movement.getDestinationProject(), 'destination_function': context_movement.getDestinationFunction(), 'destination_payment': context_movement.getDestinationPayment(), 'start_date': context_movement.getStartDate(), 'stop_date': context_movement.getStopDate(), 'resource': resource, 'quantity': (context_movement.getCorrectedQuantity() * context_movement.getPrice(0.0)) * accounting_rule_cell_line.getQuantity(), 'price': 1, 'causality_value': business_path, } from Products.ERP5Type.Document import newTempSimulationMovement temporary_movement = newTempSimulationMovement(self.getPortalObjec(), '1', **prevision_line) #set asset_price on movement when resource is different from price #currency of the source/destination section if resource is not None: currency = self.restrictedTraverse(resource) currency_url = currency.getRelativeUrl() destination_section = prevision_line['destination_section'] if destination_section is not None: destination_currency_url = self.restrictedTraverse( destination_section).getProperty('price_currency', None) else: destination_currency_url = None if destination_currency_url is not None \ and currency_url != destination_currency_url: precision = destination_section.getPriceCurrencyValue() \ .getQuantityPrecision() destination_exchange_ratio = currency.getPrice( context=temporary_movement.asContext( categories=['price_currency/%s' % destination_currency_url, 'resource/%s' % currency_url], start_date=temporary_movement.getStartDate())) if destination_exchange_ratio is not None: prevision_line.update(destination_total_asset_price=round( (destination_exchange_ratio* applied_rule.getParentValue().getTotalPrice()),precision)) source_section = prevision_line['source_section'] if source_section is not None: source_currency_url = self.restrictedTraverse( 'source_section').getProperty('price_currency', None) else: source_currency_url = None if source_currency_url is not None \ and currency_url != source_currency_url: precision = source_section.getPriceCurrencyValue() \ .getQuantityPrecision() source_exchange_ratio = currency.getPrice( context=temporary_movement.asContext( categories=['price_currency/%s' % source_currency_url, 'resource/%s' % currency_url], start_date=temporary_movement.getStartDate())) if source_exchange_ratio is not None: prevision_line.update(source_total_asset_price = round( source_exchange_ratio*applied_rule.getParentValue().getTotalPrice(), precision)) if accounting_rule_cell_line.hasProperty( 'generate_prevision_script_id'): generate_prevision_script_id = \ accounting_rule_cell_line.getGeneratePrevisionScriptId() prevision_line.update(getattr(context_movement, generate_prevision_script_id)(prevision_line)) prevision_list.append(prevision_line) return prevision_list # Matrix related security.declareProtected( Permissions.ModifyPortalContent, 'newCellContent' ) def newCellContent(self, id, portal_type='Accounting Rule Cell', **kw): """ Creates a new Cell. """ self.invokeFactory(type_name=portal_type, id=id) new_cell = self.get(id) return new_cell # Deliverability / orderability def isOrderable(self, m): return 1 def isDeliverable(self, m): if m.getSimulationState() in self.getPortalDraftOrderStateList(): return 0 return 1