Merged PR 1936: fix(remission): filter search results by stock availability and display stock...

fix(remission): filter search results by stock availability and display stock info

- Add stock resource integration to search item component
- Filter search results to only show items with available stock (> 0)
- Display current stock information in search result items
- Implement calculateAvailableStock utility for accurate stock calculation
- Add inStock input parameter to SearchItemToRemitComponent
- Create reusable instock.resource for stock data fetching

The search now only displays items that are actually available for remission,
improving user experience by preventing selection of out-of-stock items.

Ref: #5318
This commit is contained in:
Nino Righi
2025-09-03 13:18:47 +00:00
committed by Andreas Schickinger
parent e58ec93087
commit c52f18e979
5 changed files with 84 additions and 7 deletions

View File

@@ -0,0 +1,39 @@
import { inject, resource } from '@angular/core';
import { RemissionStockService } from '@isa/remission/data-access';
export const createInStockResource = (
params: () => {
itemIds: number[];
},
) => {
const remissionStockService = inject(RemissionStockService);
return resource({
params,
loader: async ({ abortSignal, params }) => {
if (!params?.itemIds || params.itemIds.length === 0) {
return;
}
const assignedStock =
await remissionStockService.fetchAssignedStock(abortSignal);
if (!assignedStock || !assignedStock.id) {
throw new Error('No current stock available');
}
const itemIds = params.itemIds;
if (itemIds.some((id) => isNaN(id))) {
throw new Error('Invalid Catalog Product Number provided');
}
return await remissionStockService.fetchStock(
{
itemIds,
assignedStockId: assignedStock.id,
},
abortSignal,
);
},
});
};

View File

@@ -38,12 +38,16 @@
@if (searchResource.value()?.result; as items) {
@for (item of items; track item.id) {
@defer {
<remi-search-item-to-remit
[item]="item"
data-what="list-item"
data-which="search-result"
[attr.data-item-id]="item.id"
></remi-search-item-to-remit>
@let inStock = getAvailableStockForItem(item);
@if (inStock > 0) {
<remi-search-item-to-remit
[item]="item"
[inStock]="inStock"
data-what="list-item"
data-which="search-result"
[attr.data-item-id]="item.id"
></remi-search-item-to-remit>
}
}
}
}

View File

@@ -1,6 +1,7 @@
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
OnInit,
resource,
@@ -28,6 +29,8 @@ import {
import { SearchItemToRemitDialogComponent } from './search-item-to-remit-dialog.component';
import { CdkTrapFocus } from '@angular/cdk/a11y';
import { TooltipDirective } from '@isa/ui/tooltip';
import { createInStockResource } from './instock.resource';
import { calculateAvailableStock } from '@isa/remission/data-access';
@Component({
selector: 'remi-search-item-to-remit-list',
templateUrl: './search-item-to-remit-list.component.html',
@@ -52,6 +55,30 @@ export class SearchItemToRemitListComponent implements OnInit {
searchParams = signal<SearchByTermInput | undefined>(undefined);
inStockResource = createInStockResource(() => {
return {
itemIds:
this.searchResource
.value()
?.result?.map((item) => item?.id)
.filter((id) => !!id) ?? [],
};
});
inStockResponseValue = computed(() => this.inStockResource.value());
stockInfoMap = computed(() => {
const infos = this.inStockResponseValue() ?? [];
return new Map(infos.map((info) => [info.itemId, info]));
});
getAvailableStockForItem(item: Item): number {
const stockInfo = this.stockInfoMap().get(item.id);
return calculateAvailableStock({
stock: stockInfo?.inStock,
removedFromStock: stockInfo?.removedFromStock,
});
}
triggerSearch(): void {
this.searchParams.set({
searchTerm: this.host.searchTerm(),

View File

@@ -6,7 +6,13 @@
[orientation]="productInfoOrientation()"
[innerGridClass]="'grid-cols-[minmax(20rem,1fr),minmax(18rem,auto)]'"
></remi-product-info>
<div class="text-right">
<div class="flex flex-col items-end justify-center gap-6">
<div
class="text-isa-neutral-900 w-[18rem] flex flex-row items-center justify-between"
>
<span class="isa-text-body-2-regular">Aktueller Bestand</span>
<span class="isa-text-body-2-bold">{{ inStock() }}x</span>
</div>
<button
class="-mr-5"
type="button"

View File

@@ -22,6 +22,7 @@ export class SearchItemToRemitComponent {
host = inject(SearchItemToRemitDialogComponent);
item = input.required<Item>();
inStock = input.required<number>();
desktopBreakpoint = breakpoint([Breakpoint.DekstopL, Breakpoint.DekstopXL]);