Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion src/ecommerce.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Types from './types';
import Constants from './constants';
import { extend } from './utils';
import { extend, parseNumber } from './utils';

var Messages = Constants.Messages;

Expand Down Expand Up @@ -47,6 +47,33 @@ export default function Ecommerce(mpInstance) {
}
};

// When the caller does not supply a transaction-level total via
// transactionAttributes.Revenue, derive it from the product list
// (quantity * price) plus shipping and tax. A total that the caller provided
// (including 0) is never overwritten.
this.calculateProductActionTotalAmount = function(productAction) {
Comment thread
alexs-mparticle marked this conversation as resolved.
if (!productAction || productAction.TotalAmount != null) {
return productAction;
}

let totalAmount = 0;

if (Array.isArray(productAction.ProductList)) {
productAction.ProductList.forEach(function(product) {
totalAmount +=
parseNumber(product.Quantity) * parseNumber(product.Price);
});
}

totalAmount +=
parseNumber(productAction.ShippingAmount) +
parseNumber(productAction.TaxAmount);

productAction.TotalAmount = totalAmount;

return productAction;
};

this.getProductActionEventName = function(productActionType) {
switch (productActionType) {
case Types.ProductActionType.AddToCart:
Expand Down
8 changes: 8 additions & 0 deletions src/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,14 @@ export default function Events(mpInstance) {
commerceEvent.EventAttributes = attrs;
}

// When no transaction total (Revenue) was provided, derive it from
// the products, shipping, and tax.
if (commerceEvent.ProductAction) {
mpInstance._Ecommerce.calculateProductActionTotalAmount(
commerceEvent.ProductAction
);
}

mpInstance._APIClient.sendEventToServer(commerceEvent, options);

// https://go.mparticle.com/work/SQDSDKS-6038
Expand Down
84 changes: 83 additions & 1 deletion test/src/tests-eCommerce.js
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,46 @@ describe('eCommerce', function() {
checkoutEvent.data.product_action.products[1].should.have.property('id', 'galaxySKU');
});

it('should auto-calculate total amount from the product list when revenue is not provided', async () => {
await waitForCondition(hasIdentifyReturned);
const product1 = mParticle.eCommerce.createProduct('iphone', 'iphoneSKU', 999, 2);
const product2 = mParticle.eCommerce.createProduct('galaxy', 'galaxySKU', 799, 1);

mParticle.eCommerce.logProductAction(mParticle.ProductActionType.Checkout, [product1, product2], null, null, {Step: 4, Option: 'Visa'});

const checkoutEvent = findEventFromRequest(fetchMock.calls(), 'checkout');

Should(checkoutEvent).be.ok();
// 999 * 2 + 799 * 1 = 2797
checkoutEvent.data.product_action.should.have.property('total_amount', 2797);
});

it('should include shipping and tax in the auto-calculated total amount', async () => {
await waitForCondition(hasIdentifyReturned);
const product = mParticle.eCommerce.createProduct('iphone', 'iphoneSKU', 999, 1);

mParticle.eCommerce.logProductAction(mParticle.ProductActionType.Checkout, [product], null, null, {Shipping: 10, Tax: 5});

const checkoutEvent = findEventFromRequest(fetchMock.calls(), 'checkout');

Should(checkoutEvent).be.ok();
// 999 * 1 + 10 shipping + 5 tax = 1014
checkoutEvent.data.product_action.should.have.property('total_amount', 1014);
});

it('should not override a revenue that the caller provided', async () => {
await waitForCondition(hasIdentifyReturned);
const product1 = mParticle.eCommerce.createProduct('iphone', 'iphoneSKU', 999, 1);
const product2 = mParticle.eCommerce.createProduct('galaxy', 'galaxySKU', 799, 1);

mParticle.eCommerce.logProductAction(mParticle.ProductActionType.Checkout, [product1, product2], null, null, {Revenue: 5});

const checkoutEvent = findEventFromRequest(fetchMock.calls(), 'checkout');

Should(checkoutEvent).be.ok();
checkoutEvent.data.product_action.should.have.property('total_amount', 5);
});

it('should log checkout option', async () => {
await waitForCondition(hasIdentifyReturned);
const product = mParticle.eCommerce.createProduct('iPhone', '12345', 400);
Expand Down Expand Up @@ -1423,12 +1463,54 @@ describe('eCommerce', function() {
productAction.Affiliation.should.equal("affiliation")
productAction.CouponCode.should.equal("couponCode")

// convert strings to 0
// convert strings to 0
productAction.TotalAmount.should.equal(0)
productAction.ShippingAmount.should.equal(0)
productAction.TaxAmount.should.equal(0)
});

it('should derive total amount from the product list, shipping, and tax', () => {
const productAction = {
ProductList: [
{ Price: 100, Quantity: 2 },
{ Price: 50, Quantity: 1 },
],
ShippingAmount: 10,
TaxAmount: 5,
};

mParticle
.getInstance()
._Ecommerce.calculateProductActionTotalAmount(productAction);

// 100 * 2 + 50 * 1 + 10 shipping + 5 tax = 265
productAction.TotalAmount.should.equal(265);
});

it('should default to zero when there are no products, shipping, or tax', () => {
const productAction = { ProductList: [] };

mParticle
.getInstance()
._Ecommerce.calculateProductActionTotalAmount(productAction);

productAction.TotalAmount.should.equal(0);
});

it('should not override a total amount that was already set', () => {
const productAction = {
TotalAmount: 0,
ProductList: [{ Price: 100, Quantity: 1 }],
};

mParticle
.getInstance()
._Ecommerce.calculateProductActionTotalAmount(productAction);

// An explicitly supplied total (including 0) is never recalculated
productAction.TotalAmount.should.equal(0);
});

it('should allow a user to pass in a source_message_id to a commerce event', async () => {
await waitForCondition(hasIdentifyReturned);
const product = mParticle.eCommerce.createProduct(
Expand Down
Loading