odoo-addon-account-dashboard-banner 16.0.1.0.1.1__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.
- 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 +259 -0
- odoo/addons/account_dashboard_banner/i18n/fr.po +287 -0
- odoo/addons/account_dashboard_banner/i18n/it.po +274 -0
- odoo/addons/account_dashboard_banner/models/__init__.py +1 -0
- odoo/addons/account_dashboard_banner/models/account_dashboard_banner_cell.py +331 -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.1.1.dist-info/METADATA +168 -0
- odoo_addon_account_dashboard_banner-16.0.1.0.1.1.dist-info/RECORD +29 -0
- odoo_addon_account_dashboard_banner-16.0.1.0.1.1.dist-info/WHEEL +5 -0
- odoo_addon_account_dashboard_banner-16.0.1.0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,331 @@
|
|
|
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.tools import date_utils
|
|
10
|
+
from odoo.tools.misc import format_amount, format_date
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AccountDashboardBannerCell(models.Model):
|
|
14
|
+
_name = "account.dashboard.banner.cell"
|
|
15
|
+
_description = "Accounting Dashboard Banner Cell"
|
|
16
|
+
_order = "sequence, id"
|
|
17
|
+
|
|
18
|
+
sequence = fields.Integer()
|
|
19
|
+
cell_type = fields.Selection(
|
|
20
|
+
[
|
|
21
|
+
("income_fiscalyear", "Fiscal Year-to-date Income"),
|
|
22
|
+
("income_year", "Year-to-date Income"),
|
|
23
|
+
("income_quarter", "Quarter-to-date Income"),
|
|
24
|
+
("income_month", "Month-to-date Income"),
|
|
25
|
+
("liquidity", "Liquidity"),
|
|
26
|
+
("customer_debt", "Customer Debt"),
|
|
27
|
+
("customer_overdue", "Customer Overdue"),
|
|
28
|
+
("supplier_debt", "Supplier Debt"),
|
|
29
|
+
# for lock dates, the key matches exactly the field name on res.company
|
|
30
|
+
("tax_lock_date", "Tax Return Lock Date"),
|
|
31
|
+
("period_lock_date", "Journals Entries Lock Date"),
|
|
32
|
+
("fiscalyear_lock_date", "All Users Lock Date"),
|
|
33
|
+
],
|
|
34
|
+
required=True,
|
|
35
|
+
)
|
|
36
|
+
custom_label = fields.Char()
|
|
37
|
+
custom_tooltip = fields.Char()
|
|
38
|
+
warn = fields.Boolean(string="Warning")
|
|
39
|
+
warn_lock_date_days = fields.Integer(
|
|
40
|
+
compute="_compute_warn_fields", store=True, readonly=False, precompute=True
|
|
41
|
+
)
|
|
42
|
+
warn_min = fields.Float(string="Minimum")
|
|
43
|
+
warn_max = fields.Float(string="Maximum")
|
|
44
|
+
warn_type_show = fields.Boolean(
|
|
45
|
+
compute="_compute_warn_fields", store=True, precompute=True
|
|
46
|
+
)
|
|
47
|
+
warn_type = fields.Selection(
|
|
48
|
+
[
|
|
49
|
+
("under", "Under Minimum"),
|
|
50
|
+
("above", "Above Maximum"),
|
|
51
|
+
("outside", "Under Minimum or Above Maximum"),
|
|
52
|
+
("inside", "Between Minimum and Maximum"),
|
|
53
|
+
],
|
|
54
|
+
default="under",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
_sql_constraints = [
|
|
58
|
+
(
|
|
59
|
+
"warn_lock_date_days_positive",
|
|
60
|
+
"CHECK(warn_lock_date_days >= 0)",
|
|
61
|
+
"Warn if lock date is older than N days must be positive or null.",
|
|
62
|
+
)
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
@api.constrains("warn_min", "warn_max", "warn_type", "warn", "cell_type")
|
|
66
|
+
def _check_warn_config(self):
|
|
67
|
+
for cell in self:
|
|
68
|
+
if (
|
|
69
|
+
cell.cell_type
|
|
70
|
+
and not cell.cell_type.endswith("_lock_date")
|
|
71
|
+
and cell.warn
|
|
72
|
+
and cell.warn_type in ("outside", "inside")
|
|
73
|
+
and cell.warn_max <= cell.warn_min
|
|
74
|
+
):
|
|
75
|
+
cell_type2label = dict(
|
|
76
|
+
self.fields_get("cell_type", "selection")["cell_type"]["selection"]
|
|
77
|
+
)
|
|
78
|
+
raise ValidationError(
|
|
79
|
+
_(
|
|
80
|
+
"On cell '%(cell_type)s' with warning enabled, "
|
|
81
|
+
"the minimum (%(warn_min)s) must be under "
|
|
82
|
+
"the maximum (%(warn_max)s).",
|
|
83
|
+
cell_type=cell_type2label[cell.cell_type],
|
|
84
|
+
warn_min=cell.warn_min,
|
|
85
|
+
warn_max=cell.warn_max,
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
@api.model
|
|
90
|
+
def _default_warn_lock_date_days(self, cell_type):
|
|
91
|
+
defaultmap = {
|
|
92
|
+
"tax_lock_date": 61, # 2 months
|
|
93
|
+
"period_lock_date": 61, # 2 months
|
|
94
|
+
"fiscalyear_lock_date": 520, # FY final closing, 1 year + 5 months
|
|
95
|
+
}
|
|
96
|
+
return defaultmap.get(cell_type)
|
|
97
|
+
|
|
98
|
+
@api.depends("cell_type", "warn")
|
|
99
|
+
def _compute_warn_fields(self):
|
|
100
|
+
for cell in self:
|
|
101
|
+
warn_type_show = False
|
|
102
|
+
warn_lock_date_days = 0
|
|
103
|
+
if cell.cell_type and cell.warn:
|
|
104
|
+
if cell.cell_type.endswith("_lock_date"):
|
|
105
|
+
warn_lock_date_days = self._default_warn_lock_date_days(
|
|
106
|
+
cell.cell_type
|
|
107
|
+
)
|
|
108
|
+
else:
|
|
109
|
+
warn_type_show = True
|
|
110
|
+
cell.warn_type_show = warn_type_show
|
|
111
|
+
cell.warn_lock_date_days = warn_lock_date_days
|
|
112
|
+
|
|
113
|
+
@api.model
|
|
114
|
+
def get_banner_data(self):
|
|
115
|
+
"""This is the method called by the JS code that displays the banner"""
|
|
116
|
+
company = self.env.company
|
|
117
|
+
return self._prepare_banner_data(company)
|
|
118
|
+
|
|
119
|
+
def _prepare_speedy(self, company):
|
|
120
|
+
lock_date_fields = [
|
|
121
|
+
"tax_lock_date",
|
|
122
|
+
"period_lock_date",
|
|
123
|
+
"fiscalyear_lock_date",
|
|
124
|
+
]
|
|
125
|
+
speedy = {
|
|
126
|
+
"cell_type2label": dict(
|
|
127
|
+
self.fields_get("cell_type", "selection")["cell_type"]["selection"]
|
|
128
|
+
),
|
|
129
|
+
"lock_date2help": {
|
|
130
|
+
key: value["help"]
|
|
131
|
+
for (key, value) in company.fields_get(lock_date_fields, "help").items()
|
|
132
|
+
},
|
|
133
|
+
"today": fields.Date.context_today(self),
|
|
134
|
+
}
|
|
135
|
+
return speedy
|
|
136
|
+
|
|
137
|
+
@api.model
|
|
138
|
+
def _prepare_banner_data(self, company):
|
|
139
|
+
# The order in this list will be the display order in the banner
|
|
140
|
+
# In fact, it's not a list but a dict. I tried to make it work by returning
|
|
141
|
+
# a list but it seems OWL only accepts dicts (I always get errors on lists)
|
|
142
|
+
cells = self.search([])
|
|
143
|
+
speedy = cells._prepare_speedy(company)
|
|
144
|
+
res = {}
|
|
145
|
+
seq = 0
|
|
146
|
+
for cell in cells:
|
|
147
|
+
seq += 1
|
|
148
|
+
cell_data = cell._prepare_cell_data(company, speedy)
|
|
149
|
+
cell._update_cell_warn(cell_data)
|
|
150
|
+
res[seq] = cell_data
|
|
151
|
+
# from pprint import pprint
|
|
152
|
+
# pprint(res)
|
|
153
|
+
return res
|
|
154
|
+
|
|
155
|
+
def _prepare_cell_data_liquidity(self, company, speedy):
|
|
156
|
+
self.ensure_one()
|
|
157
|
+
journals = self.env["account.journal"].search(
|
|
158
|
+
[
|
|
159
|
+
("company_id", "=", company.id),
|
|
160
|
+
("type", "in", ("bank", "cash", "credit")),
|
|
161
|
+
("default_account_id", "!=", False),
|
|
162
|
+
]
|
|
163
|
+
)
|
|
164
|
+
accounts = journals.default_account_id
|
|
165
|
+
return (accounts, 1, False, False, False)
|
|
166
|
+
|
|
167
|
+
def _prepare_cell_data_supplier_debt(self, company, speedy):
|
|
168
|
+
accounts = self.env["ir.property"]._get(
|
|
169
|
+
"property_account_payable_id", "res.partner"
|
|
170
|
+
)
|
|
171
|
+
return (accounts, -1, False, False, False)
|
|
172
|
+
|
|
173
|
+
def _prepare_cell_data_income(self, company, speedy):
|
|
174
|
+
cell_type = self.cell_type
|
|
175
|
+
accounts = self.env["account.account"].search(
|
|
176
|
+
[
|
|
177
|
+
("company_id", "=", company.id),
|
|
178
|
+
("account_type", "in", ("income", "income_other")),
|
|
179
|
+
]
|
|
180
|
+
)
|
|
181
|
+
if cell_type == "income_fiscalyear":
|
|
182
|
+
start_date, end_date = date_utils.get_fiscal_year(
|
|
183
|
+
speedy["today"],
|
|
184
|
+
day=company.fiscalyear_last_day,
|
|
185
|
+
month=int(company.fiscalyear_last_month),
|
|
186
|
+
)
|
|
187
|
+
elif cell_type == "income_month":
|
|
188
|
+
start_date = speedy["today"] + relativedelta(day=1)
|
|
189
|
+
elif cell_type == "income_year":
|
|
190
|
+
start_date = speedy["today"] + relativedelta(day=1, month=1)
|
|
191
|
+
elif cell_type == "income_quarter":
|
|
192
|
+
month_start_quarter = 3 * ((speedy["today"].month - 1) // 3) + 1
|
|
193
|
+
start_date = speedy["today"] + relativedelta(
|
|
194
|
+
day=1, month=month_start_quarter
|
|
195
|
+
)
|
|
196
|
+
specific_domain = [("date", ">=", start_date)]
|
|
197
|
+
specific_tooltip = _(
|
|
198
|
+
"Balance of account(s) {account_codes} since %s.",
|
|
199
|
+
format_date(self.env, start_date),
|
|
200
|
+
)
|
|
201
|
+
return (accounts, -1, specific_domain, False, specific_tooltip)
|
|
202
|
+
|
|
203
|
+
def _prepare_cell_data_customer_debt(self, company, speedy):
|
|
204
|
+
accounts = self.env["ir.property"]._get(
|
|
205
|
+
"property_account_receivable_id", "res.partner"
|
|
206
|
+
)
|
|
207
|
+
if (
|
|
208
|
+
hasattr(company, "account_default_pos_receivable_account_id")
|
|
209
|
+
and company.account_default_pos_receivable_account_id
|
|
210
|
+
):
|
|
211
|
+
accounts |= company.account_default_pos_receivable_account_id
|
|
212
|
+
return (accounts, 1, False, False, False)
|
|
213
|
+
|
|
214
|
+
def _prepare_cell_data_customer_overdue(self, company, speedy):
|
|
215
|
+
(
|
|
216
|
+
accounts,
|
|
217
|
+
sign,
|
|
218
|
+
specific_domain,
|
|
219
|
+
specific_aggregate,
|
|
220
|
+
specific_tooltip,
|
|
221
|
+
) = self._prepare_cell_data_customer_debt(company, speedy)
|
|
222
|
+
specific_domain = [("date_maturity", "<", speedy["today"])]
|
|
223
|
+
specific_aggregate = "amount_residual:sum"
|
|
224
|
+
specific_tooltip = _(
|
|
225
|
+
"Residual amount of account(s) {account_codes} with due date in the past."
|
|
226
|
+
)
|
|
227
|
+
return (accounts, sign, specific_domain, specific_aggregate, specific_tooltip)
|
|
228
|
+
|
|
229
|
+
def _prepare_cell_data(self, company, speedy):
|
|
230
|
+
"""Inherit this method to change the computation of a cell type"""
|
|
231
|
+
self.ensure_one()
|
|
232
|
+
cell_type = self.cell_type
|
|
233
|
+
value = raw_value = tooltip = warn = False
|
|
234
|
+
if cell_type.endswith("lock_date"):
|
|
235
|
+
raw_value = company[cell_type]
|
|
236
|
+
value = raw_value and format_date(self.env, raw_value)
|
|
237
|
+
tooltip = speedy["lock_date2help"][cell_type]
|
|
238
|
+
if self.warn:
|
|
239
|
+
if not raw_value:
|
|
240
|
+
warn = True
|
|
241
|
+
elif raw_value < speedy["today"] - relativedelta(
|
|
242
|
+
days=self.warn_lock_date_days
|
|
243
|
+
):
|
|
244
|
+
warn = True
|
|
245
|
+
else:
|
|
246
|
+
accounts = False
|
|
247
|
+
if hasattr(self, f"_prepare_cell_data_{cell_type}"):
|
|
248
|
+
specific_method = getattr(self, f"_prepare_cell_data_{cell_type}")
|
|
249
|
+
(
|
|
250
|
+
accounts,
|
|
251
|
+
sign,
|
|
252
|
+
specific_domain,
|
|
253
|
+
specific_aggregate,
|
|
254
|
+
specific_tooltip,
|
|
255
|
+
) = specific_method(company, speedy)
|
|
256
|
+
elif cell_type.startswith("income_"):
|
|
257
|
+
(
|
|
258
|
+
accounts,
|
|
259
|
+
sign,
|
|
260
|
+
specific_domain,
|
|
261
|
+
specific_aggregate,
|
|
262
|
+
specific_tooltip,
|
|
263
|
+
) = self._prepare_cell_data_income(company, speedy)
|
|
264
|
+
if accounts:
|
|
265
|
+
domain = (specific_domain or []) + [
|
|
266
|
+
("company_id", "=", company.id),
|
|
267
|
+
("account_id", "in", accounts.ids),
|
|
268
|
+
("date", "<=", speedy["today"]),
|
|
269
|
+
("parent_state", "=", "posted"),
|
|
270
|
+
]
|
|
271
|
+
aggregate = specific_aggregate or "balance:sum"
|
|
272
|
+
rg_res = self.env["account.move.line"]._read_group(
|
|
273
|
+
domain, [aggregate], []
|
|
274
|
+
)
|
|
275
|
+
assert sign in (1, -1)
|
|
276
|
+
raw_value = (
|
|
277
|
+
rg_res and rg_res[0].get(aggregate.split(":")[0]) or 0
|
|
278
|
+
) * sign
|
|
279
|
+
value = format_amount(self.env, raw_value, company.currency_id)
|
|
280
|
+
tooltip_src = specific_tooltip or _(
|
|
281
|
+
"Balance of account(s) {account_codes}."
|
|
282
|
+
)
|
|
283
|
+
tooltip = tooltip_src.format(
|
|
284
|
+
account_codes=", ".join(accounts.mapped("code"))
|
|
285
|
+
)
|
|
286
|
+
res = {
|
|
287
|
+
"cell_type": cell_type,
|
|
288
|
+
"label": self.custom_label or speedy["cell_type2label"][cell_type],
|
|
289
|
+
"raw_value": raw_value,
|
|
290
|
+
"value": value or _("None"),
|
|
291
|
+
"tooltip": self.custom_tooltip or tooltip,
|
|
292
|
+
"warn": warn,
|
|
293
|
+
}
|
|
294
|
+
return res
|
|
295
|
+
|
|
296
|
+
def _update_cell_warn(self, cell_data):
|
|
297
|
+
self.ensure_one()
|
|
298
|
+
if (
|
|
299
|
+
not cell_data.get("warn")
|
|
300
|
+
and self.warn
|
|
301
|
+
and self.warn_type
|
|
302
|
+
and isinstance(cell_data["raw_value"], (int | float))
|
|
303
|
+
):
|
|
304
|
+
raw_value = cell_data["raw_value"]
|
|
305
|
+
if (
|
|
306
|
+
(self.warn_type == "under" and raw_value < self.warn_min)
|
|
307
|
+
or (self.warn_type == "above" and raw_value > self.warn_max)
|
|
308
|
+
or (
|
|
309
|
+
self.warn_type == "outside"
|
|
310
|
+
and (raw_value < self.warn_min or raw_value > self.warn_max)
|
|
311
|
+
)
|
|
312
|
+
or (
|
|
313
|
+
self.warn_type == "inside"
|
|
314
|
+
and raw_value > self.warn_min
|
|
315
|
+
and raw_value < self.warn_max
|
|
316
|
+
)
|
|
317
|
+
):
|
|
318
|
+
cell_data["warn"] = True
|
|
319
|
+
|
|
320
|
+
@api.depends("cell_type", "custom_label")
|
|
321
|
+
def _compute_display_name(self):
|
|
322
|
+
type2name = dict(
|
|
323
|
+
self.fields_get("cell_type", "selection")["cell_type"]["selection"]
|
|
324
|
+
)
|
|
325
|
+
for cell in self:
|
|
326
|
+
display_name = "-"
|
|
327
|
+
if cell.custom_label:
|
|
328
|
+
display_name = cell.custom_label
|
|
329
|
+
elif cell.cell_type:
|
|
330
|
+
display_name = type2name[cell.cell_type]
|
|
331
|
+
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!
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|
2
|
+
access_account_dashboard_banner_cell_manager,Full access on account.dashboard.banner.cell,model_account_dashboard_banner_cell,account.group_account_manager,1,1,1,1
|
|
3
|
+
access_account_dashboard_banner_cell_user,Read access on account.dashboard.banner.cell,model_account_dashboard_banner_cell,account.group_account_user,1,0,0,0
|
|
4
|
+
access_account_dashboard_banner_cell_auditor,Read access on account.dashboard.banner.cell,model_account_dashboard_banner_cell,account.group_account_readonly,1,0,0,0
|
|
Binary file
|
|
Binary file
|
|
Binary file
|