trailpack-proxy-cart
Version:
eCommerce - Trailpack for Proxy Engine
1,538 lines (1,463 loc) • 69.2 kB
JavaScript
/* 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
}
})
})