Commit d8b193b1 authored by Guillaume Michon's avatar Guillaume Michon

Amortisation system generisation


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@2883 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent ffe2fa9f
...@@ -34,9 +34,9 @@ from Products.ERP5.Core import MetaNode, MetaResource ...@@ -34,9 +34,9 @@ from Products.ERP5.Core import MetaNode, MetaResource
from Products.CMFCore.WorkflowCore import WorkflowMethod from Products.CMFCore.WorkflowCore import WorkflowMethod
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5.Document.Amount import Amount from Products.ERP5.Document.Amount import Amount
from string import capitalize
from zLOG import LOG from zLOG import LOG
class Immobilisation(XMLObject): class Immobilisation(XMLObject):
...@@ -119,30 +119,30 @@ an accounting immobilisation (in order to amortise an object) ...@@ -119,30 +119,30 @@ an accounting immobilisation (in order to amortise an object)
} }
security.declareProtected(Permissions.View, 'getAmortisationOrDefaultAmortisationPrice')
def getAmortisationOrDefaultAmortisationPrice(self, with_currency=0, **kw): security.declareProtected(Permissions.View, 'getDefaultDurability')
def getDefaultDurability(self, **kw):
""" """
Returns the amortisation value. Returns a calculated value of the remaining durability
If it is None, returns the default amortisation value. of the item at the immobilisation movement date
""" """
amortisation_price = self.getAmortisationBeginningPrice() item = self.getParent()
if amortisation_price is not None: current_date = self.getStopDate()
return amortisation_price if current_date is None or item is None:
else: return None
return self.getDefaultAmortisationPrice(with_currency=with_currency, **kw) return item.getRemainingDurability(current_date, from_immobilisation=1, **kw)
security.declareProtected(Permissions.View, 'getAmortisationOrDefaultAmortisationDuration') security.declareProtected(Permissions.View, 'getDurabilityOrDefaultDurability')
def getAmortisationOrDefaultAmortisationDuration(self, **kw): def getDurabilityOrDefaultDurability(self, **kw):
""" """
Returns the remaining amortisation duration. Returns the remaining durability.
If it is None, returns the default remaining amortisation duration. If it is None, returns the default durability
""" """
amortisation_duration = self.getAmortisationDuration() durability = self.getDurability()
if amortisation_duration is not None: if durability is None:
return amortisation_duration durability = self.getDefaultDurability(**kw)
else: return durability
return self.getDefaultAmortisationDuration(**kw)
security.declareProtected(Permissions.View, 'getDefaultAmortisationDuration') security.declareProtected(Permissions.View, 'getDefaultAmortisationDuration')
...@@ -155,10 +155,21 @@ an accounting immobilisation (in order to amortise an object) ...@@ -155,10 +155,21 @@ an accounting immobilisation (in order to amortise an object)
current_date = self.getStopDate() current_date = self.getStopDate()
if current_date is None or item is None: if current_date is None or item is None:
return None return None
return item.getRemainingAmortisationDuration(current_date, from_immobilisation=1, **kw) return item.getRemainingAmortisationDuration(current_date, from_immobilisation=1, **kw)
security.declareProtected(Permissions.View, 'getAmortisationOrDefaultAmortisationDuration')
def getAmortisationOrDefaultAmortisationDuration(self, **kw):
"""
Returns the remaining amortisation duration.
If it is None, returns the default remaining amortisation duration.
"""
amortisation_duration = self.getAmortisationDuration()
if amortisation_duration is None:
amortisation_duration = self.getDefaultAmortisationDuration(**kw)
return amortisation_duration
security.declareProtected(Permissions.ModifyPortalContent, 'getDefaultAmortisationPrice') security.declareProtected(Permissions.ModifyPortalContent, 'getDefaultAmortisationPrice')
def getDefaultAmortisationPrice(self, with_currency=0, **kw): def getDefaultAmortisationPrice(self, with_currency=0, **kw):
""" """
...@@ -169,45 +180,86 @@ an accounting immobilisation (in order to amortise an object) ...@@ -169,45 +180,86 @@ an accounting immobilisation (in order to amortise an object)
current_date = self.getStopDate() current_date = self.getStopDate()
if current_date is None or item is None: if current_date is None or item is None:
return None return None
returned_value = item.getAmortisationPrice(current_date, from_immobilisation=1, with_currency=with_currency, **kw) returned_value = item.getAmortisationPrice(current_date, from_immobilisation=1, with_currency=with_currency, **kw)
return returned_value return returned_value
security.declareProtected(Permissions.View, 'getAmortisationOrDefaultAmortisationPrice')
def getAmortisationOrDefaultAmortisationPrice(self, with_currency=0, **kw):
"""
Returns the amortisation value.
If it is None, returns the default amortisation value.
"""
amortisation_price = self.getAmortisationStartPrice()
if amortisation_price is None:
amortisation_price = self.getDefaultAmortisationPrice(with_currency=with_currency, **kw)
return amortisation_price
security.declarePrivate('_checkConsistency') security.declarePrivate('_checkConsistency')
def _checkConsistency(self, fixit=0, mapped_value_property_list=()): def _checkConsistency(self, fixit=0, mapped_value_property_list=()):
errors = []
relative_url = self.getRelativeUrl() relative_url = self.getRelativeUrl()
def checkValue(property_dict):
"""
property_dict must have the following format :
{ "property_name" : { "values" : [list of forbidden values], "message" :
["type of error", degree, "Error message"] },
...
}
"""
errors = []
for property in property_dict.keys():
getter = getattr(self, "get" + ''.join( [capitalize(x) for x in property.split("_")] ), None)
if getter is None:
errors += [(relative_url, "Accessor inconsistency", 100, "No accessor for property %s" % property)]
else:
property_value = getter()
forbidden_value_list = property_dict[property]["values"]
if property_value in forbidden_value_list:
message = property_dict[property]["message"]
errors += [(relative_url, message[0], message[1], message[2])]
return errors
item = self.getParent()
if item is None:
errors += [(relative_url, "Property value inconsistency", 100, "The immobilisation movement does not apply on an item")]
immo_date = self.getStopDate()
if immo_date is None:
errors += [(relative_url, "Property value inconsistency", 100, 'Date property is empty')]
if self.getImmobilisation(): errors = []
immo_duration = self.getAmortisationDuration()
if immo_duration is None:
errors += [(relative_url, "Property value inconsistency", 100, 'Amortisation duration property is empty')]
immo_value = self.getAmortisationBeginningPrice() # Checks common to every amortisation method
if immo_value is None: errors.extend( checkValue( { "parent" : { "values": [None], "message":
errors += [(relative_url, "Property value inconsistency", 100, 'Amortisation price property is empty')] [ "Property value inconsistency", 100,
"The immobilisation movement does not apply on an item" ] },
"stop_date" : { "values": [None], "message":
[ "Property value inconsistency", 100,
"Date property is empty" ] },
"durability": { "values": [None], "message":
[ "Property value inconsistency", 100,
"Durability property is empty" ] },
} ) )
immo_type = self.getAmortisationType()
if immo_type is None or immo_type is "":
errors += [(relative_url, "Property value inconsistency", 100, 'Amortisation type property is empty')]
if immo_type == "degressive": if self.getImmobilisation():
fiscal_coef = self.getFiscalCoefficient() errors.extend( checkValue( { "amortisation_duration" : { "values" : [None], "message":
if fiscal_coef is None: [ "Property value inconsistency", 100,
errors += [(relative_url, "Property value inconsistency", 100, 'Fiscal coefficient property is empty')] "Amortisation duration property is empty"] },
"amortisation_start_price" : { "values" : [None], "message":
[ "Property value inconsistency", 100,
"Amortisation price property is empty"] },
"amortisation_method" : { "values" : [None, ""], "message":
[ "Property value inconsistency", 100,
"No amortisation method"] },
"vat" : { "values" : [None], "message":
[ "Property value inconsistency", 100,
"VAT Amount property is empty"] },
"section_value" : { "values" : [None], "message":
[ "Property value inconsistency", 100,
"The corresponding item does not belong to an organisation at this date"] },
"disposal_price" : { "values": [None], "message":
[ "Property value inconsistency", 100,
"Disposal price property is empty" ] },
"price_currency" : { "values" : [None], "message":
[ "Property value inconsistency", 100,
"The organisation which owns the item at this date has no amortisation currency"]
} } ) )
vat = self.getVat()
if vat is None:
errors += [(relative_url, "Property value inconsistency", 100, 'VAT Amount property is empty')]
for (account, text) in ( (self.getInputAccount() , "Input Account"), for (account, text) in ( (self.getInputAccount() , "Input Account"),
(self.getOutputAccount() , "Output Account"), (self.getOutputAccount() , "Output Account"),
...@@ -219,17 +271,23 @@ an accounting immobilisation (in order to amortise an object) ...@@ -219,17 +271,23 @@ an accounting immobilisation (in order to amortise an object)
errors += [(relative_url, "Property value inconsistency", 100, text + ' property is empty')] errors += [(relative_url, "Property value inconsistency", 100, text + ' property is empty')]
section = self.getSectionValue() section = self.getSectionValue()
if section is None: if section is not None:
errors += [(relative_url, "Property value inconsistency", 100, "The corresponding item does not belong to an organisation at this date")]
else:
financial_date = section.getFinancialYearStopDate() financial_date = section.getFinancialYearStopDate()
if financial_date is None: if financial_date is None:
errors += [(relative_url, "Property value inconsistency", 100, "The organisation which owns the item at this date has no financial year end date")] errors += [(relative_url, "Property value inconsistency", 100,
"The organisation which owns the item at this date has no financial year end date")]
currency = self.getPriceCurrency() # Checks specific to each amortisation method
if currency is None: if self.getAmortisationMethod():
errors += [(relative_url, "Property value inconsistency", 100, "The organisation which owns the item at this date has no amortisation currency")] specific_parameter_list = self.getAmortisationMethodParameter("specific_parameter_list")["specific_parameter_list"]
for parameter in specific_parameter_list:
errors.extend( checkValue( { parameter : { "values" : [None], 'message':
["Property value inconsistency", 100,
"%s property is empty" % parameter ] } } ) )
if errors:
LOG("errors :", 0, repr(errors))
return errors return errors
...@@ -278,3 +336,53 @@ an accounting immobilisation (in order to amortise an object) ...@@ -278,3 +336,53 @@ an accounting immobilisation (in order to amortise an object)
""" """
return self._checkConsistency(*args, **kw) return self._checkConsistency(*args, **kw)
security.declareProtected(Permissions.View, 'getAmortisationMethodParameter')
def getAmortisationMethodParameter(self, parameter_list):
"""
Returns a dictionary containing the value of each parameter
whose name is given in parameter_list.
The value is get from the amortisation method
"""
if type(parameter_list) == type(""):
parameter_list = [parameter_list]
parameter_dict = {}
for parameter in parameter_list:
parameter_dict[parameter] = None
amortisation_method = self.getAmortisationMethod()
parameter_object = self.restrictedTraverse("erp5_accounting_" + amortisation_method)
if parameter_object is not None:
for parameter in parameter_list:
parameter_dict[parameter] = getattr(parameter_object, parameter, None)
return parameter_dict
security.declareProtected(Permissions.View, 'isUsingAmortisationMethod')
def isUsingAmortisationMethod(self, method):
"""
Return true if this item is using the given method
"""
if self.getAmortisationMethod() == method:
return 1
return 0
security.declareProtected(Permissions.View, 'isUsingEuLinearAmortisationMethod')
def isUsingEuLinearAmortisationMethod(self):
"""
Return true if this item is using this method
"""
return self.isUsingAmortisationMethod('eu/linear')
security.declareProtected(Permissions.View, 'isUsingFrDegressiveAmortisationMethod')
def isUsingFrDegressiveAmortisationMethod(self):
"""
Return true if this item is using this method
"""
return self.isUsingAmortisationMethod('fr/degressive')
security.declareProtected(Permissions.View, 'isUsingFrActualUseAmortisationMethod')
def isUsingFrActualUseAmortisationMethod(self):
"""
Return true if this item is using this method
"""
return self.isUsingAmortisationMethod('fr/actual_use')
...@@ -31,21 +31,25 @@ from Globals import InitializeClass, PersistentMapping ...@@ -31,21 +31,25 @@ from Globals import InitializeClass, PersistentMapping
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from DateTime import DateTime from DateTime import DateTime
from string import capitalize
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type.DateUtils import addToDate, getClosestDate, getIntervalBetweenDates, roundMonthToGreaterEntireYear from Products.ERP5Type.DateUtils import addToDate, getClosestDate, getIntervalBetweenDates
from Products.ERP5Type.DateUtils import getMonthAndDaysBetween, getCompletedMonthBetween, getRoundedMonthBetween from Products.ERP5Type.DateUtils import getMonthAndDaysBetween, getRoundedMonthBetween
from Products.ERP5Type.DateUtils import getMonthFraction, getYearFraction, getDecimalNumberOfYearsBetween from Products.ERP5Type.DateUtils import getMonthFraction, getYearFraction, getBissextilCompliantYearFraction
from Products.ERP5Type.DateUtils import same_movement_interval, number_of_months_in_year, centis, millis from Products.ERP5Type.DateUtils import same_movement_interval, number_of_months_in_year, centis, millis
from Products.ERP5.Document.Amount import Amount from Products.ERP5.Document.Amount import Amount
from Products.CMFCore.WorkflowCore import WorkflowMethod from Products.CMFCore.WorkflowCore import WorkflowMethod
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
from Products.ERP5.Document.Immobilisation import Immobilisation from Products.ERP5.Document.Immobilisation import Immobilisation
#from Products.ERP5.Document.AmortisationRule import AmortisationRule
from zLOG import LOG from zLOG import LOG
NEGLIGEABLE_PRICE = 10e-8
class Item(XMLObject, Amount): class Item(XMLObject, Amount):
""" """
Items in ERP5 are intended to provide a way to track objects Items in ERP5 are intended to provide a way to track objects
...@@ -126,13 +130,44 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -126,13 +130,44 @@ Items in ERP5 are intended to provide a way to track objects."""
### Amortisation ### Amortisation
# _update_data and _get_data are used to implement a semi-cache system on
# heavy calculation methods.
def _update_data(self, cached_data, date, id, value):
if getattr(cached_data, "cached_dict", None) is None:
cached_data.cached_dict = {}
if cached_data.cached_dict.get(date, None) is None:
cached_data.cached_dict[date] = {}
cached_data.cached_dict[date][id] = value
def _get_data(self, cached_data, date, id):
if cached_data:
cached_dict = getattr(cached_data,"cached_dict", None)
if cached_dict is not None:
cached_date = cached_dict.get(date, None)
if cached_date is not None:
cached_value = cached_date.get(id, None)
if cached_value is not None:
return cached_value
return None
security.declareProtected(Permissions.View, 'getImmobilisationMovementValueList') security.declareProtected(Permissions.View, 'getImmobilisationMovementValueList')
def getImmobilisationMovementValueList(self, from_date=None, to_date=None, sort_on="stop_date", filter_valid=1, owner_change=1, **kw): def getImmobilisationMovementValueList(self, from_date=None, to_date=None,
sort_on="stop_date", filter_valid=1,
owner_change=1, single_from=0, single_to=0,
property_filter=['price', 'duration', 'durability'], **kw):
""" """
Returns a list of immobilisation movements applied to current item from date to date Returns a list of immobilisation movements applied to current item from date to date
Argument filter_valid allows to select only the valid immobilisation movements Argument filter_valid allows to select only the valid immobilisation movements
Argument owner_change allows to create temporarily some immobilisation movements Argument owner_change allows to create temporarily some immobilisation movements
when the owner of the item changes. when the owner of the item changes.
Arguments single_from and single_to (exclusive from each other) allow to dramatically
reduce the calculation time, but it returns only one movement : the nearest from
from_date or to_date.
Argument property_filter has the same goal. Its role is to reduce the number of calculated
properties when a temporary immobilisation movement is created
""" """
accessor = 'get' accessor = 'get'
if sort_on is not None: if sort_on is not None:
...@@ -163,12 +198,21 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -163,12 +198,21 @@ Items in ERP5 are intended to provide a way to track objects."""
if ( to_date is None or immo_date - to_date <= 0 ) and \ if ( to_date is None or immo_date - to_date <= 0 ) and \
( from_date is None or immo_date - from_date >= 0 ): ( from_date is None or immo_date - from_date >= 0 ):
immobilisation_list.append(immobilisation) immobilisation_list.append(immobilisation)
LOG('Item.immobilisation_list for %s' % repr(self),0,immobilisation_list)
# Look for each change of ownership and an immobilisation movement within 1 hour # Look for each change of ownership and an immobilisation movement within 1 hour
# If found, adapt the immobilisation date to be correctly interpreted # If found, adapt the immobilisation date to be correctly interpreted
# If not found, and owner_change set to 1, create a context immobilisation movement # If not found, and owner_change set to 1, create a context immobilisation movement
ownership_list = self.getSectionList(to_date) ownership_list = self.getSectionList(to_date)
if single_from or single_to:
immobilisation_list.sort(cmpfunc)
if single_from:
if len(immobilisation_list) > 0: immobilisation_list = immobilisation_list[:1]
if len(ownership_list) > 0: ownership_list = ownership_list[:1]
else:
if len(immobilisation_list) > 0: immobilisation_list = immobilisation_list[-1:]
if len(ownership_list) > 0: ownership_list = ownership_list[-1:]
for ownership in ownership_list: for ownership in ownership_list:
owner_date = ownership['date'] owner_date = ownership['date']
found_immo = None found_immo = None
...@@ -193,19 +237,21 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -193,19 +237,21 @@ Items in ERP5 are intended to provide a way to track objects."""
# immobilisation movement on the change date. # immobilisation movement on the change date.
# This has to be done only if nearest_immo is defined, since the temporary # This has to be done only if nearest_immo is defined, since the temporary
# movement gets most of its data on the previous movement, which is nearest_immo # movement gets most of its data on the previous movement, which is nearest_immo
#immobilisation = nearest_immo
#if nearest_immo is not None:
added_immo = None added_immo = None
added_immo = nearest_immo.asContext() added_immo = nearest_immo.asContext()
added_immo.setStopDate(owner_date + millis) added_immo.setStopDate(owner_date + millis)
if "durability" in property_filter:
added_immo.setDurability(added_immo.getDefaultDurability(**kw))
if added_immo.getImmobilisation(): if added_immo.getImmobilisation():
if 'price' in property_filter:
vat = nearest_immo.getVat() vat = nearest_immo.getVat()
previous_value = nearest_immo.getAmortisationOrDefaultAmortisationPrice() previous_value = nearest_immo.getAmortisationOrDefaultAmortisationPrice(**kw)
current_value = added_immo.getDefaultAmortisationPrice() current_value = added_immo.getDefaultAmortisationPrice(**kw)
added_immo.setInputAccount(added_immo.getOutputAccount()) added_immo.setAmortisationStartPrice(current_value)
added_immo.setAmortisationBeginningPrice(current_value)
added_immo.setAmortisationDuration(added_immo.getDefaultAmortisationDuration())
added_immo.setVat( vat * current_value / previous_value ) added_immo.setVat( vat * current_value / previous_value )
if 'duration' in property_filter:
added_immo.setAmortisationDuration(added_immo.getDefaultAmortisationDuration(**kw))
added_immo.setInputAccount(added_immo.getOutputAccount())
immobilisation_list.append(added_immo) immobilisation_list.append(added_immo)
found_immo = added_immo found_immo = added_immo
...@@ -226,6 +272,21 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -226,6 +272,21 @@ Items in ERP5 are intended to provide a way to track objects."""
if sort_on is not None: if sort_on is not None:
immobilisation_list.sort(cmpfunc) immobilisation_list.sort(cmpfunc)
# Check if some movements have the same date. If it is the case, since
# it is impossible to know which movement has to be before the other ones,
# change arbitrarily the date of one of them, in order to at least
# have always the same behavior
for i in range(len(immobilisation_list)):
immobilisation = immobilisation_list[i]
ref_date = immobilisation.getStopDate()
immobilisation_sublist = [immobilisation]
j = 1
while i+j < len(immobilisation_list) and immobilisation_list[i+j].getStopDate() == ref_date:
immobilisation_sublist.append(immobilisation_list[i+j])
j += 1
for j in range(len(immobilisation_sublist)):
immobilisation_sublist[j].setStopDate( ref_date + j * millis )
return immobilisation_list return immobilisation_list
...@@ -273,12 +334,26 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -273,12 +334,26 @@ Items in ERP5 are intended to provide a way to track objects."""
""" """
Returns the last immobilisation movement before the given date, or now Returns the last immobilisation movement before the given date, or now
""" """
result_sql = self.getPastImmobilisationMovementValueList(at_date = at_date, owner_change=owner_change, **kw) past_list = self.getPastImmobilisationMovementValueList(at_date = at_date,
owner_change=owner_change,
single_to = 1, **kw)
result = None if len(past_list) > 0:
if len(result_sql) > 0: return past_list[-1]
result = result_sql[-1] return None
return result
security.declareProtected(Permissions.View, 'getNextImmobilisationMovementValue')
def getNextImmobilisationMovementValue(self, at_date=None, owner_change=1, **kw):
"""
Returns the last immobilisation movement after the given date, or now
"""
future_list = self.getFutureImmobilisationMovementValueList(at_date = at_date,
owner_change = owner_change,
single_from = 1, **kw)
if len(future_list) > 0:
return future_list[0]
return None
security.declareProtected(Permissions.View, 'getLastMovementAmortisationDuration') security.declareProtected(Permissions.View, 'getLastMovementAmortisationDuration')
...@@ -287,9 +362,12 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -287,9 +362,12 @@ Items in ERP5 are intended to provide a way to track objects."""
Returns total duration of amortisation for the item. Returns total duration of amortisation for the item.
It is the theorical lifetime of this type of item. It is the theorical lifetime of this type of item.
""" """
last_immobilisation_movement = self.getLastImmobilisationMovementValue(at_date = at_date, owner_change=owner_change, **kw) last_immobilisation_movement = self.getLastImmobilisationMovementValue(at_date = at_date,
owner_change=owner_change,
property_filter = ['duration'],
**kw)
if last_immobilisation_movement is not None: if last_immobilisation_movement is not None:
return last_immobilisation_movement.getAmortisationOrDefaultAmortisationDuration() return last_immobilisation_movement.getAmortisationOrDefaultAmortisationDuration(**kw)
else: else:
return None return None
...@@ -349,36 +427,131 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -349,36 +427,131 @@ Items in ERP5 are intended to provide a way to track objects."""
my_at_date = at_date - centis my_at_date = at_date - centis
else: else:
my_at_date = at_date my_at_date = at_date
immobilisation_movements = self.getPastImmobilisationMovementValueList(at_date = my_at_date, **kw)
i = len(immobilisation_movements) - 1 cached_data = kw.get("cached_data", None)
while i >= 0 and not immobilisation_movements[i].getImmobilisation(): cached_duration = self._get_data(cached_data, my_at_date, 'duration')
i -= 1 if cached_duration is not None:
if i < 0: return cached_duration
last_immobilisation_movement = self.getLastImmobilisationMovementValue(at_date = my_at_date,
property_filter = ['duration'],
**kw)
previous_loop_movement = None
start_movement = None
stop_movement = None
current_search_date = None
while last_immobilisation_movement is not None and start_movement is None:
if last_immobilisation_movement.getImmobilisation():
start_movement = last_immobilisation_movement
stop_movement = previous_loop_movement
if not start_movement:
previous_loop_movement = last_immobilisation_movement
last_date = last_immobilisation_movement.getStopDate() - centis
last_immobilisation_movement = self.getLastImmobilisationMovementValue(at_date = last_date,
property_filter = ['duration'],
**kw)
if start_movement is None:
# Neither of past immobilisation movements did immobilise the item... # Neither of past immobilisation movements did immobilise the item...
duration = self.getLastMovementAmortisationDuration(at_date=my_at_date) duration = self.getLastMovementAmortisationDuration(at_date=my_at_date, **kw)
if duration is not None: if duration is not None:
if cached_data: self._update_data(cached_data, my_at_date, 'duration', int(duration))
return int(duration) return int(duration)
return None return None
# We found the last immobilising movement # We found the last immobilising movement
# Two cases are possible : # Two cases are possible :
# - The item is still in an amortisation period (i.e. the immobilising movement is the latest) # - The item is still in an amortisation period (i.e. the immobilising movement is the latest)
# - The item is not in an amortisation period : in this case, we have to find the date of the unimmobilising movement # - The item is not in an amortisation period : in this case, we have to find the date of the unimmobilising movement
start_movement = immobilisation_movements[i] if stop_movement is None:
if i > len(immobilisation_movements) - 2:
# Item is currently in an amortisation period # Item is currently in an amortisation period
immo_period_stop_date = at_date immo_period_stop_date = at_date
else: else:
stop_movement = immobilisation_movements[i+1]
immo_period_stop_date = stop_movement.getStopDate() immo_period_stop_date = stop_movement.getStopDate()
immo_period_start_date = start_movement.getStopDate() immo_period_start_date = start_movement.getStopDate()
immo_period_remaining = start_movement.getAmortisationOrDefaultAmortisationDuration() immo_period_remaining = start_movement.getAmortisationOrDefaultAmortisationDuration(**kw)
immo_period_duration = getRoundedMonthBetween(immo_period_start_date, immo_period_stop_date) immo_period_duration = getRoundedMonthBetween(immo_period_start_date, immo_period_stop_date)
returned_value = immo_period_remaining - immo_period_duration returned_value = immo_period_remaining - immo_period_duration
if returned_value < 0: if returned_value < 0:
returned_value = 0 returned_value = 0
if cached_data: self._update_data(cached_data, my_at_date, 'duration', returned_value)
return int(returned_value) return int(returned_value)
security.declareProtected(Permissions.View, 'getRemainingDurability')
def getRemainingDurability(self, at_date=None, from_immobilisation=0, **kw):
"""
Returns the durability of the item at the given date, or now.
The durability is quantity of something which corresponds to the 'life' of the item
(ex : km for a car, or time for anything)
Each Immobilisation Movement stores the durability at a given time, so it is possible
to approximate the durability between two Immobilisation Movements by using a simple
linear calculation.
"""
if at_date is None:
at_date = DateTime()
my_at_date = at_date
if from_immobilisation:
my_at_date -= centis
cached_data = kw.get("cached_data", None)
cached_durability = self._get_data(cached_data, my_at_date, "durability")
if cached_durability is not None:
return cached_durability
last_movement = self.getLastImmobilisationMovementValue(at_date = my_at_date,
property_filter = ['durability', 'duration'],
**kw)
if last_movement is not None:
if not last_movement.getImmobilisation():
# The item is not currently amortised
# The current durability is the durability on
# last immobilisation movement
return_value = last_movement.getDurability()
if cached_data: self._update_data(cached_data, my_at_date, 'durability', return_value)
return return_value
start_durability = last_movement.getDurability()
start_date = last_movement.getStopDate()
my_at_date = at_date
if from_immobilisation:
my_at_date += centis
next_movement = self.getNextImmobilisationMovementValue(at_date = my_at_date + millis,
property_filter = ['durability'],
**kw)
if next_movement is not None:
stop_durability = next_movement.getDurability()
stop_date = last_movement.getStopDate()
else:
# In this case, we take the end of life of the item and use
# it like an immobilisation movement with values set to 0
last_remaining_months = last_movement.getAmortisationOrDefaultAmortisationDuration(**kw)
stop_date = addToDate(start_date, month=last_remaining_months)
stop_durability = 0
consumpted_durability = start_durability - stop_durability
consumpted_time = getRoundedMonthBetween(start_date, stop_date)
current_consumpted_time = getRoundedMonthBetween(start_date, at_date)
if consumpted_time <= 0 or current_consumpted_time <= 0:
return_value = start_durability
else:
return_value = start_durability - consumpted_durability * current_consumpted_time / consumpted_time
else:
return_value = None
if cached_data: self._update_data(cached_data, my_at_date, 'durability', return_value)
return return_value
security.declareProtected(Permissions.View, 'getCurrentRemainingDurability')
def getCurrentRemainingDurability(self, **kw):
"""
Returns the remaining durability at the current date
"""
return self.getRemainingDurability(at_date = DateTime(), **kw)
security.declareProtected(Permissions.View, 'getAmortisationPrice') security.declareProtected(Permissions.View, 'getAmortisationPrice')
def getAmortisationPrice(self, at_date=None, from_immobilisation=0, with_currency=0, **kw): def getAmortisationPrice(self, at_date=None, from_immobilisation=0, with_currency=0, **kw):
""" """
...@@ -390,25 +563,6 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -390,25 +563,6 @@ Items in ERP5 are intended to provide a way to track objects."""
If with_currency is set, returns a string containing the value and the corresponding currency. If with_currency is set, returns a string containing the value and the corresponding currency.
""" """
def calculateProrataTemporis(immo_period_start_date, immo_period_stop_date, raw_annuity_value=0, amortisation_type='degressive', financial_date=None):
"""
Returns the value of the annuity respecting to the amortisation
duration during this annuity.
"""
if amortisation_type == 'degressive':
month_value = raw_annuity_value / number_of_months_in_year
duration = getMonthAndDaysBetween(immo_period_start_date, immo_period_stop_date)
month_number = duration['month']
day_number = duration['day']
annuity_value = month_value * (month_number + getMonthFraction(immo_period_stop_date, day_number))
return annuity_value
else:
# Linear amortisation : it is calculated on days,
# unlike degressive amortisation which is calculated on months
return getDecimalNumberOfYearsBetween(immo_period_start_date, immo_period_stop_date, financial_date) * raw_annuity_value
if at_date is None: if at_date is None:
at_date = DateTime() at_date = DateTime()
# Find the latest movement whose immobilisation is true # Find the latest movement whose immobilisation is true
...@@ -418,166 +572,209 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -418,166 +572,209 @@ Items in ERP5 are intended to provide a way to track objects."""
my_at_date = at_date - centis my_at_date = at_date - centis
else: else:
my_at_date = at_date my_at_date = at_date
immobilisation_movements = self.getPastImmobilisationMovementValueList(at_date = my_at_date, **kw)
length = len(immobilisation_movements)
i = length - 1
while i >= 0 and not immobilisation_movements[i].getImmobilisation():
i -= 1
if i < 0: cached_data = kw.get("cached_data", None)
cached_price = self._get_data(cached_data, my_at_date, 'price')
if cached_price is not None:
return cached_price
last_immobilisation_movement = self.getLastImmobilisationMovementValue(at_date = my_at_date,
**kw)
previous_loop_movement = None
start_movement = None
stop_movement = None
current_search_date = None
while last_immobilisation_movement is not None and start_movement is None:
if last_immobilisation_movement.getImmobilisation():
start_movement = last_immobilisation_movement
stop_movement = previous_loop_movement
if not start_movement:
previous_loop_movement = last_immobilisation_movement
last_date = last_immobilisation_movement.getStopDate() - millis
last_immobilisation_movement = self.getLastImmobilisationMovementValue(at_date = last_date,
**kw)
if start_movement is None:
# Neither of past immobilisation movements did immobilise the item... # Neither of past immobilisation movements did immobilise the item...
LOG ('ERP5 Warning :',0,'Neither of past immobilisation movements did immobilise the item %s' % self.getTitle()) LOG ('ERP5 Warning :',0,'Neither of past immobilisation movements did immobilise the item %s' % self.getTitle())
if length > 0: last_immobilisation_movement = self.getLastImmobilisationMovementValue(at_date = my_at_date,
returned_value = immobilisation_movements[-1].getAmortisationOrDefaultAmortisationPrice() **kw)
if last_immobilisation_movement:
returned_price = last_immobilisation_movement.getAmortisationOrDefaultAmortisationPrice(**kw)
if with_currency: if with_currency:
return '%s %s' % (repr(round(returned_value,2)), immobilisation_movements[-1].getPriceCurrency()) return '%s %s' % (repr(round(returned_price,2)), immobilisation_movements[-1].getPriceCurrency())
return returned_value if cached_data: self._update_data(cached_data, my_at_date, 'price', returned_price)
return returned_price
return None # XXX How to find the buy value ? return None # XXX How to find the buy value ?
# Find the latest immobilisation period and gather information # Find the latest immobilisation period and gather information
start_movement = immobilisation_movements[i]
currency = start_movement.getPriceCurrency() currency = start_movement.getPriceCurrency()
if currency is not None: start_date = start_movement.getStopDate()
currency = currency.split('/')[-1] if stop_movement is None:
immo_period_start_date = start_movement.getStopDate()
if i >= len(immobilisation_movements) - 1:
# Item is currently in an amortisation period # Item is currently in an amortisation period
immo_period_stop_date = at_date stop_date = at_date
else: else:
stop_movement = immobilisation_movements[i+1] # Item is not in an amortisation period
immo_period_stop_date = stop_movement.getStopDate() stop_date = stop_movement.getStopDate()
start_value = start_movement.getAmortisationOrDefaultAmortisationPrice()
immo_period_remaining_months = start_movement.getAmortisationOrDefaultAmortisationDuration()
start_price = start_movement.getAmortisationOrDefaultAmortisationPrice(**kw)
disposal_price = start_movement.getDisposalPrice()
depreciable_price = start_price - disposal_price
start_remaining_months = start_movement.getAmortisationOrDefaultAmortisationDuration(**kw)
stop_remaining_months = 0
start_durability = start_movement.getDurability(**kw)
stop_durability = 0
section = start_movement.getSectionValue() section = start_movement.getSectionValue()
financial_date = section.getFinancialYearStopDate() financial_date = section.getFinancialYearStopDate()
amortisation_method = "erp5_accounting_" + start_movement.getAmortisationMethod()
next_date = stop_date
# Calculate the amortisation value if stop_movement is not None:
amortisation_type = start_movement.getAmortisationType() stop_remaining_months = stop_movement.getDefaultAmortisationDuration(**kw)
if amortisation_type == "linear": stop_durability = stop_movement.getDurabilityOrDefaultDurability(**kw)
# Linear amortisation prorata temporis calculation is made on a number of days
# unlike degressive amortisation, made on a number of months
raw_annuity_value = start_value / (immo_period_remaining_months / number_of_months_in_year)
annuity_value = calculateProrataTemporis(raw_annuity_value=raw_annuity_value,
amortisation_type='linear',
immo_period_stop_date=immo_period_stop_date,
immo_period_start_date=immo_period_start_date,
financial_date=financial_date)
new_value = start_value - annuity_value
if new_value < 0:
new_value = 0
if with_currency:
return '%s %s' % (repr(round(new_value,2)), currency)
return new_value
elif amortisation_type == "degressive":
if financial_date is None:
LOG('ERP5 Warning :', 100, 'Organisation object "%s" has no financial date.' % (repr(section.getTitle()),))
return None
# Degressive amortisation is made on entire annuities, unless the first.
# So, saying we immobilise on 114 months as degressive amortisation is meaningless :
# in fact, we immobilise on 120 months.
# So we need to round the remaining period to the just greater entire year
# Normally, since amortisation is made as soon as the item acquisition for degressive
# amortisation, the immobilisation can not be stopped and restarted.
# However, if we immobilised the item during an incomplete year before, we also round the
# remaining period of immobilisation
immo_period_remaining_months = roundMonthToGreaterEntireYear(immo_period_remaining_months)
# Degressive amortisation is taken in account on months, and not on days.
# So we need to adjust the immobilisation period start and stop date so that
# they are at the beginning of a month (a month of financial year - i.e. if
# the financial year date end is March 15th, immobilisation start date is fixed
# to previous 15th, and immobilisation stop date is fixed to next 15th
immo_period_start_date = getClosestDate(target_date=immo_period_start_date, date=financial_date, precision='month')
immo_period_stop_date = getClosestDate(target_date=immo_period_stop_date, date=financial_date, precision='month', before=0)
# Get the first financial end date before the beginning of the immobilisation period
# and the last financial date after the end of the immobilisation period.
first_financial_date = getClosestDate(target_date=immo_period_start_date, date=financial_date, precision='year')
last_financial_date = getClosestDate(target_date=immo_period_stop_date, date=financial_date, precision='year', before=0)
is_last_amortisation_period = 0
# Adjust the immobilisation period stop date and last financial date
# if the current period exceeds the regular immobilisation period
month_difference = getIntervalBetweenDates(first_financial_date, last_financial_date, {'month':1} )['month']
if month_difference >= immo_period_remaining_months:
last_financial_date = addToDate(last_financial_date, {'month':immo_period_remaining_months} )
is_last_amortisation_period = 1
immo_period_stop_date = last_financial_date
#entire_annuities_duration = (last_financial_date.year() - first_financial_date.year()) * 365.25
# Find the degressive coefficient
fiscal_coef = start_movement.getFiscalCoefficient()
normal_amortisation_coefficient = 1./ getYearFraction(first_financial_date, months=immo_period_remaining_months)
degressive_coef = normal_amortisation_coefficient * fiscal_coef
annuities = 0 # Cumulated annuities value
if getIntervalBetweenDates(first_financial_date, last_financial_date, {'day':1})['day'] > 0:
# First annuity is particular since we use prorata temporis ratio
second_financial_date = addToDate(first_financial_date, {'year':1})
if getIntervalBetweenDates(immo_period_stop_date, second_financial_date, {'days':1}) < 0:
annuity_end_date = immo_period_stop_date
else: else:
annuity_end_date = second_financial_date next_movement = self.getNextImmobilisationMovementValue(at_date = my_at_date + millis,
if normal_amortisation_coefficient <= round(degressive_coef, 2): property_filter = ['durability'],
applied_coef = degressive_coef **kw)
if next_movement is not None:
stop_durability = next_movement.getDurabilityOrDefaultDurability(**kw)
stop_remaining_months = next_movement.getDefaultAmortisationDuration(**kw)
next_date = next_movement.getStopDate()
else: else:
applied_coef = normal_amortisation_coefficient next_date = addToDate(start_date, month = start_remaining_months)
raw_annuity_value = start_value * applied_coef
# Get the amortisation method parameters
annuity_value = calculateProrataTemporis(immo_period_start_date, annuity_end_date, raw_annuity_value=raw_annuity_value) amortisation_parameters = start_movement.getAmortisationMethodParameter(parameter_list = [
annuities += annuity_value "cut_annuities", "price_calculation_basis", "prorata_precision",
linear_coef = 0 "round_duration", "specific_parameter_list", "date_precision"])
current_financial_date = second_financial_date cut_annuities = amortisation_parameters["cut_annuities"]
price_calculation_basis = amortisation_parameters["price_calculation_basis"]
prorata_precision = amortisation_parameters["prorata_precision"]
# Other annuities round_duration = amortisation_parameters["round_duration"]
while current_financial_date < last_financial_date: date_precision = amortisation_parameters["date_precision"]
remaining_months = immo_period_remaining_months - getIntervalBetweenDates(first_financial_date, specific_parameter_list = amortisation_parameters["specific_parameter_list"]
current_financial_date, {'month':1})['month']
if not linear_coef: # Adjust some values according to the parameters
# Linear coef has not been set yet, so we have to check start_date = getClosestDate(date=financial_date, target_date=start_date,
# if it is time to use it or not precision=date_precision, before=1, strict=0)
current_value = start_value - annuities stop_date = getClosestDate(date=financial_date, target_date=stop_date,
linear_coef = 1./ getYearFraction(last_financial_date, months=remaining_months) precision=date_precision, before=0, strict=0)
if linear_coef <= round(degressive_coef, 2):
applied_coef = degressive_coef if prorata_precision == 'day':
linear_coef = 0 local_stop_date = addToDate(start_date, month = start_remaining_months)
start_remaining_annuities = getBissextilCompliantYearFraction(from_date = start_date,
to_date = local_stop_date,
reference_date = financial_date)
local_stop_date = addToDate(next_date, month = stop_remaining_months)
stop_remaining_annuities = getBissextilCompliantYearFraction(from_date = next_date,
to_date = local_stop_date,
reference_date = financial_date)
else: else:
applied_coef = linear_coef start_remaining_annuities = getYearFraction(months = start_remaining_months)
stop_remaining_annuities = getYearFraction(months = stop_remaining_months)
if round_duration == "greater annuity":
if start_remaining_annuities != int(start_remaining_annuities):
start_remaining_annuities = int(start_remaining_annuities) + 1
else: else:
applied_coef = linear_coef start_remaining_annuities = int(start_remaining_annuities)
elif round_duration == "lower annuity":
raw_annuity_value = current_value * applied_coef start_remaining_annuities = int(start_remaining_annuities)
if (not is_last_amortisation_period) and \
getIntervalBetweenDates(current_financial_date, last_financial_date, {'year':1} )['year'] == 1: # Get specific parameters
# It is the last annuity of the period. If we enter in this statement, it means specific_parameter_dict = {}
# the amortisation stops, but the item is not fully amortised for specific_parameter in specific_parameter_list:
annuity_value = calculateProrataTemporis(current_financial_date,immo_period_stop_date,raw_annuity_value=raw_annuity_value) getter = getattr(start_movement,
'get' + ''.join( [capitalize(x) for x in specific_parameter.split("_")] ),
None
)
if getter is not None:
specific_parameter_dict[specific_parameter] = getter()
def calculatePrice(at_date):
# First we calculate which is the current annuity
annuity_number = 0
if cut_annuities:
current_date = getClosestDate(date = financial_date,
target_date = start_date,
precision = "year",
before = 0)
if getIntervalBetweenDates(current_date, start_date, keys={'day':1})['day'] == 0:
current_date = addToDate(current_date, year=+1)
else: else:
annuity_value = raw_annuity_value current_date = addToDate(start_date, year=1)
while current_date - at_date < 0:
annuity_number += 1
current_date = addToDate(current_date, year=1)
annuity_start_date = addToDate(current_date, year=-1)
annuity_stop_date = current_date
current_annuity_stop_date = annuity_stop_date
current_annuity_start_date = annuity_start_date
if stop_date < annuity_stop_date:
current_annuity_stop_date = stop_date
if start_date > annuity_start_date:
current_annuity_start_date = start_date
# Get the current ratio
current_ratio = self.restrictedTraverse(amortisation_method).ratioCalculation(
start_remaining_annuities = start_remaining_annuities
,stop_remaining_annuities = stop_remaining_annuities
,current_annuity = annuity_number
,start_remaining_durability = start_durability
,stop_remaining_durability = stop_durability
,**specific_parameter_dict)
if current_ratio is None:
LOG("ERP5 Warning :",0,"Unable to calculate the ratio during the amortisation calculation on item %s at date %s" % (
repr(self), repr(at_date)))
return None
annuities += annuity_value # Calculate the value at the beginning of the annuity
current_financial_date = addToDate(current_financial_date, {'year':1} ) annuity_start_price = depreciable_price
if annuity_number:
annuity_start_price = calculatePrice(annuity_start_date)
if annuity_start_price is None:
return None
# Return the calculated value # Calculate the raw annuity value
returned_value = start_value - annuities if price_calculation_basis == "start price":
if returned_value < 0: raw_annuity_price = depreciable_price * current_ratio
returned_value = 0. elif price_calculation_basis == "annuity start price":
if with_currency: raw_annuity_price = annuity_start_price * current_ratio
return '%s %s' % (repr(round(returned_value, 2)), currency)
return returned_value
# Apply the prorata temporis on the raw annuity value
if start_date <= annuity_start_date and stop_date >= annuity_stop_date:
annuity_value = raw_annuity_price
else: else:
# Unknown amortisation type if prorata_precision == 'month':
LOG('ERP5 Warning :', 0, 'Unknown amortisation type. (%s)' % (repr(amortisation_type),)) month_value = raw_annuity_price / number_of_months_in_year
duration = getMonthAndDaysBetween(current_annuity_start_date, current_annuity_stop_date)
month_number = duration['month']
day_number = duration['day']
annuity_value = month_value * (month_number + getMonthFraction(current_annuity_stop_date, day_number))
elif prorata_precision == 'day':
annuity_value = raw_annuity_price * getBissextilCompliantYearFraction(current_annuity_start_date,
current_annuity_stop_date,
reference_date=financial_date)
# Deduct the price at the given date
returned_price = annuity_start_price - annuity_value
if returned_price < 0:
returned_price = 0
return returned_price
### End of calculatePrice()
calculated_price = calculatePrice(at_date)
if calculated_price is None:
return None return None
if calculated_price < NEGLIGEABLE_PRICE:
calculated_price = 0.
returned_price = calculated_price + disposal_price
if cached_data: self._update_data(cached_data, my_at_date, 'price', returned_price)
if with_currency:
return '%0.2f %s' % (returned_price, currency)
return returned_price
security.declareProtected(Permissions.View, 'getCurrentAmortisationPrice') security.declareProtected(Permissions.View, 'getCurrentAmortisationPrice')
def getCurrentAmortisationPrice(self, with_currency=0, **kw): def getCurrentAmortisationPrice(self, with_currency=0, **kw):
...@@ -607,7 +804,13 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -607,7 +804,13 @@ Items in ERP5 are intended to provide a way to track objects."""
security.declareProtected(Permissions.ModifyPortalContent, '_createAmortisationRule') security.declareProtected(Permissions.ModifyPortalContent, '_createAmortisationRule')
def _createAmortisationRule(self): def _createAmortisationRule(self):
my_applied_rule_list = self.getCausalityRelatedValueList(portal_type='Applied Rule') applied_rule_list = self.getCausalityRelatedValueList(portal_type='Applied Rule')
my_applied_rule_list = []
for applied_rule in applied_rule_list:
specialise_value = applied_rule.getSpecialiseValue()
if specialise_value is not None and specialise_value.getPortalType() == "Amortisation Rule":
my_applied_rule_list.append(applied_rule)
if len(my_applied_rule_list) == 0: if len(my_applied_rule_list) == 0:
# Create a new applied order rule (portal_rules.order_rule) # Create a new applied order rule (portal_rules.order_rule)
portal_rules = getToolByName(self, 'portal_rules') portal_rules = getToolByName(self, 'portal_rules')
...@@ -720,3 +923,35 @@ Items in ERP5 are intended to provide a way to track objects.""" ...@@ -720,3 +923,35 @@ Items in ERP5 are intended to provide a way to track objects."""
Return the current owner of the item Return the current owner of the item
""" """
return self.getSectionValue( at_date = DateTime() ) return self.getSectionValue( at_date = DateTime() )
security.declareProtected(Permissions.View, 'isUsingAmortisationMethod')
def isUsingAmortisationMethod(self, method):
"""
Return true if this item is using the given method
"""
if self.getAmortisationMethod() == method:
return 1
return 0
security.declareProtected(Permissions.View, 'isUsingEuLinearAmortisationMethod')
def isUsingEuLinearAmortisationMethod(self):
"""
Return true if this item is using this method
"""
return self.isUsingAmortisationMethod('eu/linear')
security.declareProtected(Permissions.View, 'isUsingFrDegressiveAmortisationMethod')
def isUsingFrDegressiveAmortisationMethod(self):
"""
Return true if this item is using this method
"""
return self.isUsingAmortisationMethod('fr/degressive')
security.declareProtected(Permissions.View, 'isUsingFrActualUseAmortisationMethod')
def isUsingFrActualUseAmortisationMethod(self):
"""
Return true if this item is using this method
"""
return self.isUsingAmortisationMethod('fr/actual_use')
...@@ -42,14 +42,35 @@ class Amortisation: ...@@ -42,14 +42,35 @@ class Amortisation:
""" """
_properties = ( _properties = (
{ 'id' : 'amortisation_beginning_price', # Common properties
{ 'id' : 'amortisation_start_price',
'description' : 'The value to use to calculate the accounting amortisation movements (net of tax)', 'description' : 'The value to use to calculate the accounting amortisation movements (net of tax)',
'type' : 'float', 'type' : 'float',
'acquisition_base_category' : ('parent',), 'acquisition_base_category' : ('parent',),
'acquisition_portal_type' : Expression('python: portal.getPortalItemTypeList()'), 'acquisition_portal_type' : Expression('python: portal.getPortalItemTypeList()'),
'acquisition_copy_value' : 1, 'acquisition_copy_value' : 1,
'acquisition_mask_value' : 1, 'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getAmortisationBeginningPrice', 'acquisition_accessor_id' : 'getAmortisationStartPrice',
'acquisition_depends' : None,
'mode' : 'w' },
{ 'id' : 'disposal_price',
'description' : 'The estimated price at the end of the lifetime (net of tax)',
'type' : 'float',
'acquisition_base_category' : ('parent',),
'acquisition_portal_type' : Expression('python: portal.getPortalItemTypeList()'),
'acquisition_copy_value' : 1,
'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getDisposalPrice',
'acquisition_depends' : None,
'mode' : 'w' },
{ 'id' : 'durability',
'description' : 'The remaining durability of the item',
'type' : 'float',
'acquisition_base_category' : ('parent',),
'acquisition_portal_type' : Expression('python: portal.getPortalItemTypeList()'),
'acquisition_copy_value' : 1,
'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getDurability',
'acquisition_depends' : None, 'acquisition_depends' : None,
'mode' : 'w' }, 'mode' : 'w' },
{ 'id' : 'amortisation_duration', { 'id' : 'amortisation_duration',
...@@ -72,27 +93,38 @@ class Amortisation: ...@@ -72,27 +93,38 @@ class Amortisation:
'acquisition_accessor_id' : 'getImmobilisation', 'acquisition_accessor_id' : 'getImmobilisation',
'acquisition_depends' : None, 'acquisition_depends' : None,
'mode' : 'w' }, 'mode' : 'w' },
{ 'id' : 'fiscal_coefficient', { 'id' : 'vat', # XXX Naming problem according to JPS
'description' : 'The fiscal coefficient to use in degressive amortisation', 'description' : 'The VAT at the beginning of the immobilisation period',
'type' : 'float', 'type' : 'float',
'acquisition_base_category' : ('parent',), 'acquisition_base_category' : ('parent',),
'acquisition_portal_type' : Expression('python: portal.getPortalItemTypeList()'), 'acquisition_portal_type' : Expression('python: portal.getPortalItemTypeList()'),
'acquisition_copy_value' : 1, 'acquisition_copy_value' : 1,
'acquisition_mask_value' : 1, 'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getFiscalCoefficient', 'acquisition_accessor_id' : 'getVat',
'acquisition_depends' : None, 'acquisition_depends' : None,
'mode' : 'w' }, 'mode' : 'w' },
{ 'id' : 'vat', # XXX Naming problem according to JPS { 'id' : 'amortisation_method',
'description' : 'The VAT at the beginning of the immobilisation period', 'description' : 'The amortisation method used for this particular immobilisation period',
'type' : 'float', 'type' : 'string',
'acquisition_base_category' : ('parent',), 'acquisition_base_category' : ('parent',),
'acquisition_portal_type' : Expression('python: portal.getPortalItemTypeList()'), 'acquisition_portal_type' : Expression('python: portal.getPortalItemTypeList()'),
'acquisition_copy_value' : 1, 'acquisition_copy_value' : 1,
'acquisition_mask_value' : 1, 'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getVat', 'acquisition_accessor_id' : 'getAmortisationMethod',
'acquisition_depends' : None, 'acquisition_depends' : None,
'mode' : 'w' }, 'mode' : 'w' },
# Properties specific to each amortisation method
{ 'id' : 'degressive_coefficient',
'description' : 'The fiscal coefficient to use in degressive amortisation',
'type' : 'float',
'acquisition_base_category' : ('parent',),
'acquisition_portal_type' : Expression('python: portal.getPortalItemTypeList()'),
'acquisition_copy_value' : 1,
'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getDegressiveCoefficient',
'acquisition_depends' : None,
'mode' : 'w' },
) )
_categories = ('input_account', 'output_account', 'immobilisation_account', _categories = ('input_account', 'output_account', 'immobilisation_account',
......
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