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