[ADD] AccountingNone (singleton) to differentiate balances among which the debit and the credit are zero and balances among which debit and credit nullify
parent
cafbdda1c0
commit
8e4e046aee
|
@ -0,0 +1,136 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Provides the AccountingNone singleton
|
||||||
|
|
||||||
|
AccountingNone is a null value that dissolves in basic arithmetic operations,
|
||||||
|
as illustrated in the examples below
|
||||||
|
|
||||||
|
>>> 1 + 1
|
||||||
|
2
|
||||||
|
>>> 1 + AccountingNone
|
||||||
|
1
|
||||||
|
>>> AccountingNone + 1
|
||||||
|
1
|
||||||
|
>>> AccountingNone + None
|
||||||
|
AccountingNone
|
||||||
|
>>> +AccountingNone
|
||||||
|
AccountingNone
|
||||||
|
>>> -AccountingNone
|
||||||
|
AccountingNone
|
||||||
|
>>> -(AccountingNone)
|
||||||
|
AccountingNone
|
||||||
|
>>> AccountingNone - 1
|
||||||
|
-1
|
||||||
|
>>> 1 - AccountingNone
|
||||||
|
1
|
||||||
|
>>> AccountingNone - None
|
||||||
|
AccountingNone
|
||||||
|
>>> AccountingNone / 2
|
||||||
|
0.0
|
||||||
|
>>> 2 / AccountingNone
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ZeroDivisionError
|
||||||
|
>>> AccountingNone / AccountingNone
|
||||||
|
AccountingNone
|
||||||
|
>>> AccountingNone // 2
|
||||||
|
0.0
|
||||||
|
>>> 2 // AccountingNone
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ZeroDivisionError
|
||||||
|
>>> AccountingNone // AccountingNone
|
||||||
|
AccountingNone
|
||||||
|
>>> AccountingNone * 2
|
||||||
|
0.0
|
||||||
|
>>> 2 * AccountingNone
|
||||||
|
0.0
|
||||||
|
>>> AccountingNone * AccountingNone
|
||||||
|
AccountingNone
|
||||||
|
>>> AccountingNone * None
|
||||||
|
AccountingNone
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class AccountingNoneType(object):
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
if other is None:
|
||||||
|
return AccountingNone
|
||||||
|
return other
|
||||||
|
|
||||||
|
__radd__ = __add__
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
if other is None:
|
||||||
|
return AccountingNone
|
||||||
|
return -other
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
if other is None:
|
||||||
|
return AccountingNone
|
||||||
|
return other
|
||||||
|
|
||||||
|
def __iadd__(self, other):
|
||||||
|
if other is None:
|
||||||
|
return AccountingNone
|
||||||
|
return other
|
||||||
|
|
||||||
|
def __isub__(self, other):
|
||||||
|
if other is None:
|
||||||
|
return AccountingNone
|
||||||
|
return -other
|
||||||
|
|
||||||
|
def __pos__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __floordiv__(self, other):
|
||||||
|
"""
|
||||||
|
Overload of the // operator
|
||||||
|
"""
|
||||||
|
if other is AccountingNone:
|
||||||
|
return AccountingNone
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
def __rfloordiv__(self, other):
|
||||||
|
raise ZeroDivisionError
|
||||||
|
|
||||||
|
def __truediv__(self, other):
|
||||||
|
"""
|
||||||
|
Overload of the / operator
|
||||||
|
"""
|
||||||
|
if other is AccountingNone:
|
||||||
|
return AccountingNone
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
def __rtruediv__(self, other):
|
||||||
|
raise ZeroDivisionError
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
if other is None or other is AccountingNone:
|
||||||
|
return AccountingNone
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
def __rmul__(self, other):
|
||||||
|
if other is None or other is AccountingNone:
|
||||||
|
return AccountingNone
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'AccountingNone'
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
AccountingNone = AccountingNoneType()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import doctest
|
||||||
|
doctest.testmod()
|
|
@ -8,6 +8,8 @@ from collections import defaultdict
|
||||||
from openerp.exceptions import Warning as UserError
|
from openerp.exceptions import Warning as UserError
|
||||||
from openerp.models import expression
|
from openerp.models import expression
|
||||||
from openerp.tools.safe_eval import safe_eval
|
from openerp.tools.safe_eval import safe_eval
|
||||||
|
from openerp.tools.translate import _
|
||||||
|
from .accounting_none import AccountingNone
|
||||||
|
|
||||||
MODE_VARIATION = 'p'
|
MODE_VARIATION = 'p'
|
||||||
MODE_INITIAL = 'i'
|
MODE_INITIAL = 'i'
|
||||||
|
@ -228,12 +230,13 @@ class AccountingExpressionProcessor(object):
|
||||||
field, mode, account_codes, domain = self._parse_match_object(mo)
|
field, mode, account_codes, domain = self._parse_match_object(mo)
|
||||||
key = (domain, mode)
|
key = (domain, mode)
|
||||||
account_ids_data = self._data[key]
|
account_ids_data = self._data[key]
|
||||||
v = 0.0
|
v = AccountingNone
|
||||||
for account_code in account_codes:
|
for account_code in account_codes:
|
||||||
account_ids = self._account_ids_by_code[account_code]
|
account_ids = self._account_ids_by_code[account_code]
|
||||||
for account_id in account_ids:
|
for account_id in account_ids:
|
||||||
debit, credit = \
|
debit, credit = \
|
||||||
account_ids_data.get(account_id, (0.0, 0.0))
|
account_ids_data.get(account_id,
|
||||||
|
(AccountingNone, AccountingNone))
|
||||||
if field == 'bal':
|
if field == 'bal':
|
||||||
v += debit - credit
|
v += debit - credit
|
||||||
elif field == 'deb':
|
elif field == 'deb':
|
||||||
|
|
|
@ -16,6 +16,7 @@ from openerp.tools.safe_eval import safe_eval
|
||||||
|
|
||||||
from .aep import AccountingExpressionProcessor as AEP
|
from .aep import AccountingExpressionProcessor as AEP
|
||||||
from .aggregate import _sum, _avg, _min, _max
|
from .aggregate import _sum, _avg, _min, _max
|
||||||
|
from .accounting_none import AccountingNone
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -142,8 +143,8 @@ class MisReportKpi(models.Model):
|
||||||
def render(self, lang_id, value):
|
def render(self, lang_id, value):
|
||||||
""" render a KPI value as a unicode string, ready for display """
|
""" render a KPI value as a unicode string, ready for display """
|
||||||
assert len(self) == 1
|
assert len(self) == 1
|
||||||
if value is None:
|
if value is None or value is AccountingNone:
|
||||||
return '#N/A'
|
return ''
|
||||||
elif self.type == 'num':
|
elif self.type == 'num':
|
||||||
return self._render_num(lang_id, value, self.divider,
|
return self._render_num(lang_id, value, self.divider,
|
||||||
self.dp, self.prefix, self.suffix)
|
self.dp, self.prefix, self.suffix)
|
||||||
|
@ -469,6 +470,7 @@ class MisReportInstancePeriod(models.Model):
|
||||||
'max': _max,
|
'max': _max,
|
||||||
'len': len,
|
'len': len,
|
||||||
'avg': _avg,
|
'avg': _avg,
|
||||||
|
'AccountingNone': AccountingNone,
|
||||||
}
|
}
|
||||||
|
|
||||||
localdict.update(self._fetch_queries())
|
localdict.update(self._fetch_queries())
|
||||||
|
@ -515,7 +517,7 @@ class MisReportInstancePeriod(models.Model):
|
||||||
AEP.has_account_var(kpi.expression))
|
AEP.has_account_var(kpi.expression))
|
||||||
|
|
||||||
res[kpi.name] = {
|
res[kpi.name] = {
|
||||||
'val': kpi_val,
|
'val': None if kpi_val is AccountingNone else kpi_val,
|
||||||
'val_r': kpi_val_rendered,
|
'val_r': kpi_val_rendered,
|
||||||
'val_c': kpi_val_comment,
|
'val_c': kpi_val_comment,
|
||||||
'style': kpi_style,
|
'style': kpi_style,
|
||||||
|
|
Loading…
Reference in New Issue