feature(libs-remission): Improvements and Refactoring of Remission List Component

Ref: #5340
This commit is contained in:
Nino
2025-12-16 17:26:28 +01:00
parent 3d82e7f0af
commit a9a80a192e
8 changed files with 595 additions and 235 deletions

View File

@@ -0,0 +1,185 @@
import {
effect,
Signal,
untracked,
ResourceStatus,
inject,
} from '@angular/core';
import { injectDialog } from '@isa/ui/dialog';
import { SearchItemToRemitDialogComponent } from '@isa/remission/shared/search-item-to-remit-dialog';
import { RemissionStore, RemissionItem } from '@isa/remission/data-access';
/**
* Configuration for the empty search result handler.
* Provides all necessary signals and callbacks for handling empty search scenarios.
*/
export interface EmptySearchResultHandlerConfig {
/**
* Signal returning the status of the remission resource.
*/
remissionResourceStatus: Signal<ResourceStatus>;
/**
* Signal returning the status of the stock resource.
*/
stockResourceStatus: Signal<ResourceStatus>;
/**
* Signal returning the current search term.
*/
searchTerm: Signal<string | undefined>;
/**
* Signal returning the number of search hits.
*/
hits: Signal<number>;
/**
* Signal indicating whether there is a valid search term.
*/
hasValidSearchTerm: Signal<boolean>;
/**
* Signal indicating whether the search was triggered by user interaction.
*/
searchTriggeredByUser: Signal<boolean>;
/**
* Signal indicating whether a remission has been started.
*/
remissionStarted: Signal<boolean>;
/**
* Signal indicating whether the current list type is "Abteilung".
*/
isDepartment: Signal<boolean>;
/**
* Signal returning the first item in the list (for auto-preselection).
*/
firstItem: Signal<RemissionItem | undefined>;
/**
* Callback to preselect a remission item.
*/
preselectItem: (item: RemissionItem) => void;
/**
* Callback to remit items after dialog selection.
* @param options - Options for the remit operation
*/
remitItems: (options: { addItemFlow: boolean }) => Promise<void>;
/**
* Callback to navigate to the default remission list.
*/
navigateToDefaultList: () => Promise<void>;
/**
* Callback to reload the list and return data.
*/
reloadData: () => void;
}
/**
* Creates an effect that handles scenarios where a search yields no or few results.
*
* This handler implements two behaviors:
* 1. **Auto-Preselection**: When exactly one item is found and remission is started,
* automatically preselects that item for convenience.
* 2. **Empty Search Dialog**: When no items are found after a user-initiated search,
* opens a dialog allowing the user to add items to remit.
*
* @param config - Configuration object containing all required signals and callbacks
* @returns The created effect (for potential cleanup if needed)
*
* @example
* ```typescript
* // In a component
* emptySearchEffect = injectEmptySearchResultHandler({
* remissionResourceStatus: () => this.remissionResource.status(),
* stockResourceStatus: () => this.inStockResource.status(),
* searchTerm: this.searchTerm,
* hits: this.hits,
* // ... other config
* });
* ```
*
* @remarks
* - The effect tracks `remissionResourceStatus`, `stockResourceStatus`, and `searchTerm`
* - Other signals are accessed via `untracked()` to avoid unnecessary re-evaluations
* - The dialog subscription handles async flows for adding items to remission
*/
export const injectEmptySearchResultHandler = (
config: EmptySearchResultHandlerConfig,
) => {
const store = inject(RemissionStore);
const searchItemToRemitDialog = injectDialog(
SearchItemToRemitDialogComponent,
);
return effect(() => {
const status = config.remissionResourceStatus();
const stockStatus = config.stockResourceStatus();
const searchTerm = config.searchTerm();
// Wait until both resources are resolved
if (status !== 'resolved' || stockStatus !== 'resolved') {
return;
}
untracked(() => {
const hits = config.hits();
// Early return conditions - only proceed if:
// - No hits (hits === 0, so !!hits is false)
// - Valid search term exists
// - Search was triggered by user
if (
!!hits ||
!searchTerm ||
!config.hasValidSearchTerm() ||
!config.searchTriggeredByUser()
) {
// #5338 - Auto-select item if exactly one hit after search
if (hits === 1 && config.remissionStarted()) {
store.clearSelectedItems();
const firstItem = config.firstItem();
if (firstItem) {
config.preselectItem(firstItem);
}
}
return;
}
// Open dialog to allow user to add items when search returns no results
searchItemToRemitDialog({
data: {
searchTerm,
},
}).closed.subscribe(async (result) => {
store.clearSelectedItems();
if (result) {
if (config.remissionStarted()) {
// Select all items from dialog result
for (const item of result) {
if (item?.id) {
store.selectRemissionItem(item.id, item);
}
}
// Remit the selected items
await config.remitItems({ addItemFlow: true });
} else if (config.isDepartment()) {
// Navigate to default list if in department mode without active remission
await config.navigateToDefaultList();
return;
}
}
// Always reload data after dialog closes
config.reloadData();
});
});
});
};

View File

@@ -0,0 +1,6 @@
export { RemissionActionComponent } from './remission-action.component';
export {
RemissionActionService,
RemitItemsContext,
RemitItemsOptions,
} from './remission-action.service';

View File

@@ -0,0 +1,21 @@
@if (remissionStarted()) {
<ui-stateful-button
(clicked)="remitItems()"
(action)="remitItems()"
[(state)]="actionService.state"
defaultContent="Remittieren"
defaultWidth="13rem"
[errorContent]="actionService.error()"
errorWidth="32rem"
errorAction="Erneut versuchen"
successContent="Hinzugefügt"
successWidth="20rem"
size="large"
color="brand"
[pending]="actionService.inProgress()"
[disabled]="isDisabled()"
data-what="button"
data-which="remit-items"
>
</ui-stateful-button>
}

View File

@@ -0,0 +1,124 @@
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
input,
output,
} from '@angular/core';
import { StatefulButtonComponent } from '@isa/ui/buttons';
import {
RemissionStore,
RemissionItem,
RemissionListType,
} from '@isa/remission/data-access';
import {
RemissionActionService,
RemitItemsContext,
RemitItemsOptions,
} from './remission-action.service';
/**
* RemissionActionComponent
*
* Standalone component that encapsulates the "Remittieren" (remit) button
* and its associated logic. Manages the remit workflow including:
* - Displaying the stateful button with appropriate states
* - Triggering the remit action
* - Handling loading, success, and error states
*
* @remarks
* This component requires the RemissionActionService to be provided,
* either by itself or by a parent component.
*
* @example
* ```html
* <remi-feature-remission-action
* [getAvailableStockForItem]="getAvailableStockForItem"
* [selectedRemissionListType]="selectedRemissionListType()"
* [disabled]="removeItemInProgress()"
* (actionCompleted)="onActionCompleted()"
* />
* ```
*/
@Component({
selector: 'remi-feature-remission-action',
templateUrl: './remission-action.component.html',
styleUrl: './remission-action.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [StatefulButtonComponent],
providers: [RemissionActionService],
})
export class RemissionActionComponent {
readonly #store = inject(RemissionStore);
readonly actionService = inject(RemissionActionService);
/**
* Function to get available stock for a remission item.
* Required for calculating quantities during remit.
*/
getAvailableStockForItem = input.required<(item: RemissionItem) => number>();
/**
* The currently selected remission list type.
* Required for determining item types during remit.
*/
selectedRemissionListType = input.required<RemissionListType>();
/**
* Additional disabled state from parent component.
* Combined with internal disabled logic.
*/
disabled = input<boolean>(false);
/**
* Emitted when the remit action is completed (success or error).
* Parent component should use this to reload list data.
*/
actionCompleted = output<void>();
/**
* Computed signal indicating whether a remission has been started.
* The button is only visible when remission is started.
*/
remissionStarted = computed(() => this.#store.remissionStarted());
/**
* Computed signal indicating whether there are selected items.
*/
hasSelectedItems = computed(
() => Object.keys(this.#store.selectedItems()).length > 0,
);
/**
* Computed signal for the combined disabled state.
* Button is disabled when:
* - No items are selected
* - An external disabled condition is true
* - A remit operation is in progress
*/
isDisabled = computed(
() =>
!this.hasSelectedItems() ||
this.disabled() ||
this.actionService.inProgress(),
);
/**
* Handles the remit button click.
* Delegates to the action service and emits completion event.
*
* @param options - Options for the remit operation
*/
async remitItems(
options: RemitItemsOptions = { addItemFlow: false },
): Promise<void> {
const context: RemitItemsContext = {
getAvailableStockForItem: this.getAvailableStockForItem(),
selectedRemissionListType: this.selectedRemissionListType(),
};
await this.actionService.remitItems(context, options);
this.actionCompleted.emit();
}
}

View File

@@ -0,0 +1,217 @@
import { inject, Injectable, signal } from '@angular/core';
import { logger } from '@isa/core/logging';
import {
RemissionStore,
RemissionReturnReceiptService,
RemissionListType,
RemissionResponseArgsErrorMessage,
RemissionItem,
getStockToRemit,
getItemType,
} from '@isa/remission/data-access';
import { StatefulButtonState } from '@isa/ui/buttons';
import { injectFeedbackErrorDialog } from '@isa/ui/dialog';
import { firstValueFrom } from 'rxjs';
/**
* Configuration options for the remit items operation.
*/
export interface RemitItemsOptions {
/** Whether this operation is part of an add-item flow (e.g., from search dialog) */
addItemFlow: boolean;
}
/**
* Context required for remitting items.
* Provides stock information lookup for calculating quantities.
*/
export interface RemitItemsContext {
/** Function to get available stock for a remission item */
getAvailableStockForItem: (item: RemissionItem) => number;
/** The currently selected remission list type */
selectedRemissionListType: RemissionListType;
}
/**
* Service responsible for handling the remission action workflow.
* Manages the state and logic for remitting selected items.
*
* This service encapsulates:
* - State management for the remit button (progress, error, success states)
* - The remitItems business logic
* - Error handling and user feedback
* - Navigation after successful remission
*
* @remarks
* This service should be provided at the component level, not root level,
* as it maintains UI state specific to a single remission action context.
*
* @example
* ```typescript
* // In component providers
* providers: [RemissionActionService]
*
* // Usage
* readonly actionService = inject(RemissionActionService);
* await this.actionService.remitItems(context);
* ```
*/
@Injectable()
export class RemissionActionService {
readonly #store = inject(RemissionStore);
readonly #remissionReturnReceiptService = inject(
RemissionReturnReceiptService,
);
readonly #errorDialog = injectFeedbackErrorDialog();
readonly #logger = logger(() => ({
service: 'RemissionActionService',
}));
/**
* Signal representing the current state of the remit button.
*/
readonly state = signal<StatefulButtonState>('default');
/**
* Signal containing the current error message, if any.
*/
readonly error = signal<string | null>(null);
/**
* Signal indicating whether a remit operation is currently in progress.
*/
readonly inProgress = signal(false);
/**
* Computed signal indicating whether there are selected items in the store.
*/
get hasSelectedItems(): boolean {
return Object.keys(this.#store.selectedItems()).length > 0;
}
/**
* Computed signal indicating whether a remission has been started.
*/
get remissionStarted(): boolean {
return this.#store.remissionStarted();
}
/**
* Initiates the process to remit selected items.
*
* If remission is already started, items are added directly to the remission.
* Handles the full workflow including:
* - Preventing duplicate operations
* - Processing each selected item
* - Error handling with user feedback
* - State management for UI feedback
*
* @param context - Context providing stock information and list type
* @param options - Options for the remit operation
* @returns A promise that resolves when the operation is complete
*/
async remitItems(
context: RemitItemsContext,
options: RemitItemsOptions = { addItemFlow: false },
): Promise<void> {
if (this.inProgress()) {
return;
}
this.inProgress.set(true);
try {
await this.#processSelectedItems(context, options);
this.state.set('success');
} catch (error) {
await this.#handleRemitItemsError(error);
}
this.#store.clearSelectedItems();
this.inProgress.set(false);
}
/**
* Processes all selected items for remission.
* @param context - Context providing stock information and list type
* @param options - Options for the remit operation
*/
async #processSelectedItems(
context: RemitItemsContext,
options: RemitItemsOptions,
): Promise<void> {
// #5273, #5280 Fix - Bei gestarteter Remission dürfen Items die über den AddItemDialog
// hinzugefügt und direkt remittiert werden, nur als ReturnItem (statt ReturnSuggestion)
// zum WBS hinzugefügt werden
const remissionListType = options.addItemFlow
? RemissionListType.Pflicht
: context.selectedRemissionListType;
const selected = this.#store.selectedItems();
const quantities = this.#store.selectedQuantity();
for (const [remissionItemId, item] of Object.entries(selected)) {
const returnId = this.#store.returnId();
const receiptId = this.#store.receiptId();
const remissionItemIdNumber = Number(remissionItemId);
const quantity = quantities[remissionItemIdNumber];
const inStock = context.getAvailableStockForItem(item);
const stockToRemit = getStockToRemit({
remissionItem: item,
remissionListType,
availableStock: inStock,
});
const quantityToRemit = quantity ?? stockToRemit;
if (returnId && receiptId) {
await this.#remissionReturnReceiptService.remitItem({
itemId: remissionItemIdNumber,
addItem: {
returnId,
receiptId,
quantity: quantityToRemit,
inStock,
impedimentComment: stockToRemit > quantity ? 'Restmenge' : '',
remainingQuantity:
isNaN(quantity) || inStock - quantity <= 0
? undefined
: inStock - quantity,
},
type: getItemType(item, remissionListType),
});
}
}
}
/**
* Handles errors that occur during the remission of items.
* Logs the error, displays an error dialog, and updates state.
*
* @param error - The error object caught during the remission process
*/
async #handleRemitItemsError(error: unknown): Promise<void> {
this.#logger.error('Failed to remit items', error as Error);
const errorMessage =
(error as { error?: { message?: string }; message?: string })?.error
?.message ??
(error as { message?: string })?.message ??
'Artikel konnten nicht remittiert werden';
this.error.set(errorMessage);
await firstValueFrom(
this.#errorDialog({
data: {
errorMessage,
},
}).closed,
);
if (errorMessage === RemissionResponseArgsErrorMessage.AlreadyCompleted) {
this.#store.clearState();
}
this.state.set('error');
}
}

View File

@@ -59,23 +59,10 @@
[class.scroll-top-button-spacing-bottom]="remissionStarted()"
></utils-scroll-top-button>
@if (remissionStarted()) {
<ui-stateful-button
class="flex flex-col self-end fixed bottom-6 mr-6"
(clicked)="remitItems()"
(action)="remitItems()"
[(state)]="remitItemsState"
defaultContent="Remittieren"
defaultWidth="13rem"
[errorContent]="remitItemsError()"
errorWidth="32rem"
errorAction="Erneut versuchen"
successContent="Hinzugefügt"
successWidth="20rem"
size="large"
color="brand"
[pending]="remitItemsInProgress()"
[disabled]="!hasSelectedItems() || removeItemInProgress()"
>
</ui-stateful-button>
}
<remi-feature-remission-action
class="flex flex-col self-end fixed bottom-6 mr-6"
[getAvailableStockForItem]="getAvailableStockForItem.bind(this)"
[selectedRemissionListType]="selectedRemissionListType()"
[disabled]="removeItemInProgress()"
(actionCompleted)="reloadListAndReturnData()"
></remi-feature-remission-action>

View File

@@ -3,10 +3,9 @@ import {
Component,
inject,
computed,
effect,
untracked,
signal,
linkedSignal,
viewChild,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import {
@@ -29,12 +28,9 @@ import {
createRemissionProductGroupResource,
} from './resources';
import { injectRemissionListType } from './injects/inject-remission-list-type';
import { injectEmptySearchResultHandler } from './injects/inject-empty-search-result-handler';
import { RemissionListItemComponent } from './remission-list-item/remission-list-item.component';
import {
IconButtonComponent,
StatefulButtonComponent,
StatefulButtonState,
} from '@isa/ui/buttons';
import { IconButtonComponent } from '@isa/ui/buttons';
import {
ReturnItem,
StockInfo,
@@ -42,22 +38,16 @@ import {
RemissionStore,
RemissionItem,
calculateAvailableStock,
RemissionReturnReceiptService,
getStockToRemit,
RemissionListType,
RemissionResponseArgsErrorMessage,
UpdateItem,
orderByListItems,
getItemType,
getStockToRemit,
} from '@isa/remission/data-access';
import { injectDialog, injectFeedbackErrorDialog } from '@isa/ui/dialog';
import { SearchItemToRemitDialogComponent } from '@isa/remission/shared/search-item-to-remit-dialog';
import { RemissionReturnCardComponent } from './remission-return-card/remission-return-card.component';
import { logger } from '@isa/core/logging';
import { RemissionListDepartmentElementsComponent } from './remission-list-department-elements/remission-list-department-elements.component';
import { injectTabId } from '@isa/core/tabs';
import { RemissionListEmptyStateComponent } from './remission-list-empty-state/remission-list-empty-state.component';
import { firstValueFrom } from 'rxjs';
import { RemissionActionComponent } from './remission-action';
function querySettingsFactory() {
return inject(ActivatedRoute).snapshot.data['querySettings'];
@@ -96,7 +86,7 @@ function querySettingsFactory() {
RemissionListSelectComponent,
RemissionListItemComponent,
IconButtonComponent,
StatefulButtonComponent,
RemissionActionComponent,
RemissionListDepartmentElementsComponent,
RemissionListEmptyStateComponent,
ScrollTopButtonComponent,
@@ -125,8 +115,10 @@ export class RemissionListComponent {
*/
activatedTabId = injectTabId();
searchItemToRemitDialog = injectDialog(SearchItemToRemitDialogComponent);
errorDialog = injectFeedbackErrorDialog();
/**
* Reference to the RemissionActionComponent for triggering remit actions.
*/
remissionAction = viewChild(RemissionActionComponent);
/**
* FilterService instance for managing filter state and queries.
@@ -140,20 +132,6 @@ export class RemissionListComponent {
*/
#store = inject(RemissionStore);
/**
* RemissionReturnReceiptService instance for handling return receipt operations.
* @private
*/
#remissionReturnReceiptService = inject(RemissionReturnReceiptService);
/**
* Logger instance for logging component events and errors.
* @private
*/
#logger = logger(() => ({
component: 'RemissionListComponent',
}));
/**
* Restores scroll position when navigating back to this component.
*/
@@ -294,32 +272,6 @@ export class RemissionListComponent {
*/
remissionStarted = computed(() => this.#store.remissionStarted());
/**
* Computed signal indicating whether there are selected items in the remission store.
* @returns True if there are selected items, false otherwise.
*/
hasSelectedItems = computed(() => {
return Object.keys(this.#store.selectedItems()).length > 0;
});
/**
* Signal for the current remission list type.
* @returns The current RemissionListType.
*/
remitItemsState = signal<StatefulButtonState>('default');
/**
* Signal for any error messages related to remission items.
* @returns Error message string or null if no error.
*/
remitItemsError = signal<string | null>(null);
/**
* Signal indicating whether remission items are currently being processed.
* @returns True if in progress, false otherwise.
*/
remitItemsInProgress = signal(false);
/**
* Commits the current filter state and triggers a new search.
*
@@ -414,132 +366,35 @@ export class RemissionListComponent {
});
/**
* Effect that handles scenarios where a search yields no results.
* If the search was user-initiated and returned no hits, it opens a dialog
* to allow the user to add a new item to remit.
* If only one hit is found and a remission is started, it selects that item automatically.
* This effect runs whenever the remission or stock resource status changes,
* or when the search term changes.
* It ensures that the user is prompted appropriately based on their actions and the current state of the remission process.
* It also checks if the remission is started or if the list type is 'Abteilung' to determine navigation behavior.
* @see {@link
* https://angular.dev/guide/effects} for more information on Angular effects.
* @remarks This effect uses `untracked` to avoid unnecessary re-evaluations
* when accessing certain signals.
* Computed signal returning the first item in the list.
* Used for auto-preselection when exactly one item is found.
*/
emptySearchResultEffect = effect(() => {
const status = this.remissionResource.status();
const stockStatus = this.inStockResource.status();
const searchTerm: string | undefined = this.searchTerm();
#firstItem = computed(() => this.items()[0]);
if (status !== 'resolved' || stockStatus !== 'resolved') {
return;
}
untracked(() => {
const hits = this.hits();
// #5338 - Select item automatically if only one hit after search
if (
!!hits ||
!searchTerm ||
!this.hasValidSearchTerm() ||
!this.searchTriggeredByUser()
) {
if (hits === 1 && this.remissionStarted()) {
this.#store.clearSelectedItems();
this.preselectRemissionItem(this.items()[0]);
}
return;
}
this.searchItemToRemitDialog({
data: {
searchTerm,
},
}).closed.subscribe(async (result) => {
this.#store.clearSelectedItems();
if (result) {
if (this.remissionStarted()) {
for (const item of result) {
if (item?.id) {
this.#store.selectRemissionItem(item.id, item);
}
}
await this.remitItems({ addItemFlow: true });
} else if (this.isDepartment()) {
return await this.navigateToDefaultRemissionList();
}
}
this.reloadListAndReturnData();
});
});
});
// TODO: Improvement - In Separate Komponente zusammen mit Remi-Button Auslagern
/**
* Initiates the process to remit selected items.
* If remission is already started, items are added directly to the remission.
* If not, navigates to the default remission list.
* @param options - Options for remitting items, including whether it's part of an add-item flow.
* @returns A promise that resolves when the operation is complete.
* Effect that handles scenarios where a search yields no or few results.
* - Auto-preselects item when exactly one hit is found
* - Opens dialog to add items when no results are found
*
* @see injectEmptySearchResultHandler for implementation details
*/
async remitItems(options: { addItemFlow: boolean } = { addItemFlow: false }) {
if (this.remitItemsInProgress()) {
return;
}
this.remitItemsInProgress.set(true);
try {
// #5273, #5280 Fix - Bei gestarteter Remission dürfen Items die über den AddItemDialog hinzugefügt und direkt remittiert werden, nur als ReturnItem (statt ReturnSuggestion) zum WBS hinzugefügt werden
const remissionListType = options.addItemFlow
? RemissionListType.Pflicht
: this.selectedRemissionListType();
const selected = this.#store.selectedItems();
const quantities = this.#store.selectedQuantity();
for (const [remissionItemId, item] of Object.entries(selected)) {
const returnId = this.#store.returnId();
const receiptId = this.#store.receiptId();
const remissionItemIdNumber = Number(remissionItemId);
const quantity = quantities[remissionItemIdNumber];
const inStock = this.getAvailableStockForItem(item);
const stockToRemit = getStockToRemit({
remissionItem: item,
remissionListType,
availableStock: inStock,
});
const quantityToRemit = quantity ?? stockToRemit;
if (returnId && receiptId) {
await this.#remissionReturnReceiptService.remitItem({
itemId: remissionItemIdNumber,
addItem: {
returnId,
receiptId,
quantity: quantityToRemit,
inStock,
impedimentComment: stockToRemit > quantity ? 'Restmenge' : '',
remainingQuantity:
isNaN(quantity) || inStock - quantity <= 0
? undefined
: inStock - quantity,
},
type: getItemType(item, remissionListType),
});
}
}
this.remitItemsState.set('success');
this.reloadListAndReturnData();
} catch (error) {
await this.handleRemitItemsError(error);
}
this.#store.clearSelectedItems();
this.remitItemsInProgress.set(false);
}
emptySearchResultEffect = injectEmptySearchResultHandler({
remissionResourceStatus: computed(() => this.remissionResource.status()),
stockResourceStatus: computed(() => this.inStockResource.status()),
searchTerm: this.searchTerm,
hits: this.hits,
hasValidSearchTerm: this.hasValidSearchTerm,
searchTriggeredByUser: this.searchTriggeredByUser,
remissionStarted: this.remissionStarted,
isDepartment: this.isDepartment,
firstItem: this.#firstItem,
preselectItem: (item) => this.preselectRemissionItem(item),
remitItems: async (options) => {
await this.remissionAction()?.remitItems(options);
},
navigateToDefaultList: () => this.navigateToDefaultRemissionList(),
reloadData: () => this.reloadListAndReturnData(),
});
/**
* Reloads the remission list and return data.
@@ -572,41 +427,6 @@ export class RemissionListComponent {
}
}
/**
* Handles errors that occur during the remission of items.
* Logs the error, displays an error dialog, and reloads the list and return data.
* If the error indicates that the remission is already completed, it clears the remission state.
* Sets the stateful button to 'error' to indicate the failure.
* @param error - The error object caught during the remission process.
* @returns A promise that resolves when the error handling is complete.
*/
async handleRemitItemsError(error: any) {
this.#logger.error('Failed to remit items', error);
const errorMessage =
error?.error?.message ??
error?.message ??
'Artikel konnten nicht remittiert werden';
this.remitItemsError.set(errorMessage);
await firstValueFrom(
this.errorDialog({
data: {
errorMessage,
},
}).closed,
);
if (errorMessage === RemissionResponseArgsErrorMessage.AlreadyCompleted) {
this.#store.clearState();
}
this.reloadListAndReturnData();
this.remitItemsState.set('error'); // Stateful-Button auf Error setzen
}
/**
* Navigates to the default remission list based on the current activated tab ID.
* This method is used to redirect the user to the remission list after completing or starting a remission.