Merge pull request #753 from Safelite/feature/digital/SSR-1183
SSR-1183 ITAC Pricing Endpoint
This commit is contained in:
commit
e2116b8339
10 changed files with 141 additions and 86 deletions
|
|
@ -76,6 +76,10 @@ const endpoints = Object.freeze({
|
|||
url: `${PRICE_BASE_URL}/order-items-with-insurance-pricing`,
|
||||
method: 'GET'
|
||||
},
|
||||
GetITACPriceOrderItems: {
|
||||
url: `${PRICE_BASE_URL}/order-items-with-itac-pricing`,
|
||||
method: 'POST'
|
||||
},
|
||||
GetProviders: {
|
||||
url: `${LOCATION_BASE_URL}/providers`,
|
||||
method: 'GET'
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export async function getPricedMobileFeePart(serviceZipCode) {
|
|||
|
||||
// Get the Mobile Fee Part Price
|
||||
const pricingResults = await useMainStore()
|
||||
.getPriceOrderItems([mobileFeePart.data]);
|
||||
.getInsurancePriceOrderItems([mobileFeePart.data]);
|
||||
|
||||
return Promise.resolve(pricingResults[0]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -668,32 +668,7 @@ describe('coverageStatement.vue', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
describe('ITAC CoverageType', () => {
|
||||
test('set ITAC coverageType once component is initialized', async () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
currentDeductible: 666
|
||||
}
|
||||
};
|
||||
getPriceOfLineItems.mockReturnValue(333);
|
||||
const { wrapper } = getMountedComponent(mainInitialState);
|
||||
|
||||
// Act
|
||||
await wrapper.vm.initializeComponent();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.mainStore.updateCoverageType)
|
||||
.toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(wrapper.vm.mainStore.updateCoverageType)
|
||||
.toHaveBeenCalledWith(coverageType.ITAC);
|
||||
});
|
||||
|
||||
describe('claim registration api call', () => {
|
||||
it('Loaded duplicate with previously registered claim => claim registration is not called', async () => {
|
||||
// Arrange
|
||||
const sellingPrice = getRandomInt(50, 100);
|
||||
|
|
@ -722,7 +697,14 @@ describe('coverageStatement.vue', () => {
|
|||
useMainStore().getWipers = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getRainDefense = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getSupportingItems = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getPriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve([
|
||||
useMainStore().getInsurancePriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve([
|
||||
{
|
||||
sellingPrice,
|
||||
kitPrice: 0,
|
||||
laborAmount: 0
|
||||
}
|
||||
]));
|
||||
useMainStore().getITACPriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve([
|
||||
{
|
||||
sellingPrice,
|
||||
kitPrice: 0,
|
||||
|
|
@ -743,15 +725,14 @@ describe('coverageStatement.vue', () => {
|
|||
// Assert
|
||||
expect(wrapper.vm.mainStore.registerClaim).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
describe('claim registration api call', () => {
|
||||
it('claim registration not required => method not called', async () => {
|
||||
// Arrange
|
||||
const mockStoreActions = () => {
|
||||
useMainStore().getWipers = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getRainDefense = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getSupportingItems = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getPriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getInsurancePriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getITACPriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
};
|
||||
const { wrapper } = getMountedComponent({
|
||||
issConfig: {
|
||||
|
|
@ -796,7 +777,8 @@ describe('coverageStatement.vue', () => {
|
|||
useMainStore().getWipers = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getRainDefense = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getSupportingItems = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getPriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getInsurancePriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
useMainStore().getITACPriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve());
|
||||
};
|
||||
const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions);
|
||||
const next = (method) => { method(wrapper.vm); };
|
||||
|
|
|
|||
|
|
@ -124,7 +124,6 @@ import widgetFields from '@/constants/cms-widget-fields.js';
|
|||
import { formatAmountInDollars } from '@/helpers/text-helper.js';
|
||||
import showIssLoadingModal from '@/helpers/loading-modal-helper';
|
||||
import { getPriceOfLineItems } from '@/helpers/price-calculator';
|
||||
import coverageType from '@/constants/coverage-type';
|
||||
import coverageStatuses from '@/constants/coverage-statuses';
|
||||
|
||||
const SAFELITE_PROVIDER = 'Safelite';
|
||||
|
|
@ -146,8 +145,7 @@ export default {
|
|||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to?.query?.issPage);
|
||||
const supportingItemsPromise =
|
||||
await useMainStore().getSupportingItems();
|
||||
const supportingItemsPromise = await useMainStore().getSupportingItems();
|
||||
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
|
|
@ -162,53 +160,23 @@ export default {
|
|||
];
|
||||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
const clonedGlassParts = useMainStore().lineItems.glassParts
|
||||
? JSON.parse(JSON.stringify(useMainStore().lineItems.glassParts))
|
||||
: [];
|
||||
// TODO SSR-1165: Recycle fee needs removed from quote calculation
|
||||
const availableLineItems = [
|
||||
...(resultMap.supportingItems ?? []),
|
||||
...(clonedGlassParts ?? [])
|
||||
];
|
||||
|
||||
let hasBailedOut = false;
|
||||
let pricingResults = [];
|
||||
const { vehicle, isPolicyLookupSuccessful, isITAC, isDeductible } = useMainStore();
|
||||
if (isPolicyLookupSuccessful && vehicle.policyVehicleId >= 0) {
|
||||
if (isITAC || isDeductible) {
|
||||
await useMainStore().getFinalDeductible();
|
||||
}
|
||||
|
||||
pricingResults = await useMainStore()
|
||||
.getPriceOrderItems(availableLineItems)
|
||||
.catch((err) => {
|
||||
useMainStore().setBailout(bailoutMessage.pricingResponseError(
|
||||
availableLineItems.map((li) => li.partNumber),
|
||||
{
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
data: err.data
|
||||
}
|
||||
));
|
||||
hasBailedOut = true;
|
||||
next(`/?issPage=${issPageValues.BAILOUT_PAGE}`);
|
||||
});
|
||||
}
|
||||
|
||||
if (!hasBailedOut) {
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next(async (vm) => {
|
||||
showIssLoadingModal(true);
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.setSupportingItems(resultMap.supportingItems);
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
vm.setBaseServiceLineItems(pricingResults);
|
||||
await vm.initializeComponent();
|
||||
if (!vm.unverified) {
|
||||
useMainStore().disableKeyFields();
|
||||
}
|
||||
});
|
||||
}
|
||||
next(async (vm) => {
|
||||
showIssLoadingModal(true);
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.setSupportingItems(resultMap.supportingItems);
|
||||
await vm.initializeComponent();
|
||||
if (!vm.unverified) {
|
||||
useMainStore().disableKeyFields();
|
||||
}
|
||||
});
|
||||
},
|
||||
setup() {
|
||||
const mainStore = useMainStore();
|
||||
|
|
@ -372,12 +340,30 @@ export default {
|
|||
this.mainStore.updateCoverageStatus(coverageStatuses.VERIFIED);
|
||||
}
|
||||
|
||||
// TODO this should be determined based on the result of the ITAC price call
|
||||
// TODO it seems that ITAC now requires claim registration calls. If this call fails
|
||||
if (this.mainStore.isVerified
|
||||
&& this.mainStore.isDeductible
|
||||
&& this.deductibleValue > this.totalServicePrice) {
|
||||
this.mainStore.updateCoverageType(coverageType.ITAC);
|
||||
if (this.mainStore.isVerified) {
|
||||
const clonedGlassParts = useMainStore().lineItems.glassParts
|
||||
? JSON.parse(JSON.stringify(useMainStore().lineItems.glassParts))
|
||||
: [];
|
||||
const availableLineItems = [
|
||||
...(this.supportingItems ?? []),
|
||||
...(clonedGlassParts ?? [])
|
||||
];
|
||||
|
||||
const pricingResults = await useMainStore()
|
||||
.getITACPriceOrderItems(availableLineItems)
|
||||
.catch((err) => {
|
||||
useMainStore().setBailout(bailoutMessage.pricingResponseError(
|
||||
availableLineItems.map((li) => li.partNumber),
|
||||
{
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
data: err.data
|
||||
}
|
||||
));
|
||||
this.navigateWithScenario(navigationScenarios.PRICING_LOOKUP_ERROR);
|
||||
});
|
||||
|
||||
this.setBaseServiceLineItems(pricingResults);
|
||||
}
|
||||
|
||||
showIssLoadingModal(false);
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ export default {
|
|||
|
||||
let hasBailedOut = false;
|
||||
const pricedVaps = await useMainStore()
|
||||
.getPriceOrderItems(unpricedVaps)
|
||||
.getInsurancePriceOrderItems(unpricedVaps)
|
||||
.catch((err) => {
|
||||
useMainStore().setBailout(bailoutMessage.pricingResponseError(
|
||||
unpricedVaps.map((li) => li.partNumber),
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ export default {
|
|||
|
||||
const premiumFeeWithPricePromise = premiumFeePromise.then((result) => {
|
||||
if (result.data) {
|
||||
return useMainStore().getPriceOrderItems(result.data);
|
||||
return useMainStore().getInsurancePriceOrderItems(result.data);
|
||||
}
|
||||
return result.data;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ export default {
|
|||
|
||||
let hasBailedOut = false;
|
||||
const pricingResults = await store
|
||||
.getPriceOrderItems(availableLineItems)
|
||||
.getInsurancePriceOrderItems(availableLineItems)
|
||||
.catch((err) => {
|
||||
useMainStore().setBailout(bailoutMessage.pricingResponseError(
|
||||
availableLineItems.map((li) => li.partNumber),
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ const navigationScenarios = Object.freeze({
|
|||
// Coverage Statement
|
||||
CLICKED_BACK_WITH_REPAIR: 'CLICKED_BACK_WITH_REPAIR',
|
||||
CLICKED_FORWARD_WITH_INVALID_STATE: 'CLICKED_FORWARD_WITH_INVALID_STATE',
|
||||
PRICING_LOOKUP_ERROR: 'PRICING_LOOKUP_ERROR',
|
||||
|
||||
// TPA Search
|
||||
CLICKED_DO_NOT_SEE_MY_SHOP_LINK: 'CLICKED_DO_NOT_SEE_MY_SHOP_LINK',
|
||||
|
|
|
|||
|
|
@ -535,6 +535,10 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.PRICING_LOOKUP_ERROR,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1036,7 +1036,7 @@ export const useMainStore = defineStore({
|
|||
});
|
||||
},
|
||||
|
||||
async getPriceOrderItems(availableLineItems) {
|
||||
async getInsurancePriceOrderItems(availableLineItems) {
|
||||
const zipCodeToUse = this.order.serviceLocation.zipCode;
|
||||
if (!this.order.serviceLocation.zipCodeCtu) {
|
||||
const zipInfo = await this.validateZip({ zip: zipCodeToUse });
|
||||
|
|
@ -1081,6 +1081,84 @@ export const useMainStore = defineStore({
|
|||
return availableLineItems;
|
||||
},
|
||||
|
||||
async getITACPriceOrderItems(availableLineItems) {
|
||||
const zipCodeToUse = this.order.serviceLocation.zipCode;
|
||||
if (!this.order.serviceLocation.zipCodeCtu) {
|
||||
const zipInfo = await this.validateZip({ zip: zipCodeToUse });
|
||||
this.order.serviceLocation.zipCodeCtu = zipInfo.zipCodeCtu;
|
||||
}
|
||||
|
||||
const { policy, vehicle, contactInfo, serviceLocation, insuranceCoverage } = this.order;
|
||||
|
||||
const response = await globalMethods
|
||||
.callHttpClient({
|
||||
method: endpoints.GetITACPriceOrderItems.method,
|
||||
endpoint: endpoints.GetITACPriceOrderItems.url,
|
||||
payload: {
|
||||
Eon: this.order.eon,
|
||||
ReferralNumber: this.order.referralNumber,
|
||||
ReferralSequenceNumber: this.order.referralSequenceNumber,
|
||||
ReferralDate: `${this.order.referralDate}Z`,
|
||||
lineItems: availableLineItems,
|
||||
ServerData: this.order.lineItems.serverData,
|
||||
ServiceZipCode: zipCodeToUse,
|
||||
Customer: {
|
||||
PhoneNumber: contactInfo.servicePhone,
|
||||
State: policy.damageState,
|
||||
ZipCode: policy.policyZipCode
|
||||
},
|
||||
Provider: {
|
||||
ProviderNumber: serviceLocation.zipCodeCtu,
|
||||
ProviderCtuNumber: serviceLocation.zipCodeCtu
|
||||
},
|
||||
Vehicle: {
|
||||
CarId: vehicle.carId,
|
||||
Year: vehicle.year,
|
||||
Make: vehicle.make,
|
||||
Model: vehicle.model
|
||||
},
|
||||
Insurance: {
|
||||
AccountNumber: this.order.parentAccountNumber,
|
||||
Deductible: this.order.currentDeductible ?? 0,
|
||||
LineOfBusiness: 'PERSONAL',
|
||||
PolicyNumber: policy.policyNumber,
|
||||
CoverageStatus: insuranceCoverage.coverageStatus,
|
||||
IsNoComp: this.isNoComp
|
||||
}
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
throw error;
|
||||
});
|
||||
|
||||
const { lineItems, serverData, isItac, primaryBillToNumber } = response.data;
|
||||
|
||||
if (serverData) {
|
||||
this.order.lineItems.serverData = serverData;
|
||||
}
|
||||
|
||||
if (isItac) {
|
||||
// if the ITAC Pricing API call returns isItac = true then we should switch to ITAC coverage type
|
||||
this.updateCoverageType(coverageType.ITAC);
|
||||
} else if (this.isITAC) {
|
||||
// If the ITAC pricing API call returns isItac = false, and we are currently ITAC in the store then we should switch to deductible coverage type
|
||||
this.updateCoverageType(coverageType.Deductible);
|
||||
}
|
||||
|
||||
if (primaryBillToNumber && primaryBillToNumber.length !== 0) {
|
||||
this.issConfig.billToAccountNumber = primaryBillToNumber;
|
||||
} else {
|
||||
await this.getBillToInfo();
|
||||
}
|
||||
|
||||
if (lineItems) {
|
||||
const retAvailableLineItems = addPricesToLineItems(availableLineItems, lineItems);
|
||||
return retAvailableLineItems;
|
||||
}
|
||||
|
||||
return availableLineItems;
|
||||
},
|
||||
|
||||
getMobileFeePart() {
|
||||
const damageType = this.damage.isRepair ? 'Repair' : 'Replace';
|
||||
|
||||
|
|
@ -1670,7 +1748,7 @@ export const useMainStore = defineStore({
|
|||
)
|
||||
];
|
||||
|
||||
await this.getPriceOrderItems(lineItemsToTax);
|
||||
await this.getInsurancePriceOrderItems(lineItemsToTax);
|
||||
await this.getTaxOrderItems(lineItemsToTax);
|
||||
},
|
||||
|
||||
|
|
@ -2295,7 +2373,7 @@ export const useMainStore = defineStore({
|
|||
providerNumber: order.serviceLocation.zipCodeCtu.toString(),
|
||||
typeOfClaim: 'GLASS ONLY',
|
||||
lineOfBusiness: 'PERSONAL',
|
||||
isItac: false
|
||||
isItac: this.isITAC
|
||||
});
|
||||
|
||||
const response = await globalMethods.callHttpClient({
|
||||
|
|
|
|||
Loading…
Reference in a new issue