Skip to content

Commit

Permalink
generalize auto expand
Browse files Browse the repository at this point in the history
Auto Expand V2
  • Loading branch information
TeoGoddet committed Feb 8, 2022
1 parent 3f2d1d9 commit 08d07d0
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 79 deletions.
2 changes: 2 additions & 0 deletions mis_builder/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@
from . import aep
from . import mis_kpi_data
from . import prorata_read_group_mixin
from . import account_account
from . import account_analytic_account
10 changes: 10 additions & 0 deletions mis_builder/models/account_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright 2017 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.osv import expression

class AccountAccount(models.Model):
_name = 'account.account'
_inherit = ['account.account']
10 changes: 10 additions & 0 deletions mis_builder/models/account_analytic_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright 2017 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.osv import expression

class AccountAnalyticAccount(models.Model):
_name = 'account.analytic.account'
_inherit = ['account.analytic.account']
166 changes: 120 additions & 46 deletions mis_builder/models/aep.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from odoo.tools.safe_eval import datetime, dateutil, safe_eval, time

from .accounting_none import AccountingNone

import pprint
try:
import itertools.izip as zip
except ImportError:
Expand Down Expand Up @@ -306,6 +306,8 @@ def do_queries(
date_to,
additional_move_line_filter=None,
aml_model=None,
auto_expand_col_name = None,
rdi_transformer = lambda i: (i[0], str(i[1])) if i else ('other', _('Other'))
):
"""Query sums of debit and credit for all accounts and domains
used in expressions.
Expand All @@ -323,7 +325,7 @@ def do_queries(
domain_by_mode = {}
ends = []
for key in self._map_account_ids:
domain, mode = key
(domain, mode) = key
if mode == self.MODE_END and self.smart_end:
# postpone computation of ending balance
ends.append((domain, mode))
Expand All @@ -336,13 +338,21 @@ def do_queries(
domain.append(("account_id", "in", self._map_account_ids[key]))
if additional_move_line_filter:
domain.extend(additional_move_line_filter)

get_fields = ["debit", "credit", "account_id", "company_id"]
group_by_fields = ["account_id", "company_id"]
if auto_expand_col_name:
get_fields = [ auto_expand_col_name ] + get_fields
group_by_fields = [ auto_expand_col_name ] + group_by_fields

# fetch sum of debit/credit, grouped by account_id
accs = aml_model.read_group(
domain,
["debit", "credit", "account_id", "company_id"],
["account_id", "company_id"],
get_fields,
group_by_fields,
lazy=False,
)

for acc in accs:
rate, dp = company_rates[acc["company_id"][0]]
debit = acc["debit"] or 0.0
Expand All @@ -352,19 +362,33 @@ def do_queries(
):
# in initial mode, ignore accounts with 0 balance
continue
self._data[key][acc["account_id"][0]] = (debit * rate, credit * rate)
rdi_id = rdi_transformer(acc[auto_expand_col_name])
if not self._data[key].get(rdi_id, False):
self._data[key][rdi_id] = defaultdict(dict)
self._data[key][rdi_id][acc["account_id"][0]] = (debit * rate, credit * rate)
# compute ending balances by summing initial and variation
for key in ends:
domain, mode = key
initial_data = self._data[(domain, self.MODE_INITIAL)]
variation_data = self._data[(domain, self.MODE_VARIATION)]
account_ids = set(initial_data.keys()) | set(variation_data.keys())
for account_id in account_ids:
di, ci = initial_data.get(account_id, (AccountingNone, AccountingNone))
dv, cv = variation_data.get(
account_id, (AccountingNone, AccountingNone)
)
self._data[key][account_id] = (di + dv, ci + cv)
rdis = set(initial_data.keys()) | set(variation_data.keys())
for rdi in rdis:
if not initial_data.get(rdi, False):
initial_data[rdi] = defaultdict(dict)
if not variation_data.get(rdi, False):
variation_data[rdi] = defaultdict(dict)
if not self._data[key].get(rdi, False):
self._data[key][rdi] = defaultdict(dict)
pprint.pprint(rdis)
pprint.pprint(rdi)
pprint.pprint(initial_data)
pprint.pprint(variation_data)

account_ids = set(initial_data[rdi].keys()) | set(variation_data[rdi].keys())
for account_id in account_ids:
di, ci = initial_data[rdi].get(account_id, (AccountingNone, AccountingNone))
dv, cv = variation_data[rdi].get(account_id, (AccountingNone, AccountingNone))
self._data[key][rdi][account_id] = (di + dv, ci + cv)

def replace_expr(self, expr):
"""Replace accounting variables in an expression by their amount.
Expand All @@ -377,23 +401,25 @@ def replace_expr(self, expr):
def f(mo):
field, mode, acc_domain, ml_domain = self._parse_match_object(mo)
key = (ml_domain, mode)
account_ids_data = self._data[key]
rdi_ids_data = self._data[key]
v = AccountingNone
account_ids = self._account_ids_by_acc_domain[acc_domain]
for account_id in account_ids:
debit, credit = account_ids_data.get(
account_id, (AccountingNone, AccountingNone)
)
if field == "bal":
v += debit - credit
elif field == "pbal" and debit >= credit:
v += debit - credit
elif field == "nbal" and debit < credit:
v += debit - credit
elif field == "deb":
v += debit
elif field == "crd":
v += credit
for rdi in rdi_ids_data:
account_ids_data = self._data[key][rdi]
for account_id in account_ids:
debit, credit = account_ids_data.get(
account_id, (AccountingNone, AccountingNone)
)
if field == "bal":
v += debit - credit
elif field == "pbal" and debit >= credit:
v += debit - credit
elif field == "nbal" and debit < credit:
v += debit - credit
elif field == "deb":
v += debit
elif field == "crd":
v += credit
# in initial balance mode, assume 0 is None
# as it does not make sense to distinguish 0 from "no data"
if (
Expand Down Expand Up @@ -424,25 +450,21 @@ def f(mo):
return "(AccountingNone)"
# here we know account_id is involved in acc_domain
account_ids_data = self._data[key]
debit, credit = account_ids_data.get(
account_id, (AccountingNone, AccountingNone)
)
if field == "bal":
v = debit - credit
elif field == "pbal":
if debit >= credit:
v = debit - credit
else:
v = AccountingNone
elif field == "nbal":
if debit < credit:
v = debit - credit
else:
v = AccountingNone
elif field == "deb":
v = debit
elif field == "crd":
v = credit
for rdi in rdi_ids_data:
account_ids_data = self._data[key][rdi]
debit, credit = account_ids_data.get(
account_id, (AccountingNone, AccountingNone)
)
if field == "bal":
v += debit - credit
elif field == "pbal" and debit >= credit:
v += debit - credit
elif field == "nbal" and debit < credit:
v += debit - credit
elif field == "deb":
v += debit
elif field == "crd":
v += credit
# in initial balance mode, assume 0 is None
# as it does not make sense to distinguish 0 from "no data"
if (
Expand All @@ -466,6 +488,58 @@ def f(mo):
for account_id in account_ids:
yield account_id, [self._ACC_RE.sub(f, expr) for expr in exprs]

def replace_exprs_by_row_detail(self, exprs):
"""Replace accounting variables in a list of expression
by their amount, iterating by accounts involved in the expression.
yields account_id, replaced_expr
This method must be executed after do_queries().
"""

def f(mo):
field, mode, acc_domain, ml_domain = self._parse_match_object(mo)
key = (ml_domain, mode)
v = AccountingNone
account_ids_data = self._data[key][rdi_id]
account_ids = self._account_ids_by_acc_domain[acc_domain]

for account_id in account_ids:
debit, credit = account_ids_data.get(
account_id, (AccountingNone, AccountingNone)
)
if field == "bal":
v += debit - credit
elif field == "pbal" and debit >= credit:
v += debit - credit
elif field == "nbal" and debit < credit:
v += debit - credit
elif field == "deb":
v += debit
elif field == "crd":
v += credit
# in initial balance mode, assume 0 is None
# as it does not make sense to distinguish 0 from "no data"
if (
v is not AccountingNone
and mode in (self.MODE_INITIAL, self.MODE_UNALLOCATED)
and float_is_zero(v, precision_digits=self.dp)
):
v = AccountingNone
return "(" + repr(v) + ")"

rdi_ids = set()
for expr in exprs:
for mo in self._ACC_RE.finditer(expr):
field, mode, acc_domain, ml_domain = self._parse_match_object(mo)
key = (ml_domain, mode)
rdis_data = self._data[key]
for rdi_id in rdis_data.keys():
rdi_ids.add(rdi_id)

for rdi_id in rdi_ids:
yield rdi_id, [self._ACC_RE.sub(f, expr) for expr in exprs]

@classmethod
def _get_balances(cls, mode, companies, date_from, date_to):
expr = "deb{mode}[], crd{mode}[]".format(mode=mode)
Expand Down
21 changes: 20 additions & 1 deletion mis_builder/models/expression_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ def __init__(
self.aml_model = aml_model
self._aep_queries_done = False

def aep_do_queries(self):
def aep_do_queries(self, auto_expand_col_name = None):
if self.aep and not self._aep_queries_done:
self.aep.do_queries(
self.date_from,
self.date_to,
self.additional_move_line_filter,
self.aml_model,
auto_expand_col_name
)
self._aep_queries_done = True

Expand All @@ -55,6 +56,7 @@ def eval_expressions(self, expressions, locals_dict):
drilldown_args.append(None)
return vals, drilldown_args, name_error

# TODO remove or keep for compatibility
def eval_expressions_by_account(self, expressions, locals_dict):
if not self.aep:
return
Expand All @@ -71,3 +73,20 @@ def eval_expressions_by_account(self, expressions, locals_dict):
else:
drilldown_args.append(None)
yield account_id, vals, drilldown_args, name_error

def eval_expressions_by_row_detail(self, expressions, locals_dict):
if not self.aep:
return
exprs = [e and e.name or "AccountingNone" for e in expressions]
for rdi_id, replaced_exprs in self.aep.replace_exprs_by_row_detail(exprs):
vals = []
drilldown_args = []
name_error = False
for expr, replaced_expr in zip(exprs, replaced_exprs):
val = mis_safe_eval(replaced_expr, locals_dict)
vals.append(val)
if replaced_expr != expr:
drilldown_args.append({"expr": expr, "row_detail": rdi_id})
else:
drilldown_args.append(None)
yield rdi_id, vals, drilldown_args, name_error
Loading

0 comments on commit 08d07d0

Please sign in to comment.