← back to Dw Photo Capture

grs_per_yard.py

49 lines

#!/usr/bin/env python3
"""All GRS- products are sold PER YARD. Update: variant option 'Roll'->'Per Yard',
   global.unit_of_measure->'Priced Per Yard', dwc.order_unit->'Yard'. Price unchanged
   (already per-yard = cost*0.75). DRY default; CONFIRM=1 writes."""
import os,sys,json,time,urllib.request
SHOP="designer-laboratory-sandbox.myshopify.com";API="2024-10";DRY=os.environ.get("CONFIRM","0")!="1"
TOKEN=""
for l in open(os.path.expanduser("~/Projects/secrets-manager/.env")):
    if l.startswith("SHOPIFY_ADMIN_TOKEN="): TOKEN=l.strip().split("=",1)[1];break
H={"X-Shopify-Access-Token":TOKEN,"Content-Type":"application/json"}
def api(m,p,pl=None):
    d=json.dumps(pl).encode() if pl is not None else None
    r=urllib.request.Request(f"https://{SHOP}/admin/api/{API}"+p,data=d,headers=H,method=m)
    with urllib.request.urlopen(r,timeout=60) as x: b=x.read().decode();return x.status,(json.loads(b) if b.strip() else {})
def gql(q,v=None): return api("POST","/graphql.json",{"query":q,"variables":v or {}})[1]
Q="""query($c:String){products(first:100,query:"vendor:Fentucci",after:$c){pageInfo{hasNextPage endCursor}
 edges{node{legacyResourceId status uom:metafield(namespace:"global",key:"unit_of_measure"){value}
  v:variants(first:3){edges{node{legacyResourceId sku title}}}}}}}"""
c=None;targets=[]
while True:
    d=gql(Q,{"c":c})["data"]["products"]
    for e in d["edges"]:
        n=e["node"]
        if n["status"]=="ARCHIVED": continue
        roll=next((v["node"] for v in n["v"]["edges"] if (v["node"]["sku"] or "").upper().startswith("GRS-") and not (v["node"]["sku"] or "").upper().endswith("-SAMPLE")),None)
        if not roll: continue
        uom=(n.get("uom") or {}).get("value")
        if roll["title"]!="Per Yard" or uom!="Priced Per Yard":
            targets.append({"pid":n["legacyResourceId"],"vid":roll["legacyResourceId"],"sku":roll["sku"],"title":roll["title"],"uom":uom})
    if d["pageInfo"]["hasNextPage"]: c=d["pageInfo"]["endCursor"]
    else: break
print(f"GRS products to update -> Per Yard: {len(targets)}")
for t in targets[:6]: print(f"   {t['sku']:12} variant '{t['title']}'->Per Yard | uom '{t['uom']}'->Priced Per Yard")
if DRY: print("\n[DRY] CONFIRM=1 to apply."); sys.exit()
n=0
for t in targets:
    try:
        if t["title"]!="Per Yard":
            api("PUT",f"/variants/{t['vid']}.json",{"variant":{"id":t["vid"],"option1":"Per Yard"}})
        gql("""mutation($id:ID!){metafieldsSet(metafields:[
          {ownerId:$id,namespace:"global",key:"unit_of_measure",type:"single_line_text_field",value:"Priced Per Yard"},
          {ownerId:$id,namespace:"dwc",key:"order_unit",type:"single_line_text_field",value:"Yard"}]){userErrors{message}}}""",
          {"id":f"gid://shopify/Product/{t['pid']}"})
        n+=1
        if n%25==0: print(f"  ...{n}")
        time.sleep(0.4)
    except Exception as e: print(f"  ERR {t['sku']}: {e}")
print(f"\nDONE — set {n} GRS products to Per Yard")