Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix price updates #1633

Merged
merged 8 commits into from
Dec 20, 2016
Merged
162 changes: 161 additions & 1 deletion imports/plugins/core/revisions/server/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Products, Revisions, Tags, Media } from "/lib/collections";
import { Logger } from "/server/api";
import { RevisionApi } from "../lib/api";


function convertMetadata(modifierObject) {
const metadata = {};
for (const prop in modifierObject) {
Expand All @@ -18,6 +17,145 @@ function convertMetadata(modifierObject) {
return metadata;
}

const ProductRevision = {
getProductPriceRange(productId) {
const product = Products.findOne(productId);
if (!product) {
return "";
}
const variants = this.getTopVariants(product._id);

if (variants.length > 0) {
const variantPrices = [];
variants.forEach(variant => {
if (variant.isVisible === true) {
const range = this.getVariantPriceRange(variant._id);
if (typeof range === "string") {
const firstPrice = parseFloat(range.substr(0, range.indexOf(" ")));
const lastPrice = parseFloat(range.substr(range.lastIndexOf(" ") + 1));
variantPrices.push(firstPrice, lastPrice);
} else {
variantPrices.push(range);
}
} else {
variantPrices.push(0, 0);
}
});
const priceMin = _.min(variantPrices);
const priceMax = _.max(variantPrices);
let priceRange = `${priceMin} - ${priceMax}`;
// if we don't have a range
if (priceMin === priceMax) {
priceRange = priceMin.toString();
}
const priceObject = {
range: priceRange,
min: priceMin,
max: priceMax
};
return priceObject;
}
// if we have no variants subscribed to (client)
// we'll get the price object previously from the product
return product.price;
},

getVariantPriceRange(variantId) {
const children = this.getVariants(variantId);
const visibleChildren = children.filter(child => child.isVisible);

switch (visibleChildren.length) {
case 0:
const topVariant = this.getProduct(variantId);
// topVariant could be undefined when we removing last top variant
return topVariant && topVariant.price;
case 1:
return visibleChildren[0].price;
default:
let priceMin = Number.POSITIVE_INFINITY;
let priceMax = Number.NEGATIVE_INFINITY;

visibleChildren.map(child => {
if (child.price < priceMin) {
priceMin = child.price;
}
if (child.price > priceMax) {
priceMax = child.price;
}
});

if (priceMin === priceMax) {
// TODO check impact on i18n/formatPrice from moving return to string
return priceMin.toString();
}
return `${priceMin} - ${priceMax}`;
}
},

findRevision({ documentId }) {
return Revisions.findOne({
"documentId": documentId,
"workflow.status": {
$nin: [
"revision/published"
]
}
});
},

getProduct(variantId) {
const product = Products.findOne(variantId);
const revision = this.findRevision({
documentId: variantId
});

return revision && revision.documentData || product;
},

getTopVariants(id) {
const variants = [];

Products.find({
ancestors: [id],
type: "variant",
isDeleted: false
}).map((product) => {
const revision = this.findRevision({
documentId: product._id
});

if (revision && revision.documentData.isVisible) {
variants.push(revision.documentData);
} else if (!revision && product.isVisible) {
variants.push(product);
}
});

return variants;
},

getVariants(id, type) {
const variants = [];

Products.find({
ancestors: { $in: [id] },
type: type || "variant",
isDeleted: false
}).forEach((product) => {
const revision = this.findRevision({
documentId: product._id
});

if (revision && revision.documentData.isVisible) {
variants.push(revision.documentData);
} else if (!revision && product.isVisible) {
variants.push(product);
}
});
return variants;
}
};

Media.files.before.insert((userid, media) => {
if (RevisionApi.isRevisionControlEnabled() === false) {
return true;
Expand Down Expand Up @@ -232,6 +370,28 @@ Products.before.update(function (userId, product, fieldNames, modifier, options)
revisionModifier.$addToSet = {};
}
revisionModifier.$addToSet[`documentData.${property}`] = modifier.$push[property];
} else if (operation === "$set" && property === "price" && Array.isArray(product.ancestors) && product.ancestors.length) {
Revisions.update(revisionSelector, {
$set: {
"documentData.price": modifier.$set.price
}
});

const updateId = product.ancestors[0] || product._id;
const priceRange = ProductRevision.getProductPriceRange(updateId);

Meteor.call("products/updateProductField", updateId, "price", priceRange);
} else if (operation === "$set" && property === "isVisible" && Array.isArray(product.ancestors) && product.ancestors.length) {
Revisions.update(revisionSelector, {
$set: {
"documentData.isVisible": modifier.$set.isVisible
}
});

const updateId = product.ancestors[0] || product._id;
const priceRange = ProductRevision.getProductPriceRange(updateId);

Meteor.call("products/updateProductField", updateId, "price", priceRange);
} else {
// Let everything else through
revisionModifier[operation][`documentData.${property}`] = modifier[operation][property];
Expand Down
53 changes: 25 additions & 28 deletions lib/api/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,29 @@ export default Catalog = {
* @return {Object} range, min, max
*/
getProductPriceRange(productId) {
const product = Products.findOne(productId);
const product = applyProductRevision(Products.findOne(productId));
if (!product) {
return "";
}
const variants = this.getTopVariants(product._id);
// if we have variants we have a price range.
// this processing will default on the server
if (variants.length > 0) {
const visibileVariant = variants.filter(variant => variant.isVisible === true);

if (visibileVariant.length > 0) {
const variantPrices = [];
variants.forEach(variant => {
const range = this.getVariantPriceRange(variant._id);
if (typeof range === "string") {
const firstPrice = parseFloat(range.substr(0, range.indexOf(" ")));
const lastPrice = parseFloat(range.substr(range.lastIndexOf(" ") + 1));
variantPrices.push(firstPrice, lastPrice);
if (variant.isVisible === true) {
const range = this.getVariantPriceRange(variant._id);
if (typeof range === "string") {
const firstPrice = parseFloat(range.substr(0, range.indexOf(" ")));
const lastPrice = parseFloat(range.substr(range.lastIndexOf(" ") + 1));
variantPrices.push(firstPrice, lastPrice);
} else {
variantPrices.push(range);
}
} else {
variantPrices.push(range);
variantPrices.push(0, 0);
}
});
const priceMin = _.min(variantPrices);
Expand Down Expand Up @@ -83,24 +89,27 @@ export default Catalog = {
*/
getVariantPriceRange(variantId) {
const children = this.getVariants(variantId);
const visibleChildren = children.filter(child => child.isVisible);

switch (children.length) {
switch (visibleChildren.length) {
case 0:
const topVariant = applyProductRevision(Products.findOne(variantId));
// topVariant could be undefined when we removing last top variant
return topVariant && topVariant.price;
case 1:
return children[0].price;
return visibleChildren[0].price;
default:
let priceMin = Number.POSITIVE_INFINITY;
let priceMax = Number.NEGATIVE_INFINITY;

children.map(child => {
if (child.price < priceMin) {
priceMin = child.price;
}
if (child.price > priceMax) {
priceMax = child.price;
if (child.isVisible === true) {
if (child.price < priceMin) {
priceMin = child.price;
}
if (child.price > priceMax) {
priceMax = child.price;
}
}
});

Expand Down Expand Up @@ -134,19 +143,7 @@ export default Catalog = {
* @return {Object} product document
*/
getPublishedOrRevision(product) {
if (product.__revisions && product.__revisions.length) {
const cleanProduct = Object.assign({}, product);
delete cleanProduct.__revisions;

return Object.assign({},
product.__revisions[0].documentData,
{
__published: cleanProduct,
__draft: product.__revisions[0]
}
);
}
return product;
return applyProductRevision(product);
},

/**
Expand Down
5 changes: 4 additions & 1 deletion lib/collections/schemas/products.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,16 +281,19 @@ export const ProductVariant = new SimpleSchema({

export const PriceRange = new SimpleSchema({
range: {
type: String
type: String,
defaultValue: "0.00"
},
min: {
type: Number,
decimal: true,
defaultValue: 0,
optional: true
},
max: {
type: Number,
decimal: true,
defaultValue: 0,
optional: true
}
});
Expand Down
9 changes: 8 additions & 1 deletion server/methods/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _ from "lodash";
import { EJSON } from "meteor/ejson";
import { check } from "meteor/check";
import { Meteor } from "meteor/meteor";
import { Catalog, copyFile } from "/lib/api";
import { Catalog, copyFile, ReactionProduct } from "/lib/api";
import { Media, Products, Revisions, Tags } from "/lib/collections";
import { Logger, Reaction } from "/server/api";

Expand Down Expand Up @@ -1258,6 +1258,13 @@ Meteor.methods({
}
});

if (Array.isArray(product.ancestors) && product.ancestors.length) {
const updateId = product.ancestors[0] || product._id;
const updatedPriceRange = ReactionProduct.getProductPriceRange(updateId);

Meteor.call("products/updateProductField", updateId, "price", updatedPriceRange);
}

// if collection updated we return new `isVisible` state
return res === 1 && !product.isVisible;
}
Expand Down
11 changes: 10 additions & 1 deletion server/publications/collections/product.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,16 @@ Meteor.publish("Product", function (productId) {
}
}).observe({
added: (revision) => {
this.added("Revisions", revision._id, revision);
let product;
if (!revision.parentDocument) {
product = Products.findOne(revision.documentId);
} else {
product = Products.findOne(revision.parentDocument);
}
if (product) {
this.added("Products", product._id, product);
this.added("Revisions", revision._id, revision);
}
},
changed: (revision) => {
let product;
Expand Down
12 changes: 11 additions & 1 deletion server/publications/collections/products.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,17 @@ Meteor.publish("Products", function (productScrollLimit = 24, productFilters, so
}
}).observe({
added: (revision) => {
this.added("Revisions", revision._id, revision);
let product;
if (!revision.documentType || revision.documentType === "product") {
product = Products.findOne(revision.documentId);
} else if (revision.documentType === "image" || revision.documentType === "tag") {
product = Products.findOne(revision.parentDocument);
}

if (product) {
this.added("Products", product._id, product);
this.added("Revisions", revision._id, revision);
}
},
changed: (revision) => {
let product;
Expand Down