Commit 626f45ac authored by Jérome Perrin's avatar Jérome Perrin

enable constraints for accounting transaction validation.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@18381 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 173c22a0
##############################################################################
#
# 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
...@@ -63,8 +63,40 @@ class AccountingTransaction(Delivery): ...@@ -63,8 +63,40 @@ class AccountingTransaction(Delivery):
, PropertySheet.Amount , PropertySheet.Amount
, PropertySheet.Reference , PropertySheet.Reference
, PropertySheet.PaymentCondition , 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 # Compatibility
# It may be necessary to create an alias after removing the Transaction class # It may be necessary to create an alias after removing the Transaction class
# Products.ERP5Type.Document.Transaction = AccountingTransaction # Products.ERP5Type.Document.Transaction = AccountingTransaction
...@@ -57,6 +57,7 @@ class AccountingTransactionLine(DeliveryLine): ...@@ -57,6 +57,7 @@ class AccountingTransactionLine(DeliveryLine):
, PropertySheet.Task , PropertySheet.Task
, PropertySheet.Arrow , PropertySheet.Arrow
, PropertySheet.Price , PropertySheet.Price
, PropertySheet.AccountingTransactionLine
) )
# Declarative interfaces # Declarative interfaces
......
...@@ -106,6 +106,7 @@ class BalanceTransaction(AccountingTransaction, Inventory): ...@@ -106,6 +106,7 @@ class BalanceTransaction(AccountingTransaction, Inventory):
, PropertySheet.Amount , PropertySheet.Amount
, PropertySheet.Reference , PropertySheet.Reference
, PropertySheet.PaymentCondition , PropertySheet.PaymentCondition
, PropertySheet.AccountingTransaction
) )
......
##############################################################################
#
# 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',
},
)
##############################################################################
#
# 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'
},
)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment