mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccbb399685 | ||
|
|
f381d433ec | ||
|
|
2ee49f7496 | ||
|
|
bb299aa595 | ||
|
|
a66867463e | ||
|
|
de50b89e5c | ||
|
|
c4ee143354 | ||
|
|
a2227016e5 | ||
|
|
4d6f65b179 | ||
|
|
758ebbe261 | ||
|
|
279890e922 | ||
|
|
44fae36b82 | ||
|
|
fc2fac80af | ||
|
|
5ad9a9654b | ||
|
|
8a4034cc5d | ||
|
|
5649657bf0 | ||
|
|
c929a7cb27 | ||
|
|
eeedb789a9 | ||
|
|
321af8c271 | ||
|
|
fd4d86e797 | ||
|
|
49988e27a2 | ||
|
|
8c94ee5982 |
37
CHANGELOG.md
37
CHANGELOG.md
@@ -2,6 +2,43 @@
|
|||||||
|
|
||||||
All notable changes to Bigcapital server-side will be in this file.
|
All notable changes to Bigcapital server-side will be in this file.
|
||||||
|
|
||||||
|
# [0.20.5]
|
||||||
|
|
||||||
|
* fix: Disable tabs of the pdf customization if the first field not filed up by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/701
|
||||||
|
* fix: Invoice form layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/705
|
||||||
|
* refactor: Invoice, estimate, receipt, credit note and payment received date input fields by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/707
|
||||||
|
* feat: Add customize templates button to edit forms by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/708
|
||||||
|
* feat: Track account, invoice and item viewed events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/709
|
||||||
|
|
||||||
|
# [0.20.4]
|
||||||
|
|
||||||
|
* fix: Delete company logo from the PDF template by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/699
|
||||||
|
* fix: Set max width/height to company logo of pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/700
|
||||||
|
|
||||||
|
# [0.20.3]
|
||||||
|
|
||||||
|
* feat: Assign default PDF template automatically by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/687
|
||||||
|
* fix: pdf template addresses controlling by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/688
|
||||||
|
* fix: Remove empty lines from address formats by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/690
|
||||||
|
* fix: Pdf templates layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/691
|
||||||
|
* feat: Download invoice pdf of the payment link by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/689
|
||||||
|
* fix: Display country name by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/693
|
||||||
|
* feat: Add shared packages to Docker container by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/694
|
||||||
|
|
||||||
|
# [0.20.2]
|
||||||
|
|
||||||
|
* feat: Assign default PDF template automatically by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/687
|
||||||
|
* fix: pdf template addresses controlling by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/688
|
||||||
|
* fix: Remove empty lines from address formats by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/690
|
||||||
|
* fix: Pdf templates layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/691
|
||||||
|
* feat: Download invoice pdf of the payment link by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/689
|
||||||
|
* fix: Display country name by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/693
|
||||||
|
* feat: Add shared packages to Docker container by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/694
|
||||||
|
|
||||||
|
# [0.20.1]
|
||||||
|
|
||||||
|
* fix: Getting uploaded object uri by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/684
|
||||||
|
|
||||||
# [0.20.0]
|
# [0.20.0]
|
||||||
|
|
||||||
* feat: Customize pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/667
|
* feat: Customize pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/667
|
||||||
|
|||||||
@@ -108,7 +108,14 @@ block head
|
|||||||
.#{prefix}-table__cell--right {
|
.#{prefix}-table__cell--right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.#{prefix}-table__cell--item .item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell--item .item .item__description{
|
||||||
|
color: #5f6b7c;
|
||||||
|
}
|
||||||
.#{prefix}-totals {
|
.#{prefix}-totals {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -143,7 +150,7 @@ block head
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
.#{prefix}-statement__value {
|
.#{prefix}-statement__value {
|
||||||
/* Styles for statement value */
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|
||||||
block content
|
block content
|
||||||
@@ -183,17 +190,20 @@ block content
|
|||||||
table(class=`${prefix}-table`)
|
table(class=`${prefix}-table`)
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
th(class=`${prefix}-table__header`) #{'Item'}
|
th(class=`${prefix}-table__header ${prefix}-table__header--item`) #{'Item'}
|
||||||
th(class=`${prefix}-table__header`) #{'Description'}
|
th(class=`${prefix}-table__header ${prefix}-table__header--quantity ${prefix}-table__header--right`) #{'Quantity'}
|
||||||
th(class=`${prefix}-table__header`) #{'Rate'}
|
th(class=`${prefix}-table__header ${prefix}-table__header--rate ${prefix}-table__header--right`) #{'Rate'}
|
||||||
th(class=`${prefix}-table__header`) #{'Total'}
|
th(class=`${prefix}-table__header ${prefix}-table__header--total ${prefix}-table__header--right`) #{'Total'}
|
||||||
tbody
|
tbody
|
||||||
each line in lines
|
each line in lines
|
||||||
tr(class=`${prefix}-table__row`)
|
tr(class=`${prefix}-table__row`)
|
||||||
td(class=`${prefix}-table__cell`) #{line.item}
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--item ${prefix}-table__cell--item`)
|
||||||
td(class=`${prefix}-table__cell`) #{line.description}
|
div.item
|
||||||
td(class=`${prefix}-table__cell--right`) #{line.rate}
|
div.item__label #{line.item}
|
||||||
td(class=`${prefix}-table__cell--right`) #{line.total}
|
div.item__description #{line.description}
|
||||||
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--quantity ${prefix}-table__cell--right`) #{line.quantity}
|
||||||
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--rate ${prefix}-table__cell--right`) #{line.rate}
|
||||||
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--total ${prefix}-table__cell--right`) #{line.total}
|
||||||
|
|
||||||
div(class=`${prefix}-totals`)
|
div(class=`${prefix}-totals`)
|
||||||
if showSubtotal
|
if showSubtotal
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ block head
|
|||||||
.#{prefix}-table__header--right {
|
.#{prefix}-table__header--right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.#{prefix}-table__header--item{
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
.#{prefix}-table__cell {
|
.#{prefix}-table__cell {
|
||||||
border-bottom: 1px solid #F6F6F6;
|
border-bottom: 1px solid #F6F6F6;
|
||||||
padding: 12px 10px;
|
padding: 12px 10px;
|
||||||
@@ -109,6 +112,14 @@ block head
|
|||||||
.#{prefix}-table__cell:last-of-type {
|
.#{prefix}-table__cell:last-of-type {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
.#{prefix}-table__cell--item .item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell--item .item .item__description{
|
||||||
|
color: #5f6b7c;
|
||||||
|
}
|
||||||
.#{prefix}-totals {
|
.#{prefix}-totals {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -143,6 +154,7 @@ block head
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
.#{prefix}-statement__value {
|
.#{prefix}-statement__value {
|
||||||
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|
||||||
block content
|
block content
|
||||||
@@ -192,17 +204,20 @@ block content
|
|||||||
table(class=`${prefix}-table`)
|
table(class=`${prefix}-table`)
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
th(class=`${prefix}-table__header`) Item
|
th(class=`${prefix}-table__header ${prefix}-table__header--item`) Item
|
||||||
th(class=`${prefix}-table__header`) Description
|
th(class=`${prefix}-table__header ${prefix}-table__header--quantity ${prefix}-table__header--right`) Qty
|
||||||
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Rate
|
th(class=`${prefix}-table__header ${prefix}-table__header--rate ${prefix}-table__header--right`) Rate
|
||||||
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Total
|
th(class=`${prefix}-table__header ${prefix}-table__header--total ${prefix}-table__header--right`) Total
|
||||||
tbody
|
tbody
|
||||||
each line in lines
|
each line in lines
|
||||||
tr
|
tr
|
||||||
td(class=`${prefix}-table__cell`) #{line.item}
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--item`)
|
||||||
td(class=`${prefix}-table__cell`) #{line.description}
|
div.item
|
||||||
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.rate}
|
div.item__label #{line.item}
|
||||||
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.total}
|
div.item__description #{line.description}
|
||||||
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--quantity ${prefix}-table__cell--right`) #{line.quantity}
|
||||||
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--rate ${prefix}-table__cell--right`) #{line.rate}
|
||||||
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--total ${prefix}-table__cell--right`) #{line.total}
|
||||||
|
|
||||||
//- Totals section
|
//- Totals section
|
||||||
div(class=`${prefix}-totals`)
|
div(class=`${prefix}-totals`)
|
||||||
@@ -216,12 +231,12 @@ block content
|
|||||||
div(class=`${prefix}-totals__item-amount`) #{total}
|
div(class=`${prefix}-totals__item-amount`) #{total}
|
||||||
|
|
||||||
//- Statements section
|
//- Statements section
|
||||||
|
if showTermsConditions && termsConditions
|
||||||
|
div(class=`${prefix}-statement`)
|
||||||
|
div(class=`${prefix}-statement__label`) #{termsConditionsLabel}
|
||||||
|
div(class=`${prefix}-statement__value`) #{termsConditions}
|
||||||
|
|
||||||
if showCustomerNote && customerNote
|
if showCustomerNote && customerNote
|
||||||
div(class=`${prefix}-statement`)
|
div(class=`${prefix}-statement`)
|
||||||
div(class=`${prefix}-statement__label`) #{customerNoteLabel}
|
div(class=`${prefix}-statement__label`) #{customerNoteLabel}
|
||||||
div(class=`${prefix}-statement__value`) #{customerNote}
|
div(class=`${prefix}-statement__value`) #{customerNote}
|
||||||
|
|
||||||
if showTermsConditions && termsConditions
|
|
||||||
div(class=`${prefix}-statement`)
|
|
||||||
div(class=`${prefix}-statement__label`) #{termsConditionsLabel}
|
|
||||||
div(class=`${prefix}-statement__value`) #{termsConditions}
|
|
||||||
@@ -102,6 +102,9 @@ block head
|
|||||||
.#{prefix}-table__header--right {
|
.#{prefix}-table__header--right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.#{prefix}-table__header--item {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
.#{prefix}-table__cell {
|
.#{prefix}-table__cell {
|
||||||
border-bottom: 1px solid #F6F6F6;
|
border-bottom: 1px solid #F6F6F6;
|
||||||
padding: 12px 10px;
|
padding: 12px 10px;
|
||||||
@@ -115,6 +118,14 @@ block head
|
|||||||
.#{prefix}-table__cell--right {
|
.#{prefix}-table__cell--right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.#{prefix}-table__cell--item .item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell--item .item__description {
|
||||||
|
color: #5f6b7c;
|
||||||
|
}
|
||||||
.#{prefix}-totals {
|
.#{prefix}-totals {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -149,7 +160,7 @@ block head
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
.#{prefix}-paragraph__value {
|
.#{prefix}-paragraph__value {
|
||||||
/* Styles for values within the paragraph section */
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
block content
|
block content
|
||||||
//- block head
|
//- block head
|
||||||
@@ -199,15 +210,18 @@ block content
|
|||||||
table(class=`${prefix}-table`)
|
table(class=`${prefix}-table`)
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
th(class=`${prefix}-table__header`) #{lineItemLabel}
|
th(class=`${prefix}-table__header ${prefix}-table__header--item`) #{lineItemLabel}
|
||||||
th(class=`${prefix}-table__header`) #{lineDescriptionLabel}
|
th(class=`${prefix}-table__header ${prefix}-table__header--quantity ${prefix}-table__header--right`) #{lineQuantityLabel}
|
||||||
th(class=`${prefix}-table__header ${prefix}-table__header--right`) #{lineRateLabel}
|
th(class=`${prefix}-table__header ${prefix}-table__header--rate ${prefix}-table__header--right`) #{lineRateLabel}
|
||||||
th(class=`${prefix}-table__header ${prefix}-table__header--right`) #{lineTotalLabel}
|
th(class=`${prefix}-table__header ${prefix}-table__header--total ${prefix}-table__header--right`) #{lineTotalLabel}
|
||||||
tbody
|
tbody
|
||||||
each line in lines
|
each line in lines
|
||||||
tr
|
tr
|
||||||
td(class=`${prefix}-table__cell`) #{line.item}
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--item`)
|
||||||
td(class=`${prefix}-table__cell`) #{line.description}
|
div.item
|
||||||
|
div.item__label #{line.item}
|
||||||
|
div.item__description #{line.description}
|
||||||
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.quantity}
|
||||||
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.rate}
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.rate}
|
||||||
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.total}
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.total}
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ block head
|
|||||||
.#{prefix}-table__header--right {
|
.#{prefix}-table__header--right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.#{prefix}-table__header--item{
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
.#{prefix}-table__cell {
|
.#{prefix}-table__cell {
|
||||||
border-bottom: 1px solid #F6F6F6;
|
border-bottom: 1px solid #F6F6F6;
|
||||||
padding: 12px 10px;
|
padding: 12px 10px;
|
||||||
@@ -105,6 +108,14 @@ block head
|
|||||||
.#{prefix}-table__cell--right {
|
.#{prefix}-table__cell--right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.#{prefix}-table__cell--item .item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.#{prefix}-table__cell--item .item .item__description{
|
||||||
|
color: #5f6b7c;
|
||||||
|
}
|
||||||
.#{prefix}-totals {
|
.#{prefix}-totals {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -133,7 +144,9 @@ block head
|
|||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
.#{prefix}-statement__label {}
|
.#{prefix}-statement__label {}
|
||||||
.#{prefix}-statement__value {}
|
.#{prefix}-statement__value {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
block content
|
block content
|
||||||
//- block head
|
//- block head
|
||||||
@@ -178,17 +191,20 @@ block content
|
|||||||
table(class=`${prefix}-table`)
|
table(class=`${prefix}-table`)
|
||||||
thead(class=`${prefix}-table__header`)
|
thead(class=`${prefix}-table__header`)
|
||||||
tr
|
tr
|
||||||
th(class=`${prefix}-table__header`) Item
|
th(class=`${prefix}-table__header ${prefix}-table__header--item`) Item
|
||||||
th(class=`${prefix}-table__header`) Description
|
th(class=`${prefix}-table__header ${prefix}-table__header--quantity ${prefix}-table__header--right`) Qty
|
||||||
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Rate
|
th(class=`${prefix}-table__header ${prefix}-table__header--rate ${prefix}-table__header--right`) Rate
|
||||||
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Total
|
th(class=`${prefix}-table__header ${prefix}-table__header--total ${prefix}-table__header--right`) Total
|
||||||
tbody
|
tbody
|
||||||
each line in lines
|
each line in lines
|
||||||
tr(class=`${prefix}-table__row`)
|
tr(class=`${prefix}-table__row`)
|
||||||
td(class=`${prefix}-table__cell`)= line.item
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--item`)
|
||||||
td(class=`${prefix}-table__cell`)= line.description
|
div.item
|
||||||
td(class=`${prefix}-table__cell${prefix}-table__cell--right`)= line.rate
|
div.item__label #{line.item}
|
||||||
td(class=`${prefix}-table__cell${prefix}-table__cell--right`)= line.total
|
div.item__description #{line.description}
|
||||||
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.quantity}
|
||||||
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.rate}
|
||||||
|
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.total}
|
||||||
|
|
||||||
//- Totals Section
|
//- Totals Section
|
||||||
div(class=`${prefix}-totals`)
|
div(class=`${prefix}-totals`)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export default class SalesReceiptsController extends BaseController {
|
|||||||
CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt),
|
CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt),
|
||||||
asyncMiddleware(this.getSaleReceiptState.bind(this)),
|
asyncMiddleware(this.getSaleReceiptState.bind(this)),
|
||||||
this.handleServiceErrors
|
this.handleServiceErrors
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/:id',
|
'/:id',
|
||||||
CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt),
|
CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt),
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -3,14 +3,21 @@ export const SALE_INVOICE_EDITED = 'Sale invoice edited';
|
|||||||
export const SALE_INVOICE_DELETED = 'Sale invoice deleted';
|
export const SALE_INVOICE_DELETED = 'Sale invoice deleted';
|
||||||
export const SALE_INVOICE_MAIL_DELIVERED = 'Sale invoice mail delivered';
|
export const SALE_INVOICE_MAIL_DELIVERED = 'Sale invoice mail delivered';
|
||||||
export const SALE_INVOICE_VIEWED = 'Sale invoice viewed';
|
export const SALE_INVOICE_VIEWED = 'Sale invoice viewed';
|
||||||
|
export const SALE_INVOICE_PDF_VIEWED = 'Sale invoice PDF viewed';
|
||||||
|
|
||||||
export const SALE_ESTIMATE_CREATED = 'Sale estimate created';
|
export const SALE_ESTIMATE_CREATED = 'Sale estimate created';
|
||||||
export const SALE_ESTIMATE_EDITED = 'Sale estimate edited';
|
export const SALE_ESTIMATE_EDITED = 'Sale estimate edited';
|
||||||
export const SALE_ESTIMATE_DELETED = 'Sale estimate deleted';
|
export const SALE_ESTIMATE_DELETED = 'Sale estimate deleted';
|
||||||
|
export const SALE_ESTIMATE_PDF_VIEWED = 'Sale estimate PDF viewed';
|
||||||
|
|
||||||
export const PAYMENT_RECEIVED_CREATED = 'Payment received created';
|
export const PAYMENT_RECEIVED_CREATED = 'Payment received created';
|
||||||
export const PAYMENT_RECEIVED_EDITED = 'payment received edited';
|
export const PAYMENT_RECEIVED_EDITED = 'payment received edited';
|
||||||
export const PAYMENT_RECEIVED_DELETED = 'Payment received deleted';
|
export const PAYMENT_RECEIVED_DELETED = 'Payment received deleted';
|
||||||
|
export const PAYMENT_RECEIVED_PDF_VIEWED = 'Payment received PDF viewed';
|
||||||
|
|
||||||
|
export const SALE_RECEIPT_PDF_VIEWED = 'Sale credit PDF viewed';
|
||||||
|
|
||||||
|
export const CREDIT_NOTE_PDF_VIEWED = 'Credit note PDF viewed';
|
||||||
|
|
||||||
export const BILL_CREATED = 'Bill created';
|
export const BILL_CREATED = 'Bill created';
|
||||||
export const BILL_EDITED = 'Bill edited';
|
export const BILL_EDITED = 'Bill edited';
|
||||||
@@ -82,7 +89,8 @@ export const PAYMENT_METHOD_DELETED = 'Payment method deleted';
|
|||||||
|
|
||||||
export const INVOICE_PAYMENT_LINK_GENERATED = 'Invoice payment link generated';
|
export const INVOICE_PAYMENT_LINK_GENERATED = 'Invoice payment link generated';
|
||||||
|
|
||||||
export const STRIPE_INTEGRAION_CONNECTED = 'Stripe integration oauth2 connected';
|
export const STRIPE_INTEGRAION_CONNECTED =
|
||||||
|
'Stripe integration oauth2 connected';
|
||||||
|
|
||||||
// # Event Groups
|
// # Event Groups
|
||||||
export const ACCOUNT_GROUP = 'Account';
|
export const ACCOUNT_GROUP = 'Account';
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ export const ACCOUNT_TYPES = [
|
|||||||
normal: ACCOUNT_NORMAL.CREDIT,
|
normal: ACCOUNT_NORMAL.CREDIT,
|
||||||
rootType: ACCOUNT_ROOT_TYPE.LIABILITY,
|
rootType: ACCOUNT_ROOT_TYPE.LIABILITY,
|
||||||
parentType: ACCOUNT_PARENT_TYPE.CURRENT_LIABILITY,
|
parentType: ACCOUNT_PARENT_TYPE.CURRENT_LIABILITY,
|
||||||
|
multiCurrency: true,
|
||||||
balanceSheet: true,
|
balanceSheet: true,
|
||||||
incomeSheet: false,
|
incomeSheet: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ export default {
|
|||||||
name: 'item.field.note',
|
name: 'item.field.note',
|
||||||
fieldType: 'text',
|
fieldType: 'text',
|
||||||
},
|
},
|
||||||
category: {
|
categoryId: {
|
||||||
name: 'item.field.category',
|
name: 'item.field.category',
|
||||||
fieldType: 'relation',
|
fieldType: 'relation',
|
||||||
relationModel: 'ItemCategory',
|
relationModel: 'ItemCategory',
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ export default class UncategorizedCashflowTransaction extends mixin(
|
|||||||
) {
|
) {
|
||||||
id!: number;
|
id!: number;
|
||||||
date!: Date | string;
|
date!: Date | string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transaction amount.
|
||||||
|
* Negative represents to spending and positive to deposit/card charge.
|
||||||
|
* @param {number}
|
||||||
|
*/
|
||||||
amount!: number;
|
amount!: number;
|
||||||
categorized!: boolean;
|
categorized!: boolean;
|
||||||
accountId!: number;
|
accountId!: number;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export class AccountTransformer extends Transformer {
|
|||||||
*/
|
*/
|
||||||
public includeAttributes = (): string[] => {
|
public includeAttributes = (): string[] => {
|
||||||
return [
|
return [
|
||||||
|
'accountTypeLabel',
|
||||||
|
'accountNormalFormatted',
|
||||||
'formattedAmount',
|
'formattedAmount',
|
||||||
'flattenName',
|
'flattenName',
|
||||||
'bankBalanceFormatted',
|
'bankBalanceFormatted',
|
||||||
@@ -84,6 +86,22 @@ export class AccountTransformer extends Transformer {
|
|||||||
return account.plaidItem?.isPaused || false;
|
return account.plaidItem?.isPaused || false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves formatted account type label.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected accountTypeLabel = (account: any): string => {
|
||||||
|
return this.context.i18n.__(account.accountTypeLabel);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves formatted account normal.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected accountNormalFormatted = (account: any): string => {
|
||||||
|
return this.context.i18n.__(account.accountNormalFormatted);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transformes the accounts collection to flat or nested array.
|
* Transformes the accounts collection to flat or nested array.
|
||||||
* @param {IAccount[]}
|
* @param {IAccount[]}
|
||||||
|
|||||||
@@ -51,10 +51,6 @@ export class GetAccount {
|
|||||||
// Triggers `onAccountViewed` event.
|
// Triggers `onAccountViewed` event.
|
||||||
await this.eventPublisher.emitAsync(events.accounts.onViewed, eventPayload);
|
await this.eventPublisher.emitAsync(events.accounts.onViewed, eventPayload);
|
||||||
|
|
||||||
return this.i18nService.i18nApply(
|
return transformed;
|
||||||
[['accountTypeLabel'], ['accountNormalFormatted']],
|
|
||||||
transformed,
|
|
||||||
tenantId
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,27 @@ import {
|
|||||||
Institution as PlaidInstitution,
|
Institution as PlaidInstitution,
|
||||||
AccountBase as PlaidAccount,
|
AccountBase as PlaidAccount,
|
||||||
TransactionBase as PlaidTransactionBase,
|
TransactionBase as PlaidTransactionBase,
|
||||||
|
AccountType as PlaidAccountType,
|
||||||
} from 'plaid';
|
} from 'plaid';
|
||||||
import {
|
import {
|
||||||
CreateUncategorizedTransactionDTO,
|
CreateUncategorizedTransactionDTO,
|
||||||
IAccountCreateDTO,
|
IAccountCreateDTO,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
|
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the system account type from the given Plaid account type.
|
||||||
|
* @param {PlaidAccountType} plaidAccountType
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
const getAccountTypeFromPlaidAccountType = (
|
||||||
|
plaidAccountType: PlaidAccountType
|
||||||
|
) => {
|
||||||
|
if (plaidAccountType === PlaidAccountType.Credit) {
|
||||||
|
return ACCOUNT_TYPE.CREDIT_CARD;
|
||||||
|
}
|
||||||
|
return ACCOUNT_TYPE.BANK;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transformes the Plaid account to create cashflow account DTO.
|
* Transformes the Plaid account to create cashflow account DTO.
|
||||||
@@ -28,7 +44,7 @@ export const transformPlaidAccountToCreateAccount = R.curry(
|
|||||||
code: '',
|
code: '',
|
||||||
description: plaidAccount.official_name,
|
description: plaidAccount.official_name,
|
||||||
currencyCode: plaidAccount.balances.iso_currency_code,
|
currencyCode: plaidAccount.balances.iso_currency_code,
|
||||||
accountType: 'cash',
|
accountType: getAccountTypeFromPlaidAccountType(plaidAccount.type),
|
||||||
active: true,
|
active: true,
|
||||||
bankBalance: plaidAccount.balances.current,
|
bankBalance: plaidAccount.balances.current,
|
||||||
accountMask: plaidAccount.mask,
|
accountMask: plaidAccount.mask,
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import {
|
import { ILedgerEntry, ICashflowTransaction } from '../../interfaces';
|
||||||
ILedgerEntry,
|
import { transformCashflowTransactionType } from './utils';
|
||||||
ICashflowTransaction,
|
|
||||||
AccountNormal,
|
|
||||||
} from '../../interfaces';
|
|
||||||
import {
|
|
||||||
transformCashflowTransactionType,
|
|
||||||
getCashflowAccountTransactionsTypes,
|
|
||||||
} from './utils';
|
|
||||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||||
import Ledger from '@/services/Accounting/Ledger';
|
import Ledger from '@/services/Accounting/Ledger';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
@@ -70,7 +63,7 @@ export default class CashflowTransactionJournalEntries {
|
|||||||
debit: cashflowTransaction.isCashDebit
|
debit: cashflowTransaction.isCashDebit
|
||||||
? cashflowTransaction.localAmount
|
? cashflowTransaction.localAmount
|
||||||
: 0,
|
: 0,
|
||||||
accountNormal: AccountNormal.DEBIT,
|
accountNormal: cashflowTransaction?.cashflowAccount?.accountNormal,
|
||||||
index: 1,
|
index: 1,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -143,6 +136,7 @@ export default class CashflowTransactionJournalEntries {
|
|||||||
// Retrieves the cashflow transactions with associated entries.
|
// Retrieves the cashflow transactions with associated entries.
|
||||||
const transaction = await CashflowTransaction.query(trx)
|
const transaction = await CashflowTransaction.query(trx)
|
||||||
.findById(cashflowTransactionId)
|
.findById(cashflowTransactionId)
|
||||||
|
.withGraphFetched('cashflowAccount')
|
||||||
.withGraphFetched('creditAccount');
|
.withGraphFetched('creditAccount');
|
||||||
|
|
||||||
// Retrieves the cashflow transaction ledger.
|
// Retrieves the cashflow transaction ledger.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { CashflowAccountTransformer } from './CashflowAccountTransformer';
|
|||||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
|
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class GetCashflowAccountsService {
|
export default class GetCashflowAccountsService {
|
||||||
@@ -41,14 +42,20 @@ export default class GetCashflowAccountsService {
|
|||||||
const accounts = await CashflowAccount.query().onBuild((builder) => {
|
const accounts = await CashflowAccount.query().onBuild((builder) => {
|
||||||
dynamicList.buildQuery()(builder);
|
dynamicList.buildQuery()(builder);
|
||||||
|
|
||||||
builder.whereIn('account_type', ['bank', 'cash']);
|
builder.whereIn('account_type', [
|
||||||
|
ACCOUNT_TYPE.BANK,
|
||||||
|
ACCOUNT_TYPE.CASH,
|
||||||
|
ACCOUNT_TYPE.CREDIT_CARD,
|
||||||
|
]);
|
||||||
builder.modify('inactiveMode', filter.inactiveMode);
|
builder.modify('inactiveMode', filter.inactiveMode);
|
||||||
});
|
});
|
||||||
// Retrieves the transformed accounts.
|
// Retrieves the transformed accounts.
|
||||||
return this.transformer.transform(
|
const transformed = await this.transformer.transform(
|
||||||
tenantId,
|
tenantId,
|
||||||
accounts,
|
accounts,
|
||||||
new CashflowAccountTransformer()
|
new CashflowAccountTransformer()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return transformed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class GetCashflowTransactionService {
|
|||||||
private tenancy: HasTenancyService;
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private transfromer: TransformerInjectable;
|
private transformer: TransformerInjectable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the given cashflow transaction.
|
* Retrieve the given cashflow transaction.
|
||||||
@@ -37,7 +37,7 @@ export class GetCashflowTransactionService {
|
|||||||
this.throwErrorCashflowTranscationNotFound(cashflowTransaction);
|
this.throwErrorCashflowTranscationNotFound(cashflowTransaction);
|
||||||
|
|
||||||
// Transformes the cashflow transaction model to POJO.
|
// Transformes the cashflow transaction model to POJO.
|
||||||
return this.transfromer.transform(
|
return this.transformer.transform(
|
||||||
tenantId,
|
tenantId,
|
||||||
cashflowTransaction,
|
cashflowTransaction,
|
||||||
new CashflowTransactionTransformer()
|
new CashflowTransactionTransformer()
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { CreditNoteBrandingTemplate } from './CreditNoteBrandingTemplate';
|
|||||||
import { CreditNotePdfTemplateAttributes } from '@/interfaces';
|
import { CreditNotePdfTemplateAttributes } from '@/interfaces';
|
||||||
import HasTenancyService from '../Tenancy/TenancyService';
|
import HasTenancyService from '../Tenancy/TenancyService';
|
||||||
import { transformCreditNoteToPdfTemplate } from './utils';
|
import { transformCreditNoteToPdfTemplate } from './utils';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class GetCreditNotePdf {
|
export default class GetCreditNotePdf {
|
||||||
@@ -24,12 +26,19 @@ export default class GetCreditNotePdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private creditNoteBrandingTemplate: CreditNoteBrandingTemplate;
|
private creditNoteBrandingTemplate: CreditNoteBrandingTemplate;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 +48,37 @@ 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
|
||||||
|
);
|
||||||
|
const eventPayload = { tenantId, creditNoteId };
|
||||||
|
|
||||||
|
// Triggers the `onCreditNotePdfViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.creditNote.onPdfViewed,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
|
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}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
PAYMENT_RECEIVED_CREATED,
|
PAYMENT_RECEIVED_CREATED,
|
||||||
PAYMENT_RECEIVED_EDITED,
|
PAYMENT_RECEIVED_EDITED,
|
||||||
PAYMENT_RECEIVED_DELETED,
|
PAYMENT_RECEIVED_DELETED,
|
||||||
|
PAYMENT_RECEIVED_PDF_VIEWED,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -34,6 +35,10 @@ export class PaymentReceivedEventsTracker extends EventSubscriber {
|
|||||||
events.paymentReceive.onDeleted,
|
events.paymentReceive.onDeleted,
|
||||||
this.handleTrackDeletedPaymentReceivedEvent
|
this.handleTrackDeletedPaymentReceivedEvent
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.paymentReceive.onPdfViewed,
|
||||||
|
this.handleTrackPdfViewedPaymentReceivedEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTrackPaymentReceivedCreatedEvent = ({
|
private handleTrackPaymentReceivedCreatedEvent = ({
|
||||||
@@ -65,4 +70,14 @@ export class PaymentReceivedEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleTrackPdfViewedPaymentReceivedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: IPaymentReceivedDeletedPayload) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: PAYMENT_RECEIVED_PDF_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
SALE_ESTIMATE_CREATED,
|
SALE_ESTIMATE_CREATED,
|
||||||
SALE_ESTIMATE_EDITED,
|
SALE_ESTIMATE_EDITED,
|
||||||
SALE_ESTIMATE_DELETED,
|
SALE_ESTIMATE_DELETED,
|
||||||
|
SALE_ESTIMATE_PDF_VIEWED,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -34,6 +35,10 @@ export class SaleEstimateEventsTracker extends EventSubscriber {
|
|||||||
events.saleEstimate.onDeleted,
|
events.saleEstimate.onDeleted,
|
||||||
this.handleTrackDeletedEstimateEvent
|
this.handleTrackDeletedEstimateEvent
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleEstimate.onPdfViewed,
|
||||||
|
this.handleTrackPdfViewedEstimateEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTrackEstimateCreatedEvent = ({
|
private handleTrackEstimateCreatedEvent = ({
|
||||||
@@ -65,4 +70,14 @@ export class SaleEstimateEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleTrackPdfViewedEstimateEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ISaleEstimateDeletedPayload) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: SALE_ESTIMATE_PDF_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
SALE_INVOICE_CREATED,
|
SALE_INVOICE_CREATED,
|
||||||
SALE_INVOICE_DELETED,
|
SALE_INVOICE_DELETED,
|
||||||
SALE_INVOICE_EDITED,
|
SALE_INVOICE_EDITED,
|
||||||
|
SALE_INVOICE_PDF_VIEWED,
|
||||||
SALE_INVOICE_VIEWED,
|
SALE_INVOICE_VIEWED,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
@@ -38,6 +39,10 @@ export class SaleInvoiceEventsTracker extends EventSubscriber {
|
|||||||
events.saleInvoice.onViewed,
|
events.saleInvoice.onViewed,
|
||||||
this.handleTrackViewedInvoiceEvent
|
this.handleTrackViewedInvoiceEvent
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleInvoice.onPdfViewed,
|
||||||
|
this.handleTrackPdfViewedInvoiceEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTrackInvoiceCreatedEvent = ({
|
private handleTrackInvoiceCreatedEvent = ({
|
||||||
@@ -77,4 +82,12 @@ export class SaleInvoiceEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleTrackPdfViewedInvoiceEvent = ({ tenantId }) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: SALE_INVOICE_PDF_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ export const valueParser =
|
|||||||
// Parses the enumeration value.
|
// Parses the enumeration value.
|
||||||
} else if (field.fieldType === 'enumeration') {
|
} else if (field.fieldType === 'enumeration') {
|
||||||
const option = get(field, 'options', []).find(
|
const option = get(field, 'options', []).find(
|
||||||
(option) => option.label === value
|
(option) => option.label?.toLowerCase() === value?.toLowerCase()
|
||||||
);
|
);
|
||||||
_value = get(option, 'key');
|
_value = get(option, 'key');
|
||||||
// Parses the numeric value.
|
// Parses the numeric value.
|
||||||
@@ -433,8 +433,8 @@ export const getMapToPath = (to: string, group = '') =>
|
|||||||
group ? `${group}.${to}` : to;
|
group ? `${group}.${to}` : to;
|
||||||
|
|
||||||
export const getImportsStoragePath = () => {
|
export const getImportsStoragePath = () => {
|
||||||
return path.join(global.__storage_dir, `/imports`);
|
return path.join(global.__storage_dir, `/imports`);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the imported file from the storage and database.
|
* Deletes the imported file from the storage and database.
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|||||||
import { SaleEstimatePdfTemplate } from '../Invoices/SaleEstimatePdfTemplate';
|
import { SaleEstimatePdfTemplate } from '../Invoices/SaleEstimatePdfTemplate';
|
||||||
import { transformEstimateToPdfTemplate } from './utils';
|
import { transformEstimateToPdfTemplate } from './utils';
|
||||||
import { EstimatePdfBrandingAttributes } from './constants';
|
import { EstimatePdfBrandingAttributes } from './constants';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleEstimatesPdf {
|
export class SaleEstimatesPdf {
|
||||||
@@ -24,12 +26,22 @@ export class SaleEstimatesPdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private estimatePdfTemplate: SaleEstimatePdfTemplate;
|
private estimatePdfTemplate: SaleEstimatePdfTemplate;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice pdf content.
|
* Retrieve sale invoice pdf content.
|
||||||
* @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 +51,32 @@ 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
|
||||||
|
);
|
||||||
|
const eventPayload = { tenantId, saleEstimateId };
|
||||||
|
|
||||||
|
// Triggers the `onSaleEstimatePdfViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleEstimate.onPdfViewed,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
|
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}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const transformEstimateToPdfTemplate = (
|
|||||||
})),
|
})),
|
||||||
total: estimate.formattedSubtotal,
|
total: estimate.formattedSubtotal,
|
||||||
subtotal: estimate.formattedSubtotal,
|
subtotal: estimate.formattedSubtotal,
|
||||||
customerNote: estimate.customerNote,
|
customerNote: estimate.note,
|
||||||
termsConditions: estimate.termsConditions,
|
termsConditions: estimate.termsConditions,
|
||||||
customerAddress: contactAddressTextFormat(estimate.customer),
|
customerAddress: contactAddressTextFormat(estimate.customer),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|||||||
import { transformInvoiceToPdfTemplate } from './utils';
|
import { transformInvoiceToPdfTemplate } from './utils';
|
||||||
import { InvoicePdfTemplateAttributes } from '@/interfaces';
|
import { InvoicePdfTemplateAttributes } from '@/interfaces';
|
||||||
import { SaleInvoicePdfTemplate } from './SaleInvoicePdfTemplate';
|
import { SaleInvoicePdfTemplate } from './SaleInvoicePdfTemplate';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleInvoicePdf {
|
export class SaleInvoicePdf {
|
||||||
@@ -24,6 +26,9 @@ export class SaleInvoicePdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private invoiceBrandingTemplateService: SaleInvoicePdfTemplate;
|
private invoiceBrandingTemplateService: SaleInvoicePdfTemplate;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice pdf content.
|
* Retrieve sale invoice pdf content.
|
||||||
* @param {number} tenantId - Tenant Id.
|
* @param {number} tenantId - Tenant Id.
|
||||||
@@ -33,7 +38,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 +51,35 @@ 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
|
||||||
|
);
|
||||||
|
const eventPayload = { tenantId, saleInvoiceId: invoiceId };
|
||||||
|
|
||||||
|
// Triggers the `onSaleInvoicePdfViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleInvoice.onPdfViewed,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
|
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}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ export const defaultInvoicePdfTemplateAttributes = {
|
|||||||
|
|
||||||
// Entries
|
// Entries
|
||||||
lineItemLabel: 'Item',
|
lineItemLabel: 'Item',
|
||||||
lineDescriptionLabel: 'Description',
|
lineQuantityLabel: 'Qty',
|
||||||
lineRateLabel: 'Rate',
|
lineRateLabel: 'Rate',
|
||||||
lineTotalLabel: 'Total',
|
lineTotalLabel: 'Total',
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|||||||
import { PaymentReceivedBrandingTemplate } from './PaymentReceivedBrandingTemplate';
|
import { PaymentReceivedBrandingTemplate } from './PaymentReceivedBrandingTemplate';
|
||||||
import { transformPaymentReceivedToPdfTemplate } from './utils';
|
import { transformPaymentReceivedToPdfTemplate } from './utils';
|
||||||
import { PaymentReceivedPdfTemplateAttributes } from '@/interfaces';
|
import { PaymentReceivedPdfTemplateAttributes } from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class GetPaymentReceivedPdf {
|
export default class GetPaymentReceivedPdf {
|
||||||
@@ -24,6 +26,9 @@ export default class GetPaymentReceivedPdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private paymentBrandingTemplateService: PaymentReceivedBrandingTemplate;
|
private paymentBrandingTemplateService: PaymentReceivedBrandingTemplate;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice pdf content.
|
* Retrieve sale invoice pdf content.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
@@ -32,19 +37,51 @@ export default class GetPaymentReceivedPdf {
|
|||||||
*/
|
*/
|
||||||
async getPaymentReceivePdf(
|
async getPaymentReceivePdf(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
paymentReceiveId: number
|
paymentReceivedId: number
|
||||||
): Promise<Buffer> {
|
): Promise<[Buffer, string]> {
|
||||||
const brandingAttributes = await this.getPaymentBrandingAttributes(
|
const brandingAttributes = await this.getPaymentBrandingAttributes(
|
||||||
tenantId,
|
tenantId,
|
||||||
paymentReceiveId
|
paymentReceivedId
|
||||||
);
|
);
|
||||||
const htmlContent = await this.templateInjectable.render(
|
const htmlContent = await this.templateInjectable.render(
|
||||||
tenantId,
|
tenantId,
|
||||||
'modules/payment-receive-standard',
|
'modules/payment-receive-standard',
|
||||||
brandingAttributes
|
brandingAttributes
|
||||||
);
|
);
|
||||||
|
const filename = await this.getPaymentReceivedFilename(
|
||||||
|
tenantId,
|
||||||
|
paymentReceivedId
|
||||||
|
);
|
||||||
// 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
|
||||||
|
);
|
||||||
|
const eventPayload = { tenantId, paymentReceivedId };
|
||||||
|
|
||||||
|
// Triggers the `onCreditNotePdfViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.paymentReceive.onPdfViewed,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
|
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}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|||||||
import { SaleReceiptBrandingTemplate } from './SaleReceiptBrandingTemplate';
|
import { SaleReceiptBrandingTemplate } from './SaleReceiptBrandingTemplate';
|
||||||
import { transformReceiptToBrandingTemplateAttributes } from './utils';
|
import { transformReceiptToBrandingTemplateAttributes } from './utils';
|
||||||
import { ISaleReceiptBrandingTemplateAttributes } from '@/interfaces';
|
import { ISaleReceiptBrandingTemplateAttributes } from '@/interfaces';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleReceiptsPdf {
|
export class SaleReceiptsPdf {
|
||||||
@@ -24,6 +26,9 @@ export class SaleReceiptsPdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private saleReceiptBrandingTemplate: SaleReceiptBrandingTemplate;
|
private saleReceiptBrandingTemplate: SaleReceiptBrandingTemplate;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves sale invoice pdf content.
|
* Retrieves sale invoice pdf content.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
@@ -31,6 +36,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 +49,35 @@ 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
|
||||||
|
);
|
||||||
|
const eventPayload = { tenantId, saleReceiptId };
|
||||||
|
|
||||||
|
// Triggers the `onSaleReceiptPdfViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleReceipt.onPdfViewed,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
|
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}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -133,6 +133,8 @@ export default {
|
|||||||
onViewed: 'onSaleInvoiceItemViewed',
|
onViewed: 'onSaleInvoiceItemViewed',
|
||||||
onListViewed: 'onSaleInvoiceListViewed',
|
onListViewed: 'onSaleInvoiceListViewed',
|
||||||
|
|
||||||
|
onPdfViewed: 'onSaleInvoicePdfViewed',
|
||||||
|
|
||||||
onCreate: 'onSaleInvoiceCreate',
|
onCreate: 'onSaleInvoiceCreate',
|
||||||
onCreating: 'onSaleInvoiceCreating',
|
onCreating: 'onSaleInvoiceCreating',
|
||||||
onCreated: 'onSaleInvoiceCreated',
|
onCreated: 'onSaleInvoiceCreated',
|
||||||
@@ -178,6 +180,8 @@ export default {
|
|||||||
* Sales estimates service.
|
* Sales estimates service.
|
||||||
*/
|
*/
|
||||||
saleEstimate: {
|
saleEstimate: {
|
||||||
|
onPdfViewed: 'onSaleEstimatePdfViewed',
|
||||||
|
|
||||||
onCreating: 'onSaleEstimateCreating',
|
onCreating: 'onSaleEstimateCreating',
|
||||||
onCreated: 'onSaleEstimateCreated',
|
onCreated: 'onSaleEstimateCreated',
|
||||||
|
|
||||||
@@ -215,6 +219,8 @@ export default {
|
|||||||
* Sales receipts service.
|
* Sales receipts service.
|
||||||
*/
|
*/
|
||||||
saleReceipt: {
|
saleReceipt: {
|
||||||
|
onPdfViewed: 'onSaleReceiptPdfViewed',
|
||||||
|
|
||||||
onCreating: 'onSaleReceiptsCreating',
|
onCreating: 'onSaleReceiptsCreating',
|
||||||
onCreated: 'onSaleReceiptsCreated',
|
onCreated: 'onSaleReceiptsCreated',
|
||||||
|
|
||||||
@@ -242,6 +248,8 @@ export default {
|
|||||||
* Payment receipts service.
|
* Payment receipts service.
|
||||||
*/
|
*/
|
||||||
paymentReceive: {
|
paymentReceive: {
|
||||||
|
onPdfViewed: 'onPaymentReceivedPdfViewed',
|
||||||
|
|
||||||
onCreated: 'onPaymentReceiveCreated',
|
onCreated: 'onPaymentReceiveCreated',
|
||||||
onCreating: 'onPaymentReceiveCreating',
|
onCreating: 'onPaymentReceiveCreating',
|
||||||
|
|
||||||
@@ -345,7 +353,7 @@ export default {
|
|||||||
*/
|
*/
|
||||||
item: {
|
item: {
|
||||||
onViewed: 'onItemViewed',
|
onViewed: 'onItemViewed',
|
||||||
|
|
||||||
onCreated: 'onItemCreated',
|
onCreated: 'onItemCreated',
|
||||||
onCreating: 'onItemCreating',
|
onCreating: 'onItemCreating',
|
||||||
|
|
||||||
@@ -464,6 +472,8 @@ export default {
|
|||||||
* Credit note service.
|
* Credit note service.
|
||||||
*/
|
*/
|
||||||
creditNote: {
|
creditNote: {
|
||||||
|
onPdfViewed: 'onCreditNotePdfViewed',
|
||||||
|
|
||||||
onCreate: 'onCreditNoteCreate',
|
onCreate: 'onCreditNoteCreate',
|
||||||
onCreating: 'onCreditNoteCreating',
|
onCreating: 'onCreditNoteCreating',
|
||||||
onCreated: 'onCreditNoteCreated',
|
onCreated: 'onCreditNoteCreated',
|
||||||
@@ -722,7 +732,7 @@ export default {
|
|||||||
// Payment methods integrations
|
// Payment methods integrations
|
||||||
paymentIntegrationLink: {
|
paymentIntegrationLink: {
|
||||||
onPaymentIntegrationLink: 'onPaymentIntegrationLink',
|
onPaymentIntegrationLink: 'onPaymentIntegrationLink',
|
||||||
onPaymentIntegrationDeleteLink: 'onPaymentIntegrationDeleteLink'
|
onPaymentIntegrationDeleteLink: 'onPaymentIntegrationDeleteLink',
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stripe Payment Integration
|
// Stripe Payment Integration
|
||||||
@@ -739,6 +749,6 @@ export default {
|
|||||||
// Stripe Payment Webhooks
|
// Stripe Payment Webhooks
|
||||||
stripeWebhooks: {
|
stripeWebhooks: {
|
||||||
onCheckoutSessionCompleted: 'onStripeCheckoutSessionCompleted',
|
onCheckoutSessionCompleted: 'onStripeCheckoutSessionCompleted',
|
||||||
onAccountUpdated: 'onStripeAccountUpdated'
|
onAccountUpdated: 'onStripeAccountUpdated',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ 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>
|
||||||
<div class="dialog__header-actions">
|
<div class="dialog__header-actions">
|
||||||
@@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Classes, Text } from '@blueprintjs/core';
|
||||||
import { Box, Group, Stack } from '@/components';
|
import { Box, Group, Stack } from '@/components';
|
||||||
import {
|
import {
|
||||||
PaperTemplate,
|
PaperTemplate,
|
||||||
@@ -67,6 +68,12 @@ export interface CreditNotePaperTemplateProps extends PaperTemplateProps {
|
|||||||
creditNoteNumebr?: string;
|
creditNoteNumebr?: string;
|
||||||
creditNoteNumberLabel?: string;
|
creditNoteNumberLabel?: string;
|
||||||
showCreditNoteNumber?: boolean;
|
showCreditNoteNumber?: boolean;
|
||||||
|
|
||||||
|
// Entries
|
||||||
|
lineItemLabel?: string;
|
||||||
|
lineQuantityLabel?: string;
|
||||||
|
lineRateLabel?: string;
|
||||||
|
lineTotalLabel?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CreditNotePaperTemplate({
|
export function CreditNotePaperTemplate({
|
||||||
@@ -127,6 +134,12 @@ export function CreditNotePaperTemplate({
|
|||||||
creditNoteDate = 'September 3, 2024',
|
creditNoteDate = 'September 3, 2024',
|
||||||
showCreditNoteDate = true,
|
showCreditNoteDate = true,
|
||||||
creditNoteDateLabel = 'Credit Note Date',
|
creditNoteDateLabel = 'Credit Note Date',
|
||||||
|
|
||||||
|
// Entries
|
||||||
|
lineItemLabel = 'Item',
|
||||||
|
lineQuantityLabel = 'Qty',
|
||||||
|
lineRateLabel = 'Rate',
|
||||||
|
lineTotalLabel = 'Total',
|
||||||
}: CreditNotePaperTemplateProps) {
|
}: CreditNotePaperTemplateProps) {
|
||||||
return (
|
return (
|
||||||
<PaperTemplate primaryColor={primaryColor} secondaryColor={secondaryColor}>
|
<PaperTemplate primaryColor={primaryColor} secondaryColor={secondaryColor}>
|
||||||
@@ -172,10 +185,23 @@ export function CreditNotePaperTemplate({
|
|||||||
<Stack spacing={0}>
|
<Stack spacing={0}>
|
||||||
<PaperTemplate.Table
|
<PaperTemplate.Table
|
||||||
columns={[
|
columns={[
|
||||||
{ label: 'Item', accessor: 'item' },
|
{
|
||||||
{ label: 'Description', accessor: 'description' },
|
label: lineItemLabel,
|
||||||
{ label: 'Rate', accessor: 'rate', align: 'right' },
|
accessor: (data) => (
|
||||||
{ label: 'Total', accessor: 'total', align: 'right' },
|
<Stack spacing={2}>
|
||||||
|
<Text>{data.item}</Text>
|
||||||
|
<Text
|
||||||
|
className={Classes.TEXT_MUTED}
|
||||||
|
style={{ fontSize: 12 }}
|
||||||
|
>
|
||||||
|
{data.description}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ label: lineQuantityLabel, accessor: 'quantity' },
|
||||||
|
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
||||||
|
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
||||||
]}
|
]}
|
||||||
data={lines}
|
data={lines}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Classes, Text } from '@blueprintjs/core';
|
||||||
import { Box, Group, Stack } from '@/components';
|
import { Box, Group, Stack } from '@/components';
|
||||||
import {
|
import {
|
||||||
PaperTemplate,
|
PaperTemplate,
|
||||||
@@ -69,6 +70,13 @@ export interface EstimatePaperTemplateProps extends PaperTemplateProps {
|
|||||||
quantity: string;
|
quantity: string;
|
||||||
total: string;
|
total: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
// Lines
|
||||||
|
lineItemLabel?: string,
|
||||||
|
lineQuantityLabel?: string,
|
||||||
|
lineRateLabel?: string,
|
||||||
|
lineTotalLabel?: string,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function EstimatePaperTemplate({
|
export function EstimatePaperTemplate({
|
||||||
@@ -134,6 +142,13 @@ export function EstimatePaperTemplate({
|
|||||||
expirationDateLabel = 'Expiration Date',
|
expirationDateLabel = 'Expiration Date',
|
||||||
showExpirationDate = true,
|
showExpirationDate = true,
|
||||||
expirationDate = 'September 3, 2024',
|
expirationDate = 'September 3, 2024',
|
||||||
|
|
||||||
|
// Entries
|
||||||
|
lineItemLabel = 'Item',
|
||||||
|
lineQuantityLabel = 'Qty',
|
||||||
|
lineRateLabel = 'Rate',
|
||||||
|
lineTotalLabel = 'Total',
|
||||||
|
|
||||||
}: EstimatePaperTemplateProps) {
|
}: EstimatePaperTemplateProps) {
|
||||||
return (
|
return (
|
||||||
<PaperTemplate primaryColor={primaryColor} secondaryColor={secondaryColor}>
|
<PaperTemplate primaryColor={primaryColor} secondaryColor={secondaryColor}>
|
||||||
@@ -183,10 +198,23 @@ export function EstimatePaperTemplate({
|
|||||||
<Stack spacing={0}>
|
<Stack spacing={0}>
|
||||||
<PaperTemplate.Table
|
<PaperTemplate.Table
|
||||||
columns={[
|
columns={[
|
||||||
{ label: 'Item', accessor: 'item' },
|
{
|
||||||
{ label: 'Description', accessor: 'description' },
|
label: lineItemLabel,
|
||||||
{ label: 'Rate', accessor: 'rate', align: 'right' },
|
accessor: (data) => (
|
||||||
{ label: 'Total', accessor: 'total', align: 'right' },
|
<Stack spacing={2}>
|
||||||
|
<Text>{data.item}</Text>
|
||||||
|
<Text
|
||||||
|
className={Classes.TEXT_MUTED}
|
||||||
|
style={{ fontSize: 12 }}
|
||||||
|
>
|
||||||
|
{data.description}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ label: lineQuantityLabel, accessor: 'quantity' },
|
||||||
|
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
||||||
|
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
||||||
]}
|
]}
|
||||||
data={lines}
|
data={lines}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import { Classes, Text } from '@blueprintjs/core';
|
||||||
import { PaperTemplate, PaperTemplateTotalBorder } from './PaperTemplate';
|
import { PaperTemplate, PaperTemplateTotalBorder } from './PaperTemplate';
|
||||||
import { Box, Group, Stack } from '@/components';
|
import { Box, Group, Stack } from '@/components';
|
||||||
import {
|
import {
|
||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
DefaultPdfTemplateAddressBilledTo,
|
DefaultPdfTemplateAddressBilledTo,
|
||||||
DefaultPdfTemplateAddressBilledFrom,
|
DefaultPdfTemplateAddressBilledFrom,
|
||||||
} from '@/constants/PdfTemplates';
|
} from '@/constants/PdfTemplates';
|
||||||
|
|
||||||
interface PapaerLine {
|
interface PapaerLine {
|
||||||
item?: string;
|
item?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
@@ -55,7 +56,7 @@ export interface InvoicePaperTemplateProps {
|
|||||||
|
|
||||||
// Entries
|
// Entries
|
||||||
lineItemLabel?: string;
|
lineItemLabel?: string;
|
||||||
lineDescriptionLabel?: string;
|
lineQuantityLabel?: string;
|
||||||
lineRateLabel?: string;
|
lineRateLabel?: string;
|
||||||
lineTotalLabel?: string;
|
lineTotalLabel?: string;
|
||||||
|
|
||||||
@@ -129,7 +130,7 @@ export function InvoicePaperTemplate({
|
|||||||
|
|
||||||
// Entries
|
// Entries
|
||||||
lineItemLabel = 'Item',
|
lineItemLabel = 'Item',
|
||||||
lineDescriptionLabel = 'Description',
|
lineQuantityLabel = 'Qty',
|
||||||
lineRateLabel = 'Rate',
|
lineRateLabel = 'Rate',
|
||||||
lineTotalLabel = 'Total',
|
lineTotalLabel = 'Total',
|
||||||
|
|
||||||
@@ -214,7 +215,6 @@ export function InvoicePaperTemplate({
|
|||||||
<Box dangerouslySetInnerHTML={{ __html: companyAddress }} />
|
<Box dangerouslySetInnerHTML={{ __html: companyAddress }} />
|
||||||
</PaperTemplate.Address>
|
</PaperTemplate.Address>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showCustomerAddress && (
|
{showCustomerAddress && (
|
||||||
<PaperTemplate.Address>
|
<PaperTemplate.Address>
|
||||||
<strong>{billedToLabel}</strong>
|
<strong>{billedToLabel}</strong>
|
||||||
@@ -226,8 +226,21 @@ export function InvoicePaperTemplate({
|
|||||||
<Stack spacing={0}>
|
<Stack spacing={0}>
|
||||||
<PaperTemplate.Table
|
<PaperTemplate.Table
|
||||||
columns={[
|
columns={[
|
||||||
{ label: lineItemLabel, accessor: 'item' },
|
{
|
||||||
{ label: lineDescriptionLabel, accessor: 'description' },
|
label: lineItemLabel,
|
||||||
|
accessor: (data) => (
|
||||||
|
<Stack spacing={2}>
|
||||||
|
<Text>{data.item}</Text>
|
||||||
|
<Text
|
||||||
|
className={Classes.TEXT_MUTED}
|
||||||
|
style={{ fontSize: 12 }}
|
||||||
|
>
|
||||||
|
{data.description}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ label: lineQuantityLabel, accessor: 'quantity' },
|
||||||
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
||||||
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'classnames';
|
import clsx from 'classnames';
|
||||||
import { get } from 'lodash';
|
import { get, isFunction } from 'lodash';
|
||||||
import { Box, Group, GroupProps } from '@/components';
|
import { Box, Group, GroupProps } from '@/components';
|
||||||
import styles from './InvoicePaperTemplate.module.scss';
|
import styles from './InvoicePaperTemplate.module.scss';
|
||||||
|
|
||||||
@@ -26,8 +26,9 @@ export function PaperTemplate({
|
|||||||
|
|
||||||
interface PaperTemplateTableProps {
|
interface PaperTemplateTableProps {
|
||||||
columns: Array<{
|
columns: Array<{
|
||||||
accessor: string;
|
accessor: string | ((data: Record<string, any>) => JSX.Element);
|
||||||
label: string;
|
label: string;
|
||||||
|
value?: JSX.Element;
|
||||||
align?: 'left' | 'center' | 'right';
|
align?: 'left' | 'center' | 'right';
|
||||||
}>;
|
}>;
|
||||||
data: Array<Record<string, any>>;
|
data: Array<Record<string, any>>;
|
||||||
@@ -71,7 +72,9 @@ PaperTemplate.Table = ({ columns, data }: PaperTemplateTableProps) => {
|
|||||||
<tr>
|
<tr>
|
||||||
{columns.map((column, index) => (
|
{columns.map((column, index) => (
|
||||||
<td align={column.align} key={index}>
|
<td align={column.align} key={index}>
|
||||||
{get(_data, column.accessor)}
|
{isFunction(column?.accessor)
|
||||||
|
? column?.accessor(_data)
|
||||||
|
: get(_data, column.accessor)}
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
@@ -115,9 +118,9 @@ PaperTemplate.TotalLine = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
PaperTemplate.MutedText = () => {};
|
PaperTemplate.MutedText = () => { };
|
||||||
|
|
||||||
PaperTemplate.Text = () => {};
|
PaperTemplate.Text = () => { };
|
||||||
|
|
||||||
PaperTemplate.AddressesGroup = (props: GroupProps) => {
|
PaperTemplate.AddressesGroup = (props: GroupProps) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Classes, Text } from '@blueprintjs/core';
|
||||||
import { Box, Group, Stack } from '@/components';
|
import { Box, Group, Stack } from '@/components';
|
||||||
import {
|
import {
|
||||||
PaperTemplate,
|
PaperTemplate,
|
||||||
@@ -67,6 +68,12 @@ export interface ReceiptPaperTemplateProps extends PaperTemplateProps {
|
|||||||
receiptNumebr?: string;
|
receiptNumebr?: string;
|
||||||
receiptNumberLabel?: string;
|
receiptNumberLabel?: string;
|
||||||
showReceiptNumber?: boolean;
|
showReceiptNumber?: boolean;
|
||||||
|
|
||||||
|
// Entries
|
||||||
|
lineItemLabel?: string;
|
||||||
|
lineQuantityLabel?: string;
|
||||||
|
lineRateLabel?: string;
|
||||||
|
lineTotalLabel?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReceiptPaperTemplate({
|
export function ReceiptPaperTemplate({
|
||||||
@@ -115,13 +122,22 @@ export function ReceiptPaperTemplate({
|
|||||||
total: '$1000.00',
|
total: '$1000.00',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// Receipt Number
|
||||||
showReceiptNumber = true,
|
showReceiptNumber = true,
|
||||||
receiptNumberLabel = 'Receipt Number',
|
receiptNumberLabel = 'Receipt Number',
|
||||||
receiptNumebr = '346D3D40-0001',
|
receiptNumebr = '346D3D40-0001',
|
||||||
|
|
||||||
|
// Receipt Date
|
||||||
receiptDate = 'September 3, 2024',
|
receiptDate = 'September 3, 2024',
|
||||||
showReceiptDate = true,
|
showReceiptDate = true,
|
||||||
receiptDateLabel = 'Receipt Date',
|
receiptDateLabel = 'Receipt Date',
|
||||||
|
|
||||||
|
// Entries
|
||||||
|
lineItemLabel = 'Item',
|
||||||
|
lineQuantityLabel = 'Qty',
|
||||||
|
lineRateLabel = 'Rate',
|
||||||
|
lineTotalLabel = 'Total',
|
||||||
}: ReceiptPaperTemplateProps) {
|
}: ReceiptPaperTemplateProps) {
|
||||||
return (
|
return (
|
||||||
<PaperTemplate primaryColor={primaryColor} secondaryColor={secondaryColor}>
|
<PaperTemplate primaryColor={primaryColor} secondaryColor={secondaryColor}>
|
||||||
@@ -167,10 +183,23 @@ export function ReceiptPaperTemplate({
|
|||||||
<Stack spacing={0}>
|
<Stack spacing={0}>
|
||||||
<PaperTemplate.Table
|
<PaperTemplate.Table
|
||||||
columns={[
|
columns={[
|
||||||
{ label: 'Item', accessor: 'item' },
|
{
|
||||||
{ label: 'Description', accessor: 'description' },
|
label: lineItemLabel,
|
||||||
{ label: 'Rate', accessor: 'rate', align: 'right' },
|
accessor: (data) => (
|
||||||
{ label: 'Total', accessor: 'total', align: 'right' },
|
<Stack spacing={2}>
|
||||||
|
<Text>{data.item}</Text>
|
||||||
|
<Text
|
||||||
|
className={Classes.TEXT_MUTED}
|
||||||
|
style={{ fontSize: 12 }}
|
||||||
|
>
|
||||||
|
{data.description}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ label: lineQuantityLabel, accessor: 'quantity' },
|
||||||
|
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
||||||
|
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
||||||
]}
|
]}
|
||||||
data={lines}
|
data={lines}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user