From cd4816aa3b5c9756295fd47f8f290a511569dfa8 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Tue, 25 Nov 2025 23:46:41 +0200 Subject: [PATCH] fix: printing sale receipts --- .../CreditNotes/CreditNotes.controller.ts | 37 ++++++++++++++----- .../PaymentsReceived.controller.ts | 11 +++++- .../SaleEstimates/SaleEstimates.controller.ts | 2 +- .../queries/GetSaleEstimatePdf.ts | 8 ++-- .../SaleInvoices/SaleInvoices.controller.ts | 7 ++-- .../SaleReceipts/SaleReceipts.controller.ts | 6 +-- 6 files changed, 48 insertions(+), 23 deletions(-) diff --git a/packages/server/src/modules/CreditNotes/CreditNotes.controller.ts b/packages/server/src/modules/CreditNotes/CreditNotes.controller.ts index 3ca2b48da..eec6c4e28 100644 --- a/packages/server/src/modules/CreditNotes/CreditNotes.controller.ts +++ b/packages/server/src/modules/CreditNotes/CreditNotes.controller.ts @@ -1,3 +1,4 @@ +import { Response } from 'express'; import { ApiTags, ApiOperation, @@ -11,10 +12,12 @@ import { Controller, Delete, Get, + Headers, Param, Post, Put, Query, + Res, } from '@nestjs/common'; import { CreditNoteApplication } from './CreditNoteApplication.service'; import { ICreditNotesQueryDTO } from './types/CreditNotes.types'; @@ -26,6 +29,7 @@ import { BulkDeleteDto, ValidateBulkDeleteResponseDto, } from '@/common/dtos/BulkDelete.dto'; +import { AcceptType } from '@/constants/accept-type'; @Controller('credit-notes') @ApiTags('Credit Notes') @@ -65,8 +69,26 @@ export class CreditNotesController { }, }) @ApiResponse({ status: 404, description: 'Credit note not found' }) - getCreditNote(@Param('id') creditNoteId: number) { - return this.creditNoteApplication.getCreditNote(creditNoteId); + async getCreditNote( + @Param('id') creditNoteId: number, + @Headers('accept') acceptHeader: string, + @Res({ passthrough: true }) res: Response, + ) { + if (acceptHeader.includes(AcceptType.ApplicationPdf)) { + const [pdfContent, filename] = + await this.creditNoteApplication.getCreditNotePdf(creditNoteId); + + res.set({ + 'Content-Type': 'application/pdf', + 'Content-Length': pdfContent.length, + 'Content-Disposition': `attachment; filename="${filename}"`, + }); + res.status(200).send(pdfContent); + } else { + const creditNote = + await this.creditNoteApplication.getCreditNote(creditNoteId); + return creditNote; + } } @Get() @@ -144,13 +166,10 @@ export class CreditNotesController { status: 200, description: 'Credit notes deleted successfully', }) - bulkDeleteCreditNotes( - @Body() bulkDeleteDto: BulkDeleteDto, - ): Promise { - return this.creditNoteApplication.bulkDeleteCreditNotes( - bulkDeleteDto.ids, - { skipUndeletable: bulkDeleteDto.skipUndeletable ?? false }, - ); + bulkDeleteCreditNotes(@Body() bulkDeleteDto: BulkDeleteDto): Promise { + return this.creditNoteApplication.bulkDeleteCreditNotes(bulkDeleteDto.ids, { + skipUndeletable: bulkDeleteDto.skipUndeletable ?? false, + }); } @Delete(':id') diff --git a/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts b/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts index 06a501e17..65d23f50a 100644 --- a/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts +++ b/packages/server/src/modules/PaymentReceived/PaymentsReceived.controller.ts @@ -1,3 +1,4 @@ +import { Response } from 'express'; import { ApiExtraModels, ApiOperation, @@ -17,6 +18,7 @@ import { Post, Put, Query, + Res, } from '@nestjs/common'; import { PaymentReceivesApplication } from './PaymentReceived.application'; import { @@ -225,11 +227,18 @@ export class PaymentReceivesController { public async getPaymentReceive( @Param('id', ParseIntPipe) paymentReceiveId: number, @Headers('accept') acceptHeader: string, + @Res({ passthrough: true }) res: Response, ) { if (acceptHeader.includes(AcceptType.ApplicationPdf)) { - return this.paymentReceivesApplication.getPaymentReceivePdf( + const [pdfContent, filename] = await this.paymentReceivesApplication.getPaymentReceivePdf( paymentReceiveId, ); + res.set({ + 'Content-Type': 'application/pdf', + 'Content-Length': pdfContent.length, + 'Content-Disposition': `attachment; filename="${filename}"`, + }); + res.send(pdfContent); } else if (acceptHeader.includes(AcceptType.ApplicationTextHtml)) { const htmlContent = await this.paymentReceivesApplication.getPaymentReceivedHtml( diff --git a/packages/server/src/modules/SaleEstimates/SaleEstimates.controller.ts b/packages/server/src/modules/SaleEstimates/SaleEstimates.controller.ts index a8fe3d2f5..fe11ea3a6 100644 --- a/packages/server/src/modules/SaleEstimates/SaleEstimates.controller.ts +++ b/packages/server/src/modules/SaleEstimates/SaleEstimates.controller.ts @@ -318,7 +318,7 @@ export class SaleEstimatesController { @Res({ passthrough: true }) res: Response, ) { if (acceptHeader.includes(AcceptType.ApplicationPdf)) { - const pdfContent = + const [pdfContent] = await this.saleEstimatesApplication.getSaleEstimatePdf(estimateId); res.set({ diff --git a/packages/server/src/modules/SaleEstimates/queries/GetSaleEstimatePdf.ts b/packages/server/src/modules/SaleEstimates/queries/GetSaleEstimatePdf.ts index 7b3cbf0c7..cb6bfb6b7 100644 --- a/packages/server/src/modules/SaleEstimates/queries/GetSaleEstimatePdf.ts +++ b/packages/server/src/modules/SaleEstimates/queries/GetSaleEstimatePdf.ts @@ -26,7 +26,7 @@ export class GetSaleEstimatePdf { @Inject(SaleEstimate.name) private readonly saleEstimateModel: TenantModelProxy, - ) {} + ) { } /** * Retrieve sale estimate html content. @@ -50,9 +50,9 @@ export class GetSaleEstimatePdf { // Retrieves the sale estimate html. const htmlContent = await this.saleEstimateHtml(saleEstimateId); - - const content = + const buffer = await this.chromiumlyTenancy.convertHtmlContent(htmlContent); + const eventPayload = { saleEstimateId }; // Triggers the `onSaleEstimatePdfViewed` event. @@ -60,7 +60,7 @@ export class GetSaleEstimatePdf { events.saleEstimate.onPdfViewed, eventPayload, ); - return [content, filename]; + return [buffer, filename]; } /** diff --git a/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts b/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts index 476c7cd3b..d206313a2 100644 --- a/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts +++ b/packages/server/src/modules/SaleInvoices/SaleInvoices.controller.ts @@ -82,9 +82,7 @@ export class SaleInvoicesController { status: 200, description: 'Sale invoices deleted successfully', }) - bulkDeleteSaleInvoices( - @Body() bulkDeleteDto: BulkDeleteDto, - ): Promise { + bulkDeleteSaleInvoices(@Body() bulkDeleteDto: BulkDeleteDto): Promise { return this.saleInvoiceApplication.bulkDeleteSaleInvoices( bulkDeleteDto.ids, { skipUndeletable: bulkDeleteDto.skipUndeletable ?? false }, @@ -212,7 +210,8 @@ export class SaleInvoicesController { @Res({ passthrough: true }) res: Response, ) { if (acceptHeader.includes(AcceptType.ApplicationPdf)) { - const pdfContent = await this.saleInvoiceApplication.saleInvoicePdf(id); + const [pdfContent, filename] = + await this.saleInvoiceApplication.saleInvoicePdf(id); res.set({ 'Content-Type': 'application/pdf', diff --git a/packages/server/src/modules/SaleReceipts/SaleReceipts.controller.ts b/packages/server/src/modules/SaleReceipts/SaleReceipts.controller.ts index 9b907f7e0..35b9495d0 100644 --- a/packages/server/src/modules/SaleReceipts/SaleReceipts.controller.ts +++ b/packages/server/src/modules/SaleReceipts/SaleReceipts.controller.ts @@ -74,9 +74,7 @@ export class SaleReceiptsController { status: 200, description: 'Sale receipts deleted successfully', }) - bulkDeleteSaleReceipts( - @Body() bulkDeleteDto: BulkDeleteDto, - ): Promise { + bulkDeleteSaleReceipts(@Body() bulkDeleteDto: BulkDeleteDto): Promise { return this.saleReceiptApplication.bulkDeleteSaleReceipts( bulkDeleteDto.ids, { skipUndeletable: bulkDeleteDto.skipUndeletable ?? false }, @@ -165,7 +163,7 @@ export class SaleReceiptsController { @Res({ passthrough: true }) res: Response, ) { if (acceptHeader.includes(AcceptType.ApplicationPdf)) { - const pdfContent = + const [pdfContent] = await this.saleReceiptApplication.getSaleReceiptPdf(id); res.set({