[object Object]

← back to Zero Price Fix

Zero-price free-checkout fix prep — dry-run draft tooling for PR Elowen (no live writes)

0270566c412f78d391ef0c5da445767ed0a2df97 · 2026-08-01 22:11:36 -0700 · Steve Abrams

Files touched

Diff

commit 0270566c412f78d391ef0c5da445767ed0a2df97
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 1 22:11:36 2026 -0700

    Zero-price free-checkout fix prep — dry-run draft tooling for PR Elowen (no live writes)
---
 .gitignore         |  5 +++++
 README.md          |  6 ++++++
 data/worklist.json | 10 ++++++++++
 fix.py             | 30 ++++++++++++++++++++++++++++++
 reverse.py         | 16 ++++++++++++++++
 5 files changed, 67 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ff2422c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+node_modules/
+.env*
+tmp/
+*.log
+.DS_Store
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0af37d6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+# Zero-price / free-checkout fix (PREP — nothing live)
+PR "Elowen Paperweave Barley & Black Gloss" (7877230755891) has a $0.00 'Standard' variant
+(PWV-11886, inv_policy=continue) with no cost -> orderable FREE. Default fix: DRAFT (off storefront,
+reversible, keeps the $4.25 sample). Live: `ZEROFIX_CONFIRM=YES python3 fix.py --apply`. Undo: reverse.py.
+Alt (manual, rule-correct): quote-tag it if confirmed quote-only, or set a real price once cost exists.
+Memo: ~/.claude/yolo-queue/pending-approval/2026-08-01-zero-price-orderable-outlier.md
diff --git a/data/worklist.json b/data/worklist.json
new file mode 100644
index 0000000..23a1e0d
--- /dev/null
+++ b/data/worklist.json
@@ -0,0 +1,10 @@
+[
+{
+"num": "7877230755891",
+"prev_status": "active",
+"title": "Elowen Paperweave Barley & Black Gloss | Phillipe Romano",
+"zero_variant_ids": [
+44494274199603
+]
+}
+]
\ No newline at end of file
diff --git a/fix.py b/fix.py
new file mode 100644
index 0000000..9824d60
--- /dev/null
+++ b/fix.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+"""Free-checkout fix (PR Elowen). PREP / DRY-RUN BY DEFAULT.
+'Elowen Paperweave' has a $0.00 sellable 'Standard' variant (inv_policy=continue) with no cost
+-> orderable FREE. Default fix: DRAFT the product (off storefront, reversible, sample kept intact).
+Live requires --apply + ZEROFIX_CONFIRM=YES. reverse.py restores prev status.
+Alt (rule-correct, manual): quote-tag it once it's a confirmed quote-only line, or set a real price."""
+import os,sys,json,time,urllib.request,urllib.error
+HERE=os.path.dirname(os.path.abspath(__file__)); SHOP="designer-laboratory-sandbox.myshopify.com"; API="2024-10"
+def token():
+    for l in open(os.path.expanduser("~/Projects/secrets-manager/.env")):
+        if l.startswith("SHOPIFY_ADMIN_TOKEN="): return l.split("=",1)[1].strip()
+    sys.exit("no token")
+def main():
+    target="archived" if "--archive" in sys.argv else "draft"
+    live="--apply" in sys.argv and os.environ.get("ZEROFIX_CONFIRM")=="YES"
+    if "--apply" in sys.argv and not live: print("REFUSING --apply without ZEROFIX_CONFIRM=YES. DRY-RUN.\n")
+    w=json.load(open(os.path.join(HERE,"data","worklist.json")))
+    print(f"=== zero-price fix [{'LIVE' if live else 'DRY-RUN'}] -> {target.upper()} — {len(w)} product ===")
+    tok=token() if live else None
+    for r in w:
+        line=f"{r['num']} {r['title']} (prev={r['prev_status']})"
+        if not live: print(f"WOULD set -> {target} | {line}"); continue
+        try:
+            req=urllib.request.Request(f"https://{SHOP}/admin/api/{API}/products/{r['num']}.json",
+                data=json.dumps({"product":{"id":int(r['num']),"status":target}}).encode(),method="PUT",
+                headers={"X-Shopify-Access-Token":tok,"Content-Type":"application/json"})
+            urllib.request.urlopen(req,timeout=40); print(f"OK -> {target} | {line}")
+        except urllib.error.HTTPError as e: print(f"FAIL {line} HTTP {e.code}")
+    print("DRY-RUN complete. Nothing written." if not live else "applied. Reverse with reverse.py")
+if __name__=="__main__": main()
diff --git a/reverse.py b/reverse.py
new file mode 100644
index 0000000..bf18c4e
--- /dev/null
+++ b/reverse.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+"""Reverse: restore PR Elowen to its prev status. DRY-RUN default; live needs --apply + ZEROFIX_CONFIRM=YES."""
+import os,sys,json,urllib.request,urllib.error
+HERE=os.path.dirname(os.path.abspath(__file__)); SHOP="designer-laboratory-sandbox.myshopify.com"; API="2024-10"
+def token():
+    for l in open(os.path.expanduser("~/Projects/secrets-manager/.env")):
+        if l.startswith("SHOPIFY_ADMIN_TOKEN="): return l.split("=",1)[1].strip()
+    sys.exit("no token")
+live="--apply" in sys.argv and os.environ.get("ZEROFIX_CONFIRM")=="YES"
+for r in json.load(open(os.path.join(HERE,"data","worklist.json"))):
+    if not live: print(f"WOULD: {r['num']} -> {r['prev_status']}"); continue
+    tok=token()
+    req=urllib.request.Request(f"https://{SHOP}/admin/api/{API}/products/{r['num']}.json",
+        data=json.dumps({"product":{"id":int(r['num']),"status":r['prev_status']}}).encode(),method="PUT",
+        headers={"X-Shopify-Access-Token":tok,"Content-Type":"application/json"})
+    urllib.request.urlopen(req,timeout=40); print(f"OK: {r['num']} -> {r['prev_status']}")

(oldest)  ·  back to Zero Price Fix  ·  (newest)