Merged PR 1888: fix: improve sorting of remission return receipts

fix: improve sorting of remission return receipts

- Refactor data fetching to use a single API call for all returns
- Apply sorting separately to completed and incomplete returns
- Fix template tracking to use index instead of potentially undefined ID
- Remove redundant API calls for incomplete returns

This ensures proper sorting of remission return receipts while maintaining
the separation between completed and incomplete items in the display order.

Ref: #5224
This commit is contained in:
Lorenz Hilpert
2025-07-18 06:39:44 +00:00
committed by Nino Righi
parent 442670bdd0
commit 598df7d5ed
3 changed files with 69 additions and 66 deletions

View File

@@ -50,7 +50,7 @@ export class RemissionReturnReceiptService {
* const completedReturns = await service
* .fetchCompletedRemissionReturnReceipts(controller.signal);
*/
async fetchCompletedRemissionReturnReceipts(
async fetchRemissionReturnReceipts(
abortSignal?: AbortSignal,
): Promise<Return[]> {
this.#logger.debug('Fetching completed remission return receipts');
@@ -108,50 +108,50 @@ export class RemissionReturnReceiptService {
* const incompleteReturns = await service
* .fetchIncompletedRemissionReturnReceipts();
*/
async fetchIncompletedRemissionReturnReceipts(
abortSignal?: AbortSignal,
): Promise<Return[]> {
this.#logger.debug('Fetching incomplete remission return receipts');
// async fetchIncompletedRemissionReturnReceipts(
// abortSignal?: AbortSignal,
// ): Promise<Return[]> {
// this.#logger.debug('Fetching incomplete remission return receipts');
const assignedStock =
await this.#remissionStockService.fetchAssignedStock(abortSignal);
// const assignedStock =
// await this.#remissionStockService.fetchAssignedStock(abortSignal);
this.#logger.info('Fetching incomplete returns from API', () => ({
stockId: assignedStock.id,
startDate: subDays(new Date(), 7).toISOString(),
}));
// this.#logger.info('Fetching incomplete returns from API', () => ({
// stockId: assignedStock.id,
// startDate: subDays(new Date(), 7).toISOString(),
// }));
let req$ = this.#returnService.ReturnQueryReturns({
stockId: assignedStock.id,
queryToken: {
input: { returncompleted: 'false' },
start: subDays(new Date(), 7).toISOString(),
eagerLoading: 3,
},
});
// let req$ = this.#returnService.ReturnQueryReturns({
// stockId: assignedStock.id,
// queryToken: {
// input: { returncompleted: 'false' },
// start: subDays(new Date(), 7).toISOString(),
// eagerLoading: 3,
// },
// });
if (abortSignal) {
this.#logger.debug('Request configured with abort signal');
req$ = req$.pipe(takeUntilAborted(abortSignal));
}
// if (abortSignal) {
// this.#logger.debug('Request configured with abort signal');
// req$ = req$.pipe(takeUntilAborted(abortSignal));
// }
const res = await firstValueFrom(req$);
// const res = await firstValueFrom(req$);
if (res?.error) {
this.#logger.error(
'Failed to fetch incomplete returns',
new Error(res.message || 'Unknown error'),
);
throw new ResponseArgsError(res);
}
// if (res?.error) {
// this.#logger.error(
// 'Failed to fetch incomplete returns',
// new Error(res.message || 'Unknown error'),
// );
// throw new ResponseArgsError(res);
// }
const returns = (res?.result as Return[]) || [];
this.#logger.debug('Successfully fetched incomplete returns', () => ({
returnCount: returns.length,
}));
// const returns = (res?.result as Return[]) || [];
// this.#logger.debug('Successfully fetched incomplete returns', () => ({
// returnCount: returns.length,
// }));
return returns;
}
// return returns;
// }
/**
* Fetches a specific remission return receipt by receipt and return IDs.

View File

@@ -3,7 +3,7 @@
</div>
<div class="grid grid-flow-rows grid-cols-1 gap-4">
@for (remissionReturn of returns(); track remissionReturn[1].id) {
@for (remissionReturn of returns(); track $index) {
<a [routerLink]="[remissionReturn[0].id, remissionReturn[1].id]">
<remi-return-receipt-list-item
[remissionReturn]="remissionReturn[0]"

View File

@@ -64,19 +64,19 @@ export class RemissionReturnReceiptListComponent {
* Resource that fetches completed remission return receipts.
* Automatically loads when the component is initialized.
*/
completedRemissionReturnsResource = resource({
remissionReturnsResource = resource({
loader: () =>
this.#remissionReturnReceiptService.fetchCompletedRemissionReturnReceipts(),
this.#remissionReturnReceiptService.fetchRemissionReturnReceipts(),
});
/**
* Resource that fetches incomplete remission return receipts.
* Automatically loads when the component is initialized.
*/
incompletedRemissionReturnsResource = resource({
loader: () =>
this.#remissionReturnReceiptService.fetchIncompletedRemissionReturnReceipts(),
});
// incompletedRemissionReturnsResource = resource({
// loader: () =>
// this.#remissionReturnReceiptService.fetchIncompletedRemissionReturnReceipts(),
// });
/**
* Computed signal that combines completed and incomplete returns.
@@ -85,32 +85,35 @@ export class RemissionReturnReceiptListComponent {
* @returns {Array<[Return, Receipt]>} Array of tuples containing return and receipt pairs
*/
returns = computed(() => {
const completed = this.completedRemissionReturnsResource.value() || [];
const incompleted = this.incompletedRemissionReturnsResource.value() || [];
const returns = this.remissionReturnsResource.value() || [];
let completed = returns.filter((ret) => ret.completed);
let incompleted = returns.filter((ret) => !ret.completed);
const orderBy = this.orderDateBy();
const allReturnReceiptTuples = [...incompleted, ...completed].flatMap(
(ret) =>
ret.receipts
.filter((rec) => rec.data != null)
.map((rec) => [ret, rec.data] as [Return, Receipt]),
);
if (orderBy) {
const compareFn = (a: string | undefined, b: string | undefined) => {
if (a === undefined) return 1;
if (b === undefined) return -1;
return (orderBy.dir === 'desc' ? compareDesc : compareAsc)(a, b);
};
if (!orderBy) {
return allReturnReceiptTuples;
const orderByField = orderBy.by as 'created' | 'completed';
completed = orderByKey(completed, orderByField, compareFn);
incompleted = orderByKey(incompleted, orderByField, compareFn);
}
const orderByField = orderBy.by as 'created' | 'completed';
const compareFn = orderBy.dir === 'desc' ? compareDesc : compareAsc;
return allReturnReceiptTuples.sort((a, b) => {
const dateA = a[1][orderByField];
const dateB = b[1][orderByField];
if (!dateA) return -1;
if (!dateB) return 1;
return compareFn(dateA, dateB);
});
return [...incompleted, ...completed].flatMap((ret) =>
ret.receipts
.filter((rec) => rec.data != null)
.map((rec) => [ret, rec.data] as [Return, Receipt]),
);
});
}
function orderByKey<T, K extends keyof T>(
items: T[],
by: K,
compareFn: T[K] extends infer U ? (a: U, b: U) => number : never,
): T[] {
return [...items].sort((a, b) => compareFn(a[by], b[by]) ?? 0);
}