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,29 @@
# POS Dashboard Cooperative
Real-time BI dashboards for cooperative supermarkets, built on top of
Point of Sale data.
This module extends `report.pos.order` with three new dimensions:
| Field | Description |
|---|---|
| `is_member_at_date` | True if the customer held a `share.line` whose `effective_date` was on or before the order date. |
| `categ_root_id` | Product family (root of `product.category` parent_path). |
| `categ_parent_id` | Product subfamily (parent of the leaf category). |
| `date_day` | Order date truncated to day. |
| `hour_of_day` | Hour of the order (`EXTRACT(HOUR FROM date_order)`). |
| `day_of_week` | Day of week, 1 = Monday … 7 = Sunday (ISO). |
Pre-configured menus under *Point of Sale → Reporting → Cooperative
Dashboards* cover overview, members vs. non-members, categories, top
products, and time distributions. Four tiles surface the headline KPIs
on the `web_dashboard_tile` board.
For spreadsheet dashboards published in Odoo's *Dashboards* menu, install
the companion module `spreadsheet_dashboard_pos_cooperative`.
See `readme/` for the OCA-style structured documentation.
## License
AGPL-3.0 or later.

View file

@ -0,0 +1 @@
from . import report

View file

@ -0,0 +1,27 @@
# Copyright 2026 Criptomart
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "POS Dashboard Cooperative",
"version": "16.0.1.0.0",
"summary": "Real-time BI dashboards for cooperative supermarkets (POS)",
"license": "AGPL-3",
"author": "Criptomart, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/pos",
"category": "Point of Sale",
"depends": [
"point_of_sale",
"cooperator",
"pos_margin",
"web_dashboard_tile",
"web_pivot_computed_measure",
],
"data": [
"report/pos_order_report_views.xml",
"views/pos_order_report_member_views.xml",
"views/pos_order_report_category_views.xml",
"views/pos_order_report_time_views.xml",
"views/dashboard_tiles.xml",
"views/menus.xml",
],
"installable": True,
}

View file

@ -0,0 +1,151 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_dashboard_cooperative
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-06-05 12:00+0000\n"
"Last-Translator: Criptomart\n"
"Language-Team: Spanish\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_by_day_of_week
msgid "Sales by Day of Week"
msgstr "Ventas por día de la semana"
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_by_hour
msgid "Sales by Hour of Day"
msgstr "Ventas por hora del día"
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_categories
msgid "Sales by Category"
msgstr "Ventas por categoría"
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_members
msgid "Members vs Non-members"
msgstr "Socios vs no socios"
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_overview
msgid "Cooperative POS Overview"
msgstr "Vista general TPV cooperativa"
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_top_members
msgid "Top Member Buyers"
msgstr "Top socias compradoras"
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_top_products
msgid "Top Products"
msgstr "Top productos"
#. module: pos_dashboard_cooperative
#: model:ir.model,name:pos_dashboard_cooperative.model_report_pos_order
msgid "Point of Sale Orders Report"
msgstr "Informe de pedidos del TPV"
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__categ_parent_id
msgid "Product Subfamily (parent)"
msgstr "Subfamilia de producto (padre)"
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__categ_root_id
msgid "Product Family (root)"
msgstr "Familia de producto (raíz)"
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__date_day
msgid "Order Day"
msgstr "Día del pedido"
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__day_of_week
msgid "Day of Week (1=Mon, 7=Sun)"
msgstr "Día de la semana (1=lun, 7=dom)"
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__hour_of_day
msgid "Hour of Day"
msgstr "Hora del día"
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__is_member_at_date
msgid "Member at Order Date"
msgstr "Socio en la fecha del pedido"
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_by_day
msgid "By Day of Week"
msgstr "Por día de la semana"
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_by_hour
msgid "By Hour of Day"
msgstr "Por hora del día"
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_categories
msgid "By Category"
msgstr "Por categoría"
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_cooperative
msgid "Cooperative Dashboards"
msgstr "Tableros de la cooperativa"
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_members
msgid "Members vs Non-members"
msgstr "Socios vs no socios"
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_overview
msgid "Overview"
msgstr "Vista general"
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_top_members
msgid "Top Members"
msgstr "Top socias"
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_top_products
msgid "Top Products"
msgstr "Top productos"
#. module: pos_dashboard_cooperative
#: model:tile.category,name:pos_dashboard_cooperative.tile_category_cooperative
msgid "Cooperative POS"
msgstr "TPV cooperativa"
#. module: pos_dashboard_cooperative
#: model:tile.tile,name:pos_dashboard_cooperative.tile_pos_margin_last_30_days
msgid "Margin last 30 days (€)"
msgstr "Margen últimos 30 días (€)"
#. module: pos_dashboard_cooperative
#: model:tile.tile,name:pos_dashboard_cooperative.tile_pos_sales_last_30_days
msgid "Sales last 30 days (€)"
msgstr "Ventas últimos 30 días (€)"
#. module: pos_dashboard_cooperative
#: model:tile.tile,name:pos_dashboard_cooperative.tile_pos_sales_members_last_30_days
msgid "Members sales last 30 days (€)"
msgstr "Ventas a socias últimos 30 días (€)"
#. module: pos_dashboard_cooperative
#: model:tile.tile,name:pos_dashboard_cooperative.tile_pos_sales_non_members_last_30_days
msgid "Non-members sales last 30 days (€)"
msgstr "Ventas a no socias últimos 30 días (€)"

View file

@ -0,0 +1,149 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_dashboard_cooperative
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_by_day_of_week
msgid "Sales by Day of Week"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_by_hour
msgid "Sales by Hour of Day"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_categories
msgid "Sales by Category"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_members
msgid "Members vs Non-members"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_overview
msgid "Cooperative POS Overview"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_top_members
msgid "Top Member Buyers"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.actions.act_window,name:pos_dashboard_cooperative.action_pos_dashboard_top_products
msgid "Top Products"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.model,name:pos_dashboard_cooperative.model_report_pos_order
msgid "Point of Sale Orders Report"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__categ_parent_id
msgid "Product Subfamily (parent)"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__categ_root_id
msgid "Product Family (root)"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__date_day
msgid "Order Day"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__day_of_week
msgid "Day of Week (1=Mon, 7=Sun)"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__hour_of_day
msgid "Hour of Day"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.model.fields,field_description:pos_dashboard_cooperative.field_report_pos_order__is_member_at_date
msgid "Member at Order Date"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_by_day
msgid "By Day of Week"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_by_hour
msgid "By Hour of Day"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_categories
msgid "By Category"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_cooperative
msgid "Cooperative Dashboards"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_members
msgid "Members vs Non-members"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_overview
msgid "Overview"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_top_members
msgid "Top Members"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:ir.ui.menu,name:pos_dashboard_cooperative.menu_pos_dashboard_top_products
msgid "Top Products"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:tile.category,name:pos_dashboard_cooperative.tile_category_cooperative
msgid "Cooperative POS"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:tile.tile,name:pos_dashboard_cooperative.tile_pos_margin_last_30_days
msgid "Margin last 30 days (€)"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:tile.tile,name:pos_dashboard_cooperative.tile_pos_sales_last_30_days
msgid "Sales last 30 days (€)"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:tile.tile,name:pos_dashboard_cooperative.tile_pos_sales_members_last_30_days
msgid "Members sales last 30 days (€)"
msgstr ""
#. module: pos_dashboard_cooperative
#: model:tile.tile,name:pos_dashboard_cooperative.tile_pos_sales_non_members_last_30_days
msgid "Non-members sales last 30 days (€)"
msgstr ""

View file

@ -0,0 +1,7 @@
No specific configuration is required after installation. The module
auto-installs ``pos_margin``, ``web_dashboard_tile`` and
``web_pivot_computed_measure`` if they are not already present.
To control who sees the new dashboards, adjust the standard POS
security groups (``point_of_sale.group_pos_manager``) — the new analyses
inherit the access rules already in place for ``report.pos.order``.

View file

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

View file

@ -0,0 +1,24 @@
Real-time Business Intelligence dashboards for cooperative supermarkets
built on top of Point of Sale data. Extends the standard
``report.pos.order`` view to expose new dimensions that the standard POS
analysis lacks:
* **Cooperative member dimension** — ``is_member_at_date`` flags each
ticket line according to the customer's share status *at the order
date*, so historic comparisons (members vs. non-members) reflect the
reality of the moment.
* **Category hierarchy** — ``categ_root_id`` (family) and
``categ_parent_id`` (subfamily) derived from
``product.category.parent_path``, ready to drive drilldowns.
* **Time dimensions** — ``date_day``, ``hour_of_day``, ``day_of_week``
for daily/weekly/hourly distributions.
Pre-configured menu entries under *Point of Sale → Reporting →
Cooperative Dashboards* cover overview, members vs. non-members,
categories, top products, day-of-week and hour-of-day analyses. Four
dashboard tiles surface the most important KPIs on the
``web_dashboard_tile`` board.
The companion module ``spreadsheet_dashboard_pos_cooperative`` publishes
spreadsheet dashboards in Odoo's *Dashboards* menu that consume the
same data.

View file

@ -0,0 +1,12 @@
* Materialize the SQL view into a regular table populated by a nightly
cron, in case real-time aggregation becomes too slow on very large
POS datasets.
* Refine the ``is_member_at_date`` calculation by consuming
``subscription.register`` to subtract sell-back and transfer
operations from the share balance. The current implementation simply
checks for the existence of a ``share.line`` dated on or before the
order date, which over-reports memberships when shares are returned.
* Add multi-company test coverage once a clean multi-company test
fixture is available.
* Add Galician, Catalan, French, Dutch and Croatian translations
(currently shipped with Spanish only).

View file

@ -0,0 +1,39 @@
Open *Point of Sale → Reporting → Cooperative Dashboards* and pick one
of the predefined analyses:
* **Overview** — sales, margin and order count grouped by month.
* **Members vs Non-members** — same axes split by cooperative
membership at the order date.
* **Top Members** — sales aggregated per cooperative member, sort by
total to obtain the top buyers.
* **By Category** — sales by product family (root category); drill down
to subfamily and product from the *Group By* menu.
* **Top Products** — sales per product.
* **By Day of Week / By Hour of Day** — temporal distributions.
In every view, additional dimensions are available from the *Filters*
and *Group By* menus: ``Members``, ``Non members``, ``Member``,
``Product Family``, ``Product Subfamily``, ``Day of Week``,
``Hour of Day``.
Time dimensions
===============
* ``day_of_week`` is the ISO day number (``EXTRACT(ISODOW FROM
date_order)``): **1 = Monday, 2 = Tuesday, 3 = Wednesday, 4 =
Thursday, 5 = Friday, 6 = Saturday, 7 = Sunday**.
* ``hour_of_day`` is the integer hour in the order's timezone
(``EXTRACT(HOUR FROM date_order)``), 0..23.
These are deliberately exposed as integers rather than translated
labels so they aggregate cleanly in pivots, charts and spreadsheet
formulas. Map the numbers to weekday names in the consumer (for
example, in the o-spreadsheet dashboards) if a localized label is
needed.
Pivot views support the ratios defined by ``web_pivot_computed_measure``
(e.g. average ticket = total sales / order count). Click *Insert
Computed Measure* in the pivot's *Measures* menu.
For curated dashboards rendered in Odoo's *Dashboards* menu, install
the companion module ``spreadsheet_dashboard_pos_cooperative``.

View file

@ -0,0 +1 @@
from . import pos_order_report

View file

@ -0,0 +1,130 @@
# Copyright 2026 Criptomart
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class PosOrderReport(models.Model):
_inherit = "report.pos.order"
# --- Time dimensions ----------------------------------------------------
date_day = fields.Date(
string="Order Day",
readonly=True,
)
hour_of_day = fields.Integer(
string="Hour of Day",
readonly=True,
group_operator=None,
)
day_of_week = fields.Integer(
string="Day of Week (1=Mon, 7=Sun)",
readonly=True,
group_operator=None,
)
# --- Category hierarchy -------------------------------------------------
# Family = root category, subfamily = direct parent of categ_id.
# Computed from product.category.parent_path (materialized path
# "1/3/7/"), which is maintained automatically by Odoo (_parent_store).
categ_root_id = fields.Many2one(
"product.category",
string="Product Family (root)",
readonly=True,
)
categ_root_id_int = fields.Integer(
string="Product Family ID (root)",
readonly=True,
group_operator=None,
)
categ_parent_id = fields.Many2one(
"product.category",
string="Product Subfamily (parent)",
readonly=True,
)
categ_parent_id_int = fields.Integer(
string="Product Subfamily ID (parent)",
readonly=True,
group_operator=None,
)
product_categ_id_int = fields.Integer(
string="Product Category ID (leaf)",
readonly=True,
group_operator=None,
)
product_id_int = fields.Integer(
string="Product ID",
readonly=True,
group_operator=None,
)
# --- Cooperative member dimension --------------------------------------
# True when the customer of the ticket held at least one share line whose
# effective_date was on or before the order date, in the same company.
#
# NOTE: this is an approximation. cooperator does not store an end date
# on share.line; share buy-backs and transfers are recorded only in
# subscription.register. A future version may refine the calculation by
# consuming subscription.register to substract sell_back/transfer
# operations, at the cost of a more expensive subquery.
is_member_at_date = fields.Boolean(
string="Member at Order Date",
readonly=True,
)
def _select(self):
return (
super()._select()
+ """,
s.date_order::date AS date_day,
EXTRACT(HOUR FROM (s.date_order AT TIME ZONE 'UTC') AT TIME ZONE 'Europe/Madrid')::int AS hour_of_day,
EXTRACT(ISODOW FROM s.date_order)::int AS day_of_week,
cat_root.id AS categ_root_id,
cat_root.id AS categ_root_id_int,
pt_categ.parent_id AS categ_parent_id,
pt_categ.parent_id AS categ_parent_id_int,
pt.categ_id AS product_categ_id_int,
pt.id AS product_id_int,
CASE WHEN EXISTS (
SELECT 1 FROM share_line sl
WHERE sl.partner_id = s.partner_id
AND sl.company_id = s.company_id
AND sl.effective_date IS NOT NULL
AND sl.effective_date <= s.date_order::date
) THEN TRUE ELSE FALSE END AS is_member_at_date
"""
)
def _from(self):
# Extra joins to resolve:
# - pt_categ: direct category of the product (used for parent_id and
# parent_path lookup).
# - cat_root: root category, found by taking the first id in
# parent_path. parent_path looks like "1/3/7/".
return (
super()._from()
+ """
LEFT JOIN product_category pt_categ ON pt_categ.id = pt.categ_id
LEFT JOIN product_category cat_root
ON cat_root.id = NULLIF(
split_part(pt_categ.parent_path, '/', 1), ''
)::int
"""
)
def _group_by(self):
# Append non-aggregated expressions added in _select() that are not
# already in the parent GROUP BY. s.date_order is already there
# (point_of_sale.report.pos_order_report._group_by), so the time
# dimensions derived from it (date_day, hour_of_day, day_of_week)
# are functionally dependent and don't need to be listed.
# is_member_at_date is a scalar subquery; not needed either.
return (
super()._group_by()
+ """,
cat_root.id,
pt_categ.parent_id,
pt.categ_id,
pt.id
"""
)

View file

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2026 Criptomart - License AGPL-3 -->
<odoo>
<!--
Extend the core search view to expose:
- Member dimension filters (members / non-members)
- Category hierarchy group-bys (family, subfamily)
- Time dimension group-bys (day of week, hour of day)
- Filters by company (since this module is multi-company aware)
-->
<record id="report_pos_order_view_search" model="ir.ui.view">
<field name="name">report.pos.order.search.cooperative</field>
<field name="model">report.pos.order</field>
<field name="inherit_id" ref="point_of_sale.view_report_pos_order_search"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='not_invoiced']" position="after">
<separator/>
<filter
string="Members"
name="members"
domain="[('is_member_at_date', '=', True)]"
/>
<filter
string="Non members"
name="non_members"
domain="[('is_member_at_date', '=', False)]"
/>
</xpath>
<xpath expr="//group" position="inside">
<separator/>
<filter
string="Member"
name="group_member"
context="{'group_by': 'is_member_at_date'}"
/>
<filter
string="Product Family"
name="group_categ_root"
context="{'group_by': 'categ_root_id'}"
/>
<filter
string="Product Subfamily"
name="group_categ_parent"
context="{'group_by': 'categ_parent_id'}"
/>
<separator/>
<filter
string="Day of Week"
name="group_day_of_week"
context="{'group_by': 'day_of_week'}"
/>
<filter
string="Hour of Day"
name="group_hour_of_day"
context="{'group_by': 'hour_of_day'}"
/>
</xpath>
</field>
</record>
<!--
Extend the core pivot view to include the most useful measures for
a cooperative supermarket dashboard: price total (sales), margin,
margin rate and order count. Members vs non-members can then be
compared by adding the "Member" group-by from the search bar.
-->
<record id="report_pos_order_view_pivot" model="ir.ui.view">
<field name="name">report.pos.order.pivot.cooperative</field>
<field name="model">report.pos.order</field>
<field name="inherit_id" ref="point_of_sale.view_report_pos_order_pivot"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='price_total']" position="after">
<field name="margin" type="measure"/>
<field name="margin_rate" type="measure"/>
</xpath>
</field>
</record>
<!--
Extend the core tree view to expose the new dimensions as optional
columns so users can toggle them without changing the default UI.
-->
<record id="report_pos_order_view_tree" model="ir.ui.view">
<field name="name">report.pos.order.tree.cooperative</field>
<field name="model">report.pos.order</field>
<field name="inherit_id" ref="point_of_sale.report_pos_order_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='product_categ_id']" position="after">
<field name="categ_root_id" optional="hide"/>
<field name="categ_parent_id" optional="hide"/>
<field name="is_member_at_date" optional="show"/>
<field name="hour_of_day" optional="hide"/>
<field name="day_of_week" optional="hide"/>
</xpath>
</field>
</record>
<!--
Cooperative-flavoured action over report.pos.order. Defaults to a
graph view grouped by month, with the most relevant measures
pre-selected. Pivot + tree are also available.
-->
<record id="action_pos_dashboard_overview" model="ir.actions.act_window">
<field name="name">Cooperative POS Overview</field>
<field name="res_model">report.pos.order</field>
<field name="view_mode">graph,pivot,tree</field>
<field name="search_view_id"
ref="point_of_sale.view_report_pos_order_search"/>
<field name="context">{
'search_default_order_month': 1,
'group_by_no_leaf': 1,
'group_by': [],
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No data yet!
</p><p>
Sell some products at the Point of Sale to populate this
cooperative dashboard.
</p>
</field>
</record>
</odoo>

View file

@ -0,0 +1 @@
from . import test_pos_dashboard

View file

@ -0,0 +1,215 @@
# Copyright 2026 Criptomart
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from datetime import date, timedelta
import odoo
from odoo.tests.common import TransactionCase
@odoo.tests.tagged("post_install", "-at_install")
class TestPosDashboardCooperative(TransactionCase):
"""Validate the extra dimensions added by pos_dashboard_cooperative on
top of point_of_sale.report.pos.order:
- time dimensions (date_day, hour_of_day, day_of_week)
- product category hierarchy (categ_root_id, categ_parent_id)
- cooperative member historic flag (is_member_at_date)
These tests use a minimal hand-built pos.session to stay independent
from any existing data in the database and from the heavy
TestPoSCommon setup machinery, which requires a clean accounting
setup.
"""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.company = cls.env.ref("base.main_company")
# Two-level category hierarchy: family -> subfamily -> leaf
cls.family_root = cls.env["product.category"].create(
{"name": "DASHBOARD-FAMILY-ROOT"}
)
cls.subfamily = cls.env["product.category"].create(
{"name": "DASHBOARD-SUBFAMILY", "parent_id": cls.family_root.id}
)
cls.categ_leaf = cls.env["product.category"].create(
{"name": "DASHBOARD-LEAF", "parent_id": cls.subfamily.id}
)
# Product sold at the POS in every test.
cls.product = cls.env["product.product"].create(
{
"name": "DASHBOARD-PRODUCT",
"type": "consu",
"available_in_pos": True,
"list_price": 10.0,
"standard_price": 6.0,
"categ_id": cls.categ_leaf.id,
}
)
# Share product needed by cooperator.share.line.
cls.share_product = cls.env["product.product"].create(
{
"name": "DASHBOARD-SHARE",
"is_share": True,
"type": "service",
"list_price": 25.0,
}
)
# Two partners: one cooperative member (share dated in the past)
# and one non-member.
cls.member_partner = cls.env["res.partner"].create(
{"name": "DASHBOARD-MEMBER"}
)
cls.non_member_partner = cls.env["res.partner"].create(
{"name": "DASHBOARD-NON-MEMBER"}
)
cls.env["share.line"].create(
{
"partner_id": cls.member_partner.id,
"share_product_id": cls.share_product.id,
"share_number": 1,
"share_unit_price": 25.0,
"effective_date": date.today() - timedelta(days=365),
"company_id": cls.company.id,
}
)
# Reuse an existing POS config to avoid bootstrapping payment
# methods and journals from scratch. Any installed config is
# acceptable since we never go through the POS UI.
cls.pos_config = cls.env["pos.config"].search(
[("company_id", "=", cls.company.id)], limit=1
)
assert cls.pos_config, "A pos.config in the main company is required."
if not cls.pos_config.current_session_id:
cls.pos_config.open_ui()
cls.pos_session = cls.pos_config.current_session_id
# ------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------
def _create_pos_order(self, partner):
return self.env["pos.order"].create(
{
"session_id": self.pos_session.id,
"partner_id": partner.id,
"company_id": self.company.id,
"lines": [
(
0,
0,
{
"name": "DASHBOARD-OL",
"product_id": self.product.id,
"price_unit": 10.0,
"discount": 0,
"qty": 1.0,
"price_subtotal": 10.0,
"price_subtotal_incl": 10.0,
"total_cost": 6.0,
},
)
],
"amount_total": 10.0,
"amount_tax": 0.0,
"amount_paid": 0.0,
"amount_return": 0.0,
}
)
def _reports_for(self, partner):
return (
self.env["report.pos.order"]
.sudo()
.search(
[
("product_id", "=", self.product.id),
("partner_id", "=", partner.id),
],
order="id",
)
)
# ------------------------------------------------------------------
# Tests
# ------------------------------------------------------------------
def test_member_flag_for_member_partner(self):
"""Partner with a share line predating the order is flagged as
member."""
self._create_pos_order(self.member_partner)
reports = self._reports_for(self.member_partner)
self.assertTrue(reports)
self.assertTrue(reports[0].is_member_at_date)
def test_member_flag_for_non_member_partner(self):
"""Partner without any share line is not flagged as member."""
self._create_pos_order(self.non_member_partner)
reports = self._reports_for(self.non_member_partner)
self.assertTrue(reports)
self.assertFalse(reports[0].is_member_at_date)
def test_member_flag_is_historic(self):
"""Share line dated AFTER the order date does not mark the partner
as member for that order."""
future_member = self.env["res.partner"].create(
{"name": "DASHBOARD-FUTURE-MEMBER"}
)
self._create_pos_order(future_member)
self.env["share.line"].create(
{
"partner_id": future_member.id,
"share_product_id": self.share_product.id,
"share_number": 1,
"share_unit_price": 25.0,
"effective_date": date.today() + timedelta(days=30),
"company_id": self.company.id,
}
)
reports = self._reports_for(future_member)
self.assertTrue(reports)
self.assertFalse(
reports[0].is_member_at_date,
"Member flag must reflect the situation at order date.",
)
def test_category_hierarchy_is_resolved(self):
"""categ_root_id and categ_parent_id reflect the product category
ancestors derived from parent_path."""
self._create_pos_order(self.member_partner)
reports = self._reports_for(self.member_partner)
self.assertTrue(reports)
report = reports[0]
self.assertEqual(report.categ_root_id, self.family_root)
self.assertEqual(report.categ_parent_id, self.subfamily)
def test_time_dimensions_are_populated(self):
"""date_day matches order date; hour_of_day / day_of_week are in
valid ranges."""
self._create_pos_order(self.member_partner)
reports = self._reports_for(self.member_partner)
self.assertTrue(reports)
report = reports[0]
self.assertIsNotNone(report.date_day)
self.assertGreaterEqual(report.hour_of_day, 0)
self.assertLess(report.hour_of_day, 24)
self.assertGreaterEqual(report.day_of_week, 1)
self.assertLessEqual(report.day_of_week, 7)
def test_read_group_by_member_flag(self):
"""Aggregations grouped by the member flag return one row per
member status when both kinds of partners have purchased."""
self._create_pos_order(self.member_partner)
self._create_pos_order(self.non_member_partner)
results = self.env["report.pos.order"].read_group(
[("product_id", "=", self.product.id)],
["price_total", "is_member_at_date"],
["is_member_at_date"],
)
statuses = {r["is_member_at_date"] for r in results}
self.assertIn(True, statuses)
self.assertIn(False, statuses)

View file

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2026 Criptomart - License AGPL-3 -->
<odoo noupdate="1">
<!--
Tile categories shown in the dashboard.
Two categories: a "members" one (members only) and a generic one
for global sales KPIs.
-->
<record id="tile_category_cooperative" model="tile.category">
<field name="name">Cooperative POS</field>
<field name="sequence">100</field>
<field name="active" eval="True"/>
</record>
<!--
Tile: Total sales (excluding refunds).
Sums price_total over the last 30 days. Uses paid/done/invoiced
states to exclude drafts and cancellations.
NOTE on accuracy: tile.tile counts/aggregates records of the
target model. report.pos.order is at line granularity, so:
- SUM(price_total) is correctly the total amount sold.
- COUNT() would count *lines*, not tickets. To count tickets we
would need DISTINCT order_id which tile.tile does not support.
Use the pivot views for ticket-accurate metrics; tiles are
intentionally limited to monetary KPIs that aggregate by simple
SUM over lines.
-->
<record id="tile_pos_sales_last_30_days" model="tile.tile">
<field name="name">Sales last 30 days (€)</field>
<field name="sequence">10</field>
<field name="category_id" ref="tile_category_cooperative"/>
<field name="model_id" ref="point_of_sale.model_report_pos_order"/>
<field name="action_id" ref="action_pos_dashboard_overview"/>
<field name="primary_function">sum</field>
<field name="primary_field_id"
ref="point_of_sale.field_report_pos_order__price_total"/>
<field name="primary_format">{:,.2f} €</field>
<field
name="domain"
>[('state', 'in', ['paid', 'done', 'invoiced']),
('date', '&gt;=', (context_today - relativedelta(days=30)).strftime('%Y-%m-%d'))]</field>
<field name="background_color">#2E7D32</field>
<field name="font_color">#FFFFFF</field>
</record>
<record id="tile_pos_margin_last_30_days" model="tile.tile">
<field name="name">Margin last 30 days (€)</field>
<field name="sequence">20</field>
<field name="category_id" ref="tile_category_cooperative"/>
<field name="model_id" ref="point_of_sale.model_report_pos_order"/>
<field name="action_id" ref="action_pos_dashboard_overview"/>
<field name="primary_function">sum</field>
<field name="primary_field_id"
ref="point_of_sale.field_report_pos_order__margin"/>
<field name="primary_format">{:,.2f} €</field>
<field
name="domain"
>[('state', 'in', ['paid', 'done', 'invoiced']),
('date', '&gt;=', (context_today - relativedelta(days=30)).strftime('%Y-%m-%d'))]</field>
<field name="background_color">#1565C0</field>
<field name="font_color">#FFFFFF</field>
</record>
<record id="tile_pos_sales_members_last_30_days" model="tile.tile">
<field name="name">Members sales last 30 days (€)</field>
<field name="sequence">30</field>
<field name="category_id" ref="tile_category_cooperative"/>
<field name="model_id" ref="point_of_sale.model_report_pos_order"/>
<field name="action_id" ref="action_pos_dashboard_members"/>
<field name="primary_function">sum</field>
<field name="primary_field_id"
ref="point_of_sale.field_report_pos_order__price_total"/>
<field name="primary_format">{:,.2f} €</field>
<field
name="domain"
>[('state', 'in', ['paid', 'done', 'invoiced']),
('is_member_at_date', '=', True),
('date', '&gt;=', (context_today - relativedelta(days=30)).strftime('%Y-%m-%d'))]</field>
<field name="background_color">#EF6C00</field>
<field name="font_color">#FFFFFF</field>
</record>
<record id="tile_pos_sales_non_members_last_30_days" model="tile.tile">
<field name="name">Non-members sales last 30 days (€)</field>
<field name="sequence">40</field>
<field name="category_id" ref="tile_category_cooperative"/>
<field name="model_id" ref="point_of_sale.model_report_pos_order"/>
<field name="action_id" ref="action_pos_dashboard_members"/>
<field name="primary_function">sum</field>
<field name="primary_field_id"
ref="point_of_sale.field_report_pos_order__price_total"/>
<field name="primary_format">{:,.2f} €</field>
<field
name="domain"
>[('state', 'in', ['paid', 'done', 'invoiced']),
('is_member_at_date', '=', False),
('date', '&gt;=', (context_today - relativedelta(days=30)).strftime('%Y-%m-%d'))]</field>
<field name="background_color">#6A1B9A</field>
<field name="font_color">#FFFFFF</field>
</record>
</odoo>

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2026 Criptomart - License AGPL-3 -->
<odoo>
<menuitem
id="menu_pos_dashboard_cooperative"
name="Cooperative Dashboards"
parent="point_of_sale.menu_point_rep"
sequence="50"
/>
<menuitem
id="menu_pos_dashboard_overview"
name="Overview"
parent="menu_pos_dashboard_cooperative"
action="action_pos_dashboard_overview"
sequence="10"
/>
<menuitem
id="menu_pos_dashboard_members"
name="Members vs Non-members"
parent="menu_pos_dashboard_cooperative"
action="action_pos_dashboard_members"
sequence="20"
/>
<menuitem
id="menu_pos_dashboard_top_members"
name="Top Members"
parent="menu_pos_dashboard_cooperative"
action="action_pos_dashboard_top_members"
sequence="30"
/>
<menuitem
id="menu_pos_dashboard_categories"
name="By Category"
parent="menu_pos_dashboard_cooperative"
action="action_pos_dashboard_categories"
sequence="40"
/>
<menuitem
id="menu_pos_dashboard_top_products"
name="Top Products"
parent="menu_pos_dashboard_cooperative"
action="action_pos_dashboard_top_products"
sequence="50"
/>
<menuitem
id="menu_pos_dashboard_by_day"
name="By Day of Week"
parent="menu_pos_dashboard_cooperative"
action="action_pos_dashboard_by_day_of_week"
sequence="60"
/>
<menuitem
id="menu_pos_dashboard_by_hour"
name="By Hour of Day"
parent="menu_pos_dashboard_cooperative"
action="action_pos_dashboard_by_hour"
sequence="70"
/>
</odoo>

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2026 Criptomart - License AGPL-3 -->
<odoo>
<!--
Category analysis: sales broken down by family (root), subfamily
(parent of leaf), and leaf category. Designed to answer the
"category traction" and "top families" questions from the BI brief.
-->
<record id="action_pos_dashboard_categories" model="ir.actions.act_window">
<field name="name">Sales by Category</field>
<field name="res_model">report.pos.order</field>
<field name="view_mode">pivot,graph,tree</field>
<field name="search_view_id"
ref="point_of_sale.view_report_pos_order_search"/>
<field name="context">{
'search_default_group_categ_root': 1,
'search_default_order_month': 1,
'group_by_no_leaf': 1,
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No data yet!
</p><p>
Add Subfamily or Product group-bys to drill down. Useful
to detect tractor categories.
</p>
</field>
</record>
<record id="action_pos_dashboard_top_products" model="ir.actions.act_window">
<field name="name">Top Products</field>
<field name="res_model">report.pos.order</field>
<field name="view_mode">pivot,tree,graph</field>
<field name="search_view_id"
ref="point_of_sale.view_report_pos_order_search"/>
<field name="context">{
'group_by': ['product_id'],
'group_by_no_leaf': 1,
}</field>
</record>
</odoo>

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2026 Criptomart - License AGPL-3 -->
<odoo>
<!--
Members vs non-members comparison.
Opens the analysis pre-grouped by member flag and by month, with
sales / orders / margin as primary measures.
-->
<record id="action_pos_dashboard_members" model="ir.actions.act_window">
<field name="name">Members vs Non-members</field>
<field name="res_model">report.pos.order</field>
<field name="view_mode">pivot,graph,tree</field>
<field name="search_view_id"
ref="point_of_sale.view_report_pos_order_search"/>
<field name="context">{
'search_default_group_member': 1,
'search_default_order_month': 1,
'group_by_no_leaf': 1,
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No data yet!
</p><p>
This view contrasts cooperative members and non-members on
the same axes (sales, ticket count, margin).
</p>
</field>
</record>
<!--
Detail per member: top buyers, per-partner KPIs.
Filters to members only and groups by partner.
-->
<record id="action_pos_dashboard_top_members" model="ir.actions.act_window">
<field name="name">Top Member Buyers</field>
<field name="res_model">report.pos.order</field>
<field name="view_mode">pivot,tree,graph</field>
<field name="search_view_id"
ref="point_of_sale.view_report_pos_order_search"/>
<field name="domain">[('is_member_at_date', '=', True)]</field>
<field name="context">{
'search_default_members': 1,
'group_by': ['partner_id'],
'group_by_no_leaf': 1,
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No data yet!
</p><p>
Sales aggregated per cooperative member. Sort by total
price to obtain the top buyers.
</p>
</field>
</record>
</odoo>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2026 Criptomart - License AGPL-3 -->
<odoo>
<!--
Time-based analyses: distribution of sales across days of the week
and hours of the day, to size shifts and detect peaks.
-->
<record id="action_pos_dashboard_by_day_of_week" model="ir.actions.act_window">
<field name="name">Sales by Day of Week</field>
<field name="res_model">report.pos.order</field>
<field name="view_mode">graph,pivot,tree</field>
<field name="search_view_id"
ref="point_of_sale.view_report_pos_order_search"/>
<field name="context">{
'search_default_group_day_of_week': 1,
'group_by_no_leaf': 1,
}</field>
</record>
<record id="action_pos_dashboard_by_hour" model="ir.actions.act_window">
<field name="name">Sales by Hour of Day</field>
<field name="res_model">report.pos.order</field>
<field name="view_mode">graph,pivot,tree</field>
<field name="search_view_id"
ref="point_of_sale.view_report_pos_order_search"/>
<field name="context">{
'search_default_group_hour_of_day': 1,
'group_by_no_leaf': 1,
}</field>
</record>
</odoo>

View file

@ -0,0 +1,31 @@
# Spreadsheet Dashboard POS Cooperative
Publishes spreadsheet-based dashboards in Odoo's *Dashboards* menu, in
a dedicated *Cooperative* group. Built on top of o-spreadsheet, the
same technology used by Odoo's official `spreadsheet_dashboard_*`
modules (`spreadsheet_dashboard_sale`, `spreadsheet_dashboard_purchase`,
etc.).
Depends on `pos_dashboard_cooperative` for the underlying data model
(`report.pos.order` extended with member / category / time
dimensions).
Four dashboards are bundled:
| Dashboard | Contents |
|---|---|
| POS Overview | Sales, Orders, Avg ticket, Margin + monthly chart |
| POS Members | Members vs non-members KPIs + monthly chart + TOP 50 buyers |
| POS Categories | Members contribution + TOP 10 families / subfamilies / products |
| POS Sales by Time | Sales by day of week + sales by hour of day |
The JSON files under `data/files/` are hand-authored and intentionally
diff-friendly. To redesign one of them visually, install
`spreadsheet_oca`, edit in the o-spreadsheet UI, export to JSON and
replace the matching file.
See `readme/USAGE.rst` for the full procedure and metric definitions.
## License
AGPL-3.0 or later.

View file

@ -0,0 +1 @@
# Part of the spreadsheet_dashboard ecosystem. Nothing to import.

View file

@ -0,0 +1,20 @@
# Copyright 2026 Criptomart
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Spreadsheet dashboard for POS cooperative",
"version": "16.0.1.0.0",
"category": "Hidden",
"summary": "Spreadsheet dashboards for cooperative supermarkets POS",
"license": "AGPL-3",
"author": "Criptomart, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/pos",
"depends": [
"spreadsheet_dashboard",
"pos_dashboard_cooperative",
],
"data": [
"data/dashboards.xml",
],
"auto_install": ["pos_dashboard_cooperative"],
"installable": True,
}

View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2026 Criptomart - License AGPL-3 -->
<odoo>
<!--
New dashboard group "Cooperative" sitting alongside the standard
Odoo groups (Sales, Finance, HR, ...). Sequence 150 places it
right after Sales (100) and before Finance (300).
-->
<record id="spreadsheet_dashboard_group_cooperative"
model="spreadsheet.dashboard.group">
<field name="name">PDV Supermercado</field>
<field name="sequence">150</field>
</record>
<!--
Section 1: high-level KPIs and monthly evolution for the whole
store. The "headline" dashboard, intentionally compact so it
loads first.
-->
<record id="spreadsheet_dashboard_pos_overview"
model="spreadsheet.dashboard">
<field name="name">Ventas</field>
<field name="data" type="base64"
file="spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json"/>
<field name="dashboard_group_id"
ref="spreadsheet_dashboard_group_cooperative"/>
<field name="group_ids"
eval="[Command.link(ref('point_of_sale.group_pos_manager'))]"/>
<field name="sequence">100</field>
</record>
<!--
Section 4 of the BI brief: sales distribution across days of the
week and hours of the day. Two line charts.
-->
<record id="spreadsheet_dashboard_pos_time"
model="spreadsheet.dashboard">
<field name="name">Ventas horas y días</field>
<field name="data" type="base64"
file="spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json"/>
<field name="dashboard_group_id"
ref="spreadsheet_dashboard_group_cooperative"/>
<field name="group_ids"
eval="[Command.link(ref('point_of_sale.group_pos_manager'))]"/>
<field name="sequence">400</field>
</record>
<!--
Section 3 of the BI brief: TOP families / subfamilies / products,
plus members contribution KPIs (purchase share, penetration).
-->
<record id="spreadsheet_dashboard_pos_categories"
model="spreadsheet.dashboard">
<field name="name">Categorías</field>
<field name="data" type="base64"
file="spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json"/>
<field name="dashboard_group_id"
ref="spreadsheet_dashboard_group_cooperative"/>
<field name="group_ids"
eval="[Command.link(ref('point_of_sale.group_pos_manager'))]"/>
<field name="sequence">300</field>
</record>
<!--
Section 2 of the BI brief: detailed comparison between members
and non-members. KPIs per group, monthly evolution chart and a
TOP 50 of the best buying members.
-->
<record id="spreadsheet_dashboard_pos_members"
model="spreadsheet.dashboard">
<field name="name">Socias</field>
<field name="data" type="base64"
file="spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json"/>
<field name="dashboard_group_id"
ref="spreadsheet_dashboard_group_cooperative"/>
<field name="group_ids"
eval="[Command.link(ref('point_of_sale.group_pos_manager'))]"/>
<field name="sequence">200</field>
</record>
</odoo>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,367 @@
{
"version": 12,
"sheets": [
{
"id": "dashboard",
"name": "Overview",
"colNumber": 26,
"rowNumber": 50,
"rows": {
"0": {
"size": 40
}
},
"cols": {},
"merges": [],
"cells": {
"A1": {
"style": 1,
"content": "=_t(\"Cooperative POS — Overview\")"
},
"A3": {
"style": 2,
"content": "=_t(\"Monthly evolution\")"
}
},
"conditionalFormats": [],
"figures": [
{
"id": "kpi-sales",
"x": 0,
"y": 60,
"width": 200,
"height": 120,
"tag": "chart",
"data": {
"type": "scorecard",
"title": "Sales (€)",
"keyValue": "Data!D1",
"baseline": "Data!E1",
"baselineDescr": "since last period",
"baselineMode": "percentage",
"baselineColorUp": "#00A04A",
"baselineColorDown": "#DC6965"
}
},
{
"id": "kpi-orders",
"x": 220,
"y": 60,
"width": 200,
"height": 120,
"tag": "chart",
"data": {
"type": "scorecard",
"title": "Orders",
"keyValue": "Data!D2",
"baseline": "Data!E2",
"baselineDescr": "since last period",
"baselineMode": "percentage",
"baselineColorUp": "#00A04A",
"baselineColorDown": "#DC6965"
}
},
{
"id": "kpi-ticket",
"x": 440,
"y": 60,
"width": 200,
"height": 120,
"tag": "chart",
"data": {
"type": "scorecard",
"title": "Average ticket (€)",
"keyValue": "Data!D3",
"baseline": "Data!E3",
"baselineDescr": "since last period",
"baselineMode": "percentage",
"baselineColorUp": "#00A04A",
"baselineColorDown": "#DC6965"
}
},
{
"id": "kpi-margin",
"x": 660,
"y": 60,
"width": 200,
"height": 120,
"tag": "chart",
"data": {
"type": "scorecard",
"title": "Gross margin (€)",
"keyValue": "Data!D4",
"baseline": "Data!E4",
"baselineDescr": "since last period",
"baselineMode": "percentage",
"baselineColorUp": "#00A04A",
"baselineColorDown": "#DC6965"
}
},
{
"id": "chart-monthly-sales",
"x": 0,
"y": 220,
"width": 1000,
"height": 300,
"tag": "chart",
"data": {
"title": "Monthly sales (€)",
"background": "#FFFFFF",
"legendPosition": "none",
"metaData": {
"groupBy": [
"date:month"
],
"measure": "price_total",
"order": null,
"resModel": "report.pos.order"
},
"searchParams": {
"comparison": null,
"context": {
"group_by": [
"date:month"
],
"group_by_no_leaf": 1
},
"domain": [
[
"state",
"in",
[
"paid",
"done",
"invoiced"
]
]
],
"groupBy": [
"date:month"
],
"orderBy": []
},
"type": "odoo_line",
"verticalAxisPosition": "left"
}
}
],
"areGridLinesVisible": false,
"isVisible": true
},
{
"id": "data",
"name": "Data",
"colNumber": 5,
"rowNumber": 10,
"rows": {},
"cols": {
"0": {
"size": 200
},
"1": {
"size": 140
},
"2": {
"size": 140
},
"3": {
"size": 140
},
"4": {
"size": 140
}
},
"merges": [],
"cells": {
"A1": {
"content": "=_t(\"KPI - Sales\")"
},
"A2": {
"content": "=_t(\"KPI - Orders\")"
},
"A3": {
"content": "=_t(\"KPI - Average ticket\")"
},
"A4": {
"content": "=_t(\"KPI - Margin\")"
},
"B1": {
"content": "=ODOO.PIVOT(1,\"price_total\")"
},
"B2": {
"content": "=ODOO.PIVOT(1,\"order_id\")"
},
"B3": {
"content": "=IFERROR(B1/B2,0)"
},
"B4": {
"content": "=ODOO.PIVOT(1,\"margin\")"
},
"C1": {
"content": "=ODOO.PIVOT(2,\"price_total\")"
},
"C2": {
"content": "=ODOO.PIVOT(2,\"order_id\")"
},
"C3": {
"content": "=IFERROR(C1/C2,0)"
},
"C4": {
"content": "=ODOO.PIVOT(2,\"margin\")"
},
"D1": {
"content": "=FORMAT.LARGE.NUMBER(B1)"
},
"D2": {
"content": "=FORMAT.LARGE.NUMBER(B2)"
},
"D3": {
"content": "=FORMAT.LARGE.NUMBER(B3)"
},
"D4": {
"content": "=FORMAT.LARGE.NUMBER(B4)"
},
"E1": {
"content": "=FORMAT.LARGE.NUMBER(C1)"
},
"E2": {
"content": "=FORMAT.LARGE.NUMBER(C2)"
},
"E3": {
"content": "=FORMAT.LARGE.NUMBER(C3)"
},
"E4": {
"content": "=FORMAT.LARGE.NUMBER(C4)"
}
},
"conditionalFormats": [],
"figures": [],
"areGridLinesVisible": true,
"isVisible": false
}
],
"entities": {},
"styles": {
"1": {
"bold": true,
"fontSize": 22,
"textColor": "#2E7D32"
},
"2": {
"bold": true,
"fontSize": 14,
"textColor": "#333333"
}
},
"formats": {
"1": "#,##0.00 [$€]",
"2": "#,##0"
},
"borders": {},
"revisionId": "START_REVISION",
"chartOdooMenusReferences": {},
"odooVersion": 1,
"lists": {},
"listNextId": 1,
"pivots": {
"1": {
"colGroupBys": [],
"context": {
"group_by_no_leaf": 1,
"group_by": []
},
"domain": [
[
"state",
"in",
[
"paid",
"done",
"invoiced"
]
]
],
"id": "1",
"measures": [
{
"field": "price_total"
},
{
"field": "margin"
},
{
"field": "order_id"
}
],
"model": "report.pos.order",
"rowGroupBys": [],
"name": "POS KPIs - current",
"sortedColumn": null
},
"2": {
"colGroupBys": [],
"context": {
"group_by_no_leaf": 1,
"group_by": []
},
"domain": [
[
"state",
"in",
[
"paid",
"done",
"invoiced"
]
]
],
"id": "2",
"measures": [
{
"field": "price_total"
},
{
"field": "margin"
},
{
"field": "order_id"
}
],
"model": "report.pos.order",
"rowGroupBys": [],
"name": "POS KPIs - previous",
"sortedColumn": null
}
},
"pivotNextId": 3,
"globalFilters": [
{
"id": "11111111-1111-1111-1111-111111111111",
"type": "date",
"label": "Period",
"defaultValue": {},
"rangeType": "month",
"defaultsToCurrentPeriod": true,
"pivotFields": {
"1": {
"field": "date",
"type": "datetime",
"offset": 0
},
"2": {
"field": "date",
"type": "datetime",
"offset": -1
}
},
"listFields": {},
"graphFields": {
"chart-monthly-sales": {
"field": "date",
"type": "datetime",
"offset": 0
}
}
}
]
}

View file

@ -0,0 +1,221 @@
{
"version": 12,
"sheets": [
{
"id": "dashboard",
"name": "Sales by time",
"colNumber": 26,
"rowNumber": 50,
"rows": {
"0": {
"size": 40
}
},
"cols": {},
"merges": [],
"cells": {
"A1": {
"style": 1,
"content": "=_t(\"Cooperative POS — Sales by hours and days\")"
},
"A3": {
"style": 2,
"content": "=_t(\"Sales by day of the week\")"
},
"A20": {
"style": 2,
"content": "=_t(\"Sales by hour of the day\")"
}
},
"conditionalFormats": [],
"figures": [
{
"id": "chart-by-day",
"x": 0,
"y": 60,
"width": 1000,
"height": 280,
"tag": "chart",
"data": {
"title": "Sales by day of week",
"background": "#FFFFFF",
"legendPosition": "none",
"metaData": {
"groupBy": [
"day_of_week"
],
"measure": "price_total",
"order": null,
"resModel": "report.pos.order"
},
"searchParams": {
"comparison": null,
"context": {
"group_by": [
"day_of_week"
],
"group_by_no_leaf": 1
},
"domain": [
[
"state",
"in",
[
"paid",
"done",
"invoiced"
]
]
],
"groupBy": [
"day_of_week"
],
"orderBy": []
},
"type": "odoo_line",
"verticalAxisPosition": "left"
}
},
{
"id": "chart-by-hour",
"x": 0,
"y": 380,
"width": 1000,
"height": 280,
"tag": "chart",
"data": {
"title": "Sales by hour of day",
"background": "#FFFFFF",
"legendPosition": "none",
"metaData": {
"groupBy": [
"hour_of_day"
],
"measure": "price_total",
"order": null,
"resModel": "report.pos.order"
},
"searchParams": {
"comparison": null,
"context": {
"group_by": [
"hour_of_day"
],
"group_by_no_leaf": 1
},
"domain": [
[
"state",
"in",
[
"paid",
"done",
"invoiced"
]
]
],
"groupBy": [
"hour_of_day"
],
"orderBy": []
},
"type": "odoo_line",
"verticalAxisPosition": "left"
}
}
],
"areGridLinesVisible": false,
"isVisible": true
}
],
"entities": {},
"styles": {
"1": {
"bold": true,
"fontSize": 22,
"textColor": "#2E7D32"
},
"2": {
"bold": true,
"fontSize": 14,
"textColor": "#333333"
}
},
"formats": {},
"borders": {},
"revisionId": "START_REVISION",
"chartOdooMenusReferences": {},
"odooVersion": 1,
"lists": {},
"listNextId": 1,
"pivots": {},
"pivotNextId": 1,
"globalFilters": [
{
"id": "11111111-1111-1111-1111-111111111111",
"type": "date",
"label": "Period",
"defaultValue": {},
"rangeType": "month",
"defaultsToCurrentPeriod": true,
"pivotFields": {},
"listFields": {},
"graphFields": {
"chart-by-day": {
"field": "date",
"type": "datetime",
"offset": 0
},
"chart-by-hour": {
"field": "date",
"type": "datetime",
"offset": 0
}
}
},
{
"id": "22222222-2222-2222-2222-222222222222",
"type": "relation",
"label": "Product Category",
"modelName": "product.category",
"defaultValue": [],
"defaultValueDisplayNames": [],
"rangeType": "year",
"defaultsToCurrentPeriod": false,
"pivotFields": {},
"listFields": {},
"graphFields": {
"chart-by-day": {
"field": "product_categ_id",
"type": "many2one"
},
"chart-by-hour": {
"field": "product_categ_id",
"type": "many2one"
}
}
},
{
"id": "33333333-3333-3333-3333-333333333333",
"type": "relation",
"label": "Product",
"modelName": "product.product",
"defaultValue": [],
"defaultValueDisplayNames": [],
"rangeType": "year",
"defaultsToCurrentPeriod": false,
"pivotFields": {},
"listFields": {},
"graphFields": {
"chart-by-day": {
"field": "product_id",
"type": "many2one"
},
"chart-by-hour": {
"field": "product_id",
"type": "many2one"
}
}
}
]
}

View file

@ -0,0 +1,634 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * spreadsheet_dashboard_pos_cooperative
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-06-05 15:43+0000\n"
"PO-Revision-Date: 2026-06-05 15:43+0000\n"
"Last-Translator: Criptomart\n"
"Language-Team: Spanish\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Average ticket (€)"
msgstr "Ticket medio (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Categoría"
msgstr "Categoría"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Compradoras distintas (no socias)"
msgstr "Compradoras distintas (no socias)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Compradoras distintas (socias)"
msgstr "Compradoras distintas (socias)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Contribución de las socias"
msgstr "Contribución de las socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Cooperative POS — Categories"
msgstr "TPV Cooperativa — Categorías"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Cooperative POS — Members vs Non-members"
msgstr "TPV Cooperativa — Socias vs no socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Cooperative POS — Overview"
msgstr "TPV Cooperativa — Vista general"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Cooperative POS — Sales by hours and days"
msgstr "TPV Cooperativa — Ventas por horas y días"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Cuota %"
msgstr "Cuota %"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Cuota de compra"
msgstr "Cuota de compra"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Cuota de compra %"
msgstr "Cuota de compra %"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Evolución mensual por condición de socia"
msgstr "Evolución mensual por condición de socia"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Evolución mensual — Socias vs no socias"
msgstr "Evolución mensual — Socias vs no socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Evolución mensual — Socias vs no socias (€)"
msgstr "Evolución mensual — Socias vs no socias (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Familia"
msgstr "Familia"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Frecuencia socias"
msgstr "Frecuencia socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Gross margin (€)"
msgstr "Margen bruto (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "KPI - Average ticket"
msgstr "KPI - Ticket medio"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "KPI - Margin"
msgstr "KPI - Margen"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "KPI - Orders"
msgstr "KPI - Tickets"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "KPI - Sales"
msgstr "KPI - Ventas"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "KPIs de no socias"
msgstr "KPIs de no socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "KPIs de socias"
msgstr "KPIs de socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Mejores compradoras (socias)"
msgstr "Mejores compradoras (socias)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Monthly evolution"
msgstr "Evolución mensual"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Monthly sales (€)"
msgstr "Ventas mensuales (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Orders"
msgstr "Tickets"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "POS KPIs"
msgstr "POS KPIs"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Penetración socias"
msgstr "Penetración socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Penetración socias %"
msgstr "Penetración socias %"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Period"
msgstr "Período"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Product"
msgstr "Producto"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Product Category"
msgstr "Categoría de producto"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Producto"
msgstr "Producto"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Sales (€)"
msgstr "Ventas (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Sales by day of the week"
msgstr "Ventas por día de la semana"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Sales by day of week"
msgstr "Ventas por día de la semana"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Sales by hour of day"
msgstr "Ventas por hora del día"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Sales by hour of the day"
msgstr "Ventas por hora del día"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socia"
msgstr "Socia"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - Compradoras distintas"
msgstr "Socias - Compradoras distintas"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - Frecuencia"
msgstr "Socias - Frecuencia"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - Ticket medio"
msgstr "Socias - Ticket medio"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - Tickets"
msgstr "Socias - Tickets"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - UPT"
msgstr "Socias - UPT"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - Ventas"
msgstr "Socias - Ventas"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Subfamilia"
msgstr "Subfamilia"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "TOP 50 socias compradoras"
msgstr "TOP 50 socias compradoras"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "TPV por condición de socia"
msgstr "TPV por condición de socia"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ticket medio (€)"
msgstr "Ticket medio (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ticket medio no socias (€)"
msgstr "Ticket medio no socias (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ticket medio socias (€)"
msgstr "Ticket medio socias (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Tickets"
msgstr "Tickets"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Tickets no socias"
msgstr "Tickets no socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Tickets socias"
msgstr "Tickets socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top 10 categorías"
msgstr "Top 10 categorías"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top 10 familias"
msgstr "Top 10 familias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top 10 productos"
msgstr "Top 10 productos"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top 10 subfamilias"
msgstr "Top 10 subfamilias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top categorías"
msgstr "Top categorías"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top familias"
msgstr "Top familias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top productos"
msgstr "Top productos"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top subfamilias"
msgstr "Top subfamilias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Total compradoras distintas"
msgstr "Total compradoras distintas"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Total ventas"
msgstr "Total ventas"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "UPT"
msgstr "UPT"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "UPT no socias"
msgstr "UPT no socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "UPT socias"
msgstr "UPT socias"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Unidades"
msgstr "Unidades"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ventas (€)"
msgstr "Ventas (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ventas no socias (€)"
msgstr "Ventas no socias (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ventas socias (€)"
msgstr "Ventas socias (€)"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "en el período"
msgstr "en el período"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "in selected period"
msgstr "en el período seleccionado"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "sales / orders"
msgstr "ventas / tickets"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "socias compradoras / total"
msgstr "socias compradoras / total"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "socias únicas"
msgstr "socias únicas"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "tickets"
msgstr "tickets"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "tickets / compradora"
msgstr "tickets / compradora"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "uds / ticket"
msgstr "uds / ticket"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "ventas / tickets"
msgstr "ventas / tickets"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "ventas socias / total"
msgstr "ventas socias / total"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "únicas"
msgstr "únicas"

View file

@ -0,0 +1,633 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * spreadsheet_dashboard_pos_cooperative
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-06-05 15:43+0000\n"
"PO-Revision-Date: 2026-06-05 15:43+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Average ticket (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Categoría"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Compradoras distintas (no socias)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Compradoras distintas (socias)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Contribución de las socias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Cooperative POS — Categories"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Cooperative POS — Members vs Non-members"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Cooperative POS — Overview"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Cooperative POS — Sales by hours and days"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Cuota %"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Cuota de compra"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Cuota de compra %"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Evolución mensual por condición de socia"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Evolución mensual — Socias vs no socias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Evolución mensual — Socias vs no socias (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Familia"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Frecuencia socias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Gross margin (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "KPI - Average ticket"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "KPI - Margin"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "KPI - Orders"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "KPI - Sales"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "KPIs de no socias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "KPIs de socias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Mejores compradoras (socias)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Monthly evolution"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Monthly sales (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Orders"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "POS KPIs"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Penetración socias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Penetración socias %"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Period"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Product"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Product Category"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Producto"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "Sales (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Sales by day of the week"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Sales by day of week"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Sales by hour of day"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/time_dashboard.json:0
#, python-format
msgid "Sales by hour of the day"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socia"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - Compradoras distintas"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - Frecuencia"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - Ticket medio"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - Tickets"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - UPT"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Socias - Ventas"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Subfamilia"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "TOP 50 socias compradoras"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "TPV por condición de socia"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ticket medio (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ticket medio no socias (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ticket medio socias (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Tickets"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Tickets no socias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Tickets socias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top 10 categorías"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top 10 familias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top 10 productos"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top 10 subfamilias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top categorías"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top familias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top productos"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#, python-format
msgid "Top subfamilias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Total compradoras distintas"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Total ventas"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "UPT"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "UPT no socias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "UPT socias"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Unidades"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/categories_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ventas (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ventas no socias (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "Ventas socias (€)"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "en el período"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "in selected period"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "sales / orders"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "socias compradoras / total"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "socias únicas"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/overview_dashboard.json:0
#, python-format
msgid "tickets"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "tickets / compradora"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "uds / ticket"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "ventas / tickets"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "ventas socias / total"
msgstr ""
#. module: spreadsheet_dashboard_pos_cooperative
#. odoo-javascript
#: code:addons/spreadsheet_dashboard_pos_cooperative/data/files/members_dashboard.json:0
#, python-format
msgid "únicas"
msgstr ""

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.