From 6d83aba105991669fc1ee4566b042d14d9a9d9bf Mon Sep 17 00:00:00 2001 From: Nat Hamilton Date: Tue, 2 Apr 2019 17:54:54 -0500 Subject: [PATCH 1/2] fix: rounding pricing maths in the cart now making sure any math done against a price value is rounded correctly. Signed-off-by: Nat Hamilton --- .../core/cart/server/no-meteor/util/addCartItems.js | 5 +++-- .../no-meteor/util/xformCartGroupToCommonOrder.js | 12 +++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/imports/plugins/core/cart/server/no-meteor/util/addCartItems.js b/imports/plugins/core/cart/server/no-meteor/util/addCartItems.js index 642e157485c..21942dba59f 100644 --- a/imports/plugins/core/cart/server/no-meteor/util/addCartItems.js +++ b/imports/plugins/core/cart/server/no-meteor/util/addCartItems.js @@ -1,5 +1,6 @@ import Random from "@reactioncommerce/random"; import SimpleSchema from "simpl-schema"; +import { toFixed } from "accounting-js"; import ReactionError from "@reactioncommerce/reaction-error"; import findProductAndVariant from "/imports/plugins/core/catalog/server/no-meteor/utils/findProductAndVariant"; @@ -133,7 +134,7 @@ export default async function addCartItems(context, currentItems, inputItems, op shopId: catalogProduct.shopId, // Subtotal will be kept updated by event handler watching for catalog changes. subtotal: { - amount: variantPriceInfo.price * quantity, + amount: +toFixed(variantPriceInfo.price * quantity, 3), currencyCode: price.currencyCode }, taxCode: chosenVariant.taxCode, @@ -166,7 +167,7 @@ export default async function addCartItems(context, currentItems, inputItems, op // testable code. const updatedQuantity = currentCartItem.quantity + cartItem.quantity; // Recalculate subtotal with new quantity number - const updatedSubtotalAmount = updatedQuantity * cartItem.price.amount; + const updatedSubtotalAmount = +toFixed(updatedQuantity * cartItem.price.amount, 3); updatedItemList[currentMatchingItemIndex] = { ...currentCartItem, ...cartItem, diff --git a/imports/plugins/core/cart/server/no-meteor/util/xformCartGroupToCommonOrder.js b/imports/plugins/core/cart/server/no-meteor/util/xformCartGroupToCommonOrder.js index 93bbbf42b8b..138582c5276 100644 --- a/imports/plugins/core/cart/server/no-meteor/util/xformCartGroupToCommonOrder.js +++ b/imports/plugins/core/cart/server/no-meteor/util/xformCartGroupToCommonOrder.js @@ -1,3 +1,5 @@ +import { toFixed } from "accounting-js"; + /** * @param {Object} cart A cart * @param {Object} group The cart fulfillment group @@ -25,7 +27,7 @@ export default async function xformCartGroupToCommonOrder(cart, group, context) quantity: item.quantity, shopId: item.shopId, subtotal: { - amount: item.price.amount * item.quantity, + amount: +toFixed(item.price.amount * item.quantity, 3), currencyCode }, taxCode: item.taxCode, @@ -55,7 +57,7 @@ export default async function xformCartGroupToCommonOrder(cart, group, context) currencyCode }, total: { - amount: (shipmentMethod.handling || 0) + shipmentMethod.rate, + amount: +toFixed((shipmentMethod.handling || 0) + shipmentMethod.rate, 3), currencyCode } }; @@ -66,7 +68,7 @@ export default async function xformCartGroupToCommonOrder(cart, group, context) // TODO: In the future, we should update this with a discounts update // Discounts are stored as the sum of all discounts, per cart. This will need to be updated when we refactor discounts to go by group. const discountTotal = cart.discount || 0; - const groupItemTotal = items.reduce((sum, item) => (sum + item.subtotal.amount), 0); + const groupItemTotal = +toFixed(items.reduce((sum, item) => (sum + item.subtotal.amount), 0), 3); // orderItemTotal will need to be updated to be the actual total when we eventually have more than one group available const orderItemTotal = groupItemTotal; @@ -80,7 +82,7 @@ export default async function xformCartGroupToCommonOrder(cart, group, context) currencyCode: cart.currencyCode }, groupTotal: { - amount: groupItemTotal - discountTotal, + amount: +toFixed(groupItemTotal - discountTotal, 3), currencyCode: cart.currencyCode }, orderDiscountTotal: { @@ -92,7 +94,7 @@ export default async function xformCartGroupToCommonOrder(cart, group, context) currencyCode: cart.currencyCode }, orderTotal: { - amount: orderItemTotal - discountTotal, + amount: +toFixed(orderItemTotal - discountTotal, 3), currencyCode: cart.currencyCode } }; From 80bfa92bc3718cd5d934406dd2b6dbff21f0a2d0 Mon Sep 17 00:00:00 2001 From: Nat Hamilton Date: Tue, 2 Apr 2019 17:56:49 -0500 Subject: [PATCH 2/2] fix: rounding price maths in orders, making sure that any caculations against a price value rounds correctly. Signed-off-by: Nat Hamilton --- .../server/no-meteor/util/addInvoiceToGroup.js | 5 +++-- .../orders/server/no-meteor/util/buildOrderItem.js | 3 ++- .../no-meteor/util/xformOrderGroupToCommonOrder.js | 12 +++++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/imports/plugins/core/orders/server/no-meteor/util/addInvoiceToGroup.js b/imports/plugins/core/orders/server/no-meteor/util/addInvoiceToGroup.js index a409dfa1e7b..1b6c49c797d 100644 --- a/imports/plugins/core/orders/server/no-meteor/util/addInvoiceToGroup.js +++ b/imports/plugins/core/orders/server/no-meteor/util/addInvoiceToGroup.js @@ -1,3 +1,4 @@ +import { toFixed } from "accounting-js"; /** * @summary Calculate final shipping, discounts, surcharges, and taxes; builds an invoice object @@ -19,7 +20,7 @@ export default function addInvoiceToGroup({ taxTotal }) { // Items - const itemTotal = group.items.reduce((sum, item) => (sum + item.subtotal), 0); + const itemTotal = +toFixed(group.items.reduce((sum, item) => (sum + item.subtotal), 0), 3); // Taxes const effectiveTaxRate = taxableAmount > 0 ? taxTotal / taxableAmount : 0; @@ -32,7 +33,7 @@ export default function addInvoiceToGroup({ // Totals // To avoid rounding errors, be sure to keep this calculation the same between here and // `buildOrderInputFromCart.js` in the client code. - const total = Math.max(0, itemTotal + fulfillmentTotal + taxTotal + groupSurchargeTotal - groupDiscountTotal); + const total = +toFixed(Math.max(0, itemTotal + fulfillmentTotal + taxTotal + groupSurchargeTotal - groupDiscountTotal), 3); group.invoice = { currencyCode, diff --git a/imports/plugins/core/orders/server/no-meteor/util/buildOrderItem.js b/imports/plugins/core/orders/server/no-meteor/util/buildOrderItem.js index 9271eb368b4..31e1831ee14 100644 --- a/imports/plugins/core/orders/server/no-meteor/util/buildOrderItem.js +++ b/imports/plugins/core/orders/server/no-meteor/util/buildOrderItem.js @@ -1,3 +1,4 @@ +import { toFixed } from "accounting-js"; import Random from "@reactioncommerce/random"; import ReactionError from "@reactioncommerce/reaction-error"; @@ -48,7 +49,7 @@ export default async function buildOrderItem(context, { currencyCode, inputItem productVendor: chosenProduct.vendor, quantity, shopId: chosenProduct.shopId, - subtotal: quantity * finalPrice, + subtotal: +toFixed(quantity * finalPrice, 3), title: chosenProduct.title, updatedAt: now, variantId: chosenVariant.variantId, diff --git a/imports/plugins/core/orders/server/no-meteor/util/xformOrderGroupToCommonOrder.js b/imports/plugins/core/orders/server/no-meteor/util/xformOrderGroupToCommonOrder.js index ef783879e88..b8d764f0d6a 100644 --- a/imports/plugins/core/orders/server/no-meteor/util/xformOrderGroupToCommonOrder.js +++ b/imports/plugins/core/orders/server/no-meteor/util/xformOrderGroupToCommonOrder.js @@ -1,3 +1,5 @@ +import { toFixed } from "accounting-js"; + /** * @param {Object} [billingAddress] Billing address, if one was collected * @param {String} [cartId] The source cart ID, if applicable @@ -20,7 +22,7 @@ export default async function xformOrderGroupToCommonOrder({ billingAddress = nu quantity: item.quantity, shopId: item.shopId, subtotal: { - amount: item.price.amount * item.quantity, + amount: +toFixed(item.price.amount * item.quantity, 3), currencyCode }, taxCode: item.taxCode, @@ -50,7 +52,7 @@ export default async function xformOrderGroupToCommonOrder({ billingAddress = nu currencyCode }, total: { - amount: (shipmentMethod.handling || 0) + (shipmentMethod.rate || 0), + amount: +toFixed((shipmentMethod.handling || 0) + (shipmentMethod.rate || 0), 3), currencyCode } }; @@ -60,7 +62,7 @@ export default async function xformOrderGroupToCommonOrder({ billingAddress = nu // TODO: In the future, we should update this with a discounts update // Discounts are stored as the sum of all discounts, per cart. This will need to be updated when we refactor discounts to go by group. - const groupItemTotal = group.items.reduce((sum, item) => (sum + item.subtotal), 0); + const groupItemTotal = +toFixed(group.items.reduce((sum, item) => (sum + item.subtotal), 0), 3); // orderItemTotal will need to be updated to be the actual total when we eventually have more than one group available const orderItemTotal = groupItemTotal; @@ -74,7 +76,7 @@ export default async function xformOrderGroupToCommonOrder({ billingAddress = nu currencyCode }, groupTotal: { - amount: groupItemTotal - discountTotal, + amount: +toFixed(groupItemTotal - discountTotal, 3), currencyCode }, orderDiscountTotal: { @@ -86,7 +88,7 @@ export default async function xformOrderGroupToCommonOrder({ billingAddress = nu currencyCode }, orderTotal: { - amount: orderItemTotal - discountTotal, + amount: +toFixed(orderItemTotal - discountTotal, 3), currencyCode } };