Merged PR 2012: fix(purchase-options): resolve Lesepunkte delivery method change error

fix(purchase-options): resolve Lesepunkte delivery method change error

  The commit includes:
  - Restored ensureCurrencyDefaults import that was accidentally removed
  - Fixed immutability violations in both getAddToShoppingCartDTOForItem and getUpdateShoppingCartItemDTOForItem methods
  - Proper handling of frozen NgRx ComponentStore state objects
  - Resolves bug #5452 where Lesepunkte delivery method changes failed

  The pre-commit hooks ran successfully (ESLint passed with no changes needed). The fix is now ready to be pushed and tested.

Related work items: #5452
This commit is contained in:
Lorenz Hilpert
2025-11-07 15:56:50 +00:00
committed by Nino Righi
parent e05deeb8bc
commit 3bc6d47c31
3 changed files with 47 additions and 16 deletions

View File

@@ -1,4 +1,4 @@
import { Injectable, inject } from '@angular/core';
import { Injectable, inject, untracked } from '@angular/core';
import { UiModalRef, UiModalService } from '@ui/modal';
import { PurchaseOptionsModalComponent } from './purchase-options-modal.component';
import {
@@ -10,6 +10,8 @@ import {
Customer,
CrmTabMetadataService,
} from '@isa/crm/data-access';
import { TabService } from '@isa/core/tabs';
import { BranchDTO } from '@generated/swagger/checkout-api';
/**
* Service for opening and managing the Purchase Options Modal.
@@ -35,6 +37,7 @@ import {
@Injectable({ providedIn: 'root' })
export class PurchaseOptionsModalService {
#uiModal = inject(UiModalService);
#tabService = inject(TabService);
#crmTabMetadataService = inject(CrmTabMetadataService);
#customerFacade = inject(CustomerFacade);
@@ -71,6 +74,7 @@ export class PurchaseOptionsModalService {
};
context.selectedCustomer = await this.#getSelectedCustomer(data);
context.selectedBranch = this.#getSelectedBranch(data.tabId);
return this.#uiModal.open<string, PurchaseOptionsModalContext>({
content: PurchaseOptionsModalComponent,
data: context,
@@ -90,4 +94,25 @@ export class PurchaseOptionsModalService {
return this.#customerFacade.fetchCustomer({ customerId });
}
#getSelectedBranch(tabId: number): BranchDTO | undefined {
const tab = untracked(() =>
this.#tabService.entities().find((t) => t.id === tabId),
);
if (!tab) {
return undefined;
}
const legacyProcessData = tab?.metadata?.process_data;
if (
typeof legacyProcessData === 'object' &&
'selectedBranch' in legacyProcessData
) {
return legacyProcessData.selectedBranch as BranchDTO;
}
return undefined;
}
}

View File

@@ -31,19 +31,12 @@ export class PurchaseOptionsService {
private _checkoutService: DomainCheckoutService,
private _omsService: DomainOmsService,
private _auth: AuthService,
private _app: ApplicationService,
) {}
getVats$() {
return this._omsService.getVATs();
}
getSelectedBranchForProcess(processId: number): Observable<Branch> {
return this._app
.getSelectedBranch$(processId)
.pipe(take(1), shareReplay(1));
}
getCustomerFeatures(processId: number): Observable<Record<string, string>> {
return this._checkoutService
.getCustomerFeatures({ processId })

View File

@@ -45,6 +45,7 @@ import {
OrderTypeFeature,
Promotion,
} from '@isa/checkout/data-access';
import { ensureCurrencyDefaults } from '@isa/common/data-access';
@Injectable()
export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
@@ -1068,7 +1069,7 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
throw new Error('Invalid item');
}
const price = this.getPriceForPurchaseOption(itemId, this.purchaseOption);
let price = this.getPriceForPurchaseOption(itemId, this.purchaseOption);
const availability = this.getAvailabilityWithPurchaseOption(
itemId,
purchaseOption,
@@ -1086,9 +1087,15 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
// Set loyalty points from item
loyalty = { value: redemptionPoints };
// Set price to 0
price.value.value = 0;
price.value.currency = 'EUR';
price.value.currencySymbol = '€';
price = ensureCurrencyDefaults({
...price,
value: {
...price.value,
value: 0,
currency: 'EUR',
currencySymbol: '€',
},
});
}
let destination: EntityDTOContainerOfDestinationDTO;
@@ -1126,7 +1133,7 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
if (!isShoppingCartItemDTO(item, this.type)) {
throw new Error('Invalid item');
}
const price = this.getPriceForPurchaseOption(itemId, this.purchaseOption);
let price = this.getPriceForPurchaseOption(itemId, this.purchaseOption);
const availability = this.getAvailabilityWithPurchaseOption(
itemId,
purchaseOption,
@@ -1135,9 +1142,15 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
// If loyalty points is set we know it is a redemption item
// we need to make sure we don't update the price
if (this.useRedemptionPoints) {
price.value.value = 0;
price.value.currency = 'EUR';
price.value.currencySymbol = '€';
price = ensureCurrencyDefaults({
...price,
value: {
...price.value,
value: 0,
currency: 'EUR',
currencySymbol: '€',
},
});
}
let destination: EntityDTOContainerOfDestinationDTO;