mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1844: feat(oms-data-access, oms-shared-task-list): add Tolino return receipt print support and improve task action typing
feat(oms-data-access, oms-shared-task-list): add Tolino return receipt print support and improve task action typing - Add `PrintTolinoReturnReceiptService` to `oms-data-access` for printing Tolino return receipts via office printers, including direct integration with the OMS print API. - Extend `TaskActionType` to include `receiptItemId` for more precise task identification and action handling. - Update `return-task-list-item` and `return-task-list` components in `oms-shared-task-list` to support the new Tolino print action, including UI and logic for triggering the print dialog. - Refactor print-related service and test code to use the new print API signature and improve type safety. - Add and update unit tests to cover new print flows and ensure correct integration. Ref: #5121
This commit is contained in:
committed by
Lorenz Hilpert
parent
bcd3c800b1
commit
543de57190
@@ -244,14 +244,15 @@ export { ResponseArgsOfIEnumerableOfReceiptDTO } from './models/response-args-of
|
||||
export { GenerateCollectiveReceiptsArgs } from './models/generate-collective-receipts-args';
|
||||
export { ResponseArgsOfIEnumerableOfString } from './models/response-args-of-ienumerable-of-string';
|
||||
export { DateRange } from './models/date-range';
|
||||
export { ResponseArgsOfString } from './models/response-args-of-string';
|
||||
export { ListResponseArgsOfReceiptItemTaskListItemDTO } from './models/list-response-args-of-receipt-item-task-list-item-dto';
|
||||
export { ResponseArgsOfIEnumerableOfReceiptItemTaskListItemDTO } from './models/response-args-of-ienumerable-of-receipt-item-task-list-item-dto';
|
||||
export { ListResponseArgsOfReceiptListItemDTO } from './models/list-response-args-of-receipt-list-item-dto';
|
||||
export { ResponseArgsOfIEnumerableOfReceiptListItemDTO } from './models/response-args-of-ienumerable-of-receipt-list-item-dto';
|
||||
export { ReceiptListItemDTO } from './models/receipt-list-item-dto';
|
||||
export { ListResponseArgsOfReceiptItemListItemDTO } from './models/list-response-args-of-receipt-item-list-item-dto';
|
||||
export { ResponseArgsOfIEnumerableOfReceiptItemListItemDTO } from './models/response-args-of-ienumerable-of-receipt-item-list-item-dto';
|
||||
export { ReceiptItemListItemDTO } from './models/receipt-item-list-item-dto';
|
||||
export { ListResponseArgsOfReceiptItemTaskListItemDTO } from './models/list-response-args-of-receipt-item-task-list-item-dto';
|
||||
export { ResponseArgsOfIEnumerableOfReceiptItemTaskListItemDTO } from './models/response-args-of-ienumerable-of-receipt-item-task-list-item-dto';
|
||||
export { ResponseArgsOfIEnumerableOfValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO } from './models/response-args-of-ienumerable-of-value-tuple-of-long-and-receipt-type-and-entity-dtocontainer-of-receipt-dto';
|
||||
export { ValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO } from './models/value-tuple-of-long-and-receipt-type-and-entity-dtocontainer-of-receipt-dto';
|
||||
export { ReceiptOrderItemSubsetReferenceValues } from './models/receipt-order-item-subset-reference-values';
|
||||
|
||||
@@ -6,6 +6,7 @@ import { EntityDTOContainerOfBranchDTO } from './entity-dtocontainer-of-branch-d
|
||||
import { KeyValueDTOOfStringAndString } from './key-value-dtoof-string-and-string';
|
||||
import { EntityDTOContainerOfReceiptItemDTO } from './entity-dtocontainer-of-receipt-item-dto';
|
||||
import { EntityDTOContainerOfLabelDTO } from './entity-dtocontainer-of-label-dto';
|
||||
import { LinkedRecordDTO } from './linked-record-dto';
|
||||
import { EntityDTOContainerOfOrderDTO } from './entity-dtocontainer-of-order-dto';
|
||||
import { EntityDTOContainerOfPaymentDTO } from './entity-dtocontainer-of-payment-dto';
|
||||
import { PaymentInfoDTO } from './payment-info-dto';
|
||||
@@ -57,6 +58,11 @@ export interface ReceiptDTO extends EntityDTOBaseOfReceiptDTOAndIReceipt{
|
||||
*/
|
||||
label?: EntityDTOContainerOfLabelDTO;
|
||||
|
||||
/**
|
||||
* Verknüpfte Datensätze (readonly)
|
||||
*/
|
||||
linkedRecords?: Array<LinkedRecordDTO>;
|
||||
|
||||
/**
|
||||
* Bestellung
|
||||
*/
|
||||
|
||||
@@ -45,11 +45,26 @@ export interface ReceiptItemTaskListItemDTO {
|
||||
*/
|
||||
features?: {[key: string]: string};
|
||||
|
||||
/**
|
||||
* Details
|
||||
*/
|
||||
handlingDetails?: string;
|
||||
|
||||
/**
|
||||
* Reason / Grund
|
||||
*/
|
||||
handlingReason?: string;
|
||||
|
||||
/**
|
||||
* Task ID
|
||||
*/
|
||||
id?: number;
|
||||
|
||||
/**
|
||||
* Item condition / Artikelzustand
|
||||
*/
|
||||
itemCondition?: string;
|
||||
|
||||
/**
|
||||
* Aufgabentyp
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/* tslint:disable */
|
||||
import { ResponseArgs } from './response-args';
|
||||
export interface ResponseArgsOfString extends ResponseArgs{
|
||||
|
||||
/**
|
||||
* Wert
|
||||
*/
|
||||
result?: string;
|
||||
}
|
||||
@@ -16,10 +16,11 @@ import { ResponseArgsOfIEnumerableOfReceiptDTO } from '../models/response-args-o
|
||||
import { GenerateCollectiveReceiptsArgs } from '../models/generate-collective-receipts-args';
|
||||
import { ResponseArgsOfIEnumerableOfString } from '../models/response-args-of-ienumerable-of-string';
|
||||
import { DateRange } from '../models/date-range';
|
||||
import { ListResponseArgsOfReceiptListItemDTO } from '../models/list-response-args-of-receipt-list-item-dto';
|
||||
import { QueryTokenDTO } from '../models/query-token-dto';
|
||||
import { ListResponseArgsOfReceiptItemListItemDTO } from '../models/list-response-args-of-receipt-item-list-item-dto';
|
||||
import { ResponseArgsOfString } from '../models/response-args-of-string';
|
||||
import { ListResponseArgsOfReceiptItemTaskListItemDTO } from '../models/list-response-args-of-receipt-item-task-list-item-dto';
|
||||
import { QueryTokenDTO } from '../models/query-token-dto';
|
||||
import { ListResponseArgsOfReceiptListItemDTO } from '../models/list-response-args-of-receipt-list-item-dto';
|
||||
import { ListResponseArgsOfReceiptItemListItemDTO } from '../models/list-response-args-of-receipt-item-list-item-dto';
|
||||
import { ResponseArgsOfIEnumerableOfValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO } from '../models/response-args-of-ienumerable-of-value-tuple-of-long-and-receipt-type-and-entity-dtocontainer-of-receipt-dto';
|
||||
import { ReceiptOrderItemSubsetReferenceValues } from '../models/receipt-order-item-subset-reference-values';
|
||||
@Injectable({
|
||||
@@ -33,6 +34,8 @@ class ReceiptService extends __BaseService {
|
||||
static readonly ReceiptSetReceiptItemTaskToNOKPath = '/receipt/item/task/{taskId}/nok';
|
||||
static readonly ReceiptGenerateCollectiveReceiptsPath = '/receipt/collectivereceipts';
|
||||
static readonly ReceiptGenerateCollectiveReceiptsSimulationSummaryPath = '/receipt/collectivereceipts/simulationsummary';
|
||||
static readonly ReceiptPostRuecknahmebelegPath = '/ruecknahmebeleg/{receiptId}';
|
||||
static readonly ReceiptQueryReceiptItemTasksPath = '/receipt/item/task/s';
|
||||
static readonly ReceiptQueryReceiptPath = '/receipt/s';
|
||||
static readonly ReceiptQueryReceiptItemPath = '/receipt/item/s';
|
||||
static readonly ReceiptCreateShippingNotePath = '/receipt/shippingnote/fromorder';
|
||||
@@ -40,7 +43,6 @@ class ReceiptService extends __BaseService {
|
||||
static readonly ReceiptCreateInvoicePath = '/receipt/invoice/fromorder';
|
||||
static readonly ReceiptCreateInvoice2Path = '/receipt/invoice/fromitems';
|
||||
static readonly ReceiptCreateReturnReceiptPath = '/receipt/return-receipt';
|
||||
static readonly ReceiptQueryReceiptItemTasksPath = '/receipt/item/task/s';
|
||||
static readonly ReceiptReceiptItemTaskCompletedPath = '/receipt/item/task/{taskId}/completed';
|
||||
static readonly ReceiptGetReceiptsByOrderItemSubsetPath = '/order/orderitem/orderitemsubset/receipts';
|
||||
|
||||
@@ -307,6 +309,74 @@ class ReceiptService extends __BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param receiptId undefined
|
||||
*/
|
||||
ReceiptPostRuecknahmebelegResponse(receiptId: number): __Observable<__StrictHttpResponse<ResponseArgsOfString>> {
|
||||
let __params = this.newParams();
|
||||
let __headers = new HttpHeaders();
|
||||
let __body: any = null;
|
||||
|
||||
let req = new HttpRequest<any>(
|
||||
'POST',
|
||||
this.rootUrl + `/ruecknahmebeleg/${encodeURIComponent(String(receiptId))}`,
|
||||
__body,
|
||||
{
|
||||
headers: __headers,
|
||||
params: __params,
|
||||
responseType: 'json'
|
||||
});
|
||||
|
||||
return this.http.request<any>(req).pipe(
|
||||
__filter(_r => _r instanceof HttpResponse),
|
||||
__map((_r) => {
|
||||
return _r as __StrictHttpResponse<ResponseArgsOfString>;
|
||||
})
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @param receiptId undefined
|
||||
*/
|
||||
ReceiptPostRuecknahmebeleg(receiptId: number): __Observable<ResponseArgsOfString> {
|
||||
return this.ReceiptPostRuecknahmebelegResponse(receiptId).pipe(
|
||||
__map(_r => _r.body as ResponseArgsOfString)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param queryToken undefined
|
||||
*/
|
||||
ReceiptQueryReceiptItemTasksResponse(queryToken: QueryTokenDTO): __Observable<__StrictHttpResponse<ListResponseArgsOfReceiptItemTaskListItemDTO>> {
|
||||
let __params = this.newParams();
|
||||
let __headers = new HttpHeaders();
|
||||
let __body: any = null;
|
||||
__body = queryToken;
|
||||
let req = new HttpRequest<any>(
|
||||
'POST',
|
||||
this.rootUrl + `/receipt/item/task/s`,
|
||||
__body,
|
||||
{
|
||||
headers: __headers,
|
||||
params: __params,
|
||||
responseType: 'json'
|
||||
});
|
||||
|
||||
return this.http.request<any>(req).pipe(
|
||||
__filter(_r => _r instanceof HttpResponse),
|
||||
__map((_r) => {
|
||||
return _r as __StrictHttpResponse<ListResponseArgsOfReceiptItemTaskListItemDTO>;
|
||||
})
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @param queryToken undefined
|
||||
*/
|
||||
ReceiptQueryReceiptItemTasks(queryToken: QueryTokenDTO): __Observable<ListResponseArgsOfReceiptItemTaskListItemDTO> {
|
||||
return this.ReceiptQueryReceiptItemTasksResponse(queryToken).pipe(
|
||||
__map(_r => _r.body as ListResponseArgsOfReceiptItemTaskListItemDTO)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Belege
|
||||
* @param params The `ReceiptService.ReceiptQueryReceiptParams` containing the following parameters:
|
||||
@@ -647,42 +717,6 @@ class ReceiptService extends __BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suche nach Bestellpostenstatus-Aufgaben
|
||||
* @param queryToken Suchkriterien
|
||||
*/
|
||||
ReceiptQueryReceiptItemTasksResponse(queryToken: QueryTokenDTO): __Observable<__StrictHttpResponse<ListResponseArgsOfReceiptItemTaskListItemDTO>> {
|
||||
let __params = this.newParams();
|
||||
let __headers = new HttpHeaders();
|
||||
let __body: any = null;
|
||||
__body = queryToken;
|
||||
let req = new HttpRequest<any>(
|
||||
'POST',
|
||||
this.rootUrl + `/receipt/item/task/s`,
|
||||
__body,
|
||||
{
|
||||
headers: __headers,
|
||||
params: __params,
|
||||
responseType: 'json'
|
||||
});
|
||||
|
||||
return this.http.request<any>(req).pipe(
|
||||
__filter(_r => _r instanceof HttpResponse),
|
||||
__map((_r) => {
|
||||
return _r as __StrictHttpResponse<ListResponseArgsOfReceiptItemTaskListItemDTO>;
|
||||
})
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Suche nach Bestellpostenstatus-Aufgaben
|
||||
* @param queryToken Suchkriterien
|
||||
*/
|
||||
ReceiptQueryReceiptItemTasks(queryToken: QueryTokenDTO): __Observable<ListResponseArgsOfReceiptItemTaskListItemDTO> {
|
||||
return this.ReceiptQueryReceiptItemTasksResponse(queryToken).pipe(
|
||||
__map(_r => _r.body as ListResponseArgsOfReceiptItemTaskListItemDTO)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aufgabe auf erledigt setzen
|
||||
* @param taskId undefined
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ResponseArgs } from '../models/response-args';
|
||||
import { PrintRequestOfIEnumerableOfLong } from '../models/print-request-of-ienumerable-of-long';
|
||||
import { PrintRequestOfIEnumerableOfDisplayOrderDTO } from '../models/print-request-of-ienumerable-of-display-order-dto';
|
||||
import { PrintRequestOfIEnumerableOfPriceQRCodeDTO } from '../models/print-request-of-ienumerable-of-price-qrcode-dto';
|
||||
import { PrintRequestOfLong } from '../models/print-request-of-long';
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
@@ -25,6 +26,8 @@ class OMSPrintService extends __BaseService {
|
||||
static readonly OMSPrintReturnReceiptPath = '/print/return-receipt';
|
||||
static readonly OMSPrintKleinbetragsrechnungPath = '/print/kleinbetragsrechnung';
|
||||
static readonly OMSPrintKleinbetragsrechnungPdfPath = '/print/kleinbetragsrechnung/{receiptId}/pdf';
|
||||
static readonly OMSPrintTolinoRetourenscheinPath = '/print/tolino-retourenschein';
|
||||
static readonly OMSPrintTolinoRetourenscheinPdfPath = '/print/tolino-retourenschein/{receipItemtId}/pdf';
|
||||
|
||||
constructor(
|
||||
config: __Configuration,
|
||||
@@ -392,6 +395,78 @@ class OMSPrintService extends __BaseService {
|
||||
__map(_r => _r.body as Blob)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tolino Retourenschein
|
||||
* @param data Retourenbelegpostion PKs
|
||||
*/
|
||||
OMSPrintTolinoRetourenscheinResponse(data: PrintRequestOfLong): __Observable<__StrictHttpResponse<ResponseArgs>> {
|
||||
let __params = this.newParams();
|
||||
let __headers = new HttpHeaders();
|
||||
let __body: any = null;
|
||||
__body = data;
|
||||
let req = new HttpRequest<any>(
|
||||
'POST',
|
||||
this.rootUrl + `/print/tolino-retourenschein`,
|
||||
__body,
|
||||
{
|
||||
headers: __headers,
|
||||
params: __params,
|
||||
responseType: 'json'
|
||||
});
|
||||
|
||||
return this.http.request<any>(req).pipe(
|
||||
__filter(_r => _r instanceof HttpResponse),
|
||||
__map((_r) => {
|
||||
return _r as __StrictHttpResponse<ResponseArgs>;
|
||||
})
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Tolino Retourenschein
|
||||
* @param data Retourenbelegpostion PKs
|
||||
*/
|
||||
OMSPrintTolinoRetourenschein(data: PrintRequestOfLong): __Observable<ResponseArgs> {
|
||||
return this.OMSPrintTolinoRetourenscheinResponse(data).pipe(
|
||||
__map(_r => _r.body as ResponseArgs)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tolino Retourenschein PDF
|
||||
* @param receipItemtId Retourenbelegposition PK
|
||||
*/
|
||||
OMSPrintTolinoRetourenscheinPdfResponse(receipItemtId: number): __Observable<__StrictHttpResponse<Blob>> {
|
||||
let __params = this.newParams();
|
||||
let __headers = new HttpHeaders();
|
||||
let __body: any = null;
|
||||
|
||||
let req = new HttpRequest<any>(
|
||||
'GET',
|
||||
this.rootUrl + `/print/tolino-retourenschein/${encodeURIComponent(String(receipItemtId))}/pdf`,
|
||||
__body,
|
||||
{
|
||||
headers: __headers,
|
||||
params: __params,
|
||||
responseType: 'blob'
|
||||
});
|
||||
|
||||
return this.http.request<any>(req).pipe(
|
||||
__filter(_r => _r instanceof HttpResponse),
|
||||
__map((_r) => {
|
||||
return _r as __StrictHttpResponse<Blob>;
|
||||
})
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Tolino Retourenschein PDF
|
||||
* @param receipItemtId Retourenbelegposition PK
|
||||
*/
|
||||
OMSPrintTolinoRetourenscheinPdf(receipItemtId: number): __Observable<Blob> {
|
||||
return this.OMSPrintTolinoRetourenscheinPdfResponse(receipItemtId).pipe(
|
||||
__map(_r => _r.body as Blob)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module OMSPrintService {
|
||||
|
||||
@@ -143,7 +143,10 @@ describe('PrintService', () => {
|
||||
|
||||
it('should fetch printers of the specified type', async () => {
|
||||
// Act
|
||||
await spectator.service.print(PrinterType.LABEL, mockPrint);
|
||||
await spectator.service.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: mockPrint,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(printSpy).toHaveBeenCalledWith(PrinterType.LABEL);
|
||||
@@ -151,7 +154,10 @@ describe('PrintService', () => {
|
||||
|
||||
it('should attempt direct printing on desktop when a printer is selected', async () => {
|
||||
// Act
|
||||
await spectator.service.print(PrinterType.LABEL, mockPrint);
|
||||
await spectator.service.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: mockPrint,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(mockPrint).toHaveBeenCalledWith(mockPrinters[1]);
|
||||
@@ -160,10 +166,10 @@ describe('PrintService', () => {
|
||||
|
||||
it('should return the selected printer after successful direct print', async () => {
|
||||
// Act
|
||||
const result = await spectator.service.print(
|
||||
PrinterType.LABEL,
|
||||
mockPrint,
|
||||
);
|
||||
const result = await spectator.service.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: mockPrint,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual({ printer: mockPrinters[1] });
|
||||
@@ -175,7 +181,10 @@ describe('PrintService', () => {
|
||||
mockPrint.mockRejectedValueOnce(printError);
|
||||
|
||||
// Act
|
||||
await spectator.service.print(PrinterType.LABEL, mockPrint);
|
||||
await spectator.service.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: mockPrint,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(mockDialogFn).toHaveBeenCalledWith({
|
||||
@@ -192,7 +201,10 @@ describe('PrintService', () => {
|
||||
platform.ANDROID = true;
|
||||
|
||||
// Act
|
||||
await spectator.service.print(PrinterType.LABEL, mockPrint);
|
||||
await spectator.service.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: mockPrint,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(mockPrint).not.toHaveBeenCalled();
|
||||
@@ -204,7 +216,10 @@ describe('PrintService', () => {
|
||||
platform.IOS = true;
|
||||
|
||||
// Act
|
||||
await spectator.service.print(PrinterType.LABEL, mockPrint);
|
||||
await spectator.service.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: mockPrint,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(mockPrint).not.toHaveBeenCalled();
|
||||
@@ -221,7 +236,10 @@ describe('PrintService', () => {
|
||||
printSpy.mockResolvedValueOnce(printersWithoutSelection);
|
||||
|
||||
// Act
|
||||
await spectator.service.print(PrinterType.LABEL, mockPrint);
|
||||
await spectator.service.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: mockPrint,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(mockPrint).not.toHaveBeenCalled();
|
||||
@@ -233,7 +251,10 @@ describe('PrintService', () => {
|
||||
platform.ANDROID = true; // Force dialog to show
|
||||
|
||||
// Act
|
||||
await spectator.service.print(PrinterType.LABEL, mockPrint);
|
||||
await spectator.service.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: mockPrint,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(mockDialogFn).toHaveBeenCalledWith({
|
||||
@@ -252,10 +273,10 @@ describe('PrintService', () => {
|
||||
mockDialogClosedObservable.closed = of({ printer: selectedPrinter });
|
||||
|
||||
// Act
|
||||
const result = await spectator.service.print(
|
||||
PrinterType.LABEL,
|
||||
mockPrint,
|
||||
);
|
||||
const result = await spectator.service.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: mockPrint,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual({ printer: selectedPrinter });
|
||||
@@ -267,10 +288,10 @@ describe('PrintService', () => {
|
||||
mockDialogClosedObservable.closed = of(undefined);
|
||||
|
||||
// Act
|
||||
const result = await spectator.service.print(
|
||||
PrinterType.LABEL,
|
||||
mockPrint,
|
||||
);
|
||||
const result = await spectator.service.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: mockPrint,
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual({ printer: undefined });
|
||||
|
||||
@@ -48,18 +48,27 @@ export class PrintService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a print operation with platform-specific optimizations
|
||||
* On desktop, attempts to print directly using the default printer if available
|
||||
* Falls back to showing a printer selection dialog if needed or on mobile devices
|
||||
* Initiates a print operation with platform-specific optimizations.
|
||||
* On desktop, attempts to print directly using the default printer if available.
|
||||
* Falls back to showing a printer selection dialog if needed or on mobile devices.
|
||||
*
|
||||
* @param printerType The type of printer to use (LABEL or OFFICE)
|
||||
* @param printFn Function that performs the actual print operation with the selected printer
|
||||
* @returns Object containing the selected printer or undefined if operation was cancelled
|
||||
* @param printerType - The type of printer to use (LABEL or OFFICE).
|
||||
* @param printFn - Function that performs the actual print operation with the selected printer.
|
||||
* @param directPrint - (Optional) If true, forces direct print without showing the dialog.
|
||||
* If false, always shows the print dialog.
|
||||
* If undefined, uses direct print only on non-mobile platforms with a selected printer.
|
||||
* @returns An object containing the selected printer if the operation was successful,
|
||||
* or undefined if the operation was cancelled.
|
||||
*/
|
||||
async print(
|
||||
printerType: PrinterType,
|
||||
printFn: (printer: Printer) => Promise<any>,
|
||||
): Promise<{ printer?: Printer }> {
|
||||
async print({
|
||||
printerType,
|
||||
printFn,
|
||||
directPrint,
|
||||
}: {
|
||||
printerType: PrinterType;
|
||||
printFn: (printer: Printer) => Promise<unknown>;
|
||||
directPrint?: boolean | undefined;
|
||||
}): Promise<{ printer?: Printer }> {
|
||||
// Get the list of printers based on the printer type
|
||||
const printers = await this.printers(printerType);
|
||||
|
||||
@@ -69,10 +78,12 @@ export class PrintService {
|
||||
// and we can try to print directly to the selected printer.
|
||||
// If it fails, we show the print dialog with the error.
|
||||
const directPrintAllowed =
|
||||
selectedPrinter && !(this.#platform.ANDROID || this.#platform.IOS);
|
||||
directPrint === undefined
|
||||
? selectedPrinter && !(this.#platform.ANDROID || this.#platform.IOS)
|
||||
: directPrint;
|
||||
|
||||
let error: unknown | undefined = undefined;
|
||||
if (directPrintAllowed) {
|
||||
if (directPrintAllowed && selectedPrinter) {
|
||||
try {
|
||||
await printFn(selectedPrinter);
|
||||
return { printer: selectedPrinter };
|
||||
|
||||
@@ -12,6 +12,7 @@ export type TaskActionTypeType =
|
||||
export interface TaskActionType {
|
||||
type: TaskActionTypeType;
|
||||
taskId: number;
|
||||
receiptItemId?: number;
|
||||
updateTo?: Exclude<TaskActionTypeType, 'UNKNOWN'>;
|
||||
actions?: Array<KeyValueDTOOfStringAndString>;
|
||||
}
|
||||
|
||||
@@ -4,3 +4,4 @@ export * from './print-receipts.service';
|
||||
export * from './return-process.service';
|
||||
export * from './return-search.service';
|
||||
export * from './return-task-list.service';
|
||||
export * from './print-tolino-return-receipt.service';
|
||||
|
||||
@@ -35,17 +35,19 @@ describe('PrintReceiptsService', () => {
|
||||
it('should call the print service with the correct parameters', async () => {
|
||||
const mockReturnReceiptIds = [1, 2, 3];
|
||||
|
||||
mockPrintService.print.mockImplementation((printerType, callback) => {
|
||||
expect(printerType).toBe(PrinterType.LABEL);
|
||||
const mockPrinter: Printer = {
|
||||
key: 'mockPrinterKey',
|
||||
value: 'Mock Printer',
|
||||
selected: true,
|
||||
enabled: true,
|
||||
description: 'Mock printer description',
|
||||
};
|
||||
return callback(mockPrinter);
|
||||
});
|
||||
mockPrintService.print.mockImplementation(
|
||||
async ({ printerType, printFn }) => {
|
||||
expect(printerType).toBe(PrinterType.LABEL);
|
||||
const mockPrinter: Printer = {
|
||||
key: 'mockPrinterKey',
|
||||
value: 'Mock Printer',
|
||||
selected: true,
|
||||
enabled: true,
|
||||
description: 'Mock printer description',
|
||||
};
|
||||
return printFn(mockPrinter).then(() => ({ printer: mockPrinter }));
|
||||
},
|
||||
);
|
||||
|
||||
mockOmsPrintService.OMSPrintReturnReceipt.mockReturnValue(
|
||||
of({ error: false }),
|
||||
@@ -56,8 +58,10 @@ describe('PrintReceiptsService', () => {
|
||||
});
|
||||
|
||||
expect(mockPrintService.print).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.any(Function),
|
||||
expect.objectContaining({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: expect.any(Function),
|
||||
}),
|
||||
);
|
||||
expect(mockOmsPrintService.OMSPrintReturnReceipt).toHaveBeenCalledWith({
|
||||
printer: expect.any(String),
|
||||
|
||||
@@ -31,13 +31,16 @@ export class PrintReceiptsService {
|
||||
throw new Error('No return receipt IDs provided');
|
||||
}
|
||||
|
||||
return this.#printService.print(PrinterType.LABEL, (printer) => {
|
||||
return firstValueFrom(
|
||||
this.#omsPrintService.OMSPrintReturnReceipt({
|
||||
printer: printer.key,
|
||||
data: returnReceiptIds,
|
||||
}),
|
||||
);
|
||||
return this.#printService.print({
|
||||
printerType: PrinterType.LABEL,
|
||||
printFn: (printer) => {
|
||||
return firstValueFrom(
|
||||
this.#omsPrintService.OMSPrintReturnReceipt({
|
||||
printer: printer.key,
|
||||
data: returnReceiptIds,
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { SpectatorService, createServiceFactory } from '@ngneat/spectator/jest';
|
||||
import { PrintTolinoReturnReceiptService } from './print-tolino-return-receipt.service';
|
||||
import { OMSPrintService } from '@generated/swagger/print-api';
|
||||
import { PrintService, Printer, PrinterType } from '@isa/common/print';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
describe('PrintTolinoReturnReceiptService', () => {
|
||||
let spectator: SpectatorService<PrintTolinoReturnReceiptService>;
|
||||
|
||||
const createService = createServiceFactory({
|
||||
service: PrintTolinoReturnReceiptService,
|
||||
mocks: [OMSPrintService, PrintService],
|
||||
});
|
||||
|
||||
let mockPrintService: jest.Mocked<PrintService>;
|
||||
let mockOmsPrintService: jest.Mocked<OMSPrintService>;
|
||||
|
||||
beforeEach(() => {
|
||||
spectator = createService();
|
||||
mockPrintService = spectator.inject(PrintService);
|
||||
mockOmsPrintService = spectator.inject(OMSPrintService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(spectator.service).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('printTolinoReturnReceipt', () => {
|
||||
it('should throw an error if no return receipt ID is provided', async () => {
|
||||
await expect(
|
||||
spectator.service.printTolinoReturnReceipt({
|
||||
returnReceiptId: undefined as unknown as number,
|
||||
}),
|
||||
).rejects.toThrow('No return receipt ID provided');
|
||||
});
|
||||
|
||||
it('should call the print service with the correct parameters', async () => {
|
||||
const mockReturnReceiptId = 42;
|
||||
|
||||
mockPrintService.print.mockImplementation(
|
||||
async ({ printerType, printFn, directPrint }) => {
|
||||
expect(printerType).toBe(PrinterType.OFFICE);
|
||||
expect(directPrint).toBe(false);
|
||||
const mockPrinter: Printer = {
|
||||
key: 'mockPrinterKey',
|
||||
value: 'Mock Printer',
|
||||
selected: true,
|
||||
enabled: true,
|
||||
description: 'Mock printer description',
|
||||
};
|
||||
return printFn(mockPrinter).then(() => ({ printer: mockPrinter }));
|
||||
},
|
||||
);
|
||||
|
||||
mockOmsPrintService.OMSPrintTolinoRetourenschein.mockReturnValue(
|
||||
of({ error: false }),
|
||||
);
|
||||
|
||||
await spectator.service.printTolinoReturnReceipt({
|
||||
returnReceiptId: mockReturnReceiptId,
|
||||
});
|
||||
|
||||
expect(mockPrintService.print).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
printerType: PrinterType.OFFICE,
|
||||
printFn: expect.any(Function),
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
mockOmsPrintService.OMSPrintTolinoRetourenschein,
|
||||
).toHaveBeenCalledWith({
|
||||
printer: expect.any(String),
|
||||
data: mockReturnReceiptId,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { OMSPrintService } from '@generated/swagger/print-api';
|
||||
import { PrinterType, PrintService } from '@isa/common/print';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PrintTolinoReturnReceiptService {
|
||||
#omsPrintService = inject(OMSPrintService);
|
||||
#printService = inject(PrintService);
|
||||
|
||||
/**
|
||||
* Prints a Tolino return receipt using the office printer.
|
||||
*
|
||||
* @param params - The parameters for printing.
|
||||
* @param params.returnReceiptId - The unique identifier of the return receipt to print.
|
||||
* @returns A promise that resolves when the print job is completed.
|
||||
* @throws {Error} If no return receipt ID is provided.
|
||||
*
|
||||
* @example
|
||||
* await printTolinoReturnReceiptService.printTolinoReturnReceipt({ returnReceiptId: 123 });
|
||||
*/
|
||||
async printTolinoReturnReceipt({
|
||||
returnReceiptId,
|
||||
}: {
|
||||
returnReceiptId: number;
|
||||
}) {
|
||||
if (!returnReceiptId) {
|
||||
throw new Error('No return receipt ID provided');
|
||||
}
|
||||
|
||||
return this.#printService.print({
|
||||
printerType: PrinterType.OFFICE,
|
||||
printFn: (printer) => {
|
||||
return firstValueFrom(
|
||||
this.#omsPrintService.OMSPrintTolinoRetourenschein({
|
||||
printer: printer.key,
|
||||
data: returnReceiptId,
|
||||
}),
|
||||
);
|
||||
},
|
||||
directPrint: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<oms-shared-return-product-info
|
||||
class="product-info"
|
||||
[product]="product()"
|
||||
data-what="component"
|
||||
data-which="return-product-info"
|
||||
@@ -6,19 +7,25 @@
|
||||
|
||||
@let taskItem = item();
|
||||
@let taskActionType = type();
|
||||
<div
|
||||
class="h-full p-4 flex flex-col gap-4 rounded-lg bg-isa-secondary-100"
|
||||
[class.unkown-type]="taskActionType === 'UNKNOWN'"
|
||||
>
|
||||
|
||||
@if (taskActionType === 'UNKNOWN') {
|
||||
<div
|
||||
data-what="task-list"
|
||||
data-which="processing-comment"
|
||||
class="processing-comment"
|
||||
class="processing-comment-unknown"
|
||||
>
|
||||
{{ processingComment() }}
|
||||
</div>
|
||||
@if (!taskItem?.completed) {
|
||||
@if (taskActionType !== 'UNKNOWN') {
|
||||
} @else {
|
||||
<div class="task-item-infos">
|
||||
<div
|
||||
data-what="task-list"
|
||||
data-which="processing-comment"
|
||||
class="processing-comment"
|
||||
>
|
||||
{{ processingComment() }}
|
||||
</div>
|
||||
@if (!taskItem?.completed) {
|
||||
<button
|
||||
class="flex items-center gap-2 self-end"
|
||||
type="button"
|
||||
@@ -31,21 +38,21 @@
|
||||
<ng-icon name="isaActionCheck" uiButtonIcon></ng-icon>
|
||||
Als erledigt markieren
|
||||
</button>
|
||||
} @else {
|
||||
<span
|
||||
class="flex items-center gap-2 text-isa-accent-green isa-text-body-2-bold self-end"
|
||||
data-what="info"
|
||||
data-which="completed"
|
||||
>
|
||||
<ng-icon name="isaActionCheck" uiButtonIcon></ng-icon>
|
||||
Abgeschlossen
|
||||
</span>
|
||||
}
|
||||
} @else {
|
||||
<span
|
||||
class="flex items-center gap-2 text-isa-accent-green isa-text-body-2-bold self-end"
|
||||
data-what="info"
|
||||
data-which="completed"
|
||||
>
|
||||
<ng-icon name="isaActionCheck" uiButtonIcon></ng-icon>
|
||||
Abgeschlossen
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (taskActionType === 'UNKNOWN' && !taskItem?.completed) {
|
||||
<div class="flex flex-row gap-3 h-full py-2">
|
||||
<div class="task-unknown-actions">
|
||||
<button
|
||||
class="flex items-center"
|
||||
type="button"
|
||||
@@ -71,11 +78,11 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (taskItem?.actions && taskItem.actions.length > 0) {
|
||||
@if (displayPrintTolino()) {
|
||||
<button
|
||||
data-what="button"
|
||||
data-which="print-receipt"
|
||||
class="self-start"
|
||||
class="tolino-print-cta"
|
||||
(click)="onActionClick({ type: taskActionType, actions: taskItem.actions })"
|
||||
uiInfoButton
|
||||
>
|
||||
|
||||
@@ -3,21 +3,58 @@
|
||||
}
|
||||
|
||||
.oms-shared-return-task-list-item__review {
|
||||
@apply grid grid-cols-[1fr,1fr] desktop:grid-cols-[1fr,1fr,minmax(20rem,auto)] gap-6 py-6 text-isa-secondary-900 items-center border-b border-solid border-isa-neutral-300 last:pb-0 last:border-none;
|
||||
@apply grid grid-cols-[1fr,1fr] desktop:grid-cols-[1fr,1fr,minmax(20rem,auto)] gap-x-6 py-6 text-isa-secondary-900 items-center border-b border-solid border-isa-neutral-300 last:pb-0 last:border-none;
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
grid-template-areas:
|
||||
'product infos'
|
||||
'unknown-comment actions';
|
||||
|
||||
.product-info {
|
||||
grid-area: product;
|
||||
}
|
||||
|
||||
.task-item-infos {
|
||||
grid-area: infos;
|
||||
}
|
||||
|
||||
.tolino-print-cta,
|
||||
.task-unknown-actions {
|
||||
@apply mt-6 desktop:mt-0;
|
||||
grid-area: actions;
|
||||
}
|
||||
|
||||
.processing-comment-unknown {
|
||||
@apply mt-6 desktop:mt-0;
|
||||
grid-area: unknown-comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.oms-shared-return-task-list-item__main {
|
||||
@apply bg-white rounded-2xl p-6 flex flex-col gap-6 w-[24.25rem];
|
||||
|
||||
.tolino-print-cta {
|
||||
@apply self-end;
|
||||
}
|
||||
}
|
||||
|
||||
.task-item-infos {
|
||||
@apply h-full p-4 flex flex-col gap-4 rounded-lg bg-isa-secondary-100;
|
||||
}
|
||||
|
||||
.tolino-print-cta {
|
||||
@apply w-fit self-end justify-self-end desktop:self-start;
|
||||
}
|
||||
|
||||
.task-unknown-actions {
|
||||
@apply flex flex-row gap-3 h-full py-2 items-center;
|
||||
}
|
||||
|
||||
.processing-comment {
|
||||
@apply isa-text-body-2-bold;
|
||||
}
|
||||
|
||||
.unkown-type {
|
||||
@apply rounded-none bg-isa-white;
|
||||
|
||||
.processing-comment {
|
||||
@apply isa-text-body-1-regular;
|
||||
}
|
||||
.processing-comment-unknown {
|
||||
@apply isa-text-body-1-regular;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,13 @@ export class ReturnTaskListItemComponent {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
displayPrintTolino = computed(() => {
|
||||
const item = this.item();
|
||||
return item?.actions?.some(
|
||||
(keyValue) => keyValue.key === 'PRINT_TOLINO_RETURN_RECEIPT',
|
||||
);
|
||||
});
|
||||
|
||||
type: Signal<TaskActionTypeType> = computed(() => {
|
||||
const item = this.item();
|
||||
const mappedType: Omit<TaskActionType, 'taskId'> = {
|
||||
@@ -77,8 +84,10 @@ export class ReturnTaskListItemComponent {
|
||||
|
||||
onActionClick(type: Omit<TaskActionType, 'taskId'>) {
|
||||
const taskId = this.item().id;
|
||||
const receiptItemId = this.item()?.receiptItem?.id;
|
||||
const actionType: TaskActionType = {
|
||||
taskId,
|
||||
receiptItemId,
|
||||
type: type.type,
|
||||
updateTo: type?.updateTo,
|
||||
actions: type?.actions,
|
||||
|
||||
@@ -10,7 +10,9 @@ import {
|
||||
} from '@angular/core';
|
||||
import { ReturnTaskListItemComponent } from './return-task-list-item/return-task-list-item.component';
|
||||
import {
|
||||
PrintTolinoReturnReceiptService,
|
||||
QueryTokenInput,
|
||||
ReceiptItemTaskListItem,
|
||||
ReturnTaskListService,
|
||||
ReturnTaskListStore,
|
||||
TaskActionType,
|
||||
@@ -39,6 +41,7 @@ import { logger, provideLoggerContext } from '@isa/core/logging';
|
||||
export class ReturnTaskListComponent {
|
||||
appearance = input<'main' | 'review'>('main');
|
||||
#returnTaskListService = inject(ReturnTaskListService);
|
||||
#printTolinoReturnReceiptService = inject(PrintTolinoReturnReceiptService);
|
||||
#returnTaskListStore = inject(ReturnTaskListStore);
|
||||
#logger = logger();
|
||||
|
||||
@@ -99,10 +102,15 @@ export class ReturnTaskListComponent {
|
||||
}
|
||||
|
||||
async handleAction(action: TaskActionType) {
|
||||
if (action.actions && action.actions.length > 0) {
|
||||
// TODO: Spezieller Print request für Tolino - DIN-A4 Retourenschein Drucken
|
||||
console.log('Tolino Print action: ', action.actions);
|
||||
return;
|
||||
if (
|
||||
action?.actions?.some(
|
||||
(keyValue) => keyValue.key === 'PRINT_TOLINO_RETURN_RECEIPT',
|
||||
) &&
|
||||
action.receiptItemId
|
||||
) {
|
||||
return await this.#printTolinoReturnReceiptService.printTolinoReturnReceipt(
|
||||
{ returnReceiptId: action.receiptItemId },
|
||||
);
|
||||
}
|
||||
|
||||
if (action.type === 'UNKNOWN' && !!action.updateTo) {
|
||||
|
||||
Reference in New Issue
Block a user