odoo-addon-account-financial-report 16.0.1.5.4.4__py3-none-any.whl → 16.0.1.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of odoo-addon-account-financial-report might be problematic. Click here for more details.

Files changed (27) hide show
  1. odoo/addons/account_financial_report/README.rst +36 -1
  2. odoo/addons/account_financial_report/__manifest__.py +4 -1
  3. odoo/addons/account_financial_report/i18n/account_financial_report.pot +105 -5
  4. odoo/addons/account_financial_report/i18n/es.po +103 -0
  5. odoo/addons/account_financial_report/models/__init__.py +2 -0
  6. odoo/addons/account_financial_report/models/account_age_report_configuration.py +47 -0
  7. odoo/addons/account_financial_report/models/res_config_settings.py +14 -0
  8. odoo/addons/account_financial_report/readme/CONFIGURE.rst +26 -0
  9. odoo/addons/account_financial_report/readme/CONTRIBUTORS.rst +1 -0
  10. odoo/addons/account_financial_report/readme/DESCRIPTION.rst +4 -0
  11. odoo/addons/account_financial_report/report/aged_partner_balance.py +69 -8
  12. odoo/addons/account_financial_report/report/aged_partner_balance_xlsx.py +145 -104
  13. odoo/addons/account_financial_report/report/templates/aged_partner_balance.xml +428 -333
  14. odoo/addons/account_financial_report/security/ir.model.access.csv +2 -0
  15. odoo/addons/account_financial_report/security/security.xml +10 -0
  16. odoo/addons/account_financial_report/static/description/index.html +45 -21
  17. odoo/addons/account_financial_report/tests/__init__.py +1 -0
  18. odoo/addons/account_financial_report/tests/test_age_report_configuration.py +41 -0
  19. odoo/addons/account_financial_report/tests/test_aged_partner_balance.py +88 -7
  20. odoo/addons/account_financial_report/view/account_age_report_configuration_views.xml +41 -0
  21. odoo/addons/account_financial_report/view/res_config_settings_views.xml +56 -0
  22. odoo/addons/account_financial_report/wizard/aged_partner_balance_wizard.py +4 -0
  23. odoo/addons/account_financial_report/wizard/aged_partner_balance_wizard_view.xml +2 -3
  24. {odoo_addon_account_financial_report-16.0.1.5.4.4.dist-info → odoo_addon_account_financial_report-16.0.1.6.0.dist-info}/METADATA +37 -2
  25. {odoo_addon_account_financial_report-16.0.1.5.4.4.dist-info → odoo_addon_account_financial_report-16.0.1.6.0.dist-info}/RECORD +27 -20
  26. {odoo_addon_account_financial_report-16.0.1.5.4.4.dist-info → odoo_addon_account_financial_report-16.0.1.6.0.dist-info}/WHEEL +0 -0
  27. {odoo_addon_account_financial_report-16.0.1.5.4.4.dist-info → odoo_addon_account_financial_report-16.0.1.6.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,26 @@
1
+ To configure dynamic intervals for Aged Partner Balance you need to:
2
+
3
+ Go on 'Settings' -> 'Invoicing' -> 'OCA Aged Report Configuration'.
4
+
5
+ Click on option 'Configurations' and create new record.
6
+
7
+ Create new interval.
8
+ The name established on line will be the column to display in Aged Partner Balance.
9
+ Inferior limit established on line is the interval
10
+
11
+ Example of configuration inferior limit:
12
+
13
+ -> 15
14
+ -> 30
15
+ -> 60
16
+
17
+ It means the first interval is from 0 to 15, the second from 16 to 30, and the third is 61+.
18
+
19
+ Go on 'Invoicing' -> 'Reports' -> 'OCA accounting reports' -> 'Aged Partner Balance'
20
+
21
+ When wizard is open, you need to select your interval configuration and print report.
22
+
23
+ If you want to get default interval configuration any time you wish to print Aged Partner Report,
24
+ you can set default interval configuration per company in:
25
+
26
+ 'Settings' -> 'Invoicing' -> 'OCA Aged Report Configuration'.
@@ -24,6 +24,7 @@
24
24
  * João Marques
25
25
  * Alexandre D. Díaz
26
26
  * Víctor Martínez
27
+ * Carolina Fernandez
27
28
 
28
29
  * `Sygel <https://www.sygel.es>`__:
29
30
 
@@ -14,3 +14,7 @@ currency used in account move lines is properly shown.
14
14
 
15
15
  In case that in an account has not been configured a second currency foreign
16
16
  currency balances are not available.
17
+
18
+ Invoicing / Settings / Invoicing / OCA Aged Report Configuration you will be able to set
19
+ dynamic intervals that will appear on the Aged Partner Balance.
20
+ For further information, check CONFIGURE.rst
@@ -25,6 +25,8 @@ class AgedPartnerBalanceReport(models.AbstractModel):
25
25
  ag_pb_data[acc_id]["90_days"] = 0.0
26
26
  ag_pb_data[acc_id]["120_days"] = 0.0
27
27
  ag_pb_data[acc_id]["older"] = 0.0
28
+ for interval_line in self.env.context["age_partner_config"].line_ids:
29
+ ag_pb_data[acc_id][interval_line] = 0.0
28
30
  return ag_pb_data
29
31
 
30
32
  @api.model
@@ -39,6 +41,8 @@ class AgedPartnerBalanceReport(models.AbstractModel):
39
41
  ag_pb_data[acc_id][prt_id]["120_days"] = 0.0
40
42
  ag_pb_data[acc_id][prt_id]["older"] = 0.0
41
43
  ag_pb_data[acc_id][prt_id]["move_lines"] = []
44
+ for interval_line in self.env.context["age_partner_config"].line_ids:
45
+ ag_pb_data[acc_id][prt_id][interval_line] = 0.0
42
46
  return ag_pb_data
43
47
 
44
48
  @api.model
@@ -47,6 +51,7 @@ class AgedPartnerBalanceReport(models.AbstractModel):
47
51
  ):
48
52
  ag_pb_data[acc_id]["residual"] += residual
49
53
  ag_pb_data[acc_id][prt_id]["residual"] += residual
54
+ interval_lines = self.env.context["age_partner_config"].line_ids
50
55
  today = date_at_object
51
56
  if not due_date or today <= due_date:
52
57
  ag_pb_data[acc_id]["current"] += residual
@@ -66,8 +71,29 @@ class AgedPartnerBalanceReport(models.AbstractModel):
66
71
  else:
67
72
  ag_pb_data[acc_id]["older"] += residual
68
73
  ag_pb_data[acc_id][prt_id]["older"] += residual
74
+ days_difference = abs((today - due_date).days)
75
+ for index, line in enumerate(interval_lines):
76
+ lower_limit = 0 if not index else interval_lines[index - 1].inferior_limit
77
+ next_line = interval_lines[index] if index < len(interval_lines) else None
78
+ interval_range = self._get_values_for_range_intervals(
79
+ lower_limit, next_line.inferior_limit
80
+ )
81
+ if (
82
+ days_difference in interval_range
83
+ or days_difference == line.inferior_limit
84
+ ):
85
+ ag_pb_data[acc_id][line] += residual
86
+ ag_pb_data[acc_id][prt_id][line] += residual
87
+ break
69
88
  return ag_pb_data
70
89
 
90
+ def _get_values_for_range_intervals(self, num1, num2):
91
+ min_num = min(num1, num2)
92
+ max_num = max(num1, num2)
93
+ if abs(num2 - num1) == 1:
94
+ return [max_num]
95
+ return list(range(min_num + 1, max_num))
96
+
71
97
  def _get_account_partial_reconciled(self, company_id, date_at_object):
72
98
  domain = [("max_date", ">", date_at_object), ("company_id", "=", company_id)]
73
99
  fields = [
@@ -235,6 +261,9 @@ class AgedPartnerBalanceReport(models.AbstractModel):
235
261
  "older": 0.0,
236
262
  }
237
263
  )
264
+ interval_lines = self.env.context["age_partner_config"].line_ids
265
+ for interval_line in interval_lines:
266
+ ml[interval_line] = 0.0
238
267
  due_date = ml["due_date"]
239
268
  amount = ml["residual"]
240
269
  today = date_at_object
@@ -250,6 +279,19 @@ class AgedPartnerBalanceReport(models.AbstractModel):
250
279
  ml["120_days"] += amount
251
280
  else:
252
281
  ml["older"] += amount
282
+ days_difference = abs((today - due_date).days)
283
+ for index, interval_line in enumerate(interval_lines):
284
+ lower_limit = 0 if not index else interval_lines[index - 1].inferior_limit
285
+ next_line = interval_lines[index] if index < len(interval_lines) else None
286
+ interval_range = self._get_values_for_range_intervals(
287
+ lower_limit, next_line.inferior_limit
288
+ )
289
+ if (
290
+ days_difference in interval_range
291
+ or days_difference == interval_line.inferior_limit
292
+ ):
293
+ ml[interval_line] += amount
294
+ break
253
295
 
254
296
  def _create_account_list(
255
297
  self,
@@ -261,6 +303,7 @@ class AgedPartnerBalanceReport(models.AbstractModel):
261
303
  date_at_oject,
262
304
  ):
263
305
  aged_partner_data = []
306
+ interval_lines = self.env.context["age_partner_config"].line_ids
264
307
  for account in accounts_data.values():
265
308
  acc_id = account["id"]
266
309
  account.update(
@@ -275,6 +318,8 @@ class AgedPartnerBalanceReport(models.AbstractModel):
275
318
  "partners": [],
276
319
  }
277
320
  )
321
+ for interval_line in interval_lines:
322
+ account[interval_line] = ag_pb_data[acc_id][interval_line]
278
323
  for prt_id in ag_pb_data[acc_id]:
279
324
  if isinstance(prt_id, int):
280
325
  partner = {
@@ -287,6 +332,10 @@ class AgedPartnerBalanceReport(models.AbstractModel):
287
332
  "120_days": ag_pb_data[acc_id][prt_id]["120_days"],
288
333
  "older": ag_pb_data[acc_id][prt_id]["older"],
289
334
  }
335
+ for interval_line in interval_lines:
336
+ partner[interval_line] = ag_pb_data[acc_id][prt_id][
337
+ interval_line
338
+ ]
290
339
  if show_move_line_details:
291
340
  move_lines = []
292
341
  for ml in ag_pb_data[acc_id][prt_id]["move_lines"]:
@@ -306,6 +355,7 @@ class AgedPartnerBalanceReport(models.AbstractModel):
306
355
 
307
356
  @api.model
308
357
  def _calculate_percent(self, aged_partner_data):
358
+ interval_lines = self.env.context["age_partner_config"].line_ids
309
359
  for account in aged_partner_data:
310
360
  if abs(account["residual"]) > 0.01:
311
361
  total = account["residual"]
@@ -331,6 +381,10 @@ class AgedPartnerBalanceReport(models.AbstractModel):
331
381
  ),
332
382
  }
333
383
  )
384
+ for interval_line in interval_lines:
385
+ account[f"percent_{interval_line.id}"] = abs(
386
+ round((account[interval_line] / total) * 100, 2)
387
+ )
334
388
  else:
335
389
  account.update(
336
390
  {
@@ -342,6 +396,8 @@ class AgedPartnerBalanceReport(models.AbstractModel):
342
396
  "percent_older": 0.0,
343
397
  }
344
398
  )
399
+ for interval_line in interval_lines:
400
+ account[f"percent_{interval_line.id}"] = 0.0
345
401
  return aged_partner_data
346
402
 
347
403
  def _get_report_values(self, docids, data):
@@ -355,12 +411,12 @@ class AgedPartnerBalanceReport(models.AbstractModel):
355
411
  date_from = data["date_from"]
356
412
  only_posted_moves = data["only_posted_moves"]
357
413
  show_move_line_details = data["show_move_line_details"]
358
- (
359
- ag_pb_data,
360
- accounts_data,
361
- partners_data,
362
- journals_data,
363
- ) = self._get_move_lines_data(
414
+ aged_partner_configuration = self.env[
415
+ "account.age.report.configuration"
416
+ ].browse(data["age_partner_config_id"])
417
+ (ag_pb_data, accounts_data, partners_data, journals_data,) = self.with_context(
418
+ age_partner_config=aged_partner_configuration
419
+ )._get_move_lines_data(
364
420
  company_id,
365
421
  account_ids,
366
422
  partner_ids,
@@ -369,7 +425,9 @@ class AgedPartnerBalanceReport(models.AbstractModel):
369
425
  only_posted_moves,
370
426
  show_move_line_details,
371
427
  )
372
- aged_partner_data = self._create_account_list(
428
+ aged_partner_data = self.with_context(
429
+ age_partner_config=aged_partner_configuration
430
+ )._create_account_list(
373
431
  ag_pb_data,
374
432
  accounts_data,
375
433
  partners_data,
@@ -377,7 +435,9 @@ class AgedPartnerBalanceReport(models.AbstractModel):
377
435
  show_move_line_details,
378
436
  date_at_object,
379
437
  )
380
- aged_partner_data = self._calculate_percent(aged_partner_data)
438
+ aged_partner_data = self.with_context(
439
+ age_partner_config=aged_partner_configuration
440
+ )._calculate_percent(aged_partner_data)
381
441
  return {
382
442
  "doc_ids": [wizard_id],
383
443
  "doc_model": "open.items.report.wizard",
@@ -388,6 +448,7 @@ class AgedPartnerBalanceReport(models.AbstractModel):
388
448
  "only_posted_moves": only_posted_moves,
389
449
  "aged_partner_balance": aged_partner_data,
390
450
  "show_move_lines_details": show_move_line_details,
451
+ "age_partner_config": aged_partner_configuration,
391
452
  }
392
453
 
393
454
  def _get_ml_fields(self):
@@ -1,6 +1,7 @@
1
1
  # Author: Julien Coux
2
2
  # Copyright 2016 Camptocamp SA
3
3
  # Copyright 2021 Tecnativa - João Marques
4
+ # Copyright 2023 Tecnativa - Carolina Fernandez
4
5
  # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
5
6
 
6
7
  from odoo import _, models
@@ -20,67 +21,84 @@ class AgedPartnerBalanceXslx(models.AbstractModel):
20
21
  report_name = report_name + suffix
21
22
  return report_name
22
23
 
23
- def _get_report_columns(self, report):
24
- if not report.show_move_line_details:
25
- return {
26
- 0: {"header": _("Partner"), "field": "name", "width": 70},
27
- 1: {
28
- "header": _("Residual"),
29
- "field": "residual",
30
- "field_footer_total": "residual",
31
- "type": "amount",
32
- "width": 14,
33
- },
34
- 2: {
35
- "header": _("Current"),
36
- "field": "current",
37
- "field_footer_total": "current",
38
- "field_footer_percent": "percent_current",
39
- "type": "amount",
40
- "width": 14,
41
- },
42
- 3: {
43
- "header": _("Age ≤ 30 d."),
44
- "field": "30_days",
45
- "field_footer_total": "30_days",
46
- "field_footer_percent": "percent_30_days",
47
- "type": "amount",
48
- "width": 14,
49
- },
50
- 4: {
51
- "header": _("Age ≤ 60 d."),
52
- "field": "60_days",
53
- "field_footer_total": "60_days",
54
- "field_footer_percent": "percent_60_days",
55
- "type": "amount",
56
- "width": 14,
57
- },
58
- 5: {
59
- "header": _("Age ≤ 90 d."),
60
- "field": "90_days",
61
- "field_footer_total": "90_days",
62
- "field_footer_percent": "percent_90_days",
63
- "type": "amount",
64
- "width": 14,
65
- },
66
- 6: {
67
- "header": _("Age ≤ 120 d."),
68
- "field": "120_days",
69
- "field_footer_total": "120_days",
70
- "field_footer_percent": "percent_120_days",
71
- "type": "amount",
72
- "width": 14,
73
- },
74
- 7: {
75
- "header": _("Older"),
76
- "field": "older",
77
- "field_footer_total": "older",
78
- "field_footer_percent": "percent_older",
79
- "type": "amount",
80
- "width": 14,
81
- },
24
+ def _get_report_columns_without_move_line_details(self, report, column_index):
25
+ report_columns = {
26
+ 0: {"header": _("Partner"), "field": "name", "width": 70},
27
+ 1: {
28
+ "header": _("Residual"),
29
+ "field": "residual",
30
+ "field_footer_total": "residual",
31
+ "type": "amount",
32
+ "width": 14,
33
+ },
34
+ 2: {
35
+ "header": _("Current"),
36
+ "field": "current",
37
+ "field_footer_total": "current",
38
+ "field_footer_percent": "percent_current",
39
+ "type": "amount",
40
+ "width": 14,
41
+ },
42
+ }
43
+ if not report.age_partner_config_id:
44
+ report_columns.update(
45
+ {
46
+ 3: {
47
+ "header": _("Age ≤ 30 d."),
48
+ "field": "30_days",
49
+ "field_footer_total": "30_days",
50
+ "field_footer_percent": "percent_30_days",
51
+ "type": "amount",
52
+ "width": 14,
53
+ },
54
+ 4: {
55
+ "header": _("Age ≤ 60 d."),
56
+ "field": "60_days",
57
+ "field_footer_total": "60_days",
58
+ "field_footer_percent": "percent_60_days",
59
+ "type": "amount",
60
+ "width": 14,
61
+ },
62
+ 5: {
63
+ "header": _("Age ≤ 90 d."),
64
+ "field": "90_days",
65
+ "field_footer_total": "90_days",
66
+ "field_footer_percent": "percent_90_days",
67
+ "type": "amount",
68
+ "width": 14,
69
+ },
70
+ 6: {
71
+ "header": _("Age ≤ 120 d."),
72
+ "field": "120_days",
73
+ "field_footer_total": "120_days",
74
+ "field_footer_percent": "percent_120_days",
75
+ "type": "amount",
76
+ "width": 14,
77
+ },
78
+ 7: {
79
+ "header": _("Older"),
80
+ "field": "older",
81
+ "field_footer_total": "older",
82
+ "field_footer_percent": "percent_older",
83
+ "type": "amount",
84
+ "width": 14,
85
+ },
86
+ }
87
+ )
88
+ for interval in report.age_partner_config_id.line_ids:
89
+ report_columns[column_index] = {
90
+ "header": interval.name,
91
+ "field": interval,
92
+ "field_footer_total": interval,
93
+ "field_footer_percent": f"percent_{interval.id}",
94
+ "type": "amount",
95
+ "width": 14,
82
96
  }
83
- return {
97
+ column_index += 1
98
+ return report_columns
99
+
100
+ def _get_report_columns_with_move_line_details(self, report, column_index):
101
+ report_columns = {
84
102
  0: {"header": _("Date"), "field": "date", "width": 11},
85
103
  1: {"header": _("Entry"), "field": "entry", "width": 18},
86
104
  2: {"header": _("Journal"), "field": "journal", "width": 8},
@@ -105,52 +123,75 @@ class AgedPartnerBalanceXslx(models.AbstractModel):
105
123
  "type": "amount",
106
124
  "width": 14,
107
125
  },
108
- 9: {
109
- "header": _("Age ≤ 30 d."),
110
- "field": "30_days",
111
- "field_footer_total": "30_days",
112
- "field_footer_percent": "percent_30_days",
113
- "field_final_balance": "30_days",
114
- "type": "amount",
115
- "width": 14,
116
- },
117
- 10: {
118
- "header": _("Age ≤ 60 d."),
119
- "field": "60_days",
120
- "field_footer_total": "60_days",
121
- "field_footer_percent": "percent_60_days",
122
- "field_final_balance": "60_days",
123
- "type": "amount",
124
- "width": 14,
125
- },
126
- 11: {
127
- "header": _("Age ≤ 90 d."),
128
- "field": "90_days",
129
- "field_footer_total": "90_days",
130
- "field_footer_percent": "percent_90_days",
131
- "field_final_balance": "90_days",
132
- "type": "amount",
133
- "width": 14,
134
- },
135
- 12: {
136
- "header": _("Age ≤ 120 d."),
137
- "field": "120_days",
138
- "field_footer_total": "120_days",
139
- "field_footer_percent": "percent_120_days",
140
- "field_final_balance": "120_days",
141
- "type": "amount",
142
- "width": 14,
143
- },
144
- 13: {
145
- "header": _("Older"),
146
- "field": "older",
147
- "field_footer_total": "older",
148
- "field_footer_percent": "percent_older",
149
- "field_final_balance": "older",
126
+ }
127
+ if not report.age_partner_config_id:
128
+ report_columns.update(
129
+ {
130
+ 9: {
131
+ "header": _("Age ≤ 30 d."),
132
+ "field": "30_days",
133
+ "field_footer_total": "30_days",
134
+ "field_footer_percent": "percent_30_days",
135
+ "field_final_balance": "30_days",
136
+ "type": "amount",
137
+ "width": 14,
138
+ },
139
+ 10: {
140
+ "header": _("Age ≤ 60 d."),
141
+ "field": "60_days",
142
+ "field_footer_total": "60_days",
143
+ "field_footer_percent": "percent_60_days",
144
+ "field_final_balance": "60_days",
145
+ "type": "amount",
146
+ "width": 14,
147
+ },
148
+ 11: {
149
+ "header": _("Age ≤ 90 d."),
150
+ "field": "90_days",
151
+ "field_footer_total": "90_days",
152
+ "field_footer_percent": "percent_90_days",
153
+ "field_final_balance": "90_days",
154
+ "type": "amount",
155
+ "width": 14,
156
+ },
157
+ 12: {
158
+ "header": _("Age ≤ 120 d."),
159
+ "field": "120_days",
160
+ "field_footer_total": "120_days",
161
+ "field_footer_percent": "percent_120_days",
162
+ "field_final_balance": "120_days",
163
+ "type": "amount",
164
+ "width": 14,
165
+ },
166
+ 13: {
167
+ "header": _("Older"),
168
+ "field": "older",
169
+ "field_footer_total": "older",
170
+ "field_footer_percent": "percent_older",
171
+ "field_final_balance": "older",
172
+ "type": "amount",
173
+ "width": 14,
174
+ },
175
+ }
176
+ )
177
+ for interval in report.age_partner_config_id.line_ids:
178
+ report_columns[column_index] = {
179
+ "header": interval.name,
180
+ "field": interval,
181
+ "field_footer_total": interval,
182
+ "field_footer_percent": f"percent_{interval.id}",
150
183
  "type": "amount",
151
184
  "width": 14,
152
- },
153
- }
185
+ }
186
+ column_index += 1
187
+ return report_columns
188
+
189
+ def _get_report_columns(self, report):
190
+ if not report.show_move_line_details:
191
+ return self._get_report_columns_without_move_line_details(
192
+ report, column_index=3
193
+ )
194
+ return self._get_report_columns_with_move_line_details(report, column_index=9)
154
195
 
155
196
  def _get_report_filters(self, report):
156
197
  return [