Skip to content

Instantly share code, notes, and snippets.

@pbalan
Created March 7, 2018 09:49
Show Gist options
  • Save pbalan/33d96fef24a6988dd4af762161862ea4 to your computer and use it in GitHub Desktop.
Save pbalan/33d96fef24a6988dd4af762161862ea4 to your computer and use it in GitHub Desktop.
Context data gets lost when changing database
Model.observe('before save', (ctx, next) => {
const promises = [];
let userId = null;
if (undefined !== ctx.options.accessToken) {
userId = ctx.options.accessToken && ctx.options.accessToken.userId;
}
ctx.hookState.relations = {};
/* istanbul ignore if */
/* istanbul ignore else */
if (ctx.isNewInstance && ctx.instance) {
Object.keys(Model.relations).forEach((related) => {
ctx.hookState.relations[related] = ctx.instance [related]();
});
} else if (ctx.instance) {
Object.keys(Model.relations).forEach((related) => {
ctx.hookState.relations[related] = ctx.instance [related]();
});
}
if (ctx.isNewInstance) {
ctx.instance.createdBy = userId || ctx.instance.createdBy;
ctx.instance.createdAt = new Date();
ctx.instance.totalPrice = 0;
ctx.instance.grandTotal = 0;
// this collection would hold the unit price for each product
ctx.instance.__data['price'] = [];
// this collection would hold the quantity for each product
ctx.instance.__data['qty'] = [];
// this collection would hold the discount for each product
ctx.instance.__data['discount'] = [];
// this collection would hold the active Group Buy product IDs
ctx.instance.__data['activeGroupBuyProduct'] = [];
// this collection would hold the bonusProduct discount for same product
ctx.instance.__data['bonusProduct'] = [];
// this collection would hold the isMembershipDiscount for each product
// as it is required to update the product discount based on if the
// member who bought the item has membership or not
ctx.instance.__data['isMembershipDiscount'] = [];
ctx.instance.__data['hasMembership'] = false;
let transactionDetail = ctx.hookState.relations['transactionDetail'];
transactionDetail.forEach((detail) => {
let promise = getProductPriceFromTransactionDetail(
ctx,
detail,
detail.productId,
next
);
promises.push(promise);
});
// by default, new transactions are pending
if (ctx.instance.transactionStatusId === undefined) {
ctx.instance.transactionStatusId = '1';
}
}
let promise = isMember(ctx, ctx.instance.boughtBy, ctx.instance.venueId);
promises.push(promise);
// add transaction discount to the promise chain
promise =
app
.models
.TransactionDiscount
.hasTransactionDiscount(ctx, ctx.instance.__data['venueId']);
promises.push(promise);
/* istanbul ignore next */
Promise.all(promises)
.then((result) => {
let totalDiscount = 0;
// check if any transaction discounts are applied
// Transaction discounts take precedence over product discounts,
// thus it should make all product discounts 0
let totalQty = 0;
let totalPrice = 0;
let cntr = 0;
let txnDiscount = ctx.instance.__data['transactionDiscount'];
let txnDetail = ctx.hookState.relations['transactionDetail'];
if (undefined !== txnDiscount) {
// remove bonus product if exists
txnDetail = txnDetail.filter((item) => {
return item.discount === undefined;
});
ctx.instance.__data['qty'] = ctx.instance.__data['qty'].map(
(qty, item) => {
if (ctx.instance.__data['bonusProduct'][item] !== undefined) {
qty -= ctx.instance.__data['bonusProduct'][item];
}
totalQty += qty;
return qty;
});
ctx.instance.__data['price'].map(
(price, item) => {
totalPrice += (ctx.instance.__data['qty'][item] * price);
});
ctx.instance.transactionDiscountId = txnDiscount.id;
ctx.instance.totalDiscount =
app
.models
.TransactionDiscount
.getTotalTransactionDiscount(
txnDiscount.discount,
totalPrice,
totalQty
);
cntr = 0;
ctx.instance.__data['price'].map((price, item) => {
let subTotal = price * ctx.instance.__data['qty'][item];
txnDetail[cntr]['subTotal'] = subTotal;
txnDetail[cntr]['netTotal'] = subTotal;
txnDetail[cntr]['discount'] = 0;
ctx.instance.totalPrice += subTotal;
ctx.instance.grandTotal =
ctx.instance.totalPrice - ctx.instance.totalDiscount;
cntr++;
});
} else {
// iterate over each product price
// and calculate subTotal, qty, product discount, and netTotal
cntr = 0;
ctx.instance.__data['price'].map((price, item) => {
let appliedDiscount = 0;
// special case, when bonus products are of same type, we must
// update the quantity here
txnDetail[cntr]['quantity'] = ctx.instance.__data['qty'][item];
let subTotal = price * ctx.instance.__data['qty'][item];
// when its membership discount and user has no membership,
// reset discount to 0
if (ctx.instance.__data['isMembershipDiscount'][item] &&
ctx.instance.__data['hasMembership'] === false
) {
ctx.instance.__data['discount'][item] = 0;
}
appliedDiscount += ctx.instance.__data['discount'][item];
txnDetail[cntr]['subTotal'] = subTotal;
txnDetail[cntr]['discount'] = appliedDiscount;
txnDetail[cntr]['netTotal'] = subTotal - appliedDiscount;
ctx.instance.totalPrice += subTotal;
ctx.instance.totalDiscount += appliedDiscount;
// Calculate grandTotal by subtracting discount from totalPrice
ctx.instance.grandTotal =
ctx.instance.totalPrice - ctx.instance.totalDiscount;
cntr++;
});
}
ctx.hookState.relations['transactionDetail'] = txnDetail;
delete ctx.instance.__data['price'];
delete ctx.instance.__data['qty'];
delete ctx.instance.__data['discount'];
delete ctx.instance.__data['hasMembership'];
delete ctx.instance.__data['isMembershipDiscount'];
delete ctx.instance.__data['transactionDiscount'];
delete ctx.instance.__data['bonusProduct'];
next();
})
.catch(err => {
/* istanbul ignore next */
next(err);
});
});
/**
* @param {Object} ctx LoopbackContext
* @param {Object} detail TransactionDetail Model instance
* @param {Integer} productId Product Id
* @param {Function} next callback
* @return {Object} ctx LoopbackContext
*/
function getProductPriceFromTransactionDetail(
ctx, detail, productId, next) {
let promise = Promise.resolve('ready');
promise = app.models.Product.findById(productId, {
include: [
{
relation: 'productPricing',
scope: {
order: 'id DESC',
limit: 1,
},
},
{
relation: 'productDiscount',
scope: {
order: 'id DESC',
limit: 1,
where: {
and: [{
or: [{
endDate: null,
}, {
endDate: {
gte: Date.now(),
},
}],
},
{
startDate: {
lte: Date.now(),
},
}],
},
include: {
relation: 'discount',
scope: {
order: 'id DESC',
limit: 1,
},
},
},
},
],
}, (err, instance) => {
let discount = 0;
if (null !== instance) {
const data = instance.toJSON();
const productPricing = data.productPricing[0];
ctx.instance.__data['price'][productId] = productPricing.unitPrice;
ctx.instance.__data['qty'][productId] = detail.quantity;
ctx.instance.__data['discount'][productId] = discount;
if (data.productDiscount.length > 0) {
const discountObj = data.productDiscount[0].discount;
let discountTypeId = undefined;
if (discountObj !== undefined && discountObj !== null) {
discountTypeId = discountObj.discountTypeId;
}
discount = app.models.Discount.getProductDiscounts(
productPricing,
discountObj,
detail.quantity
);
if (discountTypeId ===
discountTypes.DISCOUNT_TYPE_MEMBERSHIP_PRICE_OFF) {
ctx.instance.__data['isMembershipDiscount'][productId] = true;
} else {
ctx.instance.__data['isMembershipDiscount'][productId] = false;
}
ctx.instance.__data['discount'][productId] = discount;
// run once per transaction
// NOTE: group buy product must be checked before bonus product
// to prevent group buy product being offered as a bonus product
if (ctx.instance.__data['activeGroupBuyProduct'].length === 0) {
getActiveGroupBuyProducts(ctx);
}
if (discountTypeId === discountTypes.DISCOUNT_TYPE_GROUP_BUY) {
// check if this user is allowed for group buy discount
isGroupBuyAllowed(
ctx,
discountObj,
productPricing,
detail.quantity,
productId,
ctx.instance.boughtBy,
next
);
}
if (discountTypeId ===
discountTypes.DISCOUNT_TYPE_BONUS_PRODUCT) {
// TODO: @prashant
// add configured bonus products to the cart
// by either updating the quantity if same product
// or getProduct with unitPrice 0.0
addBonusProduct(
ctx,
discountObj,
productPricing,
productId
);
}
}
}
return ctx;
});
return promise;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment