# title
Fusion daily output (STORIS × Trakwell) v2

# description
v2 of the daily-fusion contract. Breaking change from v1: dollar fields (net_sales, location_net_sales, units, avg_ticket, location_sales_per_visitor) now allow SIGNED values to accommodate cross-day returns. v1 typed them as nonNegativeNumber; that constraint became invalid the moment we split closes-counting from revenue-summing (see PROGRESS.md 2026-05-11 entry). Closes-counting fields (transactions_raw, location_unique_closes, location_traffic) stay non-negative.

ALSO IN v2: location_traffic source switched from UserSummaryCountNew.door (raw camera count) to salestatsloc.opportunities (= raw − excluded), the canonical close-ratio denominator. Schema enforces nothing about this — it's the pipeline's responsibility — but the description here is the canonical statement of intent.

VOCABULARY: see docs/CONCEPTS.md. PRIMITIVES: pulled via $ref from _common.schema.json.

AUTHORITATIVE-SOURCE BOUNDARY: per FUSION_PLAN §3a-bis, sales/orders columns come from STORIS; traffic/closes columns come from Trakwell. Fields are named *_raw for per-store STORIS facts; location_* for per-physical-location aggregates that drive the headline KPIs.

METHODOLOGY (load-bearing, see memories closes_vs_revenue_methodology_split + zzz_is_commission_reversal_vehicle + transactioncodeid_00_for_performance):
  - Row-identity filter: writtenFlag AND transactionCodeId == '00' (merchandise only).
  - OrderItem-level consolidation across ALL salespeople including ZZZ/ASH (so commission-reversal pairs net at the orderItem level).
  - Drop orderItems whose summed netSales is zero (full reversals, amendments).
  - Keep orderItems with negative summed netSales (cross-day returns) for revenue; do NOT count them as closes.
  - location_unique_closes counts customerIds in POSITIVE-net orderItems only.

# $defs.FusionDailyRow.title
Single (store_id, date) row of fusion_daily v2

# $defs.FusionDailyRow.properties.physical_location.description
Null only for store_id=='777' (online — no physical door).

# $defs.FusionDailyRow.properties.transactions_raw.description
Per-store count of NON-ZERO orderItems attributed to this store today — positive AND negative both count (a return is a transaction). Matches STORIS 'Total For Location' row count. Stays non-negative because it's a count.

# $defs.FusionDailyRow.properties.units.description
Per-store unit count from STORIS netUnits, post-orderItemId consolidation. CAN be negative on heavy-return days (returns reduce units).

# $defs.FusionDailyRow.properties.net_sales.description
Per-store STORIS net sales, post-orderItemId-consolidation, including negative-summed orderItems (cross-day returns). CAN be negative on a day where returns exceed sales for the store. Chain sum exact-matches STORIS 'Report Written Sales by Salesperson' Grand Total.

# $defs.FusionDailyRow.properties.gross_profit.description
Per-store STORIS gross profit. Same signed treatment as net_sales.

# $defs.FusionDailyRow.properties.location_traffic.description
Trakwell `opportunities` (= raw door count − excluded) for the physical_location for this date, summed across co-located stores from salestatsloc.json. The canonical close-ratio denominator. Same value repeated across co-located rows (003/004 share). Null when store_id=='777' OR Trakwell data missing/incomplete. NOT UserSummaryCountNew.door — that was the v1 source and produced structurally wrong close ratios on parts-pickup days.

# $defs.FusionDailyRow.properties.location_unique_closes.description
Distinct STORIS customerId in POSITIVE-net orderItems across all stores within the physical_location for this date. A return doesn't make the customer a 'close' today (Trakwell-aligned). Same value repeated across co-located rows. Null when store_id=='777'.

# $defs.FusionDailyRow.properties.location_net_sales.description
Sum of net_sales across stores within the physical_location, including signed cross-day returns. Same value repeated across co-located rows. CAN be negative. Null when store_id=='777'.

# $defs.FusionDailyRow.properties.location_conversion.description
location_unique_closes / location_traffic. Null when location_traffic is null or 0. Decimal not percent — 0.42 means 42%. Math can produce values >1 when STORIS sees more closing customers than Trakwell logged opportunities (linkage hygiene gap); presentation layer typically caps display at 100% with a (+N) overflow indicator.

# $defs.FusionDailyRow.properties.location_sales_per_visitor.description
location_net_sales / location_traffic. CAN be negative on heavy-return days. Null when location_traffic is null or 0.

# $defs.FusionDailyRow.properties.location_gp_per_visitor.description
location_gross_profit / location_traffic. Can be negative if combined GP is negative. Null when location_traffic is null or 0.

# $defs.FusionDailyRow.properties.avg_ticket.description
Per-store: net_sales / transactions_raw. CAN be negative if net_sales is negative. Null when transactions_raw == 0. Pure STORIS metric — never derived from Trakwell. Per-buying-customer avg = net_sales / location_unique_closes is preferred for ops display ('Avg Sale').

# $defs.FusionDailyRow.properties.ghost_attribution_net_sales.description
Per-store sum of net_sales attributed to inactive salesperson codes after their `last_known_active_at` boundary (per docs/salesperson_map.json). Indicates how much of net_sales is post-termination ghost-attribution. Null when no salesperson_map was loaded (couldn't check); 0 means we did check and found nothing. CAN be negative if ghost rows include returns. See GSHEETS_IDENTITY_RESOLVER.md v4 for the methodology and limitations (LOWER bound on true ghost volume — see caveat).

# $defs.FusionDailyRow.properties.ghost_attribution_pct.description
ghost_attribution_net_sales / net_sales. Null when net_sales is 0 OR salesperson_map wasn't loaded. Decimal not percent — 0.07 means 7%.

# $defs.FusionDailyRow.properties.storis_run.description
Pointer to salesLog/<dir> the STORIS facts were derived from.

# $defs.FusionDailyRow.properties.trakwell_run.description
Pointer to doorLog/<dir>. Null when no Trakwell data was used (e.g. store 777 row, or the entire Trakwell pull failed).

# $defs.FusionDailyRow.allOf[].title
Store 777 (online) has no physical_location and no traffic

# $defs.FusionDailyRow.allOf[].title
Stores 003 and 004 share the Decatur outlet location

# $defs.FusionDailyRow.allOf[].title
If location_traffic is null, all derived per-visitor KPIs must be null

# $defs.FusionDailyRow.allOf[].title
If transactions_raw is 0, avg_ticket must be null (no division by zero)

# $defs.FusionDailyFile.title
Array wrapper for a per-day fusion CSV/JSON output

# $defs.FusionDailyFile.description
A complete daily run produces one row per (store_id, date) for every store_id in the store map.

EXTRA-SCHEMA INVARIANTS to enforce in the loader (JSON Schema 2020-12 cannot express composite-key uniqueness natively):
  - (store_id, date) is unique across rows.
  - For any given date present in the file, every store_id with has_door_traffic in store_map must appear (or fail loud — never silent missing rows).
  - For co-located stores in the same physical_location, location_traffic / location_unique_closes / location_net_sales / location_conversion / location_sales_per_visitor / location_gp_per_visitor MUST be identical across the co-located rows.