From 626f45acf12bdf93baa5e99ad31ed3bd69da3528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com> Date: Tue, 18 Dec 2007 09:42:19 +0000 Subject: [PATCH] enable constraints for accounting transaction validation. git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@18381 20353a03-c40f-0410-a6d1-a30d3c3de9de --- .../AccountingTransactionBalance.py | 79 +++++++++++++ .../ERP5/Document/AccountingTransaction.py | 32 ++++++ .../Document/AccountingTransactionLine.py | 1 + product/ERP5/Document/BalanceTransaction.py | 1 + .../PropertySheet/AccountingTransaction.py | 71 ++++++++++++ .../AccountingTransactionLine.py | 107 ++++++++++++++++++ 6 files changed, 291 insertions(+) create mode 100644 product/ERP5/Constraint/AccountingTransactionBalance.py create mode 100644 product/ERP5/PropertySheet/AccountingTransaction.py create mode 100644 product/ERP5/PropertySheet/AccountingTransactionLine.py diff --git a/product/ERP5/Constraint/AccountingTransactionBalance.py b/product/ERP5/Constraint/AccountingTransactionBalance.py new file mode 100644 index 0000000000..2dbe0c3dfc --- /dev/null +++ b/product/ERP5/Constraint/AccountingTransactionBalance.py @@ -0,0 +1,79 @@ +############################################################################## +# +# Copyright (c) 2007 Nexedi SA and Contributors. All Rights Reserved. +# Jerome Perrin <jerome@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 Products.ERP5Type.Constraint import Constraint +from Products.ERP5Type.Message import Message +N_ = lambda msg: msg # just to extract messages + +class AccountingTransactionBalance(Constraint): + """Check that accounting transaction total debit and total credit are equals. + """ + + _message_id_list = [ 'message_transaction_not_balanced_for_source', + 'message_transaction_not_balanced_for_destination' ] + + message_transaction_not_balanced_for_source = N_( + 'Transaction is not Balanced') + message_transaction_not_balanced_for_destination = N_( + 'Transaction is not Balanced') + + def checkConsistency(self, obj, fixit=0): + """Implement here the consistency checker + """ + error_list = [] + source_sum = 0 + destination_sum = 0 + for line in obj.getMovementList( + portal_type=obj.getPortalAccountingMovementTypeList()): + source_sum += line.getSourceInventoriatedTotalAssetPrice() or 0 + destination_sum += \ + line.getDestinationInventoriatedTotalAssetPrice() or 0 + + source_section = obj.getSourceSectionValue() + destination_section = obj.getDestinationSectionValue() + source_precision = destination_precision = 2 + + if source_section is not None and\ + source_section.getPortalType() == 'Organisation': + source_currency = source_section.getPriceCurrencyValue() + if source_currency is not None: + source_precision = source_currency.getQuantityPrecision() + if round(source_sum, source_precision) != 0: + error_list.append(self._generateError(obj, self._getMessage( + 'message_transaction_not_balanced_for_source'))) + + if destination_section is not None and\ + destination_section.getPortalType() == 'Organisation': + destination_currency = destination_section.getPriceCurrencyValue() + if destination_currency is not None: + destination_precision = destination_currency.getQuantityPrecision() + if round(destination_sum, destination_precision) != 0: + error_list.append(self._generateError(obj, self._getMessage( + 'message_transaction_not_balanced_for_destination'))) + + return error_list diff --git a/product/ERP5/Document/AccountingTransaction.py b/product/ERP5/Document/AccountingTransaction.py index c9ae251d23..6f3a2f4771 100644 --- a/product/ERP5/Document/AccountingTransaction.py +++ b/product/ERP5/Document/AccountingTransaction.py @@ -63,7 +63,39 @@ class AccountingTransaction(Delivery): , PropertySheet.Amount , PropertySheet.Reference , PropertySheet.PaymentCondition + , PropertySheet.AccountingTransaction ) + + def hasSourceSectionAccounting(self): + """Return true if we should take into account accounting for source + section. + """ + section = self.getSourceSectionValue() + if section is not None: + preference_tool = getToolByName(self, 'portal_preferences') + preferred_section_category = preference_tool.\ + getPreferredAccountingTransactionSectionCategory() + if preferred_section_category: + if section.getPortalType() == 'Person': + return 0 + return section.isMemberOf(preferred_section_category) + return 0 + + def hasDestinationSectionAccounting(self): + """Return true if we should take into account accounting for destination + section. + """ + section = self.getDestinationSectionValue() + if section is not None: + preference_tool = getToolByName(self, 'portal_preferences') + preferred_section_category = preference_tool.\ + getPreferredAccountingTransactionSectionCategory() + if preferred_section_category: + if section.getPortalType() == 'Person': + return 0 + return section.isMemberOf(preferred_section_category) + return 0 + # Compatibility # It may be necessary to create an alias after removing the Transaction class diff --git a/product/ERP5/Document/AccountingTransactionLine.py b/product/ERP5/Document/AccountingTransactionLine.py index 4dc3884d70..c38f559378 100644 --- a/product/ERP5/Document/AccountingTransactionLine.py +++ b/product/ERP5/Document/AccountingTransactionLine.py @@ -57,6 +57,7 @@ class AccountingTransactionLine(DeliveryLine): , PropertySheet.Task , PropertySheet.Arrow , PropertySheet.Price + , PropertySheet.AccountingTransactionLine ) # Declarative interfaces diff --git a/product/ERP5/Document/BalanceTransaction.py b/product/ERP5/Document/BalanceTransaction.py index 373f4993f1..1714b9b80d 100644 --- a/product/ERP5/Document/BalanceTransaction.py +++ b/product/ERP5/Document/BalanceTransaction.py @@ -106,6 +106,7 @@ class BalanceTransaction(AccountingTransaction, Inventory): , PropertySheet.Amount , PropertySheet.Reference , PropertySheet.PaymentCondition + , PropertySheet.AccountingTransaction ) diff --git a/product/ERP5/PropertySheet/AccountingTransaction.py b/product/ERP5/PropertySheet/AccountingTransaction.py new file mode 100644 index 0000000000..3abfa0961c --- /dev/null +++ b/product/ERP5/PropertySheet/AccountingTransaction.py @@ -0,0 +1,71 @@ +############################################################################## +# +# Copyright (c) 2007 Nexedi SA and Contributors. All Rights Reserved. +# Jerome Perrin <jerome@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. +# +############################################################################## + +class AccountingTransaction: + """Constraints for Accounting Transactions + """ + + _constraints = ( + { 'id': 'section_existence', + 'description': 'Both sections must be defined for invoices', + 'type': 'CategoryExistence', + 'destination_section' : 1, + 'source_section' : 1, + 'portal_type': ('Person', 'Organisation'), + 'condition' : 'python: object.getPortalType() in' + ' portal.getPortalInvoiceTypeList()', + }, + + { 'id': 'date_existence', + 'description': 'Date must be defined', + 'message_property_not_set': 'Date must be defined', + 'condition' : 'python: object.getSimulationState() not' + ' in ("cancelled", "deleted")', + 'type': 'PropertyExistence', + 'start_date' : 1, + }, + + { 'id': 'currency_existence', + 'description': 'Currency must be defined', + 'message_category_not_set': + 'Currency must be defined', + 'portal_type': ('Currency',), + 'condition' : 'python: object.getSimulationState() not' + ' in ("cancelled", "deleted")', + 'type': 'CategoryExistence', + 'resource' : 1, + }, + + { 'id': 'debit_credit_balance', + 'description': 'Total Debit must equal Total Credit', + 'condition' : 'python: object.getSimulationState() not' + ' in ("cancelled", "deleted")', + 'type': 'AccountingTransactionBalance', + }, + + ) diff --git a/product/ERP5/PropertySheet/AccountingTransactionLine.py b/product/ERP5/PropertySheet/AccountingTransactionLine.py new file mode 100644 index 0000000000..0b6673779a --- /dev/null +++ b/product/ERP5/PropertySheet/AccountingTransactionLine.py @@ -0,0 +1,107 @@ +############################################################################## +# +# Copyright (c) 2006 Nexedi SA and Contributors. All Rights Reserved. +# Jerome Perrin <jerome@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 Products.CMFCore.Expression import Expression + +class AccountingTransactionLine: + """Constraints for Accounting Transaction Lines + """ + + _constraints = ( + # We need an account if we have a quantity for this side + { 'id': 'source_existence', + 'description': 'Accounting Transaction Lines must use an account', + 'condition' : 'python: object.getSourceInventoriatedTotalAssetPrice()'\ + ' and object.hasSourceSectionAccounting()'\ + ' and not object.getDestination(portal_type="Account")', + 'type': 'CategoryExistence', + 'source' : 1, + 'portal_type': 'Account', + 'message_category_not_set': 'Account must be defined on lines', + }, + { 'id': 'destination_existence', + 'description': 'Accounting Transaction Lines must use an account', + 'condition' : + 'python: object.getDestinationInventoriatedTotalAssetPrice()'\ + ' and object.hasDestinationSectionAccounting()'\ + ' and not object.getSource(portal_type="Account")', + 'type': 'CategoryExistence', + 'destination' : 1, + 'portal_type': 'Account', + 'message_category_not_set': 'Account must be defined on lines', + }, + + # We need a mirror section for recievable / payable accounts + { 'id': 'destination_section_existence', + 'condition' : + 'python: object.getSourceValue(portal_type="Account") is not None'\ + ' and object.getSourceValue(portal_type="Account").getAccountTypeId()'\ + ' in ("receivable", "payable")', + 'type': 'CategoryAcquiredExistence', + 'destination_section' : 1, + 'portal_type': ('Person', 'Organisation'), + 'message_category_not_set': 'Third party must be defined for '\ + 'payable or receivable accounts.' + }, + { 'id': 'source_section_existence', + 'condition' : + 'python: object.getDestinationValue(portal_type="Account") is not None'\ + ' and object.getDestinationValue(portal_type="Account").getAccountTypeId()'\ + ' in ("receivable", "payable")', + 'type': 'CategoryAcquiredExistence', + 'source_section' : 1, + 'portal_type': ('Person', 'Organisation'), + 'message_category_not_set': 'Third party must be defined for '\ + 'payable or receivable accounts.' + }, + + # We need a payment for bank accounts + { 'id': 'source_payment_existence', + 'condition' : 'python: object.hasSourceSectionAccounting() and'\ + ' object.getSourceValue(portal_type="Account") is not None'\ + ' and object.getSourceValue(portal_type="Account").getAccountType()'\ + ' == "asset/cash/bank"', + 'type': 'CategoryAcquiredExistence', + 'source_payment' : 1, + 'portal_type': Expression('portal/getPortalPaymentNodeTypeList'), + 'message_category_not_set': 'Bank account must be defined for '\ + 'bank type accounts' + }, + { 'id': 'destination_payment_existence', + 'condition' : 'python: object.hasDestinationSectionAccounting()'\ + ' and object.getDestinationValue(portal_type="Account") is not None'\ + ' and object.getDestinationValue(portal_type="Account").getAccountType()'\ + ' == "asset/cash/bank"', + 'type': 'CategoryAcquiredExistence', + 'destination_payment' : 1, + 'portal_type': Expression('portal/getPortalPaymentNodeTypeList'), + 'message_category_not_set': 'Bank account must be defined for '\ + 'bank type accounts' + }, + ) + -- 2.30.9