new product defaults are set correctly

This commit is contained in:
Ben Miller
2024-11-17 06:57:03 -07:00
parent 475eee70ea
commit 5d0ae653fa
6 changed files with 253 additions and 75 deletions

7
package-lock.json generated
View File

@ -11,7 +11,7 @@
"@types/google-apps-script": "^1.0.85", "@types/google-apps-script": "^1.0.85",
"gas-webpack-plugin": "^2.6.0", "gas-webpack-plugin": "^2.6.0",
"graphql-tag": "^2.12.6", "graphql-tag": "^2.12.6",
"shopify-admin-api-typings": "^1.2.2", "shopify-admin-api-typings": "github:beepmill/shopify-admin-api-typings",
"ts-loader": "^9.5.1", "ts-loader": "^9.5.1",
"webpack": "^5.96.1", "webpack": "^5.96.1",
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"
@ -1474,9 +1474,8 @@
} }
}, },
"node_modules/shopify-admin-api-typings": { "node_modules/shopify-admin-api-typings": {
"version": "1.2.2", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/shopify-admin-api-typings/-/shopify-admin-api-typings-1.2.2.tgz", "resolved": "git+ssh://git@github.com/beepmill/shopify-admin-api-typings.git#e8ba51acff0a4e66c31ee61d62f5244ad4f4233a",
"integrity": "sha512-n9NSBPBKX+TSfAr8ibpXKoGdHCUCY6WBKV2FhD9xvjGG3DIqqUylqhC3OXKiWcrTQuyb3WnaRCvl16i3uhJMiQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {

View File

@ -13,7 +13,7 @@
"@types/google-apps-script": "^1.0.85", "@types/google-apps-script": "^1.0.85",
"gas-webpack-plugin": "^2.6.0", "gas-webpack-plugin": "^2.6.0",
"graphql-tag": "^2.12.6", "graphql-tag": "^2.12.6",
"shopify-admin-api-typings": "^1.2.2", "shopify-admin-api-typings": "github:beepmill/shopify-admin-api-typings",
"ts-loader": "^9.5.1", "ts-loader": "^9.5.1",
"webpack": "^5.96.1", "webpack": "^5.96.1",
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"

View File

@ -2,6 +2,9 @@
"folders": [ "folders": [
{ {
"path": "." "path": "."
},
{
"path": "../shopify-admin-api-typings"
} }
], ],
"settings": { "settings": {

View File

@ -30,7 +30,7 @@ export class Product {
type: string = "" type: string = ""
weight_grams: number = 0 weight_grams: number = 0
photos: string = "" photos: string = ""
shopify_product: ShopifyProduct shopify_product: shopify.Product
shopify_default_variant_id: string = "" shopify_default_variant_id: string = ""
shopify_default_option_id: string = "" shopify_default_option_id: string = ""
shopify_default_option_value_id: string = "" shopify_default_option_value_id: string = ""
@ -73,52 +73,17 @@ export class Product {
} }
} }
MatchToShopifyProduct(shop: Shop) { MatchToShopifyProduct(shop: Shop): shopify.Product {
/* if (this.shopify_id.startsWith("gid://shopify/Product/")) { let product = shop.GetProductBySku(this.sku)
return if (product == undefined || product.id == undefined || product.id == "") {
} */ console.log("MatchToShopifyProduct: no product matched")
let query = new ShopifyProductsQuery("sku:" + this.sku, ["id", "title"])
console.log(query.JSON)
let response = shop.shopifyGraphQLAPI(query.JSON)
console.log(response)
let productsResponse = new ShopifyProductsResponse(response.content)
if (productsResponse.products.edges.length <= 0) {
console.log("no products matched")
return return
} }
if (productsResponse.products.edges.length > 1) { this.shopify_product = product
console.log("more than one product matched")
return
}
this.shopify_product = productsResponse.products.edges[0].node
this.shopify_id = this.shopify_product.id.toString() this.shopify_id = this.shopify_product.id.toString()
this.shopify_default_variant_id = this.shopify_default_variant_id = product.variants.nodes[0].id
productsResponse.products.edges[0].node.variants.nodes[0].id this.shopify_default_option_id = product.options[0].id
console.log(JSON.stringify(productsResponse, null, 2)) this.shopify_default_option_value_id = product.options[0].optionValues[0].id
console.log(JSON.stringify(productsResponse.products, null, 2))
console.log(JSON.stringify(productsResponse.products.edges[0], null, 2))
console.log(
JSON.stringify(productsResponse.products.edges[0].node, null, 2)
)
console.log(
JSON.stringify(
productsResponse.products.edges[0].node.options[0],
null,
2
)
)
console.log(
JSON.stringify(
productsResponse.products.edges[0].node.options[0].id,
null,
2
)
)
this.shopify_default_option_id =
productsResponse.products.edges[0].node.options[0].id
this.shopify_default_option_value_id =
productsResponse.products.edges[0].node.options[0].optionValues[0].id
let productInventorySheet = let productInventorySheet =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("product_inventory") SpreadsheetApp.getActiveSpreadsheet().getSheetByName("product_inventory")
let row = getRowByColumnValue("product_inventory", "sku", this.sku) let row = getRowByColumnValue("product_inventory", "sku", this.sku)
@ -134,7 +99,7 @@ export class Product {
if (this.shopify_id != "") { if (this.shopify_id != "") {
sps.id = this.shopify_id sps.id = this.shopify_id
} }
if (this.shopify_status.match('DRAFT|ACTIVE|ARCHIVED')) { if (this.shopify_status.match("DRAFT|ACTIVE|ARCHIVED")) {
sps.status = this.shopify_status sps.status = this.shopify_status
} }
sps.productType = this.product_type sps.productType = this.product_type
@ -151,26 +116,50 @@ export class Product {
variant.price = this.price variant.price = this.price
sps.variants.push(variant) sps.variants.push(variant)
console.log("ToShopifyProductSet:\n" + JSON.stringify(sps, null, 2)) console.log("ToShopifyProductSet:\n" + JSON.stringify(sps, null, 2))
//TODO: add sales channels
//TODO: add initial inventory //TODO: add initial inventory
return sps return sps
} }
UpdateShopifyProduct(shop: Shop) { UpdateShopifyProduct(shop: Shop) {
console.log("UpdateShopifyProduct()")
var newProduct = false
let config = new Config()
this.MatchToShopifyProduct(shop) this.MatchToShopifyProduct(shop)
if (this.shopify_id == "") {
console.log(
"UpdateShopifyProduct: no product matched, this will be a new product"
)
newProduct = true
}
console.log("UpdateShopifyProduct: calling productSet")
let sps = this.ToShopifyProductSet() let sps = this.ToShopifyProductSet()
console.log("sps: " + JSON.stringify(sps)) console.log("sps: " + JSON.stringify(sps))
let query = new ShopifyProductSetQuery(sps) let query = new ShopifyProductSetQuery(sps)
console.log(JSON.stringify(query))
let response = shop.shopifyGraphQLAPI(query.JSON) let response = shop.shopifyGraphQLAPI(query.JSON)
console.log(JSON.stringify(response, null, 2)) let product = response.content.data.productSet.product
this.MatchToShopifyProduct(shop) this.shopify_id = product.id
if (newProduct) {
console.log("UpdateShopifyProduct: setting defaults on new product")
let item: shopify.InventoryItem
do {
console.log("UpdateShopifyProduct: attempting to get inventory item")
item = shop.GetInventoryItemBySku(this.sku)
} while (item.id == "")
console.log("UpdateShopifyProduct: setting defaults on inventory item")
shop.SetInventoryItemDefaults(item, config)
console.log("UpdateShopifyProduct: publishing to online store")
response = this.PublishToShopifyOnlineStore(shop) response = this.PublishToShopifyOnlineStore(shop)
this.MatchToShopifyProduct(shop) console.log("UpdateShopifyProduct: adjusting inventory item quantity")
shop.UpdateInventoryItemQuantity(item, 1, config)
console.log(JSON.stringify(response, null, 2)) console.log(JSON.stringify(response, null, 2))
} }
// Update to include new changes
console.log("UpdateShopifyProduct: updating inventory match")
this.MatchToShopifyProduct(shop)
}
PublishToShopifyOnlineStore(shop: Shop) { PublishToShopifyOnlineStore(shop: Shop) {
console.log("PublishToShopifyOnlineStore")
let config = new Config() let config = new Config()
let query = /* GraphQL */ ` let query = /* GraphQL */ `
mutation publishablePublish($id: ID!, $input: [PublicationInput!]!) { mutation publishablePublish($id: ID!, $input: [PublicationInput!]!) {
@ -191,12 +180,13 @@ export class Product {
message message
} }
} }
}`
let variables = {
"id": this.shopify_id,
"input": {
"publicationId": config.shopifyStorePublicationId
} }
`
let variables = {
id: this.shopify_id,
input: {
publicationId: config.shopifyStorePublicationId,
},
} }
let j = `{ let j = `{
"query": ${formatGqlForJSON(String(query))}, "query": ${formatGqlForJSON(String(query))},

View File

@ -7,7 +7,9 @@ export class Config {
shopifyAdminApiAccessToken: string shopifyAdminApiAccessToken: string
shopifyApiURI: string shopifyApiURI: string
shopifyStorePublicationId: string shopifyStorePublicationId: string
shopifyLocationId: string
shopifyCountryCodeOfOrigin: string
shopifyProvinceCodeOfOrigin: string
constructor() { constructor() {
let ss = SpreadsheetApp.getActive() let ss = SpreadsheetApp.getActive()
@ -49,5 +51,23 @@ export class Config {
"shopifyStorePublicationId", "shopifyStorePublicationId",
"value" "value"
) )
this.shopifyLocationId = vlookupByColumns(
"vars",
"key",
"shopifyLocationId",
"value"
)
this.shopifyCountryCodeOfOrigin = vlookupByColumns(
"vars",
"key",
"shopifyCountryCodeOfOrigin",
"value"
)
this.shopifyProvinceCodeOfOrigin = vlookupByColumns(
"vars",
"key",
"shopifyProvinceCodeOfOrigin",
"value"
)
} }
} }

View File

@ -517,6 +517,160 @@ export class Shop {
} while (!done) } while (!done)
} }
GetProductBySku(sku: string) {
console.log("GetProductBySku('" + sku + "')")
let gql = /* GraphQL */ `
query productBySku {
products(first: 1, query: "sku:${sku}") {
edges {
node {
id
title
handle
variants(first: 1) {
nodes {
id
sku
}
}
options {
id
name
optionValues {
id
name
}
}
}
}
}
}
`
let query = buildGqlQuery(gql, {})
let response = this.shopifyGraphQLAPI(query)
if (response.content.data.products.edges.length <= 0) {
console.log("GetProductBySku: no product matched")
return
}
let product = response.content.data.products.edges[0].node
console.log("Product found:\n" + JSON.stringify(product, null, 2))
return product
}
GetInventoryItemBySku(sku: string) {
console.log('GetInventoryItemBySku("' + sku + '")')
let gql = /* GraphQL */ `
query inventoryItems {
inventoryItems(first:1, query:"sku:${sku}") {
edges {
node {
id
tracked
sku
}
}
}
}
`
let query = buildGqlQuery(gql, {})
let response = this.shopifyGraphQLAPI(query)
let item: shopify.InventoryItem =
response.content.data.inventoryItems.edges[0].node
console.log(
"GetInventoryItemBySku: found item:\n" + JSON.stringify(item, null, 2)
)
return item
}
UpdateInventoryItemQuantity(
item: shopify.InventoryItem,
delta: number = 1,
config: Config
) {
console.log("UpdateInventoryItemQuantity(" + JSON.stringify(item) + ")")
let gql = /* GraphQL */ `
mutation inventoryAdjustQuantities(
$input: InventoryAdjustQuantitiesInput!
) {
inventoryAdjustQuantities(input: $input) {
userErrors {
field
message
}
inventoryAdjustmentGroup {
createdAt
reason
referenceDocumentUri
changes {
name
delta
}
}
}
}
`
let variables = {
input: {
reason: "correction",
name: "available",
changes: [
{
delta: delta,
inventoryItemId: item.id,
locationId: config.shopifyLocationId,
},
],
},
}
let query = buildGqlQuery(gql, variables)
let response = this.shopifyGraphQLAPI(query)
let newItem: shopify.InventoryItem = response.content
console.log("new item:\n" + JSON.stringify(newItem, null, 2))
return newItem
}
SetInventoryItemDefaults(item: shopify.InventoryItem, config: Config) {
let gql = /* GraphQL */ `
mutation inventoryItemUpdate($id: ID!, $input: InventoryItemInput!) {
inventoryItemUpdate(id: $id, input: $input) {
inventoryItem {
id
unitCost {
amount
}
tracked
countryCodeOfOrigin
provinceCodeOfOrigin
harmonizedSystemCode
countryHarmonizedSystemCodes(first: 1) {
edges {
node {
harmonizedSystemCode
countryCode
}
}
}
}
userErrors {
message
}
}
}
`
let variables = {
id: item.id,
input: {
tracked: true,
countryCodeOfOrigin: config.shopifyCountryCodeOfOrigin,
provinceCodeOfOrigin: config.shopifyProvinceCodeOfOrigin,
},
}
let query = buildGqlQuery(gql, variables)
let response = this.shopifyGraphQLAPI(query)
let newItem: shopify.InventoryItem = response.content
return newItem
}
shopifyAPI(endpoint: string, query: {}, next = "") { shopifyAPI(endpoint: string, query: {}, next = "") {
var options: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions = { var options: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions = {
method: "get", method: "get",
@ -541,6 +695,7 @@ export class Shop {
} }
shopifyGraphQLAPI(query: {}, next = "") { shopifyGraphQLAPI(query: {}, next = "") {
console.log("shopifyGraphQLAPI:query: " + JSON.stringify(query))
let endpoint = Shop.endpoints.graphql let endpoint = Shop.endpoints.graphql
let options: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions = { let options: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions = {
method: "post", method: "post",
@ -552,10 +707,13 @@ export class Shop {
muteHttpExceptions: true, muteHttpExceptions: true,
} }
var url = this.buildURL(endpoint) var url = this.buildURL(endpoint)
console.log(UrlFetchApp.getRequest(url, options)) console.log(
"shopifyGraphQLAPI sending request:\n" +
JSON.stringify(UrlFetchApp.getRequest(url, options), null, 2)
)
var resp = UrlFetchApp.fetch(url, options) var resp = UrlFetchApp.fetch(url, options)
let content = resp.getContentText() let content = resp.getContentText()
console.log(content) console.log("shopifyGraphQLAPI got response:\n" + content)
let content_json = JSON.parse(content) let content_json = JSON.parse(content)
console.log(JSON.stringify(content_json, null, 2)) console.log(JSON.stringify(content_json, null, 2))
@ -737,8 +895,8 @@ export class ShopifyVariant {
optionValues: [{}] = [ optionValues: [{}] = [
{ {
optionName: "Title", optionName: "Title",
name: "Default Title" name: "Default Title",
} },
] ]
} }
@ -756,7 +914,7 @@ export class ShopifyProductsQuery {
query: string = "", query: string = "",
fields: string[] = ["id", "title", "handle"], fields: string[] = ["id", "title", "handle"],
cursor: string = "", cursor: string = "",
pageSize: number = 10, pageSize: number = 10
) { ) {
let cursorText: string let cursorText: string
if (cursor == "") { if (cursor == "") {
@ -838,7 +996,8 @@ export class ShopifyProductSetQuery {
let j = `{ let j = `{
"query": ${formatGqlForJSON(String(this.GQL))}, "query": ${formatGqlForJSON(String(this.GQL))},
"variables": { "variables": {
"productSet": ${JSON.stringify(product)} "productSet": ${JSON.stringify(product)},
"synchronous": ${synchronous}
} }
}` }`
console.log(j) console.log(j)
@ -862,9 +1021,9 @@ export class ShopifyProductSetInput {
{ {
name: "Title", name: "Title",
values: { values: {
name: "Default Title" name: "Default Title",
} },
} },
] ]
} }
@ -904,4 +1063,11 @@ export function getShopifyProducts() {
shop.GetProducts() shop.GetProducts()
} }
(global as any) export function buildGqlQuery(gql: string, variables: {}) {
let query = `{
"query": ${formatGqlForJSON(String(gql))},
"variables": ${JSON.stringify(variables)}
}`
console.log("buildGqlQuery:\n" + query)
return JSON.parse(query)
}