mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 12:50:38 +00:00
feat: Invoice number in downloaded pdf document
This commit is contained in:
@@ -471,13 +471,14 @@ export default class PaymentReceivesController extends BaseController {
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent = await this.creditNotePdf.getCreditNotePdf(
|
||||
const [pdfContent, filename] = await this.creditNotePdf.getCreditNotePdf(
|
||||
tenantId,
|
||||
creditNoteId
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
'Content-Disposition': `attachment; filename="${filename}"`,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
} else {
|
||||
|
||||
@@ -408,7 +408,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId } = req;
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
const data = await this.paymentReceiveApplication.getPaymentReceivedState(
|
||||
@@ -473,7 +473,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
]);
|
||||
// Response in pdf format.
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const pdfContent =
|
||||
const [pdfContent, filename] =
|
||||
await this.paymentReceiveApplication.getPaymentReceivePdf(
|
||||
tenantId,
|
||||
paymentReceiveId
|
||||
@@ -481,6 +481,7 @@ export default class PaymentReceivesController extends BaseController {
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
'Content-Disposition': `attachment; filename="${filename}"`,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
// Response in json format.
|
||||
|
||||
@@ -398,13 +398,15 @@ export default class SalesEstimatesController extends BaseController {
|
||||
]);
|
||||
// Retrieves estimate in pdf format.
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
|
||||
const pdfContent = await this.saleEstimatesApplication.getSaleEstimatePdf(
|
||||
tenantId,
|
||||
estimateId
|
||||
);
|
||||
const [pdfContent, filename] =
|
||||
await this.saleEstimatesApplication.getSaleEstimatePdf(
|
||||
tenantId,
|
||||
estimateId
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
'Content-Disposition': `attachment; filename="${filename}"`,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
// Retrieves estimates in json format.
|
||||
|
||||
@@ -441,13 +441,15 @@ export default class SaleInvoicesController extends BaseController {
|
||||
]);
|
||||
// Retrieves invoice in pdf format.
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
|
||||
const pdfContent = await this.saleInvoiceApplication.saleInvoicePdf(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
);
|
||||
const [pdfContent, filename] =
|
||||
await this.saleInvoiceApplication.saleInvoicePdf(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
'Content-Disposition': `attachment; filename="${filename}"`,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
// Retrieves invoice in json format.
|
||||
|
||||
@@ -113,7 +113,7 @@ export default class SalesReceiptsController extends BaseController {
|
||||
CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt),
|
||||
asyncMiddleware(this.getSaleReceiptState.bind(this)),
|
||||
this.handleServiceErrors
|
||||
);
|
||||
);
|
||||
router.get(
|
||||
'/:id',
|
||||
CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt),
|
||||
@@ -356,13 +356,15 @@ export default class SalesReceiptsController extends BaseController {
|
||||
]);
|
||||
// Retrieves receipt in pdf format.
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
|
||||
const pdfContent = await this.saleReceiptsApplication.getSaleReceiptPdf(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
const [pdfContent, filename] =
|
||||
await this.saleReceiptsApplication.getSaleReceiptPdf(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': pdfContent.length,
|
||||
'Content-Disposition': `attachment; filename="${filename}"`,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
// Retrieves receipt in json format.
|
||||
|
||||
@@ -28,8 +28,12 @@ export default class GetCreditNotePdf {
|
||||
* Retrieves sale invoice pdf content.
|
||||
* @param {number} tenantId - Tenant 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(
|
||||
tenantId,
|
||||
creditNoteId
|
||||
@@ -39,7 +43,30 @@ export default class GetCreditNotePdf {
|
||||
'modules/credit-note-standard',
|
||||
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}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,14 @@ export class SaleEstimatesPdf {
|
||||
* @param {number} tenantId -
|
||||
* @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(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
@@ -39,7 +46,25 @@ export class SaleEstimatesPdf {
|
||||
'modules/estimate-regular',
|
||||
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}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,9 @@ export class SaleInvoicePdf {
|
||||
public async saleInvoicePdf(
|
||||
tenantId: number,
|
||||
invoiceId: number
|
||||
): Promise<Buffer> {
|
||||
): Promise<[Buffer, string]> {
|
||||
const filename = await this.getInvoicePdfFilename(tenantId, invoiceId);
|
||||
|
||||
const brandingAttributes = await this.getInvoiceBrandingAttributes(
|
||||
tenantId,
|
||||
invoiceId
|
||||
@@ -44,7 +46,29 @@ export class SaleInvoicePdf {
|
||||
brandingAttributes
|
||||
);
|
||||
// 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}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ export default class GetPaymentReceivedPdf {
|
||||
async getPaymentReceivePdf(
|
||||
tenantId: number,
|
||||
paymentReceiveId: number
|
||||
): Promise<Buffer> {
|
||||
): Promise<[Buffer, string]> {
|
||||
const brandingAttributes = await this.getPaymentBrandingAttributes(
|
||||
tenantId,
|
||||
paymentReceiveId
|
||||
@@ -43,8 +43,33 @@ export default class GetPaymentReceivedPdf {
|
||||
'modules/payment-receive-standard',
|
||||
brandingAttributes
|
||||
);
|
||||
const filename = await this.getPaymentReceivedFilename(
|
||||
tenantId,
|
||||
paymentReceiveId
|
||||
);
|
||||
// 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}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,8 @@ export class SaleReceiptsPdf {
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
public async saleReceiptPdf(tenantId: number, saleReceiptId: number) {
|
||||
const filename = await this.getSaleReceiptFilename(tenantId, saleReceiptId);
|
||||
|
||||
const brandingAttributes = await this.getReceiptBrandingAttributes(
|
||||
tenantId,
|
||||
saleReceiptId
|
||||
@@ -42,7 +44,28 @@ export class SaleReceiptsPdf {
|
||||
brandingAttributes
|
||||
);
|
||||
// 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}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,8 +11,8 @@ import { compose } from '@/utils';
|
||||
function CreditNotePdfPreviewDialogContent({
|
||||
subscriptionForm: { creditNoteId },
|
||||
}) {
|
||||
const { isLoading, pdfUrl } = usePdfCreditNote(creditNoteId);
|
||||
|
||||
const { isLoading, pdfUrl, filename } = usePdfCreditNote(creditNoteId);
|
||||
|
||||
return (
|
||||
<DialogContent>
|
||||
<div class="dialog__header-actions">
|
||||
@@ -27,7 +27,7 @@ function CreditNotePdfPreviewDialogContent({
|
||||
|
||||
<AnchorButton
|
||||
href={pdfUrl}
|
||||
download={'creditNote.pdf'}
|
||||
download={filename}
|
||||
minimal={true}
|
||||
outlined={true}
|
||||
>
|
||||
|
||||
@@ -14,7 +14,7 @@ function EstimatePdfPreviewDialogContent({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
const { isLoading, pdfUrl } = usePdfEstimate(estimateId);
|
||||
const { isLoading, pdfUrl, filename } = usePdfEstimate(estimateId);
|
||||
|
||||
return (
|
||||
<DialogContent>
|
||||
@@ -30,7 +30,7 @@ function EstimatePdfPreviewDialogContent({
|
||||
|
||||
<AnchorButton
|
||||
href={pdfUrl}
|
||||
download={'estimate.pdf'}
|
||||
download={filename}
|
||||
minimal={true}
|
||||
outlined={true}
|
||||
>
|
||||
|
||||
@@ -13,7 +13,7 @@ function InvoicePdfPreviewDialogContent({
|
||||
// #withDialog
|
||||
closeDialog,
|
||||
}) {
|
||||
const { isLoading, pdfUrl } = usePdfInvoice(invoiceId);
|
||||
const { isLoading, pdfUrl, filename } = usePdfInvoice(invoiceId);
|
||||
|
||||
return (
|
||||
<DialogContent>
|
||||
@@ -29,7 +29,7 @@ function InvoicePdfPreviewDialogContent({
|
||||
|
||||
<AnchorButton
|
||||
href={pdfUrl}
|
||||
download={'invoice.pdf'}
|
||||
download={filename}
|
||||
minimal={true}
|
||||
outlined={true}
|
||||
>
|
||||
|
||||
@@ -11,7 +11,7 @@ import { compose } from '@/utils';
|
||||
function PaymentReceivePdfPreviewDialogContent({
|
||||
subscriptionForm: { paymentReceiveId },
|
||||
}) {
|
||||
const { isLoading, pdfUrl } = usePdfPaymentReceive(paymentReceiveId);
|
||||
const { isLoading, pdfUrl, filename } = usePdfPaymentReceive(paymentReceiveId);
|
||||
|
||||
return (
|
||||
<DialogContent>
|
||||
@@ -27,7 +27,7 @@ function PaymentReceivePdfPreviewDialogContent({
|
||||
|
||||
<AnchorButton
|
||||
href={pdfUrl}
|
||||
download={'payment.pdf'}
|
||||
download={filename}
|
||||
minimal={true}
|
||||
outlined={true}
|
||||
>
|
||||
|
||||
@@ -13,7 +13,7 @@ function ReceiptPdfPreviewDialogContent({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
const { isLoading, pdfUrl } = usePdfReceipt(receiptId);
|
||||
const { isLoading, pdfUrl, filename } = usePdfReceipt(receiptId);
|
||||
|
||||
return (
|
||||
<DialogContent>
|
||||
@@ -29,7 +29,7 @@ function ReceiptPdfPreviewDialogContent({
|
||||
|
||||
<AnchorButton
|
||||
href={pdfUrl}
|
||||
download={'receipt.pdf'}
|
||||
download={filename}
|
||||
minimal={true}
|
||||
outlined={true}
|
||||
>
|
||||
|
||||
@@ -8,6 +8,7 @@ export const useRequestPdf = (httpProps) => {
|
||||
const [isLoaded, setIsLoaded] = React.useState(false);
|
||||
const [pdfUrl, setPdfUrl] = React.useState('');
|
||||
const [response, setResponse] = React.useState(null);
|
||||
const [filename, setFilename] = React.useState<string>('');
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsLoading(true);
|
||||
@@ -25,10 +26,21 @@ export const useRequestPdf = (httpProps) => {
|
||||
// Build a URL from the 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);
|
||||
setIsLoading(false);
|
||||
setIsLoaded(true);
|
||||
setResponse(response);
|
||||
setFilename(_filename);
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -37,5 +49,6 @@ export const useRequestPdf = (httpProps) => {
|
||||
isLoaded,
|
||||
pdfUrl,
|
||||
response,
|
||||
filename
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user