mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Commits Summary Commit 1: fix(checkout): add complete price structure for reward delivery orders File: libs/checkout/data-access/src/lib/services/shopping-cart.service.ts - Fixed incomplete price object when adding reward items to cart - Added currency (EUR) and currencySymbol (€) to price value - This resolves the API error: "Error converting value 0 to type PriceDTO" Commit 2: feat(checkout): enable delivery options for reward shop items Files: - libs/checkout/feature/reward-catalog/src/lib/reward-action/reward-action.component.ts - libs/checkout/feature/reward-shopping-cart/src/lib/reward-shopping-cart-item/reward-shopping-cart-item.component.ts - libs/checkout/feature/reward-order-confirmation/src/lib/reward-order-confirmation.component.ts Changes: - Re-enabled 'delivery' and 'dig-delivery' purchase options for reward items - Removed unused import - Applied code formatting Related work items: #5405
398 lines
13 KiB
TypeScript
398 lines
13 KiB
TypeScript
import { inject, Injectable } from '@angular/core';
|
|
import {
|
|
ItemsResult,
|
|
StoreCheckoutShoppingCartService,
|
|
ItemPayload,
|
|
AddToShoppingCartDTO,
|
|
UpdateShoppingCartItemDTO,
|
|
} from '@generated/swagger/checkout-api';
|
|
import {
|
|
AddItemToShoppingCartParams,
|
|
AddItemToShoppingCartParamsSchema,
|
|
CanAddItemsToShoppingCartParams,
|
|
CanAddItemsToShoppingCartParamsSchema,
|
|
RemoveShoppingCartItemParams,
|
|
RemoveShoppingCartItemParamsSchema,
|
|
UpdateShoppingCartItemParams,
|
|
UpdateShoppingCartItemParamsSchema,
|
|
} from '../schemas';
|
|
import { RewardSelectionItem, ShoppingCart, ShoppingCartItem } from '../models';
|
|
import { ResponseArgsError, takeUntilAborted } from '@isa/common/data-access';
|
|
import { firstValueFrom } from 'rxjs';
|
|
import { logger } from '@isa/core/logging';
|
|
import { CheckoutMetadataService } from './checkout-metadata.service';
|
|
import { getItemKey } from '../helpers';
|
|
|
|
@Injectable({ providedIn: 'root' })
|
|
export class ShoppingCartService {
|
|
#logger = logger(() => ({ service: 'ShoppingCartService' }));
|
|
#storeCheckoutShoppingCartService = inject(StoreCheckoutShoppingCartService);
|
|
#checkoutMetadataService = inject(CheckoutMetadataService);
|
|
|
|
async createShoppingCart(): Promise<ShoppingCart> {
|
|
const req$ =
|
|
this.#storeCheckoutShoppingCartService.StoreCheckoutShoppingCartCreateShoppingCart();
|
|
|
|
const res = await firstValueFrom(req$);
|
|
|
|
if (res.error) {
|
|
const err = new ResponseArgsError(res);
|
|
this.#logger.error('Failed to create shopping cart', err);
|
|
throw err;
|
|
}
|
|
|
|
return res.result as ShoppingCart;
|
|
}
|
|
|
|
async getShoppingCart(
|
|
shoppingCartId: number,
|
|
abortSignal?: AbortSignal,
|
|
): Promise<ShoppingCart | undefined> {
|
|
let req$ =
|
|
this.#storeCheckoutShoppingCartService.StoreCheckoutShoppingCartGetShoppingCart(
|
|
{
|
|
shoppingCartId,
|
|
},
|
|
);
|
|
|
|
if (abortSignal) {
|
|
req$ = req$.pipe(takeUntilAborted(abortSignal));
|
|
}
|
|
|
|
const res = await firstValueFrom(req$);
|
|
|
|
if (res.error) {
|
|
const err = new ResponseArgsError(res);
|
|
this.#logger.error('Failed to fetch shopping cart', err);
|
|
throw err;
|
|
}
|
|
return res.result as ShoppingCart;
|
|
}
|
|
|
|
async canAddItems(
|
|
params: CanAddItemsToShoppingCartParams,
|
|
): Promise<ItemsResult[]> {
|
|
const parsed = CanAddItemsToShoppingCartParamsSchema.parse(params);
|
|
const req$ =
|
|
this.#storeCheckoutShoppingCartService.StoreCheckoutShoppingCartCanAddItems(
|
|
{
|
|
shoppingCartId: parsed.shoppingCartId,
|
|
payload: parsed.payload as ItemPayload[],
|
|
},
|
|
);
|
|
|
|
const res = await firstValueFrom(req$);
|
|
|
|
if (res.error) {
|
|
const err = new ResponseArgsError(res);
|
|
this.#logger.error(
|
|
'Failed to check if items can be added to shopping cart',
|
|
err,
|
|
);
|
|
throw err;
|
|
}
|
|
|
|
return res.result as unknown as ItemsResult[];
|
|
}
|
|
|
|
async addItem(params: AddItemToShoppingCartParams): Promise<ShoppingCart> {
|
|
const parsed = AddItemToShoppingCartParamsSchema.parse(params);
|
|
const req$ =
|
|
this.#storeCheckoutShoppingCartService.StoreCheckoutShoppingCartAddItemToShoppingCart(
|
|
{
|
|
shoppingCartId: parsed.shoppingCartId,
|
|
items: parsed.items as AddToShoppingCartDTO[],
|
|
},
|
|
);
|
|
|
|
const res = await firstValueFrom(req$);
|
|
|
|
if (res.error) {
|
|
const err = new ResponseArgsError(res);
|
|
this.#logger.error('Failed to add item to shopping cart', err);
|
|
throw err;
|
|
}
|
|
|
|
return res.result as ShoppingCart;
|
|
}
|
|
|
|
async updateItem(
|
|
params: UpdateShoppingCartItemParams,
|
|
): Promise<ShoppingCart> {
|
|
console.log('UpdateShoppingCartItemParams', params);
|
|
const parsed = UpdateShoppingCartItemParamsSchema.parse(params);
|
|
|
|
const req$ =
|
|
this.#storeCheckoutShoppingCartService.StoreCheckoutShoppingCartUpdateShoppingCartItem(
|
|
{
|
|
shoppingCartId: parsed.shoppingCartId,
|
|
shoppingCartItemId: parsed.shoppingCartItemId,
|
|
values: parsed.values as UpdateShoppingCartItemDTO,
|
|
},
|
|
);
|
|
|
|
const res = await firstValueFrom(req$);
|
|
|
|
if (res.error) {
|
|
const err = new ResponseArgsError(res);
|
|
this.#logger.error('Failed to update shopping cart item', err);
|
|
throw err;
|
|
}
|
|
|
|
return res.result as ShoppingCart;
|
|
}
|
|
|
|
async removeItem(
|
|
params: RemoveShoppingCartItemParams,
|
|
): Promise<ShoppingCart> {
|
|
const parsed = RemoveShoppingCartItemParamsSchema.parse(params);
|
|
const req$ =
|
|
this.#storeCheckoutShoppingCartService.StoreCheckoutShoppingCartUpdateShoppingCartItem(
|
|
{
|
|
shoppingCartId: parsed.shoppingCartId,
|
|
shoppingCartItemId: parsed.shoppingCartItemId,
|
|
values: {
|
|
quantity: 0,
|
|
},
|
|
},
|
|
);
|
|
|
|
const res = await firstValueFrom(req$);
|
|
|
|
if (res.error) {
|
|
const err = new ResponseArgsError(res);
|
|
this.#logger.error('Failed to remove item from shopping cart', err);
|
|
throw err;
|
|
}
|
|
|
|
return res.result as ShoppingCart;
|
|
}
|
|
|
|
async completeRewardSelection({
|
|
tabId,
|
|
rewardSelectionItems,
|
|
}: {
|
|
tabId: number;
|
|
rewardSelectionItems: RewardSelectionItem[];
|
|
}) {
|
|
// Fetch or create both shopping cart IDs
|
|
const shoppingCartId = await this.#getOrCreateShoppingCartId(tabId);
|
|
const rewardShoppingCartId =
|
|
await this.#getOrCreateRewardShoppingCartId(tabId);
|
|
|
|
// Get Current Carts
|
|
const currentCart = await this.getShoppingCart(shoppingCartId);
|
|
const currentRewardCart = await this.getShoppingCart(rewardShoppingCartId);
|
|
|
|
// Get Current Items from Cart
|
|
const currentCartItems = currentCart?.items?.map((item) => item.data) ?? [];
|
|
const currentRewardCartItems =
|
|
currentRewardCart?.items?.map((item) => item.data) ?? [];
|
|
|
|
for (const selectionItem of rewardSelectionItems) {
|
|
// Search by Key needed because of different ShoppingCartItem IDs the items have in the different carts
|
|
const selectionItemKey = getItemKey(selectionItem.item);
|
|
const currentInCart = currentCartItems.find(
|
|
(item) => item && getItemKey(item) === selectionItemKey,
|
|
);
|
|
const currentInRewardCart = currentRewardCartItems.find(
|
|
(item) => item && getItemKey(item) === selectionItemKey,
|
|
);
|
|
|
|
// Handle regular cart
|
|
await this.#handleCart({
|
|
shoppingCartId,
|
|
itemId: currentInCart?.id,
|
|
currentCartItem: currentInCart,
|
|
rewardSelectionItem: selectionItem,
|
|
});
|
|
|
|
// Handle reward cart
|
|
await this.#handleRewardCart({
|
|
rewardShoppingCartId,
|
|
itemId: currentInRewardCart?.id,
|
|
currentRewardCartItem: currentInRewardCart,
|
|
rewardSelectionItem: selectionItem,
|
|
});
|
|
}
|
|
}
|
|
|
|
async #handleCart({
|
|
shoppingCartId,
|
|
itemId,
|
|
currentCartItem,
|
|
rewardSelectionItem,
|
|
}: {
|
|
shoppingCartId: number;
|
|
itemId: number | undefined;
|
|
currentCartItem: ShoppingCartItem | undefined;
|
|
rewardSelectionItem: RewardSelectionItem;
|
|
}) {
|
|
const desiredCartQuantity = rewardSelectionItem.cartQuantity;
|
|
if (currentCartItem && itemId) {
|
|
const currentQuantity = currentCartItem.quantity;
|
|
if (desiredCartQuantity !== currentQuantity) {
|
|
if (desiredCartQuantity === 0) {
|
|
this.#logger.info('Removing item from regular cart', () => ({
|
|
shoppingCartId,
|
|
itemId,
|
|
currentQuantity,
|
|
}));
|
|
await this.removeItem({
|
|
shoppingCartId,
|
|
shoppingCartItemId: itemId,
|
|
});
|
|
} else {
|
|
this.#logger.info('Updating item quantity in regular cart', () => ({
|
|
shoppingCartId,
|
|
itemId,
|
|
currentQuantity,
|
|
desiredCartQuantity,
|
|
}));
|
|
await this.updateItem({
|
|
shoppingCartId,
|
|
shoppingCartItemId: itemId,
|
|
values: { quantity: desiredCartQuantity },
|
|
});
|
|
}
|
|
}
|
|
} else if (desiredCartQuantity > 0) {
|
|
this.#logger.info('Adding item to regular cart', () => ({
|
|
shoppingCartId,
|
|
itemId,
|
|
desiredCartQuantity,
|
|
productNumber: rewardSelectionItem?.item?.product?.catalogProductNumber,
|
|
}));
|
|
await this.addItem({
|
|
shoppingCartId,
|
|
items: [
|
|
{
|
|
destination: rewardSelectionItem.item.destination,
|
|
product: {
|
|
...rewardSelectionItem.item.product,
|
|
catalogProductNumber:
|
|
rewardSelectionItem?.item?.product?.catalogProductNumber ??
|
|
String(rewardSelectionItem?.item?.id),
|
|
},
|
|
availability: {
|
|
...rewardSelectionItem.item.availability,
|
|
price:
|
|
rewardSelectionItem?.availabilityPrice ??
|
|
rewardSelectionItem?.catalogPrice,
|
|
},
|
|
promotion: rewardSelectionItem?.item?.promotion,
|
|
quantity: desiredCartQuantity,
|
|
loyalty: undefined,
|
|
},
|
|
],
|
|
});
|
|
}
|
|
}
|
|
|
|
async #handleRewardCart({
|
|
rewardShoppingCartId,
|
|
itemId,
|
|
currentRewardCartItem,
|
|
rewardSelectionItem,
|
|
}: {
|
|
rewardShoppingCartId: number;
|
|
itemId: number | undefined;
|
|
currentRewardCartItem: ShoppingCartItem | undefined;
|
|
rewardSelectionItem: RewardSelectionItem;
|
|
}) {
|
|
const desiredRewardCartQuantity = rewardSelectionItem.rewardCartQuantity;
|
|
if (currentRewardCartItem && itemId) {
|
|
const currentQuantity = currentRewardCartItem.quantity;
|
|
if (desiredRewardCartQuantity !== currentQuantity) {
|
|
if (desiredRewardCartQuantity === 0) {
|
|
this.#logger.info('Removing item from reward cart', () => ({
|
|
rewardShoppingCartId,
|
|
itemId,
|
|
currentQuantity,
|
|
}));
|
|
await this.removeItem({
|
|
shoppingCartId: rewardShoppingCartId,
|
|
shoppingCartItemId: itemId,
|
|
});
|
|
} else {
|
|
this.#logger.info('Updating item quantity in reward cart', () => ({
|
|
rewardShoppingCartId,
|
|
itemId,
|
|
currentQuantity,
|
|
desiredRewardCartQuantity,
|
|
}));
|
|
await this.updateItem({
|
|
shoppingCartId: rewardShoppingCartId,
|
|
shoppingCartItemId: itemId,
|
|
values: { quantity: desiredRewardCartQuantity },
|
|
});
|
|
}
|
|
}
|
|
} else if (desiredRewardCartQuantity > 0) {
|
|
this.#logger.info('Adding item to reward cart', () => ({
|
|
rewardShoppingCartId,
|
|
itemId,
|
|
desiredRewardCartQuantity,
|
|
rewardPoints: rewardSelectionItem.catalogRewardPoints,
|
|
productNumber: rewardSelectionItem?.item?.product?.catalogProductNumber,
|
|
}));
|
|
await this.addItem({
|
|
shoppingCartId: rewardShoppingCartId,
|
|
items: [
|
|
{
|
|
destination: rewardSelectionItem.item.destination,
|
|
product: {
|
|
...rewardSelectionItem.item.product,
|
|
catalogProductNumber:
|
|
rewardSelectionItem?.item?.product?.catalogProductNumber ??
|
|
String(rewardSelectionItem?.item?.id),
|
|
},
|
|
quantity: desiredRewardCartQuantity,
|
|
promotion: undefined, // If loyalty is set, we need to remove promotion
|
|
loyalty: { value: rewardSelectionItem.catalogRewardPoints }, // Set loyalty points from item
|
|
availability: {
|
|
...rewardSelectionItem.item.availability,
|
|
price: {
|
|
value: {
|
|
value: 0,
|
|
currency: 'EUR',
|
|
currencySymbol: '€',
|
|
},
|
|
vat: undefined,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
}
|
|
|
|
async #getOrCreateShoppingCartId(tabId: number): Promise<number> {
|
|
const shoppingCartId =
|
|
this.#checkoutMetadataService.getShoppingCartId(tabId);
|
|
|
|
if (shoppingCartId) {
|
|
return shoppingCartId;
|
|
}
|
|
|
|
const shoppingCart = await this.createShoppingCart();
|
|
this.#checkoutMetadataService.setShoppingCartId(tabId!, shoppingCart.id);
|
|
return shoppingCart.id!;
|
|
}
|
|
|
|
async #getOrCreateRewardShoppingCartId(tabId: number): Promise<number> {
|
|
const rewardShoppingCartId =
|
|
this.#checkoutMetadataService.getRewardShoppingCartId(tabId);
|
|
if (rewardShoppingCartId) {
|
|
return rewardShoppingCartId;
|
|
}
|
|
|
|
const shoppingCart = await this.createShoppingCart();
|
|
this.#checkoutMetadataService.setRewardShoppingCartId(
|
|
tabId!,
|
|
shoppingCart.id,
|
|
);
|
|
return shoppingCart.id!;
|
|
}
|
|
}
|