odoo-addon-account-dashboard-banner 16.0.1.0.0.4__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-dashboard-banner might be problematic. Click here for more details.

Files changed (28) hide show
  1. odoo/addons/account_dashboard_banner/README.rst +151 -0
  2. odoo/addons/account_dashboard_banner/__init__.py +2 -0
  3. odoo/addons/account_dashboard_banner/__manifest__.py +29 -0
  4. odoo/addons/account_dashboard_banner/i18n/account_dashboard_banner.pot +244 -0
  5. odoo/addons/account_dashboard_banner/i18n/fr.po +270 -0
  6. odoo/addons/account_dashboard_banner/models/__init__.py +1 -0
  7. odoo/addons/account_dashboard_banner/models/account_dashboard_banner_cell.py +324 -0
  8. odoo/addons/account_dashboard_banner/post_install.py +28 -0
  9. odoo/addons/account_dashboard_banner/readme/CONFIGURE.md +33 -0
  10. odoo/addons/account_dashboard_banner/readme/CONTRIBUTORS.md +1 -0
  11. odoo/addons/account_dashboard_banner/readme/DESCRIPTION.md +13 -0
  12. odoo/addons/account_dashboard_banner/readme/USAGE.md +3 -0
  13. odoo/addons/account_dashboard_banner/security/ir.model.access.csv +4 -0
  14. odoo/addons/account_dashboard_banner/static/description/account_dashboard_banner.png +0 -0
  15. odoo/addons/account_dashboard_banner/static/description/banner_cell_config.png +0 -0
  16. odoo/addons/account_dashboard_banner/static/description/cell_form_with_warning.png +0 -0
  17. odoo/addons/account_dashboard_banner/static/description/icon.png +0 -0
  18. odoo/addons/account_dashboard_banner/static/description/index.html +483 -0
  19. odoo/addons/account_dashboard_banner/static/src/views/account_dashboard_kanban_banner.esm.js +47 -0
  20. odoo/addons/account_dashboard_banner/static/src/views/account_dashboard_kanban_banner.xml +49 -0
  21. odoo/addons/account_dashboard_banner/tests/__init__.py +1 -0
  22. odoo/addons/account_dashboard_banner/tests/test_banner.py +56 -0
  23. odoo/addons/account_dashboard_banner/views/account_dashboard_banner_cell.xml +92 -0
  24. odoo/addons/account_dashboard_banner/views/account_journal_dashboard.xml +19 -0
  25. odoo_addon_account_dashboard_banner-16.0.1.0.0.4.dist-info/METADATA +168 -0
  26. odoo_addon_account_dashboard_banner-16.0.1.0.0.4.dist-info/RECORD +28 -0
  27. odoo_addon_account_dashboard_banner-16.0.1.0.0.4.dist-info/WHEEL +5 -0
  28. odoo_addon_account_dashboard_banner-16.0.1.0.0.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,270 @@
1
+ # Translation of Odoo Server.
2
+ # This file contains the translation of the following modules:
3
+ # * account_dashboard_banner
4
+ #
5
+ msgid ""
6
+ msgstr ""
7
+ "Project-Id-Version: Odoo Server 18.0\n"
8
+ "Report-Msgid-Bugs-To: \n"
9
+ "POT-Creation-Date: 2025-07-06 14:17+0000\n"
10
+ "PO-Revision-Date: 2025-07-06 14:17+0000\n"
11
+ "Last-Translator: Alexis de Lattre <alexis.delattre@akretion.com>\n"
12
+ "Language-Team: \n"
13
+ "Language: fr\n"
14
+ "MIME-Version: 1.0\n"
15
+ "Content-Type: text/plain; charset=UTF-8\n"
16
+ "Content-Transfer-Encoding: \n"
17
+ "Plural-Forms: \n"
18
+
19
+ #. module: account_dashboard_banner
20
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__warn_type__above
21
+ msgid "Above Maximum"
22
+ msgstr "supérieur au maximum"
23
+
24
+ #. module: account_dashboard_banner
25
+ #: model:ir.model,name:account_dashboard_banner.model_account_dashboard_banner_cell
26
+ msgid "Accounting Dashboard Banner Cell"
27
+ msgstr "Cellule du bandeau du tableau de bord de la comptabilité"
28
+
29
+ #. module: account_dashboard_banner
30
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__fiscalyear_lock_date
31
+ #, fuzzy
32
+ msgid "All Users Lock Date"
33
+ msgstr "Date de verrouillage des ventes"
34
+
35
+ #. module: account_dashboard_banner
36
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__warn_type__inside
37
+ msgid "Between Minimum and Maximum"
38
+ msgstr "entre le minimum et le maximum"
39
+
40
+ #. module: account_dashboard_banner
41
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__cell_type
42
+ msgid "Cell Type"
43
+ msgstr "Type de cellule"
44
+
45
+ #. module: account_dashboard_banner
46
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__create_uid
47
+ msgid "Created by"
48
+ msgstr "Créé par"
49
+
50
+ #. module: account_dashboard_banner
51
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__create_date
52
+ msgid "Created on"
53
+ msgstr "Créé le"
54
+
55
+ #. module: account_dashboard_banner
56
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__custom_label
57
+ msgid "Custom Label"
58
+ msgstr "Libellé personnalisé"
59
+
60
+ #. module: account_dashboard_banner
61
+ #: model_terms:ir.ui.view,arch_db:account_dashboard_banner.account_dashboard_banner_cell_form
62
+ #: model_terms:ir.ui.view,arch_db:account_dashboard_banner.account_dashboard_banner_cell_list
63
+ msgid "Custom Label (optional)"
64
+ msgstr "Libellé personnalisé (facultatif)"
65
+
66
+ #. module: account_dashboard_banner
67
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__custom_tooltip
68
+ msgid "Custom Tooltip"
69
+ msgstr "Info-bulle personnalisée"
70
+
71
+ #. module: account_dashboard_banner
72
+ #: model_terms:ir.ui.view,arch_db:account_dashboard_banner.account_dashboard_banner_cell_form
73
+ msgid "Custom Tooltip (optional)"
74
+ msgstr "Info-bulle personnalisée (facultatif)"
75
+
76
+ #. module: account_dashboard_banner
77
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__customer_debt
78
+ msgid "Customer Debt"
79
+ msgstr "Dette client"
80
+
81
+ #. module: account_dashboard_banner
82
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__customer_overdue
83
+ msgid "Customer Overdue"
84
+ msgstr "Impayés client"
85
+
86
+ #. module: account_dashboard_banner
87
+ #: model:ir.ui.menu,name:account_dashboard_banner.account_dashboard_config
88
+ msgid "Dashboard"
89
+ msgstr "Tableau de bord"
90
+
91
+ #. module: account_dashboard_banner
92
+ #: model:ir.actions.act_window,name:account_dashboard_banner.account_dashboard_banner_cell_action
93
+ #: model:ir.ui.menu,name:account_dashboard_banner.account_dashboard_banner_cell_menu
94
+ msgid "Dashboard Banner Cells"
95
+ msgstr "Cellules du bandeau du tableau de bord"
96
+
97
+ #. module: account_dashboard_banner
98
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__display_name
99
+ msgid "Display Name"
100
+ msgstr "Nom affiché"
101
+
102
+ #. module: account_dashboard_banner
103
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__income_fiscalyear
104
+ msgid "Fiscal Year-to-date Income"
105
+ msgstr "Revenus depuis le début de l'exercice comptable"
106
+
107
+ #. module: account_dashboard_banner
108
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__id
109
+ msgid "ID"
110
+ msgstr "ID"
111
+
112
+ #. module: account_dashboard_banner
113
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__period_lock_date
114
+ #, fuzzy
115
+ msgid "Journals Entries Lock Date"
116
+ msgstr "Date de verrouillage des ventes"
117
+
118
+ #. module: account_dashboard_banner
119
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell____last_update
120
+ #, fuzzy
121
+ msgid "Last Modified on"
122
+ msgstr "Dernière mise à jour le"
123
+
124
+ #. module: account_dashboard_banner
125
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__write_uid
126
+ msgid "Last Updated by"
127
+ msgstr "Dernière mise à jour par"
128
+
129
+ #. module: account_dashboard_banner
130
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__write_date
131
+ msgid "Last Updated on"
132
+ msgstr "Dernière mise à jour le"
133
+
134
+ #. module: account_dashboard_banner
135
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__liquidity
136
+ msgid "Liquidity"
137
+ msgstr "Liquidités"
138
+
139
+ #. module: account_dashboard_banner
140
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__warn_max
141
+ msgid "Maximum"
142
+ msgstr "Maximum"
143
+
144
+ #. module: account_dashboard_banner
145
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__warn_min
146
+ msgid "Minimum"
147
+ msgstr "Minimum"
148
+
149
+ #. module: account_dashboard_banner
150
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__income_month
151
+ msgid "Month-to-date Income"
152
+ msgstr "Revenus depuis le début du mois"
153
+
154
+ #. module: account_dashboard_banner
155
+ #. odoo-python
156
+ #: code:addons/account_dashboard_banner/models/account_dashboard_banner_cell.py:0
157
+ #, python-format
158
+ msgid ""
159
+ "On cell '%(cell_type)s' with warning enabled, the minimum (%(warn_min)s) "
160
+ "must be under the maximum (%(warn_max)s)."
161
+ msgstr ""
162
+ "Pour la cellule '%(cell_type)s' dont l'avertissement est activé, le minimum "
163
+ "(%(warn_min)s) doit être inférieur au maximum (%(warn_max)s)."
164
+
165
+ #. module: account_dashboard_banner
166
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__income_quarter
167
+ msgid "Quarter-to-date Income"
168
+ msgstr "Revenus depuis le début du trimestre"
169
+
170
+ #. module: account_dashboard_banner
171
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__sequence
172
+ msgid "Sequence"
173
+ msgstr "Séquence"
174
+
175
+ #. module: account_dashboard_banner
176
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__supplier_debt
177
+ msgid "Supplier Debt"
178
+ msgstr "Dette fournisseur"
179
+
180
+ #. module: account_dashboard_banner
181
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__tax_lock_date
182
+ msgid "Tax Return Lock Date"
183
+ msgstr "Date de verrouillage de la déclaration de TVA"
184
+
185
+ #. module: account_dashboard_banner
186
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__warn_type__under
187
+ msgid "Under Minimum"
188
+ msgstr "inférieur au minimum"
189
+
190
+ #. module: account_dashboard_banner
191
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__warn_type__outside
192
+ msgid "Under Minimum or Above Maximum"
193
+ msgstr "inférieur au minimum ou supérieur au maximum"
194
+
195
+ #. module: account_dashboard_banner
196
+ #: model_terms:ir.ui.view,arch_db:account_dashboard_banner.account_dashboard_banner_cell_form
197
+ msgid "Warn If"
198
+ msgstr "Avertissement si"
199
+
200
+ #. module: account_dashboard_banner
201
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__warn_lock_date_days
202
+ #, fuzzy
203
+ msgid "Warn Lock Date Days"
204
+ msgstr "Date de verrouillage ferme"
205
+
206
+ #. module: account_dashboard_banner
207
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__warn_type
208
+ msgid "Warn Type"
209
+ msgstr "Type d'avertissement"
210
+
211
+ #. module: account_dashboard_banner
212
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__warn_type_show
213
+ msgid "Warn Type Show"
214
+ msgstr "Afficher le type d'avertissement"
215
+
216
+ #. module: account_dashboard_banner
217
+ #: model_terms:ir.ui.view,arch_db:account_dashboard_banner.account_dashboard_banner_cell_form
218
+ msgid "Warn if lock date is older than"
219
+ msgstr "Avertissement si la date de verrouillage est antérieure à"
220
+
221
+ #. module: account_dashboard_banner
222
+ #: model:ir.model.constraint,message:account_dashboard_banner.constraint_account_dashboard_banner_cell_warn_lock_date_days_positive
223
+ msgid "Warn if lock date is older than N days must be positive or null."
224
+ msgstr ""
225
+ "Avertissement si la date de verrouillage est supérieure à N jours. Doit être "
226
+ "positif ou nul."
227
+
228
+ #. module: account_dashboard_banner
229
+ #: model:ir.model.fields,field_description:account_dashboard_banner.field_account_dashboard_banner_cell__warn
230
+ msgid "Warning"
231
+ msgstr "Avertissement"
232
+
233
+ #. module: account_dashboard_banner
234
+ #: model:ir.model.fields.selection,name:account_dashboard_banner.selection__account_dashboard_banner_cell__cell_type__income_year
235
+ msgid "Year-to-date Income"
236
+ msgstr "Revenus depuis le début de l'année"
237
+
238
+ #. module: account_dashboard_banner
239
+ #: model_terms:ir.ui.view,arch_db:account_dashboard_banner.account_dashboard_banner_cell_form
240
+ msgid "days"
241
+ msgstr "jours"
242
+
243
+ #. module: account_dashboard_banner
244
+ #. odoo-python
245
+ #: code:addons/account_dashboard_banner/models/account_dashboard_banner_cell.py:0
246
+ #, python-format
247
+ msgid "from %s"
248
+ msgstr "depuis le %s"
249
+
250
+ #. module: account_dashboard_banner
251
+ #. odoo-python
252
+ #: code:addons/account_dashboard_banner/models/account_dashboard_banner_cell.py:0
253
+ #, python-format
254
+ msgid "with due date before %s"
255
+ msgstr "avec date d'échéance antérieure au %s"
256
+
257
+ #~ msgid "Balance of account(s) %(account_codes)s%(specific)s."
258
+ #~ msgstr "Solde du/des compte(s) %(account_codes)s%(specific)s."
259
+
260
+ #~ msgid "Global Lock Date"
261
+ #~ msgstr "Date de verrouillage globale"
262
+
263
+ #~ msgid "None"
264
+ #~ msgstr "Aucun"
265
+
266
+ #~ msgid "Purchase Lock Date"
267
+ #~ msgstr "Date de verrouillage des achats"
268
+
269
+ #~ msgid "_compute_warn_fields"
270
+ #~ msgstr "calculer les champs d'avertissement"
@@ -0,0 +1 @@
1
+ from . import account_dashboard_banner_cell
@@ -0,0 +1,324 @@
1
+ # Copyright 2025 Akretion France (https://www.akretion.com/)
2
+ # @author: Alexis de Lattre <alexis.delattre@akretion.com>
3
+ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
4
+
5
+ from dateutil.relativedelta import relativedelta
6
+
7
+ from odoo import _, api, fields, models
8
+ from odoo.exceptions import ValidationError
9
+ from odoo.osv import expression
10
+ from odoo.tools import date_utils
11
+ from odoo.tools.misc import format_amount, format_date
12
+
13
+
14
+ class AccountDashboardBannerCell(models.Model):
15
+ _name = "account.dashboard.banner.cell"
16
+ _description = "Accounting Dashboard Banner Cell"
17
+ _order = "sequence, id"
18
+
19
+ sequence = fields.Integer()
20
+ cell_type = fields.Selection(
21
+ [
22
+ ("income_fiscalyear", "Fiscal Year-to-date Income"),
23
+ ("income_year", "Year-to-date Income"),
24
+ ("income_quarter", "Quarter-to-date Income"),
25
+ ("income_month", "Month-to-date Income"),
26
+ ("liquidity", "Liquidity"),
27
+ ("customer_debt", "Customer Debt"),
28
+ ("customer_overdue", "Customer Overdue"),
29
+ ("supplier_debt", "Supplier Debt"),
30
+ # for lock dates, the key matches exactly the field name on res.company
31
+ ("tax_lock_date", "Tax Return Lock Date"),
32
+ ("period_lock_date", "Journals Entries Lock Date"),
33
+ ("fiscalyear_lock_date", "All Users Lock Date"),
34
+ ],
35
+ required=True,
36
+ )
37
+ custom_label = fields.Char()
38
+ custom_tooltip = fields.Char()
39
+ warn = fields.Boolean(string="Warning")
40
+ warn_lock_date_days = fields.Integer(
41
+ compute="_compute_warn_fields", store=True, readonly=False, precompute=True
42
+ )
43
+ warn_min = fields.Float(string="Minimum")
44
+ warn_max = fields.Float(string="Maximum")
45
+ warn_type_show = fields.Boolean(
46
+ compute="_compute_warn_fields", store=True, precompute=True
47
+ )
48
+ warn_type = fields.Selection(
49
+ [
50
+ ("under", "Under Minimum"),
51
+ ("above", "Above Maximum"),
52
+ ("outside", "Under Minimum or Above Maximum"),
53
+ ("inside", "Between Minimum and Maximum"),
54
+ ],
55
+ default="under",
56
+ )
57
+
58
+ _sql_constraints = [
59
+ (
60
+ "warn_lock_date_days_positive",
61
+ "CHECK(warn_lock_date_days >= 0)",
62
+ "Warn if lock date is older than N days must be positive or null.",
63
+ )
64
+ ]
65
+
66
+ @api.constrains("warn_min", "warn_max", "warn_type", "warn", "cell_type")
67
+ def _check_warn_config(self):
68
+ for cell in self:
69
+ if (
70
+ cell.cell_type
71
+ and not cell.cell_type.endswith("_lock_date")
72
+ and cell.warn
73
+ and cell.warn_type in ("outside", "inside")
74
+ and cell.warn_max <= cell.warn_min
75
+ ):
76
+ cell_type2label = dict(
77
+ self.fields_get("cell_type", "selection")["cell_type"]["selection"]
78
+ )
79
+ raise ValidationError(
80
+ _(
81
+ "On cell '%(cell_type)s' with warning enabled, "
82
+ "the minimum (%(warn_min)s) must be under "
83
+ "the maximum (%(warn_max)s).",
84
+ cell_type=cell_type2label[cell.cell_type],
85
+ warn_min=cell.warn_min,
86
+ warn_max=cell.warn_max,
87
+ )
88
+ )
89
+
90
+ @api.model
91
+ def _default_warn_lock_date_days(self, cell_type):
92
+ defaultmap = {
93
+ "tax_lock_date": 61, # 2 months
94
+ "period_lock_date": 61, # 2 months
95
+ "fiscalyear_lock_date": 520, # FY final closing, 1 year + 5 months
96
+ }
97
+ return defaultmap.get(cell_type)
98
+
99
+ @api.depends("cell_type", "warn")
100
+ def _compute_warn_fields(self):
101
+ for cell in self:
102
+ warn_type_show = False
103
+ warn_lock_date_days = 0
104
+ if cell.cell_type and cell.warn:
105
+ if cell.cell_type.endswith("_lock_date"):
106
+ warn_lock_date_days = self._default_warn_lock_date_days(
107
+ cell.cell_type
108
+ )
109
+ else:
110
+ warn_type_show = True
111
+ cell.warn_type_show = warn_type_show
112
+ cell.warn_lock_date_days = warn_lock_date_days
113
+
114
+ @api.model
115
+ def get_banner_data(self):
116
+ """This is the method called by the JS code that displays the banner"""
117
+ company = self.env.company
118
+ return self._prepare_banner_data(company)
119
+
120
+ def _prepare_speedy(self, company):
121
+ lock_date_fields = [
122
+ "tax_lock_date",
123
+ "period_lock_date",
124
+ "fiscalyear_lock_date",
125
+ ]
126
+ speedy = {
127
+ "cell_type2label": dict(
128
+ self.fields_get("cell_type", "selection")["cell_type"]["selection"]
129
+ ),
130
+ "lock_date2help": {
131
+ key: value["help"]
132
+ for (key, value) in company.fields_get(lock_date_fields, "help").items()
133
+ },
134
+ "today": fields.Date.context_today(self),
135
+ }
136
+ return speedy
137
+
138
+ @api.model
139
+ def _prepare_banner_data(self, company):
140
+ # The order in this list will be the display order in the banner
141
+ # In fact, it's not a list but a dict. I tried to make it work by returning
142
+ # a list but it seems OWL only accepts dicts (I always get errors on lists)
143
+ cells = self.search([])
144
+ speedy = cells._prepare_speedy(company)
145
+ res = {}
146
+ seq = 0
147
+ for cell in cells:
148
+ seq += 1
149
+ cell_data = cell._prepare_cell_data(company, speedy)
150
+ cell._update_cell_warn(cell_data)
151
+ res[seq] = cell_data
152
+ # from pprint import pprint
153
+ # pprint(res)
154
+ return res
155
+
156
+ def _prepare_cell_data_liquidity(self, company, speedy):
157
+ self.ensure_one()
158
+ journals = self.env["account.journal"].search(
159
+ [
160
+ ("company_id", "=", company.id),
161
+ ("type", "in", ("bank", "cash", "credit")),
162
+ ("default_account_id", "!=", False),
163
+ ]
164
+ )
165
+ accounts = journals.default_account_id
166
+ return (accounts, 1, False, False)
167
+
168
+ def _prepare_cell_data_supplier_debt(self, company, speedy):
169
+ accounts = self.env["ir.property"]._get(
170
+ "property_account_payable_id", "res.partner"
171
+ )
172
+ return (accounts, -1, False, False)
173
+
174
+ def _prepare_cell_data_income(self, company, speedy):
175
+ cell_type = self.cell_type
176
+ accounts = self.env["account.account"].search(
177
+ [
178
+ ("company_id", "=", company.id),
179
+ ("account_type", "in", ("income", "income_other")),
180
+ ]
181
+ )
182
+ if cell_type == "income_fiscalyear":
183
+ start_date, end_date = date_utils.get_fiscal_year(
184
+ speedy["today"],
185
+ day=company.fiscalyear_last_day,
186
+ month=int(company.fiscalyear_last_month),
187
+ )
188
+ elif cell_type == "income_month":
189
+ start_date = speedy["today"] + relativedelta(day=1)
190
+ elif cell_type == "income_year":
191
+ start_date = speedy["today"] + relativedelta(day=1, month=1)
192
+ elif cell_type == "income_quarter":
193
+ month_start_quarter = 3 * ((speedy["today"].month - 1) // 3) + 1
194
+ start_date = speedy["today"] + relativedelta(
195
+ day=1, month=month_start_quarter
196
+ )
197
+ specific_domain = [("date", ">=", start_date)]
198
+ specific_tooltip = _("from %s") % format_date(self.env, start_date)
199
+ return (accounts, -1, specific_domain, specific_tooltip)
200
+
201
+ def _prepare_cell_data_customer_debt(self, company, speedy):
202
+ accounts = self.env["ir.property"]._get(
203
+ "property_account_receivable_id", "res.partner"
204
+ )
205
+ if hasattr(company, "account_default_pos_receivable_account_id"):
206
+ accounts |= company.account_default_pos_receivable_account_id
207
+ return (accounts, 1, False, False)
208
+
209
+ def _prepare_cell_data_customer_overdue(self, company, speedy):
210
+ (
211
+ accounts,
212
+ sign,
213
+ specific_domain,
214
+ specific_tooltip,
215
+ ) = self._prepare_cell_data_customer_debt(company, speedy)
216
+ specific_domain = expression.OR(
217
+ [
218
+ [("date_maturity", "=", False)],
219
+ [("date_maturity", "<", speedy["today"])],
220
+ [
221
+ ("date_maturity", "=", speedy["today"]),
222
+ ("journal_id.type", "!=", "sale"),
223
+ ],
224
+ ]
225
+ )
226
+ specific_tooltip = _("with due date before %s") % format_date(
227
+ self.env, speedy["today"]
228
+ )
229
+ return (accounts, sign, specific_domain, specific_tooltip)
230
+
231
+ def _prepare_cell_data(self, company, speedy):
232
+ """Inherit this method to change the computation of a cell type"""
233
+ self.ensure_one()
234
+ cell_type = self.cell_type
235
+ value = raw_value = tooltip = warn = False
236
+ if cell_type.endswith("lock_date"):
237
+ raw_value = company[cell_type]
238
+ value = raw_value and format_date(self.env, raw_value)
239
+ tooltip = speedy["lock_date2help"][cell_type]
240
+ if self.warn:
241
+ if not raw_value:
242
+ warn = True
243
+ elif raw_value < speedy["today"] - relativedelta(
244
+ days=self.warn_lock_date_days
245
+ ):
246
+ warn = True
247
+ else:
248
+ accounts = False
249
+ if hasattr(self, f"_prepare_cell_data_{cell_type}"):
250
+ specific_method = getattr(self, f"_prepare_cell_data_{cell_type}")
251
+ accounts, sign, specific_domain, specific_tooltip = specific_method(
252
+ company, speedy
253
+ )
254
+ elif cell_type.startswith("income_"):
255
+ (
256
+ accounts,
257
+ sign,
258
+ specific_domain,
259
+ specific_tooltip,
260
+ ) = self._prepare_cell_data_income(company, speedy)
261
+ if accounts:
262
+ domain = (specific_domain or []) + [
263
+ ("company_id", "=", company.id),
264
+ ("account_id", "in", accounts.ids),
265
+ ("date", "<=", speedy["today"]),
266
+ ("parent_state", "=", "posted"),
267
+ ]
268
+ rg_res = self.env["account.move.line"]._read_group(
269
+ domain, ["balance:sum"], []
270
+ )
271
+ assert sign in (1, -1)
272
+ raw_value = (rg_res and rg_res[0].get("balance") or 0) * sign
273
+ value = format_amount(self.env, raw_value, company.currency_id)
274
+ tooltip = _(
275
+ "Balance of account(s) %(account_codes)s%(specific)s.",
276
+ account_codes=", ".join(accounts.mapped("code")),
277
+ specific=specific_tooltip and f" {specific_tooltip}" or "",
278
+ )
279
+ res = {
280
+ "cell_type": cell_type,
281
+ "label": self.custom_label or speedy["cell_type2label"][cell_type],
282
+ "raw_value": raw_value,
283
+ "value": value or _("None"),
284
+ "tooltip": self.custom_tooltip or tooltip,
285
+ "warn": warn,
286
+ }
287
+ return res
288
+
289
+ def _update_cell_warn(self, cell_data):
290
+ self.ensure_one()
291
+ if (
292
+ not cell_data.get("warn")
293
+ and self.warn
294
+ and self.warn_type
295
+ and isinstance(cell_data["raw_value"], (int | float))
296
+ ):
297
+ raw_value = cell_data["raw_value"]
298
+ if (
299
+ (self.warn_type == "under" and raw_value < self.warn_min)
300
+ or (self.warn_type == "above" and raw_value > self.warn_max)
301
+ or (
302
+ self.warn_type == "outside"
303
+ and (raw_value < self.warn_min or raw_value > self.warn_max)
304
+ )
305
+ or (
306
+ self.warn_type == "inside"
307
+ and raw_value > self.warn_min
308
+ and raw_value < self.warn_max
309
+ )
310
+ ):
311
+ cell_data["warn"] = True
312
+
313
+ @api.depends("cell_type", "custom_label")
314
+ def _compute_display_name(self):
315
+ type2name = dict(
316
+ self.fields_get("cell_type", "selection")["cell_type"]["selection"]
317
+ )
318
+ for cell in self:
319
+ display_name = "-"
320
+ if cell.custom_label:
321
+ display_name = cell.custom_label
322
+ elif cell.cell_type:
323
+ display_name = type2name[cell.cell_type]
324
+ cell.display_name = display_name
@@ -0,0 +1,28 @@
1
+ # Copyright 2025 Akretion France (https://www.akretion.com/)
2
+ # @author: Alexis de Lattre <alexis.delattre@akretion.com>
3
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4
+
5
+ # I create default cells via post-install script instead of
6
+ # data/account_dashboard_banner_cell.xml
7
+ # to avoid the problem when a user deletes a cell that has an XMLID
8
+ # and Odoo would re-create the cells when the module is reloaded
9
+
10
+ from odoo import SUPERUSER_ID, api
11
+
12
+
13
+ def create_default_account_dashboard_cells(cr, registry):
14
+ env = api.Environment(cr, SUPERUSER_ID, {})
15
+ vals_list = [
16
+ {"cell_type": "fiscalyear_lock_date", "sequence": 10, "warn": True},
17
+ {"cell_type": "income_fiscalyear", "sequence": 20},
18
+ {"cell_type": "customer_overdue", "sequence": 30},
19
+ {"cell_type": "customer_debt", "sequence": 40},
20
+ {"cell_type": "supplier_debt", "sequence": 50},
21
+ {
22
+ "cell_type": "liquidity",
23
+ "sequence": 60,
24
+ "warn": True,
25
+ "warn_type": "under",
26
+ },
27
+ ]
28
+ env["account.dashboard.banner.cell"].create(vals_list)
@@ -0,0 +1,33 @@
1
+ Go to the menu **Invoicing \> Configuration \> Dashboard \> Dashboard
2
+ Banner Cells**: in this menu, you can add or remove cells from the
3
+ banner, change the cell type, modify the order via drag-and-drop,
4
+ customize the labels if necessary.
5
+
6
+ ![Cell configuration menu](../static/description/banner_cell_config.png)
7
+
8
+ Many cell types are available:
9
+
10
+ - **Income** with 4 options: *Fiscal Year-to-date Income*, *Year-to-date
11
+ Income*, *Quarter-to-date Income* and *Month-to-date Income*. It
12
+ displays the period balance of the accounts with type *Income* and
13
+ *Other Income*.
14
+ - **Liquidity**: it display the ending balance of the accounts linked to
15
+ a bank or cash or credit journal.
16
+ - **Customer Debt**: it displays the ending balance of the default
17
+ *Account Receivable* and, if the point of sale is installed, the
18
+ intermediary account used for unidentified customers.
19
+ - **Customer Overdue**: same as the *Customer Debt*, but limited to
20
+ journal items with a due date in the past.
21
+ - **Supplier Debt**: it displays the ending balance of the default
22
+ *Account Payable*.
23
+ - **Lock dates**: all the lock dates are available: *Tax Return Lock
24
+ Date*, *Journals Entries Lock Date* and *All Users Lock Date*.
25
+
26
+ The module is designed to allow the modification of the computation of
27
+ the different cell types by inheriting the method
28
+ *\_prepare_cell_data_<cell_type>()* or *\_prepare_cell_data()*.
29
+ It is also easy for a developper to add more cell types.
30
+
31
+ It is possible to display a cell as a warning cell (yellow background color instead of light grey): click on the *Warning* option and customize the conditions to trigger the warning.
32
+
33
+ ![Cell form with warning](../static/description/cell_form_with_warning.png)
@@ -0,0 +1 @@
1
+ - Alexis de Lattre \<<alexis.delattre@akretion.com>\>
@@ -0,0 +1,13 @@
1
+ The development of this module started with a simple analysis: accountants
2
+ tend to forget to update the lock dates. Part of the problem lies in the
3
+ fact that the lock dates can be seen in the wizard to update the lock
4
+ dates, but accountants have no incentive to start this wizard regularly
5
+ to check the lock dates. The idea was to display the lock dates in a
6
+ banner at the top of the accounting dashboard, so that accountants
7
+ have the value of the lock dates in front of their eyes.
8
+
9
+ With such a banner in the accounting dashboard to display the lock
10
+ dates, there was a great temptation to display other interesting
11
+ information in that banner, such as some key figures of the accounting:
12
+ liquidity, turnover, customer overdue, etc. It gave birth to the idea to
13
+ have a configurable banner in the accounting dashboard!
@@ -0,0 +1,3 @@
1
+ Enjoy the accounting dashboard with the banner at the top:
2
+
3
+ ![Accounting dashboard with banner](../static/description/account_dashboard_banner.png)