@gameroom/cli
Version:
A command line tool for Gameroom
161 lines (155 loc) • 8.57 kB
JavaScript
const cosmetic = require('cosmetic'),
{ join } = require('path'),
{ Shopify } = require('../../models'),
{ componentString, getAll, grGreen, timeout, writeCSVFile } = require('../../helpers'),
{ models: { Address, Container, Image, Price, Product, Store, Unit, Unit_Extended } } = require('@gameroom/kit'),
{ config, conversions, google_product_categories, spinner } = require('../../refs')
module.exports = async ({ verbose }) => {
if (!process.env.SHOPIFY_STORE_URL) throw new Error('missing shopify env keys')
spinner.info(`auditing ${cosmetic.green(process.env.SHOPIFY_STORE_URL)} @ ${new Date().toLocaleString()}`)
// get higher level models for later checks
// stores
spinner.text = `getting ${grGreen('stores')}`
const stores = await Store.getAll()
spinner.info(`got ${stores.length} ${grGreen('stores')}`)
// store addresses
spinner.text = `getting ${grGreen('addresses')}`
const address_filters = []
for (const store of stores) address_filters.push({ key: 'addressable_id', value: store.id })
const addresses = await Address.getAll({ filter: { or: address_filters } })
spinner.info(`got ${addresses.length} ${grGreen('addresses')}`)
// containers
spinner.text = `getting ${grGreen('containers')}`
const containers = await Container.getAll()
spinner.info(`got ${containers.length} ${grGreen('containers')}`)
// shopify locations
spinner.text = `getting ${cosmetic.green('locations')}`
const locations = await Shopify.Location.get()
spinner.info(`got ${locations.length} ${cosmetic.green('locations')}`)
// for every unit check, only get units that were updated before the last time shopify update ran
let units
// units shopified with zero or negative quantity
spinner.text = `getting ${grGreen('units')} shopified with quantity <= 0`
units = await Unit_Extended.getAll({
filter: { and: [
{ key: 'quantity', comparison: '<=', value: 0 },
{ key: 'shopified', value: true }
]}
})
spinner.info(`got ${units.length} ${grGreen('units')} shopified with quantity <= 0`)
for (const [i, u] of units.entries()) {
spinner.text = `unshopifying ${i}/${units.length} ${grGreen('units')}`
// update gameroom unit
await Unit.update({ id: u.id, shopified: false })
// delete shopify product
const product = await Shopify.Product.getWithHandle(u.id)
if (!product && verbose) spinner.warn(`${grGreen('unit')} ${u.id} had no ${cosmetic.green('shopify product')}`)
if (product) await Shopify.Product.delete(product.id)
if (verbose) spinner.info(`unshopified ${grGreen('unit')} ${u.id}`)
}
// offered units that arent on shopify
spinner.text = `getting ${grGreen('units')} offered but not shopified`
units = await Unit_Extended.getAll({
filter: { and: [
{ key: 'quantity', comparison: '>', value: 0},
{ key: 'offered', value: true },
{ key: 'shopified', value: false }
]}
})
spinner.info(`got ${units.length} ${grGreen('units')} offered and not shopified`)
let no_image = 0, no_location = 0, updated = 0
for (const [i, u] of units.entries()) {
spinner.text = `checking ${i}/${units.length} ${grGreen('units')}`
// find unit's store on shipify 'location'
const store = u.store_id ? stores.find(s => s.id === u.store_id) : null
const store_address = store ? addresses.find(a => a.addressable_id === store.id) : null
const street_number = store_address ? store_address.street1.split(' ')[0] : null
const location = street_number ? locations.find(l => l.address1.includes(street_number)) : null
const container = u.container_id ? containers.find(i => i.id === u.container_id) : null
// if (!location && verbose) spinner.warn(`${grGreen('unit')} ${u.id} has no ${cosmetic.green('location')}`)
// if (!u.image && verbose) spinner.warn(`${grGreen('unit')} ${u.id} has no ${grGreen('image')}`)
if (!u.image) no_image++
if (!location) no_location++
// values required to be shopified
const shopifyable = store && store.offered && container && container.offered && u.offered && u.image && u.amount > 0 && u.quantity > 0 && location
if (shopifyable) {
await Unit.update({ id: u.id, offered: false })
await Unit.update({ id: u.id, offered: true })
updated++
if (verbose) spinner.info(`updated ${grGreen('unit')} ${u.id} for ${cosmetic.green('shopify')}`)
}
}
if (updated) spinner.info(`updated ${updated} ${grGreen('units')} for ${cosmetic.green('shopify')}`)
if (verbose && no_image) spinner.warn(`${no_image} offered ${grGreen('units')} without an ${grGreen('image')}`)
if (verbose && no_location) spinner.warn(`${no_location} offered ${grGreen('units')} without a ${cosmetic.green('location')}`)
// get all shopify products
spinner.text = `getting all shopified ${grGreen('units')}`
units = await Unit_Extended.getAll({
filter: { and: [
// { key: 'quantity', comparison: '>', value: 0},
{ key: 'shopified', value: true }
]}
})
spinner.info(`got ${units.length} shopified ${grGreen('units')}`)
// get all shopified units
spinner.text = `getting all ${cosmetic.green('shopify products')}`
const products = await Shopify.Product.getAll(spinner)
spinner.info(`got ${products.length} ${cosmetic.green('shopify products')}`)
let removed = 0
// check shopify products against units
for (const [i, p] of products.entries()) {
spinner.text = `checking ${i}/${products.length} ${cosmetic.green('shopify products')}`
const unit = units.find(i => i.id === p.handle)
const store = unit ? stores.find(i => i.id === unit.store_id) : null
const store_address = store ? addresses.find(i => i.addressable_id === store.id) : null
const street_number = store_address ? store_address.street1.split(' ')[0] : null
const location = street_number ? locations.find(i => i.address1.includes(street_number)) : null
const container = unit ? containers.find(i => i.id === unit.container_id) : null
const shopifyable = store && store.offered && container && container.offered && unit.offered && unit.image && unit.amount > 0 && unit.quantity > 0 && location
if (!shopifyable) {
await Shopify.Product.delete(p.id)
if (verbose) spinner.succeed(`removed ${cosmetic.green('shopify product')} ${p.handle}`)
removed++
}
}
spinner.info(`removed ${removed} ${cosmetic.green('shopify products')}`)
// check units against shopify products
updated = 0
for (const [i, u] of units.entries()) {
spinner.text = `checking ${i}/${products.length} ${cosmetic.green('shopify products')}`
const p = products.find(p => u.id === p.handle)
// if no product or if products inventory count is 0
if (!p || !p.variants[0] || p.variants[0].inventory_quantity <= 0) {
await Unit.update({ id: u.id, shopified: false })
if (verbose) spinner.succeed(`unshopified ${grGreen('unit')} ${u.id}`)
updated++
}
}
spinner.info(`unshopified ${updated} ${grGreen('units')}`)
if (units.length + updated !== products.length - removed) spinner.warn(`${grGreen('units')} and ${cosmetic.green('shopify product')} counts do not match`)
// More or less than 1 variant
const variant = products.filter(p => p.variants.length !== 1)
spinner.info(`found ${variant.length} variant issue ${cosmetic.green('products')}`)
if (verbose) for (const [i, product] of variant.entries()) spinner.warn(`variant issue ${cosmetic.green('product')} ${product.handle}`)
// Duplicates
// const duplicates = products.filter(p => p.handle.split('-').length > 5)
// spinner.info(`found ${duplicates.length} duplicate ${cosmetic.green('products')}`)
// for (const [i, product] of duplicates.entries()) {
// spinner.text = `deleting ${i + 1}/${duplicates.length} duplicate ${cosmetic.green('products')}`
// let product_deleted = product ? false : true
// while (!product_deleted) {
// try {
// await Shopify.Product.delete(product.id)
// product_deleted = true
// if (verbose) spinner.succeed(`deleted duplicate ${cosmetic.green('product')} ${product.handle}`)
// } catch(err) {
// spinner.warn(`error deleting ${cosmetic.green('shopify_product')}: ${product.id} ${err}`)
// await timeout()
// }
// }
// }
// spinner.info(`deleted ${duplicates.length} duplicate ${cosmetic.green('products')}`)
// units where quantity < 1 && shopified
// const out_of_stock = await getAll({ })
spinner.succeed(`completed ${cosmetic.green('shopify')} audit @ ${new Date().toLocaleString()}`).stop()
}