add spreadsheet_dashboard_pos_cooperative & pos_dashboard_cooperative

This commit is contained in:
Luis 2026-06-05 19:55:00 +02:00
parent 86101218e9
commit 5247938b86
34 changed files with 7974 additions and 0 deletions

View file

@ -0,0 +1 @@
* Criptomart <https://criptomart.net>

View file

@ -0,0 +1,23 @@
Publishes spreadsheet-based dashboards for cooperative supermarkets
in Odoo's *Dashboards* menu, under a dedicated *Cooperative* group.
The dashboards consume the data prepared by ``pos_dashboard_cooperative``
(extended ``report.pos.order`` with member-at-date flag, category
hierarchy and time dimensions). They use Odoo's o-spreadsheet engine,
the same as Odoo Enterprise's official ``spreadsheet_dashboard_*``
modules.
Four dashboards ship out of the box:
* **POS Overview** — headline KPIs (Sales, Orders, Average ticket,
Margin) and monthly evolution.
* **POS Members** — members vs non-members comparison, monthly
evolution chart, TOP 50 buying members.
* **POS Categories** — members contribution KPIs and TOP 10 families,
subfamilies and products.
* **POS Sales by Time** — sales by day of the week and hour of the
day.
Each dashboard is a small hand-authored JSON file. They can be
redesigned visually inside o-spreadsheet (with ``spreadsheet_oca``
installed) and re-exported; see USAGE for the workflow.

View file

@ -0,0 +1,38 @@
* Period-over-period comparisons on the scorecards (the BI brief shows
"X% desde el último periodo" arrows on every KPI). In o-spreadsheet
this is wired through ``baseline`` references plus a comparison
range on the date global filter. Postponed until the four dashboards
are validated by the customer in production.
* Quarter-level selection (*Q1..Q4*) on the ``Period`` filter. The
o-spreadsheet date filter in Odoo 16 only accepts a single
``rangeType`` per filter; we picked ``month`` because it is the
finest granularity. A second date filter with ``rangeType:
"quarter"`` would coexist visually but duplicate the year picker,
which the customer rejected. Revisit in Odoo 17 where the filter
UI allows choosing the granularity dynamically.
* Rolling-window date filter (*Last 30 / 90 / 180 days*) as an
alternative to the month picker. Same limitation as above (one
``rangeType`` per filter).
* "Sales concentration" KPI on the Categories dashboard: share of
sales captured by the top 20% of buying members. The Top buyers
pivot already exists in the Members dashboard; only an aggregation
formula plus a scorecard would be needed.
* Stricter definition of **Members penetration** as ``distinct member
buyers / total members in the cooperative`` (instead of the current
``/ total buyers in the period``). Requires sourcing the total from
``share.line`` rather than from ``report.pos.order``.
* Translate ``spreadsheet.dashboard.name`` / ``spreadsheet.dashboard.group.name``
so the entries in the *Dashboards* menu render in the user's
language (currently always English: *POS Overview*, *POS Members*,
etc.). Blocked upstream: the core ``name`` fields are not declared
``translate=True``. Either patch the core fields downstream or rename
the records to the customer's primary language in the XML.
* Add Galician, Catalan, French, Dutch and Croatian translations.
Spanish (``es.po``) ships out of the box. The other locales can be
produced from the same ``.pot`` by translating each ``msgstr``.

View file

@ -0,0 +1,149 @@
Open *Dashboards → Cooperative* in the Odoo backend. Four dashboards
are published:
The workbook content (titles, KPI labels, table headers, scorecard
descriptions) is available in **English (source) and Spanish** out of
the box. Users with ``lang = es_ES`` will see all in-cell texts in
Spanish; the rest of the locales can be added by translating the
``i18n/spreadsheet_dashboard_pos_cooperative.pot`` template.
Note: the dashboard names shown in the *Dashboards* menu sidebar
(*POS Overview*, *POS Members*, *POS Categories*, *POS Sales by
Time*) are not translatable because ``spreadsheet.dashboard.name`` is
not declared as a translatable field in Odoo core.
POS Overview
============
Headline KPIs and monthly evolution for the whole store.
* **Sales (€)** — sum of ``price_total`` over orders in the period.
* **Orders** — count of distinct ``order_id``.
* **Average ticket (€)** — ``Sales / Orders``.
* **Gross margin (€)** — sum of ``margin``.
* A line chart with monthly sales evolution.
POS Members
===========
Detailed comparison between cooperative members and non-members. For
each group the dashboard shows:
* Sales (€), Orders, Average ticket (€), UPT, Distinct buyers, and
(members only) Frequency.
* Monthly evolution chart with one line per group.
* TOP 50 buying members (ranked by sales), with sales, orders, units,
average ticket and UPT.
POS Categories
==============
Members contribution KPIs and the top product breakdowns.
* **Members sales (€)** — sales from orders where the customer was a
cooperative member at the order date.
* **Purchase share** — ``members sales / total sales``.
* **Members penetration** — ``distinct members buyers / distinct total
buyers`` (see "Metric definitions" below for the caveat).
* TOP 10 product families (root categories) with sales, orders, share %.
* TOP 10 product subfamilies (parent categories) with the same columns.
* TOP 10 products with sales, units, share %.
POS Sales by Time
=================
* Line chart of sales by ISO day of the week (1 = Monday, 7 = Sunday).
* Line chart of sales by hour of the day (0..23).
Global filters
==============
Every dashboard ships a ``Period`` date filter. Members, Categories
and Sales by Time also ship ``Product Category``, ``Product`` and
(Members only) ``Member`` relation filters. Each filter is wired to
every pivot and chart on the dashboard, so changing one selection
updates the whole view.
* **Period** — year picker plus a month dropdown
(January..December). Pick a year only to look at the full year
(e.g. *2026*, *2025*), or add a month to narrow the range (e.g.
*March 2026*). Defaults to the current month.
* **Product Category** — relation picker over ``product.category``.
Choose one or more categories to restrict every metric to products
of those categories. Empty = no restriction.
* **Product** — relation picker over ``product.product``. Useful to
audit a specific reference.
* **Member** — relation picker over ``res.partner``. Available only
on the Members dashboard; restricts the comparison and the TOP 50
to the selected partners.
Note: the o-spreadsheet date filter in Odoo 16 only accepts a single
``rangeType`` per filter (year, quarter or month). We chose ``month``
because it is the finest granularity the customer requested; for
year-only views, just pick a year and leave the month dropdown
unset. Rolling ranges (*Last 30 / 90 / 180 days*) and quarter
selection (*Q1..Q4*) are not available on the spreadsheet filters;
both are handled in ``pos_dashboard_cooperative`` through the
standard search view on *Point of Sale → Reporting → Cooperative
Dashboards*, which exposes the usual Odoo date filters.
Metric definitions
==================
The BI brief from the customer left several KPIs without a precise
formulation. The dashboards use the following standard retail
definitions; revise them in the JSON if your cooperative prefers a
different convention.
* **UPT** (Units per Ticket) = ``total units sold / number of
orders``. ``product_qty`` summed and divided by distinct
``order_id``.
* **Frequency** = ``number of orders / number of distinct buyers``.
Measures how many tickets the average member buyer made in the
period.
* **Average ticket (€)** = ``total sales / number of orders``. POS has
no separate "average ticket value", so this metric matches the
"Ticket medio" and "Valor medio del ticket" rows in the BI brief.
* **Purchase share %** = ``members sales / total sales``. Share of
total revenue contributed by member-driven orders.
* **Members penetration %** = ``distinct member buyers / distinct
total buyers`` *in the selected period*. Note: a stricter definition
would be ``distinct member buyers / total members in the
cooperative`` (regardless of whether they bought). That denominator
is not exposed by ``report.pos.order``; a future iteration may add a
separate KPI sourced from ``share.line``.
* **Sales concentration** (not yet rendered) would be the share of
sales captured by the top X% of buying members, typically X = 20.
Easy to add later from the same ``Top buyers`` pivot.
Iterating on the layouts
========================
Each dashboard is a ``data/files/*.json`` file shipped as a base64
binary on a ``spreadsheet.dashboard`` record. They are intentionally
small and hand-authored to be diff-friendly.
To redesign one of them visually:
1. Install ``spreadsheet_oca`` (from OCA's ``spreadsheet`` repository)
on top of this module. It provides full o-spreadsheet editing in
the backend, including saving back to ``spreadsheet.dashboard``.
2. Open the dashboard in *Dashboards → Cooperative*.
3. Edit pivots, charts, styles and global filters from the
o-spreadsheet UI. The data sources are pre-wired against
``report.pos.order`` and consume the dimensions exposed by
``pos_dashboard_cooperative``:
* ``is_member_at_date`` (boolean) — cooperative member flag at the
order date.
* ``categ_root_id`` / ``categ_parent_id`` — product family /
subfamily.
* ``hour_of_day`` / ``day_of_week`` — time distributions.
4. Export the workbook to JSON.
5. Replace the matching ``data/files/<name>.json`` and reinstall (or
upgrade) the module.
Reference: the same workflow is used by Odoo core's
``spreadsheet_dashboard_sale``, ``spreadsheet_dashboard_purchase`` and
similar modules.