UNPKG

@gameroom/cli

Version:

A command line tool for Gameroom

161 lines (155 loc) 8.57 kB
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() }