@gameroom/cli
Version:
A command line tool for Gameroom
146 lines (144 loc) • 6.91 kB
JavaScript
const cosmetic = require('cosmetic'),
{ models: { Price, Product, Unit } } = require('@gameroom/kit'),
{ grGreen } = require('../../../helpers'),
{ ShopifyV2 } = require('../../../models'),
{ config, spinner } = require('../../../refs'),
LIMIT = 500
module.exports = async ({ containers, stores, verbose }) => {
// Gameroom units >> Shopify inventory levels
// date filter
let filter
if (config.shopify_unit_sync) {
const date = new Date(config.shopify_unit_sync)
spinner.info(`last unit sync ${date.toLocaleString()}`)
// just in case...
// date.setTime(date.getTime() - 1)
filter = { key: 'updated_at', comparison: '>', value: date }
} else {
spinner.info(`first unit sync`)
}
// Get updated units and add or update or remove them on shopify
let offset = 0, done = false, processed = {}, prices = {}
// update shopify
let added = 0, removed = 0, skipped = 0, updated = 0, current = 0, total = 0
spinner.text = `0 / 0 processed, 0 ${cosmetic.green('added')}, 0 ${cosmetic.red('removed')}, 0 ${cosmetic.yellow('updated')}, 0 skipped, 0 not offered`
while (!done) {
if (config.should_exit) break
// get batch of units
const units = await Unit.get({ filter, limit: LIMIT, offset, sort: [{ updated_at: 1 }] })
done = units.length === 0
offset += units.length
total += units.length
if (verbose) spinner.info(`got ${units.length} ${grGreen('units')}`)
// iterate through batch
for (let [i, unit] of units.entries()) {
try {
spinner.text = `${current} / ${total} processed, ${added} ${cosmetic.green('added')}, ${removed} ${cosmetic.red('removed')}, ${updated} ${cosmetic.yellow('updated')}, ${skipped} skipped`
current++
if (config.should_exit) break
if (verbose) spinner.info(`syncing gameroom ${grGreen('unit')} ${unit.id}`)
// snapshot unit properties
const { properties: { shopify_id }, updated_at } = unit
// check updated_at
if (updated_at.equals(processed[unit.id])) {
config.shopify_unit_sync = new Date(updated_at * 1000)
if (verbose) spinner.succeed(`already processed ${grGreen('unit')}`)
skipped++
continue
}
// get store and container
const store = stores.find(s => s.id === unit.store_id)
const container = containers.find(s => s.id === unit.container_id)
// shopifyable checks
if (verbose) {
if (!store) spinner.warn(`missing ${cosmetic.green('store')}`)
if (!store?.shopify_id) spinner.warn(`missing ${cosmetic.green('location')}`)
if (!container) spinner.warn(`missing ${cosmetic.green('container')}`)
if (!unit.product_id) spinner.warn(`missing ${grGreen('product')}`)
if (!unit.price_id) spinner.warn(`missing ${grGreen('price')}`)
// if (!unit.image) spinner.warn(`missing ${grGreen('image')}`)
}
// get shopify inventory level
let shopify_inventory = shopify_id ? await ShopifyV2.Variant.getWithId(shopify_id) : null
// // conditions
const shopifyable = store?.offered && store?.shopify_id && container?.offered && unit.offered && unit.quantity > 0
const add = !shopify_inventory && shopifyable
const remove = shopify_inventory && !shopifyable
const update = shopify_inventory && shopifyable
if (add) {
// get gameroom price
const price = prices[unit.price_id] || await Price.find(unit.price_id)
if (price) prices[price.id] = price
if (!price) throw new Error('price not found')
if (price.amount <= 0) {
// will be removed at the product level
config.shopify_unit_sync = new Date(updated_at * 1000)
if (verbose) spinner.warn(`skipping ${grGreen('unit')}`)
skipped++
continue
}
// create shopify inventory
shopify_inventory = await ShopifyV2.Inventory.fromUnitAndPriceAndStore(unit, price, store)
shopify_inventory = await shopify_inventory.save()
unit.properties.shopify_id = shopify_inventory.id
if (verbose) spinner.info(`created shopify ${cosmetic.green('inventory')}`)
added++
} else if (remove) {
// delete shopify inventory
await ShopifyV2.Inventory.delete(shopify_id)
shopify_inventory = null
if (verbose) spinner.info(`removed shopify ${cosmetic.green('inventory')}`)
removed++
} else if (update) {
// get gameroom price
// update inventory location
// update inventory price
// update images?
// const price = prices[unit.price_id] || await Price.find(unit.price_id)
// if (price) prices[price.id] = price
// if (!price) throw new Error('price not found')
// if (price.amount <= 0) {
// // will be removed at the product level
// config.shopify_unit_sync = new Date(updated_at * 1000)
// if (verbose) spinner.warn(`skipping ${grGreen('unit')}`)
// skipped++
// continue
// }
// update shopify inventory
shopify_inventory = await shopify_inventory.updateFromUnitAndStore(unit)
if (verbose) spinner.info(`updated shopify ${cosmetic.green('inventory')}`)
updated++
} else {
// not offered and not shopified, no action necessary
config.shopify_unit_sync = new Date(updated_at * 1000)
if (verbose) spinner.succeed(`skipping ${grGreen('unit')}`)
skipped++
continue
}
// update shopify id
if (shopify_inventory) {
unit.properties.shopify_id = shopify_inventory.id
} else {
delete unit.properties.shopify_id
}
// update unit if need be
if (unit.properties.shopify_id != shopify_id) {
unit = await Unit.update({ id: unit.id, properties: unit.properties })
if (verbose) spinner.info(`updated gameroom ${grGreen('unit')}`)
// updated units go to the end of the pagination line and mess up the offset...
offset--
}
config.shopify_unit_sync = new Date(updated_at * 1000)
processed[unit.id] = unit.updated_at
if (verbose) spinner.succeed(`synced ${grGreen('unit')}`)
} catch (err) {
skipped++
// if verbose, the id has already been printed
spinner.fail(`failed ${grGreen('unit')}${verbose ? '' : ` ${unit.id}`} ${err}`)
return
}
}
}
spinner.succeed(`${total} processed, ${added} ${cosmetic.green('added')}, ${removed} ${cosmetic.red('removed')}, ${updated} ${cosmetic.yellow('updated')}, ${skipped} skipped`)
spinner.succeed(`completed unit sync to ${cosmetic.green('shopify')} @ ${new Date().toLocaleString()}`)
}