mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
committed by
Nino Righi
parent
1d4c900d3a
commit
89b3d9aa60
@@ -0,0 +1,95 @@
|
||||
import {
|
||||
computed,
|
||||
inject,
|
||||
Injectable,
|
||||
resource,
|
||||
signal,
|
||||
Signal,
|
||||
} from '@angular/core';
|
||||
import { OrdersService } from '../services/orders.service';
|
||||
import { DisplayOrderDTO } from '@generated/swagger/oms-api';
|
||||
|
||||
/**
|
||||
* Resource for fetching display orders by their numeric IDs.
|
||||
*
|
||||
* Provides reactive access to display order data with loading and error states.
|
||||
* Supports both single and multiple display order fetching.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Fetch single display order
|
||||
* readonly displayOrdersResource = inject(DisplayOrdersResource);
|
||||
* this.displayOrdersResource.loadOrder(123);
|
||||
*
|
||||
* // Fetch multiple display orders
|
||||
* this.displayOrdersResource.loadOrders([123, 456, 789]);
|
||||
*
|
||||
* // Access data
|
||||
* const orders = this.displayOrdersResource.orders();
|
||||
* const isLoading = this.displayOrdersResource.loading();
|
||||
* ```
|
||||
*/
|
||||
@Injectable()
|
||||
export class DisplayOrdersResource {
|
||||
#ordersService = inject(OrdersService);
|
||||
|
||||
#orderIds = signal<number[] | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Internal resource that manages data fetching and caching
|
||||
*/
|
||||
#resource = resource({
|
||||
params: computed(() => ({
|
||||
orderIds: this.#orderIds(),
|
||||
})),
|
||||
loader: async ({ params, abortSignal }): Promise<DisplayOrderDTO[]> => {
|
||||
if (!params?.orderIds?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return await this.#ordersService.getDisplayOrders(
|
||||
params.orderIds,
|
||||
abortSignal,
|
||||
);
|
||||
},
|
||||
defaultValue: [],
|
||||
});
|
||||
|
||||
/**
|
||||
* Signal containing the array of fetched display orders.
|
||||
* Returns empty array when loading or on error.
|
||||
*/
|
||||
readonly orders: Signal<readonly DisplayOrderDTO[]> =
|
||||
this.#resource.value.asReadonly();
|
||||
|
||||
/**
|
||||
* Signal indicating whether data is currently being fetched
|
||||
*/
|
||||
readonly loading: Signal<boolean> = this.#resource.isLoading;
|
||||
|
||||
/**
|
||||
* Signal containing error message if fetch failed, otherwise null
|
||||
*/
|
||||
readonly error = computed(() => this.#resource.error()?.message ?? null);
|
||||
|
||||
/**
|
||||
* Load a single display order by its numeric ID
|
||||
*/
|
||||
loadOrder(orderId: number): void {
|
||||
this.#orderIds.set([orderId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load multiple display orders by their numeric IDs
|
||||
*/
|
||||
loadOrders(orderIds: number[] | undefined): void {
|
||||
this.#orderIds.set(orderIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually refresh the current display orders data
|
||||
*/
|
||||
refresh(): void {
|
||||
this.#resource.reload();
|
||||
}
|
||||
}
|
||||
@@ -1 +1,4 @@
|
||||
export * from './display-orders.resource';
|
||||
export * from './open-reward-tasks.resource';
|
||||
export * from './order-item-subset.resource';
|
||||
export * from './orders.resource';
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { computed, inject, Injectable, resource, Signal } from '@angular/core';
|
||||
import { DBHOrderItemListItemDTO } from '@generated/swagger/oms-api';
|
||||
import { OpenRewardTasksService } from '../services/open-reward-tasks.service';
|
||||
|
||||
/**
|
||||
* Global resource for managing open reward distribution tasks (Prämienausgabe).
|
||||
*
|
||||
* Provides reactive access to unfinished reward orders across the application.
|
||||
* This resource is provided at root level to ensure a single shared instance
|
||||
* for both the side menu indicator and the reward catalog carousel.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // In component
|
||||
* readonly openTasksResource = inject(OpenRewardTasksResource);
|
||||
* readonly hasOpenTasks = computed(() =>
|
||||
* (this.openTasksResource.tasks()?.length ?? 0) > 0
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class OpenRewardTasksResource {
|
||||
#openRewardTasksService = inject(OpenRewardTasksService);
|
||||
|
||||
/**
|
||||
* Internal resource that manages data fetching and caching
|
||||
*/
|
||||
#resource = resource({
|
||||
loader: async ({ abortSignal }): Promise<DBHOrderItemListItemDTO[]> => {
|
||||
return await this.#openRewardTasksService.getOpenRewardTasks(
|
||||
abortSignal,
|
||||
);
|
||||
},
|
||||
defaultValue: [],
|
||||
});
|
||||
|
||||
/**
|
||||
* Signal containing the array of open reward tasks.
|
||||
* Returns empty array when loading or on error.
|
||||
*/
|
||||
readonly tasks: Signal<readonly DBHOrderItemListItemDTO[]> =
|
||||
this.#resource.value.asReadonly();
|
||||
|
||||
/**
|
||||
* Signal indicating whether data is currently being fetched
|
||||
*/
|
||||
readonly loading: Signal<boolean> = this.#resource.isLoading;
|
||||
|
||||
/**
|
||||
* Signal containing error message if fetch failed, otherwise null
|
||||
*/
|
||||
readonly error = computed(
|
||||
() => this.#resource.error()?.message ?? null,
|
||||
);
|
||||
|
||||
/**
|
||||
* Signal indicating whether there are any open tasks
|
||||
*/
|
||||
readonly hasOpenTasks = computed(() => (this.tasks()?.length ?? 0) > 0);
|
||||
|
||||
/**
|
||||
* Signal containing the count of open tasks
|
||||
*/
|
||||
readonly taskCount = computed(() => this.tasks()?.length ?? 0);
|
||||
|
||||
/**
|
||||
* Manually refresh the open tasks data.
|
||||
* Useful for updating after a task is completed.
|
||||
*/
|
||||
refresh(): void {
|
||||
this.#resource.reload();
|
||||
}
|
||||
}
|
||||
92
libs/oms/data-access/src/lib/resources/orders.resource.ts
Normal file
92
libs/oms/data-access/src/lib/resources/orders.resource.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import {
|
||||
computed,
|
||||
inject,
|
||||
Injectable,
|
||||
resource,
|
||||
signal,
|
||||
Signal,
|
||||
} from '@angular/core';
|
||||
import { OrderDTO } from '@generated/swagger/oms-api';
|
||||
import { OrdersService } from '../services/orders.service';
|
||||
|
||||
/**
|
||||
* Resource for fetching orders by their numeric IDs.
|
||||
*
|
||||
* Provides reactive access to order data with loading and error states.
|
||||
* Supports both single and multiple order fetching.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Fetch single order
|
||||
* readonly ordersResource = inject(OrdersResource);
|
||||
* this.ordersResource.loadOrder(123);
|
||||
*
|
||||
* // Fetch multiple orders
|
||||
* this.ordersResource.loadOrders([123, 456, 789]);
|
||||
*
|
||||
* // Access data
|
||||
* const orders = this.ordersResource.orders();
|
||||
* const isLoading = this.ordersResource.loading();
|
||||
* ```
|
||||
*/
|
||||
@Injectable()
|
||||
export class OrdersResource {
|
||||
#ordersService = inject(OrdersService);
|
||||
|
||||
#orderIds = signal<number[] | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Internal resource that manages data fetching and caching
|
||||
*/
|
||||
#resource = resource({
|
||||
params: computed(() => ({
|
||||
orderIds: this.#orderIds(),
|
||||
})),
|
||||
loader: async ({ params, abortSignal }): Promise<OrderDTO[]> => {
|
||||
if (!params?.orderIds?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return await this.#ordersService.getOrders(params.orderIds, abortSignal);
|
||||
},
|
||||
defaultValue: [],
|
||||
});
|
||||
|
||||
/**
|
||||
* Signal containing the array of fetched orders.
|
||||
* Returns empty array when loading or on error.
|
||||
*/
|
||||
readonly orders: Signal<readonly OrderDTO[]> =
|
||||
this.#resource.value.asReadonly();
|
||||
|
||||
/**
|
||||
* Signal indicating whether data is currently being fetched
|
||||
*/
|
||||
readonly loading: Signal<boolean> = this.#resource.isLoading;
|
||||
|
||||
/**
|
||||
* Signal containing error message if fetch failed, otherwise null
|
||||
*/
|
||||
readonly error = computed(() => this.#resource.error()?.message ?? null);
|
||||
|
||||
/**
|
||||
* Load a single order by its numeric ID
|
||||
*/
|
||||
loadOrder(orderId: number): void {
|
||||
this.#orderIds.set([orderId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load multiple orders by their numeric IDs
|
||||
*/
|
||||
loadOrders(orderIds: number[] | undefined): void {
|
||||
this.#orderIds.set(orderIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually refresh the current orders data
|
||||
*/
|
||||
refresh(): void {
|
||||
this.#resource.reload();
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,73 @@
|
||||
import {
|
||||
BuyerTypeSchema,
|
||||
EntitySchema,
|
||||
KeyValueOfStringAndStringSchema,
|
||||
NotificationChannelSchema,
|
||||
} from '@isa/common/data-access';
|
||||
import { z } from 'zod';
|
||||
import { DisplayAddresseeSchema } from './display-addressee.schema';
|
||||
import { DisplayBranchSchema } from './display-branch.schema';
|
||||
import { DisplayLogisticianSchema } from './display-logistician.schema';
|
||||
import { DisplayOrderItemSchema } from './display-order-item.schema';
|
||||
import { DisplayOrderPaymentSchema } from './display-order-payment.schema';
|
||||
import { EnvironmentChannelSchema } from './environment-channel.schema';
|
||||
import { LinkedRecordSchema } from './linked-record.schema';
|
||||
import { OrderTypeSchema } from './order-type.schema';
|
||||
import { TermsOfDeliverySchema } from './terms-of-delivery.schema';
|
||||
|
||||
export const DisplayOrderSchema = z
|
||||
.object({
|
||||
actions: z.array(KeyValueOfStringAndStringSchema).describe('Actions').optional(),
|
||||
buyer: DisplayAddresseeSchema.describe('Buyer information').optional(),
|
||||
buyerComment: z.string().describe('Buyer comment').optional(),
|
||||
buyerIsGuestAccount: z.boolean().describe('Buyer is guest account').optional(),
|
||||
buyerNumber: z.string().describe('Unique buyer identifier number').optional(),
|
||||
buyerType: BuyerTypeSchema.describe('Buyer type').optional(),
|
||||
clientChannel: EnvironmentChannelSchema.describe('Client channel').optional(),
|
||||
completedDate: z.string().describe('Completed date').optional(),
|
||||
features: z.record(z.string().describe('Features'), z.string()).optional(),
|
||||
items: z.array(DisplayOrderItemSchema).describe('List of items').optional(),
|
||||
itemsCount: z.number().describe('Number of itemss').optional(),
|
||||
linkedRecords: z.array(LinkedRecordSchema).describe('List of linked records').optional(),
|
||||
logistician: DisplayLogisticianSchema.describe('Logistician information').optional(),
|
||||
notificationChannels: NotificationChannelSchema.describe('Notification channels').optional(),
|
||||
orderBranch: DisplayBranchSchema.describe('Order branch').optional(),
|
||||
orderDate: z.string().describe('Order date').optional(),
|
||||
orderNumber: z.string().describe('Order number').optional(),
|
||||
orderType: OrderTypeSchema.describe('Order type'),
|
||||
orderValue: z.number().describe('Order value').optional(),
|
||||
orderValueCurrency: z.string().describe('Order value currency').optional(),
|
||||
payer: DisplayAddresseeSchema.describe('Payer information').optional(),
|
||||
payerIsGuestAccount: z.boolean().describe('Payer is guest account').optional(),
|
||||
payerNumber: z.string().describe('Unique payer account number').optional(),
|
||||
payment: DisplayOrderPaymentSchema.describe('Payment').optional(),
|
||||
shippingAddress: DisplayAddresseeSchema.describe('Shipping address information').optional(),
|
||||
targetBranch: DisplayBranchSchema.describe('Target branch').optional(),
|
||||
termsOfDelivery: TermsOfDeliverySchema.describe('Terms of delivery').optional(),
|
||||
})
|
||||
.extend(EntitySchema.shape);
|
||||
|
||||
export type DisplayOrder = z.infer<typeof DisplayOrderSchema>;
|
||||
import {
|
||||
BuyerTypeSchema,
|
||||
EntitySchema,
|
||||
KeyValueOfStringAndStringSchema,
|
||||
NotificationChannelSchema,
|
||||
} from '@isa/common/data-access';
|
||||
import { z } from 'zod';
|
||||
import { DisplayAddresseeSchema } from './display-addressee.schema';
|
||||
import { DisplayBranchSchema } from './display-branch.schema';
|
||||
import { DisplayLogisticianSchema } from './display-logistician.schema';
|
||||
import { DisplayOrderItemSchema } from './display-order-item.schema';
|
||||
import { DisplayOrderPaymentSchema } from './display-order-payment.schema';
|
||||
import { EnvironmentChannelSchema } from './environment-channel.schema';
|
||||
import { LinkedRecordSchema } from './linked-record.schema';
|
||||
import { OrderTypeSchema } from './order-type.schema';
|
||||
import { TermsOfDeliverySchema } from './terms-of-delivery.schema';
|
||||
|
||||
export const DisplayOrderSchema = z
|
||||
.object({
|
||||
actions: z
|
||||
.array(KeyValueOfStringAndStringSchema)
|
||||
.describe('Actions')
|
||||
.optional(),
|
||||
buyer: DisplayAddresseeSchema.describe('Buyer information').optional(),
|
||||
buyerComment: z.string().describe('Buyer comment').optional(),
|
||||
buyerIsGuestAccount: z
|
||||
.boolean()
|
||||
.describe('Buyer is guest account')
|
||||
.optional(),
|
||||
buyerNumber: z
|
||||
.string()
|
||||
.describe('Unique buyer identifier number')
|
||||
.optional(),
|
||||
buyerType: BuyerTypeSchema.describe('Buyer type').optional(),
|
||||
clientChannel:
|
||||
EnvironmentChannelSchema.describe('Client channel').optional(),
|
||||
completedDate: z.string().describe('Completed date').optional(),
|
||||
features: z.record(z.string().describe('Features'), z.string()).optional(),
|
||||
items: z.array(DisplayOrderItemSchema).describe('List of items').optional(),
|
||||
itemsCount: z.number().describe('Number of itemss').optional(),
|
||||
linkedRecords: z
|
||||
.array(LinkedRecordSchema)
|
||||
.describe('List of linked records')
|
||||
.optional(),
|
||||
logistician: DisplayLogisticianSchema.describe(
|
||||
'Logistician information',
|
||||
).optional(),
|
||||
notificationChannels: NotificationChannelSchema.describe(
|
||||
'Notification channels',
|
||||
).optional(),
|
||||
orderBranch: DisplayBranchSchema.describe('Order branch').optional(),
|
||||
orderDate: z.string().describe('Order date').optional(),
|
||||
orderNumber: z.string().describe('Order number').optional(),
|
||||
orderType: OrderTypeSchema.describe('Order type'),
|
||||
orderValue: z.number().describe('Order value').optional(),
|
||||
orderValueCurrency: z.string().describe('Order value currency').optional(),
|
||||
payer: DisplayAddresseeSchema.describe('Payer information').optional(),
|
||||
payerIsGuestAccount: z
|
||||
.boolean()
|
||||
.describe('Payer is guest account')
|
||||
.optional(),
|
||||
payerNumber: z.string().describe('Unique payer account number').optional(),
|
||||
payment: DisplayOrderPaymentSchema.describe('Payment').optional(),
|
||||
shippingAddress: DisplayAddresseeSchema.describe(
|
||||
'Shipping address information',
|
||||
).optional(),
|
||||
targetBranch: DisplayBranchSchema.describe('Target branch').optional(),
|
||||
termsOfDelivery:
|
||||
TermsOfDeliverySchema.describe('Terms of delivery').optional(),
|
||||
})
|
||||
.extend(EntitySchema.shape);
|
||||
|
||||
export type DisplayOrder = z.infer<typeof DisplayOrderSchema>;
|
||||
|
||||
@@ -3,12 +3,13 @@ export * from './dbh-order-item-list-item.schema';
|
||||
export * from './display-addressee.schema';
|
||||
export * from './display-branch.schema';
|
||||
export * from './display-logistician.schema';
|
||||
export * from './display-order-item.schema';
|
||||
export * from './display-order-item-subset.schema';
|
||||
export * from './display-order-item.schema';
|
||||
export * from './display-order-payment.schema';
|
||||
export * from './display-order.schema';
|
||||
export * from './environment-channel.schema';
|
||||
export * from './fetch-order-item-subset.schema';
|
||||
export * from './fetch-receipts-by-order-item-subset-ids.schema';
|
||||
export * from './fetch-return-details.schema';
|
||||
export * from './gender.schema';
|
||||
export * from './handle-command.schema';
|
||||
@@ -32,4 +33,3 @@ export * from './shipping-type.schema';
|
||||
export * from './terms-of-delivery.schema';
|
||||
export * from './type-of-delivery.schema';
|
||||
export * from './vat-type.schema';
|
||||
export * from './fetch-receipts-by-order-item-subset-ids.schema';
|
||||
|
||||
@@ -1,14 +1 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const OrderType = {
|
||||
NotSet: 0,
|
||||
Branch: 1,
|
||||
Mail: 2,
|
||||
Download: 4,
|
||||
BranchAndDownload: 5, // Branch | Download
|
||||
MailAndDownload: 6, // Mail | Download
|
||||
} as const;
|
||||
|
||||
export const OrderTypeSchema = z.nativeEnum(OrderType).describe('Order type');
|
||||
|
||||
export type OrderType = z.infer<typeof OrderTypeSchema>;
|
||||
export { OrderTypeSchema } from '@isa/common/data-access';
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
export * from './handle-command.service';
|
||||
export * from './logistician.service';
|
||||
export * from './oms-metadata.service';
|
||||
export * from './open-reward-tasks.service';
|
||||
export * from './order-creation.service';
|
||||
export * from './order-reward-collect.service';
|
||||
export * from './orders.service';
|
||||
export * from './print-receipts.service';
|
||||
export * from './print-tolino-return-receipt.service';
|
||||
export * from './return-can-return.service';
|
||||
@@ -8,5 +11,3 @@ export * from './return-details.service';
|
||||
export * from './return-process.service';
|
||||
export * from './return-search.service';
|
||||
export * from './return-task-list.service';
|
||||
export * from './order-reward-collect.service';
|
||||
export * from './handle-command.service';
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { TabService, getMetadataHelper } from '@isa/core/tabs';
|
||||
import { DisplayOrder, DisplayOrderSchema } from '../schemas';
|
||||
import { OMS_DISPLAY_ORDERS_KEY } from '../constants';
|
||||
import z from 'zod';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class OmsMetadataService {
|
||||
#tabService = inject(TabService);
|
||||
|
||||
getDisplayOrders(tabId: number) {
|
||||
return getMetadataHelper(
|
||||
tabId,
|
||||
OMS_DISPLAY_ORDERS_KEY,
|
||||
z
|
||||
.array(DisplayOrderSchema.extend({ shoppingCartId: z.number() }))
|
||||
.optional(),
|
||||
this.#tabService.entityMap(),
|
||||
);
|
||||
}
|
||||
|
||||
addDisplayOrders(
|
||||
tabId: number,
|
||||
orders: DisplayOrder[],
|
||||
shoppingCartId: number,
|
||||
) {
|
||||
const existingOrders = this.getDisplayOrders(tabId) || [];
|
||||
this.#tabService.patchTabMetadata(tabId, {
|
||||
[OMS_DISPLAY_ORDERS_KEY]: [
|
||||
...existingOrders,
|
||||
...orders.map((order) => ({ ...order, shoppingCartId })),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
clearDisplayOrders(tabId: number) {
|
||||
this.#tabService.patchTabMetadata(tabId, {
|
||||
[OMS_DISPLAY_ORDERS_KEY]: undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import {
|
||||
AbholfachService,
|
||||
DBHOrderItemListItemDTO,
|
||||
QueryTokenDTO,
|
||||
} from '@generated/swagger/oms-api';
|
||||
import { ResponseArgsError, takeUntilAborted } from '@isa/common/data-access';
|
||||
import { logger } from '@isa/core/logging';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { Cache, CacheTimeToLive, InFlight } from '@isa/common/decorators';
|
||||
|
||||
/**
|
||||
* Service for fetching open reward distribution tasks (Prämienausgabe) from the OMS API.
|
||||
*
|
||||
* Provides cached access to unfinished reward orders with automatic request deduplication.
|
||||
* These are reward orders in processing statuses 16 (InPreparation) and 128 (ReadyForPickup)
|
||||
* that need to be completed.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class OpenRewardTasksService {
|
||||
#logger = logger(() => ({ service: 'OpenRewardTasksService' }));
|
||||
#abholfachService = inject(AbholfachService);
|
||||
|
||||
/**
|
||||
* Fetches open reward distribution tasks (unfinished Prämienausgabe orders).
|
||||
*
|
||||
* Returns reward orders that are:
|
||||
* - In status 16 (InPreparation) or 128 (ReadyForPickup)
|
||||
* - Flagged as reward items (praemie: "1-")
|
||||
*
|
||||
* Results are cached for 1 minute to balance freshness with performance.
|
||||
* Cache is automatically invalidated on refresh.
|
||||
*
|
||||
* @param abortSignal Optional abort signal for request cancellation
|
||||
* @returns Promise resolving to array of open reward tasks
|
||||
* @throws ResponseArgsError if the API call fails
|
||||
*/
|
||||
async getOpenRewardTasks(
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<DBHOrderItemListItemDTO[]> {
|
||||
this.#logger.debug('Fetching open reward tasks');
|
||||
|
||||
const payload: QueryTokenDTO = {
|
||||
input: {},
|
||||
filter: {
|
||||
orderitemprocessingstatus: '16', // InPreparation(16) and ReadyForPickup(128)
|
||||
praemie: '1-', // Reward items only
|
||||
},
|
||||
orderBy: [],
|
||||
};
|
||||
|
||||
let req$ = this.#abholfachService.AbholfachWarenausgabe(payload);
|
||||
|
||||
if (abortSignal) {
|
||||
req$ = req$.pipe(takeUntilAborted(abortSignal));
|
||||
}
|
||||
|
||||
const res = await firstValueFrom(req$);
|
||||
|
||||
if (res.error) {
|
||||
const error = new ResponseArgsError(res);
|
||||
this.#logger.error('Failed to fetch open reward tasks', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
const tasks = res.result ?? [];
|
||||
|
||||
this.#logger.debug('Open reward tasks fetched', () => ({
|
||||
taskCount: tasks.length,
|
||||
}));
|
||||
|
||||
return tasks;
|
||||
}
|
||||
}
|
||||
107
libs/oms/data-access/src/lib/services/orders.service.ts
Normal file
107
libs/oms/data-access/src/lib/services/orders.service.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import {
|
||||
OrderService,
|
||||
OrderDTO,
|
||||
DisplayOrderDTO,
|
||||
} from '@generated/swagger/oms-api';
|
||||
import { ResponseArgsError, takeUntilAborted } from '@isa/common/data-access';
|
||||
import { logger } from '@isa/core/logging';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class OrdersService {
|
||||
#logger = logger(() => ({ service: 'OrdersService' }));
|
||||
#orderService = inject(OrderService);
|
||||
|
||||
/**
|
||||
* Fetch a single order by its numeric ID
|
||||
*/
|
||||
async getOrder(
|
||||
orderId: number,
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<OrderDTO | null> {
|
||||
let req$ = this.#orderService.OrderGetOrder(orderId);
|
||||
|
||||
if (abortSignal) {
|
||||
req$ = req$.pipe(takeUntilAborted(abortSignal));
|
||||
}
|
||||
|
||||
const res = await firstValueFrom(req$);
|
||||
|
||||
if (res.error) {
|
||||
const error = new ResponseArgsError(res);
|
||||
this.#logger.error('Failed to fetch order', { orderId, error });
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.result ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch multiple orders by their numeric IDs
|
||||
* Uses Promise.all to fetch orders concurrently
|
||||
*/
|
||||
async getOrders(
|
||||
orderIds: number[],
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<OrderDTO[]> {
|
||||
if (!orderIds.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const promises = orderIds.map((orderId) =>
|
||||
this.getOrder(orderId, abortSignal),
|
||||
);
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
// Filter out null results (failed fetches)
|
||||
return results.filter((order): order is OrderDTO => order !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a single display order by its numeric ID
|
||||
*/
|
||||
async getDisplayOrder(
|
||||
orderId: number,
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<DisplayOrderDTO | null> {
|
||||
let req$ = this.#orderService.OrderGetDisplayOrder(orderId);
|
||||
|
||||
if (abortSignal) {
|
||||
req$ = req$.pipe(takeUntilAborted(abortSignal));
|
||||
}
|
||||
|
||||
const res = await firstValueFrom(req$);
|
||||
|
||||
if (res.error) {
|
||||
const error = new ResponseArgsError(res);
|
||||
this.#logger.error('Failed to fetch display order', { orderId, error });
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.result ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch multiple display orders by their numeric IDs
|
||||
* Uses Promise.all to fetch orders concurrently
|
||||
*/
|
||||
async getDisplayOrders(
|
||||
orderIds: number[],
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<DisplayOrderDTO[]> {
|
||||
if (!orderIds.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const promises = orderIds.map((orderId) =>
|
||||
this.getDisplayOrder(orderId, abortSignal),
|
||||
);
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
// Filter out null results (failed fetches)
|
||||
return results.filter((order): order is DisplayOrderDTO => !!order);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user