UNPKG

trailpack-proxy-cart

Version:

eCommerce - Trailpack for Proxy Engine

1,538 lines (1,463 loc) 69.2 kB
/* eslint no-console: [0] */ 'use strict' const Service = require('trails/service') const _ = require('lodash') const Errors = require('proxy-engine-errors') const PRODUCT_DEFAULTS = require('../../lib').Enums.PRODUCT_DEFAULTS const VARIANT_DEFAULTS = require('../../lib').Enums.VARIANT_DEFAULTS const fs = require('fs') /** * @module ProductService * @description Product Service */ module.exports = class ProductService extends Service { /** * * @param item * @param options * @returns {*} */ resolveItem(item, options){ options = options || {} const Product = this.app.orm.Product const ProductVariant = this.app.orm.ProductVariant const Image = this.app.orm.ProductImage if (item.id || item.variant_id || item.product_variant_id) { const id = item.id || item.variant_id || item.product_variant_id return ProductVariant.findById(id, { transaction: options.transaction || null, include: [ { model: Product, include: [ { model: Image, as: 'images', attributes: ['src','full','thumbnail','small','medium','large','alt','position'] } ] }, { model: Image, as: 'images', attributes: ['src','full','thumbnail','small','medium','large','alt','position'] } ] }) } else if (item.product_id) { return ProductVariant.find({ where: { product_id: item.product_id, position: 1 }, transaction: options.transaction || null, include: [ { model: Product, include: [ { model: Image, as: 'images', attributes: ['src','full','thumbnail','small','medium','large','alt','position'] } ] }, { model: Image, as: 'images', attributes: ['src','full','thumbnail','small','medium','large','alt','position'] } ] }) } else { const err = new Errors.FoundError(Error(`${item} not found`)) return Promise.reject(err) } } /** * Add Multiple Products * @param items * @param options * @returns {Promise.<*>} */ resolveItems(items, options) { options = options || {} if (!Array.isArray(items)) { items = [items] } const Sequelize = this.app.orm['Product'].sequelize // const addedProducts = [] // Setup Transaction return Sequelize.transaction(t => { return Sequelize.Promise.mapSeries(items, item => { return this.resolveItem(item, { transaction: t }) }) }) } /** * Add Multiple Products * @param products * @param options * @returns {Promise.<*>} */ addProducts(products, options) { options = options || {} if (!Array.isArray(products)) { products = [products] } const Sequelize = this.app.orm['Product'].sequelize // const addedProducts = [] // Setup Transaction return Sequelize.transaction(t => { return Sequelize.Promise.mapSeries(products, product => { return this.addProduct(product, { transaction: t }) }) }) } /** * Add a Product * @param product * @param options * @returns {Promise} */ addProduct(product, options) { options = options || {} const Product = this.app.orm.Product return Product.findOne({ where: { host: product.host ? product.host : 'localhost', handle: product.handle }, attributes: ['id'], transaction: options.transaction || null }) .then(resProduct => { if (resProduct instanceof Product) { // console.log('UPDATING', product) // Set ID in case it's missing in this transaction product.id = resProduct.id // Update the existing product return this.updateProduct(product, options) } else { // console.log('CREATING', product) // Create a new Product return this.createProduct(product, options) } }) } /** * Create A Product with default Variant * @param product * @param options * @returns {Promise} */ // TODO Create Images and Variant Images in one command createProduct(product, options){ options = options || {} const Product = this.app.orm.Product const Tag = this.app.orm.Tag const Variant = this.app.orm.ProductVariant // const Image = this.app.orm.ProductImage const Metadata = this.app.orm.Metadata const Collection = this.app.orm.Collection const Vendor = this.app.orm.Vendor const Shop = this.app.orm.Shop if (!product) { const err = new Error('A product is required') return Promise.reject(err) } product = this.productDefaults(product) // The Default Product const create = { host: product.host, handle: product.handle, title: product.title, seo_title: product.seo_title, seo_description: product.seo_description, body: product.body, type: product.type, price: product.price, compare_at_price: product.compare_at_price, calculated_price: product.calculated_price, tax_code: product.tax_code, published: product.published, available: product.available, published_scope: product.published_scope, weight: product.weight, weight_unit: product.weight_unit, average_shipping: product.average_shipping, property_pricing: product.property_pricing, exclude_payment_types: product.exclude_payment_types, metadata: Metadata.transform(product.metadata || {}), google: product.google, amazon: product.amazon, options: product.options } // create = Product.build(create) if (product.published === true) { create.published_at = new Date() } if (product.published === false) { create.unpublished_at = new Date() } if (product.published_scope) { create.published_scope = product.published_scope } if (product.seo_title) { create.seo_title = product.seo_title } if (!product.seo_title && product.title) { create.seo_title = product.title } if (product.seo_description) { create.seo_description = this.app.services.ProxyCartService.description(product.seo_description) } if (!product.seo_description && product.body) { create.seo_description = this.app.services.ProxyCartService.description(product.body) } // Images let images = [] // If this request came with product images if (product.images.length > 0) { product.images = product.images.map(image => { image.variant = 0 return image }) images = images.concat(product.images) delete product.images } // Variants // Set a default variant based of off product let variants = [{ title: product.title, sku: product.sku, vendors: product.vendors, google: product.google, amazon: product.amazon }] // Set the published status if (product.published === true) { variants[0].published_at = create.published_at } if (product.published === false) { variants[0].unpublished_at = create.unpublished_at } // If this is not a true variant because it is missing a sku (which is required), let's remove it. if (!variants[0].sku) { variants.splice(0,1) } // Add variants to the default if (product.variants.length > 0) { variants = variants.concat(product.variants) } // For every variant, map missing defaults and images variants = variants.map((variant, index) => { // Set defaults from product to variant variant = this.variantDefaults(variant, product) // Map Variant Positions putting default at 1 variant.position = index + 1 // If this variant is not explicitly not published set to status of parent if (product.published && variant.published !== false) { variant.published = true } // If this variant is published then set published_at to same as parent if (variant.published) { variant.published_at = create.published_at } // Handle Variant Images if (variant.images.length > 0) { variant.images = variant.images.map(image => { image.variant = index return image }) images = images.concat(variant.images) delete variant.images } if (variant.option) { const keys = Object.keys(variant.option) create.options = _.union(create.options, keys) } return variant }) // Filter out undefined variants = variants.filter(variant => variant) // Assign the variants to the create model create.total_variants = variants.length create.variants = variants // Map image positions images = images.map((image, index) => { image.position = index + 1 return image }) // Set the resulting Product let resProduct return Product.create(create, { include: [ { model: Variant, as: 'variants', include: [ { model: Metadata, as: 'metadata' } ] }, { model: Metadata, as: 'metadata', } ], transaction: options.transaction || null }) .then(createdProduct => { if (!createdProduct) { throw new Error('Product was not created') } resProduct = createdProduct // console.log('createdProduct',createdProduct) if (product.tags && product.tags.length > 0) { product.tags = _.sortedUniq(product.tags.filter(n => n)) return Tag.transformTags(product.tags, {transaction: options.transaction || null}) } return }) .then(tags => { if (tags && tags.length > 0) { // Add Tags return resProduct.setTags(tags.map(tag => tag.id), {transaction: options.transaction || null}) } return }) .then(productTags => { if (product.shops && product.shops.length > 0) { product.shops = _.sortedUniq(product.shops.filter(n => n)) return Shop.transformShops(product.shops, {transaction: options.transaction || null}) } return }) .then(shops => { if (shops && shops.length > 0) { return resProduct.setShops(shops, {transaction: options.transaction || null}) } return }) .then(shops => { if (product.collections && product.collections.length > 0) { // Resolve the collections product.collections = _.sortedUniq(product.collections.filter(n => n)) return Collection.transformCollections(product.collections, {transaction: options.transaction || null}) } return }) .then(collections => { if (collections && collections.length > 0) { return Product.sequelize.Promise.mapSeries(collections, collection => { // console.log('WORKING ON ADDING', collection) const through = collection.product_position ? {position: collection.product_position} : {} return resProduct.addCollection(collection.id, { through: through, transaction: options.transaction || null }) }) // return resProduct.setCollections(collections.map(c => c.id), { // through: { // positions: collections.map(c => c.position || 0) // }, // transaction: options.transaction || null // }) } return }) .then(productCollections => { if (product.vendors && product.vendors.length > 0) { return Vendor.transformVendors(product.vendors, {transaction: options.transaction || null}) } return }) .then(vendors => { if (vendors && vendors.length > 0) { // TODO add vendor_price, policies return resProduct.setVendors(vendors.map(v => v.id), { through: { vendor_price: resProduct.price }, transaction: options.transaction || null }) } return }) .then(vendors => { return Product.sequelize.Promise.mapSeries(images, image => { // If variant index, set the variant image if (typeof image.variant !== 'undefined') { if (resProduct.variants && resProduct.variants[image.variant] && resProduct.variants[image.variant].id) { image.product_variant_id = resProduct.variants[image.variant].id } delete image.variant } return resProduct.createImage(image, {transaction: options.transaction || null}) }) }) .then(createdImages => { return Product.findByIdDefault(resProduct.id, {transaction: options.transaction || null}) }) } /** * * @param products * @returns {Promise.<*>} */ updateProducts(products) { if (!Array.isArray(products)) { products = [products] } const Product = this.app.orm.Product return Product.sequelize.transaction(t => { return Product.sequelize.Promise.mapSeries(products, product => { return this.updateProduct(product, { transaction: t }) }) }) } /** * * @param product * @param options * @returns {Promise} */ // TODO Create/Update Images and Variant Images in one command updateProduct(product, options) { options = options || {} const Product = this.app.orm['Product'] const Variant = this.app.orm['ProductVariant'] const Image = this.app.orm['ProductImage'] const Tag = this.app.orm['Tag'] const Collection = this.app.orm['Collection'] const Vendor = this.app.orm['Vendor'] // const Metadata = this.app.orm['Metadata'] const productOptions = [] // if (!product.id) { // throw new Errors.FoundError(Error('Product is missing id')) // } let resProduct, update = {} return Product.resolve(product, { transaction: options.transaction || null }) .then(_product => { if (!_product) { throw new Error('Product not found') } resProduct = _product return resProduct.resolveVariants({transaction: options.transaction || null}) }) .then(() => { if (product.collections) { return resProduct.resolveCollections({transaction: options.transaction || null}) } return }) .then(() => { // if (product.images) { return resProduct.resolveImages({transaction: options.transaction || null}) // } // return }) .then(() => { if (product.metadata) { return resProduct.resolveMetadata({transaction: options.transaction || null}) } return }) .then(() => { if (product.associations) { return resProduct.resolveAssociations({transaction: options.transaction || null}) } return }) .then(() => { if (product.vendors) { return resProduct.resolveVendors({transaction: options.transaction || null}) } return }) .then(() => { update = { host: product.host || resProduct.host, handle: product.handle || resProduct.handle, seo_title: product.seo_title || resProduct.seo_title, seo_description: product.seo_description || resProduct.seo_description, body: product.body || resProduct.body, type: product.type || resProduct.type, published_scope: product.published_scope || resProduct.published_scope, available: product.available, average_shipping: product.average_shipping, property_pricing: product.property_pricing, exclude_payment_types: product.exclude_payment_types, weight: product.weight || resProduct.weight, weight_unit: product.weight_unit || resProduct.weight_unit, requires_shipping: product.requires_shipping || resProduct.requires_shipping, tax_code: product.tax_code || resProduct.tax_code, options: productOptions } // console.log('BROKE DEFAULT SKU', resProduct.variants[0].id, resProduct.variants[0].sku) // force array of variants product.variants = product.variants || [] // force array of images product.images = product.images || [] // force array of tags product.tags = product.tags || [] // force array of collections product.collections = product.collections || [] // force array of associations product.associations = product.associations || [] // If product is getting published if (product.published === true && resProduct.published === false) { update.published = resProduct.variants[0].published = product.published update.published_at = resProduct.variants[0].published_at = new Date() } // If product is getting unpublished if (product.published === false && resProduct.published === true) { update.published = resProduct.variants[0].published = product.published update.unpublished_at = resProduct.variants[0].unpublished_at = new Date() } // If the SKU is changing, set the default sku if (product.sku) { // let variants = [{ // title: product.title, // sku: product.sku, // vendors: product.vendors, // google: product.google, // amazon: product.amazon // }] resProduct.variants[0].sku = product.sku } // if The title is changing, set the default title if (product.title) { update.title = resProduct.variants[0].title = product.title } // if the price is changing if (product.price) { update.price = resProduct.variants[0].price = product.price } // if the compare_at_price is changing if (product.compare_at_price) { update.compare_at_price = resProduct.variants[0].compare_at_price = product.compare_at_price } // Update seo_title if provided, else update it if a new product title if (product.seo_title) { update.seo_title = product.seo_title //.substring(0,255) } // Update product_seo title if (product.title && !product.seo_title) { update.seo_title = product.title //.substring(0,255) } // Update seo_description if provided, else update it if a new product body if (product.seo_description) { update.seo_description = this.app.services.ProxyCartService.description(product.seo_description) } // Update seo_description if (!product.seo_description && product.body) { update.seo_description = this.app.services.ProxyCartService.description(product.body) } // console.log('BROKE', resProduct.variants.length, product.variants.length) // Update Existing Variant resProduct.variants = resProduct.variants.map(variant => { // Find the existing variant const variantToUpdate = product.variants.find(v => variant.id === v.id || variant.sku === v.sku) || {} // Add new Images if (variantToUpdate && variantToUpdate.images) { let newImages = variantToUpdate.images.filter(image => !image.id) // let oldImages = variantToUpdate.images.filter(image => image.id) newImages = newImages.map(image => { image.product_id = resProduct.id image.product_variant_id = variant.id return Image.build(image) }) resProduct.images = _.concat(resProduct.images, newImages) } for (const k in variantToUpdate){ if (variantToUpdate.hasOwnProperty(k) && variantToUpdate.hasOwnProperty(k)) { if (!_.isNil(variantToUpdate[k])) { variant[k] = variantToUpdate[k] } } } return variant }) // Create a List of new Variants that will be added product.variants = product.variants.filter( variant => !variant.id && !resProduct.variants.find(v => { return v.id === variant.id || v.sku === variant.sku }) ) // Build the new Variants product.variants = product.variants.map((variant) => { // Set the product id of the variant variant.product_id = resProduct.id // console.log('BROKE NEW VARIANT', variant.id, variant.sku) // Set the defaults variant = this.variantDefaults(variant, resProduct.get({plain: true})) if (variant.images.length > 0) { // Update the master image if new/updated attributes are defined resProduct.images = resProduct.images.map(image => { return _.extend(image, variant.images.find(i => i.id === image.id || i.src === image.src )) }) // Create a list of new variant images variant.images = variant.images.filter(image => !image.id) // build the new images variant.images = variant.images.map( image => { // image.variant = index image.product_id = resProduct.id return Image.build(image) }) // Add these variant images to the new array. resProduct.images = _.concat(resProduct.images, variant.images) // delete variant.images } return Variant.build(variant) }) // console.log('BROKE TO BE CREATED VARIANTS', product.variants.length) // Join all the variants and sort by current positions resProduct.variants = _.sortBy(_.concat(resProduct.variants, product.variants), 'position') // Set the new Positions resProduct.variants = resProduct.variants.map((variant, index) => { variant.position = index + 1 return variant }) // Calculate new total of variants resProduct.total_variants = resProduct.variants.length // Set the new product options resProduct.variants.forEach(variant => { if (variant.option) { const keys = Object.keys(variant.option) // resProduct.options = _.union(resProduct.options, keys) update.options = _.union(update.options, keys) } }) // Update existing Images resProduct.images = resProduct.images.map(image => { const imageToUpdate = product.images.find(i => i.id === image.id || i.src === image.src) || {} for (const k in imageToUpdate){ if (imageToUpdate.hasOwnProperty(k) && imageToUpdate.hasOwnProperty(k)) { if (!_.isNil(imageToUpdate[k])) { image[k] = imageToUpdate[k] } } } return image }) // Create a List of new Images product.images = product.images.filter( image => !image.id && !resProduct.images.find(i => { return i.id === image.id || i.src === image.src }) ) // Map the new images with their product id product.images = product.images.map(image => { image.product_id = resProduct.id return Image.build(image) }) // Join all the images resProduct.images = _.sortBy(_.concat(resProduct.images, product.images), 'position') // Set the Positions resProduct.images = resProduct.images.map((image, index) => { image.position = index + 1 return image }) // Update changed attributes return resProduct.updateAttributes(update, {transaction: options.transaction || null}) }) .then(updateProduct => { // Transform any new Tags if (product.tags && product.tags.length > 0) { product.tags = _.sortedUniq(product.tags.filter(n => n)) return Tag.transformTags(product.tags, {transaction: options.transaction || null}) } return }) .then(tags => { // Set Tags if (tags && tags.length > 0) { return resProduct.setTags(tags.map(t => t.id), {transaction: options.transaction || null}) } return }) .then(productTags => { if (product.collections && product.collections.length > 0) { // Resolve the collections product.collections = _.sortedUniq(product.collections.filter(n => n)) return Collection.transformCollections(product.collections, {transaction: options.transaction || null}) } return }) .then(collections => { // Set the collections if (collections && collections.length > 0) { return Product.sequelize.Promise.mapSeries(collections, collection => { // console.log('WORKING ON ADDING', collection) const through = collection.product_position ? {position: collection.product_position} : {} return resProduct.addCollection(collection.id, { through: through, hooks: false, individualHooks: false, returning: false, transaction: options.transaction || null }) }) // return resProduct.setCollections(collections.map(c => c.id), {transaction: options.transaction || null}) } return }) .then(() => { // if product metadata. if (product.metadata && _.isObject(product.metadata)) { resProduct.metadata.data = product.metadata || {} // save the metadata return resProduct.metadata.save({ transaction: options.transaction || null }) } return }) .then(metadata => { if (product.vendors && product.vendors.length > 0) { return Vendor.transformVendors(product.vendors, {transaction: options.transaction || null}) } return }) .then(vendors => { if (vendors && vendors.length > 0) { return resProduct.setVendors(vendors.map(v => v.id), { transaction: options.transaction || null }) } return }) .then(vendors => { return Product.sequelize.Promise.mapSeries(resProduct.variants, variant => { if (variant instanceof Variant) { // console.log('broke saving', variant.id, variant.sku) return variant.save({ transaction: options.transaction || null }) .catch(err => { console.log(err) return variant }) } else { // console.log('BROKE creating', variant.sku) return resProduct.createVariant(variant, { transaction: options.transaction || null }) } }) }) .then(variants => { return Product.sequelize.Promise.mapSeries(resProduct.images, image => { if (typeof image.variant !== 'undefined') { image.product_variant_id = resProduct.variants[image.variant].id delete image.variant } if (image instanceof Image) { return image.save({ transaction: options.transaction || null }) } else { return resProduct.createImage(image, { transaction: options.transaction || null }) } }) }) .then(images => { return Product.findByIdDefault(resProduct.id, { transaction: options.transaction || null }) }) } /** * * @param products * @returns {Promise.<*>} */ removeProducts(products) { if (!Array.isArray(products)) { products = [products] } const Product = this.app.orm['Product'] return Product.sequelize.Promise.mapSeries(products, product => { return this.removeProduct(product) }) } /** * * @param product * @param options */ removeProduct(product, options) { options = options || {} if (!product.id) { const err = new Errors.FoundError(Error('Product is missing id')) return Promise.reject(err) } const Product = this.app.orm.Product return Product.destroy({ where: { id: product.id }, transaction: options.transaction || null }) } /** * * @param variants */ removeVariants(variants){ if (!Array.isArray(variants)) { variants = [variants] } const Product = this.app.orm['Product'] return Product.sequelize.Promise.mapSeries(variants, variant => { return this.removeVariant(variant) }) } /** * * @param product * @param variant * @param options */ // TODO upload images createVariant(product, variant, options) { options = options || {} const Product = this.app.orm['Product'] const Variant = this.app.orm['ProductVariant'] let resProduct, resVariant, productOptions = [] return Product.resolve(product, {transaction: options.transaction || null}) .then(_product => { if (!_product) { throw new Errors.FoundError(Error('Could not find Product')) } resProduct = _product variant.product_id = resProduct.id variant = this.variantDefaults(variant, resProduct) return resProduct.createVariant(variant, {transaction: options.transaction || null}) // return this.resolveVariant(variant, options) }) .then(variant => { resVariant = variant return Variant.findAll({ where: { product_id: resProduct.id }, transaction: options.transaction || null }) }) .then(variants => { const updates = _.sortBy(variants, 'position') _.map(updates, (variant, index) => { variant.position = index + 1 }) _.map(updates, variant => { const keys = Object.keys(variant.option) productOptions = _.union(productOptions, keys) }) return Product.sequelize.Promise.mapSeries(updates, variant => { return variant.save({ transaction: options.transaction || null }) }) }) .then(updatedVariants => { resProduct.options = productOptions resProduct.total_variants = updatedVariants.length return resProduct.save({transaction: options.transaction || null}) }) .then(updatedProduct => { return Variant.findByIdDefault(resVariant.id, {transaction: options.transaction || null}) }) } /** * * @param product * @param variants * @param options * @returns {Promise.<*>} */ createVariants(product, variants, options) { const Product = this.app.orm['Product'] return Product.sequelize.Promise.mapSeries(variants, variant => { return this.createVariant(product, variant, options) }) } /** * * @param product * @param variant * @param options */ // TODO upload images updateVariant(product, variant, options) { options = options || {} const Product = this.app.orm['Product'] const Variant = this.app.orm['ProductVariant'] let resProduct, resVariant, productOptions = [] return Product.resolve(product, {transaction: options.transaction || null}) .then(_product => { if (!_product) { throw new Error('Product did not resolve') } resProduct = _product return Variant.resolve(variant, options) }) .then(foundVariant => { resVariant = foundVariant resVariant = _.extend(resVariant, _.omit(variant, ['id','sku'])) resVariant = this.variantDefaults(resVariant, resProduct) return resVariant.save({transaction: options.transaction || null}) }) .then(variant => { return Variant.findAll({ where: { product_id: resProduct.id }, transaction: options.transaction || null }) }) .then(variants => { const updates = _.sortBy(variants, 'position') _.map(updates, (variant, index) => { variant.position = index + 1 }) _.map(updates, variant => { const keys = Object.keys(variant.option) productOptions = _.union(productOptions, keys) }) return Product.sequelize.Promise.mapSeries(updates, variant => { return variant.save({transaction: options.transaction || null}) }) }) .then(updatedVariants => { resProduct.options = product.options return resProduct.save({transaction: options.transaction || null}) }) .then(updatedProduct => { return Variant.findByIdDefault(resVariant.id, {transaction: options.transaction || null}) }) } updateVariants(product, variants, options) { const Product = this.app.orm['Product'] return Product.sequelize.Promise.mapSeries(variants, variant => { return this.updateVariant(product, variant, options) }) } /** * * @param id * @param options */ removeVariant(id, options){ options = options || {} const Product = this.app.orm['Product'] const Variant = this.app.orm.ProductVariant let resVariant, resProduct let updates let productOptions = [] return Variant.resolve(id, {transaction: options.transaction || null}) .then(foundVariant => { resVariant = foundVariant return Product.resolve(resVariant.product_id, {transaction: options.transaction || null}) }) .then(product => { resProduct = product return Variant.findAll({ where: { product_id: resVariant.product_id }, transaction: options.transaction || null }) }) .then(foundVariants => { updates = _.sortBy(_.filter(foundVariants, variant => { if (variant.id !== resVariant.id){ return variant } }), 'position') _.map(updates, (variant, index) => { variant.position = index + 1 }) _.map(updates, variant => { const keys = Object.keys(variant.option) productOptions = _.union(productOptions, keys) }) return Variant.sequelize.Promise.mapSeries(updates, variant => { return variant.save({transaction: options.transaction || null}) }) }) .then(updatedVariants => { resProduct.options = productOptions resProduct.total_variants = updatedVariants.length return resProduct.save({transaction: options.transaction || null}) }) .then(updatedProduct => { return resVariant.destroy({transaction: options.transaction || null}) }) .then(destroyed => { return resVariant }) } /** * * @param images */ removeImages(images){ if (!Array.isArray(images)) { images = [images] } const Product = this.app.orm['Product'] return Product.sequelize.Promise.mapSeries(images, image => { const id = typeof image.id !== 'undefined' ? image.id : image return this.removeImage(id) }) } /** * * @param id * @param options */ removeImage(id, options){ options = options || {} const Image = this.app.orm['ProductImage'] const Product = this.app.orm['Product'] let resDestroy return Image.findById(id,{ transaction: options.transaction || null }) .then(foundImage => { if (!foundImage) { // TODO proper error throw new Error('Image not found') } resDestroy = foundImage return Image.findAll({ where: { product_id: resDestroy.product_id }, order: [['position','ASC']], transaction: options.transaction || null }) }) .then(foundImages => { foundImages = foundImages.filter(image => image.id !== id) foundImages = foundImages.map((image, index) => { image.position = index + 1 return image }) return Image.sequelize.Promise.mapSeries(foundImages, image => { return image.save({ transaction: options.transaction || null }) }) }) .then(updatedImages => { return resDestroy.destroy({ transaction: options.transaction || null }) }) .then(() => { return Product.findByIdDefault(resDestroy.product_id ,{transaction: options.transaction || null}) }) } /** * @param product * @param variant * @param image * @param options */ // TODO addImage(product, variant, image, options){ options = options || {} const Image = this.app.orm['ProductImage'] const Product = this.app.orm['Product'] const Variant = this.app.orm['Variant'] let resProduct, resImage, resVariant return Product.resolve(product, {transaction: options.transaction || null}) .then(foundProduct => { if (!foundProduct) { throw new Error('Product could not be resolved') } resProduct = foundProduct if (variant) { return Variant.resolve(variant, {transaction: options.transaction || null}) } else { return null } }) .then(foundVariant => { resVariant = foundVariant ? foundVariant.id : null return resProduct.createImage({ product_variant_id: resVariant, src: image, position: options.position || null, alt: options.alt || null }, { transaction: options.transaction }) }) .then(createdImage => { if (!createdImage) { throw new Error('Image Could not be created') } resImage = createdImage return Image.findAll({ where: { product_id: resProduct.id }, order: [['position','ASC']], transaction: options.transaction || null }) }) .then(foundImages => { foundImages = foundImages.map((image, index) => { image.position = index + 1 return image }) return Image.sequelize.Promise.mapSeries(foundImages, image => { return image.save({ transaction: options.transaction || null }) }) }) .then(updatedImages => { return resImage.reload() }) } createImage(product, variant, filePath, options) { options = options || {} const image = fs.readFileSync(filePath) const Image = this.app.orm['ProductImage'] const Product = this.app.orm['Product'] const Variant = this.app.orm['ProductVariant'] let resProduct, resImage, resVariant return Product.resolve(product, {transaction: options.transaction || null}) .then(_product => { if (!_product) { throw new Error('Product could not be resolved') } resProduct = _product if (variant) { return Variant.resolve(variant, {transaction: options.transaction || null}) } else { return null } }) .then(_variant => { resVariant = _variant ? _variant.id : null return this.app.services.ProxyCartService.uploadImage(image, filePath) }) .then(uploadedImage => { return resProduct.createImage({ product_variant_id: resVariant, src: uploadedImage.url, position: options.position || null, alt: options.alt || null }, { transaction: options.transaction }) }) .then(createdImage => { if (!createdImage) { throw new Error('Image Could not be created') } resImage = createdImage return Image.findAll({ where: { product_id: resProduct.id }, order: [['position','ASC']], transaction: options.transaction || null }) }) .then(foundImages => { foundImages = foundImages.map((image, index) => { image.position = index + 1 return image }) return Image.sequelize.Promise.mapSeries(foundImages, image => { return image.save({ transaction: options.transaction || null }) }) }) .then(updatedImages => { return resImage.reload() }) } /** * * @param product * @param tag * @param options * @returns {Promise.<T>} */ addTag(product, tag, options){ options = options || {} const Product = this.app.orm['Product'] const Tag = this.app.orm['Tag'] let resProduct, resTag return Product.resolve(product, {transaction: options.transaction || null}) .then(_product => { if (!_product) { throw new Errors.FoundError(Error('Product not found')) } resProduct = _product return Tag.resolve(tag, {transaction: options.transaction || null}) }) .then(_tag => { if (!_tag) { throw new Errors.FoundError(Error('Tag not found')) } resTag = _tag return resProduct.hasTag(resTag.id, {transaction: options.transaction || null}) }) .then(hasTag => { if (!hasTag) { return resProduct.addTag(resTag.id, {transaction: options.transaction || null}) } return resProduct }) .then(tag => { return Product.findByIdDefault(resProduct.id, {transaction: options.transaction || null}) }) } /** * * @param product * @param tag * @param options * @returns {Promise.<T>} */ removeTag(product, tag, options){ options = options || {} const Product = this.app.orm['Product'] const Tag = this.app.orm['Tag'] let resProduct, resTag return Product.resolve(product, {transaction: options.transaction || null}) .then(_product => { if (!_product) { throw new Errors.FoundError(Error('Product not found')) } resProduct = _product return Tag.resolve(tag, {transaction: options.transaction || null}) }) .then(_tag => { if (!_tag) { throw new Errors.FoundError(Error('Tag not found')) } resTag = _tag return resProduct.hasTag(resTag.id, {transaction: options.transaction || null}) }) .then(hasTag => { if (hasTag) { return resProduct.removeTag(resTag.id, {transaction: options.transaction || null}) } return false }) .then(newTag => { return Product.findByIdDefault(resProduct.id, {transaction: options.transaction || null}) }) } /** * * @param product * @param association * @param options * @returns {Promise.<T>} */ addAssociation(product, association, options){ options = options || {} const Product = this.app.orm['Product'] const ProductVariant = this.app.orm['ProductVariant'] let resProduct, resVariant, resAssociationProduct, resAssociationVariant, through if (!product || !association) { throw new Errors.FoundError(Error('Product or Association was not provided')) } // console.log('BROKE ASSOCIATION', product, association) return Product.resolve(product, {transaction: options.transaction || null}) .then(_product => { if (!_product) { throw new Errors.FoundError(Error('Product not found')) } resProduct = _product // If this product object also provided a sku if (product.sku) { return ProductVariant.resolve(product, {transaction: options.transaction || null}) } // Return the default Variant else { return resProduct.getDefaultVariant({transaction: options.transaction || null}) } }) .then(_variant => { if (_variant) { resVariant = _variant } return Product.resolve(association, {transaction: options.transaction || null}) }) .then(_association => { if (!_association) { throw new Errors.FoundError(Error('Product not found')) } resAssociationProduct = _association // If this product object also provided a sku if (association.sku) { return ProductVariant.resolve(association) } // Return the default Variant else { return resAssociationProduct.getDefaultVariant({transaction: options.transaction || null}) } }) .then(_variantAssociation => { if (_variantAssociation) { resAssociationVariant = _variantAssociation } through = resVariant && resAssociationVariant ? { variant_id: resVariant.id, associated_variant_id: resAssociationVariant.id, // position: } : { } if (association.position !== 'undefined') { through.position = association.position } // Check if the association exists return resProduct.hasAssociation(resAssociationProduct.id, { transaction: options.transaction || null, through: through }) }) .then(hasAssociation => { if (!hasAssociation) { return resProduct.addAssociation(resAssociationProduct.id, { transaction: options.transaction || null, through: through }) } return false }) .then(_newAssociation => { return Product.findByIdDefault(resProduct.id, {transaction: options.transaction || null}) }) } /** * * @param product * @param association * @param options * @returns {Promise.<T>} */ removeAssociation(product, association, options){ options = options || {} const Product = this.app.orm['Product'] const ProductVariant = this.app.orm['ProductVariant'] let resProduct, resVariant, resAssociationProduct, resAssociationVariant, through if (!product || !association) { throw new Errors.FoundError(Error('Product or Association was not provided')) } return Product.resolve(product, {transaction: options.transaction || null}) .then(_product => { if (!_product) { throw new Errors.FoundError(Error('Product not found')) } resProduct = _product // If this product object also provided a sku if (product.sku) { return ProductVariant.resolve(product) } return }) .then(_variant => { if (_variant) { resVariant = _variant } return Product.resolve(association, {transaction: options.transaction || null}) }) .then(_association => { if (!_association) { throw new Errors.FoundError(Error('Product not found')) } resAssociationProduct = _association // If this association is an object and also provided a sku if (association.sku) { return ProductVariant.resolve(association) } return }) .then(_variantAssociation => { if (_variantAssociation) { resAssociationVariant = _variantAssociation } // If this request was for variants through = resVariant && resAssociationVariant ? { variant_id: resVariant.id, associated_variant_id: resAssociationVariant.id, } : { } // Check if the association exists return resProduct.hasAssociation(resAssociationProduct.id, { transaction: options.transaction || null, through: through }) }) .then(hasAssociation => { if (hasAssociation) { return resProduct.removeAssociation(resAssociationProduct.id, { transaction: options.transaction || null, through: through }) } return false }) .then(_newAssociation => { return Product.findByIdDefault(resProduct.id, {transaction: options.transaction || null}) }) } /** * * @param productVariant * @param association * @param options * @returns {Promise.<T>} */ addVariantAssociation(productVariant, association, options){ options = options || {} const ProductVariant = this.app.orm['ProductVariant'] let resProductVariant, resAssociation if (!productVariant || !association) { throw new Errors.FoundError(Error('Variant or Association was not provided')) } return ProductVariant.resolve(productVariant, {transaction: options.transaction || null}) .then(_productVariant => { if (!_productVariant) { throw new Errors.FoundError(Error('ProductVariant not found')) } resProductVariant = _productVariant return ProductVariant.resolve(association, {transaction: options.transaction || null}) }) .then(_association => { if (!_association) { throw new Errors.FoundError(Error('ProductVariant not found')) } resAssociation = _association return resProductVariant.hasAssociation(resAssociation.id, { transaction: options.transaction || null, through: { product_id: resProductVariant.product_id, associated_product_id: resAssociation.product_id } }) })