← back to Dw Five Field Step0

match-pr-pointe-cost.py

114 lines

#!/usr/bin/env python3
"""
DRY-RUN: Match DW Phillipe-Romano / Los-Angeles-Fabrics private-label SKUs to the
Pointe -> Justin David vendor price list and produce a cost-load diff.

READ-ONLY. Writes NO cost to dw_unified / Shopify. The actual cost write is gated
for Steve after he reviews the unlock count.

MATCH KEY (the link between PR private-label SKUs and the upstream Pointe pattern):
  live `shopify_products.mfr_sku` of the form 'JD-PATTERN-COLOR'
    -> strip 'JD-', uppercase, drop all non-alphanumerics, concat(pattern+color)
  == price-list `jd` field 'Pattern/Color' normalized the same way.
The price list cross-references `jd` (Justin David name) <-> `pointe` (manufacturer
Pointe name); we key on the JD side because that is what the live mfr_sku carries.

  NOTE: command54_catalog.original_name (AJA/LAURUS...) is a DIFFERENT upstream and
  is NOT priced by this Pointe/JD list (0 overlap) -- do not use it here.

DTD-locked policy (panel 3/3, 2026-06-21):
  Q1 cost = cut_yd_price ONLY (tariff tracked separately as pass-through).
  Q2 load cost ONLY to priced STANDARD/ACTIVE rows; hold DISCONTINUED/CUSTOM/MTO.
  Q3 retail = cost / 0.65 / 0.85.
  Q4 the dw_unified cost-write stays gated for Steve.

NEVER expose Pointe / Justin David / Command54 customer-facing (private label).
"""
import json, re, subprocess, sys
from collections import Counter

HERE = "/Users/macstudio3/Projects/dw-five-field-step0"
PRICE = f"{HERE}/vendor-pricelists/pointe-jd-prices.json"
OUT = f"{HERE}/out/pr-pointe-cost-diff.json"

alnum = lambda s: re.sub(r'[^A-Z0-9]', '', (s or '').upper())
nstat = lambda s: (s or '').strip().upper()


def live_rows():
    sql = ("select shopify_id||'|'||coalesce(mfr_sku,'')||'|'||coalesce(dw_sku,'')"
           "||'|'||coalesce(vendor,'')||'|'||coalesce(status,'')||'|'"
           "||coalesce(cost::text,'')||'|'||replace(coalesce(title,''),'|','')"
           " from shopify_products where mfr_sku like 'JD-%';")
    out = subprocess.check_output(["psql", "-d", "dw_unified", "-tAc", sql], text=True)
    rows = []
    for line in out.splitlines():
        p = line.split('|')
        if len(p) < 7:
            continue
        rows.append(dict(shopify_id=p[0], mfr_sku=p[1], dw_sku=p[2], vendor=p[3],
                         status=p[4], cost=p[5], title=p[6]))
    return rows


def main():
    d = json.load(open(PRICE))
    idx = {}
    for r in d:
        if not r.get('jd') or '/' not in r['jd']:
            continue
        p, c = r['jd'].split('/', 1)
        idx.setdefault(alnum(p) + alnum(c), []).append(r)

    rows = live_rows()
    lkey = lambda m: alnum(m[3:] if m.upper().startswith('JD-') else m)

    matched, unmatched = [], []
    for row in rows:
        pr = idx.get(lkey(row['mfr_sku']))
        if pr:
            best = sorted(pr, key=lambda r: (1 if r.get('cut_yd_price') else 0,
                          {'STANDARD': 3, 'ACTIVE': 2}.get(nstat(r['status']), 0)),
                          reverse=True)[0]
            matched.append((row, best))
        else:
            unmatched.append(row['mfr_sku'])

    out_rows = []
    for r, b in matched:
        st, cost, tariff = nstat(b['status']), b.get('cut_yd_price'), b.get('tariff')
        load = bool(cost and st in ('STANDARD', 'ACTIVE'))
        out_rows.append(dict(
            shopify_id=r['shopify_id'], mfr_sku=r['mfr_sku'], dw_sku=r['dw_sku'] or None,
            vendor=r['vendor'], shopify_status=r['status'], current_cost=r['cost'] or None,
            private_label_title=r['title'], matched_jd=b.get('jd'), matched_pointe=b.get('pointe'),
            pointe_status=b.get('status'), type=b.get('type'),
            proposed_cost=cost, tariff_passthrough=tariff,
            proposed_retail=round(cost / 0.65 / 0.85, 2) if cost else None,
            will_load=load,
            block_reason=None if load else ('no_price' if not cost else 'discontinued_or_mto'),
        ))

    unlock = [x for x in out_rows if x['will_load']]
    blocked = [x for x in out_rows if not x['will_load']]
    costs = [x['proposed_cost'] for x in unlock]
    retails = [x['proposed_retail'] for x in unlock]
    summary = dict(
        generated='2026-06-21', mode='READ-ONLY dry-run (cost write GATED for Steve)',
        live_jd_mfr_rows=len(rows), matched=len(matched), unmatched=len(unmatched),
        unlock_priced_standard_active=len(unlock),
        unlock_currently_active_and_nocost_in_shopify=len(
            [x for x in unlock if x['shopify_status'] == 'ACTIVE' and not x['current_cost']]),
        blocked_total=len(blocked),
        blocked_breakdown=dict(Counter(x['block_reason'] for x in blocked)),
        cost_range=[min(costs), max(costs)] if costs else None,
        retail_range=[min(retails), max(retails)] if retails else None,
        unlock_vendor_split=dict(Counter(x['vendor'] for x in unlock)),
    )
    json.dump(dict(summary=summary, rows=out_rows), open(OUT, 'w'), indent=1)
    print(json.dumps(summary, indent=1))


if __name__ == '__main__':
    main()