SSR-1146. Payment Method page: Add sales tax to the cart-dropdown component. (#604)

This commit is contained in:
Johan Gunawan 2024-04-01 17:21:08 -04:00 committed by GitHub
parent ff047d7e15
commit d02ac7fb50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 260 additions and 58 deletions

View file

@ -44,7 +44,8 @@ module.exports = {
math: 'always'
}],
'import/extensions': ['error', 'always', { js: 'ignorePackages' }],
'no-param-reassign': ['error', { props: true, ignorePropertyModificationsFor: ['item'] }]
'no-param-reassign': ['error', { props: true, ignorePropertyModificationsFor: ['item'] }],
'no-restricted-syntax': ['off', 'ForOfStatement']
},
settings: {
'import/resolver': {

View file

@ -2,12 +2,7 @@
exports[`cart-dropdown component initial data rendered as expected 1`] = `
Object {
"baseServiceLineItems": Array [],
"deductible": null,
"glassToReplace": Array [],
"isExpanded": false,
"isRepair": null,
"vapsInOrder": Array [],
"widget": Object {
"amountDue": "AmountDueTextWidget",
"basePrice": "BasePriceWidget",

View file

@ -4,7 +4,7 @@ import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
// Supporting Files
import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { useMainStore } from '@/store';
import { useMainStore, getDefaultState } from '@/store';
import coverageStatuses from '@/constants/coverage-statuses';
import { formatAmountInDollars } from '@/helpers/text-helper.js';
import partTypeStrings from '@/constants/part-type-strings';
@ -455,7 +455,193 @@ describe('cart-dropdown component', () => {
// TODO when method implemented
});
describe('salesTax', () => {
// TODO when method implemented
test('salesTax is treated as 0 when line items are null', () => {
// Arrange
const storeData = getDefaultState();
storeData.order.lineItems.glassParts = null;
storeData.order.lineItems.otherParts = null;
storeData.order.lineItems.supportingItems = null;
storeData.order.lineItems.vaps = null;
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.salesTax;
// Assert
expect(result).toBe(0);
});
test("salesTax is treated as 0 when null or undefined is set as the line item's sales tax.", () => {
// Arrange
const storeData = getDefaultState();
storeData.order.lineItems.glassParts = [
{ partType: 'mock', salesTax: null },
{ partType: 'mock', salesTax: undefined }
];
storeData.order.lineItems.supportingItems = [
{ partType: 'mock', salesTax: null },
{ partType: 'mock', salesTax: undefined }
];
storeData.order.lineItems.vaps = [
{ partType: 'mock', salesTax: null },
{ partType: 'mock', salesTax: undefined }
];
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.salesTax;
// Assert
expect(result).toBe(0);
});
test('Returns 0 as sales tax when coverage is unverified and order does not contain vaps.', () => {
// Arrange
const storeData = getDefaultState();
storeData.order.lineItems.glassParts = [
{ partType: 'mock', salesTax: 10 }
];
storeData.order.lineItems.vaps = [];
storeData.order.payment.insuranceCoverage.isVerified = false;
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.salesTax;
// Assert
expect(result).toBe(0);
});
test('Returns sum of vaps sales tax when coverage is unverified and order has vaps.', () => {
// Arrange
const storeData = getDefaultState();
storeData.order.lineItems.glassParts = [
{ partType: 'mock', salesTax: 10 }
];
storeData.order.lineItems.vaps = [
{ partType: 'mock', salesTax: 1 },
{ partType: 'mock', salesTax: 2 }
];
storeData.order.payment.insuranceCoverage.isVerified = false;
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.salesTax;
// Assert
expect(result).toBe(3);
});
test('Returns Recycle Fee tax when coverage is Verified-Deductible, replace service, and no vaps.', () => {
// Arrange
const storeData = getDefaultState();
storeData.order.currentDeductible = 250;
storeData.order.lineItems.glassParts = [
{ partType: 'mock', salesTax: 10, sellingPrice: 150 }
];
storeData.order.lineItems.supportingItems = [
{ partNumber: partNumberStrings.RECYCLE_FEE, partType: 'mock', salesTax: 10, sellingPrice: 39.99 }
];
storeData.order.lineItems.vaps = null;
storeData.order.payment.insuranceCoverage.isVerified = true;
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.salesTax;
// Assert
expect(result).toBe(10);
});
test('Returns sum of vaps + recycle fee sales tax when Verified-Deductible, replace service, and has vaps.', () => {
// Arrange
const storeData = getDefaultState();
storeData.order.currentDeductible = 250;
storeData.order.lineItems.glassParts = [
{ partType: 'mock', salesTax: 10, sellingPrice: 100 }
];
storeData.order.lineItems.otherParts = [
{ partType: 'mock', salesTax: 10, kitPrice: 100 }
];
storeData.order.lineItems.supportingItems = [
{ partNumber: partNumberStrings.RECYCLE_FEE, partType: 'mock', salesTax: 10, sellingPrice: 39.99 },
{ partType: 'mock', salesTax: 10, kitPrice: 100 }
];
storeData.order.lineItems.vaps = [
{ partType: 'mock', salesTax: 2 },
{ partType: 'mock', salesTax: 3 }
];
storeData.order.payment.insuranceCoverage.isVerified = true;
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.salesTax;
// Assert
expect(result).toBe(15);
});
test('Returns sum of sales tax when Verified-ITAC.', () => {
// Arrange
const storeData = getDefaultState();
storeData.order.currentDeductible = 0;
storeData.order.lineItems.glassParts = [
{ partType: 'mock', salesTax: 10, sellingPrice: 100 }
];
storeData.order.lineItems.otherParts = [
{ partType: 'mock', salesTax: 10, kitPrice: 100 }
];
storeData.order.lineItems.supportingItems = [
{ partType: 'mock', salesTax: 10, kitPrice: 100 }
];
storeData.order.lineItems.vaps = [
{ partType: 'mock', salesTax: 5 }
];
storeData.order.payment.insuranceCoverage.isVerified = true;
storeData.order.policy.isITAC = true;
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.salesTax;
// Assert
expect(result).toBe(35);
});
test('Returns sum of sales tax when Verified-NoComp.', () => {
// Arrange
const storeData = getDefaultState();
storeData.order.currentDeductible = 0;
storeData.order.lineItems.glassParts = [
{ partType: 'mock', salesTax: 10, sellingPrice: 100 }
];
storeData.order.lineItems.otherParts = [
{ partType: 'mock', salesTax: 10, kitPrice: 100 }
];
storeData.order.lineItems.supportingItems = [
{ partType: 'mock', salesTax: 10, kitPrice: 100 }
];
storeData.order.lineItems.vaps = [
{ partType: 'mock', salesTax: 5 }
];
storeData.order.payment.insuranceCoverage.isVerified = true;
storeData.order.policy.noCoverage = true;
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.salesTax;
// Assert
expect(result).toBe(35);
});
});
describe('availableLineItems', () => {
test('returns expected when glassParts null', async () => {
@ -997,19 +1183,11 @@ describe('cart-dropdown component', () => {
describe('getDisplayed', () => {
test('returns "Verifying coverage" when not no comp, not itac, and deductible null', () => {
// Arrange
const storeData = {
order: {
payment: {
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED
}
},
policy: {
isITAC: false
},
currentDeductible: null
}
};
const storeData = getDefaultState();
storeData.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.VERIFIED;
storeData.order.policy.isITAC = false;
storeData.order.currentDeductible = null;
const { wrapper } = getMountedComponent(storeData);
const amount = 123;
@ -1021,19 +1199,11 @@ describe('cart-dropdown component', () => {
});
test('returns "Verifying coverage" when not no comp, not itac, and coverage status PENDING', () => {
// Arrange
const storeData = {
order: {
payment: {
insuranceCoverage: {
coverageStatus: coverageStatuses.PENDING
}
},
policy: {
isITAC: false
},
currentDeductible: 321
}
};
const storeData = getDefaultState();
storeData.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING;
storeData.order.policy.isITAC = false;
storeData.order.currentDeductible = 321;
const { wrapper } = getMountedComponent(storeData);
const amount = 123;

View file

@ -173,20 +173,7 @@ export default {
availableVaps: Array
},
data() {
const { currentDeductible } = useMainStore().order;
const { glassToReplace, isRepair } = useMainStore().damage;
const { supportingItems, glassParts, otherParts, vaps } = useMainStore().lineItems;
return {
isRepair,
glassToReplace: glassToReplace ?? [],
vapsInOrder: vaps ?? [],
baseServiceLineItems: [
...(supportingItems ?? []),
...(glassParts ?? []),
...(otherParts ?? [])
],
deductible: currentDeductible,
isExpanded: false,
widget: {
amountDue: 'AmountDueTextWidget',
@ -202,9 +189,20 @@ export default {
};
},
computed: {
deductible() {
// Note: added as computed so it can be used in the template.
return useMainStore().order.currentDeductible;
},
showDeductibleCartItem() {
return !useMainStore().isNoComp && !useMainStore().policy.isITAC;
},
baseServiceLineItems() {
return [
...(useMainStore().lineItems.supportingItems ?? []),
...(useMainStore().lineItems.glassParts ?? []),
...(useMainStore().lineItems.otherParts ?? [])
];
},
baseServicePrice() {
return getPriceOfLineItems(this.baseServiceLineItems);
},
@ -219,7 +217,26 @@ export default {
return basePrice + this.packagePrice;
},
salesTax() {
return 0; // TODO
function sumTax(lineItems) {
return lineItems?.reduce((accumulator, lineItem) => accumulator + (lineItem.salesTax ?? 0), 0) ?? 0;
}
let result = 0;
if (useMainStore().payment.insuranceCoverage.isVerified
&& (useMainStore().isITAC || useMainStore().isNoComp)) {
result += sumTax(this.baseServiceLineItems);
} else {
// deductible or non-verified still need to show tax for Recycle Fee or Mobile Fee
const recycleFeeLineItem = useMainStore().lineItems.supportingItems
?.find((lineItem) => lineItem.partNumber === partNumberStrings.RECYCLE_FEE);
result += recycleFeeLineItem != null ? recycleFeeLineItem.salesTax : 0;
}
result += sumTax(useMainStore().lineItems.vaps ?? []);
return result;
},
amountDue() {
return this.showAsPaid
@ -236,17 +253,17 @@ export default {
},
servicePackageTier() {
return getHighestFullySatisfiedTier(
this.glassToReplace,
useMainStore().damage.glassToReplace ?? [],
this.availableLineItems,
this.isRepair,
this.vapsInOrder
useMainStore().damage.isRepair,
useMainStore().lineItems.vaps ?? []
);
},
servicePackageCartItems() {
const packageContentTypes = getPackageContents(
this.glassToReplace,
useMainStore().damage.glassToReplace ?? [],
this.availableLineItems,
this.isRepair,
useMainStore().damage.isRepair,
this.servicePackageTier
) ?? [];
const items = [];
@ -271,7 +288,7 @@ export default {
return this.getCartItemForVapsPart(partTypeStrings.RAIN_DEFENSE);
},
packagePrice() {
return getPriceOfLineItems(this.vapsInOrder);
return getPriceOfLineItems(useMainStore().lineItems.vaps ?? []);
},
amountDueLabel() {
return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
@ -355,7 +372,7 @@ export default {
},
getCartItemForVapsPart(partType) {
const label = this.getCmsContentForVapsType(partType);
const lineItems = this.vapsInOrder
const lineItems = useMainStore().lineItems.vaps
?.filter((vapsLineItem) => vapsLineItem.partType === partType);
return this.getCartItem(label, lineItems);
},

View file

@ -115,6 +115,8 @@ export default {
const resultMap = await settleAllPromises(promiseResultMap);
await useMainStore().setPriceAndSalesTaxForOrderLineItems();
const unpricedVaps = [...resultMap.wipers, resultMap.rainDefense];
let hasBailedOut = false;

View file

@ -54,7 +54,7 @@ function getTimeSlotsAdditionalEventData(
return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ProvisionalTriggers:${provisionalTriggers.join(',')}`;
}
const getDefaultState = () => ({
export const getDefaultState = () => ({
order: {
vehicle: {
policyVehicleId: null,
@ -1556,11 +1556,28 @@ export const useMainStore = defineStore({
this.order.lineItems.mobileFee = mobileFee;
},
async setPriceAndSalesTaxForOrderLineItems() {
const lineItemsToTax = [
...(this.order.lineItems.supportingItems ?? []),
...(this.order.lineItems.glassParts ?? []),
...(this.order.lineItems.otherParts ?? []),
...(this.order.lineItems.vaps ?? [])
];
if (this.order.lineItems.mobileFee != null) {
lineItemsToTax.push(this.order.lineItems.mobileFee);
}
const pricedLineItemsToTax = await this.getPriceOrderItems(lineItemsToTax);
this.taxOrderItemsAndSaveServerData(pricedLineItemsToTax);
},
updateVehicle(vehicle) {
// Assuming that the method caller pass all the properties.
// otherwise need to check for undefined for every property.
this.vehicle.policyVehicleId = vehicle.policyVehicleId;
this.vehicle.carId = vehicle.carId;
this.order.vehicle.policyVehicleId = vehicle.policyVehicleId;
this.order.vehicle.carId = vehicle.carId;
this.order.vehicle.category = vehicle.category;
this.order.vehicle.year = vehicle.year;
this.order.vehicle.make = vehicle.make;