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.
- odoo/addons/account_dashboard_banner/README.rst +151 -0
- odoo/addons/account_dashboard_banner/__init__.py +2 -0
- odoo/addons/account_dashboard_banner/__manifest__.py +29 -0
- odoo/addons/account_dashboard_banner/i18n/account_dashboard_banner.pot +244 -0
- odoo/addons/account_dashboard_banner/i18n/fr.po +270 -0
- odoo/addons/account_dashboard_banner/models/__init__.py +1 -0
- odoo/addons/account_dashboard_banner/models/account_dashboard_banner_cell.py +324 -0
- odoo/addons/account_dashboard_banner/post_install.py +28 -0
- odoo/addons/account_dashboard_banner/readme/CONFIGURE.md +33 -0
- odoo/addons/account_dashboard_banner/readme/CONTRIBUTORS.md +1 -0
- odoo/addons/account_dashboard_banner/readme/DESCRIPTION.md +13 -0
- odoo/addons/account_dashboard_banner/readme/USAGE.md +3 -0
- odoo/addons/account_dashboard_banner/security/ir.model.access.csv +4 -0
- odoo/addons/account_dashboard_banner/static/description/account_dashboard_banner.png +0 -0
- odoo/addons/account_dashboard_banner/static/description/banner_cell_config.png +0 -0
- odoo/addons/account_dashboard_banner/static/description/cell_form_with_warning.png +0 -0
- odoo/addons/account_dashboard_banner/static/description/icon.png +0 -0
- odoo/addons/account_dashboard_banner/static/description/index.html +483 -0
- odoo/addons/account_dashboard_banner/static/src/views/account_dashboard_kanban_banner.esm.js +47 -0
- odoo/addons/account_dashboard_banner/static/src/views/account_dashboard_kanban_banner.xml +49 -0
- odoo/addons/account_dashboard_banner/tests/__init__.py +1 -0
- odoo/addons/account_dashboard_banner/tests/test_banner.py +56 -0
- odoo/addons/account_dashboard_banner/views/account_dashboard_banner_cell.xml +92 -0
- odoo/addons/account_dashboard_banner/views/account_journal_dashboard.xml +19 -0
- odoo_addon_account_dashboard_banner-16.0.1.0.0.4.dist-info/METADATA +168 -0
- odoo_addon_account_dashboard_banner-16.0.1.0.0.4.dist-info/RECORD +28 -0
- odoo_addon_account_dashboard_banner-16.0.1.0.0.4.dist-info/WHEEL +5 -0
- 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
|
+

|
|
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
|
+

|
|
@@ -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!
|