[object Object]

← back to Dig Concept Finish

DIG concept-sample finish: rename 4 quarantine samples + price+rename 1 single-variant sample to 2ft x 1ft Sample/$12; rolls out of scope, B sku left flagged

ff6afc98d031c9aacff5bc15e574d6b4b6b72a58 · 2026-06-24 13:19:48 -0700 · Steve

Files touched

Diff

commit ff6afc98d031c9aacff5bc15e574d6b4b6b72a58
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jun 24 13:19:48 2026 -0700

    DIG concept-sample finish: rename 4 quarantine samples + price+rename 1 single-variant sample to 2ft x 1ft Sample/$12; rolls out of scope, B sku left flagged
---
 .gitignore           |   5 ++
 finish.mjs           | 105 +++++++++++++++++++++++++++++++++++++++
 finish_result.json   |  44 ++++++++++++++++
 identify.mjs         | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++
 identify_result.json | 128 +++++++++++++++++++++++++++++++++++++++++++++++
 verify_rolls.mjs     |  15 ++++++
 6 files changed, 435 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/finish.mjs b/finish.mjs
new file mode 100644
index 0000000..ea48aba
--- /dev/null
+++ b/finish.mjs
@@ -0,0 +1,105 @@
+#!/usr/bin/env node
+// Finisher: apply real-sample treatment to the remaining DIG- CONCEPT-SAMPLE variants ONLY.
+// (A) 4 quarantine sample variants on product 7664622272563 — Size rename -> "2ft x 1ft Sample" (already $12).
+// (B) 1 single-variant sample on product (handle ..._wallpapers), vid 43321302351923 —
+//     price -> $12.00 AND Size value -> "2ft x 1ft Sample". SKU LEFT UNTOUCHED (corrupt @gpt, non-derivable -> flagged).
+// ZERO roll writes. ZERO archived writes. ZERO SKU writes.
+// Requires --apply to write; otherwise DRY RUN.
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+const __dir = path.dirname(fileURLToPath(import.meta.url));
+const APPLY = process.argv.includes('--apply');
+const DOMAIN = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const URL = `https://${DOMAIN}/admin/api/${API}/graphql.json`;
+const env = fs.readFileSync('/Users/stevestudio2/Projects/Designer-Wallcoverings/.env', 'utf8');
+const TOK = (env.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m) || [])[1]?.trim();
+if (!TOK) { console.error('No SHOPIFY_ADMIN_TOKEN'); process.exit(1); }
+
+const LABEL = '2ft x 1ft Sample';
+const PRICE = '12.00';
+
+async function gql(query, variables) {
+  const r = await fetch(URL, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json', 'X-Shopify-Access-Token': TOK },
+    body: JSON.stringify({ query, variables }),
+  });
+  const j = await r.json();
+  if (j.errors) { console.error(JSON.stringify(j.errors, null, 2)); throw new Error('gql err'); }
+  return j.data;
+}
+
+const MUT = `mutation($productId:ID!, $variants:[ProductVariantsBulkInput!]!){
+  productVariantsBulkUpdate(productId:$productId, variants:$variants){
+    productVariants{ id title price sku selectedOptions{name value} }
+    userErrors{ field message }
+  }
+}`;
+
+// Each group = one product's bulk update.
+// Pocket A: rename Size for the 4 quarantine variants (price already $12, do NOT include price).
+const GROUP_A = {
+  label: 'POCKET A (product 7664622272563) - Size rename only',
+  productId: 'gid://shopify/Product/7664622272563',
+  variants: [
+    { id: 'gid://shopify/ProductVariant/44019704660019', optionValues: [{ name: LABEL, optionName: 'Size' }] },
+    { id: 'gid://shopify/ProductVariant/44019704692787', optionValues: [{ name: LABEL, optionName: 'Size' }] },
+    { id: 'gid://shopify/ProductVariant/44035167813683', optionValues: [{ name: LABEL, optionName: 'Size' }] },
+    { id: 'gid://shopify/ProductVariant/44035167846451', optionValues: [{ name: LABEL, optionName: 'Size' }] },
+  ],
+};
+
+// Pocket B: price -> $12 AND Size rename. SKU untouched. productId resolved at runtime from the variant.
+const B_VID = 'gid://shopify/ProductVariant/43321302351923';
+
+(async () => {
+  // Resolve B product id + safety re-check (single variant, ACTIVE, is a sample, NOT a roll).
+  const bd = await gql(`query($id:ID!){ productVariant(id:$id){ id title price sku
+    selectedOptions{name value} product{ id handle status variants(first:50){nodes{id}} } } }`, { id: B_VID });
+  const bv = bd.productVariant;
+  if (!bv) { console.error('B variant not found — abort'); process.exit(1); }
+  const bp = bv.product;
+  const bSize = (bv.selectedOptions.find(o => o.name === 'Size') || {}).value;
+  // Safety gates for B:
+  if (bp.status === 'ARCHIVED') { console.error('B product ARCHIVED — abort (no archived writes)'); process.exit(1); }
+  if (bp.variants.nodes.length !== 1) { console.error(`B product has ${bp.variants.nodes.length} variants — expected single-variant sample. ABORT to avoid touching a roll.`); process.exit(1); }
+  if (bSize === LABEL) console.error('B already at target label (idempotent, will still set).');
+
+  const GROUP_B = {
+    label: `POCKET B (product ${bp.id.split('/').pop()}, handle ${bp.handle}) - price + Size rename, SKU untouched`,
+    productId: bp.id,
+    variants: [
+      { id: B_VID, price: PRICE, optionValues: [{ name: LABEL, optionName: 'Size' }] }, // NO sku field => SKU unchanged
+    ],
+    bMeta: { currentSize: bSize, currentPrice: bv.price, skuRedacted: bv.sku && bv.sku.includes('@gpt') ? '@gpt(REDACTED)' : bv.sku },
+  };
+
+  const groups = [GROUP_A, GROUP_B];
+
+  console.log(`MODE: ${APPLY ? 'APPLY (LIVE WRITE)' : 'DRY RUN'} | label="${LABEL}" | price=$${PRICE}`);
+  console.log(`Pocket A: 4 Size renames (no price change). Pocket B: 1 price+rename (vid ${B_VID.split('/').pop()}, Size "${GROUP_B.bMeta.currentSize}" -> "${LABEL}", $${GROUP_B.bMeta.currentPrice} -> $${PRICE}, SKU=${GROUP_B.bMeta.skuRedacted} UNCHANGED).`);
+
+  if (!APPLY) {
+    console.log('\nDRY RUN — no writes. Re-run with --apply.');
+    fs.writeFileSync(path.join(__dir, 'finish_plan.json'), JSON.stringify(groups.map(g => ({ ...g, bMeta: g.bMeta || undefined })), null, 2));
+    return;
+  }
+
+  const results = [];
+  for (const g of groups) {
+    const d = await gql(MUT, { productId: g.productId, variants: g.variants });
+    const res = d.productVariantsBulkUpdate;
+    const errs = res.userErrors || [];
+    console.log(`\n[${g.label}] userErrors: ${errs.length ? JSON.stringify(errs) : 'NONE'}`);
+    for (const v of (res.productVariants || [])) {
+      const size = (v.selectedOptions.find(o => o.name === 'Size') || {}).value;
+      console.log(`  -> ${v.id.split('/').pop()} | "${v.title}" | $${v.price} | Size="${size}"`);
+    }
+    results.push({ label: g.label, userErrors: errs, productVariants: (res.productVariants||[]).map(v => ({ id: v.id, title: v.title, price: v.price, size: (v.selectedOptions.find(o=>o.name==='Size')||{}).value })) });
+    await new Promise(r => setTimeout(r, 600)); // ~2 req/s
+  }
+  fs.writeFileSync(path.join(__dir, 'finish_result.json'), JSON.stringify(results, null, 2));
+  console.log('\nDone. Result written to finish_result.json');
+})();
diff --git a/finish_result.json b/finish_result.json
new file mode 100644
index 0000000..c61da09
--- /dev/null
+++ b/finish_result.json
@@ -0,0 +1,44 @@
+[
+  {
+    "label": "POCKET A (product 7664622272563) - Size rename only",
+    "userErrors": [],
+    "productVariants": [
+      {
+        "id": "gid://shopify/ProductVariant/44019704660019",
+        "title": "Gold Textured Metallic / 2ft x 1ft Sample",
+        "price": "12.00",
+        "size": "2ft x 1ft Sample"
+      },
+      {
+        "id": "gid://shopify/ProductVariant/44019704692787",
+        "title": "Peel and Stick / 2ft x 1ft Sample",
+        "price": "12.00",
+        "size": "2ft x 1ft Sample"
+      },
+      {
+        "id": "gid://shopify/ProductVariant/44035167813683",
+        "title": "Silver Textured Metallic / 2ft x 1ft Sample",
+        "price": "12.00",
+        "size": "2ft x 1ft Sample"
+      },
+      {
+        "id": "gid://shopify/ProductVariant/44035167846451",
+        "title": "Type 2 PVC Free Paper / 2ft x 1ft Sample",
+        "price": "12.00",
+        "size": "2ft x 1ft Sample"
+      }
+    ]
+  },
+  {
+    "label": "POCKET B (product 7582548066355, handle dig_74754_vintage_wallpaper_designer_wallpapers) - price + Size rename, SKU untouched",
+    "userErrors": [],
+    "productVariants": [
+      {
+        "id": "gid://shopify/ProductVariant/43321302351923",
+        "title": "2ft x 1ft Sample",
+        "price": "12.00",
+        "size": "2ft x 1ft Sample"
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/identify.mjs b/identify.mjs
new file mode 100644
index 0000000..8ce00bb
--- /dev/null
+++ b/identify.mjs
@@ -0,0 +1,138 @@
+#!/usr/bin/env node
+// READ-ONLY. Identify the remaining DIG- CONCEPT-SAMPLE variants in pockets (A) and (B).
+// (A) the 4 quarantine sample variants on product 7664622272563 (already $12, SKU repaired to Dig-25-25-*-sample,
+//     but Size option value still "Concept Sample").
+// (B) the single-variant $4.25 CONCEPT-SAMPLE on handle dig_74754_..._wallpapers (vid 43321302351923).
+// Also store-wide scan: any DIG- variant whose Size/sample option is still "Concept Sample"/"Concept Samples".
+// NO WRITES.
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+const __dir = path.dirname(fileURLToPath(import.meta.url));
+const DOMAIN = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const URL = `https://${DOMAIN}/admin/api/${API}/graphql.json`;
+const env = fs.readFileSync('/Users/stevestudio2/Projects/Designer-Wallcoverings/.env', 'utf8');
+const TOK = (env.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m) || [])[1]?.trim();
+if (!TOK) { console.error('No SHOPIFY_ADMIN_TOKEN'); process.exit(1); }
+
+async function gql(query, variables) {
+  const r = await fetch(URL, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json', 'X-Shopify-Access-Token': TOK },
+    body: JSON.stringify({ query, variables }),
+  });
+  const j = await r.json();
+  if (j.errors) { console.error(JSON.stringify(j.errors, null, 2)); throw new Error('gql err'); }
+  return j.data;
+}
+
+const CONCEPT = new Set(['Concept Sample', 'Concept Samples']);
+
+// --- Pocket A: product 7664622272563, dump all variants ---
+async function dumpProduct(pid) {
+  const q = `query($id:ID!){ product(id:$id){ id title handle status options{name values}
+    variants(first:50){ nodes{ id title price sku selectedOptions{name value} } } } }`;
+  const d = await gql(q, { id: `gid://shopify/Product/${pid}` });
+  return d.product;
+}
+
+// --- Pocket B: find single-variant ..._wallpapers sample by handle ---
+async function byHandle(handle) {
+  const q = `query($h:String!){ products(first:5, query:$h){ nodes{ id title handle status
+    options{name values}
+    variants(first:50){ nodes{ id title price sku selectedOptions{name value} } } } } }`;
+  const d = await gql(q, { h: `handle:${handle}` });
+  return d.products.nodes;
+}
+
+// --- Store-wide: count remaining DIG- variants still labeled Concept Sample (ACTIVE/DRAFT) ---
+async function storeWideConceptCount() {
+  let cursor = null, hasNext = true;
+  const hits = [];
+  while (hasNext) {
+    const q = `query($cursor:String){ products(first:100, query:"sku:DIG-* OR sku:Dig-* OR sku:dig-*", after:$cursor){
+      pageInfo{hasNextPage endCursor}
+      nodes{ id title handle status
+        variants(first:30){ nodes{ id title price sku selectedOptions{name value} } } } } }`;
+    const d = await gql(q, { cursor });
+    for (const p of d.products.nodes) {
+      if (p.status === 'ARCHIVED') continue; // active+draft only
+      for (const v of p.variants.nodes) {
+        for (const o of v.selectedOptions) {
+          if (CONCEPT.has(o.value)) {
+            hits.push({ product: p.id, handle: p.handle, status: p.status, vid: v.id, vtitle: v.title, price: v.price, sku: v.sku, optName: o.name, optValue: o.value });
+          }
+        }
+      }
+    }
+    hasNext = d.products.pageInfo.hasNextPage;
+    cursor = d.products.pageInfo.endCursor;
+    process.stderr.write('.');
+  }
+  process.stderr.write('\n');
+  return hits;
+}
+
+const QUAR_IDS = new Set([
+  'gid://shopify/ProductVariant/44019704660019',
+  'gid://shopify/ProductVariant/44019704692787',
+  'gid://shopify/ProductVariant/44035167813683',
+  'gid://shopify/ProductVariant/44035167846451',
+]);
+const B_VID = 'gid://shopify/ProductVariant/43321302351923';
+
+const redactSku = (s) => (s && s.includes('@gpt')) ? '@gpt(REDACTED)' : s;
+
+(async () => {
+  // A
+  const pA = await dumpProduct('7664622272563');
+  const aVariants = pA.variants.nodes.filter(v => QUAR_IDS.has(v.id)).map(v => ({
+    vid: v.id, title: v.title, price: v.price, sku: redactSku(v.sku),
+    size: (v.selectedOptions.find(o => o.name === 'Size') || {}).value,
+    selectedOptions: v.selectedOptions.map(o => ({ name: o.name, value: o.value })),
+  }));
+
+  // B
+  const bNodes = await byHandle('dig_74754_vintage_wallpaper_designer_wallcoverings');
+  // also try the explicit _wallpapers handle pattern
+  let bProduct = null, bVar = null;
+  for (const p of bNodes) {
+    const hit = p.variants.nodes.find(v => v.id === B_VID);
+    if (hit) { bProduct = p; bVar = hit; break; }
+  }
+  // fallback: direct variant lookup
+  if (!bVar) {
+    const d = await gql(`query($id:ID!){ productVariant(id:$id){ id title price sku
+      selectedOptions{name value} product{ id title handle status options{name values} } } }`, { id: B_VID });
+    if (d.productVariant) { bVar = d.productVariant; bProduct = d.productVariant.product; }
+  }
+  const bOut = bVar ? {
+    product: bProduct.id, handle: bProduct.handle, title: bProduct.title, status: bProduct.status,
+    vid: bVar.id, vtitle: bVar.title, price: bVar.price, sku: redactSku(bVar.sku),
+    options: bProduct.options,
+    size: (bVar.selectedOptions.find(o => o.name === 'Size') || {}).value,
+    selectedOptions: bVar.selectedOptions.map(o => ({ name: o.name, value: o.value })),
+  } : null;
+
+  // Store-wide
+  const allHits = await storeWideConceptCount();
+  const sanitized = allHits.map(h => ({ ...h, sku: redactSku(h.sku) }));
+
+  const out = {
+    pocketA: { product: pA.id, handle: pA.handle, title: pA.title, status: pA.status, options: pA.options, variants: aVariants },
+    pocketB: bOut,
+    storeWideConceptRemaining: { count: sanitized.length, hits: sanitized },
+  };
+  fs.writeFileSync(path.join(__dir, 'identify_result.json'), JSON.stringify(out, null, 2));
+
+  console.log('=== POCKET A (product 7664622272563) ===');
+  console.log('status:', pA.status, '| handle:', pA.handle);
+  for (const v of aVariants) console.log(`  ${v.vid.split('/').pop()} | "${v.title}" | $${v.price} | Size="${v.size}" | sku=${v.sku}`);
+  console.log('\n=== POCKET B ===');
+  if (bOut) console.log(`  ${bOut.vid.split('/').pop()} | handle=${bOut.handle} | status=${bOut.status} | "${bOut.vtitle}" | $${bOut.price} | Size="${bOut.size}" | sku=${bOut.sku}\n  options=${JSON.stringify(bOut.options)}`);
+  else console.log('  B variant NOT FOUND');
+  console.log('\n=== STORE-WIDE remaining "Concept Sample" (active+draft DIG) ===');
+  console.log('  count:', sanitized.length);
+  for (const h of sanitized) console.log(`  ${h.vid.split('/').pop()} | ${h.status} | ${h.handle} | "${h.vtitle}" | $${h.price} | ${h.optName}="${h.optValue}" | sku=${h.sku}`);
+})();
diff --git a/identify_result.json b/identify_result.json
new file mode 100644
index 0000000..dce66bf
--- /dev/null
+++ b/identify_result.json
@@ -0,0 +1,128 @@
+{
+  "pocketA": {
+    "product": "gid://shopify/Product/7664622272563",
+    "handle": "dig_74754_vintage_wallpaper_designer_wallcoverings-1",
+    "title": "Hideo Bamboo Vintage 1970's Mylar Retro | Architectural Wallcoverings",
+    "status": "ACTIVE",
+    "options": [
+      {
+        "name": "Wallpaper Type",
+        "values": [
+          "Type 2 Textured Vinyl",
+          "Gold Textured Metallic",
+          "Peel and Stick",
+          "Prepasted",
+          "Silver Textured Metallic",
+          "Type 2 PVC Free Paper"
+        ]
+      },
+      {
+        "name": "Size",
+        "values": [
+          "2ft x 1ft Sample",
+          "2ft x 12ft",
+          "2ft x 27ft"
+        ]
+      }
+    ],
+    "variants": [
+      {
+        "vid": "gid://shopify/ProductVariant/44019704660019",
+        "title": "Gold Textured Metallic / 2ft x 1ft Sample",
+        "price": "12.00",
+        "sku": "Dig-25-25-gold-metallic-sample",
+        "size": "2ft x 1ft Sample",
+        "selectedOptions": [
+          {
+            "name": "Wallpaper Type",
+            "value": "Gold Textured Metallic"
+          },
+          {
+            "name": "Size",
+            "value": "2ft x 1ft Sample"
+          }
+        ]
+      },
+      {
+        "vid": "gid://shopify/ProductVariant/44019704692787",
+        "title": "Peel and Stick / 2ft x 1ft Sample",
+        "price": "12.00",
+        "sku": "Dig-25-25-peel-stick-sample",
+        "size": "2ft x 1ft Sample",
+        "selectedOptions": [
+          {
+            "name": "Wallpaper Type",
+            "value": "Peel and Stick"
+          },
+          {
+            "name": "Size",
+            "value": "2ft x 1ft Sample"
+          }
+        ]
+      },
+      {
+        "vid": "gid://shopify/ProductVariant/44035167813683",
+        "title": "Silver Textured Metallic / 2ft x 1ft Sample",
+        "price": "12.00",
+        "sku": "Dig-25-25-silver-metallic-sample",
+        "size": "2ft x 1ft Sample",
+        "selectedOptions": [
+          {
+            "name": "Wallpaper Type",
+            "value": "Silver Textured Metallic"
+          },
+          {
+            "name": "Size",
+            "value": "2ft x 1ft Sample"
+          }
+        ]
+      },
+      {
+        "vid": "gid://shopify/ProductVariant/44035167846451",
+        "title": "Type 2 PVC Free Paper / 2ft x 1ft Sample",
+        "price": "12.00",
+        "sku": "Dig-25-25-type-2-pvc-sample",
+        "size": "2ft x 1ft Sample",
+        "selectedOptions": [
+          {
+            "name": "Wallpaper Type",
+            "value": "Type 2 PVC Free Paper"
+          },
+          {
+            "name": "Size",
+            "value": "2ft x 1ft Sample"
+          }
+        ]
+      }
+    ]
+  },
+  "pocketB": {
+    "product": "gid://shopify/Product/7582548066355",
+    "handle": "dig_74754_vintage_wallpaper_designer_wallpapers",
+    "title": "Hideo Bamboo Vintage 1970'S Mylar Chinoiserie | Architectural Wallcoverings",
+    "status": "ACTIVE",
+    "vid": "gid://shopify/ProductVariant/43321302351923",
+    "vtitle": "2ft x 1ft Sample",
+    "price": "12.00",
+    "sku": "@gpt(REDACTED)",
+    "options": [
+      {
+        "name": "Size",
+        "values": [
+          "2ft x 1ft Sample"
+        ]
+      }
+    ],
+    "size": "2ft x 1ft Sample",
+    "selectedOptions": [
+      {
+        "name": "Size",
+        "value": "2ft x 1ft Sample"
+      }
+    ]
+  },
+  "storeWideConceptRemaining": {
+    "count": 0,
+    "hits": []
+  }
+}
\ No newline at end of file
diff --git a/verify_rolls.mjs b/verify_rolls.mjs
new file mode 100644
index 0000000..ab11668
--- /dev/null
+++ b/verify_rolls.mjs
@@ -0,0 +1,15 @@
+import fs from 'fs';
+const DOMAIN='designer-laboratory-sandbox.myshopify.com';
+const URL=`https://${DOMAIN}/admin/api/2024-10/graphql.json`;
+const env=fs.readFileSync('/Users/stevestudio2/Projects/Designer-Wallcoverings/.env','utf8');
+const TOK=(env.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1].trim();
+const g=async(q,v)=>{const r=await fetch(URL,{method:'POST',headers:{'Content-Type':'application/json','X-Shopify-Access-Token':TOK},body:JSON.stringify({query:q,variables:v})});return (await r.json()).data;};
+const red=s=>s&&s.includes('@gpt')?'@gpt(REDACTED)':s;
+// B product single-variant?
+const b=await g(`query($id:ID!){product(id:$id){handle status variants(first:50){nodes{id price sku selectedOptions{name value}}}}}`,{id:'gid://shopify/Product/7582548066355'});
+console.log('B product variants:',b.product.variants.nodes.length,'| status',b.product.status);
+b.product.variants.nodes.forEach(v=>console.log('  ',v.id.split('/').pop(),'$'+v.price,'sku='+red(v.sku),JSON.stringify(v.selectedOptions.map(o=>o.value))));
+// A-sibling roll variants (the wallcoverings-1 product) - confirm rolls untouched (still their $179-$668 + @gpt skus)
+const a=await g(`query($id:ID!){product(id:$id){handle status variants(first:60){nodes{id price sku selectedOptions{name value}}}}}`,{id:'gid://shopify/Product/7664622272563'});
+console.log('\nA product (wallcoverings-1) ALL variants — rolls must be unchanged:');
+a.product.variants.nodes.forEach(v=>{const size=(v.selectedOptions.find(o=>o.name==='Size')||{}).value;console.log('  ',v.id.split('/').pop(),'$'+v.price,'Size="'+size+'"','sku='+red(v.sku));});

(oldest)  ·  back to Dig Concept Finish  ·  chore: macstudio3 migration — reconcile from mac2 + repoint d1e63c3 →