feat: Invoice number in downloaded pdf document

This commit is contained in:
Ahmed Bouhuolia
2024-10-19 13:16:06 +02:00
parent c4ee143354
commit de50b89e5c
16 changed files with 181 additions and 36 deletions

View File

@@ -471,13 +471,14 @@ export default class PaymentReceivesController extends BaseController {
ACCEPT_TYPE.APPLICATION_PDF, ACCEPT_TYPE.APPLICATION_PDF,
]); ]);
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) { if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
const pdfContent = await this.creditNotePdf.getCreditNotePdf( const [pdfContent, filename] = await this.creditNotePdf.getCreditNotePdf(
tenantId, tenantId,
creditNoteId creditNoteId
); );
res.set({ res.set({
'Content-Type': 'application/pdf', 'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length, 'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
}); });
res.send(pdfContent); res.send(pdfContent);
} else { } else {

View File

@@ -408,7 +408,7 @@ export default class PaymentReceivesController extends BaseController {
res: Response, res: Response,
next: NextFunction next: NextFunction
) { ) {
const { tenantId } = req; const { tenantId } = req;
try { try {
const data = await this.paymentReceiveApplication.getPaymentReceivedState( const data = await this.paymentReceiveApplication.getPaymentReceivedState(
@@ -473,7 +473,7 @@ export default class PaymentReceivesController extends BaseController {
]); ]);
// Response in pdf format. // Response in pdf format.
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) { if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
const pdfContent = const [pdfContent, filename] =
await this.paymentReceiveApplication.getPaymentReceivePdf( await this.paymentReceiveApplication.getPaymentReceivePdf(
tenantId, tenantId,
paymentReceiveId paymentReceiveId
@@ -481,6 +481,7 @@ export default class PaymentReceivesController extends BaseController {
res.set({ res.set({
'Content-Type': 'application/pdf', 'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length, 'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
}); });
res.send(pdfContent); res.send(pdfContent);
// Response in json format. // Response in json format.

View File

@@ -398,13 +398,15 @@ export default class SalesEstimatesController extends BaseController {
]); ]);
// Retrieves estimate in pdf format. // Retrieves estimate in pdf format.
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) { if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
const pdfContent = await this.saleEstimatesApplication.getSaleEstimatePdf( const [pdfContent, filename] =
tenantId, await this.saleEstimatesApplication.getSaleEstimatePdf(
estimateId tenantId,
); estimateId
);
res.set({ res.set({
'Content-Type': 'application/pdf', 'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length, 'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
}); });
res.send(pdfContent); res.send(pdfContent);
// Retrieves estimates in json format. // Retrieves estimates in json format.

View File

@@ -441,13 +441,15 @@ export default class SaleInvoicesController extends BaseController {
]); ]);
// Retrieves invoice in pdf format. // Retrieves invoice in pdf format.
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) { if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
const pdfContent = await this.saleInvoiceApplication.saleInvoicePdf( const [pdfContent, filename] =
tenantId, await this.saleInvoiceApplication.saleInvoicePdf(
saleInvoiceId tenantId,
); saleInvoiceId
);
res.set({ res.set({
'Content-Type': 'application/pdf', 'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length, 'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
}); });
res.send(pdfContent); res.send(pdfContent);
// Retrieves invoice in json format. // Retrieves invoice in json format.

View File

@@ -356,13 +356,15 @@ export default class SalesReceiptsController extends BaseController {
]); ]);
// Retrieves receipt in pdf format. // Retrieves receipt in pdf format.
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) { if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
const pdfContent = await this.saleReceiptsApplication.getSaleReceiptPdf( const [pdfContent, filename] =
tenantId, await this.saleReceiptsApplication.getSaleReceiptPdf(
saleReceiptId tenantId,
); saleReceiptId
);
res.set({ res.set({
'Content-Type': 'application/pdf', 'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length, 'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
}); });
res.send(pdfContent); res.send(pdfContent);
// Retrieves receipt in json format. // Retrieves receipt in json format.

View File

@@ -28,8 +28,12 @@ export default class GetCreditNotePdf {
* Retrieves sale invoice pdf content. * Retrieves sale invoice pdf content.
* @param {number} tenantId - Tenant id. * @param {number} tenantId - Tenant id.
* @param {number} creditNoteId - Credit note id. * @param {number} creditNoteId - Credit note id.
* @returns {Promise<[Buffer, string]>}
*/ */
public async getCreditNotePdf(tenantId: number, creditNoteId: number) { public async getCreditNotePdf(
tenantId: number,
creditNoteId: number
): Promise<[Buffer, string]> {
const brandingAttributes = await this.getCreditNoteBrandingAttributes( const brandingAttributes = await this.getCreditNoteBrandingAttributes(
tenantId, tenantId,
creditNoteId creditNoteId
@@ -39,7 +43,30 @@ export default class GetCreditNotePdf {
'modules/credit-note-standard', 'modules/credit-note-standard',
brandingAttributes brandingAttributes
); );
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent); const filename = await this.getCreditNoteFilename(tenantId, creditNoteId);
const document = await this.chromiumlyTenancy.convertHtmlContent(
tenantId,
htmlContent
);
return [document, filename];
}
/**
* Retrieves the filename pdf document of the given credit note.
* @param {number} tenantId
* @param {number} creditNoteId
* @returns {Promise<string>}
*/
public async getCreditNoteFilename(
tenantId: number,
creditNoteId: number
): Promise<string> {
const { CreditNote } = this.tenancy.models(tenantId);
const creditNote = await CreditNote.query().findById(creditNoteId);
return `Credit-${creditNote.creditNoteNumber}`;
} }
/** /**

View File

@@ -29,7 +29,14 @@ export class SaleEstimatesPdf {
* @param {number} tenantId - * @param {number} tenantId -
* @param {ISaleInvoice} saleInvoice - * @param {ISaleInvoice} saleInvoice -
*/ */
public async getSaleEstimatePdf(tenantId: number, saleEstimateId: number) { public async getSaleEstimatePdf(
tenantId: number,
saleEstimateId: number
): Promise<[Buffer, string]> {
const filename = await this.getSaleEstimateFilename(
tenantId,
saleEstimateId
);
const brandingAttributes = await this.getEstimateBrandingAttributes( const brandingAttributes = await this.getEstimateBrandingAttributes(
tenantId, tenantId,
saleEstimateId saleEstimateId
@@ -39,7 +46,25 @@ export class SaleEstimatesPdf {
'modules/estimate-regular', 'modules/estimate-regular',
brandingAttributes brandingAttributes
); );
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent); const content = await this.chromiumlyTenancy.convertHtmlContent(
tenantId,
htmlContent
);
return [content, filename];
}
/**
* Retrieves the filename file document of the given estimate.
* @param {number} tenantId
* @param {number} estimateId
* @returns {Promise<string>}
*/
private async getSaleEstimateFilename(tenantId: number, estimateId: number) {
const { SaleEstimate } = this.tenancy.models(tenantId);
const estimate = await SaleEstimate.query().findById(estimateId);
return `Estimate-${estimate.estimateNumber}`;
} }
/** /**

View File

@@ -33,7 +33,9 @@ export class SaleInvoicePdf {
public async saleInvoicePdf( public async saleInvoicePdf(
tenantId: number, tenantId: number,
invoiceId: number invoiceId: number
): Promise<Buffer> { ): Promise<[Buffer, string]> {
const filename = await this.getInvoicePdfFilename(tenantId, invoiceId);
const brandingAttributes = await this.getInvoiceBrandingAttributes( const brandingAttributes = await this.getInvoiceBrandingAttributes(
tenantId, tenantId,
invoiceId invoiceId
@@ -44,7 +46,29 @@ export class SaleInvoicePdf {
brandingAttributes brandingAttributes
); );
// Converts the given html content to pdf document. // Converts the given html content to pdf document.
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent); const buffer = await this.chromiumlyTenancy.convertHtmlContent(
tenantId,
htmlContent
);
return [buffer, filename];
}
/**
* Retrieves the filename pdf document of the given invoice.
* @param {number} tenantId
* @param {number} invoiceId
* @returns {Promise<string>}
*/
private async getInvoicePdfFilename(
tenantId: number,
invoiceId: number
): Promise<string> {
const { SaleInvoice } = this.tenancy.models(tenantId);
const invoice = await SaleInvoice.query().findById(invoiceId);
return `Invoice-${invoice.invoiceNo}`;
} }
/** /**

View File

@@ -33,7 +33,7 @@ export default class GetPaymentReceivedPdf {
async getPaymentReceivePdf( async getPaymentReceivePdf(
tenantId: number, tenantId: number,
paymentReceiveId: number paymentReceiveId: number
): Promise<Buffer> { ): Promise<[Buffer, string]> {
const brandingAttributes = await this.getPaymentBrandingAttributes( const brandingAttributes = await this.getPaymentBrandingAttributes(
tenantId, tenantId,
paymentReceiveId paymentReceiveId
@@ -43,8 +43,33 @@ export default class GetPaymentReceivedPdf {
'modules/payment-receive-standard', 'modules/payment-receive-standard',
brandingAttributes brandingAttributes
); );
const filename = await this.getPaymentReceivedFilename(
tenantId,
paymentReceiveId
);
// Converts the given html content to pdf document. // Converts the given html content to pdf document.
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent); const content = await this.chromiumlyTenancy.convertHtmlContent(
tenantId,
htmlContent
);
return [content, filename];
}
/**
* Retrieves the filename of the given payment.
* @param {number} tenantId
* @param {number} paymentReceivedId
* @returns {Promise<string>}
*/
private async getPaymentReceivedFilename(
tenantId: number,
paymentReceivedId: number
): Promise<string> {
const { PaymentReceive } = this.tenancy.models(tenantId);
const payment = await PaymentReceive.query().findById(paymentReceivedId);
return `Payment-${payment.paymentReceiveNo}`;
} }
/** /**

View File

@@ -31,6 +31,8 @@ export class SaleReceiptsPdf {
* @returns {Promise<Buffer>} * @returns {Promise<Buffer>}
*/ */
public async saleReceiptPdf(tenantId: number, saleReceiptId: number) { public async saleReceiptPdf(tenantId: number, saleReceiptId: number) {
const filename = await this.getSaleReceiptFilename(tenantId, saleReceiptId);
const brandingAttributes = await this.getReceiptBrandingAttributes( const brandingAttributes = await this.getReceiptBrandingAttributes(
tenantId, tenantId,
saleReceiptId saleReceiptId
@@ -42,7 +44,28 @@ export class SaleReceiptsPdf {
brandingAttributes brandingAttributes
); );
// Renders the html content to pdf document. // Renders the html content to pdf document.
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent); const content = await this.chromiumlyTenancy.convertHtmlContent(
tenantId,
htmlContent
);
return [content, filename];
}
/**
* Retrieves the filename file document of the given sale receipt.
* @param {number} tenantId
* @param {number} receiptId
* @returns {Promise<string>}
*/
public async getSaleReceiptFilename(
tenantId: number,
receiptId: number
): Promise<string> {
const { SaleReceipt } = this.tenancy.models(tenantId);
const receipt = await SaleReceipt.query().findById(receiptId);
return `Receipt-${receipt.receiptNumber}`;
} }
/** /**

View File

@@ -11,7 +11,7 @@ import { compose } from '@/utils';
function CreditNotePdfPreviewDialogContent({ function CreditNotePdfPreviewDialogContent({
subscriptionForm: { creditNoteId }, subscriptionForm: { creditNoteId },
}) { }) {
const { isLoading, pdfUrl } = usePdfCreditNote(creditNoteId); const { isLoading, pdfUrl, filename } = usePdfCreditNote(creditNoteId);
return ( return (
<DialogContent> <DialogContent>
@@ -27,7 +27,7 @@ function CreditNotePdfPreviewDialogContent({
<AnchorButton <AnchorButton
href={pdfUrl} href={pdfUrl}
download={'creditNote.pdf'} download={filename}
minimal={true} minimal={true}
outlined={true} outlined={true}
> >

View File

@@ -14,7 +14,7 @@ function EstimatePdfPreviewDialogContent({
// #withDialogActions // #withDialogActions
closeDialog, closeDialog,
}) { }) {
const { isLoading, pdfUrl } = usePdfEstimate(estimateId); const { isLoading, pdfUrl, filename } = usePdfEstimate(estimateId);
return ( return (
<DialogContent> <DialogContent>
@@ -30,7 +30,7 @@ function EstimatePdfPreviewDialogContent({
<AnchorButton <AnchorButton
href={pdfUrl} href={pdfUrl}
download={'estimate.pdf'} download={filename}
minimal={true} minimal={true}
outlined={true} outlined={true}
> >

View File

@@ -13,7 +13,7 @@ function InvoicePdfPreviewDialogContent({
// #withDialog // #withDialog
closeDialog, closeDialog,
}) { }) {
const { isLoading, pdfUrl } = usePdfInvoice(invoiceId); const { isLoading, pdfUrl, filename } = usePdfInvoice(invoiceId);
return ( return (
<DialogContent> <DialogContent>
@@ -29,7 +29,7 @@ function InvoicePdfPreviewDialogContent({
<AnchorButton <AnchorButton
href={pdfUrl} href={pdfUrl}
download={'invoice.pdf'} download={filename}
minimal={true} minimal={true}
outlined={true} outlined={true}
> >

View File

@@ -11,7 +11,7 @@ import { compose } from '@/utils';
function PaymentReceivePdfPreviewDialogContent({ function PaymentReceivePdfPreviewDialogContent({
subscriptionForm: { paymentReceiveId }, subscriptionForm: { paymentReceiveId },
}) { }) {
const { isLoading, pdfUrl } = usePdfPaymentReceive(paymentReceiveId); const { isLoading, pdfUrl, filename } = usePdfPaymentReceive(paymentReceiveId);
return ( return (
<DialogContent> <DialogContent>
@@ -27,7 +27,7 @@ function PaymentReceivePdfPreviewDialogContent({
<AnchorButton <AnchorButton
href={pdfUrl} href={pdfUrl}
download={'payment.pdf'} download={filename}
minimal={true} minimal={true}
outlined={true} outlined={true}
> >

View File

@@ -13,7 +13,7 @@ function ReceiptPdfPreviewDialogContent({
// #withDialogActions // #withDialogActions
closeDialog, closeDialog,
}) { }) {
const { isLoading, pdfUrl } = usePdfReceipt(receiptId); const { isLoading, pdfUrl, filename } = usePdfReceipt(receiptId);
return ( return (
<DialogContent> <DialogContent>
@@ -29,7 +29,7 @@ function ReceiptPdfPreviewDialogContent({
<AnchorButton <AnchorButton
href={pdfUrl} href={pdfUrl}
download={'receipt.pdf'} download={filename}
minimal={true} minimal={true}
outlined={true} outlined={true}
> >

View File

@@ -8,6 +8,7 @@ export const useRequestPdf = (httpProps) => {
const [isLoaded, setIsLoaded] = React.useState(false); const [isLoaded, setIsLoaded] = React.useState(false);
const [pdfUrl, setPdfUrl] = React.useState(''); const [pdfUrl, setPdfUrl] = React.useState('');
const [response, setResponse] = React.useState(null); const [response, setResponse] = React.useState(null);
const [filename, setFilename] = React.useState<string>('');
React.useEffect(() => { React.useEffect(() => {
setIsLoading(true); setIsLoading(true);
@@ -25,10 +26,21 @@ export const useRequestPdf = (httpProps) => {
// Build a URL from the file // Build a URL from the file
const fileURL = URL.createObjectURL(file); const fileURL = URL.createObjectURL(file);
// Extract the filename from the Content-Disposition header
const contentDisposition = response.headers.get('Content-Disposition');
let _filename = 'default.pdf'; // Default filename if not provided by server
if (contentDisposition && contentDisposition.includes('filename=')) {
const matches = contentDisposition.match(/filename="(.+)"/);
if (matches && matches[1]) {
_filename = matches[1];
}
}
setPdfUrl(fileURL); setPdfUrl(fileURL);
setIsLoading(false); setIsLoading(false);
setIsLoaded(true); setIsLoaded(true);
setResponse(response); setResponse(response);
setFilename(_filename);
}); });
}, []); }, []);
@@ -37,5 +49,6 @@ export const useRequestPdf = (httpProps) => {
isLoaded, isLoaded,
pdfUrl, pdfUrl,
response, response,
filename
}; };
}; };