UNPKG

@fabrix/spool-cart

Version:

Spool - eCommerce Spool for Fabrix

446 lines (445 loc) 16.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const errors_1 = require("@fabrix/spool-sequelize/dist/errors"); const common_1 = require("@fabrix/fabrix/dist/common"); const spool_sequelize_1 = require("@fabrix/spool-sequelize"); const lodash_1 = require("lodash"); const queryDefaults_1 = require("../utils/queryDefaults"); const enums_1 = require("../../enums"); const enums_2 = require("../../enums"); class FulfillmentResolver extends spool_sequelize_1.SequelizeResolver { findByIdDefault(id, options = {}) { options = this.app.services.SequelizeService.mergeOptionDefaults(queryDefaults_1.Fulfillment.default(this.app), options); return this.findById(id, options); } findAndCountDefault(options = {}) { options = this.app.services.SequelizeService.mergeOptionDefaults(queryDefaults_1.Fulfillment.default(this.app), options); return this.findAndCountAll(options); } resolveByInstance(cart, options = {}) { return Promise.resolve(cart); } resolveById(cart, options = {}) { return this.findById(cart.id, options) .then(resUser => { if (!resUser && options.reject !== false) { throw new errors_1.ModelError('E_NOT_FOUND', `Cart ${cart.id} not found`); } return resUser; }); } resolveByToken(cart, options = {}) { return this.findOne(lodash_1.defaultsDeep({ where: { token: cart.token } }, options)) .then(resUser => { if (!resUser && options.reject !== false) { throw new errors_1.ModelError('E_NOT_FOUND', `Cart token ${cart.token} not found`); } return resUser; }); } resolveByNumber(cart, options = {}) { return this.findById(cart, options) .then(resUser => { if (!resUser && options.reject !== false) { throw new errors_1.ModelError('E_NOT_FOUND', `Cart ${cart.token} not found`); } return resUser; }); } resolveByString(cart, options = {}) { return this.findOne(lodash_1.defaultsDeep({ where: { token: cart } }, options)) .then(resUser => { if (!resUser && options.reject !== false) { throw new errors_1.ModelError('E_NOT_FOUND', `Cart ${cart} not found`); } return resUser; }); } resolve(cart, options = {}) { const resolvers = { 'instance': cart instanceof this.instance, 'id': !!(cart && lodash_1.isObject(cart) && cart.id), 'token': !!(cart && lodash_1.isObject(cart) && cart.token), 'number': !!(cart && lodash_1.isNumber(cart)), 'string': !!(cart && lodash_1.isString(cart)) }; const type = Object.keys(resolvers).find((key) => resolvers[key]); switch (type) { case 'instance': { return this.resolveByInstance(cart, options); } case 'id': { return this.resolveById(cart, options); } case 'token': { return this.resolveByToken(cart, options); } case 'number': { return this.resolveByNumber(cart, options); } case 'string': { return this.resolveByString(cart, options); } default: { const err = new Error(`Unable to resolve Cart ${cart}`); return Promise.reject(err); } } } } exports.FulfillmentResolver = FulfillmentResolver; class Fulfillment extends common_1.FabrixModel { static get resolver() { return FulfillmentResolver; } static config(app, Sequelize) { return { options: { underscored: true, enums: { FULFILLMENT_STATUS: enums_1.FULFILLMENT_STATUS, FULFILLMENT_SERVICE: enums_2.FULFILLMENT_SERVICE }, scopes: { live: { where: { live_mode: true } }, none: { where: { status: enums_1.FULFILLMENT_STATUS.NONE } }, pending: { where: { status: enums_1.FULFILLMENT_STATUS.PENDING } }, sent: { where: { status: enums_1.FULFILLMENT_STATUS.SENT } }, partial: { where: { status: enums_1.FULFILLMENT_STATUS.PARTIAL } }, fulfilled: { where: { status: enums_1.FULFILLMENT_STATUS.FULFILLED } }, cancelled: { where: { status: enums_1.FULFILLMENT_STATUS.CANCELLED } }, }, hooks: { beforeCreate: [ (fulfillment, options) => { return app.services.FulfillmentService.beforeCreate(fulfillment, options) .catch(err => { return Promise.reject(err); }); } ], beforeUpdate: [ (fulfillment, options) => { return app.services.FulfillmentService.beforeUpdate(fulfillment, options) .catch(err => { return Promise.reject(err); }); } ], afterCreate: [ (fulfillment, options) => { return app.services.FulfillmentService.afterCreate(fulfillment, options) .catch(err => { return Promise.reject(err); }); } ], afterUpdate: [ (fulfillment, options) => { return app.services.FulfillmentService.afterUpdate(fulfillment, options) .catch(err => { return Promise.reject(err); }); } ] } } }; } static schema(app, Sequelize) { return { order_id: { type: Sequelize.INTEGER, allowNull: false }, receipt: { type: Sequelize.TEXT }, status: { type: Sequelize.ENUM, values: lodash_1.values(enums_1.FULFILLMENT_STATUS), defaultValue: enums_1.FULFILLMENT_STATUS.PENDING }, total_items: { type: Sequelize.INTEGER, defaultValue: 0 }, total_fulfilled: { type: Sequelize.INTEGER, defaultValue: 0 }, total_sent_to_fulfillment: { type: Sequelize.INTEGER, defaultValue: 0 }, total_cancelled: { type: Sequelize.INTEGER, defaultValue: 0 }, total_pending_fulfillments: { type: Sequelize.INTEGER, defaultValue: 0 }, status_url: { type: Sequelize.STRING }, has_shipping: { type: Sequelize.BOOLEAN, defaultValue: false }, service: { type: Sequelize.STRING, defaultValue: enums_2.FULFILLMENT_SERVICE.MANUAL }, tracking_company: { type: Sequelize.STRING }, tracking_number: { type: Sequelize.STRING }, extras: { type: Sequelize.JSONB, defaultValue: {} }, live_mode: { type: Sequelize.BOOLEAN, defaultValue: app.config.get('cart.live_mode') }, sent_at: { type: Sequelize.DATE }, fulfilled_at: { type: Sequelize.DATE }, cancelled_at: { type: Sequelize.DATE } }; } static associate(models) { models.Fulfillment.belongsTo(models.Order, { foreignKey: 'order_id', }); models.Fulfillment.hasMany(models.OrderItem, { as: 'order_items', foreignKey: 'fulfillment_id', }); } } exports.Fulfillment = Fulfillment; Fulfillment.prototype.pending = function () { return this; }; Fulfillment.prototype.none = function () { return this; }; Fulfillment.prototype.partial = function () { return this; }; Fulfillment.prototype.sent = function () { this.sent_at = new Date(Date.now()); this.status = enums_1.FULFILLMENT_STATUS.SENT; return this; }; Fulfillment.prototype.fulfilled = function () { this.fulfilled_at = new Date(Date.now()); this.status = enums_1.FULFILLMENT_STATUS.FULFILLED; return this; }; Fulfillment.prototype.cancelled = function () { this.cancelled_at = new Date(Date.now()); this.status = enums_1.FULFILLMENT_STATUS.CANCELLED; return this; }; Fulfillment.prototype.fulfillUpdate = function (data = {}, options = {}) { return this.resolveOrderItems({ transaction: options.transaction || null, reload: options.reload || null }) .then(() => { this.status = data.status || this.status; this.status_url = data.status_url || this.status_url; this.tracking_company = data.tracking_company || this.tracking_company; this.tracking_number = data.tracking_number || this.tracking_number; this.extras = data.extras || this.extras; this.receipt = data.receipt || this.receipt; return this.sequelize.Promise.mapSeries(this.order_items, item => { item.fulfillment_status = this.status; return item.save({ fields: ['fulfillment_status'], transaction: options.transaction || null }); }); }) .then(() => { return this.saveFulfillmentStatus({ transaction: options.transaction || null }); }); }; Fulfillment.prototype.reconcileFulfillmentStatus = function (options = {}) { return this.resolveFulfillmentStatus({ transaction: options.transaction || null, reload: options.reload || null }) .then(() => { if (this.changed('status')) { return this.getOrder({ transaction: options.transaction || null }); } else { return null; } }) .then(resOrder => { if (resOrder) { return resOrder.saveFulfillmentStatus({ transaction: options.transaction || null }); } else { return null; } }) .then(resOrder => { if (resOrder) { return resOrder.saveStatus({ transaction: options.transaction || null }); } else { return null; } }) .then(() => { return this; }); }; Fulfillment.prototype.resolveFulfillmentStatus = function (options = {}) { if (!this.id) { return Promise.resolve(this); } return this.resolveOrderItems({ transaction: options.transaction || null, reload: options.reload || null }) .then(() => { this.setFulfillmentStatus(); return this; }); }; Fulfillment.prototype.resolveOrderItems = function (options) { if (this.order_items && this.order_items.every(i => i instanceof this.app.models['OrderItem'].instance) && options.reload !== true) { return Promise.resolve(this); } else { return this.getOrder_items({ transaction: options.transaction || null }) .then(orderItems => { orderItems = orderItems || []; this.order_items = orderItems; this.setDataValue('order_items', orderItems); this.set('order_items', orderItems); return this; }); } }; Fulfillment.prototype.saveFulfillmentStatus = function (options = {}) { return this.resolveFulfillmentStatus({ transaction: options.transaction || null, reload: options.reload || null }) .then(() => { return this.save({ transaction: options.transaction || null }); }); }; Fulfillment.prototype.setFulfillmentStatus = function () { if (!this.order_items) { throw new Error('Fulfillment.setFulfillmentStatus requires order_items to be populated'); } let fulfillmentStatus = enums_1.FULFILLMENT_STATUS.PENDING; let totalFulfillments = 0; let totalPartialFulfillments = 0; let totalSentFulfillments = 0; let totalNonFulfillments = 0; let totalPendingFulfillments = 0; let totalCancelledFulfillments = 0; let totalQty = 0; this.order_items.forEach(item => { totalQty = totalQty + item.quantity; if (item.fulfillment_status === enums_1.FULFILLMENT_STATUS.FULFILLED) { totalFulfillments = totalFulfillments + item.quantity; } else if (item.fulfillment_status === enums_1.FULFILLMENT_STATUS.PARTIAL) { totalPartialFulfillments = totalPartialFulfillments + item.quantity; } else if (item.fulfillment_status === enums_1.FULFILLMENT_STATUS.SENT) { totalSentFulfillments = totalSentFulfillments + item.quantity; } else if (item.fulfillment_status === enums_1.FULFILLMENT_STATUS.PENDING) { totalPendingFulfillments = totalPendingFulfillments + item.quantity; } else if (item.fulfillment_status === enums_1.FULFILLMENT_STATUS.NONE) { totalNonFulfillments = totalNonFulfillments + item.quantity; } else if (item.fulfillment_status === enums_1.FULFILLMENT_STATUS.CANCELLED) { totalCancelledFulfillments = totalCancelledFulfillments + item.quantity; } }); if (totalFulfillments === totalQty && totalQty > 0) { fulfillmentStatus = enums_1.FULFILLMENT_STATUS.FULFILLED; } else if (totalSentFulfillments === totalQty && totalQty > 0) { fulfillmentStatus = enums_1.FULFILLMENT_STATUS.SENT; } else if (totalPartialFulfillments > 0 && totalQty > 0) { fulfillmentStatus = enums_1.FULFILLMENT_STATUS.PARTIAL; } else if (totalPendingFulfillments === totalQty && totalQty > 0) { fulfillmentStatus = enums_1.FULFILLMENT_STATUS.PENDING; } else if (totalNonFulfillments === totalQty && totalQty > 0) { fulfillmentStatus = enums_1.FULFILLMENT_STATUS.NONE; } else if (totalCancelledFulfillments === totalQty && totalQty > 0) { fulfillmentStatus = enums_1.FULFILLMENT_STATUS.CANCELLED; } this.has_shipping = this.order_items.some(i => i.requires_shipping === true); this.status = fulfillmentStatus; this.total_items = totalQty; this.total_fulfilled = totalFulfillments; this.total_sent_to_fulfillment = totalSentFulfillments; this.total_pending_fulfillments = totalPendingFulfillments; this.total_cancelled = totalCancelledFulfillments; return this; };