@fabrix/spool-cart
Version:
Spool - eCommerce Spool for Fabrix
447 lines (446 loc) • 15.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const common_1 = require("@fabrix/fabrix/dist/common");
const spool_sequelize_1 = require("@fabrix/spool-sequelize");
const errors_1 = require("@fabrix/spool-sequelize/dist/errors");
const lodash_1 = require("lodash");
const queryDefaults_1 = require("../utils/queryDefaults");
const enums_1 = require("../../enums");
const enums_2 = require("../../enums");
const enums_3 = require("../../enums");
const enums_4 = require("../../enums");
class ProductVariantResolver extends spool_sequelize_1.SequelizeResolver {
findByIdDefault(id, options = {}) {
options = this.app.services.SequelizeService.mergeOptionDefaults(options, queryDefaults_1.ProductVariant.default(this.app));
return this.findById(id, options);
}
findAllDefault(options = {}) {
options = this.app.services.SequelizeService.mergeOptionDefaults(queryDefaults_1.ProductVariant.default(this.app), options);
return this.findAll(options);
}
resolve(variant, options = {}) {
const Variant = this;
if (variant instanceof Variant.instance) {
return Promise.resolve(variant);
}
else if (variant && lodash_1.isObject(variant) && variant.id) {
return Variant.findById(variant.id, options)
.then(resVariant => {
if (!resVariant && options.reject !== false) {
throw new errors_1.ModelError('E_NOT_FOUND', `Variant ${variant.id} not found`);
}
return resVariant || variant;
});
}
else if (variant && lodash_1.isObject(variant) && variant.sku) {
return Variant.findOne(lodash_1.defaultsDeep({
where: {
sku: variant.sku
}
}, options))
.then(resVariant => {
if (!resVariant && options.reject !== false) {
throw new errors_1.ModelError('E_NOT_FOUND', `Variant ${variant.sku} not found`);
}
return resVariant || variant;
});
}
else if (variant && lodash_1.isNumber(variant)) {
return Variant.findById(variant, options)
.then(resVariant => {
if (!resVariant && options.reject !== false) {
throw new errors_1.ModelError('E_NOT_FOUND', `Variant ${variant} not found`);
}
return resVariant || variant;
});
}
else if (variant && lodash_1.isString(variant)) {
return Variant.findOne(lodash_1.defaultsDeep({
where: {
sku: variant
}
}, options))
.then(resVariant => {
if (!resVariant && options.reject !== false) {
throw new errors_1.ModelError('E_NOT_FOUND', `Variant ${variant} not found`);
}
return resVariant || variant;
});
}
else {
if (options.reject !== false) {
const err = new Error(`Unable to resolve Variant ${variant}`);
return Promise.reject(err);
}
else {
return Promise.resolve(variant);
}
}
}
}
exports.ProductVariantResolver = ProductVariantResolver;
class ProductVariant extends common_1.FabrixModel {
static get resolver() {
return ProductVariantResolver;
}
static config(app, Sequelize) {
return {
options: {
underscored: true,
enums: {
UNITS: enums_1.UNITS,
INTERVALS: enums_2.INTERVALS,
INVENTORY_POLICY: enums_3.INVENTORY_POLICY,
VARIANT_DEFAULTS: enums_4.VARIANT_DEFAULTS,
},
scopes: {
live: {
where: {
live_mode: true
}
}
},
hooks: {
beforeValidate: [
(productVariant, options) => {
if (!productVariant.calculated_price && productVariant.price) {
productVariant.calculated_price = productVariant.price;
}
}
],
beforeCreate: [
(productVariant, options) => {
return app.services.ProductService.beforeVariantCreate(productVariant, options)
.catch(err => {
return Promise.reject(err);
});
}
],
beforeUpdate: [
(productVariant, options) => {
return app.services.ProductService.beforeVariantUpdate(productVariant, options)
.catch(err => {
return Promise.reject(err);
});
}
]
}
}
};
}
static schema(app, Sequelize) {
return {
product_id: {
type: Sequelize.INTEGER,
unique: 'productvariant_sku',
},
sku: {
type: Sequelize.STRING,
unique: 'productvariant_sku',
allowNull: false,
set: function (val) {
this.setDataValue('sku', app.services.ProxyCartService.sku(val));
}
},
title: {
type: Sequelize.STRING
},
type: {
type: Sequelize.STRING
},
option: {
type: Sequelize.JSONB,
defaultValue: {}
},
property_pricing: {
type: Sequelize.JSONB,
defaultValue: {}
},
barcode: {
type: Sequelize.STRING
},
price: {
type: Sequelize.INTEGER,
defaultValue: 0
},
calculated_price: {
type: Sequelize.VIRTUAL(Sequelize.INTEGER),
defaultValue: 0
},
compare_at_price: {
type: Sequelize.INTEGER,
defaultValue: 0
},
currency: {
type: Sequelize.STRING,
defaultValue: enums_4.VARIANT_DEFAULTS.CURRENCY
},
discounted_lines: {
type: Sequelize.JSONB,
defaultValue: []
},
total_discounts: {
type: Sequelize.INTEGER,
defaultValue: 0
},
total_orders: {
type: Sequelize.INTEGER,
defaultValue: 0
},
fulfillment_service: {
type: Sequelize.STRING,
defaultValue: enums_4.VARIANT_DEFAULTS.FULFILLMENT_SERVICE
},
position: {
type: Sequelize.INTEGER,
defaultValue: 1
},
published: {
type: Sequelize.BOOLEAN,
defaultValue: enums_4.VARIANT_DEFAULTS.PUBLISHED
},
published_at: {
type: Sequelize.DATE
},
unpublished_at: {
type: Sequelize.DATE
},
available: {
type: Sequelize.BOOLEAN,
defaultValue: enums_4.VARIANT_DEFAULTS.AVAILABLE
},
requires_shipping: {
type: Sequelize.BOOLEAN,
defaultValue: enums_4.VARIANT_DEFAULTS.REQUIRES_SHIPPING
},
requires_taxes: {
type: Sequelize.BOOLEAN,
defaultValue: enums_4.VARIANT_DEFAULTS.REQUIRES_TAX
},
requires_subscription: {
type: Sequelize.BOOLEAN,
defaultValue: enums_4.VARIANT_DEFAULTS.REQUIRES_SUBSCRIPTION
},
subscription_interval: {
type: Sequelize.INTEGER,
defaultValue: enums_4.VARIANT_DEFAULTS.SUBSCRIPTION_INTERVAL
},
subscription_unit: {
type: Sequelize.ENUM,
values: lodash_1.values(enums_2.INTERVALS),
defaultValue: enums_4.VARIANT_DEFAULTS.SUBSCRIPTION_UNIT
},
inventory_management: {
type: Sequelize.BOOLEAN,
defaultValue: enums_4.VARIANT_DEFAULTS.INVENTORY_MANAGEMENT
},
inventory_policy: {
type: Sequelize.ENUM,
values: lodash_1.values(enums_3.INVENTORY_POLICY),
defaultValue: enums_4.VARIANT_DEFAULTS.INVENTORY_POLICY
},
inventory_quantity: {
type: Sequelize.INTEGER,
defaultValue: enums_4.VARIANT_DEFAULTS.INVENTORY_QUANTITY
},
inventory_lead_time: {
type: Sequelize.INTEGER,
defaultValue: enums_4.VARIANT_DEFAULTS.INVENTORY_LEAD_TIME
},
max_quantity: {
type: Sequelize.INTEGER,
defaultValue: enums_4.VARIANT_DEFAULTS.MAX_QUANTITY
},
tax_code: {
type: Sequelize.STRING,
defaultValue: enums_4.VARIANT_DEFAULTS.TAX_CODE
},
weight: {
type: Sequelize.INTEGER,
defaultValue: 0
},
weight_unit: {
type: Sequelize.ENUM,
values: lodash_1.values(enums_1.UNITS),
defaultValue: enums_4.VARIANT_DEFAULTS.WEIGHT_UNIT
},
google: {
type: Sequelize.JSONB,
defaultValue: {}
},
amazon: {
type: Sequelize.JSONB,
defaultValue: {}
},
live_mode: {
type: Sequelize.BOOLEAN,
defaultValue: app.config.get('cart.live_mode')
}
};
}
static associate(models) {
models.ProductVariant.belongsTo(models.Product, {
foreignKey: 'product_id'
});
models.ProductVariant.belongsToMany(models.Shop, {
as: 'shops',
through: {
model: models.ShopProduct,
unique: false,
},
foreignKey: 'variant_id',
});
models.ProductVariant.belongsToMany(models.ProductVariant, {
as: 'associations',
through: {
model: models.ProductAssociation,
unique: false
},
foreignKey: 'variant_id',
otherKey: 'associated_variant_id'
});
models.ProductVariant.belongsToMany(models.ProductVariant, {
as: 'relations',
through: {
model: models.ProductAssociation,
unique: false
},
foreignKey: 'associated_variant_id',
otherKey: 'variant_id'
});
models.ProductVariant.hasMany(models.ProductImage, {
as: 'images',
foreignKey: 'product_variant_id',
through: null,
onDelete: 'CASCADE'
});
models.ProductVariant.hasOne(models.Metadata, {
as: 'metadata',
foreignKey: 'product_variant_id'
});
models.ProductVariant.belongsToMany(models.Discount, {
as: 'discounts',
through: {
model: models.ItemDiscount,
unique: false,
scope: {
model: 'productvariant'
}
},
foreignKey: 'model_id',
constraints: false
});
models.ProductVariant.hasMany(models.OrderItem, {
as: 'order_items',
foreignKey: 'variant_id'
});
models.ProductVariant.belongsToMany(models.Event, {
as: 'event_items',
through: {
model: models.EventItem,
unique: false,
scope: {
object: 'productvariant'
}
},
foreignKey: 'object_id',
constraints: false
});
models.ProductVariant.belongsToMany(models.Vendor, {
as: 'vendors',
through: {
model: models.VendorProduct,
unique: false,
},
foreignKey: 'variant_id',
});
}
}
exports.ProductVariant = ProductVariant;
ProductVariant.prototype.checkRestrictions = function (customer, shippingAddress) {
return Promise.resolve(false);
};
ProductVariant.prototype.checkAvailability = function (qty, options = {}) {
let allowed = true;
if (qty > this.inventory_quantity && this.inventory_policy === enums_3.INVENTORY_POLICY.DENY) {
allowed = false;
qty = Math.max(0, qty + (this.inventory_quantity - qty));
}
if (this.inventory_policy === enums_3.INVENTORY_POLICY.RESTRICT) {
qty = Math.max(0, qty + (this.inventory_quantity - qty));
}
const res = {
title: this.title,
allowed: allowed,
quantity: qty
};
if (options.shop) {
res.shop = options.shop;
}
return this.resolveShops({ transaction: options.transaction || null })
.then(() => {
if (!res.shop && this.shops.length > 0) {
res.shop = this.shops[0];
}
return res;
})
.catch(err => {
return res;
});
};
ProductVariant.prototype.resolveShops = function (options = {}) {
if (this.shops
&& this.shops.length > 0
&& this.shops.every(d => d instanceof this.app.models['Shop'].instance)
&& options.reload !== true) {
return Promise.resolve(this);
}
else {
return this.getShops({ transaction: options.transaction || null })
.then(_shops => {
_shops = _shops || [];
this.shops = _shops;
this.setDataValue('shops', _shops);
this.set('shops', _shops);
return this;
});
}
};
ProductVariant.prototype.resolveImages = function (options = {}) {
return this;
};
ProductVariant.prototype.resolveDiscounts = function (options = {}) {
if (this.discounts
&& this.discounts.length > 0
&& this.discounts.every(d => d instanceof this.app.models['Discount'].instance)
&& options.reload !== true) {
return Promise.resolve(this);
}
else {
return this.getDiscounts({ transaction: options.transaction || null })
.then(_discounts => {
_discounts = _discounts || [];
this.discounts = _discounts;
this.setDataValue('discounts', _discounts);
this.set('discounts', _discounts);
return this;
});
}
};
ProductVariant.prototype.resolveMetadata = function (options = {}) {
if (this.metadata
&& this.metadata instanceof this.app.models['Metadata'].instance
&& options.reload !== true) {
return Promise.resolve(this);
}
else {
return this.getMetadata({ transaction: options.transaction || null })
.then(_metadata => {
_metadata = _metadata || { product_variant_id: this.id };
this.metadata = _metadata;
this.setDataValue('metadata', _metadata);
this.set('metadata', _metadata);
return this;
});
}
};