# Topic: credit_notes — posted customer CREDIT NOTES (returns), at document grain. # # Why its own topic rather than a filtered measure on a shared one: a FILTERED measure # (`store_filter_sql`) exists only on the store path, and `semantic.metric()` resolves a sum by # handing the metric's `field` to `O.sum_field(entity, domain, field)` with no room for a # per-measure filter. A metric that can be computed on one path and not the other cannot be # parity-checked — and parity is the admission bar for filtering on it. Two topics with two # DOMAINS give both paths the same scope by construction. key: credit_notes label: Customer credit notes (returns) entity: account.move domain_builder: credit_note_domain scope: documents: "posted customer credit notes only (move_type = out_refund, state = posted)" channels: >- COMPANY-LEVEL. Credit notes carry team_id = 1 for everything, so a business-unit filter on the document is meaningless — modules/returns.py says the same and attributes by the CUSTOMER instead. A BU-scoped caller is refused rather than given a company number wearing their name. excluded: >- Nothing. modules/returns.py — the number the business already reads — does not exclude the GIFTWARE DEALS partner the wholesale sales topics do. Matching it keeps one definition of "returns"; the customer-grid pool is wholesale anyway, so the intersection makes the difference unobservable there. basis: "untaxed document amounts (amount_untaxed_signed, NEGATED — see the metric)" date_field: "invoice_date" grain: "one row per posted credit note; time-filterable by invoice_date" store: table: account_move alias: m join: "LEFT JOIN res_partner rp ON rp.id = m.partner_id" date_col: "m.invoice_date" scope_sql: "m.state = 'posted' AND m.move_type = 'out_refund'" dims: partner: {col: "m.partner_id", name_col: "m.partner_name", label: "Customer"} ai_context: > Posted customer credit notes — refunds/returns. ⚠ `amount_untaxed_signed` is NEGATIVE on a credit note (verified on the tenant store 2026-07-27: all 3,006 posted refunds are negative, summing to -358,141.70), so the `returns` metric declares `negate: true` and reports a POSITIVE amount. A returns figure that comes out negative means the negation was lost, and it will read as "we have no returns" rather than as an error. This topic is COMPANY-LEVEL: credit notes are not business-unit tagged, so never present a returns number as belonging to Fisch or Royal. Odoo has no return-REASON field, so concentration (which customers, which SKUs) is the only diagnostic available.