mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merge branch 'release/4.0' into develop
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
<div class="flex flex-row w-full">
|
||||
<div
|
||||
class="flex flex-row justify-end -mb-4 desktop:mb-0 w-[13.4375rem] desktop:w-full"
|
||||
>
|
||||
@if (quantityDropdownValues().length > 1) {
|
||||
<ui-dropdown
|
||||
class="quantity-dropdown"
|
||||
[disabled]="!canReturnReceiptItem()"
|
||||
[value]="availableQuantity()"
|
||||
[value]="selectedQuantity()"
|
||||
(valueChange)="setQuantity($event)"
|
||||
>
|
||||
@for (quantity of quantityDropdownValues(); track quantity) {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
:host {
|
||||
@apply flex flex-col-reverse items-end desktop:flex-row desktop:justify-center desktop:items-center gap-4;
|
||||
@apply flex flex-col-reverse items-end desktop:flex-row desktop:justify-center desktop:items-center gap-4;
|
||||
|
||||
.product-dropdown.ui-dropdown {
|
||||
@apply max-w-[13.4375rem] desktop:max-w-full;
|
||||
}
|
||||
|
||||
:has(.product-dropdown):has(.quantity-dropdown) {
|
||||
.quantity-dropdown.ui-dropdown {
|
||||
@apply border-r-0 pr-4;
|
||||
@apply border-r-0 pr-4 pl-5 max-w-20 desktop:max-w-full;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
|
||||
@@ -15,7 +19,7 @@
|
||||
}
|
||||
|
||||
.product-dropdown.ui-dropdown {
|
||||
@apply border-l-0 pl-4;
|
||||
@apply border-l-0 max-w-[8.75rem] desktop:max-w-full pr-5 pl-4;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
|
||||
|
||||
@@ -1,50 +1,52 @@
|
||||
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
|
||||
import { MockDirective } from 'ng-mocks';
|
||||
import { createComponentFactory, Spectator } from "@ngneat/spectator/jest";
|
||||
import { MockDirective } from "ng-mocks";
|
||||
|
||||
import {
|
||||
ReceiptItem,
|
||||
ReturnDetailsService,
|
||||
ReturnDetailsStore,
|
||||
} from '@isa/oms/data-access';
|
||||
} from "@isa/oms/data-access";
|
||||
|
||||
import { ProductImageDirective } from '@isa/shared/product-image';
|
||||
import { ReturnDetailsOrderGroupItemControlsComponent } from '../return-details-order-group-item-controls/return-details-order-group-item-controls.component';
|
||||
import { CheckboxComponent } from '@isa/ui/input-controls';
|
||||
import { signal } from '@angular/core';
|
||||
import { ProductImageDirective } from "@isa/shared/product-image";
|
||||
import { ReturnDetailsOrderGroupItemControlsComponent } from "../return-details-order-group-item-controls/return-details-order-group-item-controls.component";
|
||||
import { CheckboxComponent } from "@isa/ui/input-controls";
|
||||
import { signal } from "@angular/core";
|
||||
|
||||
// Helper function to create mock ReceiptItem data
|
||||
const createMockItem = (
|
||||
ean: string,
|
||||
canReturn: boolean,
|
||||
name = 'Test Product',
|
||||
category = 'BOOK', // Add default category that's not 'unknown'
|
||||
name = "Test Product",
|
||||
category = "BOOK", // Add default category that's not 'unknown'
|
||||
availableQuantity = 2,
|
||||
selectedQuantity = 1,
|
||||
): ReceiptItem =>
|
||||
({
|
||||
id: 123,
|
||||
receiptNumber: 'R-123456', // Add the required receiptNumber property
|
||||
quantity: { quantity: 1 },
|
||||
receiptNumber: "R-123456",
|
||||
quantity: { quantity: availableQuantity },
|
||||
price: {
|
||||
value: { value: 19.99, currency: 'EUR' },
|
||||
value: { value: 19.99, currency: "EUR" },
|
||||
vat: { inPercent: 19 },
|
||||
},
|
||||
product: {
|
||||
ean: ean,
|
||||
name: name,
|
||||
contributors: 'Test Author',
|
||||
format: 'HC',
|
||||
formatDetail: 'Hardcover',
|
||||
manufacturer: 'Test Publisher',
|
||||
publicationDate: '2024-01-01T00:00:00Z',
|
||||
catalogProductNumber: '1234567890',
|
||||
volume: '1',
|
||||
contributors: "Test Author",
|
||||
format: "HC",
|
||||
formatDetail: "Hardcover",
|
||||
manufacturer: "Test Publisher",
|
||||
publicationDate: "2024-01-01T00:00:00Z",
|
||||
catalogProductNumber: "1234567890",
|
||||
volume: "1",
|
||||
},
|
||||
actions: [{ key: 'canReturn', value: String(canReturn) }],
|
||||
features: { category: category }, // Add the features property with category
|
||||
actions: [{ key: "canReturn", value: String(canReturn) }],
|
||||
features: { category: category },
|
||||
}) as ReceiptItem;
|
||||
|
||||
describe('ReturnDetailsOrderGroupItemControlsComponent', () => {
|
||||
describe("ReturnDetailsOrderGroupItemControlsComponent", () => {
|
||||
let spectator: Spectator<ReturnDetailsOrderGroupItemControlsComponent>;
|
||||
const mockItemSelectable = createMockItem('1234567890123', true);
|
||||
const mockItemSelectable = createMockItem("1234567890123", true);
|
||||
|
||||
const mockIsSelectable = signal<boolean>(true);
|
||||
const mockGetItemSelectted = signal<boolean>(false);
|
||||
@@ -52,6 +54,11 @@ describe('ReturnDetailsOrderGroupItemControlsComponent', () => {
|
||||
isLoading: signal<boolean>(true),
|
||||
};
|
||||
|
||||
// Mocks for availableQuantityMap and selectedQuantityMap
|
||||
const mockAvailableQuantityMap = { [mockItemSelectable.id]: 2 };
|
||||
const mockSelectedQuantityMap = { [mockItemSelectable.id]: 1 };
|
||||
const mockItemCategoryMap = { [mockItemSelectable.id]: "BOOK" };
|
||||
|
||||
function resetMocks() {
|
||||
mockIsSelectable.set(true);
|
||||
mockGetItemSelectted.set(false);
|
||||
@@ -68,12 +75,16 @@ describe('ReturnDetailsOrderGroupItemControlsComponent', () => {
|
||||
isSelectable: jest.fn(() => mockIsSelectable),
|
||||
getItemSelected: jest.fn(() => mockGetItemSelectted),
|
||||
canReturnResource: jest.fn(() => mockCanReturnResource),
|
||||
availableQuantityMap: jest.fn(() => mockAvailableQuantityMap),
|
||||
selectedQuantityMap: jest.fn(() => mockSelectedQuantityMap),
|
||||
itemCategoryMap: jest.fn(() => mockItemCategoryMap),
|
||||
setProductCategory: jest.fn(),
|
||||
setQuantity: jest.fn(),
|
||||
addSelectedItems: jest.fn(),
|
||||
removeSelectedItems: jest.fn(),
|
||||
},
|
||||
},
|
||||
],
|
||||
// Spectator automatically stubs standalone dependencies like ItemRowComponent, CheckboxComponent etc.
|
||||
// We don't need deep interaction, just verify the host component renders correctly.
|
||||
// If specific interactions were needed, we could provide mocks or use overrideComponents.
|
||||
overrideComponents: [
|
||||
[
|
||||
ReturnDetailsOrderGroupItemControlsComponent,
|
||||
@@ -85,50 +96,41 @@ describe('ReturnDetailsOrderGroupItemControlsComponent', () => {
|
||||
},
|
||||
],
|
||||
],
|
||||
detectChanges: false, // Control initial detection manually
|
||||
detectChanges: false,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// Default setup with a selectable item
|
||||
spectator = createComponent({
|
||||
props: {
|
||||
item: mockItemSelectable, // Use signal for input
|
||||
item: mockItemSelectable,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetMocks(); // Reset mocks after each test
|
||||
resetMocks();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
// Arrange
|
||||
spectator.detectChanges(); // Trigger initial render
|
||||
|
||||
// Assert
|
||||
it("should create", () => {
|
||||
spectator.detectChanges();
|
||||
expect(spectator.component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display the checkbox when item is selectable', () => {
|
||||
// Arrange
|
||||
mockCanReturnResource.isLoading.set(false); // Simulate the resource being ready
|
||||
mockIsSelectable.set(true); // Simulate the item being selectable
|
||||
it("should display the checkbox when item is selectable and not loading", () => {
|
||||
mockCanReturnResource.isLoading.set(false);
|
||||
mockIsSelectable.set(true);
|
||||
spectator.detectChanges();
|
||||
// Assert
|
||||
expect(spectator.component.selectable()).toBe(true);
|
||||
const checkbox = spectator.query(CheckboxComponent);
|
||||
expect(checkbox).toBeTruthy();
|
||||
expect(spectator.query(CheckboxComponent)).toBeTruthy();
|
||||
expect(
|
||||
spectator.query(`input[data-what="return-item-checkbox"]`),
|
||||
).toExist();
|
||||
});
|
||||
it('should NOT display the checkbox when item is not selectable', () => {
|
||||
// Arrange
|
||||
mockIsSelectable.set(false); // Simulate the item not being selectable
|
||||
spectator.detectChanges();
|
||||
spectator.detectComponentChanges();
|
||||
|
||||
// Assert
|
||||
it("should NOT display the checkbox when item is not selectable", () => {
|
||||
mockIsSelectable.set(false);
|
||||
mockCanReturnResource.isLoading.set(false);
|
||||
spectator.detectChanges();
|
||||
expect(spectator.component.selectable()).toBe(false);
|
||||
expect(
|
||||
spectator.query(`input[data-what="return-item-checkbox"]`),
|
||||
@@ -136,27 +138,73 @@ describe('ReturnDetailsOrderGroupItemControlsComponent', () => {
|
||||
expect(spectator.query(CheckboxComponent)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should be false when no canReturn action is present', () => {
|
||||
// Arrange
|
||||
const item = { ...createMockItem('0001', true), actions: [] };
|
||||
spectator.setInput('item', item as any);
|
||||
|
||||
// Act
|
||||
it("should show spinner when canReturnResource is loading", () => {
|
||||
mockCanReturnResource.isLoading.set(true);
|
||||
spectator.detectChanges();
|
||||
expect(
|
||||
spectator.query('ui-icon-button[data-what="load-spinner"]'),
|
||||
).toExist();
|
||||
});
|
||||
|
||||
// Assert
|
||||
it("should render correct quantity dropdown values", () => {
|
||||
spectator.detectChanges();
|
||||
expect(spectator.component.quantityDropdownValues()).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it("should call setQuantity when dropdown value changes", () => {
|
||||
const store = spectator.inject(ReturnDetailsStore);
|
||||
const spy = jest.spyOn(store, "setQuantity");
|
||||
spectator.detectChanges();
|
||||
// Simulate dropdown value change
|
||||
spectator.component.setQuantity(2);
|
||||
expect(spy).toHaveBeenCalledWith(mockItemSelectable.id, 2);
|
||||
});
|
||||
|
||||
it("should call setProductCategory when product category changes", () => {
|
||||
const store = spectator.inject(ReturnDetailsStore);
|
||||
const spy = jest.spyOn(store, "setProductCategory");
|
||||
spectator.detectChanges();
|
||||
spectator.component.setProductCategory("Buch/Kalender");
|
||||
expect(spy).toHaveBeenCalledWith(mockItemSelectable.id, "Buch/Kalender");
|
||||
});
|
||||
|
||||
it("should call addSelectedItems when setSelected(true) is called", () => {
|
||||
const store = spectator.inject(ReturnDetailsStore);
|
||||
const spy = jest.spyOn(store, "addSelectedItems");
|
||||
spectator.detectChanges();
|
||||
spectator.component.setSelected(true);
|
||||
expect(spy).toHaveBeenCalledWith([mockItemSelectable.id]);
|
||||
});
|
||||
|
||||
it("should call removeSelectedItems when setSelected(false) is called", () => {
|
||||
const store = spectator.inject(ReturnDetailsStore);
|
||||
const spy = jest.spyOn(store, "removeSelectedItems");
|
||||
spectator.detectChanges();
|
||||
spectator.component.setSelected(false);
|
||||
expect(spy).toHaveBeenCalledWith([mockItemSelectable.id]);
|
||||
});
|
||||
|
||||
it("should be false when no canReturn action is present", () => {
|
||||
const item = { ...createMockItem("0001", true), actions: [] };
|
||||
spectator.setInput("item", item as any);
|
||||
spectator.detectChanges();
|
||||
expect(spectator.component.canReturnReceiptItem()).toBe(false);
|
||||
});
|
||||
|
||||
it('should be false when canReturn action has falsy value', () => {
|
||||
// Arrange
|
||||
const item = createMockItem('0001', false);
|
||||
spectator.setInput('item', item);
|
||||
|
||||
// Act
|
||||
it("should be false when canReturn action has falsy value", () => {
|
||||
const item = createMockItem("0001", false);
|
||||
spectator.setInput("item", item);
|
||||
spectator.detectChanges();
|
||||
|
||||
// Assert
|
||||
expect(spectator.component.canReturnReceiptItem()).toBe(false);
|
||||
});
|
||||
|
||||
it("should display correct selected quantity", () => {
|
||||
spectator.detectChanges();
|
||||
expect(spectator.component.selectedQuantity()).toBe(1);
|
||||
});
|
||||
|
||||
it("should display correct product category", () => {
|
||||
spectator.detectChanges();
|
||||
expect(spectator.component.productCategory()).toBe("BOOK");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,30 +5,27 @@ import {
|
||||
inject,
|
||||
input,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { provideLoggerContext } from '@isa/core/logging';
|
||||
} from "@angular/core";
|
||||
import { provideLoggerContext } from "@isa/core/logging";
|
||||
import {
|
||||
canReturnReceiptItem,
|
||||
getReceiptItemReturnedQuantity,
|
||||
getReceiptItemProductCategory,
|
||||
getReceiptItemQuantity,
|
||||
ProductCategory,
|
||||
ReceiptItem,
|
||||
ReturnDetailsService,
|
||||
ReturnDetailsStore,
|
||||
} from '@isa/oms/data-access';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
} from "@isa/oms/data-access";
|
||||
import { IconButtonComponent } from "@isa/ui/buttons";
|
||||
import {
|
||||
CheckboxComponent,
|
||||
DropdownButtonComponent,
|
||||
DropdownOptionComponent,
|
||||
} from '@isa/ui/input-controls';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
} from "@isa/ui/input-controls";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
|
||||
@Component({
|
||||
selector: 'oms-feature-return-details-order-group-item-controls',
|
||||
templateUrl: './return-details-order-group-item-controls.component.html',
|
||||
styleUrls: ['./return-details-order-group-item-controls.component.scss'],
|
||||
selector: "oms-feature-return-details-order-group-item-controls",
|
||||
templateUrl: "./return-details-order-group-item-controls.component.html",
|
||||
styleUrls: ["./return-details-order-group-item-controls.component.scss"],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
@@ -40,7 +37,7 @@ import { FormsModule } from '@angular/forms';
|
||||
],
|
||||
providers: [
|
||||
provideLoggerContext({
|
||||
component: 'ReturnDetailsOrderGroupItemControlsComponent',
|
||||
component: "ReturnDetailsOrderGroupItemControlsComponent",
|
||||
}),
|
||||
],
|
||||
})
|
||||
@@ -66,38 +63,11 @@ export class ReturnDetailsOrderGroupItemControlsComponent {
|
||||
|
||||
availableCategories = this.#returnDetailsService.availableCategories();
|
||||
|
||||
/**
|
||||
* Computes the quantity of the current receipt item that has already been returned.
|
||||
*
|
||||
* This value is derived from the item's return history and is used to indicate
|
||||
* how many units have already been processed for return.
|
||||
*
|
||||
* @returns The number of units already returned for this receipt item.
|
||||
*/
|
||||
returnedQuantity = computed(() => {
|
||||
selectedQuantity = computed(() => {
|
||||
const item = this.item();
|
||||
return getReceiptItemReturnedQuantity(item);
|
||||
return this.#store.selectedQuantityMap()[item.id];
|
||||
});
|
||||
|
||||
/**
|
||||
* Computes the total quantity for the current receipt item.
|
||||
* Represents the original quantity as recorded in the receipt.
|
||||
*
|
||||
* @returns The total quantity for the item.
|
||||
*/
|
||||
quantity = computed(() => {
|
||||
const item = this.item();
|
||||
return getReceiptItemQuantity(item);
|
||||
});
|
||||
|
||||
/**
|
||||
* Computes the quantity of the item that is still available for return.
|
||||
* Calculated as the difference between the total quantity and the returned quantity.
|
||||
*
|
||||
* @returns The number of units available to be returned.
|
||||
*/
|
||||
availableQuantity = computed(() => this.quantity() - this.returnedQuantity());
|
||||
|
||||
/**
|
||||
* Generates the list of selectable quantities for the dropdown.
|
||||
* The values range from 1 up to the available quantity.
|
||||
@@ -105,13 +75,14 @@ export class ReturnDetailsOrderGroupItemControlsComponent {
|
||||
* @returns An array of selectable quantity values.
|
||||
*/
|
||||
quantityDropdownValues = computed(() => {
|
||||
const itemQuantity = this.availableQuantity();
|
||||
const item = this.item();
|
||||
const itemQuantity = this.#store.availableQuantityMap()[item.id];
|
||||
return Array.from({ length: itemQuantity }, (_, i) => i + 1);
|
||||
});
|
||||
|
||||
productCategory = computed(() => {
|
||||
const item = this.item();
|
||||
return getReceiptItemProductCategory(item);
|
||||
return this.#store.itemCategoryMap()[item.id];
|
||||
});
|
||||
|
||||
selectable = this.#store.isSelectable(this.item);
|
||||
@@ -127,8 +98,9 @@ export class ReturnDetailsOrderGroupItemControlsComponent {
|
||||
}
|
||||
|
||||
setQuantity(quantity: number | undefined) {
|
||||
const item = this.item();
|
||||
if (quantity === undefined) {
|
||||
quantity = this.item().quantity.quantity;
|
||||
quantity = this.#store.availableQuantityMap()[item.id];
|
||||
}
|
||||
this.#store.setQuantity(this.item().id, quantity);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
{{ i.product.manufacturer }} | {{ i.product.ean }}
|
||||
</div>
|
||||
<div class="text-isa-neutral-600 isa-text-body-2-regular">
|
||||
{{ i.product.publicationDate | date: 'dd. MMM yyyy' }}
|
||||
{{ i.product.publicationDate | date: "dd. MMM yyyy" }}
|
||||
</div>
|
||||
</div>
|
||||
<oms-feature-return-details-order-group-item-controls [item]="i">
|
||||
@@ -73,11 +73,11 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (returnedQuantity() > 0 && itemQuantity() !== returnedQuantity()) {
|
||||
@if (availableQuantity() !== quantity()) {
|
||||
<div
|
||||
class="flex items-center self-start text-isa-neutral-600 isa-text-body-2-bold pb-6"
|
||||
>
|
||||
Es wurden bereits {{ returnedQuantity() }} von {{ itemQuantity() }} Artikel
|
||||
zurückgegeben.
|
||||
Es wurden bereits {{ quantity() - availableQuantity() }} von
|
||||
{{ quantity() }} Artikel zurückgegeben.
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
import { CurrencyPipe, DatePipe, LowerCasePipe } from '@angular/common';
|
||||
import { CurrencyPipe, DatePipe, LowerCasePipe } from "@angular/common";
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
input,
|
||||
} from '@angular/core';
|
||||
import { isaActionClose, ProductFormatIconGroup } from '@isa/icons';
|
||||
} from "@angular/core";
|
||||
import { isaActionClose, ProductFormatIconGroup } from "@isa/icons";
|
||||
import {
|
||||
getReceiptItemAction,
|
||||
getReceiptItemReturnedQuantity,
|
||||
getReceiptItemQuantity,
|
||||
ReceiptItem,
|
||||
ReturnDetailsStore,
|
||||
} from '@isa/oms/data-access';
|
||||
import { ProductImageDirective } from '@isa/shared/product-image';
|
||||
import { ItemRowComponent } from '@isa/ui/item-rows';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
import { ReturnDetailsOrderGroupItemControlsComponent } from '../return-details-order-group-item-controls/return-details-order-group-item-controls.component';
|
||||
import { ProductRouterLinkDirective } from '@isa/shared/product-router-link';
|
||||
} from "@isa/oms/data-access";
|
||||
import { ProductImageDirective } from "@isa/shared/product-image";
|
||||
import { ItemRowComponent } from "@isa/ui/item-rows";
|
||||
import { NgIconComponent, provideIcons } from "@ng-icons/core";
|
||||
import { ReturnDetailsOrderGroupItemControlsComponent } from "../return-details-order-group-item-controls/return-details-order-group-item-controls.component";
|
||||
import { ProductRouterLinkDirective } from "@isa/shared/product-router-link";
|
||||
|
||||
@Component({
|
||||
selector: 'oms-feature-return-details-order-group-item',
|
||||
templateUrl: './return-details-order-group-item.component.html',
|
||||
styleUrls: ['./return-details-order-group-item.component.scss'],
|
||||
selector: "oms-feature-return-details-order-group-item",
|
||||
templateUrl: "./return-details-order-group-item.component.html",
|
||||
styleUrls: ["./return-details-order-group-item.component.scss"],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
@@ -82,7 +81,7 @@ export class ReturnDetailsOrderGroupItemComponent {
|
||||
*/
|
||||
canReturnMessage = computed(() => {
|
||||
const item = this.item();
|
||||
const canReturnAction = getReceiptItemAction(item, 'canReturn');
|
||||
const canReturnAction = getReceiptItemAction(item, "canReturn");
|
||||
|
||||
if (canReturnAction?.description) {
|
||||
return canReturnAction.description;
|
||||
@@ -90,30 +89,32 @@ export class ReturnDetailsOrderGroupItemComponent {
|
||||
|
||||
const canReturnMessage = this.canReturn()?.message;
|
||||
|
||||
return canReturnMessage ?? '';
|
||||
return canReturnMessage ?? "";
|
||||
});
|
||||
|
||||
/**
|
||||
* Computes the quantity of the current receipt item that has already been returned.
|
||||
* The original quantity of the item as recorded in the order.
|
||||
* This value is retrieved from the store and represents the total number of units
|
||||
* initially purchased for this receipt item.
|
||||
*
|
||||
* This value is derived using the item's return history and is used to display
|
||||
* how many units of this item have been processed for return so far.
|
||||
*
|
||||
* @returns The number of units already returned for this receipt item.
|
||||
* @readonly
|
||||
* @returns {number} The original quantity of the item in the order.
|
||||
*/
|
||||
returnedQuantity = computed(() => {
|
||||
const item = this.item();
|
||||
return getReceiptItemReturnedQuantity(item);
|
||||
});
|
||||
|
||||
/**
|
||||
* Computes the total quantity for the current receipt item.
|
||||
* Represents the original quantity of the item as recorded in the receipt.
|
||||
*
|
||||
* @returns The total quantity for the item.
|
||||
*/
|
||||
itemQuantity = computed(() => {
|
||||
quantity = computed(() => {
|
||||
const item = this.item();
|
||||
return getReceiptItemQuantity(item);
|
||||
});
|
||||
|
||||
/**
|
||||
* The currently available quantity of the item for return.
|
||||
* This value is computed based on the item's current state and may be less than
|
||||
* the original quantity if some units have already been returned or are otherwise unavailable.
|
||||
*
|
||||
* @readonly
|
||||
* @returns {number} The number of units available for return.
|
||||
*/
|
||||
availableQuantity = computed(() => {
|
||||
const item = this.item();
|
||||
return this.#store.availableQuantityMap()[item.id];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
resource,
|
||||
} from '@angular/core';
|
||||
@@ -40,11 +39,11 @@ import { groupBy } from 'lodash';
|
||||
ExpandableDirectives,
|
||||
ProgressBarComponent,
|
||||
],
|
||||
providers: [provideIcons({ isaActionChevronLeft })],
|
||||
providers: [provideIcons({ isaActionChevronLeft }), ReturnDetailsStore],
|
||||
})
|
||||
export class ReturnDetailsComponent {
|
||||
#logger = logger(() => ({
|
||||
component: ReturnDetailsComponent.name,
|
||||
component: 'ReturnDetailsComponent',
|
||||
itemId: this.receiptId(),
|
||||
processId: this.processId(),
|
||||
params: this.params(),
|
||||
@@ -71,10 +70,6 @@ export class ReturnDetailsComponent {
|
||||
throw new Error('No receiptId found in route params');
|
||||
});
|
||||
|
||||
// Effect resets the Store's state when the receiptId changes
|
||||
// This ensures that the store is always in sync with the current receiptId
|
||||
receiptIdEffect = effect(() => this.#store.selectStorage(this.receiptId()));
|
||||
|
||||
receiptResource = this.#store.receiptResource(this.receiptId);
|
||||
|
||||
customerReceiptsResource = resource({
|
||||
@@ -107,6 +102,8 @@ export class ReturnDetailsComponent {
|
||||
|
||||
const processId = this.processId();
|
||||
const selectedItems = this.#store.selectedItems();
|
||||
const selectedQuantites = this.#store.selectedQuantityMap();
|
||||
const selectedProductCategories = this.#store.itemCategoryMap();
|
||||
|
||||
this.#logger.info('Starting return process', () => ({
|
||||
processId: processId,
|
||||
@@ -126,7 +123,14 @@ export class ReturnDetailsComponent {
|
||||
const returns = Object.entries(itemsGrouptByReceiptId).map(
|
||||
([receiptId, items]) => ({
|
||||
receipt: receipts[Number(receiptId)],
|
||||
items,
|
||||
items: items.map((item) => {
|
||||
const receiptItem = item;
|
||||
return {
|
||||
receiptItem,
|
||||
quantity: selectedQuantites[receiptItem.id],
|
||||
category: selectedProductCategories[receiptItem.id],
|
||||
};
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -77,10 +77,10 @@ export class ReturnSearchMainComponent {
|
||||
}: CallbackResult<ListResponseArgs<ReceiptListItem>>) => {
|
||||
if (data) {
|
||||
if (data.result.length === 1) {
|
||||
this.navigate(['receipt', data.result[0].id]);
|
||||
} else if (data.result.length > 1) {
|
||||
this.navigate(['receipts']);
|
||||
return this.navigate(['receipt', data.result[0].id]);
|
||||
}
|
||||
|
||||
return this.navigate(['receipts']);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<ui-client-row data-what="search-result-item" [attr.data-which]="receiptNumber()">
|
||||
<ui-client-row
|
||||
data-what="search-result-item"
|
||||
[attr.data-which]="receiptNumber()"
|
||||
>
|
||||
<ui-client-row-content>
|
||||
<h3 class="isa-text-subtitle-1-regular">{{ name() }}</h3>
|
||||
</ui-client-row-content>
|
||||
@@ -7,12 +10,12 @@
|
||||
<ui-item-row-data-label>Belegdatum</ui-item-row-data-label>
|
||||
<ui-item-row-data-value>
|
||||
<span class="isa-text-body-2-bold">
|
||||
{{ receiptDate() | date: 'dd.MM.yy' }}
|
||||
{{ receiptDate() | date: "dd.MM.yy" }}
|
||||
</span>
|
||||
</ui-item-row-data-value>
|
||||
</ui-item-row-data-row>
|
||||
<ui-item-row-data-row>
|
||||
<ui-item-row-data-label>Rechnugsnr.</ui-item-row-data-label>
|
||||
<ui-item-row-data-label>Beleg-Nr.</ui-item-row-data-label>
|
||||
<ui-item-row-data-value>
|
||||
<span class="isa-text-body-2-bold"> {{ receiptNumber() }} </span>
|
||||
</ui-item-row-data-value>
|
||||
|
||||
Reference in New Issue
Block a user