Skip to content

Commit 08d07d0

Browse files
committed
generalize auto expand
Auto Expand V2
1 parent 3f2d1d9 commit 08d07d0

9 files changed

Lines changed: 242 additions & 79 deletions

File tree

mis_builder/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@
88
from . import aep
99
from . import mis_kpi_data
1010
from . import prorata_read_group_mixin
11+
from . import account_account
12+
from . import account_analytic_account
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright 2017 ACSONE SA/NV
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
from odoo import _, api, fields, models
5+
from odoo.exceptions import UserError
6+
from odoo.osv import expression
7+
8+
class AccountAccount(models.Model):
9+
_name = 'account.account'
10+
_inherit = ['account.account']
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright 2017 ACSONE SA/NV
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
from odoo import _, api, fields, models
5+
from odoo.exceptions import UserError
6+
from odoo.osv import expression
7+
8+
class AccountAnalyticAccount(models.Model):
9+
_name = 'account.analytic.account'
10+
_inherit = ['account.analytic.account']

mis_builder/models/aep.py

Lines changed: 120 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from odoo.tools.safe_eval import datetime, dateutil, safe_eval, time
1212

1313
from .accounting_none import AccountingNone
14-
14+
import pprint
1515
try:
1616
import itertools.izip as zip
1717
except ImportError:
@@ -306,6 +306,8 @@ def do_queries(
306306
date_to,
307307
additional_move_line_filter=None,
308308
aml_model=None,
309+
auto_expand_col_name = None,
310+
rdi_transformer = lambda i: (i[0], str(i[1])) if i else ('other', _('Other'))
309311
):
310312
"""Query sums of debit and credit for all accounts and domains
311313
used in expressions.
@@ -323,7 +325,7 @@ def do_queries(
323325
domain_by_mode = {}
324326
ends = []
325327
for key in self._map_account_ids:
326-
domain, mode = key
328+
(domain, mode) = key
327329
if mode == self.MODE_END and self.smart_end:
328330
# postpone computation of ending balance
329331
ends.append((domain, mode))
@@ -336,13 +338,21 @@ def do_queries(
336338
domain.append(("account_id", "in", self._map_account_ids[key]))
337339
if additional_move_line_filter:
338340
domain.extend(additional_move_line_filter)
341+
342+
get_fields = ["debit", "credit", "account_id", "company_id"]
343+
group_by_fields = ["account_id", "company_id"]
344+
if auto_expand_col_name:
345+
get_fields = [ auto_expand_col_name ] + get_fields
346+
group_by_fields = [ auto_expand_col_name ] + group_by_fields
347+
339348
# fetch sum of debit/credit, grouped by account_id
340349
accs = aml_model.read_group(
341350
domain,
342-
["debit", "credit", "account_id", "company_id"],
343-
["account_id", "company_id"],
351+
get_fields,
352+
group_by_fields,
344353
lazy=False,
345354
)
355+
346356
for acc in accs:
347357
rate, dp = company_rates[acc["company_id"][0]]
348358
debit = acc["debit"] or 0.0
@@ -352,19 +362,33 @@ def do_queries(
352362
):
353363
# in initial mode, ignore accounts with 0 balance
354364
continue
355-
self._data[key][acc["account_id"][0]] = (debit * rate, credit * rate)
365+
rdi_id = rdi_transformer(acc[auto_expand_col_name])
366+
if not self._data[key].get(rdi_id, False):
367+
self._data[key][rdi_id] = defaultdict(dict)
368+
self._data[key][rdi_id][acc["account_id"][0]] = (debit * rate, credit * rate)
356369
# compute ending balances by summing initial and variation
357370
for key in ends:
358371
domain, mode = key
359372
initial_data = self._data[(domain, self.MODE_INITIAL)]
360373
variation_data = self._data[(domain, self.MODE_VARIATION)]
361-
account_ids = set(initial_data.keys()) | set(variation_data.keys())
362-
for account_id in account_ids:
363-
di, ci = initial_data.get(account_id, (AccountingNone, AccountingNone))
364-
dv, cv = variation_data.get(
365-
account_id, (AccountingNone, AccountingNone)
366-
)
367-
self._data[key][account_id] = (di + dv, ci + cv)
374+
rdis = set(initial_data.keys()) | set(variation_data.keys())
375+
for rdi in rdis:
376+
if not initial_data.get(rdi, False):
377+
initial_data[rdi] = defaultdict(dict)
378+
if not variation_data.get(rdi, False):
379+
variation_data[rdi] = defaultdict(dict)
380+
if not self._data[key].get(rdi, False):
381+
self._data[key][rdi] = defaultdict(dict)
382+
pprint.pprint(rdis)
383+
pprint.pprint(rdi)
384+
pprint.pprint(initial_data)
385+
pprint.pprint(variation_data)
386+
387+
account_ids = set(initial_data[rdi].keys()) | set(variation_data[rdi].keys())
388+
for account_id in account_ids:
389+
di, ci = initial_data[rdi].get(account_id, (AccountingNone, AccountingNone))
390+
dv, cv = variation_data[rdi].get(account_id, (AccountingNone, AccountingNone))
391+
self._data[key][rdi][account_id] = (di + dv, ci + cv)
368392

369393
def replace_expr(self, expr):
370394
"""Replace accounting variables in an expression by their amount.
@@ -377,23 +401,25 @@ def replace_expr(self, expr):
377401
def f(mo):
378402
field, mode, acc_domain, ml_domain = self._parse_match_object(mo)
379403
key = (ml_domain, mode)
380-
account_ids_data = self._data[key]
404+
rdi_ids_data = self._data[key]
381405
v = AccountingNone
382406
account_ids = self._account_ids_by_acc_domain[acc_domain]
383-
for account_id in account_ids:
384-
debit, credit = account_ids_data.get(
385-
account_id, (AccountingNone, AccountingNone)
386-
)
387-
if field == "bal":
388-
v += debit - credit
389-
elif field == "pbal" and debit >= credit:
390-
v += debit - credit
391-
elif field == "nbal" and debit < credit:
392-
v += debit - credit
393-
elif field == "deb":
394-
v += debit
395-
elif field == "crd":
396-
v += credit
407+
for rdi in rdi_ids_data:
408+
account_ids_data = self._data[key][rdi]
409+
for account_id in account_ids:
410+
debit, credit = account_ids_data.get(
411+
account_id, (AccountingNone, AccountingNone)
412+
)
413+
if field == "bal":
414+
v += debit - credit
415+
elif field == "pbal" and debit >= credit:
416+
v += debit - credit
417+
elif field == "nbal" and debit < credit:
418+
v += debit - credit
419+
elif field == "deb":
420+
v += debit
421+
elif field == "crd":
422+
v += credit
397423
# in initial balance mode, assume 0 is None
398424
# as it does not make sense to distinguish 0 from "no data"
399425
if (
@@ -424,25 +450,21 @@ def f(mo):
424450
return "(AccountingNone)"
425451
# here we know account_id is involved in acc_domain
426452
account_ids_data = self._data[key]
427-
debit, credit = account_ids_data.get(
428-
account_id, (AccountingNone, AccountingNone)
429-
)
430-
if field == "bal":
431-
v = debit - credit
432-
elif field == "pbal":
433-
if debit >= credit:
434-
v = debit - credit
435-
else:
436-
v = AccountingNone
437-
elif field == "nbal":
438-
if debit < credit:
439-
v = debit - credit
440-
else:
441-
v = AccountingNone
442-
elif field == "deb":
443-
v = debit
444-
elif field == "crd":
445-
v = credit
453+
for rdi in rdi_ids_data:
454+
account_ids_data = self._data[key][rdi]
455+
debit, credit = account_ids_data.get(
456+
account_id, (AccountingNone, AccountingNone)
457+
)
458+
if field == "bal":
459+
v += debit - credit
460+
elif field == "pbal" and debit >= credit:
461+
v += debit - credit
462+
elif field == "nbal" and debit < credit:
463+
v += debit - credit
464+
elif field == "deb":
465+
v += debit
466+
elif field == "crd":
467+
v += credit
446468
# in initial balance mode, assume 0 is None
447469
# as it does not make sense to distinguish 0 from "no data"
448470
if (
@@ -466,6 +488,58 @@ def f(mo):
466488
for account_id in account_ids:
467489
yield account_id, [self._ACC_RE.sub(f, expr) for expr in exprs]
468490

491+
def replace_exprs_by_row_detail(self, exprs):
492+
"""Replace accounting variables in a list of expression
493+
by their amount, iterating by accounts involved in the expression.
494+
495+
yields account_id, replaced_expr
496+
497+
This method must be executed after do_queries().
498+
"""
499+
500+
def f(mo):
501+
field, mode, acc_domain, ml_domain = self._parse_match_object(mo)
502+
key = (ml_domain, mode)
503+
v = AccountingNone
504+
account_ids_data = self._data[key][rdi_id]
505+
account_ids = self._account_ids_by_acc_domain[acc_domain]
506+
507+
for account_id in account_ids:
508+
debit, credit = account_ids_data.get(
509+
account_id, (AccountingNone, AccountingNone)
510+
)
511+
if field == "bal":
512+
v += debit - credit
513+
elif field == "pbal" and debit >= credit:
514+
v += debit - credit
515+
elif field == "nbal" and debit < credit:
516+
v += debit - credit
517+
elif field == "deb":
518+
v += debit
519+
elif field == "crd":
520+
v += credit
521+
# in initial balance mode, assume 0 is None
522+
# as it does not make sense to distinguish 0 from "no data"
523+
if (
524+
v is not AccountingNone
525+
and mode in (self.MODE_INITIAL, self.MODE_UNALLOCATED)
526+
and float_is_zero(v, precision_digits=self.dp)
527+
):
528+
v = AccountingNone
529+
return "(" + repr(v) + ")"
530+
531+
rdi_ids = set()
532+
for expr in exprs:
533+
for mo in self._ACC_RE.finditer(expr):
534+
field, mode, acc_domain, ml_domain = self._parse_match_object(mo)
535+
key = (ml_domain, mode)
536+
rdis_data = self._data[key]
537+
for rdi_id in rdis_data.keys():
538+
rdi_ids.add(rdi_id)
539+
540+
for rdi_id in rdi_ids:
541+
yield rdi_id, [self._ACC_RE.sub(f, expr) for expr in exprs]
542+
469543
@classmethod
470544
def _get_balances(cls, mode, companies, date_from, date_to):
471545
expr = "deb{mode}[], crd{mode}[]".format(mode=mode)

mis_builder/models/expression_evaluator.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ def __init__(
2525
self.aml_model = aml_model
2626
self._aep_queries_done = False
2727

28-
def aep_do_queries(self):
28+
def aep_do_queries(self, auto_expand_col_name = None):
2929
if self.aep and not self._aep_queries_done:
3030
self.aep.do_queries(
3131
self.date_from,
3232
self.date_to,
3333
self.additional_move_line_filter,
3434
self.aml_model,
35+
auto_expand_col_name
3536
)
3637
self._aep_queries_done = True
3738

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

59+
# TODO remove or keep for compatibility
5860
def eval_expressions_by_account(self, expressions, locals_dict):
5961
if not self.aep:
6062
return
@@ -71,3 +73,20 @@ def eval_expressions_by_account(self, expressions, locals_dict):
7173
else:
7274
drilldown_args.append(None)
7375
yield account_id, vals, drilldown_args, name_error
76+
77+
def eval_expressions_by_row_detail(self, expressions, locals_dict):
78+
if not self.aep:
79+
return
80+
exprs = [e and e.name or "AccountingNone" for e in expressions]
81+
for rdi_id, replaced_exprs in self.aep.replace_exprs_by_row_detail(exprs):
82+
vals = []
83+
drilldown_args = []
84+
name_error = False
85+
for expr, replaced_expr in zip(exprs, replaced_exprs):
86+
val = mis_safe_eval(replaced_expr, locals_dict)
87+
vals.append(val)
88+
if replaced_expr != expr:
89+
drilldown_args.append({"expr": expr, "row_detail": rdi_id})
90+
else:
91+
drilldown_args.append(None)
92+
yield rdi_id, vals, drilldown_args, name_error

0 commit comments

Comments
 (0)