From a3f1701f5b9ecbfc6f447f814e027b6ed3973a7f Mon Sep 17 00:00:00 2001 From: alakhmani Date: Tue, 13 Feb 2024 14:39:46 -0800 Subject: [PATCH 01/69] test commit --- .../cart/calculator/classes/ShippingCartCalculatorSample.cls | 1 + 1 file changed, 1 insertion(+) diff --git a/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls b/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls index 8f70293..9697c1a 100644 --- a/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls +++ b/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls @@ -84,6 +84,7 @@ public class ShippingCartCalculatorSample extends CartExtension.ShippingCartCalc CartExtension.CartDeliveryGroupMethod cartDeliveryGroupMethod02 = new CartExtension.CartDeliveryGroupMethod('Next Day Air', 15.99, shippingProduct); cartDeliveryGroupMethod02.setCarrier('UPS'); cartDeliveryGroupMethod02.setClassOfService('Next Day Air'); + cartDeliveryGroupMethods.add(cartDeliveryGroupMethod01); cartDeliveryGroupMethods.add(cartDeliveryGroupMethod02); } From 4a8ba7ab450badfa6780c62a63d7e3c985c5458c Mon Sep 17 00:00:00 2001 From: alakhmani Date: Tue, 13 Feb 2024 14:47:54 -0800 Subject: [PATCH 02/69] update sample shipping calculator for Delivery Estimation --- .../classes/ShippingCartCalculatorSample.cls | 181 +++++++++++++++++- 1 file changed, 179 insertions(+), 2 deletions(-) diff --git a/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls b/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls index 9697c1a..4edea55 100644 --- a/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls +++ b/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls @@ -81,18 +81,195 @@ public class ShippingCartCalculatorSample extends CartExtension.ShippingCartCalc CartExtension.CartDeliveryGroupMethod cartDeliveryGroupMethod01 = new CartExtension.CartDeliveryGroupMethod('Ground Shipping', 10.99, shippingProduct); cartDeliveryGroupMethod01.setCarrier('USPS'); cartDeliveryGroupMethod01.setClassOfService('Ground Shipping'); + cartDeliveryGroupMethod01.setTransitTimeMin(1); + cartDeliveryGroupMethod01.setTransitTimeMax(3); + cartDeliveryGroupMethod01.setTransitTimeUnit(CartExtension.TimeUnitEnum.DAYS); + cartDeliveryGroupMethod01.setProcessTime(1); + cartDeliveryGroupMethod01.setProcessTimeUnit(CartExtension.TimeUnitEnum.WEEKS); CartExtension.CartDeliveryGroupMethod cartDeliveryGroupMethod02 = new CartExtension.CartDeliveryGroupMethod('Next Day Air', 15.99, shippingProduct); cartDeliveryGroupMethod02.setCarrier('UPS'); cartDeliveryGroupMethod02.setClassOfService('Next Day Air'); - + cartDeliveryGroupMethod02.setTransitTimeMin(1); + cartDeliveryGroupMethod02.setTransitTimeMax(4); + cartDeliveryGroupMethod02.setTransitTimeUnit(CartExtension.TimeUnitEnum.DAYS); + cartDeliveryGroupMethod02.setProcessTime(1); + cartDeliveryGroupMethod02.setProcessTimeUnit(CartExtension.TimeUnitEnum.DAYS); cartDeliveryGroupMethods.add(cartDeliveryGroupMethod01); cartDeliveryGroupMethods.add(cartDeliveryGroupMethod02); } - } + } + } + } + } + + private static String generateRandomString(Integer len) { + final String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'; + String randStr = ''; + while (randStr.length() < len) { + Integer idx = Math.mod(Math.abs(Crypto.getRandomInteger()), chars.length()); + randStr += chars.substring(idx, idx+1); + } + return randStr; + } + + // Note: This sample method currently only takes in numberOfUniqueItems as an input parameter. For + // real-world scenarios, expand the parameter list. + private ShippingOptionsAndRatesFromExternalService[] getShippingOptionsAndRatesFromExternalService( + Integer numberOfUniqueItems, CartExtension.CartValidationOutputList cartValidationOutputCollection) { + final Integer SuccessfulHttpRequest = 200; + ShippingOptionsAndRatesFromExternalService[] shippingOptions = new List(); + Http http = new Http(); + HttpRequest request = new HttpRequest(); + request.setEndpoint(externalShippingURL); + request.setMethod('GET'); + HttpResponse response = http.send(request); + + // If the request is successful, parse the JSON response. The response looks like this: + // [{"status":"calculated","rate":{"name":"Delivery Method 1","serviceName":"Test Carrier 1","serviceCode":"SNC9600","shipmentCost":11.99,"otherCost":5.99}}, undefined undefined + // {"status":"calculated","rate":{"name":"Delivery Method 2","serviceName":"Test Carrier + // 2","serviceCode":"SNC9600","shipmentCost":15.99,"otherCost":6.99}}] + if (response.getStatusCode() == SuccessfulHttpRequest) { + List results = (List) JSON.deserializeUntyped(response.getBody()); + for (Object result : results) { + Map subresult = (Map) result; + Map providerAndRate = (Map) subresult.get('rate'); + shippingOptions.add( new ShippingOptionsAndRatesFromExternalService( + (String) providerAndRate.get('name'), + (String) providerAndRate.get('serviceCode'), + (Decimal) providerAndRate.get('shipmentCost'), + (Decimal) providerAndRate.get('otherCost'), + (String) providerAndRate.get('serviceName'), + (String) providerAndRate.get('serviceName'), + (String) providerAndRate.get('serviceCode'), + generateRandomString(10), + true, + (Integer) providerAndRate.get('transitTimeMin'), + (Integer) providerAndRate.get('transitTimeMax'), + (CartExtension.TimeUnitEnum) providerAndRate.get('transitTimeUnit'), + (Integer) providerAndRate.get('processTime'), + (CartExtension.TimeUnitEnum) providerAndRate.get('processTimeUnit') + )); } + return shippingOptions; + } else { + String errorMessage = 'We failed to calculate shipping options for your cart.'; + if(response.getStatusCode() == 404) { + errorMessage = '404. You must create a sample application or add your own service which returns a valid response'; + } + + // Create a CVO with the Error + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.SHIPPING, + CartExtension.CartValidationOutputLevelEnum.ERROR + ); + cvo.setMessage(errorMessage); + cartValidationOutputCollection.add(cvo); + return null; } } + // Structure to store the shipping options retrieved from external service. + Class ShippingOptionsAndRatesFromExternalService { + private String name; + private String provider; + private Decimal rate; + private Decimal otherCost; + private String serviceName; + private String carrier; + private String classOfService; + private String referenceNumber; + private Boolean isActive; + private Integer transitTimeMin; + private Integer transitTimeMax; + private CartExtension.TimeUnitEnum transitTimeUnit; + private Integer processTime; + private CartExtension.TimeUnitEnum processTimeUnit; + + public ShippingOptionsAndRatesFromExternalService() { + name = ''; + provider = ''; + rate = 0.0; + serviceName = ''; + otherCost = 0.0; + carrier = ''; + classOfService = ''; + referenceNumber = ''; + isActive = true; + transitTimeMin = 0; + transitTimeMax = 0; + transitTimeUnit = null; + processTime = 0; + processTimeUnit = null; + } + + public ShippingOptionsAndRatesFromExternalService(String someName, String someProvider, Decimal someRate, Decimal someOtherCost, String someServiceName, + String someCarrier, String someClassOfService, String someReferenceNumber, Boolean someIsActive, Integer someTransitTimeMin, Integer someTransitTimeMax, + CartExtension.TimeUnitEnum someTransitTimeUnit, Integer someProcessTime, CartExtension.TimeUnitEnum someProcessTimeUnit) { + name = someName; + provider = someProvider; + rate = someRate; + otherCost = someOtherCost; + serviceName = someServiceName; + carrier = someCarrier; + classOfService = someClassOfService; + referenceNumber = someReferenceNumber; + isActive = someIsActive; + transitTimeMin = someTransitTimeMin; + transitTimeMax = someTransitTimeMax; + transitTimeUnit = someTransitTimeUnit; + processTime = someProcessTime; + processTimeUnit = someProcessTimeUnit; + } + + public String getProvider() { return provider; } + public Decimal getRate() { return rate; } + public Decimal getOtherCost() { return otherCost; } + public String getServiceName() { return serviceName; } + public String getName() { return name; } + public String getCarrier() { return carrier; } + public String getClassOfService() { return classOfService; } + public String getReferenceNumber() { return referenceNumber; } + public Boolean isActive() { return isActive; } + public Integer getTransitTimeMin() { return transitTimeMin; } + public Integer getTransitTimeMax() { return transitTimeMax; } + public CartExtension.TimeUnitEnum getTransitTimeUnit() { return transitTimeUnit; } + public Integer getProcessTime() { return processTime; } + public CartExtension.TimeUnitEnum getProcessTimeUnit() { return processTimeUnit; } + } + + + private void populateCartDeliveryGroupMethodWithShippingOptions( + List shippingOptions, + CartExtension.CartDeliveryGroupMethodList cartDeliveryGroupMethodCollection, + String shippingProduct, + CartExtension.CartValidationOutputList cartValidationOutputCollection + ) { + for (ShippingOptionsAndRatesFromExternalService shippingOption : shippingOptions) { + String carrier = shippingOption.serviceName; + String classOfService = shippingOption.provider; + // Create a CartDeliveryGroupMethod for every shipping option returned from the external + // service + CartExtension.CartDeliveryGroupMethod cartDeliveryGroupMethod = new CartExtension.CartDeliveryGroupMethod( + shippingOption.getName(), + shippingOption.getRate(), + shippingProduct + ); + cartDeliveryGroupMethod.setExternalProvider(shippingOption.getProvider()); + cartDeliveryGroupMethod.setCarrier(shippingOption.getCarrier()); + cartDeliveryGroupMethod.setClassOfService(shippingOption.getClassOfService()); + cartDeliveryGroupMethod.setIsActive(shippingOption.isActive()); + cartDeliveryGroupMethod.setReferenceNumber(shippingOption.getReferenceNumber()); + cartDeliveryGroupMethod.setTransitTimeMin(shippingOption.getTransitTimeMin()); + cartDeliveryGroupMethod.setTransitTimeMax(shippingOption.getTransitTimeMax()); + cartDeliveryGroupMethod.setTransitTimeUnit(shippingOption.getTransitTimeUnit()); + cartDeliveryGroupMethod.setProcessTime(shippingOption.getProcessTime()); + cartDeliveryGroupMethod.setProcessTimeUnit(shippingOption.getProcessTimeUnit()); + + cartDeliveryGroupMethodCollection.add(cartDeliveryGroupMethod); + } + } +} + private static String generateRandomString(Integer len) { final String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'; String randStr = ''; From 04b36cee4e40cacbcdf7015dee85bd6c682094c4 Mon Sep 17 00:00:00 2001 From: alakhmani Date: Tue, 13 Feb 2024 14:55:32 -0800 Subject: [PATCH 03/69] update sample shipping calculator --- .../classes/ShippingCartCalculatorSample.cls | 144 +----------------- 1 file changed, 4 insertions(+), 140 deletions(-) diff --git a/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls b/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls index 4edea55..602fb1b 100644 --- a/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls +++ b/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSample.cls @@ -203,8 +203,9 @@ public class ShippingCartCalculatorSample extends CartExtension.ShippingCartCalc } public ShippingOptionsAndRatesFromExternalService(String someName, String someProvider, Decimal someRate, Decimal someOtherCost, String someServiceName, - String someCarrier, String someClassOfService, String someReferenceNumber, Boolean someIsActive, Integer someTransitTimeMin, Integer someTransitTimeMax, - CartExtension.TimeUnitEnum someTransitTimeUnit, Integer someProcessTime, CartExtension.TimeUnitEnum someProcessTimeUnit) { + String someCarrier, String someClassOfService, String someReferenceNumber, Boolean someIsActive, + Integer someTransitTimeMin, Integer someTransitTimeMax,CartExtension.TimeUnitEnum someTransitTimeUnit, Integer someProcessTime, + CartExtension.TimeUnitEnum someProcessTimeUnit) { name = someName; provider = someProvider; rate = someRate; @@ -259,149 +260,12 @@ public class ShippingCartCalculatorSample extends CartExtension.ShippingCartCalc cartDeliveryGroupMethod.setClassOfService(shippingOption.getClassOfService()); cartDeliveryGroupMethod.setIsActive(shippingOption.isActive()); cartDeliveryGroupMethod.setReferenceNumber(shippingOption.getReferenceNumber()); + cartDeliveryGroupMethodCollection.add(cartDeliveryGroupMethod); cartDeliveryGroupMethod.setTransitTimeMin(shippingOption.getTransitTimeMin()); cartDeliveryGroupMethod.setTransitTimeMax(shippingOption.getTransitTimeMax()); cartDeliveryGroupMethod.setTransitTimeUnit(shippingOption.getTransitTimeUnit()); cartDeliveryGroupMethod.setProcessTime(shippingOption.getProcessTime()); cartDeliveryGroupMethod.setProcessTimeUnit(shippingOption.getProcessTimeUnit()); - - cartDeliveryGroupMethodCollection.add(cartDeliveryGroupMethod); - } - } -} - - private static String generateRandomString(Integer len) { - final String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'; - String randStr = ''; - while (randStr.length() < len) { - Integer idx = Math.mod(Math.abs(Crypto.getRandomInteger()), chars.length()); - randStr += chars.substring(idx, idx+1); - } - return randStr; - } - - // Note: This sample method currently only takes in numberOfUniqueItems as an input parameter. For - // real-world scenarios, expand the parameter list. - private ShippingOptionsAndRatesFromExternalService[] getShippingOptionsAndRatesFromExternalService( - Integer numberOfUniqueItems, CartExtension.CartValidationOutputList cartValidationOutputCollection) { - final Integer SuccessfulHttpRequest = 200; - ShippingOptionsAndRatesFromExternalService[] shippingOptions = new List(); - Http http = new Http(); - HttpRequest request = new HttpRequest(); - request.setEndpoint(externalShippingURL); - request.setMethod('GET'); - HttpResponse response = http.send(request); - - // If the request is successful, parse the JSON response. The response looks like this: - // [{"status":"calculated","rate":{"name":"Delivery Method 1","serviceName":"Test Carrier 1","serviceCode":"SNC9600","shipmentCost":11.99,"otherCost":5.99}}, undefined undefined - // {"status":"calculated","rate":{"name":"Delivery Method 2","serviceName":"Test Carrier - // 2","serviceCode":"SNC9600","shipmentCost":15.99,"otherCost":6.99}}] - if (response.getStatusCode() == SuccessfulHttpRequest) { - List results = (List) JSON.deserializeUntyped(response.getBody()); - for (Object result : results) { - Map subresult = (Map) result; - Map providerAndRate = (Map) subresult.get('rate'); - shippingOptions.add( new ShippingOptionsAndRatesFromExternalService( - (String) providerAndRate.get('name'), - (String) providerAndRate.get('serviceCode'), - (Decimal) providerAndRate.get('shipmentCost'), - (Decimal) providerAndRate.get('otherCost'), - (String) providerAndRate.get('serviceName'), - (String) providerAndRate.get('serviceName'), - (String) providerAndRate.get('serviceCode'), - generateRandomString(10), - true - )); - } - return shippingOptions; - } else { - String errorMessage = 'We failed to calculate shipping options for your cart.'; - if(response.getStatusCode() == 404) { - errorMessage = '404. You must create a sample application or add your own service which returns a valid response'; - } - - // Create a CVO with the Error - CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.SHIPPING, - CartExtension.CartValidationOutputLevelEnum.ERROR - ); - cvo.setMessage(errorMessage); - cartValidationOutputCollection.add(cvo); - return null; - } - } - - // Structure to store the shipping options retrieved from external service. - Class ShippingOptionsAndRatesFromExternalService { - private String name; - private String provider; - private Decimal rate; - private Decimal otherCost; - private String serviceName; - private String carrier; - private String classOfService; - private String referenceNumber; - private Boolean isActive; - - public ShippingOptionsAndRatesFromExternalService() { - name = ''; - provider = ''; - rate = 0.0; - serviceName = ''; - otherCost = 0.0; - carrier = ''; - classOfService = ''; - referenceNumber = ''; - isActive = true; - } - - public ShippingOptionsAndRatesFromExternalService(String someName, String someProvider, Decimal someRate, Decimal someOtherCost, String someServiceName, - String someCarrier, String someClassOfService, String someReferenceNumber, Boolean someIsActive) { - name = someName; - provider = someProvider; - rate = someRate; - otherCost = someOtherCost; - serviceName = someServiceName; - carrier = someCarrier; - classOfService = someClassOfService; - referenceNumber = someReferenceNumber; - isActive = someIsActive; - } - - public String getProvider() { return provider; } - public Decimal getRate() { return rate; } - public Decimal getOtherCost() { return otherCost; } - public String getServiceName() { return serviceName; } - public String getName() { return name; } - public String getCarrier() { return carrier; } - public String getClassOfService() { return classOfService; } - public String getReferenceNumber() { return referenceNumber; } - public Boolean isActive() { return isActive; } - } - - - private void populateCartDeliveryGroupMethodWithShippingOptions( - List shippingOptions, - CartExtension.CartDeliveryGroupMethodList cartDeliveryGroupMethodCollection, - String shippingProduct, - CartExtension.CartValidationOutputList cartValidationOutputCollection - ) { - for (ShippingOptionsAndRatesFromExternalService shippingOption : shippingOptions) { - String carrier = shippingOption.serviceName; - String classOfService = shippingOption.provider; - // Create a CartDeliveryGroupMethod for every shipping option returned from the external - // service - CartExtension.CartDeliveryGroupMethod cartDeliveryGroupMethod = new CartExtension.CartDeliveryGroupMethod( - shippingOption.getName(), - shippingOption.getRate(), - shippingProduct - ); - cartDeliveryGroupMethod.setExternalProvider(shippingOption.getProvider()); - cartDeliveryGroupMethod.setCarrier(shippingOption.getCarrier()); - cartDeliveryGroupMethod.setClassOfService(shippingOption.getClassOfService()); - cartDeliveryGroupMethod.setIsActive(shippingOption.isActive()); - cartDeliveryGroupMethod.setReferenceNumber(shippingOption.getReferenceNumber()); - cartDeliveryGroupMethodCollection.add(cartDeliveryGroupMethod); } } } From 2af7c33c28112b5b43138df3de6b6261de048f63 Mon Sep 17 00:00:00 2001 From: alakhmani Date: Wed, 14 Feb 2024 13:08:36 -0800 Subject: [PATCH 04/69] updated test for delivery estimation fields --- .../ShippingCartCalculatorSampleTest.cls | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSampleTest.cls b/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSampleTest.cls index ca87998..d7e3fa0 100644 --- a/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSampleTest.cls +++ b/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSampleTest.cls @@ -31,6 +31,7 @@ global with sharing class ShippingCartCalculatorSampleTest { static void testShippingMethodsAreCreated() { // Arrange CartExtension.Cart cart = CartExtension.CartTestUtil.createCart(); + getDefaultShippingChargeProduct2Id(); // Act Test.startTest(); @@ -55,10 +56,37 @@ global with sharing class ShippingCartCalculatorSampleTest { System.assertEquals('Ground Shipping', deliveryMethod01.getName()); System.assertEquals('USPS', deliveryMethod01.getCarrier()); System.assertEquals('Ground Shipping', deliveryMethod01.getClassOfService()); + System.assertEquals(1, deliveryMethod01.getTransitTimeMin()); + System.assertEquals(3, deliveryMethod01.getTransitTimeMax()); + System.assertEquals('D', deliveryMethod01.getTransitTimeUnit()); + System.assertEquals(1, deliveryMethod01.getProcessTime()); + System.assertEquals('W', deliveryMethod01.getProcessTimeUnit()); CartExtension.CartDeliveryGroupMethod deliveryMethod02 = deliveryMethods.get(1); System.assertEquals(15.99, deliveryMethod02.getShippingFee()); System.assertEquals('Next Day Air', deliveryMethod02.getName()); System.assertEquals('UPS', deliveryMethod02.getCarrier()); System.assertEquals('Next Day Air', deliveryMethod02.getClassOfService()); + System.assertEquals(1, deliveryMethod02.getTransitTimeMin()); + System.assertEquals(4, deliveryMethod02.getTransitTimeMax()); + System.assertEquals('D', deliveryMethod02.getTransitTimeUnit()); + System.assertEquals(1, deliveryMethod02.getProcessTime()); + System.assertEquals('D', deliveryMethod02.getProcessTimeUnit()); } + private Id getDefaultShippingChargeProduct2Id() { + // In this example we will name the product representing shipping charges 'Shipping Charge'. + // Check to see if a Product2 with that name already exists. + // If it doesn't exist, create one. + String shippingChargeProduct2Name = 'Shipping Charge'; + List shippingChargeProducts = [SELECT Id FROM Product2 WHERE Name = :shippingChargeProduct2Name]; + if (shippingChargeProducts.isEmpty()) { + Product2 shippingChargeProduct = new Product2( + isActive = true, + Name = shippingChargeProduct2Name + ); + insert(shippingChargeProduct); + return shippingChargeProduct.Id; + } else { + return shippingChargeProducts[0].Id; + } + } } From 77dc0511e149b6aadfd28bff8b354da8a3ef1cee Mon Sep 17 00:00:00 2001 From: alakhmani Date: Wed, 14 Feb 2024 16:02:40 -0800 Subject: [PATCH 05/69] updated comment --- .../calculator/classes/ShippingCartCalculatorSampleTest.cls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSampleTest.cls b/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSampleTest.cls index d7e3fa0..51e07f5 100644 --- a/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSampleTest.cls +++ b/commerce/domain/shipping/cart/calculator/classes/ShippingCartCalculatorSampleTest.cls @@ -73,8 +73,8 @@ global with sharing class ShippingCartCalculatorSampleTest { System.assertEquals('D', deliveryMethod02.getProcessTimeUnit()); } private Id getDefaultShippingChargeProduct2Id() { - // In this example we will name the product representing shipping charges 'Shipping Charge'. - // Check to see if a Product2 with that name already exists. + + // Check to see if a Product2 with name 'Shipping Charge' already exists. // If it doesn't exist, create one. String shippingChargeProduct2Name = 'Shipping Charge'; List shippingChargeProducts = [SELECT Id FROM Product2 WHERE Name = :shippingChargeProduct2Name]; From 1bcc64ad86659f700abc317852a67dba82ebd7d7 Mon Sep 17 00:00:00 2001 From: patricia-bagzai Date: Fri, 16 Feb 2024 14:27:19 -0500 Subject: [PATCH 06/69] adding test for PromotionCalculatorSample class --- .../classes/PromotionCalculatorSample.cls | 8 +- .../classes/PromotionCalculatorSampleTest.cls | 97 +++++++++++++++++++ 2 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls diff --git a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls index 4f20c07..56fa53c 100644 --- a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls +++ b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls @@ -74,7 +74,9 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot while(ciIter.hasNext()) { CartExtension.CartItem ci = ciIter.next(); - Decimal promotionAdjustment = (ci.getSalesPrice() * (pctDiscount/100) * ci.getQuantity()); + Decimal totalLineAmount = (ci.getTotalLineAmount() == null) ? + (ci.getSalesPrice() * ci.getQuantity()) : ci.getTotalLineAmount(); + Decimal promotionAdjustment = totalLineAmount * (pctDiscount/100); promotionAdjustment = promotionAdjustment.setScale(2,System.RoundingMode.HALF_DOWN); // Currency precision rounding CartExtension.CartItemPriceAdjustment cia = new CartExtension.CartItemPriceAdjustment(cartextension.CartAdjustmentTargetTypeEnum.ITEM, // AdjustmentTargetType @@ -87,7 +89,7 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot cia.setAdjustmentAmountScope(cartextension.AdjustmentAmountScopeEnum.TOTAL); cia.setDescription('PromotionCalculator'); ci.getCartItemPriceAdjustments().add(cia); - + // Populate TotalPromoAdjustmentAmount for cart-item & update totals based on promotion adjustment ci.setTotalPromoAdjustmentAmount(promotionAdjustment); ci.setTotalAdjustmentAmount(promotionAdjustment); @@ -95,4 +97,4 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot } } -} \ No newline at end of file +} diff --git a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls new file mode 100644 index 0000000..21401df --- /dev/null +++ b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls @@ -0,0 +1,97 @@ +/** + * @description Sample unit test for PromotionCalculatorSample. + */ +@IsTest +global class PromotionCalculatorSampleTest { + private static final String CART_NAME = 'My Cart'; + private static final String ACCOUNT_NAME = 'My Account'; + private static final String WEBSTORE_NAME = 'My WebStore'; + private static final String DELIVERYGROUP_NAME = 'Default Delivery Group'; + private static final String CART_ITEM1_NAME = 'My Cart Item 1'; + private static final String PRODUCT_NAME = '01tSB000000I0knYAC'; + private static final Decimal SALES_PRICE = 80.00; + private static final Decimal DISCOUNT_VALUE = -5.0; + private static final Decimal TOTAL_ADJUSTMENT_AMOUNT = -4.0; + + private static final PromotionCalculatorSample promotionCalculator = new PromotionCalculatorSample(); + + /** + * @description Verify that Promotion is correctly applied on cart item. + */ + @IsTest + public static void testPromotionAndPriceAdjustments() { + // Arrange + // Create a Cart with CHECKOUT status + Id cartId = createCartWithSpecifiedStatus(CartExtension.CartStatusEnum.CHECKOUT); + + // Associate an item to the Cart and load the cart + CartExtension.Cart cart = addItemToCart(cartId); + + // Act + Test.startTest(); + promotionCalculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); + Test.stopTest(); + + // Assert + // Verify that we have 1 CartItem + Assert.areEqual(1, cart.getCartItems().size()); + + // Verify that the CartItem has 1 price adjustment with correct adjustment type and value + Assert.areEqual(1, cart.getCartItems().get(0).getCartItemPriceAdjustments().size()); + Assert.areEqual(Cartextension.AdjustmentTypeEnum.ADJUSTMENT_PERCENTAGE, cart.getCartItems().get(0).getCartItemPriceAdjustments().get(0).getAdjustmentType()); + Assert.areEqual(DISCOUNT_VALUE, cart.getCartItems().get(0).getCartItemPriceAdjustments().get(0).getAdjustmentValue()); + Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(0).getCartItemPriceAdjustments().get(0).getTotalAmount()); + + // Verify CartItem adjustment and total price + Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(0).getTotalPromoAdjustmentAmount()); + Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(0).getTotalAdjustmentAmount()); + Assert.areEqual((SALES_PRICE - TOTAL_ADJUSTMENT_AMOUNT), cart.getCartItems().get(0).getTotalPriceAfterAllAdjustments()); + } + + /** + * @description Create a WebCart with the specific status. + * @param cartStatus Status of the Cart + * + * @return ID of the WebCart + */ + private static ID createCartWithSpecifiedStatus(CartExtension.CartStatusEnum cartStatus) { + Account account = new Account(Name = ACCOUNT_NAME); + insert account; + + WebStore webStore = new WebStore(Name = WEBSTORE_NAME); + insert webStore; + + WebCart webCart = new WebCart( + Name = CART_NAME, + WebStoreId = webStore.Id, + AccountId = account.Id, + Status = cartStatus.name()); + insert webCart; + + return webCart.Id; + } + + /** + * @description Add an item to the specified Cart. + * @param cartId ID of the WebCart for which we need to add three items + * + * @return Cart + */ + private static CartExtension.Cart addItemToCart(ID cartId) { + CartDeliveryGroup deliveryGroup = new CartDeliveryGroup(Name = DELIVERYGROUP_NAME, CartId = cartId); + insert deliveryGroup; + + CartItem cartItem1 = new CartItem( + Name = CART_ITEM1_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 1, + Product2Id = PRODUCT_NAME, + SalesPrice = SALES_PRICE, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem1; + + // Return Cart + return CartExtension.CartTestUtil.getCart(cartId); + } +} From 8b1d07a78f1a0331d9b713e9f334d22ab1ca39c6 Mon Sep 17 00:00:00 2001 From: patricia-bagzai-sf <157054439+patricia-bagzai-sf@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:24:37 -0500 Subject: [PATCH 07/69] Update variable name --- .../cart/calculator/classes/PromotionCalculatorSampleTest.cls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls index 21401df..f496c85 100644 --- a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls +++ b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls @@ -8,7 +8,7 @@ global class PromotionCalculatorSampleTest { private static final String WEBSTORE_NAME = 'My WebStore'; private static final String DELIVERYGROUP_NAME = 'Default Delivery Group'; private static final String CART_ITEM1_NAME = 'My Cart Item 1'; - private static final String PRODUCT_NAME = '01tSB000000I0knYAC'; + private static final String PRODUCT_ID = '01tSB000000I0knYAC'; private static final Decimal SALES_PRICE = 80.00; private static final Decimal DISCOUNT_VALUE = -5.0; private static final Decimal TOTAL_ADJUSTMENT_AMOUNT = -4.0; @@ -86,7 +86,7 @@ global class PromotionCalculatorSampleTest { CartId = cartId, CartDeliveryGroupId = deliveryGroup.Id, Quantity = 1, - Product2Id = PRODUCT_NAME, + Product2Id = PRODUCT_ID, SalesPrice = SALES_PRICE, Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); insert cartItem1; From 8e06355416e5f667ff61bac853c49089b773728a Mon Sep 17 00:00:00 2001 From: bsrilok Date: Mon, 18 Mar 2024 10:03:09 +0530 Subject: [PATCH 08/69] [Tax calculator] replaced for loops with iterators --- .../classes/TaxCartCalculatorSample.cls | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index bd0a712..12abc61 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -17,8 +17,9 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { // Clean up CVO based on tax. When new tax calculator request comes, we need to clean up // previous CVOs as they have been previously handled by the Cart Calculate API. CartExtension.CartValidationOutputList cartValidationOutputCollection = cart.getCartValidationOutputs(); - for (Integer i = (cartValidationOutputCollection.size() - 1); i >= 0; i--) { - CartExtension.CartValidationOutput cvo = cartValidationOutputCollection.get(i); + Iterator cartValidationOutputCollectionIterator = cartValidationOutputCollection.iterator(); + while (cartValidationOutputCollectionIterator.hasNext()) { + CartExtension.CartValidationOutput cvo = cartValidationOutputCollectionIterator.next(); if (cvo.getType() == CartExtension.CartValidationOutputTypeEnum.TAXES) { cartValidationOutputCollection.remove(cvo); } @@ -34,13 +35,18 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { // The cartItemCollection contains both products and shipping cart items. Map cartItemById = new Map(); Map shippingItemById = new Map(); - for (Integer i = (cartItemCollection.size() - 1); i >= 0; i--) { - if (cartItemCollection.get(i).getType() == CartExtension.SalesItemTypeEnum.PRODUCT) { - cartItemById.put(cartItemCollection.get(i).getId(), cartItemCollection.get(i)); - } else if (cartItemCollection.get(i).getType() == CartExtension.SalesItemTypeEnum.CHARGE) { + + Iterator cartItemCollectionIterator = cartItemCollection.iterator(); + + while (cartItemCollectionIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); + + if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT) { + cartItemById.put(cartItem.getId(), cartItem); + } else if (cartItem.getType() == CartExtension.SalesItemTypeEnum.CHARGE) { // Shipping cart items are uniquely identified using delivery group id. - CartExtension.CartDeliveryGroup deliveryGroup = cartItemCollection.get(i).getCartDeliveryGroup(); - shippingItemById.put(deliveryGroup.getId(), cartItemCollection.get(i)); + CartExtension.CartDeliveryGroup deliveryGroup = cartItem.getCartDeliveryGroup(); + shippingItemById.put(deliveryGroup.getId(), cartItem); } } @@ -115,9 +121,9 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { if (cartItem.getCartTaxes().size() > 0) { cartItem.getCartTaxes().remove(cartItem.getCartTaxes().get(0)); } - for (Integer i = (cartItem.getCartItemPriceAdjustments().size() - 1); i >= 0; i--) { - CartExtension.CartTaxList cipaTaxes = cartItem.getCartItemPriceAdjustments() - .get(i) + Iterator cartItemPriceAdjustmentsIterator = cartItem.getCartItemPriceAdjustments().iterator(); + while(cartItemPriceAdjustmentsIterator.hasNext()) { + CartExtension.CartTaxList cipaTaxes = cartItemPriceAdjustmentsIterator.next() .getCartTaxes(); if (cipaTaxes.size() > 0) { cipaTaxes.remove(cipaTaxes.get(0)); @@ -219,15 +225,16 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { null ? new List() : taxesFromExternalService.getItemizedPromotionTaxAmounts(); - - for (Integer i = (cartItemDto.getCartItemPriceAdjustments().size() - 1); i >= 0; i--) { - CartExtension.CartTaxList cartTaxes = cartItemDto.getCartItemPriceAdjustments() - .get(i) + Iterator cartItemPriceAdjustmentsIterator = cartItemDto.getCartItemPriceAdjustments().iterator(); + while (cartItemPriceAdjustmentsIterator.hasNext()) { + CartExtension.CartTaxList cartTaxes = cartItemPriceAdjustmentsIterator.next() .getCartTaxes(); - for (Integer j = (cartTaxes.size() - 1); j >= 0; j--) { + Iterator cartTaxesIterator = cartTaxes.iterator(); + while (cartTaxesIterator.hasNext()) { + CartExtension.CartTax cartTax = cartTaxesIterator.next(); Boolean changedAdjTax = false; for (Integer k = (ajustments.size() - 1); k >= 0; k--) { - if (cartTaxes.get(j).getAmount() == ajustments.get(k).getAmount()) + if (cartTax.getAmount() == ajustments.get(k).getAmount()) changedAdjTax = true; } if (changedAdjTax == false) @@ -242,9 +249,11 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { CartExtension.cartItemPriceAdjustmentList cipaList, String id ) { - for (Integer i = (cipaList.size() - 1); i >= 0; i--) { - if (String.valueOf(cipaList.get(i).getId()) == id) - return cipaList.get(i); + Iterator cipaIterator = cipaList.iterator(); + while (cipaIterator.hasNext()) { + CartExtension.CartItemPriceAdjustment cipa = cipaIterator.next(); + if (String.valueOf(cipa.getId()) == id) + return cipa; } return null; } @@ -365,10 +374,10 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { Double tierAdjustmentTax = (tierAdjustment!=null ? tierAdjustment : 0.00) * multiplier; CartExtension.CartItemPriceAdjustmentList itemizedPromotions = cartItem.getCartItemPriceAdjustments(); - + Iterator itemizedPromotionsIterator = itemizedPromotions.iterator(); String itemizedPromotionTaxResp = '['; - for(Integer i=0; i Date: Mon, 18 Mar 2024 11:32:07 +0530 Subject: [PATCH 09/69] Addressed comments --- .../tax/cart/calculator/classes/TaxCartCalculatorSample.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index 12abc61..f775a11 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -374,7 +374,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { Double tierAdjustmentTax = (tierAdjustment!=null ? tierAdjustment : 0.00) * multiplier; CartExtension.CartItemPriceAdjustmentList itemizedPromotions = cartItem.getCartItemPriceAdjustments(); - Iterator itemizedPromotionsIterator = itemizedPromotions.iterator(); + Iterator itemizedPromotionsIterator = itemizedPromotions.iterator(); String itemizedPromotionTaxResp = '['; while (itemizedPromotionsIterator.hasNext()) { CartExtension.CartItemPriceAdjustment itemAdj = itemizedPromotionsIterator.next(); From 9b25b3a7468702fe21d671bbbbda7926a24db59c Mon Sep 17 00:00:00 2001 From: smahbubani Date: Mon, 18 Mar 2024 19:45:53 -0400 Subject: [PATCH 10/69] code changes --- .../classes/CartCalculateSample.cls | 15 ++++++++----- .../classes/CartCalculateSampleUnitTest.cls | 21 ++++++++++++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSample.cls b/commerce/domain/orchestrators/classes/CartCalculateSample.cls index 56e292d..0d5073f 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSample.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSample.cls @@ -1,8 +1,8 @@ /** * @description This is a sample orchestrator that calls the inventory, pricing, promotions, shipping and tax calculators. * This class must extend CartExtension.CartCalculate and must be linked to the orchestrator extension point (Commerce_Domain_Cart_Calculate). -* Calculates pricing and promotions for operations: Add product to cart, remove product from cart, edit cart item quantity. -* Calculates promotions for operations: add coupon to cart, remove coupon from cart. +* Calculates pricing and promotions for operations: Add to cart, remove from cart, edit cart item. +* Calculates promotions for operations: add coupon, remove coupon. * Calculates pricing, promotions, inventory for start checkout operation (without shipping address available). * Calculates pricing, promotions, inventory, shipping, post shipping and taxes for start checkout operation (with shipping address available). * Calculates shipping, post shipping and taxes for update shipping address operation. @@ -30,12 +30,13 @@ global class CartCalculateSample extends CartExtension.CartCalculate { // Use BuyerActions to decide which calculators to invoke CartExtension.BuyerActions buyerActions = request.getBuyerActions(); + boolean isCouponAppliedInCheckout = isCouponAppliedInCheckout(buyerActions); boolean runPricing = buyerActions.isCheckoutStarted() || buyerActions.isCartItemChanged(); boolean runPromotions = buyerActions.isCheckoutStarted() || buyerActions.isCouponChanged() || buyerActions.isCartItemChanged(); boolean runInventory = buyerActions.isCheckoutStarted(); - boolean runShipping = buyerActions.isDeliveryGroupChanged(); - boolean runPostShipping = buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected(); - boolean runTaxes = buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected(); + boolean runShipping = buyerActions.isDeliveryGroupChanged() || isCouponAppliedInCheckout; + boolean runPostShipping = buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; + boolean runTaxes = buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; // OptionalBuyerActionDetails can be used to optimize the various calculators that are invoked CartExtension.CartCalculateCalculatorRequest calculatorRequest = new CartExtension.CartCalculateCalculatorRequest(cart, request.getOptionalBuyerActionDetails()); @@ -103,4 +104,8 @@ global class CartCalculateSample extends CartExtension.CartCalculate { return false; } + + private Boolean isCouponAppliedInCheckout(CartExtension.BuyerActions buyerActions) { + return buyerActions.isCheckoutStarted() && buyerActions.isCouponChanged(); + } } \ No newline at end of file diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index 9f2523a..15e811a 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -110,11 +110,30 @@ global class CartCalculateSampleUnitTest { } @IsTest - public static void shouldRunPromotionsWhenBuyerRemovesCoupon() { + public static void shouldRunShippingAndTaxesWhenBuyerAddsCouponAtCheckout() { // Arrange Cart CartExtension.Cart cart = arrangeCart(); // Arrange BuyerActions and BuyerActionDetails as if the Buyer added a coupon + CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForApplyCoupon(cart); + CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForApplyCoupon(cart.getCartAdjustmentBases().get(0)); + CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); + + // Act + act(new CartExtension.CartCalculateOrchestratorRequest(cart, buyerActions, optionalBuyerActionDetails)); + + // Assert + assertNoCartValidationOutputs(cart); + assertExpectedCalculations(cart, new List{PROMOTIONS_RECALCULATED, SHIPPING_RECALCULATED, POST_SHIPPING_COMPLETED, TAXES_RECALCULATED}); + assertUnexpectedCalculations(cart, new List{CART_REPRICED, INVENTORY_CHECKED}); + } + + @IsTest + public static void shouldRunPromotionsWhenBuyerRemovesCoupon() { + // Arrange Cart + CartExtension.Cart cart = arrangeCart(); + + // Arrange BuyerActions and BuyerActionDetails as if the Buyer deleted a coupon CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForDeleteCoupon(cart); CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForDeleteCoupon(); CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); From 0f4f9f55f31f23d0c004f1985450fdf00042788f Mon Sep 17 00:00:00 2001 From: smahbubani Date: Mon, 18 Mar 2024 19:56:12 -0400 Subject: [PATCH 11/69] update unit test --- .../classes/CartCalculateSampleUnitTest.cls | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index 15e811a..a5fb14d 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -114,8 +114,8 @@ global class CartCalculateSampleUnitTest { // Arrange Cart CartExtension.Cart cart = arrangeCart(); - // Arrange BuyerActions and BuyerActionDetails as if the Buyer added a coupon - CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForApplyCoupon(cart); + // Arrange BuyerActions and BuyerActionDetails as if the Buyer added a coupon at checkout + CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForApplyCouponAtCheckout(cart); CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForApplyCoupon(cart.getCartAdjustmentBases().get(0)); CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); @@ -376,6 +376,10 @@ global class CartCalculateSampleUnitTest { return getCouponChangedBuyerActions(cart); } + private static CartExtension.BuyerActionsMock getBuyerActionsForApplyCouponAtCheckout(CartExtension.Cart cart) { + return getCouponChangedAtCheckoutBuyerActions(cart); + } + private static CartExtension.BuyerActionDetails getBuyerActionDetailsForApplyCoupon(CartExtension.CartAdjustmentBasis cartAdjustmentBasis) { CartExtension.CouponChange couponChange = new CartExtension.CouponChange.Builder() .withChangedAdjustmentBasis(CartExtension.OptionalCartAdjustmentBasis.of(cartAdjustmentBasis)) @@ -481,4 +485,11 @@ global class CartCalculateSampleUnitTest { buyerActions.setCouponChanged(True); return buyerActions; } + + private static CartExtension.BuyerActionsMock getCouponChangedAtCheckoutBuyerActions(CartExtension.Cart cart) { + CartExtension.BuyerActionsMock buyerActions = new CartExtension.BuyerActionsMock(cart); + buyerActions.setCouponChanged(True); + buyerActions.setCheckoutStarted(True); + return buyerActions; + } } \ No newline at end of file From 114305e62e4478b85ee465ec57322ed3d53702dd Mon Sep 17 00:00:00 2001 From: smahbubani Date: Mon, 18 Mar 2024 19:59:26 -0400 Subject: [PATCH 12/69] revert comment changes --- commerce/domain/orchestrators/classes/CartCalculateSample.cls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSample.cls b/commerce/domain/orchestrators/classes/CartCalculateSample.cls index 0d5073f..80d71b8 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSample.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSample.cls @@ -1,8 +1,8 @@ /** * @description This is a sample orchestrator that calls the inventory, pricing, promotions, shipping and tax calculators. * This class must extend CartExtension.CartCalculate and must be linked to the orchestrator extension point (Commerce_Domain_Cart_Calculate). -* Calculates pricing and promotions for operations: Add to cart, remove from cart, edit cart item. -* Calculates promotions for operations: add coupon, remove coupon. +* Calculates pricing and promotions for operations: Add product to cart, remove product from cart, edit cart item quantity. +* Calculates promotions for operations: add coupon to cart, remove coupon from cart. * Calculates pricing, promotions, inventory for start checkout operation (without shipping address available). * Calculates pricing, promotions, inventory, shipping, post shipping and taxes for start checkout operation (with shipping address available). * Calculates shipping, post shipping and taxes for update shipping address operation. From 5b7b3a9ffcbedcdf600549fc403279f3d570a03d Mon Sep 17 00:00:00 2001 From: smahbubani Date: Mon, 18 Mar 2024 20:03:42 -0400 Subject: [PATCH 13/69] update unit test --- .../orchestrators/classes/CartCalculateSampleUnitTest.cls | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index a5fb14d..7e066d4 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -124,8 +124,7 @@ global class CartCalculateSampleUnitTest { // Assert assertNoCartValidationOutputs(cart); - assertExpectedCalculations(cart, new List{PROMOTIONS_RECALCULATED, SHIPPING_RECALCULATED, POST_SHIPPING_COMPLETED, TAXES_RECALCULATED}); - assertUnexpectedCalculations(cart, new List{CART_REPRICED, INVENTORY_CHECKED}); + assertExpectedCalculations(cart, new List{PROMOTIONS_RECALCULATED, SHIPPING_RECALCULATED, POST_SHIPPING_COMPLETED, TAXES_RECALCULATED, CART_REPRICED, INVENTORY_CHECKED}); } @IsTest From baea2c2f6c2c744dbffe1ba16623485103b60ce1 Mon Sep 17 00:00:00 2001 From: smahbubani Date: Wed, 20 Mar 2024 14:24:48 -0400 Subject: [PATCH 14/69] code changes --- .../orchestrators/classes/CartCalculateSample.cls | 9 +++++---- .../classes/CartCalculateSampleUnitTest.cls | 10 ++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSample.cls b/commerce/domain/orchestrators/classes/CartCalculateSample.cls index 80d71b8..a0c0f17 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSample.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSample.cls @@ -30,7 +30,7 @@ global class CartCalculateSample extends CartExtension.CartCalculate { // Use BuyerActions to decide which calculators to invoke CartExtension.BuyerActions buyerActions = request.getBuyerActions(); - boolean isCouponAppliedInCheckout = isCouponAppliedInCheckout(buyerActions); + boolean isCouponAppliedInCheckout = isCouponAppliedInCheckout(buyerActions, cart); boolean runPricing = buyerActions.isCheckoutStarted() || buyerActions.isCartItemChanged(); boolean runPromotions = buyerActions.isCheckoutStarted() || buyerActions.isCouponChanged() || buyerActions.isCartItemChanged(); boolean runInventory = buyerActions.isCheckoutStarted(); @@ -105,7 +105,8 @@ global class CartCalculateSample extends CartExtension.CartCalculate { return false; } - private Boolean isCouponAppliedInCheckout(CartExtension.BuyerActions buyerActions) { - return buyerActions.isCheckoutStarted() && buyerActions.isCouponChanged(); - } + private Boolean isCouponAppliedInCheckout(CartExtension.BuyerActions buyerActions, CartExtension.Cart cart) { + return (cart.getStatus() == CartExtension.CartStatusEnum.CHECKOUT || buyerActions.isCheckoutStarted()) && + buyerActions.isCouponChanged(); + } } \ No newline at end of file diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index 7e066d4..3f863ac 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -1,3 +1,9 @@ +/* + * Copyright 2023 salesforce.com, inc. + * All Rights Reserved + * Company Confidential + */ + /** * @description A Sample unit test for CartCalculateSample. */ @@ -110,11 +116,11 @@ global class CartCalculateSampleUnitTest { } @IsTest - public static void shouldRunShippingAndTaxesWhenBuyerAddsCouponAtCheckout() { + public static void shouldRunAllCalculatorsWhenBuyerAddsCouponAtCheckout() { // Arrange Cart CartExtension.Cart cart = arrangeCart(); - // Arrange BuyerActions and BuyerActionDetails as if the Buyer added a coupon at checkout + // Arrange BuyerActions and BuyerActionDetails as if the Buyer deleted a coupon at checkout CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForApplyCouponAtCheckout(cart); CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForApplyCoupon(cart.getCartAdjustmentBases().get(0)); CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); From 5f354b18eeef04154de75e4ccf1e47b87f5ed46e Mon Sep 17 00:00:00 2001 From: smahbubani Date: Wed, 20 Mar 2024 17:04:12 -0400 Subject: [PATCH 15/69] modify utests --- .../classes/CartCalculateSampleUnitTest.cls | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index 3f863ac..e0dadc1 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -118,9 +118,9 @@ global class CartCalculateSampleUnitTest { @IsTest public static void shouldRunAllCalculatorsWhenBuyerAddsCouponAtCheckout() { // Arrange Cart - CartExtension.Cart cart = arrangeCart(); + CartExtension.Cart cart = arrangeCart('Checkout'); - // Arrange BuyerActions and BuyerActionDetails as if the Buyer deleted a coupon at checkout + // Arrange BuyerActions and BuyerActionDetails as if the Buyer added a coupon at checkout CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForApplyCouponAtCheckout(cart); CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForApplyCoupon(cart.getCartAdjustmentBases().get(0)); CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); @@ -130,7 +130,10 @@ global class CartCalculateSampleUnitTest { // Assert assertNoCartValidationOutputs(cart); - assertExpectedCalculations(cart, new List{PROMOTIONS_RECALCULATED, SHIPPING_RECALCULATED, POST_SHIPPING_COMPLETED, TAXES_RECALCULATED, CART_REPRICED, INVENTORY_CHECKED}); + assertExpectedCalculations(cart, new List{PROMOTIONS_RECALCULATED, SHIPPING_RECALCULATED, POST_SHIPPING_COMPLETED, TAXES_RECALCULATED}); + assertUnexpectedCalculations(cart, new List{CART_REPRICED, INVENTORY_CHECKED}); + + } @IsTest @@ -249,6 +252,28 @@ global class CartCalculateSampleUnitTest { return CartExtension.CartTestUtil.getCart(testCart.Id); } + private static CartExtension.Cart arrangeCart(String cartStatus) { + Account testAccount = new Account(Name='My Account'); + insert testAccount; + + WebStore testWebStore = new WebStore(Name='My WebStore'); + insert testWebStore; + + WebCart testCart = new WebCart(Name='My Cart', WebStoreId=testWebStore.Id, AccountId=testAccount.Id, Status=cartStatus); + insert testCart; + + CartDeliveryGroup testDeliveryGroup = new CartDeliveryGroup(Name='My Delivery Group', CartId=testCart.Id); + insert testDeliveryGroup; + + CartItem testCartItem = new CartItem(Name='My Cart Item', CartId=testCart.Id, CartDeliveryGroupId=testDeliveryGroup.Id); + insert testCartItem; + + WebCartAdjustmentBasis testCartAdjustmentBasis = new WebCartAdjustmentBasis(Name='My Coupon', WebCartId=testCart.Id); + insert testCartAdjustmentBasis; + + return CartExtension.CartTestUtil.getCart(testCart.Id); + } + private static void act(CartExtension.CartCalculateOrchestratorRequest request) { Test.startTest(); cartCalculateSample.calculate(request); @@ -494,7 +519,6 @@ global class CartCalculateSampleUnitTest { private static CartExtension.BuyerActionsMock getCouponChangedAtCheckoutBuyerActions(CartExtension.Cart cart) { CartExtension.BuyerActionsMock buyerActions = new CartExtension.BuyerActionsMock(cart); buyerActions.setCouponChanged(True); - buyerActions.setCheckoutStarted(True); return buyerActions; } } \ No newline at end of file From cc8ed61ae11faede0e604305670af28c1f41788e Mon Sep 17 00:00:00 2001 From: bsrilok Date: Thu, 21 Mar 2024 16:48:46 +0530 Subject: [PATCH 16/69] Addressed comments --- .idea/.gitignore | 12 ++++ .idea/codeStyles/Project.xml | 57 +++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 ++ .idea/commerce-extensibility.iml | 9 +++ .idea/misc.xml | 6 ++ .idea/modules.xml | 8 +++ .idea/vcs.xml | 6 ++ .../classes/TaxCartCalculatorSample.cls | 11 +++- 8 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/commerce-extensibility.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..92bce6a --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,12 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Core Dev Booster ignored files +/coreModuleDependants.csv +/compile.flag +/.mavenCleaned diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..eb5ba38 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/commerce-extensibility.iml b/.idea/commerce-extensibility.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/commerce-extensibility.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..31e3ed1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c42f786 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index f775a11..44cd084 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -18,12 +18,17 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { // previous CVOs as they have been previously handled by the Cart Calculate API. CartExtension.CartValidationOutputList cartValidationOutputCollection = cart.getCartValidationOutputs(); Iterator cartValidationOutputCollectionIterator = cartValidationOutputCollection.iterator(); + List cvoToRemove = new List(); while (cartValidationOutputCollectionIterator.hasNext()) { CartExtension.CartValidationOutput cvo = cartValidationOutputCollectionIterator.next(); if (cvo.getType() == CartExtension.CartValidationOutputTypeEnum.TAXES) { - cartValidationOutputCollection.remove(cvo); + cvoToRemove.add(cvo); } } + // Remove cartValidationOutputs from cartValidationOutputCollection + for(CartExtension.CartValidationOutput cvo : cvoToRemove){ + cartValidationOutputCollection.remove(cvo); + } // There should be one delivery group per cart. CartExtension.CartDeliveryGroupList cartDeliveryGroups = cart.getCartDeliveryGroups(); @@ -42,7 +47,9 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT) { - cartItemById.put(cartItem.getId(), cartItem); + if (cartItem.getId() != null) { + cartItemById.put(cartItem.getId(), cartItem); + } } else if (cartItem.getType() == CartExtension.SalesItemTypeEnum.CHARGE) { // Shipping cart items are uniquely identified using delivery group id. CartExtension.CartDeliveryGroup deliveryGroup = cartItem.getCartDeliveryGroup(); From 244f3a0503ba4a43e1d8990f344245a4a63d5b79 Mon Sep 17 00:00:00 2001 From: bsrilok Date: Thu, 21 Mar 2024 16:49:49 +0530 Subject: [PATCH 17/69] Addressed comments --- .idea/.gitignore | 12 ------ .idea/codeStyles/Project.xml | 57 ---------------------------- .idea/codeStyles/codeStyleConfig.xml | 5 --- .idea/commerce-extensibility.iml | 9 ----- .idea/misc.xml | 6 --- .idea/modules.xml | 8 ---- .idea/vcs.xml | 6 --- 7 files changed, 103 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/codeStyles/Project.xml delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/commerce-extensibility.iml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 92bce6a..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Core Dev Booster ignored files -/coreModuleDependants.csv -/compile.flag -/.mavenCleaned diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index eb5ba38..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/commerce-extensibility.iml b/.idea/commerce-extensibility.iml deleted file mode 100644 index d6ebd48..0000000 --- a/.idea/commerce-extensibility.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 31e3ed1..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index c42f786..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 9cf6c107c400caa36acb209fc62b45bf3dacf0fb Mon Sep 17 00:00:00 2001 From: smahbubani Date: Thu, 21 Mar 2024 16:25:59 -0400 Subject: [PATCH 18/69] change method name --- .../orchestrators/classes/CartCalculateSampleUnitTest.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index e0dadc1..ec04140 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -116,7 +116,7 @@ global class CartCalculateSampleUnitTest { } @IsTest - public static void shouldRunAllCalculatorsWhenBuyerAddsCouponAtCheckout() { + public static void shouldRunPromotionsShippingAndTaxesWhenBuyerAddsCouponAtCheckout() { // Arrange Cart CartExtension.Cart cart = arrangeCart('Checkout'); From 61431942736995db893562c5d3da1e7cd7364e4b Mon Sep 17 00:00:00 2001 From: smahbubani Date: Thu, 21 Mar 2024 16:52:46 -0400 Subject: [PATCH 19/69] update orchestrator check --- commerce/domain/orchestrators/classes/CartCalculateSample.cls | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSample.cls b/commerce/domain/orchestrators/classes/CartCalculateSample.cls index a0c0f17..4867f85 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSample.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSample.cls @@ -106,7 +106,6 @@ global class CartCalculateSample extends CartExtension.CartCalculate { } private Boolean isCouponAppliedInCheckout(CartExtension.BuyerActions buyerActions, CartExtension.Cart cart) { - return (cart.getStatus() == CartExtension.CartStatusEnum.CHECKOUT || buyerActions.isCheckoutStarted()) && - buyerActions.isCouponChanged(); + return cart.getStatus() == CartExtension.CartStatusEnum.CHECKOUT && buyerActions.isCouponChanged(); } } \ No newline at end of file From dda947bb1bd2fbbc9435d7fcc4c1bfa2b5b9bfe2 Mon Sep 17 00:00:00 2001 From: smahbubani Date: Thu, 21 Mar 2024 16:54:06 -0400 Subject: [PATCH 20/69] formatting --- commerce/domain/orchestrators/classes/CartCalculateSample.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSample.cls b/commerce/domain/orchestrators/classes/CartCalculateSample.cls index 4867f85..7f10696 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSample.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSample.cls @@ -107,5 +107,5 @@ global class CartCalculateSample extends CartExtension.CartCalculate { private Boolean isCouponAppliedInCheckout(CartExtension.BuyerActions buyerActions, CartExtension.Cart cart) { return cart.getStatus() == CartExtension.CartStatusEnum.CHECKOUT && buyerActions.isCouponChanged(); - } + } } \ No newline at end of file From a7923d989dc3d8869e38a207ff8ed46932d08512 Mon Sep 17 00:00:00 2001 From: smahbubani99 <132001993+smahbubani99@users.noreply.github.com> Date: Thu, 21 Mar 2024 21:34:18 -0400 Subject: [PATCH 21/69] change method name again --- .../orchestrators/classes/CartCalculateSampleUnitTest.cls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index ec04140..61b3b26 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -116,7 +116,7 @@ global class CartCalculateSampleUnitTest { } @IsTest - public static void shouldRunPromotionsShippingAndTaxesWhenBuyerAddsCouponAtCheckout() { + public static void ShouldRunPromotionsAndShippingAndPostShippingAndTaxesWhenBuyerAddsCouponDuringCheckout() { // Arrange Cart CartExtension.Cart cart = arrangeCart('Checkout'); @@ -521,4 +521,4 @@ global class CartCalculateSampleUnitTest { buyerActions.setCouponChanged(True); return buyerActions; } -} \ No newline at end of file +} From f036aff9465de1f07d6a90b9114aef28c0cb075c Mon Sep 17 00:00:00 2001 From: smahbubani99 <132001993+smahbubani99@users.noreply.github.com> Date: Thu, 21 Mar 2024 21:36:34 -0400 Subject: [PATCH 22/69] Update CartCalculateSampleUnitTest.cls --- .../orchestrators/classes/CartCalculateSampleUnitTest.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index 61b3b26..5066400 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -116,7 +116,7 @@ global class CartCalculateSampleUnitTest { } @IsTest - public static void ShouldRunPromotionsAndShippingAndPostShippingAndTaxesWhenBuyerAddsCouponDuringCheckout() { + public static void shouldRunPromotionsAndShippingAndPostShippingAndTaxesWhenBuyerAddsCouponDuringCheckout() { // Arrange Cart CartExtension.Cart cart = arrangeCart('Checkout'); From a6220664d89cd0ffe81e0cda08c752d01a5b1082 Mon Sep 17 00:00:00 2001 From: patricia-bagzai Date: Tue, 26 Mar 2024 09:51:56 -0400 Subject: [PATCH 23/69] updating PromotionCalculatorSample example to evalaute BOGO promotion; updating tests --- .../classes/PromotionCalculatorSample.cls | 102 +++++++++++++----- .../classes/PromotionCalculatorSampleTest.cls | 79 ++++++++++---- 2 files changed, 132 insertions(+), 49 deletions(-) diff --git a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls index 56fa53c..e02cdf7 100644 --- a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls +++ b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls @@ -1,17 +1,22 @@ - /** +/** * @description This sample is for the situations where Promotion Calculation needs to be extended or overridden via the * extension point for the Promotion Calculator. You are expected to refer this and write your own implementation. * This class must extend the CartExtension.PromotionsCartCalculator class to be processed. + * + * In this example cart items are evaluated against a BOGO (Buy X, Get Y) promotion > Buy 5 items of qualifying product X, + * get $2 off a unit of target Y. */ public with sharing class PromotionCalculatorSample extends CartExtension.PromotionsCartCalculator { - // You MUST change this to be a valid promotion id. - public static final String DUMMY_PROMOTION_ID = '0c8xx000000003FAAQ'; + // You MUST change following to be a valid promotion, product ids. + public static final String DUMMY_PROMOTION_ID = '0c8xx00000004JlAAI'; + private static final String QUALIFIER_PRODUCT_ID = '01txx0000006lmmAAA'; + private static final String TARGET_PRODUCT_ID = '01txx0000006lmuAAA'; public virtual override void calculate(CartExtension.CartCalculateCalculatorRequest request) { cartextension.Cart cart = request.getCart(); resetAllAdjustments(cart); - applyAdjustments(cart); + evaluatePromotionForCartItems(cart); } /** @@ -62,39 +67,78 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot } } + /** + * @description Evaluate BOGO promotion (Buy 5 items of qualifying product, get $2 off a unit of target product, + * for cart items and apply adjustments. + * @param cart Holds details about cart + */ + private static void evaluatePromotionForCartItems(CartExtension.Cart cart) { + Integer targetCount = 0; + Integer qualifierCount = 0; + Integer adjustmentCount = 0; + Iterator ciIter = cart.getCartItems().iterator(); + + if(ciIter.hasNext()) { + qualifierCount = getProductCount(cart, QUALIFIER_PRODUCT_ID); + targetCount = getProductCount(cart, TARGET_PRODUCT_ID); + adjustmentCount = qualifierCount / 5; + adjustmentCount = Math.min(adjustmentCount, targetCount); + applyAdjustments(cart, adjustmentCount); + } + } /** - * @description Apply flat 5 percent discount across all cart items + * @description Helper method to get count of given product in cart. * @param cart Holds details about cart + * @param productId Product Id */ - public static void applyAdjustments(CartExtension.Cart cart) { + private static Integer getProductCount(CartExtension.Cart cart, String productId) { + Iterator ciIter = cart.getCartItems().iterator(); + while(ciIter.hasNext()) { + CartExtension.CartItem ci = ciIter.next(); + if (ci.getProduct2Id() == ID.valueOf(productId)) { + return ci.getQuantity().intValue(); + } + } + return 0; + } + + /** + * @description Apply $2 off discount on given number of target units + * @param cart Holds details about cart + * @param adjustmentCount Number of target units the discount applies to + */ + private static void applyAdjustments(CartExtension.Cart cart, Integer adjustmentCount) { - Decimal pctDiscount = -5; + Decimal promotionAdjustment = -2; Iterator ciIter = cart.getCartItems().iterator(); - while(ciIter.hasNext()) { + while(ciIter.hasNext() && adjustmentCount > 0) { CartExtension.CartItem ci = ciIter.next(); - Decimal totalLineAmount = (ci.getTotalLineAmount() == null) ? - (ci.getSalesPrice() * ci.getQuantity()) : ci.getTotalLineAmount(); - Decimal promotionAdjustment = totalLineAmount * (pctDiscount/100); - promotionAdjustment = promotionAdjustment.setScale(2,System.RoundingMode.HALF_DOWN); // Currency precision rounding - CartExtension.CartItemPriceAdjustment cia = new - CartExtension.CartItemPriceAdjustment(cartextension.CartAdjustmentTargetTypeEnum.ITEM, // AdjustmentTargetType - promotionAdjustment, // TotalAmount - cartextension.PriceAdjustmentSourceEnum.PROMOTION, // AdjustmentSource - cartextension.AdjustmentTypeEnum.ADJUSTMENT_PERCENTAGE, // AdjustmentType - pctDiscount, // AdjustmentValue - DUMMY_PROMOTION_ID); // PriceAdjustmentCauseId - cia.setPriority(1); - cia.setAdjustmentAmountScope(cartextension.AdjustmentAmountScopeEnum.TOTAL); - cia.setDescription('PromotionCalculator'); - ci.getCartItemPriceAdjustments().add(cia); - - // Populate TotalPromoAdjustmentAmount for cart-item & update totals based on promotion adjustment - ci.setTotalPromoAdjustmentAmount(promotionAdjustment); - ci.setTotalAdjustmentAmount(promotionAdjustment); - ci.setTotalPriceAfterAllAdjustments(ci.getSalesPrice() - promotionAdjustment); + if(ci.getProduct2Id().equals(TARGET_PRODUCT_ID)) { + Decimal totalPromotionAdjustment = adjustmentCount * promotionAdjustment; + CartExtension.CartItemPriceAdjustment cia = new + CartExtension.CartItemPriceAdjustment(cartextension.CartAdjustmentTargetTypeEnum.ITEM, // AdjustmentTargetType + totalPromotionAdjustment, // TotalAmount + cartextension.PriceAdjustmentSourceEnum.PROMOTION, // AdjustmentSource + cartextension.AdjustmentTypeEnum.ADJUSTMENT_AMOUNT, // AdjustmentType + promotionAdjustment, // AdjustmentValue + DUMMY_PROMOTION_ID); // PriceAdjustmentCauseId + Decimal totalLineAmount = (ci.getTotalLineAmount() == null) ? + (ci.getSalesPrice() * ci.getQuantity()) : ci.getTotalLineAmount(); + cia.setPriority(1); + cia.setAdjustmentAmountScope(cartextension.AdjustmentAmountScopeEnum.TOTAL); + cia.setDescription('PromotionCalculator'); + ci.getCartItemPriceAdjustments().add(cia); + + // Populate TotalPromoAdjustmentAmount for cart-item & update totals based on promotion adjustment + ci.setTotalPromoAdjustmentAmount(totalPromotionAdjustment); + ci.setTotalAdjustmentAmount(totalPromotionAdjustment); + ci.setTotalPriceAfterAllAdjustments(totalLineAmount - totalPromotionAdjustment); + } else { + continue; + } } } -} +} \ No newline at end of file diff --git a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls index f496c85..85e002c 100644 --- a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls +++ b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls @@ -8,24 +8,27 @@ global class PromotionCalculatorSampleTest { private static final String WEBSTORE_NAME = 'My WebStore'; private static final String DELIVERYGROUP_NAME = 'Default Delivery Group'; private static final String CART_ITEM1_NAME = 'My Cart Item 1'; - private static final String PRODUCT_ID = '01tSB000000I0knYAC'; - private static final Decimal SALES_PRICE = 80.00; - private static final Decimal DISCOUNT_VALUE = -5.0; + private static final String CART_ITEM2_NAME = 'My Cart Item 2'; + private static final String TARGET_PRODUCT_ID = '01txx0000006lmuAAA'; + private static final String QUALIFIER_PRODUCT_ID = '01txx0000006lmmAAA'; + private static final Decimal TARGET_SALES_PRICE = 10.00; + private static final Decimal QUALIFIER_SALES_PRICE = 20.00; + private static final Decimal DISCOUNT_VALUE = -2.0; private static final Decimal TOTAL_ADJUSTMENT_AMOUNT = -4.0; private static final PromotionCalculatorSample promotionCalculator = new PromotionCalculatorSample(); /** - * @description Verify that Promotion is correctly applied on cart item. + * @description Verify Promotion is correctly applied on cart item. */ @IsTest - public static void testPromotionAndPriceAdjustments() { + public static void testPromotionAndPriceAdjustments_WithQualifierAndTargetItems() { // Arrange // Create a Cart with CHECKOUT status Id cartId = createCartWithSpecifiedStatus(CartExtension.CartStatusEnum.CHECKOUT); - // Associate an item to the Cart and load the cart - CartExtension.Cart cart = addItemToCart(cartId); + // Associate qualifying & target items to the Cart and load the cart + CartExtension.Cart cart = addItemsToCart(cartId, 10, 3); // Act Test.startTest(); @@ -33,19 +36,45 @@ global class PromotionCalculatorSampleTest { Test.stopTest(); // Assert - // Verify that we have 1 CartItem - Assert.areEqual(1, cart.getCartItems().size()); + // Verify that we have 2 CartItems + Assert.areEqual(2, cart.getCartItems().size()); // Verify that the CartItem has 1 price adjustment with correct adjustment type and value - Assert.areEqual(1, cart.getCartItems().get(0).getCartItemPriceAdjustments().size()); - Assert.areEqual(Cartextension.AdjustmentTypeEnum.ADJUSTMENT_PERCENTAGE, cart.getCartItems().get(0).getCartItemPriceAdjustments().get(0).getAdjustmentType()); - Assert.areEqual(DISCOUNT_VALUE, cart.getCartItems().get(0).getCartItemPriceAdjustments().get(0).getAdjustmentValue()); - Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(0).getCartItemPriceAdjustments().get(0).getTotalAmount()); + Assert.areEqual(1, cart.getCartItems().get(1).getCartItemPriceAdjustments().size()); + Assert.areEqual(Cartextension.AdjustmentTypeEnum.ADJUSTMENT_AMOUNT, cart.getCartItems().get(1).getCartItemPriceAdjustments().get(0).getAdjustmentType()); + Assert.areEqual(DISCOUNT_VALUE, cart.getCartItems().get(1).getCartItemPriceAdjustments().get(0).getAdjustmentValue()); + Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(1).getCartItemPriceAdjustments().get(0).getTotalAmount()); // Verify CartItem adjustment and total price - Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(0).getTotalPromoAdjustmentAmount()); - Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(0).getTotalAdjustmentAmount()); - Assert.areEqual((SALES_PRICE - TOTAL_ADJUSTMENT_AMOUNT), cart.getCartItems().get(0).getTotalPriceAfterAllAdjustments()); + Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(1).getTotalPromoAdjustmentAmount()); + Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(1).getTotalAdjustmentAmount()); + Assert.areEqual(((TARGET_SALES_PRICE * 3) - TOTAL_ADJUSTMENT_AMOUNT), cart.getCartItems().get(1).getTotalPriceAfterAllAdjustments()); + } + + /** + * @description Verify Promotion is not applied when cart does not include enough qualifying or target items. + */ + @IsTest + public static void testPromotionAndPriceAdjustments_WithOutQualifierAndTargetItems() { + // Arrange + // Create a Cart with CHECKOUT status + Id cartId = createCartWithSpecifiedStatus(CartExtension.CartStatusEnum.CHECKOUT); + + // Associate qualifying & target items to the Cart and load the cart + CartExtension.Cart cart = addItemsToCart(cartId, 4, 1); + + // Act + Test.startTest(); + promotionCalculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); + Test.stopTest(); + + // Assert + // Verify that we have 2 CartItems + Assert.areEqual(2, cart.getCartItems().size()); + + // Verify that the CartItem has 0 price adjustments. + Assert.areEqual(0, cart.getCartItems().get(0).getCartItemPriceAdjustments().size()); + Assert.areEqual(0, cart.getCartItems().get(1).getCartItemPriceAdjustments().size()); } /** @@ -77,7 +106,7 @@ global class PromotionCalculatorSampleTest { * * @return Cart */ - private static CartExtension.Cart addItemToCart(ID cartId) { + private static CartExtension.Cart addItemsToCart(ID cartId, Decimal qualifierCount, Decimal targetCount) { CartDeliveryGroup deliveryGroup = new CartDeliveryGroup(Name = DELIVERYGROUP_NAME, CartId = cartId); insert deliveryGroup; @@ -85,12 +114,22 @@ global class PromotionCalculatorSampleTest { Name = CART_ITEM1_NAME, CartId = cartId, CartDeliveryGroupId = deliveryGroup.Id, - Quantity = 1, - Product2Id = PRODUCT_ID, - SalesPrice = SALES_PRICE, + Quantity = qualifierCount, + Product2Id = QUALIFIER_PRODUCT_ID, + SalesPrice = QUALIFIER_SALES_PRICE, Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); insert cartItem1; + CartItem cartItem2 = new CartItem( + Name = CART_ITEM2_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = targetCount, + Product2Id = TARGET_PRODUCT_ID, + SalesPrice = TARGET_SALES_PRICE, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem2; + // Return Cart return CartExtension.CartTestUtil.getCart(cartId); } From aaf5ae41246a210f8629c439309edfd19cd97ac3 Mon Sep 17 00:00:00 2001 From: patricia-bagzai Date: Wed, 27 Mar 2024 11:03:00 -0400 Subject: [PATCH 24/69] addressing PR comments --- .../classes/PromotionCalculatorSample.cls | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls index e02cdf7..83ef3fa 100644 --- a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls +++ b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls @@ -12,6 +12,8 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot public static final String DUMMY_PROMOTION_ID = '0c8xx00000004JlAAI'; private static final String QUALIFIER_PRODUCT_ID = '01txx0000006lmmAAA'; private static final String TARGET_PRODUCT_ID = '01txx0000006lmuAAA'; + private static final Decimal PROMOTION_ADJUSTMENT = -2; + private static final Decimal QUALIFIER_QUANTITY = 5; public virtual override void calculate(CartExtension.CartCalculateCalculatorRequest request) { cartextension.Cart cart = request.getCart(); @@ -37,11 +39,11 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot } // Remove all cart-item level adjustments - Iterator ciIter = cart.getCartItems().iterator(); - while(ciIter.hasNext()) { + Iterator cartItemIterator = cart.getCartItems().iterator(); + while(cartItemIterator.hasNext()) { // For every cart item, cursor through adjustments - CartExtension.CartItem ci = ciIter.next(); + CartExtension.CartItem ci = cartItemIterator.next(); Iterator ciaIter = ci.getCartItemPriceAdjustments().iterator(); List ciaToRemove= new List(); @@ -76,12 +78,13 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot Integer targetCount = 0; Integer qualifierCount = 0; Integer adjustmentCount = 0; - Iterator ciIter = cart.getCartItems().iterator(); + Iterator cartItemIterator = cart.getCartItems().iterator(); - if(ciIter.hasNext()) { + // If cartItems size is greater than 0, get qualifier, target count & apply adjustments + if(cartItemIterator.hasNext()) { qualifierCount = getProductCount(cart, QUALIFIER_PRODUCT_ID); targetCount = getProductCount(cart, TARGET_PRODUCT_ID); - adjustmentCount = qualifierCount / 5; + adjustmentCount = qualifierCount / QUALIFIER_QUANTITY; adjustmentCount = Math.min(adjustmentCount, targetCount); applyAdjustments(cart, adjustmentCount); } @@ -93,9 +96,9 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot * @param productId Product Id */ private static Integer getProductCount(CartExtension.Cart cart, String productId) { - Iterator ciIter = cart.getCartItems().iterator(); - while(ciIter.hasNext()) { - CartExtension.CartItem ci = ciIter.next(); + Iterator cartItemIterator = cart.getCartItems().iterator(); + while(cartItemIterator.hasNext()) { + CartExtension.CartItem ci = cartItemIterator.next(); if (ci.getProduct2Id() == ID.valueOf(productId)) { return ci.getQuantity().intValue(); } @@ -110,19 +113,18 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot */ private static void applyAdjustments(CartExtension.Cart cart, Integer adjustmentCount) { - Decimal promotionAdjustment = -2; - Iterator ciIter = cart.getCartItems().iterator(); - while(ciIter.hasNext() && adjustmentCount > 0) { + Iterator cartItemIterator = cart.getCartItems().iterator(); + while(cartItemIterator.hasNext() && adjustmentCount > 0) { - CartExtension.CartItem ci = ciIter.next(); + CartExtension.CartItem ci = cartItemIterator.next(); if(ci.getProduct2Id().equals(TARGET_PRODUCT_ID)) { - Decimal totalPromotionAdjustment = adjustmentCount * promotionAdjustment; + Decimal totalPromotionAdjustment = adjustmentCount * PROMOTION_ADJUSTMENT; CartExtension.CartItemPriceAdjustment cia = new CartExtension.CartItemPriceAdjustment(cartextension.CartAdjustmentTargetTypeEnum.ITEM, // AdjustmentTargetType totalPromotionAdjustment, // TotalAmount cartextension.PriceAdjustmentSourceEnum.PROMOTION, // AdjustmentSource cartextension.AdjustmentTypeEnum.ADJUSTMENT_AMOUNT, // AdjustmentType - promotionAdjustment, // AdjustmentValue + PROMOTION_ADJUSTMENT, // AdjustmentValue DUMMY_PROMOTION_ID); // PriceAdjustmentCauseId Decimal totalLineAmount = (ci.getTotalLineAmount() == null) ? (ci.getSalesPrice() * ci.getQuantity()) : ci.getTotalLineAmount(); From fc67e347c8a916cafa208e0282bcec26e212cc40 Mon Sep 17 00:00:00 2001 From: bsrilok Date: Thu, 28 Mar 2024 19:30:29 +0530 Subject: [PATCH 25/69] addressed comments --- .../classes/TaxCartCalculatorSample.cls | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index 44cd084..3d16104 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -18,17 +18,12 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { // previous CVOs as they have been previously handled by the Cart Calculate API. CartExtension.CartValidationOutputList cartValidationOutputCollection = cart.getCartValidationOutputs(); Iterator cartValidationOutputCollectionIterator = cartValidationOutputCollection.iterator(); - List cvoToRemove = new List(); while (cartValidationOutputCollectionIterator.hasNext()) { CartExtension.CartValidationOutput cvo = cartValidationOutputCollectionIterator.next(); if (cvo.getType() == CartExtension.CartValidationOutputTypeEnum.TAXES) { - cvoToRemove.add(cvo); + cartValidationOutputCollection.remove(cvo); } } - // Remove cartValidationOutputs from cartValidationOutputCollection - for(CartExtension.CartValidationOutput cvo : cvoToRemove){ - cartValidationOutputCollection.remove(cvo); - } // There should be one delivery group per cart. CartExtension.CartDeliveryGroupList cartDeliveryGroups = cart.getCartDeliveryGroups(); @@ -44,12 +39,10 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { Iterator cartItemCollectionIterator = cartItemCollection.iterator(); while (cartItemCollectionIterator.hasNext()) { - CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); + CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT) { - if (cartItem.getId() != null) { - cartItemById.put(cartItem.getId(), cartItem); - } + cartItemById.put(cartItem.getId(), cartItem); } else if (cartItem.getType() == CartExtension.SalesItemTypeEnum.CHARGE) { // Shipping cart items are uniquely identified using delivery group id. CartExtension.CartDeliveryGroup deliveryGroup = cartItem.getCartDeliveryGroup(); @@ -381,7 +374,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { Double tierAdjustmentTax = (tierAdjustment!=null ? tierAdjustment : 0.00) * multiplier; CartExtension.CartItemPriceAdjustmentList itemizedPromotions = cartItem.getCartItemPriceAdjustments(); - Iterator itemizedPromotionsIterator = itemizedPromotions.iterator(); + Iterator itemizedPromotionsIterator = itemizedPromotions.iterator(); String itemizedPromotionTaxResp = '['; while (itemizedPromotionsIterator.hasNext()) { CartExtension.CartItemPriceAdjustment itemAdj = itemizedPromotionsIterator.next(); @@ -524,4 +517,3 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { } } } - From 46773ee849ca85f7c31234bd38dad6e270b035b4 Mon Sep 17 00:00:00 2001 From: patricia-bagzai Date: Thu, 28 Mar 2024 15:02:23 -0400 Subject: [PATCH 26/69] updating logic to leverage buyerActionDetails; updating tests --- .../classes/PromotionCalculatorSample.cls | 40 ++++++++++++++---- .../classes/PromotionCalculatorSampleTest.cls | 41 +++++++++++++++++-- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls index 83ef3fa..0043c8e 100644 --- a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls +++ b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls @@ -12,20 +12,42 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot public static final String DUMMY_PROMOTION_ID = '0c8xx00000004JlAAI'; private static final String QUALIFIER_PRODUCT_ID = '01txx0000006lmmAAA'; private static final String TARGET_PRODUCT_ID = '01txx0000006lmuAAA'; - private static final Decimal PROMOTION_ADJUSTMENT = -2; - private static final Decimal QUALIFIER_QUANTITY = 5; + private static final Integer PROMOTION_ADJUSTMENT = -2; + private static final Integer QUALIFIER_QUANTITY = 5; public virtual override void calculate(CartExtension.CartCalculateCalculatorRequest request) { - cartextension.Cart cart = request.getCart(); - resetAllAdjustments(cart); - evaluatePromotionForCartItems(cart); + validateBuyerActionDetailsAndEvaluatePromotion(request.getCart(), request.getOptionalBuyerActionDetails()); + } + + /** + * @description Evaluate promotion for the cart items if qualifier and/or target products part of changed elements in cart. + * @param cart Holds details about cart + * @param optionalBuyerActionDetails The latest set of changes applied to the Cart by the Buyer + */ + private void validateBuyerActionDetailsAndEvaluatePromotion(cartextension.Cart cart, cartextension.OptionalBuyerActionDetails optionalBuyerActionDetails) { + if (optionalBuyerActionDetails.isPresent() || !optionalBuyerActionDetails.get().isCheckoutStarted()) { + List cartItemChanges = optionalBuyerActionDetails.get().getCartItemChanges(); + + for (CartExtension.CartItemChange cartItemChange : cartItemChanges) { + CartExtension.OptionalCartItem optionalCartItem = cartItemChange.getChangedItem(); + if (optionalCartItem.isPresent()) { + CartExtension.CartItem cartItem = optionalCartItem.get(); + if (cartItem.getProduct2Id() == ID.valueOf(QUALIFIER_PRODUCT_ID) || + cartItem.getProduct2Id() == ID.valueOf(TARGET_PRODUCT_ID)) { + resetAllAdjustments(cart); + evaluatePromotionForCartItems(cart); + break; + } + } + } + } } /** * @description Remove cart & cart-item level adjustments, cart validation outputs. * @param cart Holds details about cart */ - public static void resetAllAdjustments(cartextension.Cart cart) { + private static void resetAllAdjustments(cartextension.Cart cart) { // Remove all cart-level adjustments Iterator cagIter = cart.getCartAdjustmentGroups().iterator(); @@ -52,7 +74,7 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot ciaToRemove.add(ciaIter.next()); } for(CartExtension.CartItemPriceAdjustment cia : ciaToRemove) { - ci.getCartItemPriceAdjustments().remove(cia); + ci.getCartItemPriceAdjustments().remove(cia); } } @@ -69,6 +91,8 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot } } + + /** * @description Evaluate BOGO promotion (Buy 5 items of qualifying product, get $2 off a unit of target product, * for cart items and apply adjustments. @@ -137,7 +161,7 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot // Populate TotalPromoAdjustmentAmount for cart-item & update totals based on promotion adjustment ci.setTotalPromoAdjustmentAmount(totalPromotionAdjustment); ci.setTotalAdjustmentAmount(totalPromotionAdjustment); - ci.setTotalPriceAfterAllAdjustments(totalLineAmount - totalPromotionAdjustment); + ci.setTotalPriceAfterAllAdjustments(totalLineAmount + totalPromotionAdjustment); } else { continue; } diff --git a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls index 85e002c..157f025 100644 --- a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls +++ b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls @@ -30,9 +30,25 @@ global class PromotionCalculatorSampleTest { // Associate qualifying & target items to the Cart and load the cart CartExtension.Cart cart = addItemsToCart(cartId, 10, 3); + // Arrange buyer updated cart item with target + List changedCartItems = new List(); + Iterator cartItemsIterator = cart.getCartItems().iterator(); + while (cartItemsIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemsIterator.next(); + if (cartItem.getProduct2Id() == ID.valueOf(TARGET_PRODUCT_ID)) { + changedCartItems.add( + new CartExtension.CartItemChange.Builder() + .withChangedItem(CartExtension.OptionalCartItem.of(cartItem)) + .withAdded(true) + .build()); + } + } + CartExtension.BuyerActionDetails buyerActionDetails = new CartExtension.BuyerActionDetails.Builder() + .withCartItemChanges(changedCartItems).build(); + // Act Test.startTest(); - promotionCalculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); + promotionCalculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails))); Test.stopTest(); // Assert @@ -48,7 +64,7 @@ global class PromotionCalculatorSampleTest { // Verify CartItem adjustment and total price Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(1).getTotalPromoAdjustmentAmount()); Assert.areEqual(TOTAL_ADJUSTMENT_AMOUNT, cart.getCartItems().get(1).getTotalAdjustmentAmount()); - Assert.areEqual(((TARGET_SALES_PRICE * 3) - TOTAL_ADJUSTMENT_AMOUNT), cart.getCartItems().get(1).getTotalPriceAfterAllAdjustments()); + Assert.areEqual(((TARGET_SALES_PRICE * 3) + TOTAL_ADJUSTMENT_AMOUNT), cart.getCartItems().get(1).getTotalPriceAfterAllAdjustments()); } /** @@ -63,9 +79,26 @@ global class PromotionCalculatorSampleTest { // Associate qualifying & target items to the Cart and load the cart CartExtension.Cart cart = addItemsToCart(cartId, 4, 1); + // Arrange buyer updated cart item with target + List changedCartItems = new List(); + Iterator cartItemsIterator = cart.getCartItems().iterator(); + while (cartItemsIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemsIterator.next(); + if (cartItem.getProduct2Id() == ID.valueOf(TARGET_PRODUCT_ID)) { + changedCartItems.add( + new CartExtension.CartItemChange.Builder() + .withChangedItem(CartExtension.OptionalCartItem.of(cartItem)) + .withAdded(true) + .build()); + } + } + CartExtension.BuyerActionDetails buyerActionDetails = new CartExtension.BuyerActionDetails.Builder() + .withCartItemChanges(changedCartItems).build(); + + // Act Test.startTest(); - promotionCalculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); + promotionCalculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails))); Test.stopTest(); // Assert @@ -133,4 +166,4 @@ global class PromotionCalculatorSampleTest { // Return Cart return CartExtension.CartTestUtil.getCart(cartId); } -} +} \ No newline at end of file From 2a12dbb770c0b8f6e2fa6bcbb4887191c8fea629 Mon Sep 17 00:00:00 2001 From: patricia-bagzai Date: Tue, 2 Apr 2024 14:09:26 -0400 Subject: [PATCH 27/69] adding evaluate promotion logic when optionalBuyerActionDetails not present; adding test --- .../classes/PromotionCalculatorSample.cls | 40 ++++++++++--------- .../classes/PromotionCalculatorSampleTest.cls | 35 +++++++++++++++- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls index 0043c8e..7107a91 100644 --- a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls +++ b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSample.cls @@ -20,26 +20,30 @@ public with sharing class PromotionCalculatorSample extends CartExtension.Promot } /** - * @description Evaluate promotion for the cart items if qualifier and/or target products part of changed elements in cart. - * @param cart Holds details about cart - * @param optionalBuyerActionDetails The latest set of changes applied to the Cart by the Buyer + * @description Evaluate promotion for cart when OptionalBuyerActionDetails not present or + * OptionalBuyerActionDetails includes qualifying/target product. + * @param cart In memory representation of the Cart + * @param optionalBuyerActionDetails The latest set of changes applied to the Cart by the Buyer */ private void validateBuyerActionDetailsAndEvaluatePromotion(cartextension.Cart cart, cartextension.OptionalBuyerActionDetails optionalBuyerActionDetails) { - if (optionalBuyerActionDetails.isPresent() || !optionalBuyerActionDetails.get().isCheckoutStarted()) { - List cartItemChanges = optionalBuyerActionDetails.get().getCartItemChanges(); - - for (CartExtension.CartItemChange cartItemChange : cartItemChanges) { - CartExtension.OptionalCartItem optionalCartItem = cartItemChange.getChangedItem(); - if (optionalCartItem.isPresent()) { - CartExtension.CartItem cartItem = optionalCartItem.get(); - if (cartItem.getProduct2Id() == ID.valueOf(QUALIFIER_PRODUCT_ID) || - cartItem.getProduct2Id() == ID.valueOf(TARGET_PRODUCT_ID)) { - resetAllAdjustments(cart); - evaluatePromotionForCartItems(cart); - break; - } - } - } + if (!optionalBuyerActionDetails.isPresent() || optionalBuyerActionDetails.get().isCheckoutStarted()) { + resetAllAdjustments(cart); + evaluatePromotionForCartItems(cart); + return; + } + + List cartItemChanges = optionalBuyerActionDetails.get().getCartItemChanges(); + for (CartExtension.CartItemChange cartItemChange : cartItemChanges) { + CartExtension.OptionalCartItem optionalCartItem = cartItemChange.getChangedItem(); + if (optionalCartItem.isPresent()) { + CartExtension.CartItem cartItem = optionalCartItem.get(); + if (cartItem.getProduct2Id() == ID.valueOf(QUALIFIER_PRODUCT_ID) || + cartItem.getProduct2Id() == ID.valueOf(TARGET_PRODUCT_ID)) { + resetAllAdjustments(cart); + evaluatePromotionForCartItems(cart); + break; + } + } } } diff --git a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls index 157f025..6ead8ae 100644 --- a/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls +++ b/commerce/domain/promotion/cart/calculator/classes/PromotionCalculatorSampleTest.cls @@ -71,7 +71,7 @@ global class PromotionCalculatorSampleTest { * @description Verify Promotion is not applied when cart does not include enough qualifying or target items. */ @IsTest - public static void testPromotionAndPriceAdjustments_WithOutQualifierAndTargetItems() { + public static void testPromotionAndPriceAdjustments_WithInsufficientQualifierAndTargetItems() { // Arrange // Create a Cart with CHECKOUT status Id cartId = createCartWithSpecifiedStatus(CartExtension.CartStatusEnum.CHECKOUT); @@ -110,6 +110,39 @@ global class PromotionCalculatorSampleTest { Assert.areEqual(0, cart.getCartItems().get(1).getCartItemPriceAdjustments().size()); } + /** + * @description Verify Promotion is correctly applied on cart item w/o optional buyer action details. + */ + @IsTest + public static void testPromotionAndPriceAdjustments_WithOutBuyerActionDetails() { + // Arrange + // Create a Cart with CHECKOUT status + Id cartId = createCartWithSpecifiedStatus(CartExtension.CartStatusEnum.CHECKOUT); + + // Associate qualifying & target items to the Cart and load the cart + CartExtension.Cart cart = addItemsToCart(cartId, 5, 2); + + // Act + Test.startTest(); + promotionCalculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); + Test.stopTest(); + + // Assert + // Verify that we have 2 CartItems + Assert.areEqual(2, cart.getCartItems().size()); + + // Verify that the CartItem has 1 price adjustment with correct adjustment type and value + Assert.areEqual(1, cart.getCartItems().get(1).getCartItemPriceAdjustments().size()); + Assert.areEqual(Cartextension.AdjustmentTypeEnum.ADJUSTMENT_AMOUNT, cart.getCartItems().get(1).getCartItemPriceAdjustments().get(0).getAdjustmentType()); + Assert.areEqual(DISCOUNT_VALUE, cart.getCartItems().get(1).getCartItemPriceAdjustments().get(0).getAdjustmentValue()); + Assert.areEqual(DISCOUNT_VALUE, cart.getCartItems().get(1).getCartItemPriceAdjustments().get(0).getTotalAmount()); + + // Verify CartItem adjustment and total price + Assert.areEqual(DISCOUNT_VALUE, cart.getCartItems().get(1).getTotalPromoAdjustmentAmount()); + Assert.areEqual(DISCOUNT_VALUE, cart.getCartItems().get(1).getTotalAdjustmentAmount()); + Assert.areEqual(((TARGET_SALES_PRICE * 2) + DISCOUNT_VALUE), cart.getCartItems().get(1).getTotalPriceAfterAllAdjustments()); + } + /** * @description Create a WebCart with the specific status. * @param cartStatus Status of the Cart From f3fde2af14540ce7b4b3bdb2cc4189b20d6ee4ae Mon Sep 17 00:00:00 2001 From: bsrilok Date: Wed, 24 Apr 2024 19:58:26 +0530 Subject: [PATCH 28/69] @W-15346861-refactored-cart-calculator-apex --- .../classes/TaxCartCalculatorSample.cls | 793 +++++++++--------- .../classes/TaxCartCalculatorSampleTest.cls | 96 +++ 2 files changed, 482 insertions(+), 407 deletions(-) create mode 100644 commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index 3d16104..343a65e 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -1,349 +1,248 @@ -// This tax calculator extension class makes a call to an external service to retrieve tax -// information for a cart item and its adjustments and saves it to a cart data transfer object -// (DTO). For a tax calculator extension to be processed by the checkout flow, you must implement the -// CartExtension.TaxCartCalculator class. +// This class facilitates tax calculation either by invoking an external service or by implementing the tax calculation logic internally. The resulting tax values and adjustments are then stored in a Cart Data Transfer Object (DTO), references to both code implementations provided +// For a tax calculator extension to be processed by the checkout flow, you must implement the CartExtension.TaxCartCalculator class. + public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { -// You MUST change this to be your service or you must launch your own Third Party Service -// and add the host in Setup | Security | Remote site settings. - private static String externalTaxHost = 'https://example.com'; - - // You MUST change the useExternalService to True if you want to use the Third Party Service. - private static Boolean useExternalService = false; - public virtual override void calculate(CartExtension.CartCalculateCalculatorRequest request) { - try { - CartExtension.Cart cart = request.getCart(); - - // Clean up CVO based on tax. When new tax calculator request comes, we need to clean up - // previous CVOs as they have been previously handled by the Cart Calculate API. - CartExtension.CartValidationOutputList cartValidationOutputCollection = cart.getCartValidationOutputs(); - Iterator cartValidationOutputCollectionIterator = cartValidationOutputCollection.iterator(); - while (cartValidationOutputCollectionIterator.hasNext()) { - CartExtension.CartValidationOutput cvo = cartValidationOutputCollectionIterator.next(); - if (cvo.getType() == CartExtension.CartValidationOutputTypeEnum.TAXES) { - cartValidationOutputCollection.remove(cvo); - } - } + // You MUST change this to be your service or you must launch your own Third Party Service and add the host in Setup | Security | Remote site settings. + private static String externalTaxHost = 'https://example.com'; + + // You MUST change the useExternalService to True if you want to use the Third Party Service. + private static Boolean useExternalService = false; + + public virtual override void calculate(CartExtension.CartCalculateCalculatorRequest request) { + CartExtension.CartValidationOutputList cartValidationOutputCollection = null; + try{ + Integer cartItemIdSeq = 0; + CartExtension.Cart cart = request.getCart(); + if (!isValidCart(cart)){ + return; + } + + // Clean up CVO based on tax. When new tax calculator request comes, we need to clean up + // previous CVOs as they have been previously handled by the Cart Calculate API. + cartValidationOutputCollection = cart.getCartValidationOutputs(); + for (Integer i = (cartValidationOutputCollection.size() - 1); i >= 0; i--) { + CartExtension.CartValidationOutput cvo = cartValidationOutputCollection.get(i); + if (cvo.getType() == CartExtension.CartValidationOutputTypeEnum.TAXES) { + cartValidationOutputCollection.remove(cvo); + } + } - // There should be one delivery group per cart. - CartExtension.CartDeliveryGroupList cartDeliveryGroups = cart.getCartDeliveryGroups(); - CartExtension.CartDeliveryGroup cartDeliveryGroup = cartDeliveryGroups.get(0); + // There should be one delivery group per cart. + CartExtension.CartDeliveryGroupList cartDeliveryGroups = cart.getCartDeliveryGroups(); + CartExtension.CartDeliveryGroup cartDeliveryGroup = cartDeliveryGroups.get(0); - // Map cart ID to cart item with type Product. - CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + // Map cart ID to cart item with type Product. + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); - // The cartItemCollection contains both products and shipping cart items. - Map cartItemById = new Map(); - Map shippingItemById = new Map(); + // The cartItemCollection contains both products and shipping cart items. + Map cartItemByIdMap = new Map(); + Map chargeItemByDeliveryGroupIdMap = new Map(); - Iterator cartItemCollectionIterator = cartItemCollection.iterator(); + Iterator cartItemCollectionIterator = cartItemCollection.iterator(); - while (cartItemCollectionIterator.hasNext()) { - CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); + while (cartItemCollectionIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); - if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT) { - cartItemById.put(cartItem.getId(), cartItem); - } else if (cartItem.getType() == CartExtension.SalesItemTypeEnum.CHARGE) { - // Shipping cart items are uniquely identified using delivery group id. - CartExtension.CartDeliveryGroup deliveryGroup = cartItem.getCartDeliveryGroup(); - shippingItemById.put(deliveryGroup.getId(), cartItem); - } - } - - // Get the tax rates and tax amounts from an external service for all given products and its - // adjustments. - Map dataFromExternalService = null; - Map dataFromExternalServiceForShippingItems = null; - if(useExternalService){ - dataFromExternalService = getTaxesFromExternalService( - cartItemById, - CartDeliveryGroup.getDeliverToAddress().getState(), - CartDeliveryGroup.getDeliverToAddress().getCountry(), - cart.getTaxType() - ); - dataFromExternalServiceForShippingItems = getTaxesFromExternalService( - shippingItemById, - CartDeliveryGroup.getDeliverToAddress().getState(), - CartDeliveryGroup.getDeliverToAddress().getCountry(), - cart.getTaxType() - ); - } else{ - dataFromExternalService = getTaxesFromStaticResponse( - cartItemById, - CartDeliveryGroup.getDeliverToAddress().getState(), - CartDeliveryGroup.getDeliverToAddress().getCountry(), - cart.getTaxType() - ); - dataFromExternalServiceForShippingItems = getTaxesFromStaticResponse( - shippingItemById, - CartDeliveryGroup.getDeliverToAddress().getState(), - CartDeliveryGroup.getDeliverToAddress().getCountry(), - cart.getTaxType() - ); - } - - // If no tax details are returned for any cart item, add a cart validation output entry. If - // any invalid scenario found then return. - boolean isCvoPresent = false; - for (String cartItemId : cartItemById.keySet()) { - TaxDataFromExternalService taxDetails = dataFromExternalService.get(cartItemId); - if (taxDetails == null) { - // add cvo - CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.TAXES, - CartExtension.CartValidationOutputLevelEnum.INFO - ); - cvo.setMessage('No tax rates configured for this location.'); - cartValidationOutputCollection.add(cvo); - isCvoPresent = true; - } - } - if (isCvoPresent == true) - return; - - for (String cartItemId : dataFromExternalService.keySet()) { - TaxDataFromExternalService taxDetailsToCartId = dataFromExternalService.get(cartItemId); - CartExtension.CartItem cartItem = cartItemById.get(cartItemId); - - // NOTE: DELETED items get filtered out in the DtoCollection and if there is no tax setup - // against any cart item, then that's considered an invalid scenario and added to CVO. If - // cart tax numbers are changed that indicates the cart item was MODIFIED, then: - // 1. Delete existing and create new cart tax entries in cart item and cart item - // adjustments. - // 2. Update cart item tax information. Currently, we do not support taxes on tier - // adjustment in an extension. - boolean isCartItemModified = false; - if ( - (cartItem.getNetUnitPrice() != null && - cartItem.getNetUnitPrice() != taxDetailsToCartId.getNetUnitPrice()) || - !VerifyAdjustmentUpdate(cartItem, taxDetailsToCartId) - ) { - if (cartItem.getCartTaxes().size() > 0) { - cartItem.getCartTaxes().remove(cartItem.getCartTaxes().get(0)); - } - Iterator cartItemPriceAdjustmentsIterator = cartItem.getCartItemPriceAdjustments().iterator(); - while(cartItemPriceAdjustmentsIterator.hasNext()) { - CartExtension.CartTaxList cipaTaxes = cartItemPriceAdjustmentsIterator.next() - .getCartTaxes(); - if (cipaTaxes.size() > 0) { - cipaTaxes.remove(cipaTaxes.get(0)); + if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT) { + String cartItemId = (cartItem.getId() == null) ? String.valueOf(++cartItemIdSeq) : cartItem.getId(); + cartItemByIdMap.put(cartItemId, cartItem); + } else if (cartItem.getType() == CartExtension.SalesItemTypeEnum.CHARGE) { + // Shipping cart items are uniquely identified using delivery group id. + CartExtension.CartDeliveryGroup deliveryGroup = cartItem.getCartDeliveryGroup(); + chargeItemByDeliveryGroupIdMap.put(deliveryGroup.getId(), cartItem); + } } - } - isCartItemModified = true; - } - // If there are no existing cart tax entries in the cart item that indicates cart item was - // newly CREATED in the cart then: - // 1. Create new cart tax entries - // 2. Update cart item tax information - if ( - cartItem.getCartTaxes() == null || - cartItem.getCartTaxes().isEmpty() || - isCartItemModified == true - ) { - cartItem.setNetUnitPrice(taxDetailsToCartId.getNetUnitPrice()); - cartItem.setGrossUnitPrice(taxDetailsToCartId.getGrossUnitPrice()); - cartItem.setAdjustmentTaxAmount(taxDetailsToCartId.getAdjustmentTaxAmount()); - CartExtension.CartTaxList cartTaxCollection = cartItem.getCartTaxes(); - CartExtension.CartTax cartTax = new CartExtension.CartTax( - CartExtension.TaxTypeEnum.ESTIMATED, - taxDetailsToCartId.getAmount(), - taxDetailsToCartId.getTaxName() - ); - cartTax.setTaxRate(String.valueOf(taxDetailsToCartId.getRate())); - cartTaxCollection.add(cartTax); - - // Add adjustment taxes to cartItemAdjustments of cartItem and create CartTaxDto entries - // for all promotion adjustments. - if ( - taxDetailsToCartId.getItemizedPromotionTaxAmounts() != null && - !(taxDetailsToCartId.getItemizedPromotionTaxAmounts().isEmpty()) - ) - for (CartAdjustment cipaTax : taxDetailsToCartId.getItemizedPromotionTaxAmounts()) { - CartExtension.CartTax promoTax = new CartExtension.CartTax( - CartExtension.TaxTypeEnum.ESTIMATED, - cipaTax.getAmount(), - taxDetailsToCartId.getTaxName() - ); - promoTax.setTaxRate(String.valueOf(taxDetailsToCartId.getRate())); - CartExtension.cartItemPriceAdjustment adj = getAdjustmentById( - cartItem.getCartItemPriceAdjustments(), - cipaTax.getId() - ); - if (adj != null) { - adj.getCartTaxes().add(promoTax); - } + Map taxData = null; + Map taxDataShippingItems = null; + if(useExternalService){ + // Get the tax rates and tax amounts from an external service for all given products and its adjustments. + taxData = getTaxesFromExternalService( + cartItemByIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), + cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); + + taxDataShippingItems = getTaxesFromExternalService( + chargeItemByDeliveryGroupIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), + cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); + + } else{ + taxData = getTaxesFromStaticResponse( + cartItemByIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), + cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); + + taxDataShippingItems = getTaxesFromStaticResponse( + chargeItemByDeliveryGroupIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), + cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); } - } - } - - // If there are shipping items, add tax for them as well - for (String cartItemId : dataFromExternalServiceForShippingItems.keySet()) { - TaxDataFromExternalService taxDetailsToCartId = dataFromExternalServiceForShippingItems.get(cartItemId); - CartExtension.CartItem cartItem = shippingItemById.get(cartItemId); - boolean isCartItemModified = false; - // If there is any modification in unit price, delete existing and create new cart tax entries in cart item. - if (cartItem.getNetUnitPrice() != null && - cartItem.getNetUnitPrice() != taxDetailsToCartId.getNetUnitPrice()) { - cartItem.getCartTaxes().remove(cartItem.getCartTaxes().get(0)); - isCartItemModified = true; - } - if (cartItem.getCartTaxes() == null || - cartItem.getCartTaxes().isEmpty() || - isCartItemModified == true) { - cartItem.setNetUnitPrice(taxDetailsToCartId.getNetUnitPrice()); - cartItem.setGrossUnitPrice(taxDetailsToCartId.getGrossUnitPrice()); - CartExtension.CartTaxList cartTaxCollection = cartItem.getCartTaxes(); - CartExtension.CartTax cartTax = new CartExtension.CartTax( - CartExtension.TaxTypeEnum.ESTIMATED, - taxDetailsToCartId.getAmount(), - taxDetailsToCartId.getTaxName() - ); - cartTax.setTaxRate(String.valueOf(taxDetailsToCartId.getRate())); - cartTaxCollection.add(cartTax); - } - } - } catch (Exception e) { - // For testing purposes, this example treats exceptions as user errors, which means they are - // displayed to the buyer user. In production, you probably want exceptions to be admin-type - // errors. In that case, throw the exception here and make sure that a notification system is - // in place to let the admin know that the error occurred. See the README section about error - // handling for details about how to create that notification. - throw new CalloutException('There was a problem with the request.'); - } - return; - } - - // Verify if taxes from adjustments returned by external service and existing cart has changed. If - // returned true then that indicates that there was an adjustment change. - private Boolean VerifyAdjustmentUpdate( - CartExtension.CartItem cartItemDto, - TaxDataFromExternalService taxesFromExternalService - ) { - List ajustments = taxesFromExternalService.getItemizedPromotionTaxAmounts() == - null - ? new List() - : taxesFromExternalService.getItemizedPromotionTaxAmounts(); - Iterator cartItemPriceAdjustmentsIterator = cartItemDto.getCartItemPriceAdjustments().iterator(); - while (cartItemPriceAdjustmentsIterator.hasNext()) { - CartExtension.CartTaxList cartTaxes = cartItemPriceAdjustmentsIterator.next() - .getCartTaxes(); - Iterator cartTaxesIterator = cartTaxes.iterator(); - while (cartTaxesIterator.hasNext()) { - CartExtension.CartTax cartTax = cartTaxesIterator.next(); - Boolean changedAdjTax = false; - for (Integer k = (ajustments.size() - 1); k >= 0; k--) { - if (cartTax.getAmount() == ajustments.get(k).getAmount()) - changedAdjTax = true; + // TODO need to add cvo in case of tax data is null + if (taxData == null) { + // add cvo + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.TAXES, + CartExtension.CartValidationOutputLevelEnum.ERROR); + cvo.setMessage('No tax rates configured for this location.'); + cartValidationOutputCollection.add(cvo); + return; + } + + // If no tax details are returned for any cart item, add a cart validation output entry. If + // any invalid scenario found then return. + boolean isCvoPresent = false; + for (String cartItemId : cartItemByIdMap.keySet()) { + TaxData taxDetails = taxData.get(cartItemId); + if (taxDetails == null) { + // add cvo + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.TAXES, + CartExtension.CartValidationOutputLevelEnum.INFO); + cvo.setMessage('No tax rates configured for this location.'); + cartValidationOutputCollection.add(cvo); + isCvoPresent = true; // need to take an call on this we can exit for first time only + } + } + if (isCvoPresent == true) + return; + + for (String cartItemId : taxData.keySet()) { + TaxData taxDetailsToCartId = taxData.get(cartItemId); + CartExtension.CartItem cartItem = cartItemByIdMap.get(cartItemId); + + // NOTE: DELETED items get filtered out in the DtoCollection and if there is no tax setup + // against any cart item, then that's considered an invalid scenario and added to CVO. If + // cart tax numbers are changed that indicates the cart item was MODIFIED, then: + // 1. Delete existing and create new cart tax entries in cart item and cart item + // adjustments. + // 2. Update cart item tax information. Currently, we do not support taxes on tier + // adjustment in an extension. + boolean isCartItemModified = false; + if ((cartItem.getNetUnitPrice() != null && + cartItem.getNetUnitPrice() != taxDetailsToCartId.getNetUnitPrice()) || + !VerifyAdjustmentUpdate(cartItem, taxDetailsToCartId)) { + if (cartItem.getCartTaxes().size() > 0) { + cartItem.getCartTaxes().remove(cartItem.getCartTaxes().get(0)); + } + Iterator cartItemPriceAdjustmentsIterator = cartItem.getCartItemPriceAdjustments().iterator(); + while(cartItemPriceAdjustmentsIterator.hasNext()) { + CartExtension.CartTaxList cipaTaxes = cartItemPriceAdjustmentsIterator.next().getCartTaxes(); + if (cipaTaxes.size() > 0) { + cipaTaxes.remove(cipaTaxes.get(0)); + } + } + isCartItemModified = true; + } + + // If there are no existing cart tax entries in the cart item that indicates cart item was + // newly CREATED in the cart then: + // 1. Create new cart tax entries + // 2. Update cart item tax information + if (cartItem.getCartTaxes() == null || cartItem.getCartTaxes().isEmpty() || isCartItemModified == true) { + + cartItem.setNetUnitPrice(taxDetailsToCartId.getNetUnitPrice()); + cartItem.setGrossUnitPrice(taxDetailsToCartId.getGrossUnitPrice()); + cartItem.setAdjustmentTaxAmount(taxDetailsToCartId.getAdjustmentTaxAmount()); + CartExtension.CartTaxList cartTaxCollection = cartItem.getCartTaxes(); + CartExtension.CartTax cartTax = new CartExtension.CartTax(CartExtension.TaxTypeEnum.ESTIMATED, + taxDetailsToCartId.getAmount(), taxDetailsToCartId.getTaxName()); + + cartTax.setTaxRate(String.valueOf(taxDetailsToCartId.getRate())); + cartTaxCollection.add(cartTax); + + // Add adjustment taxes to cartItemAdjustments of cartItem and create CartTaxDto entries + // for all promotion adjustments. + if (taxDetailsToCartId.getItemizedPromotionTaxAmounts() != null && + !(taxDetailsToCartId.getItemizedPromotionTaxAmounts().isEmpty())){ + for (CartAdjustment cipaTax : taxDetailsToCartId.getItemizedPromotionTaxAmounts()) { + CartExtension.CartTax promoTax = new CartExtension.CartTax( + CartExtension.TaxTypeEnum.ESTIMATED, cipaTax.getAmount(), taxDetailsToCartId.getTaxName()); + promoTax.setTaxRate(String.valueOf(taxDetailsToCartId.getRate())); + CartExtension.cartItemPriceAdjustment adj = getAdjustmentById( + cartItem.getCartItemPriceAdjustments(), cipaTax.getId()); + if (adj != null) { + adj.getCartTaxes().add(promoTax); + } + } + } + } + } + + // If there are shipping items, add tax for them as well + for (String cartItemId : taxDataShippingItems.keySet()) { + TaxData taxDetailsToCartId = taxDataShippingItems.get(cartItemId); + CartExtension.CartItem cartItem = chargeItemByDeliveryGroupIdMap.get(cartItemId); + boolean isCartItemModified = false; + // If there is any modification in unit price, delete existing and create new cart tax entries in cart item. + if (cartItem.getNetUnitPrice() != null && + cartItem.getNetUnitPrice() != taxDetailsToCartId.getNetUnitPrice()) { + cartItem.getCartTaxes().remove(cartItem.getCartTaxes().get(0)); + isCartItemModified = true; + } + + if (cartItem.getCartTaxes() == null || cartItem.getCartTaxes().isEmpty() || isCartItemModified == true) { + cartItem.setNetUnitPrice(taxDetailsToCartId.getNetUnitPrice()); + cartItem.setGrossUnitPrice(taxDetailsToCartId.getGrossUnitPrice()); + CartExtension.CartTaxList cartTaxCollection = cartItem.getCartTaxes(); + CartExtension.CartTax cartTax = new CartExtension.CartTax( + CartExtension.TaxTypeEnum.ESTIMATED, + taxDetailsToCartId.getAmount(), + taxDetailsToCartId.getTaxName()); + cartTax.setTaxRate(String.valueOf(taxDetailsToCartId.getRate())); + cartTaxCollection.add(cartTax); + } + } + } catch(Exception e){ + // add cvo + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.TAXES, + CartExtension.CartValidationOutputLevelEnum.ERROR); + cvo.setMessage('There was a problem with the request.'); + cartValidationOutputCollection.add(cvo); + return; + } + + } + + // This simulates a call to an external tax service. Change this function based on your external service. + // Transform tax data returned from service into cart ID to TaxData map. + // Admin need to modify this function according to external service api contract. + private Map getTaxesFromExternalService(Map cartItemByIdMap, + String state, String country, CartExtension.TaxLocaleTypeEnum taxType) { + String requestURL = externalTaxHost+'/get-tax-rates-with-adjustments-post'; + String requestBody = + '{"state":"' + + state + + '", "country":"' + + country + + '", "taxType":"' + + taxType + + '", ' + + '"amountsBySKU":' + + JSON.serialize(cartItemByIdMap) + + '}'; + Http http = new Http(); + HttpRequest request = new HttpRequest(); + request.setEndpoint(requestURL); + request.setMethod('POST'); + request.setHeader('Content-Type', 'application/json'); + request.setBody(requestBody); + HttpResponse response = http.send(request); + + // If the request is successful, parse the JSON response. + if (response.getStatusCode() == 200) { + Map resultsFromExternalService = (Map) JSON.deserializeUntyped(response.getBody()); + return populateTax(resultsFromExternalService); } - if (changedAdjTax == false) - return false; - } + return null; } - return true; - } - - // Get cartItemAdjustment based on its ID. - private CartExtension.cartItemPriceAdjustment getAdjustmentById( - CartExtension.cartItemPriceAdjustmentList cipaList, - String id - ) { - Iterator cipaIterator = cipaList.iterator(); - while (cipaIterator.hasNext()) { - CartExtension.CartItemPriceAdjustment cipa = cipaIterator.next(); - if (String.valueOf(cipa.getId()) == id) - return cipa; - } - return null; - } - - // This similartes a call to an external tax service. Change this function based on your external - // service. Transform tax data returned from service into cart ID to TaxDataFromExternalService - // map. - private Map getTaxesFromExternalService( - Map cartItemById, - String state, - String country, - CartExtension.TaxLocaleTypeEnum taxType - ) { - String requestURL = externalTaxHost+'/get-tax-rates-with-adjustments-post'; - String requestBody = - '{"state":"' + - state + - '", "country":"' + - country + - '", "taxType":"' + - taxType + - '", ' + - '"amountsBySKU":' + - JSON.serialize(cartItemById) + - '}'; - Http http = new Http(); - HttpRequest request = new HttpRequest(); - request.setEndpoint(requestURL); - request.setMethod('POST'); - request.setHeader('Content-Type', 'application/json'); - request.setBody(requestBody); - HttpResponse response = http.send(request); - - // If the request is successful, parse the JSON response. - if (response.getStatusCode() == 200) { - Map resultsFromExternalService = (Map) JSON.deserializeUntyped( - response.getBody() - ); - return populateTax(resultsFromExternalService); - } else { - throw new CalloutException( - 'There was a problem with the request. Error: ' + response.getStatusCode() - ); - } - } - - private Map populateTax(Map resultsFromExternalService){ - Map taxDetailsFromExternalService = new Map(); - for (String cartItemId : resultsFromExternalService.keySet()) { - Map rateAndAmountFromExternalService = (Map) resultsFromExternalService.get( - cartItemId - ); - List cipaList = (List) rateAndAmountFromExternalService.get( - 'itemizedPromotionTaxAmounts' - ); - List cipaObj = new List(); - - for (Object cipa : cipaList) { - cipaObj.add( - new CartAdjustment( - (String) ((Map) cipa).get('id'), - (Decimal) ((Map) cipa).get('taxAmount') - ) - ); - } - taxDetailsFromExternalService.put( - cartItemId, - new TaxDataFromExternalService( - (Decimal) rateAndAmountFromExternalService.get('rate'), - (Decimal) rateAndAmountFromExternalService.get('amount'), - (String) rateAndAmountFromExternalService.get('taxName'), - (Decimal) rateAndAmountFromExternalService.get('adjustmentTaxAmount'), - (Decimal) rateAndAmountFromExternalService.get('totalItemizedPromotionTaxAmount'), - cipaObj, - (Decimal) rateAndAmountFromExternalService.get('grossUnitPrice'), - (Decimal) rateAndAmountFromExternalService.get('netUnitPrice') - ) - ); - } - return taxDetailsFromExternalService; - - } - private Map getTaxesFromStaticResponse(Map cartItemsMap, String state, String country, CartExtension.TaxLocaleTypeEnum taxType) { + private Map getTaxesFromStaticResponse(Map cartItemsMap, String state, String country, CartExtension.TaxLocaleTypeEnum taxType) { Double taxRate = 0.15; String responseJson = '{'; for (String key : cartItemsMap.keySet()) { CartExtension.CartItem cartItem = cartItemsMap.get(key); - ID cartItemId = cartItem.getId(); + String cartItemId = (cartItem.getId()==null) ? key : cartItem.getId(); Double amount = cartItem.getTotalAmount()==null ? 0.00 : cartItem.getTotalAmount(); Double tierAdjustment = cartItem.getAdjustmentAmount()==null ? 0.00 : cartItem.getAdjustmentAmount(); @@ -374,7 +273,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { Double tierAdjustmentTax = (tierAdjustment!=null ? tierAdjustment : 0.00) * multiplier; CartExtension.CartItemPriceAdjustmentList itemizedPromotions = cartItem.getCartItemPriceAdjustments(); - Iterator itemizedPromotionsIterator = itemizedPromotions.iterator(); + Iterator itemizedPromotionsIterator = itemizedPromotions.iterator(); String itemizedPromotionTaxResp = '['; while (itemizedPromotionsIterator.hasNext()) { CartExtension.CartItemPriceAdjustment itemAdj = itemizedPromotionsIterator.next(); @@ -418,102 +317,182 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { return populateTax(resultsFromStaticResponse); } - // Structure to store the tax data retrieved from external service. This class simplifies our - // ability to access the data when storing it in Salesforce's CartTaxDto. - class TaxDataFromExternalService { - private Decimal rate; - private Decimal amount; - private String taxName; - private Decimal adjustmentTaxAmount; - private Decimal totalItemizedPromotionTaxAmount; - private List itemizedPromotionTaxAmounts; - private Decimal grossUnitPrice; - private Decimal netUnitPrice; - - public TaxDataFromExternalService() { - rate = 0.0; - amount = 0.0; - taxName = ''; - adjustmentTaxAmount = 0.0; - totalItemizedPromotionTaxAmount = 0.0; - itemizedPromotionTaxAmounts = null; - grossUnitPrice = 0.0; - netUnitPrice = 0.0; - } - public TaxDataFromExternalService( - Decimal rateObj, - Decimal amountObj, - String taxNameObj, - Decimal adjustmentTaxAmountObj, - Decimal totalItemizedPromotionTaxAmountObj, - List itemizedPromotionTaxAmountsObj, - Decimal grossUnitPriceObj, - Decimal netUnitPriceObj - ) { - rate = rateObj; - amount = amountObj; - taxName = taxNameObj; - adjustmentTaxAmount = adjustmentTaxAmountObj; - totalItemizedPromotionTaxAmount = totalItemizedPromotionTaxAmountObj; - itemizedPromotionTaxAmounts = itemizedPromotionTaxAmountsObj; - grossUnitPrice = grossUnitPriceObj; - netUnitPrice = netUnitPriceObj; + private boolean isValidCart(CartExtension.Cart cart){ + CartExtension.CartDeliveryGroupList cartDeliveryGroups = cart.getCartDeliveryGroups(); + if(cartDeliveryGroups == null || cartDeliveryGroups.size() == 0){ + return false; + } + + CartExtension.CartDeliveryGroup cartDeliveryGroup = cartDeliveryGroups.get(0); + if(cartDeliveryGroup.getDeliverToAddress() == null){ + return false; + } + + return true; + } + + // Verify if taxes from adjustments returned by external service and existing cart has changed. If + // returned true then that indicates that there was an adjustment change. + private Boolean VerifyAdjustmentUpdate(CartExtension.CartItem cartItemDto, + TaxData taxesFromExternalService) { + List adjustments = taxesFromExternalService.getItemizedPromotionTaxAmounts() == null + ? new List() : taxesFromExternalService.getItemizedPromotionTaxAmounts(); + + Iterator cartItemPriceAdjustmentsIterator = cartItemDto.getCartItemPriceAdjustments().iterator(); + while (cartItemPriceAdjustmentsIterator.hasNext()) { + CartExtension.CartTaxList cartTaxes = cartItemPriceAdjustmentsIterator.next().getCartTaxes(); + Iterator cartTaxesIterator = cartTaxes.iterator(); + while (cartTaxesIterator.hasNext()) { + CartExtension.CartTax cartTax = cartTaxesIterator.next(); + Boolean changedAdjTax = false; + for (Integer k = (adjustments.size() - 1); k >= 0; k--) { + if (cartTax.getAmount() == adjustments.get(k).getAmount()) + changedAdjTax = true; + } + if (changedAdjTax == false) + return false; + } + } + return true; } - public Decimal getRate() { - return rate; + // Get cartItemAdjustment based on its ID. + private CartExtension.cartItemPriceAdjustment getAdjustmentById( + CartExtension.cartItemPriceAdjustmentList cipaList, String id) { + Iterator cipaIterator = cipaList.iterator(); + while (cipaIterator.hasNext()) { + CartExtension.CartItemPriceAdjustment cipa = cipaIterator.next(); + if (String.valueOf(cipa.getId()) == id) + return cipa; + } + return null; } - public Decimal getAmount() { - return amount; - } + private Map populateTax(Map resultsFromExternalService){ + Map taxDetailsFromExternalService = new Map(); + for (String cartItemId : resultsFromExternalService.keySet()) { + Map rateAndAmountFromExternalService = (Map) resultsFromExternalService.get(cartItemId); + List cipaList = (List) rateAndAmountFromExternalService.get('itemizedPromotionTaxAmounts'); + List cipaObj = new List(); - public String getTaxName() { - return taxName; - } + for (Object cipa : cipaList) { + cipaObj.add(new CartAdjustment((String) ((Map) cipa).get('id'), + (Decimal) ((Map) cipa).get('taxAmount'))); + } + taxDetailsFromExternalService.put(cartItemId, + new TaxData( + (Decimal) rateAndAmountFromExternalService.get('rate'), + (Decimal) rateAndAmountFromExternalService.get('amount'), + (String) rateAndAmountFromExternalService.get('taxName'), + (Decimal) rateAndAmountFromExternalService.get('adjustmentTaxAmount'), + (Decimal) rateAndAmountFromExternalService.get('totalItemizedPromotionTaxAmount'), + cipaObj, + (Decimal) rateAndAmountFromExternalService.get('grossUnitPrice'), + (Decimal) rateAndAmountFromExternalService.get('netUnitPrice') + )); + } + return taxDetailsFromExternalService; - public Decimal getAdjustmentTaxAmount() { - return adjustmentTaxAmount; } - public Decimal getTotalItemizedPromotionTaxAmount() { - return totalItemizedPromotionTaxAmount; - } + // Structure to store the tax data retrieved from external service. This class simplifies our + // ability to access the data when storing it in Salesforce's CartTaxDto. + class TaxData { + private Decimal rate; + private Decimal amount; + private String taxName; + private Decimal adjustmentTaxAmount; + private Decimal totalItemizedPromotionTaxAmount; + private List itemizedPromotionTaxAmounts; + private Decimal grossUnitPrice; + private Decimal netUnitPrice; + + public TaxData() { + rate = 0.0; + amount = 0.0; + taxName = ''; + adjustmentTaxAmount = 0.0; + totalItemizedPromotionTaxAmount = 0.0; + itemizedPromotionTaxAmounts = null; + grossUnitPrice = 0.0; + netUnitPrice = 0.0; + } - public List getItemizedPromotionTaxAmounts() { - return itemizedPromotionTaxAmounts; - } + public TaxData( + Decimal rateObj, + Decimal amountObj, + String taxNameObj, + Decimal adjustmentTaxAmountObj, + Decimal totalItemizedPromotionTaxAmountObj, + List itemizedPromotionTaxAmountsObj, + Decimal grossUnitPriceObj, + Decimal netUnitPriceObj + ) { + rate = rateObj; + amount = amountObj; + taxName = taxNameObj; + adjustmentTaxAmount = adjustmentTaxAmountObj; + totalItemizedPromotionTaxAmount = totalItemizedPromotionTaxAmountObj; + itemizedPromotionTaxAmounts = itemizedPromotionTaxAmountsObj; + grossUnitPrice = grossUnitPriceObj; + netUnitPrice = netUnitPriceObj; + } - public Decimal getGrossUnitPrice() { - return grossUnitPrice; - } + public Decimal getRate() { + return rate; + } - public Decimal getNetUnitPrice() { - return netUnitPrice; - } - } + public Decimal getAmount() { + return amount; + } - class CartAdjustment { - private String id; - private Decimal amount; + public String getTaxName() { + return taxName; + } - public CartAdjustment() { - id = ''; - amount = 0.0; - } + public Decimal getAdjustmentTaxAmount() { + return adjustmentTaxAmount; + } - public CartAdjustment(String idObj, Decimal taxAmountObj) { - id = idObj; - amount = taxAmountObj; - } + public Decimal getTotalItemizedPromotionTaxAmount() { + return totalItemizedPromotionTaxAmount; + } - public String getId() { - return id; - } + public List getItemizedPromotionTaxAmounts() { + return itemizedPromotionTaxAmounts; + } + + public Decimal getGrossUnitPrice() { + return grossUnitPrice; + } - public Decimal getAmount() { - return amount; + public Decimal getNetUnitPrice() { + return netUnitPrice; + } + } + + class CartAdjustment { + private String id; + private Decimal amount; + + public CartAdjustment() { + id = ''; + amount = 0.0; + } + + public CartAdjustment(String idObj, Decimal taxAmountObj) { + id = idObj; + amount = taxAmountObj; + } + + public String getId() { + return id; + } + + public Decimal getAmount() { + return amount; + } } - } + } diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls new file mode 100644 index 0000000..09a727d --- /dev/null +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls @@ -0,0 +1,96 @@ +/** + * @description A Sample unit test for TaxCartCalculatorSample. + */ +@IsTest +public class TaxCartCalculatorSampleTest { + + private static final String CART_NAME = 'My Cart'; + private static final String ACCOUNT_NAME = 'My Account'; + private static final String WEBSTORE_NAME = 'My WebStore'; + private static final String DELIVERYGROUP_NAME = 'My Delivery Group'; + private static final String CART_ITEM1_NAME = 'My Cart Item 1'; + private static final String CART_ITEM2_NAME = 'My Cart Item 2'; + private static final String CART_ITEM3_NAME = 'My Cart Item 3'; + private static final String SKU1_NAME = 'My SKU 1'; + private static final String SKU2_NAME = 'My SKU 2'; + private static final String SKU3_NAME = 'My SKU 3'; + private static final Decimal ESTIMATED_PRICE = 350.00; + private static final Decimal ACTUAL_PRICE_SKU1 = 100.00; + private static final Decimal ACTUAL_PRICE_SKU2 = 200.00; + private static final Decimal ACTUAL_PRICE_SKU3 = 300.00; + + @IsTest + static void testCalculate_NoExternalService() { + CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.ACTIVE); + + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty()); + + // Call the calculate method + TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + Test.startTest(); + calculator.calculate(request); + Test.stopTest(); + } + + /** + * @description Create and return a WebCart with the specified status and 3 items. + * + * @param cartStatus The status of the cart. + * + * @return <> + */ + private static ID arrangeCartWithSpecifiedStatus(CartExtension.CartStatusEnum cartStatus) { + Account account = new Account(Name = ACCOUNT_NAME); + insert account; + + WebStore webStore = new WebStore(Name = WEBSTORE_NAME, OptionsCartCalculateEnabled = true); + insert webStore; + + WebCart webCart = new WebCart( + Name = CART_NAME, + WebStoreId = webStore.Id, + AccountId = account.Id, + Status = cartStatus.name()); + insert webCart; + return webCart.Id; + } + + private static List arrangeThreeCartItems(ID cartId) { + CartDeliveryGroup deliveryGroup = new CartDeliveryGroup(Name = DELIVERYGROUP_NAME, CartId = cartId); + insert deliveryGroup; + + CartItem cartItem1 = new CartItem( + Name = CART_ITEM1_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 3, + SKU = SKU1_NAME, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem1; + + CartItem cartItem2 = new CartItem( + Name = CART_ITEM2_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 3, + SKU = SKU2_NAME, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem2; + + CartItem cartItem3 = new CartItem( + Name = CART_ITEM3_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 3, + SKU = SKU3_NAME, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem3; + return new List{cartItem1.Id, cartItem2.Id, cartItem3.Id}; + } + + private static CartExtension.Cart arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum cartStatus) { + Id cartId = arrangeCartWithSpecifiedStatus(cartStatus); + arrangeThreeCartItems(cartId); + return CartExtension.CartTestUtil.getCart(cartId); + } +} From 0973ccc101fbad30c3355b739f39267568bb9b5d Mon Sep 17 00:00:00 2001 From: bsrilok Date: Thu, 25 Apr 2024 16:40:46 +0530 Subject: [PATCH 29/69] @W-15346861-added-test-cases --- .../classes/TaxCartCalculatorSample.cls | 23 ++-- .../classes/TaxCartCalculatorSampleTest.cls | 102 +++++++++++++++++- 2 files changed, 111 insertions(+), 14 deletions(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index 343a65e..59f1dbb 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -1,4 +1,5 @@ -// This class facilitates tax calculation either by invoking an external service or by implementing the tax calculation logic internally. The resulting tax values and adjustments are then stored in a Cart Data Transfer Object (DTO), references to both code implementations provided +// This class facilitates tax calculation either by invoking an external service or by implementing the tax calculation logic internally. +// The resulting tax values and adjustments are then stored in a Cart Data Transfer Object (DTO), references to both code implementations provided // For a tax calculator extension to be processed by the checkout flow, you must implement the CartExtension.TaxCartCalculator class. public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { @@ -10,17 +11,21 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { private static Boolean useExternalService = false; public virtual override void calculate(CartExtension.CartCalculateCalculatorRequest request) { - CartExtension.CartValidationOutputList cartValidationOutputCollection = null; + CartExtension.Cart cart = request.getCart(); + CartExtension.CartValidationOutputList cartValidationOutputCollection = cart.getCartValidationOutputs(); try{ Integer cartItemIdSeq = 0; - CartExtension.Cart cart = request.getCart(); if (!isValidCart(cart)){ + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.TAXES, + CartExtension.CartValidationOutputLevelEnum.ERROR); + cvo.setMessage('There was a problem with delivery address in request.'); + cartValidationOutputCollection.add(cvo); return; } // Clean up CVO based on tax. When new tax calculator request comes, we need to clean up // previous CVOs as they have been previously handled by the Cart Calculate API. - cartValidationOutputCollection = cart.getCartValidationOutputs(); for (Integer i = (cartValidationOutputCollection.size() - 1); i >= 0; i--) { CartExtension.CartValidationOutput cvo = cartValidationOutputCollection.get(i); if (cvo.getType() == CartExtension.CartValidationOutputTypeEnum.TAXES) { @@ -78,11 +83,10 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { // TODO need to add cvo in case of tax data is null if (taxData == null) { - // add cvo CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); - cvo.setMessage('No tax rates configured for this location.'); + cvo.setMessage('No tax rates configured.'); cartValidationOutputCollection.add(cvo); return; } @@ -93,7 +97,6 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { for (String cartItemId : cartItemByIdMap.keySet()) { TaxData taxDetails = taxData.get(cartItemId); if (taxDetails == null) { - // add cvo CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.INFO); @@ -192,11 +195,10 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { } } } catch(Exception e){ - // add cvo CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); - cvo.setMessage('There was a problem with the request.'); + cvo.setMessage('There was a problem. ' + e.getMessage()); cartValidationOutputCollection.add(cvo); return; } @@ -237,7 +239,6 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { } private Map getTaxesFromStaticResponse(Map cartItemsMap, String state, String country, CartExtension.TaxLocaleTypeEnum taxType) { - Double taxRate = 0.15; String responseJson = '{'; for (String key : cartItemsMap.keySet()) { @@ -363,7 +364,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { Iterator cipaIterator = cipaList.iterator(); while (cipaIterator.hasNext()) { CartExtension.CartItemPriceAdjustment cipa = cipaIterator.next(); - if (String.valueOf(cipa.getId()) == id) + if (cipa.getId() != null && String.valueOf(cipa.getId()) == id) return cipa; } return null; diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls index 09a727d..2cb3609 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls @@ -20,16 +20,89 @@ public class TaxCartCalculatorSampleTest { private static final Decimal ACTUAL_PRICE_SKU3 = 300.00; @IsTest - static void testCalculate_NoExternalService() { - CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.ACTIVE); + static void testCalculate_withEmptyDeliveryAddress() { + CartExtension.Cart cartReq = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.ACTIVE); - CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty()); + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cartReq, CartExtension.OptionalBuyerActionDetails.empty()); // Call the calculate method TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); Test.startTest(); calculator.calculate(request); Test.stopTest(); + CartExtension.Cart cart = request.getCart(); + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Assert.areEqual(CART_ITEM1_NAME, cartItemCollection.get(0).getName()); + CartExtension.CartValidationOutputList cartValidationOutputCollection = cart.getCartValidationOutputs(); + Assert.areEqual(1, cartValidationOutputCollection.size()); + Assert.areEqual('There was a problem with delivery address in request.', cartValidationOutputCollection.get(0).getMessage()); + } + + @IsTest + static void testCalculate_withZeroPrice() { + CartExtension.Cart cartReq = arrangeAndLoadCartWithSpecifiedStatusAndDeliveryAddress(CartExtension.CartStatusEnum.ACTIVE); + + CartExtension.CartDeliveryGroup deliveryGroup = cartReq.getCartDeliveryGroups().get(0); + deliveryGroup.setDeliverToStreet('newStreet'); + deliveryGroup.setDeliverToCity('newCity'); + deliveryGroup.setDeliverToState('Washington'); + deliveryGroup.setDeliverToCountry('US'); + deliveryGroup.setDeliverToPostalCode('987654'); + deliveryGroup.setDeliverToLatitude(48.1); + deliveryGroup.setDeliverToLongitude(33.2); + deliveryGroup.setDeliverToGeocodeAccuracy(null); + + + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cartReq, CartExtension.OptionalBuyerActionDetails.empty()); + + + TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + Test.startTest(); + calculator.calculate(request); + Test.stopTest(); + + CartExtension.Cart cart = request.getCart(); + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Iterator cartItemCollectionIterator = cartItemCollection.iterator(); + while (cartItemCollectionIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); + Assert.areEqual(0.00, cartItem.getNetUnitPrice()); + Assert.areEqual(0.00, cartItem.getGrossUnitPrice()); + } + } + + @IsTest + static void testCalculate_withDeliveryAddress() { + CartExtension.Cart cartReq = arrangeAndLoadCartWithSpecifiedStatusAndDeliveryAddress(CartExtension.CartStatusEnum.ACTIVE); + + CartExtension.CartDeliveryGroup deliveryGroup = cartReq.getCartDeliveryGroups().get(0); + deliveryGroup.setDeliverToStreet('newStreet'); + deliveryGroup.setDeliverToCity('newCity'); + deliveryGroup.setDeliverToState('Washington'); + deliveryGroup.setDeliverToCountry('US'); + deliveryGroup.setDeliverToPostalCode('987654'); + deliveryGroup.setDeliverToLatitude(48.1); + deliveryGroup.setDeliverToLongitude(33.2); + deliveryGroup.setDeliverToGeocodeAccuracy(null); + + cartReq.getCartItems().get(0).setTotalPrice(100.00); + + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cartReq, CartExtension.OptionalBuyerActionDetails.empty()); + + + TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + Test.startTest(); + calculator.calculate(request); + Test.stopTest(); + + CartExtension.Cart cart = request.getCart(); + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Iterator cartItemCollectionIterator = cartItemCollection.iterator(); + while (cartItemCollectionIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); + Assert.areEqual(100.00, cartItem.getNetUnitPrice()); + Assert.areEqual(108.00, cartItem.getGrossUnitPrice()); + } } /** @@ -93,4 +166,27 @@ public class TaxCartCalculatorSampleTest { arrangeThreeCartItems(cartId); return CartExtension.CartTestUtil.getCart(cartId); } + + private static CartExtension.Cart arrangeAndLoadCartWithSpecifiedStatusAndDeliveryAddress(CartExtension.CartStatusEnum cartStatus) { + Id cartId = arrangeCartWithSpecifiedStatus(cartStatus); + arrangeCartItemsWithDeliveryAddress(cartId); + return CartExtension.CartTestUtil.getCart(cartId); + } + + private static List arrangeCartItemsWithDeliveryAddress(ID cartId) { + CartDeliveryGroup deliveryGroup = new CartDeliveryGroup(Name = DELIVERYGROUP_NAME, CartId = cartId); + insert deliveryGroup; + + CartItem cartItem1 = new CartItem( + Name = CART_ITEM1_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 1, + SKU = SKU1_NAME, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem1; + + return new List{cartItem1.Id}; + } + } From 2f78248f2d04c1fb8b00b96e56feeff39b092cb2 Mon Sep 17 00:00:00 2001 From: pplatonov Date: Thu, 25 Apr 2024 15:50:07 -0400 Subject: [PATCH 30/69] Use proper CartValidationOutputTypeEnum for PricingCalculator --- .../cart/calculator/EstimatedActualPricingCalculator.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commerce/domain/pricing/cart/calculator/EstimatedActualPricingCalculator.cls b/commerce/domain/pricing/cart/calculator/EstimatedActualPricingCalculator.cls index 7e2974f..065bf0d 100644 --- a/commerce/domain/pricing/cart/calculator/EstimatedActualPricingCalculator.cls +++ b/commerce/domain/pricing/cart/calculator/EstimatedActualPricingCalculator.cls @@ -65,7 +65,7 @@ public class EstimatedActualPricingCalculator extends CartExtension.PricingCartC if (pricingDataMap == Null) { // No data returned means there is an issue with underlying 3rd party service. Populate generic error message for the Buyer. CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.SHIPPING, + CartExtension.CartValidationOutputTypeEnum.PRICING, CartExtension.CartValidationOutputLevelEnum.ERROR); String errorMessage = getGenericErrorMessage(); cvo.setMessage(errorMessage); From cb5fe177ac3a0a67b38b0cacb583fe8e5a3397af Mon Sep 17 00:00:00 2001 From: Adarsh Jain <84846989+adarshjain-sf@users.noreply.github.com> Date: Mon, 29 Apr 2024 21:10:24 +0530 Subject: [PATCH 31/69] Fix errors in PricingServiceSample --- .../service/classes/PricingServiceSample.cls | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/commerce/domain/pricing/service/classes/PricingServiceSample.cls b/commerce/domain/pricing/service/classes/PricingServiceSample.cls index 6364d57..38426c2 100644 --- a/commerce/domain/pricing/service/classes/PricingServiceSample.cls +++ b/commerce/domain/pricing/service/classes/PricingServiceSample.cls @@ -123,7 +123,6 @@ public class PricingServiceSample extends commercestorepricing.PricingService { commercestorepricing.TxnPricingResponseItemCollection txnItemCollection = txnResponse.getTxnPricingResponseItems(); for (Integer j = 0; j < txnItemCollection.size(); j++) { commercestorepricing.TransactionalPricingResponseItem txnItem = txnItemCollection.get(j); - txnItem.setLineId(appendField(prefix, txnItem.getLineId())); txnItem.setProductId(appendField(prefix, txnItem.getProductId())); txnItem.setUnitPricePriceBookEntryId( appendField(prefix, txnItem.getUnitPricePriceBookEntryId()) @@ -140,11 +139,13 @@ public class PricingServiceSample extends commercestorepricing.PricingService { ); if (!txnItemCollection.isEmpty()) { - // Override success/failure of a product easily by adding an error message to the product. Here - // we are failing the first product in the response. - String customErrorMessage = 'We no longer sell this particular product.'; - String localizedErrorMessage = 'Wir verkaufen dieses spezielle Produkt nicht mehr.'; - txnItemCollection.get(0).setError(customErrorMessage, localizedErrorMessage); + // Override success/failure of a product easily by adding an error message to the product. + // Here is a sample code demonstrating how to set error messages for products. + // We are failing the first product in the response. + + // String customErrorMessage = 'We no longer sell this particular product.'; + // String localizedErrorMessage = 'Wir verkaufen dieses spezielle Produkt nicht mehr.'; + // txnItemCollection.get(0).setError(customErrorMessage, localizedErrorMessage); } return txnResponse; } From 2550e0265023000d8f040b7959255a7544fbaf0c Mon Sep 17 00:00:00 2001 From: Adarsh Jain <84846989+adarshjain-sf@users.noreply.github.com> Date: Tue, 30 Apr 2024 15:33:52 +0530 Subject: [PATCH 32/69] Update comments --- .../service/classes/PricingServiceSample.cls | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/commerce/domain/pricing/service/classes/PricingServiceSample.cls b/commerce/domain/pricing/service/classes/PricingServiceSample.cls index 38426c2..c7a7a3c 100644 --- a/commerce/domain/pricing/service/classes/PricingServiceSample.cls +++ b/commerce/domain/pricing/service/classes/PricingServiceSample.cls @@ -138,15 +138,18 @@ public class PricingServiceSample extends commercestorepricing.PricingService { txnResponse.getTotalProductAmount() + txnResponse.getTotalAdjustmentAmount() ); + /** + * Override success/failure of a product easily by adding an error message to the product. + * Here is a sample code demonstrating how to set error messages for products. + * We are failing the first product in the response. + * if (!txnItemCollection.isEmpty()) { - // Override success/failure of a product easily by adding an error message to the product. - // Here is a sample code demonstrating how to set error messages for products. - // We are failing the first product in the response. - - // String customErrorMessage = 'We no longer sell this particular product.'; - // String localizedErrorMessage = 'Wir verkaufen dieses spezielle Produkt nicht mehr.'; - // txnItemCollection.get(0).setError(customErrorMessage, localizedErrorMessage); + String customErrorMessage = 'We no longer sell this particular product.'; + String localizedErrorMessage = 'Wir verkaufen dieses spezielle Produkt nicht mehr.'; + txnItemCollection.get(0).setError(customErrorMessage, localizedErrorMessage); } + */ + return txnResponse; } From 95ca63f4958232517faab8d98f33ea82cbbede81 Mon Sep 17 00:00:00 2001 From: Adarsh Jain <84846989+adarshjain-sf@users.noreply.github.com> Date: Tue, 30 Apr 2024 21:06:37 +0530 Subject: [PATCH 33/69] Review comments --- .../domain/pricing/service/classes/PricingServiceSample.cls | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/commerce/domain/pricing/service/classes/PricingServiceSample.cls b/commerce/domain/pricing/service/classes/PricingServiceSample.cls index c7a7a3c..ec3d174 100644 --- a/commerce/domain/pricing/service/classes/PricingServiceSample.cls +++ b/commerce/domain/pricing/service/classes/PricingServiceSample.cls @@ -100,6 +100,9 @@ public class PricingServiceSample extends commercestorepricing.PricingService { // amount, total adjustment amount and total amount. Item level - line id, product id, unit price, // list price, unit pricebook entry id, unit adjustment amount, total line amount, total adjustment // amount, total price, and total list price. + // + // Caution: If you're overriding fields containing Salesforce IDs, ensure that they are valid IDs. Otherwise, + // subsequent operations may fail unexpectedly. public override commercestorepricing.TransactionalPricingResponse processTransactionalPrice( commercestorepricing.TransactionalPricingRequest request2 ) { @@ -142,13 +145,14 @@ public class PricingServiceSample extends commercestorepricing.PricingService { * Override success/failure of a product easily by adding an error message to the product. * Here is a sample code demonstrating how to set error messages for products. * We are failing the first product in the response. + * Warning: Uncommenting the code below will cause cart operations to fail. * if (!txnItemCollection.isEmpty()) { String customErrorMessage = 'We no longer sell this particular product.'; String localizedErrorMessage = 'Wir verkaufen dieses spezielle Produkt nicht mehr.'; txnItemCollection.get(0).setError(customErrorMessage, localizedErrorMessage); } - */ + */ return txnResponse; } From 0f241ebb8136ef8502787a0dc43de0572b652cbc Mon Sep 17 00:00:00 2001 From: Adarsh Jain <84846989+adarshjain-sf@users.noreply.github.com> Date: Tue, 30 Apr 2024 21:52:38 +0530 Subject: [PATCH 34/69] Update PricingServiceSample.cls --- .../service/classes/PricingServiceSample.cls | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/commerce/domain/pricing/service/classes/PricingServiceSample.cls b/commerce/domain/pricing/service/classes/PricingServiceSample.cls index ec3d174..450f653 100644 --- a/commerce/domain/pricing/service/classes/PricingServiceSample.cls +++ b/commerce/domain/pricing/service/classes/PricingServiceSample.cls @@ -146,13 +146,12 @@ public class PricingServiceSample extends commercestorepricing.PricingService { * Here is a sample code demonstrating how to set error messages for products. * We are failing the first product in the response. * Warning: Uncommenting the code below will cause cart operations to fail. - * - if (!txnItemCollection.isEmpty()) { - String customErrorMessage = 'We no longer sell this particular product.'; - String localizedErrorMessage = 'Wir verkaufen dieses spezielle Produkt nicht mehr.'; - txnItemCollection.get(0).setError(customErrorMessage, localizedErrorMessage); - } - */ + */ + // if (!txnItemCollection.isEmpty()) { + // String customErrorMessage = 'We no longer sell this particular product.'; + // String localizedErrorMessage = 'Wir verkaufen dieses spezielle Produkt nicht mehr.'; + // txnItemCollection.get(0).setError(customErrorMessage, localizedErrorMessage); + // } return txnResponse; } From fe5d211a385dd8467881f7f909b88034b752f895 Mon Sep 17 00:00:00 2001 From: bsrilok Date: Sun, 12 May 2024 15:54:50 +0530 Subject: [PATCH 35/69] @W-15346861-addressed-comments --- .../classes/TaxCartCalculatorSample.cls | 46 +++++++-------- .../classes/TaxCartCalculatorSampleTest.cls | 58 +++++++++---------- 2 files changed, 48 insertions(+), 56 deletions(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index 59f1dbb..0bf35fc 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -1,39 +1,33 @@ // This class facilitates tax calculation either by invoking an external service or by implementing the tax calculation logic internally. -// The resulting tax values and adjustments are then stored in a Cart Data Transfer Object (DTO), references to both code implementations provided +// The resulting tax values and adjustments are then stored in a Cart Data Transfer Object (DTO), references to both code implementations provided. +// This apex class will only consider first delivery group for calculating taxes though multiple delivery groups associated with cart // For a tax calculator extension to be processed by the checkout flow, you must implement the CartExtension.TaxCartCalculator class. public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { - // You MUST change this to be your service or you must launch your own Third Party Service and add the host in Setup | Security | Remote site settings. - private static String externalTaxHost = 'https://example.com'; - - // You MUST change the useExternalService to True if you want to use the Third Party Service. - private static Boolean useExternalService = false; - - public virtual override void calculate(CartExtension.CartCalculateCalculatorRequest request) { - CartExtension.Cart cart = request.getCart(); - CartExtension.CartValidationOutputList cartValidationOutputCollection = cart.getCartValidationOutputs(); - try{ + // You MUST change this to be your service or you must launch your own Third Party Service and add the host in Setup | Security | Remote site settings. + private static String externalTaxHost = 'https://example.com'; + + // You MUST change the useExternalService to True if you want to use the Third Party Service. + private static Boolean useExternalService = false; + + public virtual override void calculate(CartExtension.CartCalculateCalculatorRequest request) { + CartExtension.Cart cart = request.getCart(); + CartExtension.CartValidationOutputList cartValidationOutputCollection = cart.getCartValidationOutputs(); + try{ + // Clean up CVO based on tax. When new tax calculator request comes, we need to clean up + // previous CVOs as they have been previously handled by the Cart Calculate API. + for (Integer i = (cartValidationOutputCollection.size() - 1); i >= 0; i--) { + CartExtension.CartValidationOutput cvo = cartValidationOutputCollection.get(i); + if (cvo.getType() == CartExtension.CartValidationOutputTypeEnum.TAXES) { + cartValidationOutputCollection.remove(cvo); + } + } Integer cartItemIdSeq = 0; if (!isValidCart(cart)){ - CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.TAXES, - CartExtension.CartValidationOutputLevelEnum.ERROR); - cvo.setMessage('There was a problem with delivery address in request.'); - cartValidationOutputCollection.add(cvo); return; } - // Clean up CVO based on tax. When new tax calculator request comes, we need to clean up - // previous CVOs as they have been previously handled by the Cart Calculate API. - for (Integer i = (cartValidationOutputCollection.size() - 1); i >= 0; i--) { - CartExtension.CartValidationOutput cvo = cartValidationOutputCollection.get(i); - if (cvo.getType() == CartExtension.CartValidationOutputTypeEnum.TAXES) { - cartValidationOutputCollection.remove(cvo); - } - } - - // There should be one delivery group per cart. CartExtension.CartDeliveryGroupList cartDeliveryGroups = cart.getCartDeliveryGroups(); CartExtension.CartDeliveryGroup cartDeliveryGroup = cartDeliveryGroups.get(0); diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls index 2cb3609..851c75e 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls @@ -21,28 +21,28 @@ public class TaxCartCalculatorSampleTest { @IsTest static void testCalculate_withEmptyDeliveryAddress() { - CartExtension.Cart cartReq = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.ACTIVE); - - CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cartReq, CartExtension.OptionalBuyerActionDetails.empty()); - - // Call the calculate method + // Arrange + CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.ACTIVE); + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty()); TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + + // Act Test.startTest(); calculator.calculate(request); Test.stopTest(); - CartExtension.Cart cart = request.getCart(); + + // Assert + cart = request.getCart(); CartExtension.CartItemList cartItemCollection = cart.getCartItems(); Assert.areEqual(CART_ITEM1_NAME, cartItemCollection.get(0).getName()); - CartExtension.CartValidationOutputList cartValidationOutputCollection = cart.getCartValidationOutputs(); - Assert.areEqual(1, cartValidationOutputCollection.size()); - Assert.areEqual('There was a problem with delivery address in request.', cartValidationOutputCollection.get(0).getMessage()); + Assert.areEqual(0, cartItemCollection.get(0).getCartTaxes().size()); } @IsTest static void testCalculate_withZeroPrice() { - CartExtension.Cart cartReq = arrangeAndLoadCartWithSpecifiedStatusAndDeliveryAddress(CartExtension.CartStatusEnum.ACTIVE); - - CartExtension.CartDeliveryGroup deliveryGroup = cartReq.getCartDeliveryGroups().get(0); + // Arrange + CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatus(CartExtension.CartStatusEnum.ACTIVE); + CartExtension.CartDeliveryGroup deliveryGroup = cart.getCartDeliveryGroups().get(0); deliveryGroup.setDeliverToStreet('newStreet'); deliveryGroup.setDeliverToCity('newCity'); deliveryGroup.setDeliverToState('Washington'); @@ -51,20 +51,19 @@ public class TaxCartCalculatorSampleTest { deliveryGroup.setDeliverToLatitude(48.1); deliveryGroup.setDeliverToLongitude(33.2); deliveryGroup.setDeliverToGeocodeAccuracy(null); - - - CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cartReq, CartExtension.OptionalBuyerActionDetails.empty()); - - + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty()); TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + + // Act Test.startTest(); calculator.calculate(request); Test.stopTest(); - CartExtension.Cart cart = request.getCart(); + // Assert + cart = request.getCart(); CartExtension.CartItemList cartItemCollection = cart.getCartItems(); Iterator cartItemCollectionIterator = cartItemCollection.iterator(); - while (cartItemCollectionIterator.hasNext()) { + while (cartItemCollectionIterator.hasNext()) { CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); Assert.areEqual(0.00, cartItem.getNetUnitPrice()); Assert.areEqual(0.00, cartItem.getGrossUnitPrice()); @@ -73,9 +72,9 @@ public class TaxCartCalculatorSampleTest { @IsTest static void testCalculate_withDeliveryAddress() { - CartExtension.Cart cartReq = arrangeAndLoadCartWithSpecifiedStatusAndDeliveryAddress(CartExtension.CartStatusEnum.ACTIVE); - - CartExtension.CartDeliveryGroup deliveryGroup = cartReq.getCartDeliveryGroups().get(0); + // Arrange + CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatus(CartExtension.CartStatusEnum.ACTIVE); + CartExtension.CartDeliveryGroup deliveryGroup = cart.getCartDeliveryGroups().get(0); deliveryGroup.setDeliverToStreet('newStreet'); deliveryGroup.setDeliverToCity('newCity'); deliveryGroup.setDeliverToState('Washington'); @@ -84,18 +83,17 @@ public class TaxCartCalculatorSampleTest { deliveryGroup.setDeliverToLatitude(48.1); deliveryGroup.setDeliverToLongitude(33.2); deliveryGroup.setDeliverToGeocodeAccuracy(null); - - cartReq.getCartItems().get(0).setTotalPrice(100.00); - - CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cartReq, CartExtension.OptionalBuyerActionDetails.empty()); - - + cart.getCartItems().get(0).setTotalPrice(100.00); + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty()); TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + + // Act Test.startTest(); calculator.calculate(request); Test.stopTest(); - CartExtension.Cart cart = request.getCart(); + // Assert + cart = request.getCart(); CartExtension.CartItemList cartItemCollection = cart.getCartItems(); Iterator cartItemCollectionIterator = cartItemCollection.iterator(); while (cartItemCollectionIterator.hasNext()) { @@ -167,7 +165,7 @@ public class TaxCartCalculatorSampleTest { return CartExtension.CartTestUtil.getCart(cartId); } - private static CartExtension.Cart arrangeAndLoadCartWithSpecifiedStatusAndDeliveryAddress(CartExtension.CartStatusEnum cartStatus) { + private static CartExtension.Cart arrangeAndLoadCartWithSpecifiedStatus(CartExtension.CartStatusEnum cartStatus) { Id cartId = arrangeCartWithSpecifiedStatus(cartStatus); arrangeCartItemsWithDeliveryAddress(cartId); return CartExtension.CartTestUtil.getCart(cartId); From 337c8ef90f6b6c1d669b111532f7884cbbce006b Mon Sep 17 00:00:00 2001 From: bsrilok Date: Sun, 12 May 2024 16:39:28 +0530 Subject: [PATCH 36/69] @W-15346861-addressed-comments --- .../classes/TaxCartCalculatorSample.cls | 71 +++++++++---------- .../classes/TaxCartCalculatorSampleTest.cls | 4 +- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index 0bf35fc..c7daac7 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -23,13 +23,13 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { cartValidationOutputCollection.remove(cvo); } } - Integer cartItemIdSeq = 0; - if (!isValidCart(cart)){ - return; - } + Integer cartItemIdSeq = 0; + if (!isValidCart(cart)){ + return; + } - CartExtension.CartDeliveryGroupList cartDeliveryGroups = cart.getCartDeliveryGroups(); - CartExtension.CartDeliveryGroup cartDeliveryGroup = cartDeliveryGroups.get(0); + CartExtension.CartDeliveryGroupList cartDeliveryGroups = cart.getCartDeliveryGroups(); + CartExtension.CartDeliveryGroup cartDeliveryGroup = cartDeliveryGroups.get(0); // Map cart ID to cart item with type Product. CartExtension.CartItemList cartItemCollection = cart.getCartItems(); @@ -120,14 +120,14 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { if (cartItem.getCartTaxes().size() > 0) { cartItem.getCartTaxes().remove(cartItem.getCartTaxes().get(0)); } - Iterator cartItemPriceAdjustmentsIterator = cartItem.getCartItemPriceAdjustments().iterator(); - while(cartItemPriceAdjustmentsIterator.hasNext()) { - CartExtension.CartTaxList cipaTaxes = cartItemPriceAdjustmentsIterator.next().getCartTaxes(); - if (cipaTaxes.size() > 0) { - cipaTaxes.remove(cipaTaxes.get(0)); - } - } - isCartItemModified = true; + Iterator cartItemPriceAdjustmentsIterator = cartItem.getCartItemPriceAdjustments().iterator(); + while(cartItemPriceAdjustmentsIterator.hasNext()) { + CartExtension.CartTaxList cipaTaxes = cartItemPriceAdjustmentsIterator.next().getCartTaxes(); + if (cipaTaxes.size() > 0) { + cipaTaxes.remove(cipaTaxes.get(0)); + } + } + isCartItemModified = true; } // If there are no existing cart tax entries in the cart item that indicates cart item was @@ -146,23 +146,23 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { cartTax.setTaxRate(String.valueOf(taxDetailsToCartId.getRate())); cartTaxCollection.add(cartTax); - // Add adjustment taxes to cartItemAdjustments of cartItem and create CartTaxDto entries - // for all promotion adjustments. + // Add adjustment taxes to cartItemAdjustments of cartItem and create CartTaxDto entries + // for all promotion adjustments. if (taxDetailsToCartId.getItemizedPromotionTaxAmounts() != null && !(taxDetailsToCartId.getItemizedPromotionTaxAmounts().isEmpty())){ for (CartAdjustment cipaTax : taxDetailsToCartId.getItemizedPromotionTaxAmounts()) { CartExtension.CartTax promoTax = new CartExtension.CartTax( CartExtension.TaxTypeEnum.ESTIMATED, cipaTax.getAmount(), taxDetailsToCartId.getTaxName()); - promoTax.setTaxRate(String.valueOf(taxDetailsToCartId.getRate())); - CartExtension.cartItemPriceAdjustment adj = getAdjustmentById( - cartItem.getCartItemPriceAdjustments(), cipaTax.getId()); - if (adj != null) { - adj.getCartTaxes().add(promoTax); - } - } - } - } - } + promoTax.setTaxRate(String.valueOf(taxDetailsToCartId.getRate())); + CartExtension.cartItemPriceAdjustment adj = getAdjustmentById( + cartItem.getCartItemPriceAdjustments(), cipaTax.getId()); + if (adj != null) { + adj.getCartTaxes().add(promoTax); + } + } + } + } + } // If there are shipping items, add tax for them as well for (String cartItemId : taxDataShippingItems.keySet()) { @@ -188,16 +188,15 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { cartTaxCollection.add(cartTax); } } - } catch(Exception e){ - CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.TAXES, - CartExtension.CartValidationOutputLevelEnum.ERROR); - cvo.setMessage('There was a problem. ' + e.getMessage()); - cartValidationOutputCollection.add(cvo); - return; - } - - } + } catch(Exception e){ + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.TAXES, + CartExtension.CartValidationOutputLevelEnum.ERROR); + cvo.setMessage('There was a problem. ' + e.getMessage()); + cartValidationOutputCollection.add(cvo); + return; + } + } // This simulates a call to an external tax service. Change this function based on your external service. // Transform tax data returned from service into cart ID to TaxData map. diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls index 851c75e..26bc085 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls @@ -59,7 +59,7 @@ public class TaxCartCalculatorSampleTest { calculator.calculate(request); Test.stopTest(); - // Assert + // Assert cart = request.getCart(); CartExtension.CartItemList cartItemCollection = cart.getCartItems(); Iterator cartItemCollectionIterator = cartItemCollection.iterator(); @@ -96,7 +96,7 @@ public class TaxCartCalculatorSampleTest { cart = request.getCart(); CartExtension.CartItemList cartItemCollection = cart.getCartItems(); Iterator cartItemCollectionIterator = cartItemCollection.iterator(); - while (cartItemCollectionIterator.hasNext()) { + while (cartItemCollectionIterator.hasNext()) { CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); Assert.areEqual(100.00, cartItem.getNetUnitPrice()); Assert.areEqual(108.00, cartItem.getGrossUnitPrice()); From a84a3427fce69da93c95b08c83d534cf265e14e2 Mon Sep 17 00:00:00 2001 From: bsrilok Date: Thu, 16 May 2024 13:09:30 +0530 Subject: [PATCH 37/69] @W-15346861-addressed-comments --- .../classes/TaxCartCalculatorSample.cls | 32 +-- .../classes/TaxCartCalculatorSampleTest.cls | 198 +++++++++++++++++- 2 files changed, 213 insertions(+), 17 deletions(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index c7daac7..8431597 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -24,6 +24,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { } } Integer cartItemIdSeq = 0; + Integer deliveryGroupIdSeq = 0; if (!isValidCart(cart)){ return; } @@ -46,17 +47,18 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT) { String cartItemId = (cartItem.getId() == null) ? String.valueOf(++cartItemIdSeq) : cartItem.getId(); cartItemByIdMap.put(cartItemId, cartItem); - } else if (cartItem.getType() == CartExtension.SalesItemTypeEnum.CHARGE) { + } else if (cartItem.getType() == CartExtension.SalesItemTypeEnum.CHARGE) { // for every delivery group there will be only one cart item with type shipping charge // Shipping cart items are uniquely identified using delivery group id. CartExtension.CartDeliveryGroup deliveryGroup = cartItem.getCartDeliveryGroup(); - chargeItemByDeliveryGroupIdMap.put(deliveryGroup.getId(), cartItem); + String deliveryGroupId = (deliveryGroup.getId() == null) ? String.valueOf(++deliveryGroupIdSeq) : deliveryGroup.getId(); + chargeItemByDeliveryGroupIdMap.put(deliveryGroupId, cartItem); } } Map taxData = null; Map taxDataShippingItems = null; if(useExternalService){ - // Get the tax rates and tax amounts from an external service for all given products and its adjustments. + // Get the tax rates and tax amounts from an external service for all given products and its adjustments. taxData = getTaxesFromExternalService( cartItemByIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); @@ -64,7 +66,6 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { taxDataShippingItems = getTaxesFromExternalService( chargeItemByDeliveryGroupIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); - } else{ taxData = getTaxesFromStaticResponse( cartItemByIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), @@ -75,12 +76,11 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); } - // TODO need to add cvo in case of tax data is null - if (taxData == null) { + if (taxData == null || taxData.size() == 0) { CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); - cvo.setMessage('No tax rates configured.'); + cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); cartValidationOutputCollection.add(cvo); return; } @@ -93,10 +93,10 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { if (taxDetails == null) { CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( CartExtension.CartValidationOutputTypeEnum.TAXES, - CartExtension.CartValidationOutputLevelEnum.INFO); - cvo.setMessage('No tax rates configured for this location.'); + CartExtension.CartValidationOutputLevelEnum.ERROR); + cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); cartValidationOutputCollection.add(cvo); - isCvoPresent = true; // need to take an call on this we can exit for first time only + isCvoPresent = true; } } if (isCvoPresent == true) @@ -192,7 +192,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); - cvo.setMessage('There was a problem. ' + e.getMessage()); + cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); cartValidationOutputCollection.add(cvo); return; } @@ -234,9 +234,9 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { private Map getTaxesFromStaticResponse(Map cartItemsMap, String state, String country, CartExtension.TaxLocaleTypeEnum taxType) { Double taxRate = 0.15; String responseJson = '{'; - for (String key : cartItemsMap.keySet()) { - CartExtension.CartItem cartItem = cartItemsMap.get(key); - String cartItemId = (cartItem.getId()==null) ? key : cartItem.getId(); + for (String cartItemIdOrDeliveryGroupId : cartItemsMap.keySet()) { + CartExtension.CartItem cartItem = cartItemsMap.get(cartItemIdOrDeliveryGroupId); + String cartItemId = (cartItem.getId()==null) ? cartItemIdOrDeliveryGroupId : cartItem.getId(); Double amount = cartItem.getTotalAmount()==null ? 0.00 : cartItem.getTotalAmount(); Double tierAdjustment = cartItem.getAdjustmentAmount()==null ? 0.00 : cartItem.getAdjustmentAmount(); @@ -288,7 +288,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { netUnitPrice = amount / quantity; } - responseJson = responseJson + '"'+ key +'":'; + responseJson = responseJson + '"'+ cartItemIdOrDeliveryGroupId +'":'; responseJson = responseJson + '{'; responseJson = responseJson + '"cartItemId": "' + cartItemId + '",'; responseJson = responseJson + '"amount": ' + cartItemTax + ','; @@ -311,7 +311,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { return populateTax(resultsFromStaticResponse); } - + // cart will be considered as valid if it contains one deliveryGroup with deliverToAddress for tax calculation. private boolean isValidCart(CartExtension.Cart cart){ CartExtension.CartDeliveryGroupList cartDeliveryGroups = cart.getCartDeliveryGroups(); if(cartDeliveryGroups == null || cartDeliveryGroups.size() == 0){ diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls index 26bc085..d52602f 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls @@ -34,10 +34,36 @@ public class TaxCartCalculatorSampleTest { // Assert cart = request.getCart(); CartExtension.CartItemList cartItemCollection = cart.getCartItems(); - Assert.areEqual(CART_ITEM1_NAME, cartItemCollection.get(0).getName()); Assert.areEqual(0, cartItemCollection.get(0).getCartTaxes().size()); } + @IsTest + static void testCalculate_withEmptyCartItems() { + // Arrange + CartExtension.Cart cart = arrangeAndLoadCartWithNoCartItems(CartExtension.CartStatusEnum.ACTIVE); + CartExtension.CartDeliveryGroup deliveryGroup = cart.getCartDeliveryGroups().get(0); + deliveryGroup.setDeliverToStreet('newStreet'); + deliveryGroup.setDeliverToCity('newCity'); + deliveryGroup.setDeliverToState('Washington'); + deliveryGroup.setDeliverToCountry('US'); + deliveryGroup.setDeliverToPostalCode('987654'); + deliveryGroup.setDeliverToLatitude(48.1); + deliveryGroup.setDeliverToLongitude(33.2); + deliveryGroup.setDeliverToGeocodeAccuracy(null); + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty()); + TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + + // Act + Test.startTest(); + calculator.calculate(request); + Test.stopTest(); + + // Assert + cart = request.getCart(); + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Assert.areEqual(0, cartItemCollection.size()); + } + @IsTest static void testCalculate_withZeroPrice() { // Arrange @@ -103,6 +129,112 @@ public class TaxCartCalculatorSampleTest { } } + @IsTest + static void testCalculate_withShippingChargeItem() { + // Arrange + CartExtension.Cart cart = arrangeAndLoadCartWithShippingChargeItem(CartExtension.CartStatusEnum.ACTIVE); + CartExtension.CartDeliveryGroup deliveryGroup = cart.getCartDeliveryGroups().get(0); + deliveryGroup.setDeliverToStreet('newStreet'); + deliveryGroup.setDeliverToCity('newCity'); + deliveryGroup.setDeliverToState('Washington'); + deliveryGroup.setDeliverToCountry('US'); + deliveryGroup.setDeliverToPostalCode('987654'); + deliveryGroup.setDeliverToLatitude(48.1); + deliveryGroup.setDeliverToLongitude(33.2); + deliveryGroup.setDeliverToGeocodeAccuracy(null); + cart.getCartItems().get(0).setTotalPrice(100.00); + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty()); + TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + + // Act + Test.startTest(); + calculator.calculate(request); + Test.stopTest(); + + // Assert + cart = request.getCart(); + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Assert.areEqual(2, cartItemCollection.size()); + } + + @IsTest + static void testCalculate_withNetPrice() { + // Arrange + CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatus(CartExtension.CartStatusEnum.ACTIVE); + CartExtension.CartDeliveryGroup deliveryGroup = cart.getCartDeliveryGroups().get(0); + deliveryGroup.setDeliverToStreet('newStreet'); + deliveryGroup.setDeliverToCity('newCity'); + deliveryGroup.setDeliverToState('Washington'); + deliveryGroup.setDeliverToCountry('US'); + deliveryGroup.setDeliverToPostalCode('987654'); + deliveryGroup.setDeliverToLatitude(48.1); + deliveryGroup.setDeliverToLongitude(33.2); + deliveryGroup.setDeliverToGeocodeAccuracy(null); + cart.getCartItems().get(0).setTotalPrice(100.00); + cart.getCartItems().get(0).setNetUnitPrice(200.00); + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty()); + TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + + // Act + Test.startTest(); + calculator.calculate(request); + Test.stopTest(); + + // Assert + cart = request.getCart(); + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Iterator cartItemCollectionIterator = cartItemCollection.iterator(); + while (cartItemCollectionIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); + Assert.areEqual(100.00, cartItem.getNetUnitPrice()); + Assert.areEqual(108.00, cartItem.getGrossUnitPrice()); + } + } + + @IsTest + static void testCalculate_withPriceAdjustments() { + // Arrange + CartExtension.Cart cart = arrangeAndLoadCartWithAdjustments(CartExtension.CartStatusEnum.ACTIVE); + CartExtension.CartDeliveryGroup deliveryGroup = cart.getCartDeliveryGroups().get(0); + deliveryGroup.setDeliverToStreet('newStreet'); + deliveryGroup.setDeliverToCity('newCity'); + deliveryGroup.setDeliverToState('Washington'); + deliveryGroup.setDeliverToCountry('US'); + deliveryGroup.setDeliverToPostalCode('987654'); + deliveryGroup.setDeliverToLatitude(48.1); + deliveryGroup.setDeliverToLongitude(33.2); + deliveryGroup.setDeliverToGeocodeAccuracy(null); + + CartExtension.CartItemPriceAdjustment newItemPriceAdjustment = new CartExtension.CartItemPriceAdjustment + (CartExtension.CartAdjustmentTargetTypeEnum.ITEM, 1, + CartExtension.PriceAdjustmentSourceEnum.PROMOTION, + CartExtension.AdjustmentTypeEnum.ADJUSTMENT_AMOUNT, -2, '0c8RO0000005qNPYAY'); + newItemPriceAdjustment.setPriority(2); + newItemPriceAdjustment.setAdjustmentValue(3); + CartExtension.CartItemPriceAdjustmentList cartItemPriceAdjustments = cart.getCartItems().get(0).getCartItemPriceAdjustments(); + cartItemPriceAdjustments.add(newItemPriceAdjustment); + + cart.getCartItems().get(0).setTotalPrice(100.00); + cart.getCartItems().get(0).setNetUnitPrice(200.00); + CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty()); + TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + + // Act + Test.startTest(); + calculator.calculate(request); + Test.stopTest(); + + // Assert + cart = request.getCart(); + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Iterator cartItemCollectionIterator = cartItemCollection.iterator(); + while (cartItemCollectionIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); + Assert.areEqual(33.666666666666664, cartItem.getNetUnitPrice()); + Assert.areEqual(36.36, cartItem.getGrossUnitPrice()); + } + } + /** * @description Create and return a WebCart with the specified status and 3 items. * @@ -165,6 +297,70 @@ public class TaxCartCalculatorSampleTest { return CartExtension.CartTestUtil.getCart(cartId); } + private static List arrangeOneCartItemsWithShippingChargeType(ID cartId) { + CartDeliveryGroup deliveryGroup = new CartDeliveryGroup(Name = DELIVERYGROUP_NAME, CartId = cartId); + insert deliveryGroup; + + CartItem cartItem1 = new CartItem( + Name = CART_ITEM1_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 3, + SKU = SKU1_NAME, + Type = CartExtension.SalesItemTypeEnum.CHARGE.name()); + insert cartItem1; + + CartItem cartItem2 = new CartItem( + Name = CART_ITEM2_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 3, + SKU = SKU2_NAME, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem2; + + return new List{cartItem1.Id, cartItem2.Id}; + } + + private static CartExtension.Cart arrangeAndLoadCartWithShippingChargeItem(CartExtension.CartStatusEnum cartStatus) { + Id cartId = arrangeCartWithSpecifiedStatus(cartStatus); + arrangeOneCartItemsWithShippingChargeType(cartId); + return CartExtension.CartTestUtil.getCart(cartId); + } + + private static List arrangeOneCartItemWithPriceAdjustments(ID cartId) { + CartDeliveryGroup deliveryGroup = new CartDeliveryGroup(Name = DELIVERYGROUP_NAME, CartId = cartId); + insert deliveryGroup; + + CartItem cartItem = new CartItem( + Name = CART_ITEM1_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 3, + SKU = SKU1_NAME, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem; + return new List{cartItem.Id}; + } + + private static CartExtension.Cart arrangeAndLoadCartWithAdjustments(CartExtension.CartStatusEnum cartStatus) { + Id cartId = arrangeCartWithSpecifiedStatus(cartStatus); + arrangeOneCartItemWithPriceAdjustments(cartId); + return CartExtension.CartTestUtil.getCart(cartId); + } + + private static List arrangeDeliveryGroup(ID cartId) { + CartDeliveryGroup deliveryGroup = new CartDeliveryGroup(Name = DELIVERYGROUP_NAME, CartId = cartId); + insert deliveryGroup; + return new List{}; + } + + private static CartExtension.Cart arrangeAndLoadCartWithNoCartItems(CartExtension.CartStatusEnum cartStatus) { + Id cartId = arrangeCartWithSpecifiedStatus(cartStatus); + arrangeDeliveryGroup(cartId); + return CartExtension.CartTestUtil.getCart(cartId); + } + private static CartExtension.Cart arrangeAndLoadCartWithSpecifiedStatus(CartExtension.CartStatusEnum cartStatus) { Id cartId = arrangeCartWithSpecifiedStatus(cartStatus); arrangeCartItemsWithDeliveryAddress(cartId); From 7edf37f07e2e303c36d7072978e14c2b853ec55c Mon Sep 17 00:00:00 2001 From: bsrilok Date: Thu, 16 May 2024 13:14:41 +0530 Subject: [PATCH 38/69] @W-15346861-addressed-comments --- .../tax/cart/calculator/classes/TaxCartCalculatorSample.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index 8431597..f0a97e2 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -47,7 +47,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT) { String cartItemId = (cartItem.getId() == null) ? String.valueOf(++cartItemIdSeq) : cartItem.getId(); cartItemByIdMap.put(cartItemId, cartItem); - } else if (cartItem.getType() == CartExtension.SalesItemTypeEnum.CHARGE) { // for every delivery group there will be only one cart item with type shipping charge + } else if (cartItem.getType() == CartExtension.SalesItemTypeEnum.CHARGE) { // Shipping cart items are uniquely identified using delivery group id. CartExtension.CartDeliveryGroup deliveryGroup = cartItem.getCartDeliveryGroup(); String deliveryGroupId = (deliveryGroup.getId() == null) ? String.valueOf(++deliveryGroupIdSeq) : deliveryGroup.getId(); From fe5f02a70473cc1a69f15bc69f3971f32f9323c4 Mon Sep 17 00:00:00 2001 From: bsrilok Date: Tue, 21 May 2024 15:21:55 +0530 Subject: [PATCH 39/69] @W-15346861-adddressed-comments --- .../classes/TaxCartCalculatorSample.cls | 54 ++++--------------- .../classes/TaxCartCalculatorSampleTest.cls | 6 +++ 2 files changed, 16 insertions(+), 44 deletions(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index f0a97e2..36b626f 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -62,7 +62,6 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { taxData = getTaxesFromExternalService( cartItemByIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); - taxDataShippingItems = getTaxesFromExternalService( chargeItemByDeliveryGroupIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); @@ -78,8 +77,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { if (taxData == null || taxData.size() == 0) { CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.TAXES, - CartExtension.CartValidationOutputLevelEnum.ERROR); + CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); cartValidationOutputCollection.add(cvo); return; @@ -87,20 +85,17 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { // If no tax details are returned for any cart item, add a cart validation output entry. If // any invalid scenario found then return. - boolean isCvoPresent = false; for (String cartItemId : cartItemByIdMap.keySet()) { TaxData taxDetails = taxData.get(cartItemId); if (taxDetails == null) { CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.TAXES, - CartExtension.CartValidationOutputLevelEnum.ERROR); + CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); cartValidationOutputCollection.add(cvo); - isCvoPresent = true; + return; } } - if (isCvoPresent == true) - return; + for (String cartItemId : taxData.keySet()) { TaxData taxDetailsToCartId = taxData.get(cartItemId); @@ -190,8 +185,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { } } catch(Exception e){ CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.TAXES, - CartExtension.CartValidationOutputLevelEnum.ERROR); + CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); cartValidationOutputCollection.add(cvo); return; @@ -203,31 +197,19 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { // Admin need to modify this function according to external service api contract. private Map getTaxesFromExternalService(Map cartItemByIdMap, String state, String country, CartExtension.TaxLocaleTypeEnum taxType) { - String requestURL = externalTaxHost+'/get-tax-rates-with-adjustments-post'; - String requestBody = - '{"state":"' + - state + - '", "country":"' + - country + - '", "taxType":"' + - taxType + - '", ' + - '"amountsBySKU":' + - JSON.serialize(cartItemByIdMap) + - '}'; + String requestBody = '{"state":"' + state + '", "country":"' + country + '", "taxType":"' + taxType + '", ' + + '"amountsBySKU":' +JSON.serialize(cartItemByIdMap) + '}'; Http http = new Http(); HttpRequest request = new HttpRequest(); - request.setEndpoint(requestURL); + request.setEndpoint(externalTaxHost+'/get-tax-rates-with-adjustments-post'); request.setMethod('POST'); request.setHeader('Content-Type', 'application/json'); request.setBody(requestBody); HttpResponse response = http.send(request); // If the request is successful, parse the JSON response. - if (response.getStatusCode() == 200) { - Map resultsFromExternalService = (Map) JSON.deserializeUntyped(response.getBody()); - return populateTax(resultsFromExternalService); - } + if (response.getStatusCode() == 200) + return populateTax((Map) JSON.deserializeUntyped(response.getBody())); return null; } @@ -402,17 +384,6 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { private Decimal grossUnitPrice; private Decimal netUnitPrice; - public TaxData() { - rate = 0.0; - amount = 0.0; - taxName = ''; - adjustmentTaxAmount = 0.0; - totalItemizedPromotionTaxAmount = 0.0; - itemizedPromotionTaxAmounts = null; - grossUnitPrice = 0.0; - netUnitPrice = 0.0; - } - public TaxData( Decimal rateObj, Decimal amountObj, @@ -470,11 +441,6 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { private String id; private Decimal amount; - public CartAdjustment() { - id = ''; - amount = 0.0; - } - public CartAdjustment(String idObj, Decimal taxAmountObj) { id = idObj; amount = taxAmountObj; diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls index d52602f..1915848 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSampleTest.cls @@ -80,6 +80,11 @@ public class TaxCartCalculatorSampleTest { CartExtension.CartCalculateCalculatorRequest request = new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty()); TaxCartCalculatorSample calculator = new TaxCartCalculatorSample(); + CartExtension.CartValidationOutputList cartValidationOutputCollection = cart.getCartValidationOutputs(); + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); + cartValidationOutputCollection.add(cvo); + // Act Test.startTest(); calculator.calculate(request); @@ -88,6 +93,7 @@ public class TaxCartCalculatorSampleTest { // Assert cart = request.getCart(); CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Assert.areEqual(0, cart.getCartValidationOutputs().size()); Iterator cartItemCollectionIterator = cartItemCollection.iterator(); while (cartItemCollectionIterator.hasNext()) { CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); From 1a238d7a3b332f12a3c7ff93c872102d2de5c4a8 Mon Sep 17 00:00:00 2001 From: balaji-v Date: Thu, 30 May 2024 22:54:17 +0530 Subject: [PATCH 40/69] adding cart endpoint extension samples --- commerce/endpoint/cart/CartItemCollectionExtensionSample.cls | 5 +++++ .../cart/CartItemCollectionExtensionSample.cls-meta.xml | 5 +++++ commerce/endpoint/cart/CartItemExtensionSample.cls | 5 +++++ commerce/endpoint/cart/CartItemExtensionSample.cls-meta.xml | 5 +++++ 4 files changed, 20 insertions(+) create mode 100644 commerce/endpoint/cart/CartItemCollectionExtensionSample.cls create mode 100644 commerce/endpoint/cart/CartItemCollectionExtensionSample.cls-meta.xml create mode 100644 commerce/endpoint/cart/CartItemExtensionSample.cls create mode 100644 commerce/endpoint/cart/CartItemExtensionSample.cls-meta.xml diff --git a/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls b/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls new file mode 100644 index 0000000..3000be4 --- /dev/null +++ b/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls @@ -0,0 +1,5 @@ +public with sharing class CartItemCollectionExtensionSample { + public CartItemCollectionExtensionSample() { + + } +} \ No newline at end of file diff --git a/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls-meta.xml b/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls-meta.xml new file mode 100644 index 0000000..019e850 --- /dev/null +++ b/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls-meta.xml @@ -0,0 +1,5 @@ + + + 59.0 + Active + \ No newline at end of file diff --git a/commerce/endpoint/cart/CartItemExtensionSample.cls b/commerce/endpoint/cart/CartItemExtensionSample.cls new file mode 100644 index 0000000..8a40558 --- /dev/null +++ b/commerce/endpoint/cart/CartItemExtensionSample.cls @@ -0,0 +1,5 @@ +public with sharing class CartItemExtensionSample { + public CartItemExtensionSample() { + + } +} \ No newline at end of file diff --git a/commerce/endpoint/cart/CartItemExtensionSample.cls-meta.xml b/commerce/endpoint/cart/CartItemExtensionSample.cls-meta.xml new file mode 100644 index 0000000..019e850 --- /dev/null +++ b/commerce/endpoint/cart/CartItemExtensionSample.cls-meta.xml @@ -0,0 +1,5 @@ + + + 59.0 + Active + \ No newline at end of file From 78beff666a457b33f09e1df382318a43d4f206be Mon Sep 17 00:00:00 2001 From: balaji-v Date: Thu, 30 May 2024 23:04:33 +0530 Subject: [PATCH 41/69] adding cart endpoint samples --- .../CartItemCollectionExtensionSample.cls | 44 ++++++++++++++++++- ...ItemCollectionExtensionSample.cls-meta.xml | 2 +- .../endpoint/cart/CartItemExtensionSample.cls | 33 +++++++++++++- .../cart/CartItemExtensionSample.cls-meta.xml | 2 +- 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls b/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls index 3000be4..870c0ae 100644 --- a/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls +++ b/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls @@ -1,5 +1,45 @@ -public with sharing class CartItemCollectionExtensionSample { - public CartItemCollectionExtensionSample() { +/** + * Sample extension for updating the quantity of a cart item while adding an item to the cart. + * This corresponds to the endpoint /commerce/webstores/${webstoreId}/carts/${activeCartOrId}/cart-items + * and is identified by the EPN Commerce_Endpoint_Cart_ItemCollection for registration/mapping. + */ +public with sharing class CartItemCollectionExtensionSample extends ConnectApi.BaseEndpointExtension { + /** + * Overrides the beforePost method to update the quantity of a cart item. + * + * @param request The endpoint extension request containing cart item data. + * @return The modified endpoint extension request with updated quantity. + */ + public override ConnectApi.EndpointExtensionRequest beforePost(ConnectApi.EndpointExtensionRequest request) { + System.debug('We are in the beforePost entry method of Commerce_Endpoint_Cart_ItemCollection extension'); + + /** + * Retrieve the cart item input from the request. This is a deep copy. + * The parameter name can be found in the documentation: + * https://developer.salesforce.com/docs/commerce/salesforce-commerce/guide/extensions.html + */ + ConnectApi.CartItemInput cartItemInput = (ConnectApi.CartItemInput) request.getParam('cartItemInput'); + + // Check if cartItemInput is not null + if (cartItemInput != null) { + // Retrieve the quantity from the cart item input + String quantity = cartItemInput.getQuantity(); + + // Check if quantity is not null + if (quantity != null) { + // Set the quantity to 5 + cartItemInput.setQuantity('5'); + } + + /** + * Update the cart item input parameter in the request. + * The request needs to be set in the "before" methods since what is returned is a deep copy. + */ + request.setParam('cartItemInput', cartItemInput); + } + + // Return the modified request + return request; } } \ No newline at end of file diff --git a/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls-meta.xml b/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls-meta.xml index 019e850..ba7ea1b 100644 --- a/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls-meta.xml +++ b/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls-meta.xml @@ -1,5 +1,5 @@ - 59.0 + 62.0 Active \ No newline at end of file diff --git a/commerce/endpoint/cart/CartItemExtensionSample.cls b/commerce/endpoint/cart/CartItemExtensionSample.cls index 8a40558..aedbb2f 100644 --- a/commerce/endpoint/cart/CartItemExtensionSample.cls +++ b/commerce/endpoint/cart/CartItemExtensionSample.cls @@ -1,5 +1,34 @@ -public with sharing class CartItemExtensionSample { - public CartItemExtensionSample() { +/** + * Sample extension for handling the edit operation on a cart item. + * This corresponds to an endpoint /commerce/webstores/${webstoreId}/carts/${activeCartOrId}/cart-items/${cartItemId} + * and is identified by the EPN Commerce_Endpoint_Cart_Item for registration/mapping. + */ +public with sharing class CartItemExtensionSample extends ConnectApi.BaseEndpointExtension { + /** + * Overrides the afterPatch method to update the currency ISO code of a cart item. + * + * @param response The endpoint extension response containing cart item data. + * @param request The endpoint extension request. + * @return The modified endpoint extension response with updated currency ISO code. + */ + public override ConnectApi.EndpointExtensionResponse afterPatch(ConnectApi.EndpointExtensionResponse response, ConnectApi.EndpointExtensionRequest request) { + System.debug('Entering the afterPatch method of Commerce_Endpoint_Cart_Item extension'); + + /** + * Retrieve the cart item from the response object + * More details on the response object can be found in the documentation: + * https://developer.salesforce.com/docs/commerce/salesforce-commerce/guide/extensions.html#connectapiendpointextensionresponse + * */ + ConnectApi.CartItem cartItem = (ConnectApi.CartItem)response.getResponseObject(); + + // Check if cartItem is not null + if (cartItem != null) { + // Set the currency ISO code to AED + cartItem.setCurrencyIsoCode('AED'); + } + + // Return the modified response + return response; } } \ No newline at end of file diff --git a/commerce/endpoint/cart/CartItemExtensionSample.cls-meta.xml b/commerce/endpoint/cart/CartItemExtensionSample.cls-meta.xml index 019e850..ba7ea1b 100644 --- a/commerce/endpoint/cart/CartItemExtensionSample.cls-meta.xml +++ b/commerce/endpoint/cart/CartItemExtensionSample.cls-meta.xml @@ -1,5 +1,5 @@ - 59.0 + 62.0 Active \ No newline at end of file From b97fd25d16b3311d631f789effdb6fbbf6fe806b Mon Sep 17 00:00:00 2001 From: balaji-v Date: Sat, 1 Jun 2024 02:59:33 +0530 Subject: [PATCH 42/69] review comments fix --- .../CartItemCollectionExtensionSample.cls | 26 +++++++-- .../endpoint/cart/CartItemExtensionSample.cls | 56 +++++++++++++++++-- .../endpoint/cart/InvalidFormatException.cls | 4 ++ .../cart/InvalidFormatException.cls-meta.xml | 5 ++ 4 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 commerce/endpoint/cart/InvalidFormatException.cls create mode 100644 commerce/endpoint/cart/InvalidFormatException.cls-meta.xml diff --git a/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls b/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls index 870c0ae..34a2426 100644 --- a/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls +++ b/commerce/endpoint/cart/CartItemCollectionExtensionSample.cls @@ -1,12 +1,15 @@ /** - * Sample extension for updating the quantity of a cart item while adding an item to the cart. + * Sample extension for enforcing quantity constraints on a cart item while adding an item to the cart. * This corresponds to the endpoint /commerce/webstores/${webstoreId}/carts/${activeCartOrId}/cart-items * and is identified by the EPN Commerce_Endpoint_Cart_ItemCollection for registration/mapping. */ public with sharing class CartItemCollectionExtensionSample extends ConnectApi.BaseEndpointExtension { + // Define a constant for the maximum quantity allowed + private static final Integer MAX_QUANTITY = 100; + /** - * Overrides the beforePost method to update the quantity of a cart item. + * Overrides the beforePost method to enforce a maximum quantity of a cart item. * * @param request The endpoint extension request containing cart item data. * @return The modified endpoint extension request with updated quantity. @@ -24,12 +27,23 @@ public with sharing class CartItemCollectionExtensionSample extends ConnectApi.B // Check if cartItemInput is not null if (cartItemInput != null) { // Retrieve the quantity from the cart item input - String quantity = cartItemInput.getQuantity(); + String quantityStr = cartItemInput.getQuantity(); // Check if quantity is not null - if (quantity != null) { - // Set the quantity to 5 - cartItemInput.setQuantity('5'); + if (quantityStr != null) { + try { + // Convert quantity from String to Integer + Integer quantity = Integer.valueOf(quantityStr); + + // Enforce the maximum quantity + if (quantity > MAX_QUANTITY) { + quantity = MAX_QUANTITY; + // Set the enforced quantity back to the cart item input + cartItemInput.setQuantity(quantity.toString()); + } + } catch (Exception e) { + throw new InvalidFormatException('Invalid quantity format: ' + quantityStr); + } } /** diff --git a/commerce/endpoint/cart/CartItemExtensionSample.cls b/commerce/endpoint/cart/CartItemExtensionSample.cls index aedbb2f..a4f2105 100644 --- a/commerce/endpoint/cart/CartItemExtensionSample.cls +++ b/commerce/endpoint/cart/CartItemExtensionSample.cls @@ -1,16 +1,32 @@ /** - * Sample extension for handling the edit operation on a cart item. + * Sample extension for updating the listPrice of a cart item based on the currencyIsoCode during edit operations. * This corresponds to an endpoint /commerce/webstores/${webstoreId}/carts/${activeCartOrId}/cart-items/${cartItemId} * and is identified by the EPN Commerce_Endpoint_Cart_Item for registration/mapping. */ public with sharing class CartItemExtensionSample extends ConnectApi.BaseEndpointExtension { + // Define a constant for the USD currencyIsoCode + private static final String USD_CURRENCY_ISO_CODE = 'USD'; + + // Define a constant for the AED currencyIsoCode + private static final String AED_CURRENCY_ISO_CODE = 'AED'; + + // Define a constant for the adjustment when the currencyIsoCode is USD + private static final Decimal USD_ADJUSTMENT = 10; + + // Define a constant for the adjustment when the currencyIsoCode is AED + private static final Decimal AED_ADJUSTMENT = 5; + + // Define a constant for the adjustment for currencyIsoCodes other than USD and AED + private static final Decimal ADJUSTMENT = 2; + + /** - * Overrides the afterPatch method to update the currency ISO code of a cart item. + * Overrides the afterPatch method to update the listPrice of a cart item based on the currencyIsoCode. * * @param response The endpoint extension response containing cart item data. * @param request The endpoint extension request. - * @return The modified endpoint extension response with updated currency ISO code. + * @return The modified endpoint extension response with updated listPrice. */ public override ConnectApi.EndpointExtensionResponse afterPatch(ConnectApi.EndpointExtensionResponse response, ConnectApi.EndpointExtensionRequest request) { System.debug('Entering the afterPatch method of Commerce_Endpoint_Cart_Item extension'); @@ -24,8 +40,38 @@ public with sharing class CartItemExtensionSample extends ConnectApi.BaseEndpoin // Check if cartItem is not null if (cartItem != null) { - // Set the currency ISO code to AED - cartItem.setCurrencyIsoCode('AED'); + // Check if listPrice and currencyIsoCode are not null + if (cartItem.getListPrice() != null && cartItem.getCurrencyIsoCode() != null) { + + // Retrieve the currencyIsoCode from the cart item + String currencyCode = cartItem.getCurrencyIsoCode(); + + // Retrieve the listPrice from the cart item + String listPriceStr = cartItem.getListPrice(); + + try { + // Convert listPrice from String to Decimal + Decimal listPrice = Decimal.valueOf(listPriceStr); + + // Adjust the listPrice based on the currencyIsoCode + switch on currencyCode { + when USD_CURRENCY_ISO_CODE { + listPrice += USD_ADJUSTMENT; + } + when AED_CURRENCY_ISO_CODE { + listPrice += AED_ADJUSTMENT; + } + when else { + listPrice += ADJUSTMENT; + } + } + + // Set the adjusted listPrice back to the cart item + cartItem.setListPrice(String.valueOf(listPrice)); + } catch (Exception e) { + throw new InvalidFormatException('Invalid listPrice format: ' + listPriceStr); + } + } } // Return the modified response diff --git a/commerce/endpoint/cart/InvalidFormatException.cls b/commerce/endpoint/cart/InvalidFormatException.cls new file mode 100644 index 0000000..fc1acf9 --- /dev/null +++ b/commerce/endpoint/cart/InvalidFormatException.cls @@ -0,0 +1,4 @@ +/** + * Custom exception class for handling invalid format exceptions. + */ +public with sharing class InvalidFormatException extends Exception{} \ No newline at end of file diff --git a/commerce/endpoint/cart/InvalidFormatException.cls-meta.xml b/commerce/endpoint/cart/InvalidFormatException.cls-meta.xml new file mode 100644 index 0000000..ba7ea1b --- /dev/null +++ b/commerce/endpoint/cart/InvalidFormatException.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + \ No newline at end of file From 4faa153e1b87674d7c118276e66fcf8639002bb1 Mon Sep 17 00:00:00 2001 From: balaji-v Date: Mon, 3 Jun 2024 12:34:24 +0530 Subject: [PATCH 43/69] adding if else for currency handling --- .../endpoint/cart/CartItemExtensionSample.cls | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/commerce/endpoint/cart/CartItemExtensionSample.cls b/commerce/endpoint/cart/CartItemExtensionSample.cls index a4f2105..dcc953a 100644 --- a/commerce/endpoint/cart/CartItemExtensionSample.cls +++ b/commerce/endpoint/cart/CartItemExtensionSample.cls @@ -54,16 +54,12 @@ public with sharing class CartItemExtensionSample extends ConnectApi.BaseEndpoin Decimal listPrice = Decimal.valueOf(listPriceStr); // Adjust the listPrice based on the currencyIsoCode - switch on currencyCode { - when USD_CURRENCY_ISO_CODE { - listPrice += USD_ADJUSTMENT; - } - when AED_CURRENCY_ISO_CODE { - listPrice += AED_ADJUSTMENT; - } - when else { - listPrice += ADJUSTMENT; - } + if (currencyCode == USD_CURRENCY_ISO_CODE) { + listPrice += USD_ADJUSTMENT; + } else if (currencyCode == AED_CURRENCY_ISO_CODE) { + listPrice += AED_ADJUSTMENT; + } else { + listPrice += ADJUSTMENT; } // Set the adjusted listPrice back to the cart item From 162d2d2ade75ceb0b41e2217b5e4976e51f63d33 Mon Sep 17 00:00:00 2001 From: poddepally Date: Tue, 4 Jun 2024 21:22:10 +0530 Subject: [PATCH 44/69] Update debug logs This PR is to add additional debug logs to help customer troubleshooting. --- .../classes/TaxCartCalculatorSample.cls | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index 36b626f..1089aea 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -26,6 +26,11 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { Integer cartItemIdSeq = 0; Integer deliveryGroupIdSeq = 0; if (!isValidCart(cart)){ + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); + cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); + cartValidationOutputCollection.add(cvo); + System.debug('The cart provided is invalid for the tax calculator, cartId: ' + cart.getId()); return; } @@ -57,7 +62,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { Map taxData = null; Map taxDataShippingItems = null; - if(useExternalService){ + if (useExternalService) { // Get the tax rates and tax amounts from an external service for all given products and its adjustments. taxData = getTaxesFromExternalService( cartItemByIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), @@ -65,7 +70,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { taxDataShippingItems = getTaxesFromExternalService( chargeItemByDeliveryGroupIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); - } else{ + } else { taxData = getTaxesFromStaticResponse( cartItemByIdMap, cartDeliveryGroup.getDeliverToAddress().getState(), cartDeliveryGroup.getDeliverToAddress().getCountry(), cart.getTaxType()); @@ -80,6 +85,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); cartValidationOutputCollection.add(cvo); + System.debug('Empty response recieved from external service or static taxes generator.'); return; } @@ -92,6 +98,7 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); cartValidationOutputCollection.add(cvo); + System.debug('Tax details received from service response is null for cartItemId: ' + cartItemId); return; } } @@ -183,12 +190,13 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { cartTaxCollection.add(cartTax); } } - } catch(Exception e){ + } catch(Exception e) { CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); cartValidationOutputCollection.add(cvo); - return; + System.debug('Exception occurred, message:' + e.getMessage() + ', stacktrace: ' + e.getStackTraceString()); + throw e; } } From 4235e58405b72ede3177b4a3c2afe9fa3be0f0d2 Mon Sep 17 00:00:00 2001 From: balaji-v Date: Thu, 6 Jun 2024 21:43:01 +0530 Subject: [PATCH 45/69] empty commit to check CLA From 56334d073e0718e713fabef9cb39c8a7356e32a1 Mon Sep 17 00:00:00 2001 From: poddepally Date: Sat, 8 Jun 2024 17:01:02 +0530 Subject: [PATCH 46/69] Format the file Format the file --- .../cart/calculator/classes/TaxCartCalculatorSample.cls | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls index 1089aea..bfdff14 100644 --- a/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls +++ b/commerce/domain/tax/cart/calculator/classes/TaxCartCalculatorSample.cls @@ -27,10 +27,10 @@ public class TaxCartCalculatorSample extends CartExtension.TaxCartCalculator { Integer deliveryGroupIdSeq = 0; if (!isValidCart(cart)){ CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); - cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); - cartValidationOutputCollection.add(cvo); - System.debug('The cart provided is invalid for the tax calculator, cartId: ' + cart.getId()); + CartExtension.CartValidationOutputTypeEnum.TAXES, CartExtension.CartValidationOutputLevelEnum.ERROR); + cvo.setMessage('Something went wrong. Please contact Salesforce Admin or try again.'); + cartValidationOutputCollection.add(cvo); + System.debug('The cart provided is invalid for the tax calculator, cartId: ' + cart.getId()); return; } From 0ef89c597cb3b5cb3be650247ef01c3111a272ed Mon Sep 17 00:00:00 2001 From: skumar2 Date: Thu, 20 Jun 2024 18:17:16 +0530 Subject: [PATCH 47/69] Adding debug logs --- .../PricingCalculatorSample.cls | 4 + .../EstimatedActualPricingCalculator.cls | 94 ++++++++++--------- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/commerce/domain/pricing/cart/PricingBasicCalculator/PricingCalculatorSample.cls b/commerce/domain/pricing/cart/PricingBasicCalculator/PricingCalculatorSample.cls index d51e29a..99c2239 100644 --- a/commerce/domain/pricing/cart/PricingBasicCalculator/PricingCalculatorSample.cls +++ b/commerce/domain/pricing/cart/PricingBasicCalculator/PricingCalculatorSample.cls @@ -47,6 +47,7 @@ public class PricingCalculatorSample extends CartExtension.PricingCartCalculator CartExtension.CartValidationOutputLevelEnum.ERROR); cvo.setMessage('We are not able to process your cart. Please contact support.'); cart.getCartValidationOutputs().add(cvo); + System.debug('No Pricing data received'); return; } applyPricesToCartItems(cart, cartItems.iterator(), pricingDataMap); @@ -55,6 +56,7 @@ public class PricingCalculatorSample extends CartExtension.PricingCartCalculator // This is an example of throwing special type of Exception (CartCalculateRuntimeException). // Throwing this exception causes the rollback of all previously applied changes to the cart (in scope of given request) // and may not always be the best choice. + System.debug('Exception occurred, message:' + e.getMessage() + ', stacktrace: ' + e.getStackTraceString()); throw new CartExtension.CartCalculateRuntimeException('An integration error occurred in COMPUTE_PRICES. Contact your admin.'); } } @@ -151,6 +153,7 @@ public class PricingCalculatorSample extends CartExtension.PricingCartCalculator cartItem); cvo.setMessage('No price available for the SKU in the Cart.'); cart.getCartValidationOutputs().add(cvo); + System.debug('No price available for the SKU: ' + cartItem.getSku()); continue; } setPricingFieldsOnCart(cartItem, lineItemIdToPricingDetailsMap.get(cartItem.getSku())); @@ -195,6 +198,7 @@ public class PricingCalculatorSample extends CartExtension.PricingCartCalculator if (r.getStatusCode() != 200) { // return null in case of not successful response from 3rd party service + System.debug('Did not receive pricing data. Call to external service was not successful.'); return null; } diff --git a/commerce/domain/pricing/cart/calculator/EstimatedActualPricingCalculator.cls b/commerce/domain/pricing/cart/calculator/EstimatedActualPricingCalculator.cls index 065bf0d..4e37afd 100644 --- a/commerce/domain/pricing/cart/calculator/EstimatedActualPricingCalculator.cls +++ b/commerce/domain/pricing/cart/calculator/EstimatedActualPricingCalculator.cls @@ -45,55 +45,62 @@ public class EstimatedActualPricingCalculator extends CartExtension.PricingCartC } public virtual override void calculate(CartExtension.CartCalculateCalculatorRequest request) { - CartExtension.Cart cart = request.getCart(); + try { + CartExtension.Cart cart = request.getCart(); - if (cart.getStatus() == CartExtension.CartStatusEnum.ACTIVE) { - super.calculate(request); - return; - } - - Iterator cartItemsIterator = clearErrorsAndGetCartItemsIterator(cart, request.getOptionalBuyerActionDetails()); - - // Get the SKUs from each cart item that needs price calculations - Map skuToCartItem = new Map(); - while (cartItemsIterator.hasNext()) { - CartExtension.CartItem cartItem = cartItemsIterator.next(); - skuToCartItem.put(cartItem.getSku(), cartItem); - } - - Map pricingDataMap = getPricingDataFromExternalServiceForSkus(skuToCartItem.keySet()); - if (pricingDataMap == Null) { - // No data returned means there is an issue with underlying 3rd party service. Populate generic error message for the Buyer. - CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.PRICING, - CartExtension.CartValidationOutputLevelEnum.ERROR); - String errorMessage = getGenericErrorMessage(); - cvo.setMessage(errorMessage); - cart.getCartValidationOutputs().add(cvo); - return; - } - - cartItemsIterator = skuToCartItem.values().iterator(); - while (cartItemsIterator.hasNext()) { - CartExtension.CartItem cartItem = cartItemsIterator.next(); - if (!pricingDataMap.containsKey(cartItem.getSku())) { - // No price available for the SKU in the Cart. Populate error message for the Buyer. + if (cart.getStatus() == CartExtension.CartStatusEnum.ACTIVE) { + super.calculate(request); + return; + } + + Iterator cartItemsIterator = clearErrorsAndGetCartItemsIterator(cart, request.getOptionalBuyerActionDetails()); + + // Get the SKUs from each cart item that needs price calculations + Map skuToCartItem = new Map(); + while (cartItemsIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemsIterator.next(); + skuToCartItem.put(cartItem.getSku(), cartItem); + } + + Map pricingDataMap = getPricingDataFromExternalServiceForSkus(skuToCartItem.keySet()); + if (pricingDataMap == Null) { + // No data returned means there is an issue with underlying 3rd party service. Populate generic error message for the Buyer. CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.PRICING, - CartExtension.CartValidationOutputLevelEnum.ERROR, - cartItem); - String errorMessage = getFailedToRepriceItemMessage(cartItem); + CartExtension.CartValidationOutputTypeEnum.PRICING, + CartExtension.CartValidationOutputLevelEnum.ERROR); + String errorMessage = getGenericErrorMessage(); cvo.setMessage(errorMessage); cart.getCartValidationOutputs().add(cvo); - continue; + System.debug('No Pricing data received'); + return; } - Decimal price = pricingDataMap.get(cartItem.getSku()); - // Update cart item fields - cartItem.setListPrice(price); - cartItem.setSalesPrice(price); - cartItem.setTotalListPrice(price * cartItem.getQuantity()); - cartItem.setTotalPrice(price * cartItem.getQuantity()); + + cartItemsIterator = skuToCartItem.values().iterator(); + while (cartItemsIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemsIterator.next(); + if (!pricingDataMap.containsKey(cartItem.getSku())) { + // No price available for the SKU in the Cart. Populate error message for the Buyer. + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.PRICING, + CartExtension.CartValidationOutputLevelEnum.ERROR, + cartItem); + String errorMessage = getFailedToRepriceItemMessage(cartItem); + cvo.setMessage(errorMessage); + cart.getCartValidationOutputs().add(cvo); + continue; + } + Decimal price = pricingDataMap.get(cartItem.getSku()); + // Update cart item fields + cartItem.setListPrice(price); + cartItem.setSalesPrice(price); + cartItem.setTotalListPrice(price * cartItem.getQuantity()); + cartItem.setTotalPrice(price * cartItem.getQuantity()); + } + } catch (Exception e) { + System.debug('Exception occurred, message:' + e.getMessage() + ', stacktrace: ' + e.getStackTraceString()); + throw e; } + } /** @@ -220,6 +227,7 @@ public class EstimatedActualPricingCalculator extends CartExtension.PricingCartC if (r.getStatusCode() != 200) { // return null in case of not successful response from 3rd party service + System.debug('Did not receive pricing data. Call to external service was not successful.'); return null; } From 507b55c68b2b0a5f78af1ad042d9ed49a475e725 Mon Sep 17 00:00:00 2001 From: skumar2 Date: Fri, 21 Jun 2024 08:50:29 +0530 Subject: [PATCH 48/69] Adding caught exception to the CartCalculateRuntimeException --- .../cart/PricingBasicCalculator/PricingCalculatorSample.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commerce/domain/pricing/cart/PricingBasicCalculator/PricingCalculatorSample.cls b/commerce/domain/pricing/cart/PricingBasicCalculator/PricingCalculatorSample.cls index 99c2239..670386a 100644 --- a/commerce/domain/pricing/cart/PricingBasicCalculator/PricingCalculatorSample.cls +++ b/commerce/domain/pricing/cart/PricingBasicCalculator/PricingCalculatorSample.cls @@ -57,7 +57,7 @@ public class PricingCalculatorSample extends CartExtension.PricingCartCalculator // Throwing this exception causes the rollback of all previously applied changes to the cart (in scope of given request) // and may not always be the best choice. System.debug('Exception occurred, message:' + e.getMessage() + ', stacktrace: ' + e.getStackTraceString()); - throw new CartExtension.CartCalculateRuntimeException('An integration error occurred in COMPUTE_PRICES. Contact your admin.'); + throw new CartExtension.CartCalculateRuntimeException('An integration error occurred in COMPUTE_PRICES. Contact your admin', e); } } From 61acced11538839cd2010b46647544b14f2d56e1 Mon Sep 17 00:00:00 2001 From: Sivaprakash Somanchi Date: Fri, 12 Jul 2024 10:20:29 -0500 Subject: [PATCH 49/69] Added null check for order item unit price to support bundles. --- .../checkout/order/createOrder/classes/CreateOrderSample.cls | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/commerce/domain/checkout/order/createOrder/classes/CreateOrderSample.cls b/commerce/domain/checkout/order/createOrder/classes/CreateOrderSample.cls index affd5a7..f58a40f 100644 --- a/commerce/domain/checkout/order/createOrder/classes/CreateOrderSample.cls +++ b/commerce/domain/checkout/order/createOrder/classes/CreateOrderSample.cls @@ -13,7 +13,9 @@ public virtual class CreateOrderSample extends CartExtension.CheckoutCreateOrder List orderItems = orderGraph.getOrderItems(); Decimal roundedPrice = 0.0; for (OrderItem orderItem : orderItems) { - roundedPrice += orderItem.UnitPrice.round(System.RoundingMode.CEILING); + if(orderItem.UnitPrice != null) { + roundedPrice += orderItem.UnitPrice.round(System.RoundingMode.CEILING); + } } order.Description += roundedPrice; return response; From c8a2b55903bccdff0a2fce14ef345ca17e16a42e Mon Sep 17 00:00:00 2001 From: cboscenco Date: Fri, 12 Jul 2024 09:23:17 -0700 Subject: [PATCH 50/69] Updated the sample apex code to process the new recalculationRequested buyer action --- .../classes/CartCalculateSample.cls | 12 ++--- .../classes/CartCalculateSampleUnitTest.cls | 49 +++++++++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSample.cls b/commerce/domain/orchestrators/classes/CartCalculateSample.cls index 7f10696..85e058d 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSample.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSample.cls @@ -31,12 +31,12 @@ global class CartCalculateSample extends CartExtension.CartCalculate { // Use BuyerActions to decide which calculators to invoke CartExtension.BuyerActions buyerActions = request.getBuyerActions(); boolean isCouponAppliedInCheckout = isCouponAppliedInCheckout(buyerActions, cart); - boolean runPricing = buyerActions.isCheckoutStarted() || buyerActions.isCartItemChanged(); - boolean runPromotions = buyerActions.isCheckoutStarted() || buyerActions.isCouponChanged() || buyerActions.isCartItemChanged(); - boolean runInventory = buyerActions.isCheckoutStarted(); - boolean runShipping = buyerActions.isDeliveryGroupChanged() || isCouponAppliedInCheckout; - boolean runPostShipping = buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; - boolean runTaxes = buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; + boolean runPricing = buyerActions.isRecalculationRequested() || buyerActions.isCheckoutStarted() || buyerActions.isCartItemChanged(); + boolean runPromotions = buyerActions.isRecalculationRequested() || buyerActions.isCheckoutStarted() || buyerActions.isCouponChanged() || buyerActions.isCartItemChanged(); + boolean runInventory = buyerActions.isRecalculationRequested() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus() || buyerActions.isCheckoutStarted(); + boolean runShipping = buyerActions.isRecalculationRequested() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus() || buyerActions.isDeliveryGroupChanged() || isCouponAppliedInCheckout; + boolean runPostShipping = buyerActions.isRecalculationRequested() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus() || buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; + boolean runTaxes = buyerActions.isRecalculationRequested() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus() || buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; // OptionalBuyerActionDetails can be used to optimize the various calculators that are invoked CartExtension.CartCalculateCalculatorRequest calculatorRequest = new CartExtension.CartCalculateCalculatorRequest(cart, request.getOptionalBuyerActionDetails()); diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index 5066400..d792be3 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -230,6 +230,43 @@ global class CartCalculateSampleUnitTest { assertUnexpectedCalculations(cart, new List{CART_REPRICED, PROMOTIONS_RECALCULATED, INVENTORY_CHECKED, SHIPPING_RECALCULATED}); } + @IsTest + public static void shouldRunPricingPromotionWhenCartIsActiveAndBuyerRequestsRecalculation() { + // Arrange Cart + CartExtension.Cart cart = arrangeCart('Active');; + + // Arrange BuyerActions for a Cart Recalculation Request + CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForRecalculationRequest(cart); + CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForRecalculationRequest(); + CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); + + // Act + act(new CartExtension.CartCalculateOrchestratorRequest(cart, buyerActions, optionalBuyerActionDetails)); + + // Assert + assertNoCartValidationOutputs(cart); + assertExpectedCalculations(cart, new List{CART_REPRICED, PROMOTIONS_RECALCULATED}); + assertUnexpectedCalculations(cart, new List{INVENTORY_CHECKED, SHIPPING_RECALCULATED, POST_SHIPPING_COMPLETED, TAXES_RECALCULATED}); + } + + @IsTest + public static void shouldRunPricingPromotionInventoryShippingPostShippingTaxesWhenCartIsInCheckoutAndBuyerRequestsRecalculation() { + // Arrange Cart + CartExtension.Cart cart = arrangeCart('Checkout');; + + // Arrange BuyerActions for a Cart Recalculation Request + CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForRecalculationRequest(cart); + CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForRecalculationRequest(); + CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); + + // Act + act(new CartExtension.CartCalculateOrchestratorRequest(cart, buyerActions, optionalBuyerActionDetails)); + + // Assert + assertNoCartValidationOutputs(cart); + assertExpectedCalculations(cart, new List{CART_REPRICED, PROMOTIONS_RECALCULATED, INVENTORY_CHECKED, SHIPPING_RECALCULATED, POST_SHIPPING_COMPLETED, TAXES_RECALCULATED}); + } + private static CartExtension.Cart arrangeCart() { Account testAccount = new Account(Name='My Account'); insert testAccount; @@ -438,6 +475,12 @@ global class CartCalculateSampleUnitTest { return buyerActionDetails; } + private static CartExtension.BuyerActionsMock getBuyerActionsForRecalculationRequest(CartExtension.Cart cart) { + CartExtension.BuyerActionsMock buyerActions = new CartExtension.BuyerActionsMock(cart); + buyerActions.setRecalculationRequested(True); + return buyerActions; + } + private static CartExtension.BuyerActionsMock getBuyerActionsForStartCheckout(CartExtension.Cart cart) { CartExtension.BuyerActionsMock buyerActions = new CartExtension.BuyerActionsMock(cart); buyerActions.setCheckoutStarted(True); @@ -504,6 +547,12 @@ global class CartCalculateSampleUnitTest { return buyerActionDetails; } + private static CartExtension.BuyerActionDetails getBuyerActionDetailsForRecalculationRequest() { + CartExtension.BuyerActionDetails buyerActionDetails = new CartExtension.BuyerActionDetails.Builder() + .build(); + return buyerActionDetails; + } + private static CartExtension.BuyerActionsMock getCartItemChangedBuyerActions(CartExtension.Cart cart) { CartExtension.BuyerActionsMock buyerActions = new CartExtension.BuyerActionsMock(cart); buyerActions.setCartItemChanged(True); From c6d2c7a625d3b85ca02a591d42f3e7808b53feca Mon Sep 17 00:00:00 2001 From: cboscenco Date: Tue, 16 Jul 2024 10:45:55 -0700 Subject: [PATCH 51/69] Fixes after review. --- .../orchestrators/classes/CartCalculateSample.cls | 12 ++++++++---- .../classes/CartCalculateSampleUnitTest.cls | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSample.cls b/commerce/domain/orchestrators/classes/CartCalculateSample.cls index 85e058d..74bc5d7 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSample.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSample.cls @@ -33,10 +33,10 @@ global class CartCalculateSample extends CartExtension.CartCalculate { boolean isCouponAppliedInCheckout = isCouponAppliedInCheckout(buyerActions, cart); boolean runPricing = buyerActions.isRecalculationRequested() || buyerActions.isCheckoutStarted() || buyerActions.isCartItemChanged(); boolean runPromotions = buyerActions.isRecalculationRequested() || buyerActions.isCheckoutStarted() || buyerActions.isCouponChanged() || buyerActions.isCartItemChanged(); - boolean runInventory = buyerActions.isRecalculationRequested() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus() || buyerActions.isCheckoutStarted(); - boolean runShipping = buyerActions.isRecalculationRequested() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus() || buyerActions.isDeliveryGroupChanged() || isCouponAppliedInCheckout; - boolean runPostShipping = buyerActions.isRecalculationRequested() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus() || buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; - boolean runTaxes = buyerActions.isRecalculationRequested() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus() || buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; + boolean runInventory = isRecalculationRequestedInCheckout(buyerActions, cart) || buyerActions.isCheckoutStarted(); + boolean runShipping = isRecalculationRequestedInCheckout(buyerActions, cart) || buyerActions.isDeliveryGroupChanged() || isCouponAppliedInCheckout; + boolean runPostShipping = isRecalculationRequestedInCheckout(buyerActions, cart) || buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; + boolean runTaxes = isRecalculationRequestedInCheckout(buyerActions, cart) || buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; // OptionalBuyerActionDetails can be used to optimize the various calculators that are invoked CartExtension.CartCalculateCalculatorRequest calculatorRequest = new CartExtension.CartCalculateCalculatorRequest(cart, request.getOptionalBuyerActionDetails()); @@ -108,4 +108,8 @@ global class CartCalculateSample extends CartExtension.CartCalculate { private Boolean isCouponAppliedInCheckout(CartExtension.BuyerActions buyerActions, CartExtension.Cart cart) { return cart.getStatus() == CartExtension.CartStatusEnum.CHECKOUT && buyerActions.isCouponChanged(); } + + private Boolean isRecalculationRequestedInCheckout(CartExtension.BuyerActions buyerActions, CartExtension.Cart cart) { + return buyerActions.isRecalculationRequested() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus(); + } } \ No newline at end of file diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index d792be3..13d4358 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -233,7 +233,7 @@ global class CartCalculateSampleUnitTest { @IsTest public static void shouldRunPricingPromotionWhenCartIsActiveAndBuyerRequestsRecalculation() { // Arrange Cart - CartExtension.Cart cart = arrangeCart('Active');; + CartExtension.Cart cart = arrangeCart('Active'); // Arrange BuyerActions for a Cart Recalculation Request CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForRecalculationRequest(cart); @@ -252,7 +252,7 @@ global class CartCalculateSampleUnitTest { @IsTest public static void shouldRunPricingPromotionInventoryShippingPostShippingTaxesWhenCartIsInCheckoutAndBuyerRequestsRecalculation() { // Arrange Cart - CartExtension.Cart cart = arrangeCart('Checkout');; + CartExtension.Cart cart = arrangeCart('Checkout'); // Arrange BuyerActions for a Cart Recalculation Request CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForRecalculationRequest(cart); From 4fdb5111cf609350b5cff14a31f7618e15a27a12 Mon Sep 17 00:00:00 2001 From: "l.wei" Date: Wed, 24 Jul 2024 10:36:33 -0700 Subject: [PATCH 52/69] Add sample and tests --- .../CommerceInventoryServiceSample.cls | 142 ++++++++++++++++++ ...ommerceInventoryServiceSample.cls-meta.xml | 5 + .../CommerceInventoryServiceSampleTest.cls | 138 +++++++++++++++++ ...rceInventoryServiceSampleTest.cls-meta.xml | 5 + 4 files changed, 290 insertions(+) create mode 100644 commerce/domain/inventory/CommerceInventoryServiceSample.cls create mode 100644 commerce/domain/inventory/CommerceInventoryServiceSample.cls-meta.xml create mode 100644 commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls create mode 100644 commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls-meta.xml diff --git a/commerce/domain/inventory/CommerceInventoryServiceSample.cls b/commerce/domain/inventory/CommerceInventoryServiceSample.cls new file mode 100644 index 0000000..d9d4900 --- /dev/null +++ b/commerce/domain/inventory/CommerceInventoryServiceSample.cls @@ -0,0 +1,142 @@ +public class CommerceInventoryServiceSample extends commerce_inventory.CommerceInventoryService { + + public override commerce_inventory.UpsertReservationResponse upsertReservation(commerce_inventory.UpsertReservationRequest upsertReservationRequest, + commerce_inventory.InventoryReservation currentReservation, + String reservationChangeType) { + commerce_inventory.UpsertReservationResponse response = new commerce_inventory.UpsertReservationResponse(); + response.setSucceed(true); + response.setReservationSourceId(upsertReservationRequest.getReservationSourceId()); + response.setReservationIdentifier(upsertReservationRequest.getReservationIdentifier()); + List responseItems = new List(); + + for(commerce_inventory.UpsertItemReservationRequest item : upsertReservationRequest.getItems()) { + commerce_inventory.UpsertItemReservationResponse responseItem = new commerce_inventory.UpsertItemReservationResponse(); + responseItem.setQuantity(item.getQuantity()); + responseItem.setReservedAtLocationId(item.getReservedAtLocationId()); + responseItem.setItemReservationSourceId(item.getItemReservationSourceId()); + responseItem.setProductId(item.getProductId()); + responseItems.add(responseItem); + } + + response.setItems(responseItems); + + if (upsertReservationRequest.getItems().size() != response.getItems().size()) { + throw new NoDataFoundException(); + } + + return response; + } + + public override commerce_inventory.DeleteReservationResponse deleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { + Schema.InventoryReservation invReservation = [SELECT Id FROM InventoryReservation WHERE ID =: reservationId]; + commerce_inventory.DeleteReservationResponse response = new commerce_inventory.DeleteReservationResponse(); + response.setSucceed(true); + try { + delete(invReservation); + } catch(Exception ex) { + response.setSucceed(false); + response.setErrorMessage(ex.getMessage()); + } + return response; + } + + public override commerce_inventory.InventoryReservation getReservation(String reservationId) { + if (reservationId == 'INVALID') { + throw new NoDataFoundException(); + } + + List invReservations = [SELECT Id, + ReservationDurationInSeconds, + ErrorMessage, + ErrorCode, + ReservationSourceId, + ReservationIdentifier + FROM InventoryReservation + WHERE ReservationIdentifier =: reservationId]; + + if (invReservations.size() == 0) { + return null; + } + + Schema.InventoryReservation invReservation = invReservations.get(0); + + commerce_inventory.InventoryReservation response = new commerce_inventory.InventoryReservation(); + + response.setId(invReservation.Id); + response.setDurationInSeconds(10); + response.setReservationIdentifier(invReservation.ReservationIdentifier); + response.setReservationSourceId(invReservation.ReservationSourceId); + response.setErrorCode(invReservation.ErrorCode); + response.setErrorMessage(invReservation.ErrorMessage); + + List invItemsReservations = [SELECT Id, + InventoryReservationId, + Quantity, + ReservedAtLocationId, + ItemReservationSourceId, + ProductId, + ErrorCode, + ErrorMessage + FROM InventoryItemReservation + WHERE InventoryReservationId =: response.getId()]; + + + List responseItems = new List(); + + if (invItemsReservations.size() > 0) { + for(Schema.InventoryItemReservation item : invItemsReservations) { + commerce_inventory.InventoryItemReservation responseItem = new commerce_inventory.InventoryItemReservation(); + responseItem.setId(item.Id); + responseItem.setInventoryReservationId(item.InventoryReservationId); + responseItem.setProductId(item.ProductId); + responseItem.setQuantity(Double.valueOf(item.Quantity)); + responseItem.setItemReservationSourceId(item.ItemReservationSourceId); + responseItem.setReservedAtLocationId(item.ReservedAtLocationId); + responseItem.setErrorMessage(item.ErrorMessage); + responseItem.setErrorCode(item.ErrorCode); + responseItems.add(responseItem); + } + + response.setItems(responseItems); + + if (response.getItems() != null && responseItems.size() != response.getItems().size()) { + throw new NoDataFoundException(); + } + } + return response; + } + + public override commerce_inventory.InventoryCheckAvailability checkInventory(commerce_inventory.InventoryCheckAvailability request) { + for(commerce_inventory.InventoryCheckItemAvailability item : request.getInventoryCheckItemAvailability()) { + item.setAvailable(true); + } + return request; + } + + public override commerce_inventory.InventoryLevelsResponse getInventoryLevel(commerce_inventory.InventoryLevelsRequest request) { + commerce_inventory.InventoryLevelsResponse response = new commerce_inventory.InventoryLevelsResponse(); + Set items = new Set(); + for(commerce_inventory.InventoryLevelsItemRequest item : request.getItemInventoryLevelRequests()) { + commerce_inventory.InventoryLevelsItemResponse itemResponse = new commerce_inventory.InventoryLevelsItemResponse(); + itemResponse.setProductId(item.getProductId()); + itemResponse.setLocationSourceId(item.getLocationSourceId()); + itemResponse.setInventoryLocationSourceType('LocationGroup'); + itemResponse.setOnHand(double.valueOf('10.0')); + itemResponse.setAvailableToFulfill(double.valueOf('10.0')); + itemResponse.setAvailableToOrder(double.valueOf('10.0')); + + if (item.getProductId() != Id.valueOf('01txx0000006uwD')) { + items.add(itemResponse); + } + + } + + response.setItemsInventoryLevels(items); + + if (response.getItemsInventoryLevels().size() != request.getItemInventoryLevelRequests().size()) { + throw new NoDataFoundException(); + } + + return response; + } +} \ No newline at end of file diff --git a/commerce/domain/inventory/CommerceInventoryServiceSample.cls-meta.xml b/commerce/domain/inventory/CommerceInventoryServiceSample.cls-meta.xml new file mode 100644 index 0000000..651b172 --- /dev/null +++ b/commerce/domain/inventory/CommerceInventoryServiceSample.cls-meta.xml @@ -0,0 +1,5 @@ + + + 61.0 + Active + diff --git a/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls b/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls new file mode 100644 index 0000000..468a90d --- /dev/null +++ b/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls @@ -0,0 +1,138 @@ +@isTest +public class CommerceInventoryServiceSampleTest { + + @IsTest + public static void testUpsertReservation() { + // Arrange + commerce_inventory.UpsertItemReservationRequest itemRequest = new commerce_inventory.UpsertItemReservationRequest(double.valueOf('10.0'), Id.valueOf('0ghSG0000000JFbYAM'), Id.valueOf('0a9SG000003AfY1YAK'), Id.valueOf('01tSG000001NNgKYAW')); + List itemReqeustList = new List(); + itemReqeustList.add(itemRequest); + commerce_inventory.upsertReservationRequest request = new commerce_inventory.upsertReservationRequest('4y2ml0e1oG2qrcfdbNnNyk', '0a9SG000003AfY1YAK', itemReqeustList); + + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + // Act + Test.startTest(); + commerce_inventory.UpsertReservationResponse actualResponse = inventoryService.upsertReservation(request, null,''); + Test.stopTest(); + // Assert + commerce_inventory.UpsertItemReservationResponse itemActualResponse = actualResponse.getItems().get(0); + commerce_inventory.UpsertReservationResponse expectedResponse = createUpsertReservationResponse(); + commerce_inventory.UpsertItemReservationResponse itemExpectedResponse = expectedResponse.getItems().get(0); + + System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); + } + + @IsTest + public static void testDeleteReservation() { + // Arrange + Schema.InventoryReservation invReservation = new Schema.InventoryReservation(); + invReservation.ReservationDate = DateTime.now(); + insert invReservation; + + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + // Act + commerce_inventory.DeleteReservationResponse response = inventoryService.deleteReservation(invReservation.Id, null); + // Assert + System.assertEquals(true, response.getSucceed()); + } + + @IsTest + public static void testGetReservation() { + // Arrange + Schema.InventoryReservation invReservation = new Schema.InventoryReservation(); + invReservation.reservationDate = DateTime.now(); + invReservation.reservationIdentifier = '10rxx000007LbIRAA0'; + invReservation.errorCode = 'error'; + invReservation.errorMessage = 'errorMessage'; + insert invReservation; + + Schema.InventoryReservation invReservation2 = new Schema.InventoryReservation(); + invReservation2.reservationDate = DateTime.now(); + + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + // Act + commerce_inventory.InventoryReservation response = inventoryService.getReservation('10rxx000007LbIRAA0'); + commerce_inventory.InventoryReservation response2 = inventoryService.getReservation(invReservation2.Id); + // Assert + System.assertNotEquals(null, response); + } + + @IsTest + public static void testCheckInventory() { + // Arrange + commerce_inventory.InventoryCheckItemAvailability itemRequest = new commerce_inventory.InventoryCheckItemAvailability(); + Set itemReqeustSet = new Set(); + itemReqeustSet.add(itemRequest); + commerce_inventory.InventoryCheckAvailability request = new commerce_inventory.InventoryCheckAvailability(itemReqeustSet); + + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + // Act + Test.startTest(); + commerce_inventory.InventoryCheckAvailability actualResponse = inventoryService.checkInventory(request); + Test.stopTest(); + // Assert + commerce_inventory.InventoryCheckItemAvailability itemActualResponse; + for (commerce_inventory.InventoryCheckItemAvailability item : actualResponse.getInventoryCheckItemAvailability()) { + itemActualResponse = item; + break; + } + System.assertEquals(true, itemActualResponse.isAvailable()); + } + + @IsTest + public static void testgetInventoryLevel() { + // Arrange + commerce_inventory.InventoryLevelsItemRequest itemRequest = new commerce_inventory.InventoryLevelsItemRequest(Id.valueOf('01txx0000001aBcAAI'), 'sku1', Id.valueOf('a1Bxx0000005T9E')); + Set itemReqeustSet = new Set(); + itemReqeustSet.add(itemRequest); + commerce_inventory.InventoryLevelsRequest request = new commerce_inventory.InventoryLevelsRequest(null, itemReqeustSet); + + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + // Act + Test.startTest(); + commerce_inventory.InventoryLevelsResponse actualResponse = inventoryService.getInventoryLevel(request); + Test.stopTest(); + // Assert + commerce_inventory.InventoryLevelsItemResponse itemActualResponse; + for (commerce_inventory.InventoryLevelsItemResponse item : actualResponse.getItemsInventoryLevels()) { + itemActualResponse = item; + break; + } + commerce_inventory.InventoryLevelsResponse expectedResponse = createInventoryLevelsResponse(); + commerce_inventory.InventoryLevelsItemResponse itemExpectedResponse; + for (commerce_inventory.InventoryLevelsItemResponse item : expectedResponse.getItemsInventoryLevels()) { + itemexpectedResponse = item; + break; + } + + System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); + } + + private static commerce_inventory.UpsertReservationResponse createUpsertReservationResponse() { + commerce_inventory.UpsertReservationResponse response = new commerce_inventory.UpsertReservationResponse(); + List items = new List(); + commerce_inventory.UpsertItemReservationResponse itemResponse = new commerce_inventory.UpsertItemReservationResponse(); + itemResponse.setQuantity(double.valueOf('10.0')); + itemResponse.setReservedAtLocationId('0ghSG0000000JFbYAM'); + itemResponse.setItemReservationSourceId('0a9SG000003AfY1YAK'); + itemResponse.setProductId('01tSG000001NNgKYAW'); + items.add(itemResponse); + response.setItems(items); + return response; + } + + private static commerce_inventory.InventoryLevelsResponse createInventoryLevelsResponse() { + commerce_inventory.InventoryLevelsResponse response = new commerce_inventory.InventoryLevelsResponse(); + Set items = new Set(); + commerce_inventory.InventoryLevelsItemResponse itemResponse = new commerce_inventory.InventoryLevelsItemResponse(); + itemResponse.setProductId('01txx0000001aBcAAI'); + itemResponse.setLocationSourceId('a1Bxx0000005T9E'); + itemResponse.setInventoryLocationSourceType('LocationGroup'); + itemResponse.setOnHand(double.valueOf('10.0')); + itemResponse.setAvailableToFulfill(double.valueOf('10.0')); + itemResponse.setAvailableToOrder(double.valueOf('10.0')); + items.add(itemResponse); + response.setItemsInventoryLevels(items); + return response; + } +} \ No newline at end of file diff --git a/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls-meta.xml b/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls-meta.xml new file mode 100644 index 0000000..651b172 --- /dev/null +++ b/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 61.0 + Active + From 5c05e8764ad74d38b29f76caaf68f7bd0f25846a Mon Sep 17 00:00:00 2001 From: Adolfo Foliaco Date: Thu, 26 Sep 2024 08:03:04 -0400 Subject: [PATCH 53/69] add sample code for commerce inventory dommain, including a test to demostrate how to override and create proper mocks --- .../CommerceInventoryServiceSample.cls | 97 ++++--------------- .../CommerceInventoryServiceSampleTest.cls | 55 +++++++---- 2 files changed, 52 insertions(+), 100 deletions(-) diff --git a/commerce/domain/inventory/CommerceInventoryServiceSample.cls b/commerce/domain/inventory/CommerceInventoryServiceSample.cls index d9d4900..588187a 100644 --- a/commerce/domain/inventory/CommerceInventoryServiceSample.cls +++ b/commerce/domain/inventory/CommerceInventoryServiceSample.cls @@ -1,4 +1,4 @@ -public class CommerceInventoryServiceSample extends commerce_inventory.CommerceInventoryService { +public virtual class CommerceInventoryServiceSample extends commerce_inventory.CommerceInventoryService { public override commerce_inventory.UpsertReservationResponse upsertReservation(commerce_inventory.UpsertReservationRequest upsertReservationRequest, commerce_inventory.InventoryReservation currentReservation, @@ -28,82 +28,12 @@ public class CommerceInventoryServiceSample extends commerce_inventory.CommerceI } public override commerce_inventory.DeleteReservationResponse deleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { - Schema.InventoryReservation invReservation = [SELECT Id FROM InventoryReservation WHERE ID =: reservationId]; - commerce_inventory.DeleteReservationResponse response = new commerce_inventory.DeleteReservationResponse(); - response.setSucceed(true); - try { - delete(invReservation); - } catch(Exception ex) { - response.setSucceed(false); - response.setErrorMessage(ex.getMessage()); - } - return response; + system.debug('deleteReservation'); + return callDefaultDeleteReservation(reservationId, currentReservation); } public override commerce_inventory.InventoryReservation getReservation(String reservationId) { - if (reservationId == 'INVALID') { - throw new NoDataFoundException(); - } - - List invReservations = [SELECT Id, - ReservationDurationInSeconds, - ErrorMessage, - ErrorCode, - ReservationSourceId, - ReservationIdentifier - FROM InventoryReservation - WHERE ReservationIdentifier =: reservationId]; - - if (invReservations.size() == 0) { - return null; - } - - Schema.InventoryReservation invReservation = invReservations.get(0); - - commerce_inventory.InventoryReservation response = new commerce_inventory.InventoryReservation(); - - response.setId(invReservation.Id); - response.setDurationInSeconds(10); - response.setReservationIdentifier(invReservation.ReservationIdentifier); - response.setReservationSourceId(invReservation.ReservationSourceId); - response.setErrorCode(invReservation.ErrorCode); - response.setErrorMessage(invReservation.ErrorMessage); - - List invItemsReservations = [SELECT Id, - InventoryReservationId, - Quantity, - ReservedAtLocationId, - ItemReservationSourceId, - ProductId, - ErrorCode, - ErrorMessage - FROM InventoryItemReservation - WHERE InventoryReservationId =: response.getId()]; - - - List responseItems = new List(); - - if (invItemsReservations.size() > 0) { - for(Schema.InventoryItemReservation item : invItemsReservations) { - commerce_inventory.InventoryItemReservation responseItem = new commerce_inventory.InventoryItemReservation(); - responseItem.setId(item.Id); - responseItem.setInventoryReservationId(item.InventoryReservationId); - responseItem.setProductId(item.ProductId); - responseItem.setQuantity(Double.valueOf(item.Quantity)); - responseItem.setItemReservationSourceId(item.ItemReservationSourceId); - responseItem.setReservedAtLocationId(item.ReservedAtLocationId); - responseItem.setErrorMessage(item.ErrorMessage); - responseItem.setErrorCode(item.ErrorCode); - responseItems.add(responseItem); - } - - response.setItems(responseItems); - - if (response.getItems() != null && responseItems.size() != response.getItems().size()) { - throw new NoDataFoundException(); - } - } - return response; + return callDefaultGetReservation(reservationId); } public override commerce_inventory.InventoryCheckAvailability checkInventory(commerce_inventory.InventoryCheckAvailability request) { @@ -124,11 +54,7 @@ public class CommerceInventoryServiceSample extends commerce_inventory.CommerceI itemResponse.setOnHand(double.valueOf('10.0')); itemResponse.setAvailableToFulfill(double.valueOf('10.0')); itemResponse.setAvailableToOrder(double.valueOf('10.0')); - - if (item.getProductId() != Id.valueOf('01txx0000006uwD')) { - items.add(itemResponse); - } - + items.add(itemResponse); } response.setItemsInventoryLevels(items); @@ -139,4 +65,17 @@ public class CommerceInventoryServiceSample extends commerce_inventory.CommerceI return response; } + + @TestVisible + private virtual commerce_inventory.DeleteReservationResponse callDefaultDeleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { + system.debug('callDefaultDeleteReservation'); + return super.deleteReservation(reservationId, currentReservation); + } + + @TestVisible + private virtual commerce_inventory.InventoryReservation callDefaultGetReservation(String reservationId) { + return super.getReservation(reservationId); + } + + } \ No newline at end of file diff --git a/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls b/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls index 468a90d..b5a6a25 100644 --- a/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls +++ b/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls @@ -25,38 +25,30 @@ public class CommerceInventoryServiceSampleTest { @IsTest public static void testDeleteReservation() { // Arrange - Schema.InventoryReservation invReservation = new Schema.InventoryReservation(); - invReservation.ReservationDate = DateTime.now(); - insert invReservation; - - CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + CommerceInventoryServiceSampleMock inventoryServiceMock = new CommerceInventoryServiceSampleMock(); + // Act - commerce_inventory.DeleteReservationResponse response = inventoryService.deleteReservation(invReservation.Id, null); + commerce_inventory.DeleteReservationResponse response = inventoryServiceMock.deleteReservation('10rxx000007LbIRAA0', null); + // Assert System.assertEquals(true, response.getSucceed()); } - @IsTest + @IsTest public static void testGetReservation() { + // Arrange - Schema.InventoryReservation invReservation = new Schema.InventoryReservation(); - invReservation.reservationDate = DateTime.now(); - invReservation.reservationIdentifier = '10rxx000007LbIRAA0'; - invReservation.errorCode = 'error'; - invReservation.errorMessage = 'errorMessage'; - insert invReservation; - - Schema.InventoryReservation invReservation2 = new Schema.InventoryReservation(); - invReservation2.reservationDate = DateTime.now(); - - CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + CommerceInventoryServiceSampleMock inventoryServiceMock = new CommerceInventoryServiceSampleMock(); + // Act - commerce_inventory.InventoryReservation response = inventoryService.getReservation('10rxx000007LbIRAA0'); - commerce_inventory.InventoryReservation response2 = inventoryService.getReservation(invReservation2.Id); + commerce_inventory.InventoryReservation response = inventoryServiceMock.getReservation('10rxx000007LbIRAA0'); + commerce_inventory.InventoryReservation response2 = inventoryServiceMock.getReservation('10rxx000007LbIRAA1'); + // Assert System.assertNotEquals(null, response); + System.assertEquals(null, response2); } - + @IsTest public static void testCheckInventory() { // Arrange @@ -135,4 +127,25 @@ public class CommerceInventoryServiceSampleTest { response.setItemsInventoryLevels(items); return response; } + + private class CommerceInventoryServiceSampleMock extends CommerceInventoryServiceSample { + + public override commerce_inventory.DeleteReservationResponse callDefaultDeleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { + commerce_inventory.DeleteReservationResponse responseMock = new commerce_inventory.DeleteReservationResponse(); + responseMock.setSucceed(true); + return responseMock; + } + + + public override commerce_inventory.InventoryReservation callDefaultGetReservation(String reservationId) { + if (reservationId == '10rxx000007LbIRAA0') { + commerce_inventory.InventoryReservation responseMock = new commerce_inventory.InventoryReservation(); + responseMock.setReservationIdentifier(reservationId); + return responseMock; + } else { + return null; + } + } + } + } \ No newline at end of file From a6fccc9f9ec21c941282d61ed64d0d59618a579f Mon Sep 17 00:00:00 2001 From: Adolfo Foliaco Date: Thu, 26 Sep 2024 09:14:26 -0400 Subject: [PATCH 54/69] make method so can be overriable from the mock --- commerce/domain/inventory/CommerceInventoryServiceSample.cls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commerce/domain/inventory/CommerceInventoryServiceSample.cls b/commerce/domain/inventory/CommerceInventoryServiceSample.cls index 588187a..5b456f0 100644 --- a/commerce/domain/inventory/CommerceInventoryServiceSample.cls +++ b/commerce/domain/inventory/CommerceInventoryServiceSample.cls @@ -67,13 +67,13 @@ public virtual class CommerceInventoryServiceSample extends commerce_inventory.C } @TestVisible - private virtual commerce_inventory.DeleteReservationResponse callDefaultDeleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { + public virtual commerce_inventory.DeleteReservationResponse callDefaultDeleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { system.debug('callDefaultDeleteReservation'); return super.deleteReservation(reservationId, currentReservation); } @TestVisible - private virtual commerce_inventory.InventoryReservation callDefaultGetReservation(String reservationId) { + public virtual commerce_inventory.InventoryReservation callDefaultGetReservation(String reservationId) { return super.getReservation(reservationId); } From 83e4f910a9388c8951b3af2f7010b7d91226df4d Mon Sep 17 00:00:00 2001 From: Adolfo Foliaco Date: Fri, 27 Sep 2024 09:55:56 -0400 Subject: [PATCH 55/69] remove fix vaules and add exception for better data validation --- .../CommerceInventoryServiceSample.cls | 213 +++++++++++++----- .../CommerceInventoryServiceSampleTest.cls | 27 +++ .../InventoryValidationException.cls | 4 + .../InventoryValidationException.cls-meta.xml | 5 + 4 files changed, 192 insertions(+), 57 deletions(-) create mode 100644 commerce/domain/inventory/InventoryValidationException.cls create mode 100644 commerce/domain/inventory/InventoryValidationException.cls-meta.xml diff --git a/commerce/domain/inventory/CommerceInventoryServiceSample.cls b/commerce/domain/inventory/CommerceInventoryServiceSample.cls index 5b456f0..1312fea 100644 --- a/commerce/domain/inventory/CommerceInventoryServiceSample.cls +++ b/commerce/domain/inventory/CommerceInventoryServiceSample.cls @@ -1,81 +1,180 @@ -public virtual class CommerceInventoryServiceSample extends commerce_inventory.CommerceInventoryService { +@isTest +public class CommerceInventoryServiceSampleTest { - public override commerce_inventory.UpsertReservationResponse upsertReservation(commerce_inventory.UpsertReservationRequest upsertReservationRequest, - commerce_inventory.InventoryReservation currentReservation, - String reservationChangeType) { - commerce_inventory.UpsertReservationResponse response = new commerce_inventory.UpsertReservationResponse(); - response.setSucceed(true); - response.setReservationSourceId(upsertReservationRequest.getReservationSourceId()); - response.setReservationIdentifier(upsertReservationRequest.getReservationIdentifier()); - List responseItems = new List(); + @IsTest + public static void testUpsertReservation() { + // Arrange + commerce_inventory.UpsertItemReservationRequest itemRequest = new commerce_inventory.UpsertItemReservationRequest(double.valueOf('10.0'), Id.valueOf('0ghSG0000000JFbYAM'), Id.valueOf('0a9SG000003AfY1YAK'), Id.valueOf('01tSG000001NNgKYAW')); + List itemReqeustList = new List(); + itemReqeustList.add(itemRequest); + commerce_inventory.upsertReservationRequest request = new commerce_inventory.upsertReservationRequest('4y2ml0e1oG2qrcfdbNnNyk', '0a9SG000003AfY1YAK', itemReqeustList); + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + + // Act + Test.startTest(); + commerce_inventory.UpsertReservationResponse actualResponse = inventoryService.upsertReservation(request, null,''); + Test.stopTest(); + + // Assert + commerce_inventory.UpsertItemReservationResponse itemActualResponse = actualResponse.getItems().get(0); + commerce_inventory.UpsertReservationResponse expectedResponse = createUpsertReservationResponse(); + commerce_inventory.UpsertItemReservationResponse itemExpectedResponse = expectedResponse.getItems().get(0); + + System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); + + } + + @IsTest + public static void testDeleteReservation() { + // Arrange + CommerceInventoryServiceSampleMock inventoryServiceMock = new CommerceInventoryServiceSampleMock(); + + // Act + commerce_inventory.DeleteReservationResponse response = inventoryServiceMock.deleteReservation('10rxx000007LbIRAA0', null); + + // Assert + System.assertEquals(true, response.getSucceed()); + } - for(commerce_inventory.UpsertItemReservationRequest item : upsertReservationRequest.getItems()) { - commerce_inventory.UpsertItemReservationResponse responseItem = new commerce_inventory.UpsertItemReservationResponse(); - responseItem.setQuantity(item.getQuantity()); - responseItem.setReservedAtLocationId(item.getReservedAtLocationId()); - responseItem.setItemReservationSourceId(item.getItemReservationSourceId()); - responseItem.setProductId(item.getProductId()); - responseItems.add(responseItem); + @IsTest + public static void testGetReservation() { + + // Arrange + CommerceInventoryServiceSampleMock inventoryServiceMock = new CommerceInventoryServiceSampleMock(); + + // Act + commerce_inventory.InventoryReservation response = inventoryServiceMock.getReservation('10rxx000007LbIRAA0'); + commerce_inventory.InventoryReservation response2 = inventoryServiceMock.getReservation('10rxx000007LbIRAA1'); + + // Assert + System.assertNotEquals(null, response); + System.assertEquals(null, response2); + } + + @IsTest + public static void testCheckInventory() { + // Arrange + commerce_inventory.InventoryCheckItemAvailability itemRequest = new commerce_inventory.InventoryCheckItemAvailability(); + Set itemReqeustSet = new Set(); + itemReqeustSet.add(itemRequest); + commerce_inventory.InventoryCheckAvailability request = new commerce_inventory.InventoryCheckAvailability(itemReqeustSet); + + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + // Act + Test.startTest(); + commerce_inventory.InventoryCheckAvailability actualResponse = inventoryService.checkInventory(request); + Test.stopTest(); + // Assert + commerce_inventory.InventoryCheckItemAvailability itemActualResponse; + for (commerce_inventory.InventoryCheckItemAvailability item : actualResponse.getInventoryCheckItemAvailability()) { + itemActualResponse = item; + break; } + System.assertEquals(true, itemActualResponse.isAvailable()); + } - response.setItems(responseItems); + @IsTest + public static void testgetInventoryLevel() { + // Arrange + commerce_inventory.InventoryLevelsItemRequest itemRequest = new commerce_inventory.InventoryLevelsItemRequest(Id.valueOf('01txx0000001aBcAAI'), 'sku1', Id.valueOf('a1Bxx0000005T9E')); + Set itemRequestSet = new Set(); + itemRequestSet.add(itemRequest); + commerce_inventory.InventoryLevelsRequest request = new commerce_inventory.InventoryLevelsRequest(null, itemRequestSet); - if (upsertReservationRequest.getItems().size() != response.getItems().size()) { - throw new NoDataFoundException(); + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + // Act + Test.startTest(); + commerce_inventory.InventoryLevelsResponse actualResponse = inventoryService.getInventoryLevel(request); + Test.stopTest(); + // Assert + commerce_inventory.InventoryLevelsItemResponse itemActualResponse; + for (commerce_inventory.InventoryLevelsItemResponse item : actualResponse.getItemsInventoryLevels()) { + itemActualResponse = item; + break; + } + commerce_inventory.InventoryLevelsResponse expectedResponse = createInventoryLevelsResponse(); + commerce_inventory.InventoryLevelsItemResponse itemExpectedResponse; + for (commerce_inventory.InventoryLevelsItemResponse item : expectedResponse.getItemsInventoryLevels()) { + itemexpectedResponse = item; + break; } - return response; + System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); } - public override commerce_inventory.DeleteReservationResponse deleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { - system.debug('deleteReservation'); - return callDefaultDeleteReservation(reservationId, currentReservation); - } + @IsTest + public static void testgetInventoryLevelDataValidation() { - public override commerce_inventory.InventoryReservation getReservation(String reservationId) { - return callDefaultGetReservation(reservationId); - } + // Arrange + Set itemRequest = new Set(); + commerce_inventory.InventoryLevelsRequest request = new commerce_inventory.InventoryLevelsRequest(null, itemRequest); + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + + String errorMessage = ''; - public override commerce_inventory.InventoryCheckAvailability checkInventory(commerce_inventory.InventoryCheckAvailability request) { - for(commerce_inventory.InventoryCheckItemAvailability item : request.getInventoryCheckItemAvailability()) { - item.setAvailable(true); + // Act + Test.startTest(); + try { + commerce_inventory.InventoryLevelsResponse actualResponse = inventoryService.getInventoryLevel(request); + errorMessage = 'Sucess Response'; + } catch(InventoryValidationException validationtEx) { + errorMessage = validationtEx.getMessage(); + } - return request; + Test.stopTest(); + + // Assert + System.assertEquals(true,errorMessage.contains('Invalid request size')); } - public override commerce_inventory.InventoryLevelsResponse getInventoryLevel(commerce_inventory.InventoryLevelsRequest request) { + private static commerce_inventory.UpsertReservationResponse createUpsertReservationResponse() { + commerce_inventory.UpsertReservationResponse response = new commerce_inventory.UpsertReservationResponse(); + List items = new List(); + commerce_inventory.UpsertItemReservationResponse itemResponse = new commerce_inventory.UpsertItemReservationResponse(); + itemResponse.setQuantity(double.valueOf('10.0')); + itemResponse.setReservedAtLocationId('0ghSG0000000JFbYAM'); + itemResponse.setItemReservationSourceId('0a9SG000003AfY1YAK'); + itemResponse.setProductId('01tSG000001NNgKYAW'); + items.add(itemResponse); + response.setItems(items); + return response; + } + + private static commerce_inventory.InventoryLevelsResponse createInventoryLevelsResponse() { commerce_inventory.InventoryLevelsResponse response = new commerce_inventory.InventoryLevelsResponse(); Set items = new Set(); - for(commerce_inventory.InventoryLevelsItemRequest item : request.getItemInventoryLevelRequests()) { - commerce_inventory.InventoryLevelsItemResponse itemResponse = new commerce_inventory.InventoryLevelsItemResponse(); - itemResponse.setProductId(item.getProductId()); - itemResponse.setLocationSourceId(item.getLocationSourceId()); - itemResponse.setInventoryLocationSourceType('LocationGroup'); - itemResponse.setOnHand(double.valueOf('10.0')); - itemResponse.setAvailableToFulfill(double.valueOf('10.0')); - itemResponse.setAvailableToOrder(double.valueOf('10.0')); - items.add(itemResponse); - } - + commerce_inventory.InventoryLevelsItemResponse itemResponse = new commerce_inventory.InventoryLevelsItemResponse(); + itemResponse.setProductId('01txx0000001aBcAAI'); + itemResponse.setLocationSourceId('a1Bxx0000005T9E'); + itemResponse.setInventoryLocationSourceType('LocationGroup'); + itemResponse.setOnHand(double.valueOf('10.0')); + itemResponse.setAvailableToFulfill(double.valueOf('10.0')); + itemResponse.setAvailableToOrder(double.valueOf('10.0')); + items.add(itemResponse); response.setItemsInventoryLevels(items); - - if (response.getItemsInventoryLevels().size() != request.getItemInventoryLevelRequests().size()) { - throw new NoDataFoundException(); - } - return response; } - @TestVisible - public virtual commerce_inventory.DeleteReservationResponse callDefaultDeleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { - system.debug('callDefaultDeleteReservation'); - return super.deleteReservation(reservationId, currentReservation); - } + private class CommerceInventoryServiceSampleMock extends CommerceInventoryServiceSample { + + public override commerce_inventory.DeleteReservationResponse callDefaultDeleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { + commerce_inventory.DeleteReservationResponse responseMock = new commerce_inventory.DeleteReservationResponse(); + responseMock.setSucceed(true); + return responseMock; + } + - @TestVisible - public virtual commerce_inventory.InventoryReservation callDefaultGetReservation(String reservationId) { - return super.getReservation(reservationId); + public override commerce_inventory.InventoryReservation callDefaultGetReservation(String reservationId) { + if (reservationId == '10rxx000007LbIRAA0') { + commerce_inventory.InventoryReservation responseMock = new commerce_inventory.InventoryReservation(); + responseMock.setReservationIdentifier(reservationId); + return responseMock; + } else { + return null; + } + } } + + - } \ No newline at end of file diff --git a/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls b/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls index b5a6a25..a5152dd 100644 --- a/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls +++ b/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls @@ -100,6 +100,31 @@ public class CommerceInventoryServiceSampleTest { System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); } + @IsTest + public static void testgetInventoryLevelDataValidation() { + + // Arrange + Set itemRequest = new Set(); + commerce_inventory.InventoryLevelsRequest request = new commerce_inventory.InventoryLevelsRequest(null, itemRequest); + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + + String errorMessage = '' + + // Act + Test.startTest(); + try { + commerce_inventory.InventoryLevelsResponse actualResponse = inventoryService.getInventoryLevel(request); + String errorMessage = 'Sucess Response'; + } catch(DataConflict dataConflictEx) { + errorMessage = dataConflictEx.getMessage(); + + } + Test.stopTest(); + + // Assert + assertEquals(errorMessage.contains('Invalid request size')); + } + private static commerce_inventory.UpsertReservationResponse createUpsertReservationResponse() { commerce_inventory.UpsertReservationResponse response = new commerce_inventory.UpsertReservationResponse(); List items = new List(); @@ -147,5 +172,7 @@ public class CommerceInventoryServiceSampleTest { } } } + + } \ No newline at end of file diff --git a/commerce/domain/inventory/InventoryValidationException.cls b/commerce/domain/inventory/InventoryValidationException.cls new file mode 100644 index 0000000..e796dd6 --- /dev/null +++ b/commerce/domain/inventory/InventoryValidationException.cls @@ -0,0 +1,4 @@ +public class InventoryValidationException extends Exception { + + +} \ No newline at end of file diff --git a/commerce/domain/inventory/InventoryValidationException.cls-meta.xml b/commerce/domain/inventory/InventoryValidationException.cls-meta.xml new file mode 100644 index 0000000..651b172 --- /dev/null +++ b/commerce/domain/inventory/InventoryValidationException.cls-meta.xml @@ -0,0 +1,5 @@ + + + 61.0 + Active + From cb0fa18a856a6503b8c41e06d6c255a5545376b0 Mon Sep 17 00:00:00 2001 From: Adolfo Foliaco Date: Wed, 2 Oct 2024 10:05:26 -0400 Subject: [PATCH 56/69] add new test for calculator and comply to folder structure --- .../CommerceInventoryServiceSample.cls | 180 ---------------- .../classes/InventoryCartCalculatorSample.cls | 40 ++++ ...InventoryCartCalculatorSample.cls-meta.xml | 5 + .../InventoryCartCalculatorSampleTest.cls | 199 ++++++++++++++++++ ...InventoryCartCalculatorSampleTest.cls-meta | 5 + .../inventory/cart/calculator/package.xml | 8 + .../CommerceInventoryServiceSample.cls | 93 ++++++++ ...ommerceInventoryServiceSample.cls-meta.xml | 0 .../CommerceInventoryServiceSampleTest.cls | 25 ++- ...rceInventoryServiceSampleTest.cls-meta.xml | 0 .../classes}/InventoryValidationException.cls | 0 .../InventoryValidationException.cls-meta.xml | 0 commerce/domain/inventory/service/package.xml | 9 + 13 files changed, 371 insertions(+), 193 deletions(-) delete mode 100644 commerce/domain/inventory/CommerceInventoryServiceSample.cls create mode 100644 commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls create mode 100644 commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls-meta.xml create mode 100644 commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls create mode 100644 commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls-meta create mode 100644 commerce/domain/inventory/cart/calculator/package.xml create mode 100644 commerce/domain/inventory/service/classes/CommerceInventoryServiceSample.cls rename commerce/domain/inventory/{ => service/classes}/CommerceInventoryServiceSample.cls-meta.xml (100%) rename commerce/domain/inventory/{ => service/classes}/CommerceInventoryServiceSampleTest.cls (94%) rename commerce/domain/inventory/{ => service/classes}/CommerceInventoryServiceSampleTest.cls-meta.xml (100%) rename commerce/domain/inventory/{ => service/classes}/InventoryValidationException.cls (100%) rename commerce/domain/inventory/{ => service/classes}/InventoryValidationException.cls-meta.xml (100%) create mode 100644 commerce/domain/inventory/service/package.xml diff --git a/commerce/domain/inventory/CommerceInventoryServiceSample.cls b/commerce/domain/inventory/CommerceInventoryServiceSample.cls deleted file mode 100644 index 1312fea..0000000 --- a/commerce/domain/inventory/CommerceInventoryServiceSample.cls +++ /dev/null @@ -1,180 +0,0 @@ -@isTest -public class CommerceInventoryServiceSampleTest { - - @IsTest - public static void testUpsertReservation() { - // Arrange - commerce_inventory.UpsertItemReservationRequest itemRequest = new commerce_inventory.UpsertItemReservationRequest(double.valueOf('10.0'), Id.valueOf('0ghSG0000000JFbYAM'), Id.valueOf('0a9SG000003AfY1YAK'), Id.valueOf('01tSG000001NNgKYAW')); - List itemReqeustList = new List(); - itemReqeustList.add(itemRequest); - commerce_inventory.upsertReservationRequest request = new commerce_inventory.upsertReservationRequest('4y2ml0e1oG2qrcfdbNnNyk', '0a9SG000003AfY1YAK', itemReqeustList); - CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); - - // Act - Test.startTest(); - commerce_inventory.UpsertReservationResponse actualResponse = inventoryService.upsertReservation(request, null,''); - Test.stopTest(); - - // Assert - commerce_inventory.UpsertItemReservationResponse itemActualResponse = actualResponse.getItems().get(0); - commerce_inventory.UpsertReservationResponse expectedResponse = createUpsertReservationResponse(); - commerce_inventory.UpsertItemReservationResponse itemExpectedResponse = expectedResponse.getItems().get(0); - - System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); - - } - - @IsTest - public static void testDeleteReservation() { - // Arrange - CommerceInventoryServiceSampleMock inventoryServiceMock = new CommerceInventoryServiceSampleMock(); - - // Act - commerce_inventory.DeleteReservationResponse response = inventoryServiceMock.deleteReservation('10rxx000007LbIRAA0', null); - - // Assert - System.assertEquals(true, response.getSucceed()); - } - - @IsTest - public static void testGetReservation() { - - // Arrange - CommerceInventoryServiceSampleMock inventoryServiceMock = new CommerceInventoryServiceSampleMock(); - - // Act - commerce_inventory.InventoryReservation response = inventoryServiceMock.getReservation('10rxx000007LbIRAA0'); - commerce_inventory.InventoryReservation response2 = inventoryServiceMock.getReservation('10rxx000007LbIRAA1'); - - // Assert - System.assertNotEquals(null, response); - System.assertEquals(null, response2); - } - - @IsTest - public static void testCheckInventory() { - // Arrange - commerce_inventory.InventoryCheckItemAvailability itemRequest = new commerce_inventory.InventoryCheckItemAvailability(); - Set itemReqeustSet = new Set(); - itemReqeustSet.add(itemRequest); - commerce_inventory.InventoryCheckAvailability request = new commerce_inventory.InventoryCheckAvailability(itemReqeustSet); - - CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); - // Act - Test.startTest(); - commerce_inventory.InventoryCheckAvailability actualResponse = inventoryService.checkInventory(request); - Test.stopTest(); - // Assert - commerce_inventory.InventoryCheckItemAvailability itemActualResponse; - for (commerce_inventory.InventoryCheckItemAvailability item : actualResponse.getInventoryCheckItemAvailability()) { - itemActualResponse = item; - break; - } - System.assertEquals(true, itemActualResponse.isAvailable()); - } - - @IsTest - public static void testgetInventoryLevel() { - // Arrange - commerce_inventory.InventoryLevelsItemRequest itemRequest = new commerce_inventory.InventoryLevelsItemRequest(Id.valueOf('01txx0000001aBcAAI'), 'sku1', Id.valueOf('a1Bxx0000005T9E')); - Set itemRequestSet = new Set(); - itemRequestSet.add(itemRequest); - commerce_inventory.InventoryLevelsRequest request = new commerce_inventory.InventoryLevelsRequest(null, itemRequestSet); - - CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); - // Act - Test.startTest(); - commerce_inventory.InventoryLevelsResponse actualResponse = inventoryService.getInventoryLevel(request); - Test.stopTest(); - // Assert - commerce_inventory.InventoryLevelsItemResponse itemActualResponse; - for (commerce_inventory.InventoryLevelsItemResponse item : actualResponse.getItemsInventoryLevels()) { - itemActualResponse = item; - break; - } - commerce_inventory.InventoryLevelsResponse expectedResponse = createInventoryLevelsResponse(); - commerce_inventory.InventoryLevelsItemResponse itemExpectedResponse; - for (commerce_inventory.InventoryLevelsItemResponse item : expectedResponse.getItemsInventoryLevels()) { - itemexpectedResponse = item; - break; - } - - System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); - } - - @IsTest - public static void testgetInventoryLevelDataValidation() { - - // Arrange - Set itemRequest = new Set(); - commerce_inventory.InventoryLevelsRequest request = new commerce_inventory.InventoryLevelsRequest(null, itemRequest); - CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); - - String errorMessage = ''; - - // Act - Test.startTest(); - try { - commerce_inventory.InventoryLevelsResponse actualResponse = inventoryService.getInventoryLevel(request); - errorMessage = 'Sucess Response'; - } catch(InventoryValidationException validationtEx) { - errorMessage = validationtEx.getMessage(); - - } - Test.stopTest(); - - // Assert - System.assertEquals(true,errorMessage.contains('Invalid request size')); - } - - private static commerce_inventory.UpsertReservationResponse createUpsertReservationResponse() { - commerce_inventory.UpsertReservationResponse response = new commerce_inventory.UpsertReservationResponse(); - List items = new List(); - commerce_inventory.UpsertItemReservationResponse itemResponse = new commerce_inventory.UpsertItemReservationResponse(); - itemResponse.setQuantity(double.valueOf('10.0')); - itemResponse.setReservedAtLocationId('0ghSG0000000JFbYAM'); - itemResponse.setItemReservationSourceId('0a9SG000003AfY1YAK'); - itemResponse.setProductId('01tSG000001NNgKYAW'); - items.add(itemResponse); - response.setItems(items); - return response; - } - - private static commerce_inventory.InventoryLevelsResponse createInventoryLevelsResponse() { - commerce_inventory.InventoryLevelsResponse response = new commerce_inventory.InventoryLevelsResponse(); - Set items = new Set(); - commerce_inventory.InventoryLevelsItemResponse itemResponse = new commerce_inventory.InventoryLevelsItemResponse(); - itemResponse.setProductId('01txx0000001aBcAAI'); - itemResponse.setLocationSourceId('a1Bxx0000005T9E'); - itemResponse.setInventoryLocationSourceType('LocationGroup'); - itemResponse.setOnHand(double.valueOf('10.0')); - itemResponse.setAvailableToFulfill(double.valueOf('10.0')); - itemResponse.setAvailableToOrder(double.valueOf('10.0')); - items.add(itemResponse); - response.setItemsInventoryLevels(items); - return response; - } - - private class CommerceInventoryServiceSampleMock extends CommerceInventoryServiceSample { - - public override commerce_inventory.DeleteReservationResponse callDefaultDeleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { - commerce_inventory.DeleteReservationResponse responseMock = new commerce_inventory.DeleteReservationResponse(); - responseMock.setSucceed(true); - return responseMock; - } - - - public override commerce_inventory.InventoryReservation callDefaultGetReservation(String reservationId) { - if (reservationId == '10rxx000007LbIRAA0') { - commerce_inventory.InventoryReservation responseMock = new commerce_inventory.InventoryReservation(); - responseMock.setReservationIdentifier(reservationId); - return responseMock; - } else { - return null; - } - } - } - - - -} \ No newline at end of file diff --git a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls new file mode 100644 index 0000000..c613811 --- /dev/null +++ b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls @@ -0,0 +1,40 @@ +public class InventoryCartCalculatorSample extends CartExtension.InventoryCartCalculator { + + public InventoryCartCalculatorSample() { + super(); + } + + /** + * @description Constructor used by unit tests only. + * @param apexExecutor Executor which executes various calculators. Can be used to stub calculation results or delegate calculations to actual Calculator. See <>. + */ + public InventoryCartCalculatorSample(CartExtension.CartCalculateExecutorMock apexExecutor) { + // Must call super constructor in order for provided Executor to be used for calculations + super(apexExecutor); + } + + public virtual override void calculate(CartExtension.CartCalculateCalculatorRequest request) { + + CartExtension.Cart cart = request.getCart(); + + if (cart.getStatus() != CartExtension.CartStatusEnum.CHECKOUT) { + return; + } + + removeAllCVOsOfType(cart, CartExtension.CartValidationOutputTypeEnum.INVENTORY); + super.calculate(request); + + } + + private void removeAllCVOsOfType(CartExtension.Cart cart, CartExtension.CartValidationOutputTypeEnum type) { + CartExtension.CartValidationOutputList cartValidationOutputList = cart.getCartValidationOutputs(); + for (Integer i = (cartValidationOutputList.size() - 1); i >= 0; i--) { + CartExtension.CartValidationOutput cvo = cartValidationOutputList.get(i); + if (cvo.getType() == type) { + cartValidationOutputList.remove(cvo); + } + } + } + + +} \ No newline at end of file diff --git a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls-meta.xml b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls-meta.xml new file mode 100644 index 0000000..7d5f9e8 --- /dev/null +++ b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls-meta.xml @@ -0,0 +1,5 @@ + + + 61.0 + Active + \ No newline at end of file diff --git a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls new file mode 100644 index 0000000..3974984 --- /dev/null +++ b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls @@ -0,0 +1,199 @@ +@isTest +public class InventoryCartCalculatorSampleTest { + + private static final String CART_NAME = 'My Cart'; + private static final String ACCOUNT_NAME = 'My Account'; + private static final String WEBSTORE_NAME = 'My WebStore'; + private static final String DELIVERYGROUP_NAME = 'My Delivery Group'; + private static final String CART_ITEM1_NAME = 'My Cart Item 1'; + private static final String CART_ITEM2_NAME = 'My Cart Item 2'; + private static final String CART_ITEM3_NAME = 'My Cart Item 3'; + private static final String SKU1_NAME = 'My SKU 1'; + private static final String SKU2_NAME = 'My SKU 2'; + private static final String SKU3_NAME = 'My SKU 3'; + + @IsTest + public static void testNoCVOCartStatus() { + + // Arrange + CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.ACTIVE); + InventoryCartCalculatorSample calculator = new InventoryCartCalculatorSample(new DefaultInventoryCartCalculatoMockExecutor()); + + Test.startTest(); + calculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); + Test.stopTest(); + + // Assert + assertNoCartValidationOutputs(cart, 0); + + + } + + @IsTest + public static void testCleanCVOCartStatus() { + + // Arrange + CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.ACTIVE); + + InventoryCartCalculatorSample calculator = new InventoryCartCalculatorSample(new DefaultInventoryCartCalculatoMockExecutor()); + + Test.startTest(); + calculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); + Test.stopTest(); + + // Assert + assertNoCartValidationOutputs(cart, 0); + + + } + + @IsTest + public static void testWithCVOCartStatus() { + + // Arrange + CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.CHECKOUT); + addCvoToCart(cart); + InventoryCartCalculatorSample calculator = new InventoryCartCalculatorSample(new DefaultInventoryCartCalculatoMockExecutor()); + + Test.startTest(); + calculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); + Test.stopTest(); + + // Assert + assertNoCartValidationOutputs(cart, 1); + + + } + + + /** + * @description Sample mock executor. Stubs result of default pricing calculator + */ + public class DefaultInventoryCartCalculatoMockExecutor extends CartExtension.CartCalculateExecutorMock { + + /** + * @description This constructor should only be exposed to customers in a test context + */ + public DefaultInventoryCartCalculatoMockExecutor() {} + + public override void defaultInventory(CartExtension.CartCalculateCalculatorRequest request) { + + CartExtension.Cart cart = request.getCart(); + + removeAllCVOsOfType(cart, CartExtension.CartValidationOutputTypeEnum.INVENTORY); + + Integer i = 0; + + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Iterator cartItemCollectionIterator = cartItemCollection.iterator(); + + while (cartItemCollectionIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); + + if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT && i == 0) { + + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.INVENTORY, + CartExtension.CartValidationOutputLevelEnum.ERROR); + cart.getCartValidationOutputs().add(cvo); + } + i = i + 1; + } + } + + private void removeAllCVOsOfType(CartExtension.Cart cart, CartExtension.CartValidationOutputTypeEnum type) { + CartExtension.CartValidationOutputList cartValidationOutputList = cart.getCartValidationOutputs(); + for (Integer i = (cartValidationOutputList.size() - 1); i >= 0; i--) { + CartExtension.CartValidationOutput cvo = cartValidationOutputList.get(i); + if (cvo.getType() == type) { + cartValidationOutputList.remove(cvo); + } + } + } + } + + private static CartExtension.Cart arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum cartStatus) { + Id cartId = arrangeCartWithSpecifiedStatus(cartStatus); + arrangeThreeCartItems(cartId); + return CartExtension.CartTestUtil.getCart(cartId); + } + + private static ID arrangeCartWithSpecifiedStatus(CartExtension.CartStatusEnum cartStatus) { + Account account = new Account(Name = ACCOUNT_NAME); + insert account; + + WebStore webStore = new WebStore(Name = WEBSTORE_NAME, OptionsCartCalculateEnabled = true); + insert webStore; + + WebCart webCart = new WebCart( + Name = CART_NAME, + WebStoreId = webStore.Id, + AccountId = account.Id, + Status = cartStatus.name()); + insert webCart; + return webCart.Id; + } + + private static List arrangeThreeCartItems(ID cartId) { + CartDeliveryGroup deliveryGroup = new CartDeliveryGroup(Name = DELIVERYGROUP_NAME, CartId = cartId); + insert deliveryGroup; + + CartItem cartItem1 = new CartItem( + Name = CART_ITEM1_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 3, + SKU = SKU1_NAME, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem1; + + CartItem cartItem2 = new CartItem( + Name = CART_ITEM2_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 3, + SKU = SKU2_NAME, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem2; + + CartItem cartItem3 = new CartItem( + Name = CART_ITEM3_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 3, + SKU = SKU3_NAME, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + insert cartItem3; + return new List{cartItem1.Id, cartItem2.Id, cartItem3.Id}; + } + + private static void assertNoCartValidationOutputs(CartExtension.Cart cart, Integer expectedCVOCount) { + String errorString = ''; + Iterator cvoIterator = cart.getCartValidationOutputs().iterator(); + while (cvoIterator.hasNext()) { + errorString += cvoIterator.next().getMessage() + '; '; + } + Assert.areEqual(expectedCVOCount, cart.getCartValidationOutputs().size(), 'No CartValidationOutputs expected, but was: ' + errorString); + } + + private static void addCvoToCart( CartExtension.Cart cart) { + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Iterator cartItemCollectionIterator = cartItemCollection.iterator(); + + Integer i = 0; + while (cartItemCollectionIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); + + if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT && i == 0) { + + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.INVENTORY, + CartExtension.CartValidationOutputLevelEnum.ERROR); + cart.getCartValidationOutputs().add(cvo); + } + i = i + 1; + } + } + + +} \ No newline at end of file diff --git a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls-meta b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls-meta new file mode 100644 index 0000000..7d5f9e8 --- /dev/null +++ b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls-meta @@ -0,0 +1,5 @@ + + + 61.0 + Active + \ No newline at end of file diff --git a/commerce/domain/inventory/cart/calculator/package.xml b/commerce/domain/inventory/cart/calculator/package.xml new file mode 100644 index 0000000..825029a --- /dev/null +++ b/commerce/domain/inventory/cart/calculator/package.xml @@ -0,0 +1,8 @@ + + + + InventoryCartCalculatorSample + ApexClass + + 61.0 + \ No newline at end of file diff --git a/commerce/domain/inventory/service/classes/CommerceInventoryServiceSample.cls b/commerce/domain/inventory/service/classes/CommerceInventoryServiceSample.cls new file mode 100644 index 0000000..6578dd7 --- /dev/null +++ b/commerce/domain/inventory/service/classes/CommerceInventoryServiceSample.cls @@ -0,0 +1,93 @@ +public virtual class CommerceInventoryServiceSample extends commerce_inventory.CommerceInventoryService { + + public override commerce_inventory.UpsertReservationResponse upsertReservation(commerce_inventory.UpsertReservationRequest upsertReservationRequest, + commerce_inventory.InventoryReservation currentReservation, + String reservationChangeType) { + + commerce_inventory.UpsertReservationResponse response = new commerce_inventory.UpsertReservationResponse(); + + response.setSucceed(true); + response.setReservationSourceId(upsertReservationRequest.getReservationSourceId()); + response.setReservationIdentifier(upsertReservationRequest.getReservationIdentifier()); + List responseItems = new List(); + + for(commerce_inventory.UpsertItemReservationRequest item : upsertReservationRequest.getItems()) { + commerce_inventory.UpsertItemReservationResponse responseItem = new commerce_inventory.UpsertItemReservationResponse(); + responseItem.setQuantity(item.getQuantity()); + responseItem.setReservedAtLocationId(item.getReservedAtLocationId()); + responseItem.setItemReservationSourceId(item.getItemReservationSourceId()); + responseItem.setProductId(item.getProductId()); + responseItems.add(responseItem); + } + + response.setItems(responseItems); + + validateResponse(upsertReservationRequest.getItems().size(),response.getItems().size()); + + return response; + } + + public override commerce_inventory.DeleteReservationResponse deleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { + return callDefaultDeleteReservation(reservationId, currentReservation); + } + + public override commerce_inventory.InventoryReservation getReservation(String reservationId) { + return callDefaultGetReservation(reservationId); + } + + public override commerce_inventory.InventoryCheckAvailability checkInventory(commerce_inventory.InventoryCheckAvailability request) { + for(commerce_inventory.InventoryCheckItemAvailability item : request.getInventoryCheckItemAvailability()) { + item.setAvailable(true); + } + return request; + } + + public override commerce_inventory.InventoryLevelsResponse getInventoryLevel(commerce_inventory.InventoryLevelsRequest request) { + + commerce_inventory.InventoryLevelsResponse response = new commerce_inventory.InventoryLevelsResponse(); + Set items = new Set(); + + Integer i = 0; + for(commerce_inventory.InventoryLevelsItemRequest item : request.getItemInventoryLevelRequests()) { + commerce_inventory.InventoryLevelsItemResponse itemResponse = new commerce_inventory.InventoryLevelsItemResponse(); + itemResponse.setProductId(item.getProductId()); + itemResponse.setLocationSourceId(item.getLocationSourceId()); + itemResponse.setInventoryLocationSourceType('LocationGroup'); + itemResponse.setOnHand(double.valueOf(i * 10)); + itemResponse.setAvailableToFulfill(double.valueOf(i * 10)); + itemResponse.setAvailableToOrder(double.valueOf(i * 10)); + items.add(itemResponse); + i = i + 1; + } + + response.setItemsInventoryLevels(items); + + validateResponse(request.getItemInventoryLevelRequests().size(),response.getItemsInventoryLevels().size()); + + return response; + + } + + @TestVisible + public virtual commerce_inventory.DeleteReservationResponse callDefaultDeleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { + return super.deleteReservation(reservationId, currentReservation); + } + + @TestVisible + public virtual commerce_inventory.InventoryReservation callDefaultGetReservation(String reservationId) { + return super.getReservation(reservationId); + } + + private void validateResponse(Integer requestSize, Integer responseSize) { + + if (requestSize == 0) { + throw new InventoryValidationException('Invalid request size'); + } + + if (requestSize != responseSize) { + throw new InventoryValidationException('Invalid response from request expected: ' + requestSize + 'but got: ' + responseSize); + } + + } + +} \ No newline at end of file diff --git a/commerce/domain/inventory/CommerceInventoryServiceSample.cls-meta.xml b/commerce/domain/inventory/service/classes/CommerceInventoryServiceSample.cls-meta.xml similarity index 100% rename from commerce/domain/inventory/CommerceInventoryServiceSample.cls-meta.xml rename to commerce/domain/inventory/service/classes/CommerceInventoryServiceSample.cls-meta.xml diff --git a/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls b/commerce/domain/inventory/service/classes/CommerceInventoryServiceSampleTest.cls similarity index 94% rename from commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls rename to commerce/domain/inventory/service/classes/CommerceInventoryServiceSampleTest.cls index a5152dd..57e79c2 100644 --- a/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls +++ b/commerce/domain/inventory/service/classes/CommerceInventoryServiceSampleTest.cls @@ -8,18 +8,20 @@ public class CommerceInventoryServiceSampleTest { List itemReqeustList = new List(); itemReqeustList.add(itemRequest); commerce_inventory.upsertReservationRequest request = new commerce_inventory.upsertReservationRequest('4y2ml0e1oG2qrcfdbNnNyk', '0a9SG000003AfY1YAK', itemReqeustList); - CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); + // Act Test.startTest(); commerce_inventory.UpsertReservationResponse actualResponse = inventoryService.upsertReservation(request, null,''); Test.stopTest(); + // Assert commerce_inventory.UpsertItemReservationResponse itemActualResponse = actualResponse.getItems().get(0); commerce_inventory.UpsertReservationResponse expectedResponse = createUpsertReservationResponse(); commerce_inventory.UpsertItemReservationResponse itemExpectedResponse = expectedResponse.getItems().get(0); System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); + } @IsTest @@ -75,9 +77,9 @@ public class CommerceInventoryServiceSampleTest { public static void testgetInventoryLevel() { // Arrange commerce_inventory.InventoryLevelsItemRequest itemRequest = new commerce_inventory.InventoryLevelsItemRequest(Id.valueOf('01txx0000001aBcAAI'), 'sku1', Id.valueOf('a1Bxx0000005T9E')); - Set itemReqeustSet = new Set(); - itemReqeustSet.add(itemRequest); - commerce_inventory.InventoryLevelsRequest request = new commerce_inventory.InventoryLevelsRequest(null, itemReqeustSet); + Set itemRequestSet = new Set(); + itemRequestSet.add(itemRequest); + commerce_inventory.InventoryLevelsRequest request = new commerce_inventory.InventoryLevelsRequest(null, itemRequestSet); CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); // Act @@ -108,21 +110,21 @@ public class CommerceInventoryServiceSampleTest { commerce_inventory.InventoryLevelsRequest request = new commerce_inventory.InventoryLevelsRequest(null, itemRequest); CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); - String errorMessage = '' + String errorMessage = ''; // Act Test.startTest(); try { commerce_inventory.InventoryLevelsResponse actualResponse = inventoryService.getInventoryLevel(request); - String errorMessage = 'Sucess Response'; - } catch(DataConflict dataConflictEx) { - errorMessage = dataConflictEx.getMessage(); + errorMessage = 'Sucess Response'; + } catch(InventoryValidationException validationtEx) { + errorMessage = validationtEx.getMessage(); } Test.stopTest(); // Assert - assertEquals(errorMessage.contains('Invalid request size')); + System.assertEquals(true,errorMessage.contains('Invalid request size')); } private static commerce_inventory.UpsertReservationResponse createUpsertReservationResponse() { @@ -171,8 +173,5 @@ public class CommerceInventoryServiceSampleTest { return null; } } - } - - - + } } \ No newline at end of file diff --git a/commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls-meta.xml b/commerce/domain/inventory/service/classes/CommerceInventoryServiceSampleTest.cls-meta.xml similarity index 100% rename from commerce/domain/inventory/CommerceInventoryServiceSampleTest.cls-meta.xml rename to commerce/domain/inventory/service/classes/CommerceInventoryServiceSampleTest.cls-meta.xml diff --git a/commerce/domain/inventory/InventoryValidationException.cls b/commerce/domain/inventory/service/classes/InventoryValidationException.cls similarity index 100% rename from commerce/domain/inventory/InventoryValidationException.cls rename to commerce/domain/inventory/service/classes/InventoryValidationException.cls diff --git a/commerce/domain/inventory/InventoryValidationException.cls-meta.xml b/commerce/domain/inventory/service/classes/InventoryValidationException.cls-meta.xml similarity index 100% rename from commerce/domain/inventory/InventoryValidationException.cls-meta.xml rename to commerce/domain/inventory/service/classes/InventoryValidationException.cls-meta.xml diff --git a/commerce/domain/inventory/service/package.xml b/commerce/domain/inventory/service/package.xml new file mode 100644 index 0000000..aaedd06 --- /dev/null +++ b/commerce/domain/inventory/service/package.xml @@ -0,0 +1,9 @@ + + + + CommerceInventoryServiceSample + InventoryValidationException + ApexClass + + 61.0 + \ No newline at end of file From 1488d73e56e15d9a441e251e460cf479e8c36928 Mon Sep 17 00:00:00 2001 From: Adolfo Foliaco Date: Wed, 2 Oct 2024 19:42:59 -0400 Subject: [PATCH 57/69] Fix comments --- .../classes/InventoryCartCalculatorSample.cls | 44 ++++-- .../InventoryCartCalculatorSampleTest.cls | 125 ++++++------------ 2 files changed, 70 insertions(+), 99 deletions(-) diff --git a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls index c613811..471faed 100644 --- a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls +++ b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls @@ -1,4 +1,21 @@ -public class InventoryCartCalculatorSample extends CartExtension.InventoryCartCalculator { +/** + * @description + * + * USE CASE + * + * The customer was notified by the warehouse that during the inventory process, SKU_RED_SHIRT was accidentally made available. + * Due to a system issue, the warehouse is currently unable to mark it as unavailable again. + * Customer does want to sell the SKU-RED_SHIRT in the store front + * + * SI Solution + * Extended inventory calculator to show a message during checkout that SKU_RED_SHIRT that shipping migth be delay + * + */ + public class InventoryCartCalculatorSample extends CartExtension.InventoryCartCalculator { + + + private static final String SKU_RED_SHIRT = 'SKU_RED_SHIRT'; + private static final String SKU_RED_SHIRT_MESSAGE = 'Apologies for the inconvenience, but there may be a slight delay in the shipping of SKU_RED_SHIRT'; public InventoryCartCalculatorSample() { super(); @@ -21,20 +38,23 @@ public class InventoryCartCalculatorSample extends CartExtension.InventoryCartCa return; } - removeAllCVOsOfType(cart, CartExtension.CartValidationOutputTypeEnum.INVENTORY); super.calculate(request); - } - - private void removeAllCVOsOfType(CartExtension.Cart cart, CartExtension.CartValidationOutputTypeEnum type) { - CartExtension.CartValidationOutputList cartValidationOutputList = cart.getCartValidationOutputs(); - for (Integer i = (cartValidationOutputList.size() - 1); i >= 0; i--) { - CartExtension.CartValidationOutput cvo = cartValidationOutputList.get(i); - if (cvo.getType() == type) { - cartValidationOutputList.remove(cvo); + CartExtension.CartItemList cartItemCollection = cart.getCartItems(); + Iterator cartItemCollectionIterator = cartItemCollection.iterator(); + + while (cartItemCollectionIterator.hasNext()) { + CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); + + system.debug('CART ITEM SKU: ' + cartItem.getSku()); + if (cartItem.getSku().equals(SKU_RED_SHIRT)) { + CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( + CartExtension.CartValidationOutputTypeEnum.INVENTORY, + CartExtension.CartValidationOutputLevelEnum.WARNING, cartItem); + cvo.setMessage(SKU_RED_SHIRT_MESSAGE); + cart.getCartValidationOutputs().add(cvo); + system.debug('CART CVO: ' + cvo); } } } - - } \ No newline at end of file diff --git a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls index 3974984..9f888cb 100644 --- a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls +++ b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls @@ -10,17 +10,21 @@ public class InventoryCartCalculatorSampleTest { private static final String CART_ITEM3_NAME = 'My Cart Item 3'; private static final String SKU1_NAME = 'My SKU 1'; private static final String SKU2_NAME = 'My SKU 2'; - private static final String SKU3_NAME = 'My SKU 3'; + private static final String SKU_RED_SHIRT = 'SKU_RED_SHIRT'; + private static final String SKU_RED_SHIRT_MESSAGE = 'Apologies for the inconvenience, but there may be a slight delay in the shipping of SKU_RED_SHIRT'; @IsTest - public static void testNoCVOCartStatus() { + public static void testWithSkuRedShirtNotInCart() { // Arrange - CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.ACTIVE); + CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.CHECKOUT, false); InventoryCartCalculatorSample calculator = new InventoryCartCalculatorSample(new DefaultInventoryCartCalculatoMockExecutor()); Test.startTest(); + + // Act calculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); + Test.stopTest(); // Assert @@ -30,33 +34,17 @@ public class InventoryCartCalculatorSampleTest { } @IsTest - public static void testCleanCVOCartStatus() { + public static void testWithSkuRedShirtInCart() { // Arrange - CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.ACTIVE); - + CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.CHECKOUT, true); InventoryCartCalculatorSample calculator = new InventoryCartCalculatorSample(new DefaultInventoryCartCalculatoMockExecutor()); Test.startTest(); - calculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); - Test.stopTest(); - - // Assert - assertNoCartValidationOutputs(cart, 0); - - } - - @IsTest - public static void testWithCVOCartStatus() { - - // Arrange - CartExtension.Cart cart = arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum.CHECKOUT); - addCvoToCart(cart); - InventoryCartCalculatorSample calculator = new InventoryCartCalculatorSample(new DefaultInventoryCartCalculatoMockExecutor()); - - Test.startTest(); + // Act calculator.calculate(new CartExtension.CartCalculateCalculatorRequest(cart, CartExtension.OptionalBuyerActionDetails.empty())); + Test.stopTest(); // Assert @@ -77,44 +65,15 @@ public class InventoryCartCalculatorSampleTest { public DefaultInventoryCartCalculatoMockExecutor() {} public override void defaultInventory(CartExtension.CartCalculateCalculatorRequest request) { - - CartExtension.Cart cart = request.getCart(); - - removeAllCVOsOfType(cart, CartExtension.CartValidationOutputTypeEnum.INVENTORY); - - Integer i = 0; - - CartExtension.CartItemList cartItemCollection = cart.getCartItems(); - Iterator cartItemCollectionIterator = cartItemCollection.iterator(); - - while (cartItemCollectionIterator.hasNext()) { - CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); - - if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT && i == 0) { - - CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.INVENTORY, - CartExtension.CartValidationOutputLevelEnum.ERROR); - cart.getCartValidationOutputs().add(cvo); - } - i = i + 1; - } + // avoid the call to the implementation + return; } - private void removeAllCVOsOfType(CartExtension.Cart cart, CartExtension.CartValidationOutputTypeEnum type) { - CartExtension.CartValidationOutputList cartValidationOutputList = cart.getCartValidationOutputs(); - for (Integer i = (cartValidationOutputList.size() - 1); i >= 0; i--) { - CartExtension.CartValidationOutput cvo = cartValidationOutputList.get(i); - if (cvo.getType() == type) { - cartValidationOutputList.remove(cvo); - } - } - } } - private static CartExtension.Cart arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum cartStatus) { + private static CartExtension.Cart arrangeAndLoadCartWithSpecifiedStatusAndThreeItems(CartExtension.CartStatusEnum cartStatus, boolean ignoreRedShirtSku) { Id cartId = arrangeCartWithSpecifiedStatus(cartStatus); - arrangeThreeCartItems(cartId); + arrangeThreeCartItems(cartId, ignoreRedShirtSku); return CartExtension.CartTestUtil.getCart(cartId); } @@ -134,7 +93,7 @@ public class InventoryCartCalculatorSampleTest { return webCart.Id; } - private static List arrangeThreeCartItems(ID cartId) { + private static List arrangeThreeCartItems(ID cartId, Boolean ignoreRedShirtSku) { CartDeliveryGroup deliveryGroup = new CartDeliveryGroup(Name = DELIVERYGROUP_NAME, CartId = cartId); insert deliveryGroup; @@ -157,43 +116,35 @@ public class InventoryCartCalculatorSampleTest { insert cartItem2; CartItem cartItem3 = new CartItem( - Name = CART_ITEM3_NAME, - CartId = cartId, - CartDeliveryGroupId = deliveryGroup.Id, - Quantity = 3, - SKU = SKU3_NAME, - Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); - insert cartItem3; - return new List{cartItem1.Id, cartItem2.Id, cartItem3.Id}; + Name = CART_ITEM3_NAME, + CartId = cartId, + CartDeliveryGroupId = deliveryGroup.Id, + Quantity = 3, + SKU = SKU_RED_SHIRT, + Type = CartExtension.SalesItemTypeEnum.PRODUCT.name()); + + if (ignoreRedShirtSku) { + insert cartItem3; + } + + if (ignoreRedShirtSku) { + return new List{cartItem1.Id, cartItem2.Id}; + } else { + return new List{cartItem1.Id, cartItem2.Id, cartItem3.Id}; + } + } private static void assertNoCartValidationOutputs(CartExtension.Cart cart, Integer expectedCVOCount) { String errorString = ''; Iterator cvoIterator = cart.getCartValidationOutputs().iterator(); while (cvoIterator.hasNext()) { - errorString += cvoIterator.next().getMessage() + '; '; + errorString += cvoIterator.next().getMessage() ; } Assert.areEqual(expectedCVOCount, cart.getCartValidationOutputs().size(), 'No CartValidationOutputs expected, but was: ' + errorString); + + if (expectedCVOCount == 1) { + Assert.areEqual(SKU_RED_SHIRT_MESSAGE, errorString, SKU_RED_SHIRT_MESSAGE + ' expected, but was: ' + errorString); + } } - - private static void addCvoToCart( CartExtension.Cart cart) { - CartExtension.CartItemList cartItemCollection = cart.getCartItems(); - Iterator cartItemCollectionIterator = cartItemCollection.iterator(); - - Integer i = 0; - while (cartItemCollectionIterator.hasNext()) { - CartExtension.CartItem cartItem = cartItemCollectionIterator.next(); - - if (cartItem.getType() == CartExtension.SalesItemTypeEnum.PRODUCT && i == 0) { - - CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( - CartExtension.CartValidationOutputTypeEnum.INVENTORY, - CartExtension.CartValidationOutputLevelEnum.ERROR); - cart.getCartValidationOutputs().add(cvo); - } - i = i + 1; - } - } - - } \ No newline at end of file From 79dda81e37a2a119ecf9c046886c75ffc4aa8842 Mon Sep 17 00:00:00 2001 From: Adolfo Foliaco Date: Thu, 3 Oct 2024 10:38:48 -0400 Subject: [PATCH 58/69] address issue since warning are not supported we change the approach --- .../calculator/classes/InventoryCartCalculatorSample.cls | 8 ++------ .../classes/InventoryCartCalculatorSampleTest.cls | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls index 471faed..555ec6c 100644 --- a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls +++ b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls @@ -5,7 +5,7 @@ * * The customer was notified by the warehouse that during the inventory process, SKU_RED_SHIRT was accidentally made available. * Due to a system issue, the warehouse is currently unable to mark it as unavailable again. - * Customer does want to sell the SKU-RED_SHIRT in the store front + * Customer does NOT want to sell the SKU_RED_SHIRT in the store front * * SI Solution * Extended inventory calculator to show a message during checkout that SKU_RED_SHIRT that shipping migth be delay @@ -15,7 +15,7 @@ private static final String SKU_RED_SHIRT = 'SKU_RED_SHIRT'; - private static final String SKU_RED_SHIRT_MESSAGE = 'Apologies for the inconvenience, but there may be a slight delay in the shipping of SKU_RED_SHIRT'; + private static final String SKU_RED_SHIRT_MESSAGE = 'Apologies for the inconvenience, but there is an issue with SKU_RED_SHIRT, and it is currently unavailable.'; public InventoryCartCalculatorSample() { super(); @@ -34,10 +34,6 @@ CartExtension.Cart cart = request.getCart(); - if (cart.getStatus() != CartExtension.CartStatusEnum.CHECKOUT) { - return; - } - super.calculate(request); CartExtension.CartItemList cartItemCollection = cart.getCartItems(); diff --git a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls index 9f888cb..b676104 100644 --- a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls +++ b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls @@ -11,7 +11,7 @@ public class InventoryCartCalculatorSampleTest { private static final String SKU1_NAME = 'My SKU 1'; private static final String SKU2_NAME = 'My SKU 2'; private static final String SKU_RED_SHIRT = 'SKU_RED_SHIRT'; - private static final String SKU_RED_SHIRT_MESSAGE = 'Apologies for the inconvenience, but there may be a slight delay in the shipping of SKU_RED_SHIRT'; + private static final String SKU_RED_SHIRT_MESSAGE = 'Apologies for the inconvenience, but there is an issue with SKU_RED_SHIRT, and it is currently unavailable.'; @IsTest public static void testWithSkuRedShirtNotInCart() { From 4354939c0ab0b2bc3ead1355dea93c82795a9e97 Mon Sep 17 00:00:00 2001 From: Adolfo Foliaco Date: Thu, 3 Oct 2024 10:45:24 -0400 Subject: [PATCH 59/69] change from warning to Error --- .../cart/calculator/classes/InventoryCartCalculatorSample.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls index 555ec6c..86c0fd6 100644 --- a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls +++ b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSample.cls @@ -46,7 +46,7 @@ if (cartItem.getSku().equals(SKU_RED_SHIRT)) { CartExtension.CartValidationOutput cvo = new CartExtension.CartValidationOutput( CartExtension.CartValidationOutputTypeEnum.INVENTORY, - CartExtension.CartValidationOutputLevelEnum.WARNING, cartItem); + CartExtension.CartValidationOutputLevelEnum.ERROR, cartItem); cvo.setMessage(SKU_RED_SHIRT_MESSAGE); cart.getCartValidationOutputs().add(cvo); system.debug('CART CVO: ' + cvo); From a00dcbe5b5657b383797dd740251d0d5b5330245 Mon Sep 17 00:00:00 2001 From: Adolfo Foliaco Date: Thu, 3 Oct 2024 10:47:37 -0400 Subject: [PATCH 60/69] change method to make more sense --- .../classes/InventoryCartCalculatorSampleTest.cls | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls index b676104..315ded4 100644 --- a/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls +++ b/commerce/domain/inventory/cart/calculator/classes/InventoryCartCalculatorSampleTest.cls @@ -28,7 +28,7 @@ public class InventoryCartCalculatorSampleTest { Test.stopTest(); // Assert - assertNoCartValidationOutputs(cart, 0); + assertCartValidationOutputs(cart, 0); } @@ -48,7 +48,7 @@ public class InventoryCartCalculatorSampleTest { Test.stopTest(); // Assert - assertNoCartValidationOutputs(cart, 1); + assertCartValidationOutputs(cart, 1); } @@ -135,7 +135,7 @@ public class InventoryCartCalculatorSampleTest { } - private static void assertNoCartValidationOutputs(CartExtension.Cart cart, Integer expectedCVOCount) { + private static void assertCartValidationOutputs(CartExtension.Cart cart, Integer expectedCVOCount) { String errorString = ''; Iterator cvoIterator = cart.getCartValidationOutputs().iterator(); while (cvoIterator.hasNext()) { From 53cbf102c5f33345e2b93d3dd1ffdf758fdc9428 Mon Sep 17 00:00:00 2001 From: "l.wei" Date: Wed, 22 Jan 2025 19:57:42 -0800 Subject: [PATCH 61/69] Update test to use new constructor --- .../CommerceInventoryServiceSampleTest.cls | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/commerce/domain/inventory/service/classes/CommerceInventoryServiceSampleTest.cls b/commerce/domain/inventory/service/classes/CommerceInventoryServiceSampleTest.cls index 57e79c2..e972bab 100644 --- a/commerce/domain/inventory/service/classes/CommerceInventoryServiceSampleTest.cls +++ b/commerce/domain/inventory/service/classes/CommerceInventoryServiceSampleTest.cls @@ -4,34 +4,34 @@ public class CommerceInventoryServiceSampleTest { @IsTest public static void testUpsertReservation() { // Arrange - commerce_inventory.UpsertItemReservationRequest itemRequest = new commerce_inventory.UpsertItemReservationRequest(double.valueOf('10.0'), Id.valueOf('0ghSG0000000JFbYAM'), Id.valueOf('0a9SG000003AfY1YAK'), Id.valueOf('01tSG000001NNgKYAW')); + commerce_inventory.UpsertItemReservationRequest itemRequest = new commerce_inventory.UpsertItemReservationRequest(double.valueOf('10.0'), Id.valueOf('0ghSG0000000JFbYAM'), Id.valueOf('0a9SG000003AfY1YAK'), Id.valueOf('01tSG000001NNgKYAW'), null); List itemReqeustList = new List(); itemReqeustList.add(itemRequest); - commerce_inventory.upsertReservationRequest request = new commerce_inventory.upsertReservationRequest('4y2ml0e1oG2qrcfdbNnNyk', '0a9SG000003AfY1YAK', itemReqeustList); + commerce_inventory.upsertReservationRequest request = new commerce_inventory.upsertReservationRequest(Integer.valueOf(100),'4y2ml0e1oG2qrcfdbNnNyk', '0a9SG000003AfY1YAK', itemReqeustList); CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); - + // Act Test.startTest(); commerce_inventory.UpsertReservationResponse actualResponse = inventoryService.upsertReservation(request, null,''); Test.stopTest(); - + // Assert commerce_inventory.UpsertItemReservationResponse itemActualResponse = actualResponse.getItems().get(0); commerce_inventory.UpsertReservationResponse expectedResponse = createUpsertReservationResponse(); commerce_inventory.UpsertItemReservationResponse itemExpectedResponse = expectedResponse.getItems().get(0); - + System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); - + } - + @IsTest public static void testDeleteReservation() { // Arrange CommerceInventoryServiceSampleMock inventoryServiceMock = new CommerceInventoryServiceSampleMock(); - + // Act commerce_inventory.DeleteReservationResponse response = inventoryServiceMock.deleteReservation('10rxx000007LbIRAA0', null); - + // Assert System.assertEquals(true, response.getSucceed()); } @@ -41,7 +41,7 @@ public class CommerceInventoryServiceSampleTest { // Arrange CommerceInventoryServiceSampleMock inventoryServiceMock = new CommerceInventoryServiceSampleMock(); - + // Act commerce_inventory.InventoryReservation response = inventoryServiceMock.getReservation('10rxx000007LbIRAA0'); commerce_inventory.InventoryReservation response2 = inventoryServiceMock.getReservation('10rxx000007LbIRAA1'); @@ -50,15 +50,15 @@ public class CommerceInventoryServiceSampleTest { System.assertNotEquals(null, response); System.assertEquals(null, response2); } - + @IsTest public static void testCheckInventory() { // Arrange - commerce_inventory.InventoryCheckItemAvailability itemRequest = new commerce_inventory.InventoryCheckItemAvailability(); + commerce_inventory.InventoryCheckItemAvailability itemRequest = new commerce_inventory.InventoryCheckItemAvailability(double.valueOf('10.0'),double.valueOf('10.0'),double.valueOf('10.0'),double.valueOf('10.0'),'OnHand','10rxx000007LbIRAA0','10rxx000007LbIRAA0','LocationGroup'); Set itemReqeustSet = new Set(); itemReqeustSet.add(itemRequest); commerce_inventory.InventoryCheckAvailability request = new commerce_inventory.InventoryCheckAvailability(itemReqeustSet); - + CommerceInventoryServiceSample inventoryService = new CommerceInventoryServiceSample(); // Act Test.startTest(); @@ -70,14 +70,14 @@ public class CommerceInventoryServiceSampleTest { itemActualResponse = item; break; } - System.assertEquals(true, itemActualResponse.isAvailable()); + System.assertEquals(true, itemActualResponse.isAvailable()); } @IsTest public static void testgetInventoryLevel() { // Arrange - commerce_inventory.InventoryLevelsItemRequest itemRequest = new commerce_inventory.InventoryLevelsItemRequest(Id.valueOf('01txx0000001aBcAAI'), 'sku1', Id.valueOf('a1Bxx0000005T9E')); - Set itemRequestSet = new Set(); + commerce_inventory.InventoryLevelsItemRequest itemRequest = new commerce_inventory.InventoryLevelsItemRequest('01txx0000001aBcAAI', 'sku1','a1Bxx0000005T9E'); + Set itemRequestSet = new Set(); itemRequestSet.add(itemRequest); commerce_inventory.InventoryLevelsRequest request = new commerce_inventory.InventoryLevelsRequest(null, itemRequestSet); @@ -99,7 +99,7 @@ public class CommerceInventoryServiceSampleTest { break; } - System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); + System.assertEquals(itemExpectedResponse.getProductId(), itemActualResponse.getProductId()); } @IsTest @@ -119,7 +119,7 @@ public class CommerceInventoryServiceSampleTest { errorMessage = 'Sucess Response'; } catch(InventoryValidationException validationtEx) { errorMessage = validationtEx.getMessage(); - + } Test.stopTest(); @@ -154,24 +154,24 @@ public class CommerceInventoryServiceSampleTest { response.setItemsInventoryLevels(items); return response; } - + private class CommerceInventoryServiceSampleMock extends CommerceInventoryServiceSample { - + public override commerce_inventory.DeleteReservationResponse callDefaultDeleteReservation(String reservationId, commerce_inventory.InventoryReservation currentReservation) { commerce_inventory.DeleteReservationResponse responseMock = new commerce_inventory.DeleteReservationResponse(); responseMock.setSucceed(true); return responseMock; } - - + + public override commerce_inventory.InventoryReservation callDefaultGetReservation(String reservationId) { if (reservationId == '10rxx000007LbIRAA0') { commerce_inventory.InventoryReservation responseMock = new commerce_inventory.InventoryReservation(); responseMock.setReservationIdentifier(reservationId); - return responseMock; + return responseMock; } else { return null; - } + } } - } + } } \ No newline at end of file From 461dd8b5237e3596c0ce81adaf88ed56d56a11c4 Mon Sep 17 00:00:00 2001 From: deepalibharmal-salesforce Date: Mon, 3 Feb 2025 13:11:40 -0800 Subject: [PATCH 62/69] fix commit --- .../classes/CartCalculateSample.cls | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSample.cls b/commerce/domain/orchestrators/classes/CartCalculateSample.cls index 74bc5d7..63691f8 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSample.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSample.cls @@ -31,12 +31,37 @@ global class CartCalculateSample extends CartExtension.CartCalculate { // Use BuyerActions to decide which calculators to invoke CartExtension.BuyerActions buyerActions = request.getBuyerActions(); boolean isCouponAppliedInCheckout = isCouponAppliedInCheckout(buyerActions, cart); - boolean runPricing = buyerActions.isRecalculationRequested() || buyerActions.isCheckoutStarted() || buyerActions.isCartItemChanged(); - boolean runPromotions = buyerActions.isRecalculationRequested() || buyerActions.isCheckoutStarted() || buyerActions.isCouponChanged() || buyerActions.isCartItemChanged(); - boolean runInventory = isRecalculationRequestedInCheckout(buyerActions, cart) || buyerActions.isCheckoutStarted(); - boolean runShipping = isRecalculationRequestedInCheckout(buyerActions, cart) || buyerActions.isDeliveryGroupChanged() || isCouponAppliedInCheckout; - boolean runPostShipping = isRecalculationRequestedInCheckout(buyerActions, cart) || buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; - boolean runTaxes = isRecalculationRequestedInCheckout(buyerActions, cart) || buyerActions.isDeliveryGroupChanged() || buyerActions.isDeliveryMethodSelected() || isCouponAppliedInCheckout; + + boolean runPricing = buyerActions.isRecalculationRequested() || + buyerActions.isCheckoutStarted() || + buyerActions.isCartItemChanged(); + + boolean runPromotions = buyerActions.isRecalculationRequested() || + buyerActions.isCheckoutStarted() || + buyerActions.isCouponChanged() || + buyerActions.isCartItemChanged() || + isCouponAppliedInCheckout; + + boolean runInventory = isRecalculationRequestedInCheckout(buyerActions, cart) || + buyerActions.isCheckoutStarted() || + (buyerActions.isCartItemChanged() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()); + + boolean runShipping = buyerActions.isEvaluateShippingRequested() || + isRecalculationRequestedInCheckout(buyerActions, cart) && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()) || + isCouponAppliedInCheckout || + (buyerActions.isDeliveryGroupChanged() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()); + + boolean runPostShipping = buyerActions.isEvaluateShippingRequested() || + (isRecalculationRequestedInCheckout(buyerActions, cart) && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()) || + (buyerActions.isDeliveryGroupChanged() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()) || + (buyerActions.isDeliveryMethodSelected() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()) || + isCouponAppliedInCheckout; + + boolean runTaxes = buyerActions.isEvaluateTaxesRequested() || + (isRecalculationRequestedInCheckout(buyerActions, cart) && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()) || + (buyerActions.isDeliveryGroupChanged() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()) || + (buyerActions.isDeliveryMethodSelected() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()) || + isCouponAppliedInCheckout; // OptionalBuyerActionDetails can be used to optimize the various calculators that are invoked CartExtension.CartCalculateCalculatorRequest calculatorRequest = new CartExtension.CartCalculateCalculatorRequest(cart, request.getOptionalBuyerActionDetails()); From a4a3ce1ce172ac225a6e74db778401d70b5e7067 Mon Sep 17 00:00:00 2001 From: deepalibharmal-salesforce Date: Tue, 4 Feb 2025 15:48:56 -0800 Subject: [PATCH 63/69] Add tests --- .../classes/CartCalculateSample.cls | 3 +- .../classes/CartCalculateSampleUnitTest.cls | 90 +++++++++++++++++-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/commerce/domain/orchestrators/classes/CartCalculateSample.cls b/commerce/domain/orchestrators/classes/CartCalculateSample.cls index 63691f8..dbeb60c 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSample.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSample.cls @@ -47,7 +47,7 @@ global class CartCalculateSample extends CartExtension.CartCalculate { (buyerActions.isCartItemChanged() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()); boolean runShipping = buyerActions.isEvaluateShippingRequested() || - isRecalculationRequestedInCheckout(buyerActions, cart) && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()) || + (isRecalculationRequestedInCheckout(buyerActions, cart) && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()) || isCouponAppliedInCheckout || (buyerActions.isDeliveryGroupChanged() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()); @@ -63,6 +63,7 @@ global class CartCalculateSample extends CartExtension.CartCalculate { (buyerActions.isDeliveryMethodSelected() && CartExtension.CartStatusEnum.CHECKOUT == cart.getStatus()) || isCouponAppliedInCheckout; + // OptionalBuyerActionDetails can be used to optimize the various calculators that are invoked CartExtension.CartCalculateCalculatorRequest calculatorRequest = new CartExtension.CartCalculateCalculatorRequest(cart, request.getOptionalBuyerActionDetails()); diff --git a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls index 13d4358..fd59d25 100644 --- a/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls +++ b/commerce/domain/orchestrators/classes/CartCalculateSampleUnitTest.cls @@ -38,6 +38,25 @@ global class CartCalculateSampleUnitTest { assertExpectedCalculations(cart, new List{CART_REPRICED, PROMOTIONS_RECALCULATED}); assertUnexpectedCalculations(cart, new List{INVENTORY_CHECKED, SHIPPING_RECALCULATED, TAXES_RECALCULATED, POST_SHIPPING_COMPLETED}); } + + @IsTest + public static void shouldRunPricingPromotionsInventoryWhenBuyerAddsToCartInCheckoutState() { + // Arrange Cart + CartExtension.Cart cart = arrangeCart('Checkout'); + + // Arrange BuyerActions and BuyerActionDetails as if the Buyer has added an item to cart + CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForAddToCart(cart); + CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForAddToCart(cart.getCartItems().get(0)); + CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); + + // Act + act(new CartExtension.CartCalculateOrchestratorRequest(cart, buyerActions, optionalBuyerActionDetails)); + + // Assert + assertNoCartValidationOutputs(cart); + assertExpectedCalculations(cart, new List{CART_REPRICED, PROMOTIONS_RECALCULATED, INVENTORY_CHECKED}); + assertUnexpectedCalculations(cart, new List{SHIPPING_RECALCULATED, TAXES_RECALCULATED, POST_SHIPPING_COMPLETED}); + } @IsTest public static void shouldRunPricingAndPromotionsWhenBuyerRemovesItemToCart() { @@ -57,6 +76,25 @@ global class CartCalculateSampleUnitTest { assertExpectedCalculations(cart, new List{CART_REPRICED, PROMOTIONS_RECALCULATED}); assertUnexpectedCalculations(cart, new List{INVENTORY_CHECKED, SHIPPING_RECALCULATED, TAXES_RECALCULATED, POST_SHIPPING_COMPLETED}); } + + @IsTest + public static void shouldRunPricingPromotionsInventoryWhenBuyerRemovesItemToCartInCheckoutState() { + // Arrange Cart + CartExtension.Cart cart = arrangeCart('Checkout'); + + // Arrange BuyerActions and BuyerActionDetails as if the Buyer has added an item to cart + CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForDeleteFromCart(cart); + CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForDeleteFromCart(); + CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); + + // Act + act(new CartExtension.CartCalculateOrchestratorRequest(cart, buyerActions, optionalBuyerActionDetails)); + + // Assert + assertNoCartValidationOutputs(cart); + assertExpectedCalculations(cart, new List{CART_REPRICED, PROMOTIONS_RECALCULATED, INVENTORY_CHECKED}); + assertUnexpectedCalculations(cart, new List{SHIPPING_RECALCULATED, TAXES_RECALCULATED, POST_SHIPPING_COMPLETED}); + } @IsTest public static void shouldRunPricingAndPromotionsWhenBuyerIncreasesQuantityOfItem() { @@ -133,7 +171,6 @@ global class CartCalculateSampleUnitTest { assertExpectedCalculations(cart, new List{PROMOTIONS_RECALCULATED, SHIPPING_RECALCULATED, POST_SHIPPING_COMPLETED, TAXES_RECALCULATED}); assertUnexpectedCalculations(cart, new List{CART_REPRICED, INVENTORY_CHECKED}); - } @IsTest @@ -177,7 +214,7 @@ global class CartCalculateSampleUnitTest { @IsTest public static void shouldRunPricingPromotionsInventoryShippingTaxesWhenRegisteredBuyerStartsCheckoutGivenShippingAddressAvailable() { // Arrange Cart - CartExtension.Cart cart = arrangeCart(); + CartExtension.Cart cart = arrangeCart('Checkout'); // Arrange BuyerActions and BuyerActionDetails as if the Buyer has started Checkout CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForStartCheckoutForBuyerWithShippingAddress(cart); @@ -189,13 +226,56 @@ global class CartCalculateSampleUnitTest { // Assert assertNoCartValidationOutputs(cart); + assertExpectedCalculations(cart, new List{CART_REPRICED, PROMOTIONS_RECALCULATED, INVENTORY_CHECKED, SHIPPING_RECALCULATED, TAXES_RECALCULATED, POST_SHIPPING_COMPLETED}); } + + @IsTest + public static void shouldRunShippingTaxesWhenCartIsInCheckoutStateGivenShippingAddressAvailable() { + // Arrange Cart + CartExtension.Cart cart = arrangeCart('Checkout'); + + // Arrange BuyerActions and BuyerActionDetails as if the Buyer has started Checkout + CartExtension.BuyerActionsMock buyerActions = new CartExtension.BuyerActionsMock(cart); + buyerActions.setDeliveryGroupChanged(True); + CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForStartCheckoutForBuyerWithShippingAddress(cart.getCartDeliveryGroups().get(0)); + CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); + + // Act + act(new CartExtension.CartCalculateOrchestratorRequest(cart, buyerActions, optionalBuyerActionDetails)); + + // Assert + assertNoCartValidationOutputs(cart); + + assertExpectedCalculations(cart, new List{SHIPPING_RECALCULATED, TAXES_RECALCULATED, POST_SHIPPING_COMPLETED}); + assertUnexpectedCalculations(cart, new List{CART_REPRICED, PROMOTIONS_RECALCULATED, INVENTORY_CHECKED}); + } + + @IsTest + public static void shouldRunNoCalculatorWhenCartIsInActiveStateGivenAndDeliverMethodSelectedAndShippingAddressAvailable() { + // Arrange Cart + CartExtension.Cart cart = arrangeCart(); + + // Arrange BuyerActions and BuyerActionDetails as if the Buyer has started Checkout + CartExtension.BuyerActionsMock buyerActions = new CartExtension.BuyerActionsMock(cart); + buyerActions.setDeliveryGroupChanged(True); + buyerActions.setDeliveryMethodSelected(True); + CartExtension.BuyerActionDetails buyerActionDetails = getBuyerActionDetailsForStartCheckoutForBuyerWithShippingAddress(cart.getCartDeliveryGroups().get(0)); + CartExtension.OptionalBuyerActionDetails optionalBuyerActionDetails = CartExtension.OptionalBuyerActionDetails.of(buyerActionDetails); + + // Act + act(new CartExtension.CartCalculateOrchestratorRequest(cart, buyerActions, optionalBuyerActionDetails)); + + // Assert + assertNoCartValidationOutputs(cart); + + assertUnexpectedCalculations(cart, new List{CART_REPRICED, PROMOTIONS_RECALCULATED, INVENTORY_CHECKED, SHIPPING_RECALCULATED, TAXES_RECALCULATED, POST_SHIPPING_COMPLETED}); + } @IsTest public static void shouldRunShippingTaxesAndPostShippingWhenBuyerUpdatesShippingAddress() { // Arrange Cart - CartExtension.Cart cart = arrangeCart(); + CartExtension.Cart cart = arrangeCart('Checkout'); // Arrange BuyerActions and BuyerActionDetails as if the Buyer has updated their shipping address CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForUpdateCheckoutWithShippingAddress(cart); @@ -214,7 +294,7 @@ global class CartCalculateSampleUnitTest { @IsTest public static void shouldRunPostShippingAndTaxesWhenBuyerSelectsDeliveryMethod() { // Arrange Cart - CartExtension.Cart cart = arrangeCart(); + CartExtension.Cart cart = arrangeCart('Checkout'); // Arrange BuyerActions and BuyerActionDetails as if the Buyer selected a delivery method CartExtension.BuyerActionsMock buyerActions = getBuyerActionsForUpdateCheckoutWithSelectedDeliveryMethod(cart); @@ -570,4 +650,4 @@ global class CartCalculateSampleUnitTest { buyerActions.setCouponChanged(True); return buyerActions; } -} +} \ No newline at end of file From 2cc4873c4e25813ea3bbdcde40764b953b6dce72 Mon Sep 17 00:00:00 2001 From: alakhmani Date: Sun, 9 Feb 2025 21:20:49 -0800 Subject: [PATCH 64/69] sample gift card extension and test classes --- .../giftcard/GiftCardAdapterDefault.cls | 11 ++ .../giftcard/GiftCardAdapterSample.cls | 107 ++++++++++++++++++ .../giftcard/GiftcardAdapterUnitTest.cls | 42 +++++++ 3 files changed, 160 insertions(+) create mode 100644 commerce/domain/checkout/giftcard/GiftCardAdapterDefault.cls create mode 100644 commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls create mode 100644 commerce/domain/checkout/giftcard/GiftcardAdapterUnitTest.cls diff --git a/commerce/domain/checkout/giftcard/GiftCardAdapterDefault.cls b/commerce/domain/checkout/giftcard/GiftCardAdapterDefault.cls new file mode 100644 index 0000000..f5101fa --- /dev/null +++ b/commerce/domain/checkout/giftcard/GiftCardAdapterDefault.cls @@ -0,0 +1,11 @@ +/** + * @description Sample GiftCard Adapter Apex that calls the default implementation. + */ +global class GiftCardAdapterDefault extends CartExtension.GiftCardAdapter { + + + public override void applyGiftCard(CartExtension.ApplyGiftCardRequest request){ + super.applyGiftCard(request); + } + +} \ No newline at end of file diff --git a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls new file mode 100644 index 0000000..10eb277 --- /dev/null +++ b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls @@ -0,0 +1,107 @@ +/** + * @description Custom Gift card adapter. This class will have operations like applyGiftCard(), redeemGiftCard(), refundGiftCard(). + In this sample implementation of applyGiftCard, if the https service is registered , we make a call to that third party service + with giftCardNumber and pin passed as the input in the request. The response is expected to have availableAmount (in the gift card) + and giftCardReference. If https service is not registered then we return a hardcoded values. + */ + +global class GiftCardAdapter extends CartExtension.GiftCardAdapter{ + + private static String httpHost = 'https://giftCardProvider.com'; + private static Boolean useHTTPService = false; + + public virtual override CartExtension.ApplyGiftCardResponse applyGiftCard(CartExtension.ApplyGiftCardRequest applyRequest){ + + Double amount = applyRequest.getAmount(); + String giftCardCurrency = applyRequest.getCurrencyIsoCode(); + String giftCardCode = applyRequest.getGiftCardCode(); + String pin = applyRequest.getPin(); + + CartExtension.ApplyGiftCardResponse response = null; + + if(useHTTPService) { + response = applyGiftCardService(giftCardCode, pin, amount); + } else { + response = applyGiftCardMockedService(giftCardCode, pin, amount); + } + + return response; + + } + + private CartExtension.ApplyGiftCardResponse applyGiftCardMockedService(String giftCardCode, String giftCardPin, Double amount) { + CartExtension.ApplyGiftCardResponse applyResponse = new CartExtension.ApplyGiftCardResponse(); + + applyResponse.setGiftCardReference('123456789'); + applyResponse.setAppliedAmount(50); + applyResponse.setLastFour('6789'); + applyResponse.setCreditType('GiftCard'); + applyResponse.setStatus('Success'); + + return applyResponse; + + + } + + private CartExtension.ApplyGiftCardResponse applyGiftCardService(String giftCardCode, String giftCardPin, Double amount) { + CartExtension.ApplyGiftCardResponse applyResponse = new CartExtension.ApplyGiftCardResponse(); + + final Integer successfulHttpRequest = 200; + + Http http = new Http(); + HttpRequest request = new HttpRequest(); + request.setEndpoint(httpHost + '/apply-gift-card'); + request.setMethod('POST'); + HttpResponse response = http.send(request); + + + if (response.getStatusCode() == successfulHttpRequest) { + // Parse JSON response + String responseBody = response.getBody(); + + // Parse JSON using JSON.deserialize or JSON.deserializeUntyped + Map resultMap = (Map) JSON.deserializeUntyped(responseBody); + + String giftCardReference = (String)resultMap.get('giftCardReference'); + + Double availableAmount = (Double)resultMap.get('availableAmount'); + if(availableAmount >= amount){ + applyResponse.setAppliedAmount(amount); + } else if(availableAmount < amount && availableAmount>0){ + applyResponse.setAppliedAmount(availableAmount); + } else if(availableAmount == 0){ + applyResponse.setAppliedAmount(0); + applyResponse.setErrorMessage('Zero balance in gift card'); + applyResponse.setStatus('Fail'); + applyResponse.setGiftCardReference(giftCardReference); + applyResponse.setLastFour(giftCardReference.substring(giftCardReference.length() - 4)); + applyResponse.setCreditType('GiftCard'); + return applyResponse; + + } + applyResponse.setGiftCardReference(giftCardReference); + applyResponse.setLastFour(giftCardReference.substring(giftCardReference.length() - 4)); + applyResponse.setCreditType('GiftCard'); + applyResponse.setStatus('Success'); + + return applyResponse; + }else{ + // Read the error details + String responseBody = response.getBody(); + System.debug('Error Status Code: ' + response.getStatusCode()); + System.debug('Error Body: ' + responseBody); + + // Optional: Parse error if response is JSON + if (response.getHeader('Content-Type').contains('application/json')) { + Map errorMap = (Map) JSON.deserializeUntyped(responseBody); + Map errorDetails = (Map) errorMap.get('error'); + + String errorMessage = (String) errorDetails.get('message'); + System.debug('Error Message: ' + errorMessage); + applyResponse.setStatus('Fail'); + applyResponse.setErrorMessage(errorMessage); + } + + } + return applyResponse; + } \ No newline at end of file diff --git a/commerce/domain/checkout/giftcard/GiftcardAdapterUnitTest.cls b/commerce/domain/checkout/giftcard/GiftcardAdapterUnitTest.cls new file mode 100644 index 0000000..1c1702f --- /dev/null +++ b/commerce/domain/checkout/giftcard/GiftcardAdapterUnitTest.cls @@ -0,0 +1,42 @@ +@IsTest +global class GiftcardAdapterUnitTest{ + + /** + * @description Verify that SplitShipmentService invoked the default implementation. + */ + @IsTest + public static void applyGiftCard(){ + CartExtension.ApplyGiftCardRequest applyRequest = new CartExtension.ApplyGiftCardRequest('123456789', '1234', 50,'USD'); + GiftCardAdapterSample adapter = new GiftCardAdapterSample(); + + // Act + Test.startTest(); + CartExtension.ApplyGiftCardResponse applyResponse = adapter.applyGiftCard(applyRequest); + Test.stopTest(); + + + // Assert + System.assertEquals(applyResponse.getGiftCardReference, '123456789') + System.assertEquals(applyResponse.getStatus(), 'Success'); + System.assertEquals(applyResponse.getAppliedAmount(), 50); + System.assertEquals(applyResponse.getErrorMessage(), null); + } + + @IsTest + public static void applyGiftCardWithDefaultImplementation(){ + CartExtension.ApplyGiftCardRequest applyRequest = new CartExtension.ApplyGiftCardRequest('987654321', '1234', 50,'USD'); + GiftCardAdapterDefault defaultAdapter = new GiftCardAdapterDefault(); + try{ + // Act + Test.startTest(); + defaultAdapter.applyGiftCard(applyRequest); + Test.stopTest(); + + // If an exception was not thrown, fail the test + System.assert(false, 'Expected exception was not thrown'); + }catch(CalloutException e){ + // Assert that the exception was thrown + System.assert(e.getMessage().contains('Native gift card is not supported')); + } + } +} \ No newline at end of file From 3fda96d989f8b9fcdffa4bba4876e9de223977ce Mon Sep 17 00:00:00 2001 From: alakhmani Date: Sun, 9 Feb 2025 22:07:18 -0800 Subject: [PATCH 65/69] sample gift card extension class --- commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls index 10eb277..cacd5e8 100644 --- a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls +++ b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls @@ -101,7 +101,7 @@ global class GiftCardAdapter extends CartExtension.GiftCardAdapter{ applyResponse.setStatus('Fail'); applyResponse.setErrorMessage(errorMessage); } - } return applyResponse; - } \ No newline at end of file + } +} \ No newline at end of file From 15b0ce008587c957545f6b992f373e20e5f17bb5 Mon Sep 17 00:00:00 2001 From: alakhmani Date: Mon, 10 Feb 2025 21:16:22 -0800 Subject: [PATCH 66/69] review comments --- .../checkout/giftcard/GiftCardAdapterSample.cls | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls index cacd5e8..72ec600 100644 --- a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls +++ b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls @@ -1,13 +1,13 @@ /** * @description Custom Gift card adapter. This class will have operations like applyGiftCard(), redeemGiftCard(), refundGiftCard(). In this sample implementation of applyGiftCard, if the https service is registered , we make a call to that third party service - with giftCardNumber and pin passed as the input in the request. The response is expected to have availableAmount (in the gift card) + with giftCardNumber and pin passed as the input in the request. The response is expected to have appliedAmount and giftCardReference. If https service is not registered then we return a hardcoded values. */ global class GiftCardAdapter extends CartExtension.GiftCardAdapter{ - private static String httpHost = 'https://giftCardProvider.com'; + private static String httpHost = 'https://example.com'; private static Boolean useHTTPService = false; public virtual override CartExtension.ApplyGiftCardResponse applyGiftCard(CartExtension.ApplyGiftCardRequest applyRequest){ @@ -20,16 +20,17 @@ global class GiftCardAdapter extends CartExtension.GiftCardAdapter{ CartExtension.ApplyGiftCardResponse response = null; if(useHTTPService) { - response = applyGiftCardService(giftCardCode, pin, amount); + response = applyGiftCardService(giftCardCode, pin, amount, giftCardCurrency); } else { - response = applyGiftCardMockedService(giftCardCode, pin, amount); + response = applyGiftCardMockedService(giftCardCode, pin, amount, giftCardCurrency); } return response; } - private CartExtension.ApplyGiftCardResponse applyGiftCardMockedService(String giftCardCode, String giftCardPin, Double amount) { + private CartExtension.ApplyGiftCardResponse applyGiftCardMockedService(String giftCardCode, String giftCardPin, + Double amount, String giftCardCurrency) { CartExtension.ApplyGiftCardResponse applyResponse = new CartExtension.ApplyGiftCardResponse(); applyResponse.setGiftCardReference('123456789'); @@ -43,7 +44,8 @@ global class GiftCardAdapter extends CartExtension.GiftCardAdapter{ } - private CartExtension.ApplyGiftCardResponse applyGiftCardService(String giftCardCode, String giftCardPin, Double amount) { + private CartExtension.ApplyGiftCardResponse applyGiftCardService(String giftCardCode, String giftCardPin, + Double amount, String giftCardCurrency) { CartExtension.ApplyGiftCardResponse applyResponse = new CartExtension.ApplyGiftCardResponse(); final Integer successfulHttpRequest = 200; From ea75a80d8763b1f6f6d68f3c0e33da0df8dae7cb Mon Sep 17 00:00:00 2001 From: alakhmani Date: Tue, 11 Feb 2025 16:24:25 -0800 Subject: [PATCH 67/69] review comments --- .../giftcard/GiftCardAdapterSample.cls | 140 ++++++++++-------- .../giftcard/GiftCardMockHttpResponseFail.cls | 17 +++ .../GiftCardMockHttpResponseSuccess.cls | 17 +++ .../giftcard/GiftcardAdapterUnitTest.cls | 39 +++++ 4 files changed, 153 insertions(+), 60 deletions(-) create mode 100644 commerce/domain/checkout/giftcard/GiftCardMockHttpResponseFail.cls create mode 100644 commerce/domain/checkout/giftcard/GiftCardMockHttpResponseSuccess.cls diff --git a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls index 72ec600..5194cd3 100644 --- a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls +++ b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls @@ -44,66 +44,86 @@ global class GiftCardAdapter extends CartExtension.GiftCardAdapter{ } - private CartExtension.ApplyGiftCardResponse applyGiftCardService(String giftCardCode, String giftCardPin, - Double amount, String giftCardCurrency) { - CartExtension.ApplyGiftCardResponse applyResponse = new CartExtension.ApplyGiftCardResponse(); + private CartExtension.ApplyGiftCardResponse applyGiftCardService(String giftCardCode, String giftCardPin, Double amount) { + + final Integer successfulHttpRequest = 200; + CartExtension.ApplyGiftCardResponse applyResponse; + + Http http = new Http(); + HttpRequest request = new HttpRequest(); + request.setEndpoint(httpHost + '/apply-gift-card'); + request.setMethod('POST'); + HttpResponse response = http.send(request); + + + if (response.getStatusCode() == successfulHttpRequest) { + // Parse JSON response + String responseBody = response.getBody(); + applyResponse = processSuccessResponseFromService(responseBody, amount); + + }else{ + // Read the error details + String responseBody = response.getBody(); + applyResponse = processFailedResponseFromService(response); + + } + return applyResponse; - final Integer successfulHttpRequest = 200; - - Http http = new Http(); - HttpRequest request = new HttpRequest(); - request.setEndpoint(httpHost + '/apply-gift-card'); - request.setMethod('POST'); - HttpResponse response = http.send(request); - - - if (response.getStatusCode() == successfulHttpRequest) { - // Parse JSON response - String responseBody = response.getBody(); - - // Parse JSON using JSON.deserialize or JSON.deserializeUntyped - Map resultMap = (Map) JSON.deserializeUntyped(responseBody); - - String giftCardReference = (String)resultMap.get('giftCardReference'); - - Double availableAmount = (Double)resultMap.get('availableAmount'); - if(availableAmount >= amount){ - applyResponse.setAppliedAmount(amount); - } else if(availableAmount < amount && availableAmount>0){ - applyResponse.setAppliedAmount(availableAmount); - } else if(availableAmount == 0){ - applyResponse.setAppliedAmount(0); - applyResponse.setErrorMessage('Zero balance in gift card'); - applyResponse.setStatus('Fail'); - applyResponse.setGiftCardReference(giftCardReference); - applyResponse.setLastFour(giftCardReference.substring(giftCardReference.length() - 4)); - applyResponse.setCreditType('GiftCard'); - return applyResponse; - - } - applyResponse.setGiftCardReference(giftCardReference); - applyResponse.setLastFour(giftCardReference.substring(giftCardReference.length() - 4)); - applyResponse.setCreditType('GiftCard'); - applyResponse.setStatus('Success'); - - return applyResponse; - }else{ - // Read the error details - String responseBody = response.getBody(); - System.debug('Error Status Code: ' + response.getStatusCode()); - System.debug('Error Body: ' + responseBody); - - // Optional: Parse error if response is JSON - if (response.getHeader('Content-Type').contains('application/json')) { - Map errorMap = (Map) JSON.deserializeUntyped(responseBody); - Map errorDetails = (Map) errorMap.get('error'); - - String errorMessage = (String) errorDetails.get('message'); - System.debug('Error Message: ' + errorMessage); - applyResponse.setStatus('Fail'); - applyResponse.setErrorMessage(errorMessage); - } - } - return applyResponse; } + + private CartExtension.ApplyGiftCardResponse processSuccessResponseFromService(String thirdPartyResponse, Double amount) { + CartExtension.ApplyGiftCardResponse applyResponse = new CartExtension.ApplyGiftCardResponse(); + + // Parse JSON using JSON.deserialize or JSON.deserializeUntyped + Map resultMap = (Map) JSON.deserializeUntyped(thirdPartyResponse); + + String giftCardReference = (String)resultMap.get('giftCardReference'); + + Double availableAmount = (Double)resultMap.get('availableAmount'); + if(availableAmount >= amount){ + applyResponse.setAppliedAmount(amount); + } else if(availableAmount < amount && availableAmount>0){ + applyResponse.setAppliedAmount(availableAmount); + } else if(availableAmount == 0){ + applyResponse.setAppliedAmount(0); + applyResponse.setErrorMessage('Zero balance in gift card'); + applyResponse.setStatus('Fail'); + applyResponse.setGiftCardReference(giftCardReference); + applyResponse.setLastFour(giftCardReference.substring(giftCardReference.length() - 4)); + return applyResponse; + + } + applyResponse.setGiftCardReference(giftCardReference); + applyResponse.setLastFour(giftCardReference.substring(giftCardReference.length() - 4)); + applyResponse.setStatus('Success'); + + return applyResponse; + } + + private CartExtension.ApplyGiftCardResponse processFailedResponseFromService(HttpResponse response) { + + CartExtension.ApplyGiftCardResponse applyResponse = new CartExtension.ApplyGiftCardResponse(); + + // Read the error details + String responseBody = response.getBody(); + + System.debug('Error Status Code: ' + response.getStatusCode()); + System.debug('Error Body: ' + responseBody); + + // Optional: Parse error if response is JSON + if (response.getHeader('Content-Type').contains('application/json')) { + Map errorMap = (Map) JSON.deserializeUntyped(responseBody); + + String errorMessage = (String) errorMap.get('message'); + System.debug('Error Message: ' + errorMessage); + applyResponse.setStatus('Fail'); + applyResponse.setErrorMessage(errorMessage); + } + return applyResponse; + } + + public void shouldUseHttpService(Boolean input){ + useHTTPService = input; + } + } \ No newline at end of file diff --git a/commerce/domain/checkout/giftcard/GiftCardMockHttpResponseFail.cls b/commerce/domain/checkout/giftcard/GiftCardMockHttpResponseFail.cls new file mode 100644 index 0000000..27b1330 --- /dev/null +++ b/commerce/domain/checkout/giftcard/GiftCardMockHttpResponseFail.cls @@ -0,0 +1,17 @@ +@isTest +global class GiftCardMockHttpResponseFail implements HttpCalloutMock { + // Implement this interface method + global HTTPResponse respond(HTTPRequest req) { + // Optionally, only send a mock response for a specific endpoint + // and method. + System.assertEquals('https://example.com/apply-gift-card', req.getEndpoint()); + System.assertEquals('POST', req.getMethod()); + + // Create a fake response + HttpResponse res = new HttpResponse(); + res.setHeader('Content-Type', 'application/json'); + res.setBody('{"error": "Internal Server Error", "message": "Something went wrong"}'); + res.setStatusCode(500); + return res; + } +} \ No newline at end of file diff --git a/commerce/domain/checkout/giftcard/GiftCardMockHttpResponseSuccess.cls b/commerce/domain/checkout/giftcard/GiftCardMockHttpResponseSuccess.cls new file mode 100644 index 0000000..b168a53 --- /dev/null +++ b/commerce/domain/checkout/giftcard/GiftCardMockHttpResponseSuccess.cls @@ -0,0 +1,17 @@ +@isTest +global class GiftCardMockHttpResponseSuccess implements HttpCalloutMock { + // Implement this interface method + global HTTPResponse respond(HTTPRequest req) { + // Optionally, only send a mock response for a specific endpoint + // and method. + System.assertEquals('https://example.com/apply-gift-card', req.getEndpoint()); + System.assertEquals('POST', req.getMethod()); + + // Create a fake response + HttpResponse res = new HttpResponse(); + res.setHeader('Content-Type', 'application/json'); + res.setBody('{"giftCardReference":"111111111","availableAmount":20}'); + res.setStatusCode(200); + return res; + } +} \ No newline at end of file diff --git a/commerce/domain/checkout/giftcard/GiftcardAdapterUnitTest.cls b/commerce/domain/checkout/giftcard/GiftcardAdapterUnitTest.cls index 1c1702f..5467757 100644 --- a/commerce/domain/checkout/giftcard/GiftcardAdapterUnitTest.cls +++ b/commerce/domain/checkout/giftcard/GiftcardAdapterUnitTest.cls @@ -22,6 +22,45 @@ global class GiftcardAdapterUnitTest{ System.assertEquals(applyResponse.getErrorMessage(), null); } + @IsTest + public static void applyGiftCardWithThirdPartyServiceSuccess(){ + CartExtension.ApplyGiftCardRequest applyRequest = new CartExtension.ApplyGiftCardRequest('111111111', '1234', 50,'USD'); + GiftCardAdapter adapter = new GiftCardAdapter(); + + adapter.shouldUseHttpService(true); + + // Set mock callout class + System.Test.setMock(HttpCalloutMock.class, new GiftCardMockHttpResponseSuccess()); + // Act + Test.startTest(); + CartExtension.ApplyGiftCardResponse applyResponse = adapter.applyGiftCard(applyRequest); + Test.stopTest(); + + System.assertEquals(applyResponse.getGiftCardReference(), '111111111'); + System.assertEquals(applyResponse.getStatus(), 'Success'); + System.assertEquals(applyResponse.getAppliedAmount(), 20); + System.assertEquals(applyResponse.getLastFour(), '1111'); + + + } + + @IsTest + public static void applyGiftCardWithThirdPartyServiceFail(){ + CartExtension.ApplyGiftCardRequest applyRequest = new CartExtension.ApplyGiftCardRequest('111111111', '1234', 50,'USD'); + GiftCardAdapter adapter = new GiftCardAdapter(); + + adapter.shouldUseHttpService(true); + + // Set mock callout class + System.Test.setMock(HttpCalloutMock.class, new GiftCardMockHttpResponseFail()); + // Act + Test.startTest(); + CartExtension.ApplyGiftCardResponse applyResponse = adapter.applyGiftCard(applyRequest); + Test.stopTest(); + + System.assertEquals(applyResponse.getStatus(), 'Fail'); + } + @IsTest public static void applyGiftCardWithDefaultImplementation(){ CartExtension.ApplyGiftCardRequest applyRequest = new CartExtension.ApplyGiftCardRequest('987654321', '1234', 50,'USD'); From c8f3a98443351f44b9145d88a299f3b6ca04bd68 Mon Sep 17 00:00:00 2001 From: alakhmani Date: Tue, 11 Feb 2025 16:27:23 -0800 Subject: [PATCH 68/69] review comments --- commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls | 1 - 1 file changed, 1 deletion(-) diff --git a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls index 5194cd3..9b63570 100644 --- a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls +++ b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls @@ -36,7 +36,6 @@ global class GiftCardAdapter extends CartExtension.GiftCardAdapter{ applyResponse.setGiftCardReference('123456789'); applyResponse.setAppliedAmount(50); applyResponse.setLastFour('6789'); - applyResponse.setCreditType('GiftCard'); applyResponse.setStatus('Success'); return applyResponse; From cd816ee3534a13703191b21fa4f6f2782227dcc8 Mon Sep 17 00:00:00 2001 From: alakhmani Date: Tue, 11 Feb 2025 16:30:36 -0800 Subject: [PATCH 69/69] review comments --- commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls index 9b63570..51ba931 100644 --- a/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls +++ b/commerce/domain/checkout/giftcard/GiftCardAdapterSample.cls @@ -3,6 +3,12 @@ In this sample implementation of applyGiftCard, if the https service is registered , we make a call to that third party service with giftCardNumber and pin passed as the input in the request. The response is expected to have appliedAmount and giftCardReference. If https service is not registered then we return a hardcoded values. + + ApplyGiftCardRequest : this is the input to applyGiftCard method. request will have giftCardNumber, giftCardPin + and amount set + + ApplyGiftCardResponse : This is the output type from applyGiftCard method. Response will have giftCardReference, + appliedAmount, Success or Fail status set along with errorMessage in case of Fail status. */ global class GiftCardAdapter extends CartExtension.GiftCardAdapter{