Skip to content

Commit 695c16c

Browse files
committed
generalize auto expand
Auto Expand V2 clean and rewrite rdi transform Fix when no expand_colum fix aep remove rdi object and support compagny in roe detail identifier fixup fixup fixup fixup fixup fixup fix upo test add test case for rdi fixup do queries test replace exprs fixup test n test fix bc for account_id n test do not add dependency on aa fixup fixup fixup test better test row details some more tests even more some more debug debug values fififix ffff test test fix fix test Remove magic number black lint isort simplify do queries for linting black again autoflake fix init next fix
1 parent 034f9d6 commit 695c16c

16 files changed

Lines changed: 368 additions & 196 deletions

File tree

mis_builder/models/aep.py

Lines changed: 126 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
_DOMAIN_START_RE = re.compile(r"\(|(['\"])[!&|]\1")
1616

17+
UNCLASSIFIED_ROW_DETAIL = "other"
18+
1719

1820
def _is_domain(s):
1921
"""Test if a string looks like an Odoo domain"""
@@ -299,25 +301,22 @@ def do_queries(
299301
date_from,
300302
date_to,
301303
additional_move_line_filter=None,
302-
aml_model=None,
304+
aml_model="account.move.line",
305+
auto_expand_col_name=None,
303306
):
304307
"""Query sums of debit and credit for all accounts and domains
305308
used in expressions.
306309
307310
This method must be executed after done_parsing().
308311
"""
309-
if not aml_model:
310-
aml_model = self.env["account.move.line"]
311-
else:
312-
aml_model = self.env[aml_model]
313-
aml_model = aml_model.with_context(active_test=False)
312+
aml_model = self.env[aml_model].with_context(active_test=False)
314313
company_rates = self._get_company_rates(date_to)
315314
# {(domain, mode): {account_id: (debit, credit)}}
316315
self._data = defaultdict(dict)
317316
domain_by_mode = {}
318317
ends = []
319318
for key in self._map_account_ids:
320-
domain, mode = key
319+
(domain, mode) = key
321320
if mode == self.MODE_END and self.smart_end:
322321
# postpone computation of ending balance
323322
ends.append((domain, mode))
@@ -330,13 +329,16 @@ def do_queries(
330329
domain.append(("account_id", "in", self._map_account_ids[key]))
331330
if additional_move_line_filter:
332331
domain.extend(additional_move_line_filter)
332+
333+
get_fields = ["debit", "credit", "account_id", "company_id"]
334+
group_by_fields = ["account_id", "company_id"]
335+
if auto_expand_col_name:
336+
get_fields = [auto_expand_col_name] + get_fields
337+
group_by_fields = [auto_expand_col_name] + group_by_fields
338+
333339
# fetch sum of debit/credit, grouped by account_id
334-
accs = aml_model.read_group(
335-
domain,
336-
["debit", "credit", "account_id", "company_id"],
337-
["account_id", "company_id"],
338-
lazy=False,
339-
)
340+
accs = aml_model.read_group(domain, get_fields, group_by_fields, lazy=False)
341+
340342
for acc in accs:
341343
rate, dp = company_rates[acc["company_id"][0]]
342344
debit = acc["debit"] or 0.0
@@ -346,19 +348,45 @@ def do_queries(
346348
):
347349
# in initial mode, ignore accounts with 0 balance
348350
continue
349-
self._data[key][acc["account_id"][0]] = (debit * rate, credit * rate)
351+
if (
352+
auto_expand_col_name
353+
and auto_expand_col_name in acc
354+
and acc[auto_expand_col_name]
355+
):
356+
rdi_id = acc[auto_expand_col_name][0]
357+
else:
358+
rdi_id = UNCLASSIFIED_ROW_DETAIL
359+
if not self._data[key].get(rdi_id, False):
360+
self._data[key][rdi_id] = defaultdict(dict)
361+
self._data[key][rdi_id][acc["account_id"][0]] = (
362+
debit * rate,
363+
credit * rate,
364+
)
350365
# compute ending balances by summing initial and variation
351366
for key in ends:
352367
domain, mode = key
353368
initial_data = self._data[(domain, self.MODE_INITIAL)]
354369
variation_data = self._data[(domain, self.MODE_VARIATION)]
355-
account_ids = set(initial_data.keys()) | set(variation_data.keys())
356-
for account_id in account_ids:
357-
di, ci = initial_data.get(account_id, (AccountingNone, AccountingNone))
358-
dv, cv = variation_data.get(
359-
account_id, (AccountingNone, AccountingNone)
370+
rdis = set(initial_data.keys()) | set(variation_data.keys())
371+
for rdi in rdis:
372+
if not initial_data.get(rdi, False):
373+
initial_data[rdi] = defaultdict(dict)
374+
if not variation_data.get(rdi, False):
375+
variation_data[rdi] = defaultdict(dict)
376+
if not self._data[key].get(rdi, False):
377+
self._data[key][rdi] = defaultdict(dict)
378+
379+
account_ids = set(initial_data[rdi].keys()) | set(
380+
variation_data[rdi].keys()
360381
)
361-
self._data[key][account_id] = (di + dv, ci + cv)
382+
for account_id in account_ids:
383+
di, ci = initial_data[rdi].get(
384+
account_id, (AccountingNone, AccountingNone)
385+
)
386+
dv, cv = variation_data[rdi].get(
387+
account_id, (AccountingNone, AccountingNone)
388+
)
389+
self._data[key][rdi][account_id] = (di + dv, ci + cv)
362390

363391
def replace_expr(self, expr):
364392
"""Replace accounting variables in an expression by their amount.
@@ -371,23 +399,25 @@ def replace_expr(self, expr):
371399
def f(mo):
372400
field, mode, acc_domain, ml_domain = self._parse_match_object(mo)
373401
key = (ml_domain, mode)
374-
account_ids_data = self._data[key]
402+
rdi_ids_data = self._data[key]
375403
v = AccountingNone
376404
account_ids = self._account_ids_by_acc_domain[acc_domain]
377-
for account_id in account_ids:
378-
debit, credit = account_ids_data.get(
379-
account_id, (AccountingNone, AccountingNone)
380-
)
381-
if field == "bal":
382-
v += debit - credit
383-
elif field == "pbal" and debit >= credit:
384-
v += debit - credit
385-
elif field == "nbal" and debit < credit:
386-
v += debit - credit
387-
elif field == "deb":
388-
v += debit
389-
elif field == "crd":
390-
v += credit
405+
for rdi in rdi_ids_data:
406+
account_ids_data = self._data[key][rdi]
407+
for account_id in account_ids:
408+
debit, credit = account_ids_data.get(
409+
account_id, (AccountingNone, AccountingNone)
410+
)
411+
if field == "bal":
412+
v += debit - credit
413+
elif field == "pbal" and debit >= credit:
414+
v += debit - credit
415+
elif field == "nbal" and debit < credit:
416+
v += debit - credit
417+
elif field == "deb":
418+
v += debit
419+
elif field == "crd":
420+
v += credit
391421
# in initial balance mode, assume 0 is None
392422
# as it does not make sense to distinguish 0 from "no data"
393423
if (
@@ -401,11 +431,11 @@ def f(mo):
401431
return self._ACC_RE.sub(f, expr)
402432

403433
def replace_exprs_by_account_id(self, exprs):
404-
"""Replace accounting variables in a list of expression
405-
by their amount, iterating by accounts involved in the expression.
434+
"""This method is depreciated and replaced by replace_exprs_by_row_detail.
406435
436+
Replace accounting variables in a list of expression
437+
by their amount, iterating by accounts involved in the expression.
407438
yields account_id, replaced_expr
408-
409439
This method must be executed after do_queries().
410440
"""
411441

@@ -417,7 +447,7 @@ def f(mo):
417447
if account_id not in self._account_ids_by_acc_domain[acc_domain]:
418448
return "(AccountingNone)"
419449
# here we know account_id is involved in acc_domain
420-
account_ids_data = self._data[key]
450+
account_ids_data = self._data[key][UNCLASSIFIED_ROW_DETAIL]
421451
debit, credit = account_ids_data.get(
422452
account_id, (AccountingNone, AccountingNone)
423453
)
@@ -452,14 +482,66 @@ def f(mo):
452482
for mo in self._ACC_RE.finditer(expr):
453483
field, mode, acc_domain, ml_domain = self._parse_match_object(mo)
454484
key = (ml_domain, mode)
455-
account_ids_data = self._data[key]
485+
account_ids_data = self._data[key][UNCLASSIFIED_ROW_DETAIL]
456486
for account_id in self._account_ids_by_acc_domain[acc_domain]:
457487
if account_id in account_ids_data:
458488
account_ids.add(account_id)
459489

460490
for account_id in account_ids:
461491
yield account_id, [self._ACC_RE.sub(f, expr) for expr in exprs]
462492

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

475560
@classmethod
476561
def get_balances_initial(cls, companies, date):

mis_builder/models/expression_evaluator.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@
66

77
class ExpressionEvaluator(object):
88
def __init__(
9-
self,
10-
aep,
11-
date_from,
12-
date_to,
13-
additional_move_line_filter=None,
14-
aml_model=None,
9+
self, aep, date_from, date_to, additional_move_line_filter=None, aml_model=None
1510
):
1611
self.aep = aep
1712
self.date_from = date_from
@@ -20,13 +15,14 @@ def __init__(
2015
self.aml_model = aml_model
2116
self._aep_queries_done = False
2217

23-
def aep_do_queries(self):
18+
def aep_do_queries(self, auto_expand_col_name=None):
2419
if self.aep and not self._aep_queries_done:
2520
self.aep.do_queries(
2621
self.date_from,
2722
self.date_to,
2823
self.additional_move_line_filter,
2924
self.aml_model,
25+
auto_expand_col_name,
3026
)
3127
self._aep_queries_done = True
3228

@@ -50,6 +46,7 @@ def eval_expressions(self, expressions, locals_dict):
5046
drilldown_args.append(None)
5147
return vals, drilldown_args, name_error
5248

49+
# TODO remove or keep for compatibility
5350
def eval_expressions_by_account(self, expressions, locals_dict):
5451
if not self.aep:
5552
return
@@ -66,3 +63,20 @@ def eval_expressions_by_account(self, expressions, locals_dict):
6663
else:
6764
drilldown_args.append(None)
6865
yield account_id, vals, drilldown_args, name_error
66+
67+
def eval_expressions_by_row_detail(self, expressions, locals_dict):
68+
if not self.aep:
69+
return
70+
exprs = [e and e.name or "AccountingNone" for e in expressions]
71+
for rdi_id, replaced_exprs in self.aep.replace_exprs_by_row_detail(exprs):
72+
vals = []
73+
drilldown_args = []
74+
name_error = False
75+
for expr, replaced_expr in zip(exprs, replaced_exprs):
76+
val = mis_safe_eval(replaced_expr, locals_dict)
77+
vals.append(val)
78+
if replaced_expr != expr:
79+
drilldown_args.append({"expr": expr, "row_detail": rdi_id})
80+
else:
81+
drilldown_args.append(None)
82+
yield rdi_id, vals, drilldown_args, name_error

0 commit comments

Comments
 (0)