mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-24 08:39:49 +00:00
Compare commits
61 Commits
invoice-ma
...
fix-renew-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e293bbae8c | ||
|
|
d668d60ed5 | ||
|
|
a34b7a2106 | ||
|
|
6f12127095 | ||
|
|
b4e5bbf376 | ||
|
|
a11530d190 | ||
|
|
43d4425da5 | ||
|
|
4c1909cb73 | ||
|
|
a7b6b7a03e | ||
|
|
1f46275bde | ||
|
|
aa7e5d4ae9 | ||
|
|
bb482df3ce | ||
|
|
f878786646 | ||
|
|
652851a1a9 | ||
|
|
850f4956cb | ||
|
|
94223b6ebf | ||
|
|
e9d34e19ad | ||
|
|
107532fe26 | ||
|
|
c32aff82ee | ||
|
|
de8a867d33 | ||
|
|
17a8aba23f | ||
|
|
04b601626b | ||
|
|
802775c118 | ||
|
|
b6baa80134 | ||
|
|
b2d0f2ed3c | ||
|
|
d23f33bae4 | ||
|
|
22ea557337 | ||
|
|
b3ebbb429c | ||
|
|
51218797af | ||
|
|
2d18a6573e | ||
|
|
2646ad5bc4 | ||
|
|
51aec8d8b3 | ||
|
|
638bd95d6f | ||
|
|
f2fcc3a649 | ||
|
|
48795748d8 | ||
|
|
6ba54a994a | ||
|
|
6687db4085 | ||
|
|
ba1d9b3f28 | ||
|
|
51905825fd | ||
|
|
bd5e33855a | ||
|
|
f7fbc0e31c | ||
|
|
cb06fa342c | ||
|
|
581229053a | ||
|
|
209da69b8f | ||
|
|
d09aebcebb | ||
|
|
0cc80bc179 | ||
|
|
79dcc592bc | ||
|
|
687111851a | ||
|
|
26088a71ee | ||
|
|
cadf6b81a0 | ||
|
|
b4d3ac2f96 | ||
|
|
bc8e440814 | ||
|
|
4c0f9a0aef | ||
|
|
bbc19df6b4 | ||
|
|
c8c2786893 | ||
|
|
d79f26f1b5 | ||
|
|
32ba6f9a6c | ||
|
|
65788e344a | ||
|
|
abc242d117 | ||
|
|
6dd4968327 | ||
|
|
d805703c08 |
@@ -159,6 +159,15 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "nklmantey",
|
||||||
|
"name": "Mantey",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/90279429?v=4",
|
||||||
|
"profile": "https://nklmantey.com/",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oleynikd"><img src="https://avatars.githubusercontent.com/u/3976868?v=4?s=100" width="100px;" alt="Denis"/><br /><sub><b>Denis</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aoleynikd" title="Bug reports">🐛</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oleynikd"><img src="https://avatars.githubusercontent.com/u/3976868?v=4?s=100" width="100px;" alt="Denis"/><br /><sub><b>Denis</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aoleynikd" title="Bug reports">🐛</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://myself.vercel.app/"><img src="https://avatars.githubusercontent.com/u/42431274?v=4?s=100" width="100px;" alt="Sachin Mittal"/><br /><sub><b>Sachin Mittal</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Amittalsam98" title="Bug reports">🐛</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://myself.vercel.app/"><img src="https://avatars.githubusercontent.com/u/42431274?v=4?s=100" width="100px;" alt="Sachin Mittal"/><br /><sub><b>Sachin Mittal</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Amittalsam98" title="Bug reports">🐛</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://www.camilooviedo.com/"><img src="https://avatars.githubusercontent.com/u/64604272?v=4?s=100" width="100px;" alt="Camilo Oviedo"/><br /><sub><b>Camilo Oviedo</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=Champetaman" title="Code">💻</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://www.camilooviedo.com/"><img src="https://avatars.githubusercontent.com/u/64604272?v=4?s=100" width="100px;" alt="Camilo Oviedo"/><br /><sub><b>Camilo Oviedo</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=Champetaman" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://nklmantey.com/"><img src="https://avatars.githubusercontent.com/u/90279429?v=4?s=100" width="100px;" alt="Mantey"/><br /><sub><b>Mantey</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Anklmantey" title="Bug reports">🐛</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "lerna run dev",
|
"dev": "lerna run dev",
|
||||||
"build": "lerna run build",
|
"build": "lerna run build",
|
||||||
"dev:webapp": "lerna run dev --scope \"@bigcapital/webapp\" --scope \"@bigcapital/utils\"",
|
"dev:webapp": "lerna run dev --scope \"@bigcapital/webapp\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\"",
|
||||||
"build:webapp": "lerna run build --scope \"@bigcapital/webapp\" --scope \"@bigcapital/utils\"",
|
"build:webapp": "lerna run build --scope \"@bigcapital/webapp\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\"",
|
||||||
"dev:server": "lerna run dev --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\"",
|
"dev:server": "lerna run dev --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\" --scope \"@bigcapital/email-components\"",
|
||||||
"build:server": "lerna run build --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\"",
|
"build:server": "lerna run build --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\" --scope \"@bigcapital/email-components\"",
|
||||||
"serve:server": "lerna run serve --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\"",
|
"serve:server": "lerna run serve --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\"",
|
||||||
"test:e2e": "playwright test",
|
"test:e2e": "playwright test",
|
||||||
"prepare": "husky install"
|
"prepare": "husky install"
|
||||||
|
|||||||
@@ -20,10 +20,11 @@
|
|||||||
"bigcapital": "./bin/bigcapital.js"
|
"bigcapital": "./bin/bigcapital.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.576.0",
|
|
||||||
"@aws-sdk/s3-request-presigner": "^3.583.0",
|
|
||||||
"@bigcapital/utils": "*",
|
"@bigcapital/utils": "*",
|
||||||
"@bigcapital/email-components": "*",
|
"@bigcapital/email-components": "*",
|
||||||
|
"@bigcapital/pdf-templates": "*",
|
||||||
|
"@aws-sdk/client-s3": "^3.576.0",
|
||||||
|
"@aws-sdk/s3-request-presigner": "^3.583.0",
|
||||||
"@casl/ability": "^5.4.3",
|
"@casl/ability": "^5.4.3",
|
||||||
"@hapi/boom": "^7.4.3",
|
"@hapi/boom": "^7.4.3",
|
||||||
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
||||||
|
|||||||
@@ -449,9 +449,10 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
const acceptType = accept.types([
|
const acceptType = accept.types([
|
||||||
ACCEPT_TYPE.APPLICATION_JSON,
|
ACCEPT_TYPE.APPLICATION_JSON,
|
||||||
ACCEPT_TYPE.APPLICATION_PDF,
|
ACCEPT_TYPE.APPLICATION_PDF,
|
||||||
|
ACCEPT_TYPE.APPLICATION_TEXT_HTML,
|
||||||
]);
|
]);
|
||||||
// Retrieves invoice in pdf format.
|
// Retrieves invoice in PDF format.
|
||||||
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
|
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||||
const [pdfContent, filename] =
|
const [pdfContent, filename] =
|
||||||
await this.saleInvoiceApplication.saleInvoicePdf(
|
await this.saleInvoiceApplication.saleInvoicePdf(
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -463,7 +464,13 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
'Content-Disposition': `attachment; filename="${filename}"`,
|
'Content-Disposition': `attachment; filename="${filename}"`,
|
||||||
});
|
});
|
||||||
res.send(pdfContent);
|
res.send(pdfContent);
|
||||||
// Retrieves invoice in json format.
|
// Retrieves invoice in html json format.
|
||||||
|
} else if (ACCEPT_TYPE.APPLICATION_TEXT_HTML === acceptType) {
|
||||||
|
const htmlContent = await this.saleInvoiceApplication.saleInvoiceHtml(
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceId
|
||||||
|
);
|
||||||
|
return res.status(200).send({ htmlContent });
|
||||||
} else {
|
} else {
|
||||||
const saleInvoice = await this.saleInvoiceApplication.getSaleInvoice(
|
const saleInvoice = await this.saleInvoiceApplication.getSaleInvoice(
|
||||||
tenantId,
|
tenantId,
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ export class ShareLinkController extends BaseController {
|
|||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.generateShareLink.bind(this))
|
asyncMiddleware(this.generateShareLink.bind(this))
|
||||||
);
|
);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +52,6 @@ export class ShareLinkController extends BaseController {
|
|||||||
const link = await this.generateShareLinkService.generatePaymentLink(
|
const link = await this.generateShareLinkService.generatePaymentLink(
|
||||||
tenantId,
|
tenantId,
|
||||||
transactionId,
|
transactionId,
|
||||||
transactionType,
|
|
||||||
publicity,
|
publicity,
|
||||||
expiryDate
|
expiryDate
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,15 +12,20 @@ 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 SALE_ESTIMATE_PDF_VIEWED = 'Sale estimate PDF viewed';
|
||||||
|
export const SALE_ESTIMATE_VIEWED = 'Sale estimate viewed';
|
||||||
|
export const SALE_ESTIMATE_MAIL_SENT = 'Sale estimate mail sent';
|
||||||
|
|
||||||
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 PAYMENT_RECEIVED_PDF_VIEWED = 'Payment received PDF viewed';
|
||||||
|
export const PAYMENT_RECEIVED_MAIL_SENT = 'Payment received mail sent';
|
||||||
|
|
||||||
export const SALE_RECEIPT_PDF_VIEWED = 'Sale credit PDF viewed';
|
export const SALE_RECEIPT_PDF_VIEWED = 'Sale credit PDF viewed';
|
||||||
|
export const SALE_RECEIPT_MAIL_SENT = 'Sale credit mail sent';
|
||||||
|
|
||||||
export const CREDIT_NOTE_PDF_VIEWED = 'Credit note PDF viewed';
|
export const CREDIT_NOTE_PDF_VIEWED = 'Credit note PDF viewed';
|
||||||
|
export const CREDIT_NOTE_MAIL_SENT = 'Credit note mail sent';
|
||||||
|
|
||||||
export const BILL_CREATED = 'Bill created';
|
export const BILL_CREATED = 'Bill created';
|
||||||
export const BILL_EDITED = 'Bill edited';
|
export const BILL_EDITED = 'Bill edited';
|
||||||
@@ -50,6 +55,8 @@ export const AUTH_RESET_PASSWORD = 'Auth reset password';
|
|||||||
export const SUBSCRIPTION_CANCELLED = 'Subscription cancelled';
|
export const SUBSCRIPTION_CANCELLED = 'Subscription cancelled';
|
||||||
export const SUBSCRIPTION_RESUMED = 'Subscription resumed';
|
export const SUBSCRIPTION_RESUMED = 'Subscription resumed';
|
||||||
export const SUBSCRIPTION_PLAN_CHANGED = 'Subscription plan changed';
|
export const SUBSCRIPTION_PLAN_CHANGED = 'Subscription plan changed';
|
||||||
|
export const SUBSCRIPTION_PAYMENT_SUCCEED = 'Subscription payment succeed';
|
||||||
|
export const SUBSCRIPTION_PAYMENT_FAILED = 'Subscription payment failed';
|
||||||
|
|
||||||
export const CUSTOMER_CREATED = 'Customer created';
|
export const CUSTOMER_CREATED = 'Customer created';
|
||||||
export const CUSTOMER_EDITED = 'Customer edited';
|
export const CUSTOMER_EDITED = 'Customer edited';
|
||||||
@@ -103,3 +110,21 @@ export const SALE_GROUP = 'Sale';
|
|||||||
export const PAYMENT_GROUP = 'Payment';
|
export const PAYMENT_GROUP = 'Payment';
|
||||||
export const BILL_GROUP = 'Bill';
|
export const BILL_GROUP = 'Bill';
|
||||||
export const EXPENSE_GROUP = 'Expense';
|
export const EXPENSE_GROUP = 'Expense';
|
||||||
|
|
||||||
|
// # Reports
|
||||||
|
export const BALANCE_SHEET_VIEWED = 'Balance sheet viewed';
|
||||||
|
export const TRIAL_BALANCE_SHEET_VIEWED = 'Trial balance sheet viewed';
|
||||||
|
export const PROFIT_LOSS_SHEET_VIEWED = 'Profit loss sheet viewed';
|
||||||
|
export const CASHFLOW_STATEMENT_VIEWED = 'Cashflow statement viewed';
|
||||||
|
export const GENERAL_LEDGER_VIEWED = 'General ledger viewed';
|
||||||
|
export const JOURNAL_VIEWED = 'Journal viewed';
|
||||||
|
export const RECEIVABLE_AGING_VIEWED = 'Receivable aging viewed';
|
||||||
|
export const PAYABLE_AGING_VIEWED = 'Payable aging viewed';
|
||||||
|
export const CUSTOMER_BALANCE_SUMMARY_VIEWED =
|
||||||
|
'Customer balance summary viewed';
|
||||||
|
export const VENDOR_BALANCE_SUMMARY_VIEWED = 'Vendor balance summary viewed';
|
||||||
|
export const INVENTORY_VALUATION_VIEWED = 'Inventory valuation viewed';
|
||||||
|
export const CUSTOMER_TRANSACTIONS_VIEWED = 'Customer transactions viewed';
|
||||||
|
export const VENDOR_TRANSACTIONS_VIEWED = 'Vendor transactions viewed';
|
||||||
|
export const SALES_BY_ITEM_VIEWED = 'Sales by item viewed';
|
||||||
|
export const PURCHASES_BY_ITEM_VIEWED = 'Purchases by item viewed';
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ export const ACCEPT_TYPE = {
|
|||||||
APPLICATION_JSON_TABLE: 'application/json+table',
|
APPLICATION_JSON_TABLE: 'application/json+table',
|
||||||
APPLICATION_XLSX: 'application/xlsx',
|
APPLICATION_XLSX: 'application/xlsx',
|
||||||
APPLICATION_CSV: 'application/csv',
|
APPLICATION_CSV: 'application/csv',
|
||||||
|
APPLICATION_TEXT_HTML: 'application/json+html',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,8 +36,9 @@ export interface CommonMailOptions {
|
|||||||
to: Array<string>;
|
to: Array<string>;
|
||||||
cc?: Array<string>;
|
cc?: Array<string>;
|
||||||
bcc?: Array<string>;
|
bcc?: Array<string>;
|
||||||
data?: Record<string, any>;
|
formatArgs?: Record<string, any>;
|
||||||
|
toOptions: Array<AddressItem>;
|
||||||
|
fromOptions: Array<AddressItem>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommonMailOptionsDTO extends Partial<CommonMailOptions> {
|
export interface CommonMailOptionsDTO extends Partial<CommonMailOptions> {}
|
||||||
}
|
|
||||||
|
|||||||
@@ -338,21 +338,22 @@ export interface InvoicePdfTemplateAttributes {
|
|||||||
subtotalLabel: string;
|
subtotalLabel: string;
|
||||||
discountLabel: string;
|
discountLabel: string;
|
||||||
paymentMadeLabel: string;
|
paymentMadeLabel: string;
|
||||||
balanceDueLabel: string;
|
|
||||||
|
|
||||||
showTotal: boolean;
|
showTotal: boolean;
|
||||||
showSubtotal: boolean;
|
showSubtotal: boolean;
|
||||||
showDiscount: boolean;
|
showDiscount: boolean;
|
||||||
showTaxes: boolean;
|
showTaxes: boolean;
|
||||||
showPaymentMade: boolean;
|
showPaymentMade: boolean;
|
||||||
showDueAmount: boolean;
|
|
||||||
showBalanceDue: boolean;
|
|
||||||
|
|
||||||
total: string;
|
total: string;
|
||||||
subtotal: string;
|
subtotal: string;
|
||||||
discount: string;
|
discount: string;
|
||||||
paymentMade: string;
|
paymentMade: string;
|
||||||
balanceDue: string;
|
|
||||||
|
// Due Amount
|
||||||
|
dueAmount: string;
|
||||||
|
showDueAmount: boolean;
|
||||||
|
dueAmountLabel: string;
|
||||||
|
|
||||||
termsConditionsLabel: string;
|
termsConditionsLabel: string;
|
||||||
showTermsConditions: boolean;
|
showTermsConditions: boolean;
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ export default class Mail {
|
|||||||
subject: string = '';
|
subject: string = '';
|
||||||
content: string = '';
|
content: string = '';
|
||||||
to: string | string[];
|
to: string | string[];
|
||||||
|
cc: string | string[];
|
||||||
|
bcc: string | string[];
|
||||||
|
replyTo: string | string[];
|
||||||
from: string = `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`;
|
from: string = `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`;
|
||||||
data: { [key: string]: string | number };
|
data: { [key: string]: string | number };
|
||||||
attachments: IMailAttachment[];
|
attachments: IMailAttachment[];
|
||||||
@@ -20,9 +23,12 @@ export default class Mail {
|
|||||||
return {
|
return {
|
||||||
to: this.to,
|
to: this.to,
|
||||||
from: this.from,
|
from: this.from,
|
||||||
|
cc: this.cc,
|
||||||
|
bcc: this.bcc,
|
||||||
subject: this.subject,
|
subject: this.subject,
|
||||||
html: this.html,
|
html: this.html,
|
||||||
attachments: this.attachments,
|
attachments: this.attachments,
|
||||||
|
replyTo: this.replyTo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +66,21 @@ export default class Mail {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCC(cc: string | string[]) {
|
||||||
|
this.cc = cc;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBCC(bcc: string | string[]) {
|
||||||
|
this.bcc = bcc;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setReplyTo(replyTo: string | string[]) {
|
||||||
|
this.replyTo = replyTo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets from address to the mail.
|
* Sets from address to the mail.
|
||||||
* @param {string} from
|
* @param {string} from
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import { getUploadedObjectUri } from '@/services/Attachments/utils';
|
||||||
import TenantModel from 'models/TenantModel';
|
import TenantModel from 'models/TenantModel';
|
||||||
|
|
||||||
export class PdfTemplate extends TenantModel {
|
export class PdfTemplate extends TenantModel {
|
||||||
|
public readonly attributes: Record<string, any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
@@ -47,7 +50,17 @@ export class PdfTemplate extends TenantModel {
|
|||||||
* Virtual attributes.
|
* Virtual attributes.
|
||||||
*/
|
*/
|
||||||
static get virtualAttributes() {
|
static get virtualAttributes() {
|
||||||
return [];
|
return ['companyLogoUri'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the company logo uri.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
get companyLogoUri() {
|
||||||
|
return this.attributes?.companyLogoKey
|
||||||
|
? getUploadedObjectUri(this.attributes.companyLogoKey)
|
||||||
|
: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import I18nService from '@/services/I18n/I18nService';
|
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { AccountTransformer } from './AccountTransform';
|
import { AccountTransformer } from './AccountTransform';
|
||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
@@ -11,9 +10,6 @@ export class GetAccount {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private tenancy: HasTenancyService;
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private i18nService: I18nService;
|
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private transformer: TransformerInjectable;
|
private transformer: TransformerInjectable;
|
||||||
|
|
||||||
@@ -44,10 +40,8 @@ export class GetAccount {
|
|||||||
new AccountTransformer(),
|
new AccountTransformer(),
|
||||||
{ accountsGraph }
|
{ accountsGraph }
|
||||||
);
|
);
|
||||||
const eventPayload = {
|
const eventPayload = { tenantId, accountId };
|
||||||
tenantId,
|
|
||||||
accountId,
|
|
||||||
};
|
|
||||||
// Triggers `onAccountViewed` event.
|
// Triggers `onAccountViewed` event.
|
||||||
await this.eventPublisher.emitAsync(events.accounts.onViewed, eventPayload);
|
await this.eventPublisher.emitAsync(events.accounts.onViewed, eventPayload);
|
||||||
|
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ export default class NewCashflowTransactionService {
|
|||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
* @param {ICashflowOwnerContributionDTO} ownerContributionDTO
|
* @param {ICashflowOwnerContributionDTO} ownerContributionDTO
|
||||||
* @param {number} userId - User id.
|
* @param {number} userId - User id.
|
||||||
|
* @returns {Promise<ICashflowTransaction>}
|
||||||
*/
|
*/
|
||||||
public newCashflowTransaction = async (
|
public newCashflowTransaction = async (
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
|
|||||||
@@ -35,9 +35,12 @@ export class CreditNoteBrandingTemplate {
|
|||||||
...defaultCreditNoteBrandingAttributes,
|
...defaultCreditNoteBrandingAttributes,
|
||||||
...commonOrgBrandingAttrs,
|
...commonOrgBrandingAttrs,
|
||||||
};
|
};
|
||||||
|
const brandingTemplateAttrs = {
|
||||||
|
...template.attributes,
|
||||||
|
companyLogoUri: template.companyLogoUri,
|
||||||
|
};
|
||||||
const attributes = mergePdfTemplateWithDefaultAttributes(
|
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||||
template.attributes,
|
brandingTemplateAttrs,
|
||||||
organizationBrandingAttrs
|
organizationBrandingAttrs
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import Knex from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { IRefundCreditNote } from '@/interfaces';
|
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class RefundSyncCreditNoteBalance {
|
export default class RefundSyncCreditNoteBalance {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
PAYMENT_RECEIVED_EDITED,
|
PAYMENT_RECEIVED_EDITED,
|
||||||
PAYMENT_RECEIVED_DELETED,
|
PAYMENT_RECEIVED_DELETED,
|
||||||
PAYMENT_RECEIVED_PDF_VIEWED,
|
PAYMENT_RECEIVED_PDF_VIEWED,
|
||||||
|
PAYMENT_RECEIVED_MAIL_SENT,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -39,6 +40,10 @@ export class PaymentReceivedEventsTracker extends EventSubscriber {
|
|||||||
events.paymentReceive.onPdfViewed,
|
events.paymentReceive.onPdfViewed,
|
||||||
this.handleTrackPdfViewedPaymentReceivedEvent
|
this.handleTrackPdfViewedPaymentReceivedEvent
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.paymentReceive.onMailSent,
|
||||||
|
this.handleTrackMailSentPaymentReceivedEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTrackPaymentReceivedCreatedEvent = ({
|
private handleTrackPaymentReceivedCreatedEvent = ({
|
||||||
@@ -80,4 +85,14 @@ export class PaymentReceivedEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleTrackMailSentPaymentReceivedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: IPaymentReceivedDeletedPayload) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: PAYMENT_RECEIVED_MAIL_SENT,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { EventSubscriber } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import { ReportsEvents } from '@/constants/event-tracker';
|
||||||
|
import { PosthogService } from '../PostHog';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import {
|
||||||
|
BALANCE_SHEET_VIEWED,
|
||||||
|
TRIAL_BALANCE_SHEET_VIEWED,
|
||||||
|
PROFIT_LOSS_SHEET_VIEWED,
|
||||||
|
CASHFLOW_STATEMENT_VIEWED,
|
||||||
|
GENERAL_LEDGER_VIEWED,
|
||||||
|
JOURNAL_VIEWED,
|
||||||
|
RECEIVABLE_AGING_VIEWED,
|
||||||
|
PAYABLE_AGING_VIEWED,
|
||||||
|
CUSTOMER_BALANCE_SUMMARY_VIEWED,
|
||||||
|
VENDOR_BALANCE_SUMMARY_VIEWED,
|
||||||
|
INVENTORY_VALUATION_VIEWED,
|
||||||
|
CUSTOMER_TRANSACTIONS_VIEWED,
|
||||||
|
VENDOR_TRANSACTIONS_VIEWED,
|
||||||
|
SALES_BY_ITEM_VIEWED,
|
||||||
|
PURCHASES_BY_ITEM_VIEWED,
|
||||||
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class ReportsEventsTracker extends EventSubscriber {
|
||||||
|
@Inject()
|
||||||
|
private posthog: PosthogService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onBalanceSheetViewed,
|
||||||
|
this.handleTrackBalanceSheetViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onTrialBalanceSheetView,
|
||||||
|
this.handleTrackTrialBalanceSheetViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onProfitLossSheetViewed,
|
||||||
|
this.handleTrackProfitLossSheetViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onCashflowStatementViewed,
|
||||||
|
this.handleTrackCashflowStatementViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onGeneralLedgerViewed,
|
||||||
|
this.handleTrackGeneralLedgerViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onJournalViewed,
|
||||||
|
this.handleTrackJournalViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onReceivableAgingViewed,
|
||||||
|
this.handleTrackReceivableAgingViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onPayableAgingViewed,
|
||||||
|
this.handleTrackPayableAgingViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onCustomerBalanceSummaryViewed,
|
||||||
|
this.handleTrackCustomerBalanceSummaryViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onVendorBalanceSummaryViewed,
|
||||||
|
this.handleTrackVendorBalanceSummaryViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onInventoryValuationViewed,
|
||||||
|
this.handleTrackInventoryValuationViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onCustomerTransactionsViewed,
|
||||||
|
this.handleTrackCustomerTransactionsViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onVendorTransactionsViewed,
|
||||||
|
this.handleTrackVendorTransactionsViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onSalesByItemViewed,
|
||||||
|
this.handleTrackSalesByItemViewedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.reports.onPurchasesByItemViewed,
|
||||||
|
this.handleTrackPurchasesByItemViewedEvent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleTrackBalanceSheetViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: BALANCE_SHEET_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackTrialBalanceSheetViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: TRIAL_BALANCE_SHEET_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackProfitLossSheetViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: PROFIT_LOSS_SHEET_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackCashflowStatementViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: CASHFLOW_STATEMENT_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackGeneralLedgerViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: GENERAL_LEDGER_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackJournalViewedEvent = ({ tenantId }: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: JOURNAL_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackReceivableAgingViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: RECEIVABLE_AGING_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackPayableAgingViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: PAYABLE_AGING_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackCustomerBalanceSummaryViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: CUSTOMER_BALANCE_SUMMARY_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackVendorBalanceSummaryViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: VENDOR_BALANCE_SUMMARY_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackInventoryValuationViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: INVENTORY_VALUATION_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackCustomerTransactionsViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: CUSTOMER_TRANSACTIONS_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackVendorTransactionsViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: VENDOR_TRANSACTIONS_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackSalesByItemViewedEvent = ({ tenantId }: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: SALES_BY_ITEM_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackPurchasesByItemViewedEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: ReportsEvents) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: PURCHASES_BY_ITEM_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -12,6 +12,8 @@ import {
|
|||||||
SALE_ESTIMATE_EDITED,
|
SALE_ESTIMATE_EDITED,
|
||||||
SALE_ESTIMATE_DELETED,
|
SALE_ESTIMATE_DELETED,
|
||||||
SALE_ESTIMATE_PDF_VIEWED,
|
SALE_ESTIMATE_PDF_VIEWED,
|
||||||
|
SALE_ESTIMATE_VIEWED,
|
||||||
|
SALE_ESTIMATE_MAIL_SENT,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -39,6 +41,14 @@ export class SaleEstimateEventsTracker extends EventSubscriber {
|
|||||||
events.saleEstimate.onPdfViewed,
|
events.saleEstimate.onPdfViewed,
|
||||||
this.handleTrackPdfViewedEstimateEvent
|
this.handleTrackPdfViewedEstimateEvent
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleEstimate.onViewed,
|
||||||
|
this.handleTrackViewedEstimateEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleEstimate.onMailSent,
|
||||||
|
this.handleTrackMailSentEstimateEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTrackEstimateCreatedEvent = ({
|
private handleTrackEstimateCreatedEvent = ({
|
||||||
@@ -80,4 +90,20 @@ export class SaleEstimateEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleTrackViewedEstimateEvent = ({ tenantId }) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: SALE_ESTIMATE_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTrackMailSentEstimateEvent = ({ tenantId }) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: SALE_ESTIMATE_MAIL_SENT,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { ITransactionsLockingPartialUnlocked } from '@/interfaces';
|
|||||||
import { PosthogService } from '../PostHog';
|
import { PosthogService } from '../PostHog';
|
||||||
import {
|
import {
|
||||||
SUBSCRIPTION_CANCELLED,
|
SUBSCRIPTION_CANCELLED,
|
||||||
|
SUBSCRIPTION_PAYMENT_FAILED,
|
||||||
|
SUBSCRIPTION_PAYMENT_SUCCEED,
|
||||||
SUBSCRIPTION_PLAN_CHANGED,
|
SUBSCRIPTION_PLAN_CHANGED,
|
||||||
SUBSCRIPTION_RESUMED,
|
SUBSCRIPTION_RESUMED,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
@@ -27,6 +29,14 @@ export class TransactionsLockingEventsTracker extends EventSubscriber {
|
|||||||
events.subscription.onSubscriptionPlanChanged,
|
events.subscription.onSubscriptionPlanChanged,
|
||||||
this.handleSubscriptionPlanChangedEvent
|
this.handleSubscriptionPlanChangedEvent
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.subscription.onSubscriptionPaymentSucceed,
|
||||||
|
this.handleSubscriptionPaymentFailedEvent
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.subscription.onSubscriptionPaymentFailed,
|
||||||
|
this.handleSubscriptionPaymentSucceed
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleSubscriptionResumedEvent = ({ tenantId }) => {
|
private handleSubscriptionResumedEvent = ({ tenantId }) => {
|
||||||
@@ -52,4 +62,20 @@ export class TransactionsLockingEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleSubscriptionPaymentFailedEvent = ({ tenantId }) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: SUBSCRIPTION_PAYMENT_FAILED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleSubscriptionPaymentSucceed = ({ tenantId }) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: SUBSCRIPTION_PAYMENT_SUCCEED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { PdfTemplateEventsTracker } from './PdfTemplateEventsTracker';
|
|||||||
import { PaymentMethodEventsTracker } from './PaymentMethodEventsTracker';
|
import { PaymentMethodEventsTracker } from './PaymentMethodEventsTracker';
|
||||||
import { PaymentLinkEventsTracker } from './PaymentLinkEventsTracker';
|
import { PaymentLinkEventsTracker } from './PaymentLinkEventsTracker';
|
||||||
import { StripeIntegrationEventsTracker } from './StripeIntegrationEventsTracker';
|
import { StripeIntegrationEventsTracker } from './StripeIntegrationEventsTracker';
|
||||||
|
import { ReportsEventsTracker } from './ReportsEventsTracker';
|
||||||
|
|
||||||
export const EventsTrackerListeners = [
|
export const EventsTrackerListeners = [
|
||||||
SaleInvoiceEventsTracker,
|
SaleInvoiceEventsTracker,
|
||||||
@@ -36,4 +37,5 @@ export const EventsTrackerListeners = [
|
|||||||
PaymentMethodEventsTracker,
|
PaymentMethodEventsTracker,
|
||||||
PaymentLinkEventsTracker,
|
PaymentLinkEventsTracker,
|
||||||
StripeIntegrationEventsTracker,
|
StripeIntegrationEventsTracker,
|
||||||
|
ReportsEventsTracker,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import TenancyService from '@/services/Tenancy/TenancyService';
|
|||||||
import APAgingSummarySheet from './APAgingSummarySheet';
|
import APAgingSummarySheet from './APAgingSummarySheet';
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
import { APAgingSummaryMeta } from './APAgingSummaryMeta';
|
import { APAgingSummaryMeta } from './APAgingSummaryMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class APAgingSummaryService {
|
export class APAgingSummaryService {
|
||||||
@@ -15,6 +17,9 @@ export class APAgingSummaryService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private APAgingSummaryMeta: APAgingSummaryMeta;
|
private APAgingSummaryMeta: APAgingSummaryMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default report query.
|
* Default report query.
|
||||||
*/
|
*/
|
||||||
@@ -96,6 +101,12 @@ export class APAgingSummaryService {
|
|||||||
// Retrieve the aging summary report meta.
|
// Retrieve the aging summary report meta.
|
||||||
const meta = await this.APAgingSummaryMeta.meta(tenantId, filter);
|
const meta = await this.APAgingSummaryMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onPayableAgingViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.reports.onPayableAgingViewed, {
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import TenancyService from '@/services/Tenancy/TenancyService';
|
|||||||
import ARAgingSummarySheet from './ARAgingSummarySheet';
|
import ARAgingSummarySheet from './ARAgingSummarySheet';
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
import { ARAgingSummaryMeta } from './ARAgingSummaryMeta';
|
import { ARAgingSummaryMeta } from './ARAgingSummaryMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class ARAgingSummaryService {
|
export default class ARAgingSummaryService {
|
||||||
@@ -15,6 +17,9 @@ export default class ARAgingSummaryService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private ARAgingSummaryMeta: ARAgingSummaryMeta;
|
private ARAgingSummaryMeta: ARAgingSummaryMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default report query.
|
* Default report query.
|
||||||
*/
|
*/
|
||||||
@@ -91,6 +96,15 @@ export default class ARAgingSummaryService {
|
|||||||
// Retrieve the aging summary report meta.
|
// Retrieve the aging summary report meta.
|
||||||
const meta = await this.ARAgingSummaryMeta.meta(tenantId, filter);
|
const meta = await this.ARAgingSummaryMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onReceivableAgingViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.reports.onReceivableAgingViewed,
|
||||||
|
{
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import BalanceSheetStatement from './BalanceSheet';
|
|||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
import BalanceSheetRepository from './BalanceSheetRepository';
|
import BalanceSheetRepository from './BalanceSheetRepository';
|
||||||
import { BalanceSheetMetaInjectable } from './BalanceSheetMeta';
|
import { BalanceSheetMetaInjectable } from './BalanceSheetMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class BalanceSheetStatementService
|
export default class BalanceSheetStatementService
|
||||||
@@ -21,6 +23,9 @@ export default class BalanceSheetStatementService
|
|||||||
@Inject()
|
@Inject()
|
||||||
private balanceSheetMeta: BalanceSheetMetaInjectable;
|
private balanceSheetMeta: BalanceSheetMetaInjectable;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults balance sheet filter query.
|
* Defaults balance sheet filter query.
|
||||||
* @return {IBalanceSheetQuery}
|
* @return {IBalanceSheetQuery}
|
||||||
@@ -98,6 +103,10 @@ export default class BalanceSheetStatementService
|
|||||||
// Balance sheet meta.
|
// Balance sheet meta.
|
||||||
const meta = await this.balanceSheetMeta.meta(tenantId, filter);
|
const meta = await this.balanceSheetMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onBalanceSheetViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.reports.onBalanceSheetViewed, {
|
||||||
|
query,
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
query: filter,
|
query: filter,
|
||||||
data,
|
data,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import Ledger from '@/services/Accounting/Ledger';
|
|||||||
import CustomerBalanceSummaryRepository from './CustomerBalanceSummaryRepository';
|
import CustomerBalanceSummaryRepository from './CustomerBalanceSummaryRepository';
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
import { CustomerBalanceSummaryMeta } from './CustomerBalanceSummaryMeta';
|
import { CustomerBalanceSummaryMeta } from './CustomerBalanceSummaryMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class CustomerBalanceSummaryService
|
export class CustomerBalanceSummaryService
|
||||||
@@ -24,6 +26,9 @@ export class CustomerBalanceSummaryService
|
|||||||
@Inject()
|
@Inject()
|
||||||
private customerBalanceSummaryMeta: CustomerBalanceSummaryMeta;
|
private customerBalanceSummaryMeta: CustomerBalanceSummaryMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults balance sheet filter query.
|
* Defaults balance sheet filter query.
|
||||||
* @return {ICustomerBalanceSummaryQuery}
|
* @return {ICustomerBalanceSummaryQuery}
|
||||||
@@ -104,6 +109,15 @@ export class CustomerBalanceSummaryService
|
|||||||
// Retrieve the customer balance summary meta.
|
// Retrieve the customer balance summary meta.
|
||||||
const meta = await this.customerBalanceSummaryMeta.meta(tenantId, filter);
|
const meta = await this.customerBalanceSummaryMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onCustomerBalanceSummaryViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.reports.onCustomerBalanceSummaryViewed,
|
||||||
|
{
|
||||||
|
tenant,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: report.reportData(),
|
data: report.reportData(),
|
||||||
query: filter,
|
query: filter,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import TenancyService from '@/services/Tenancy/TenancyService';
|
|||||||
import GeneralLedgerSheet from '@/services/FinancialStatements/GeneralLedger/GeneralLedger';
|
import GeneralLedgerSheet from '@/services/FinancialStatements/GeneralLedger/GeneralLedger';
|
||||||
import { GeneralLedgerMeta } from './GeneralLedgerMeta';
|
import { GeneralLedgerMeta } from './GeneralLedgerMeta';
|
||||||
import { GeneralLedgerRepository } from './GeneralLedgerRepository';
|
import { GeneralLedgerRepository } from './GeneralLedgerRepository';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class GeneralLedgerService {
|
export class GeneralLedgerService {
|
||||||
@@ -14,6 +16,9 @@ export class GeneralLedgerService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private generalLedgerMeta: GeneralLedgerMeta;
|
private generalLedgerMeta: GeneralLedgerMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults general ledger report filter query.
|
* Defaults general ledger report filter query.
|
||||||
* @return {IBalanceSheetQuery}
|
* @return {IBalanceSheetQuery}
|
||||||
@@ -72,6 +77,11 @@ export class GeneralLedgerService {
|
|||||||
// Retrieve general ledger report metadata.
|
// Retrieve general ledger report metadata.
|
||||||
const meta = await this.generalLedgerMeta.meta(tenantId, filter);
|
const meta = await this.generalLedgerMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onGeneralLedgerViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.reports.onGeneralLedgerViewed, {
|
||||||
|
tenantId,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: reportData,
|
data: reportData,
|
||||||
query: filter,
|
query: filter,
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import { InventoryValuationSheet } from './InventoryValuationSheet';
|
|||||||
import InventoryService from '@/services/Inventory/Inventory';
|
import InventoryService from '@/services/Inventory/Inventory';
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
import { InventoryValuationMetaInjectable } from './InventoryValuationSheetMeta';
|
import { InventoryValuationMetaInjectable } from './InventoryValuationSheetMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class InventoryValuationSheetService {
|
export class InventoryValuationSheetService {
|
||||||
@@ -26,6 +28,9 @@ export class InventoryValuationSheetService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private inventoryValuationMeta: InventoryValuationMetaInjectable;
|
private inventoryValuationMeta: InventoryValuationMetaInjectable;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults balance sheet filter query.
|
* Defaults balance sheet filter query.
|
||||||
* @return {IBalanceSheetQuery}
|
* @return {IBalanceSheetQuery}
|
||||||
@@ -116,6 +121,15 @@ export class InventoryValuationSheetService {
|
|||||||
// Retrieves the inventorty valuation meta.
|
// Retrieves the inventorty valuation meta.
|
||||||
const meta = await this.inventoryValuationMeta.meta(tenantId, filter);
|
const meta = await this.inventoryValuationMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onInventoryValuationViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.reports.onInventoryValuationViewed,
|
||||||
|
{
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: inventoryValuationData,
|
data: inventoryValuationData,
|
||||||
query: filter,
|
query: filter,
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import Journal from '@/services/Accounting/JournalPoster';
|
|||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
import { transformToMap } from 'utils';
|
import { transformToMap } from 'utils';
|
||||||
import { JournalSheetMeta } from './JournalSheetMeta';
|
import { JournalSheetMeta } from './JournalSheetMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class JournalSheetService {
|
export class JournalSheetService {
|
||||||
@@ -16,6 +18,9 @@ export class JournalSheetService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private journalSheetMeta: JournalSheetMeta;
|
private journalSheetMeta: JournalSheetMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default journal sheet filter queyr.
|
* Default journal sheet filter queyr.
|
||||||
*/
|
*/
|
||||||
@@ -101,6 +106,12 @@ export class JournalSheetService {
|
|||||||
// Retrieve the journal sheet meta.
|
// Retrieve the journal sheet meta.
|
||||||
const meta = await this.journalSheetMeta.meta(tenantId, filter);
|
const meta = await this.journalSheetMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onJournalViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.reports.onJournalViewed, {
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: journalSheetData,
|
data: journalSheetData,
|
||||||
query: filter,
|
query: filter,
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { Tenant } from '@/system/models';
|
|||||||
import { mergeQueryWithDefaults } from './utils';
|
import { mergeQueryWithDefaults } from './utils';
|
||||||
import { ProfitLossSheetRepository } from './ProfitLossSheetRepository';
|
import { ProfitLossSheetRepository } from './ProfitLossSheetRepository';
|
||||||
import { ProfitLossSheetMeta } from './ProfitLossSheetMeta';
|
import { ProfitLossSheetMeta } from './ProfitLossSheetMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
// Profit/Loss sheet service.
|
// Profit/Loss sheet service.
|
||||||
@Service()
|
@Service()
|
||||||
@@ -20,6 +22,9 @@ export default class ProfitLossSheetService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private profitLossSheetMeta: ProfitLossSheetMeta;
|
private profitLossSheetMeta: ProfitLossSheetMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve profit/loss sheet statement.
|
* Retrieve profit/loss sheet statement.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -62,6 +67,15 @@ export default class ProfitLossSheetService {
|
|||||||
// Retrieve the profit/loss sheet meta.
|
// Retrieve the profit/loss sheet meta.
|
||||||
const meta = await this.profitLossSheetMeta.meta(tenantId, filter);
|
const meta = await this.profitLossSheetMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onProfitLossSheetViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.reports.onProfitLossSheetViewed,
|
||||||
|
{
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
query: filter,
|
query: filter,
|
||||||
data,
|
data,
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
IPurchasesByItemsSheet,
|
IPurchasesByItemsSheet,
|
||||||
} from '@/interfaces/PurchasesByItemsSheet';
|
} from '@/interfaces/PurchasesByItemsSheet';
|
||||||
import { PurchasesByItemsMeta } from './PurchasesByItemsMeta';
|
import { PurchasesByItemsMeta } from './PurchasesByItemsMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class PurchasesByItemsService {
|
export class PurchasesByItemsService {
|
||||||
@@ -17,6 +19,9 @@ export class PurchasesByItemsService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private purchasesByItemsMeta: PurchasesByItemsMeta;
|
private purchasesByItemsMeta: PurchasesByItemsMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults purchases by items filter query.
|
* Defaults purchases by items filter query.
|
||||||
* @return {IPurchasesByItemsReportQuery}
|
* @return {IPurchasesByItemsReportQuery}
|
||||||
@@ -92,6 +97,15 @@ export class PurchasesByItemsService {
|
|||||||
// Retrieve the purchases by items meta.
|
// Retrieve the purchases by items meta.
|
||||||
const meta = await this.purchasesByItemsMeta.meta(tenantId, query);
|
const meta = await this.purchasesByItemsMeta.meta(tenantId, query);
|
||||||
|
|
||||||
|
// Triggers `onPurchasesByItemViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.reports.onPurchasesByItemViewed,
|
||||||
|
{
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: purchasesByItemsData,
|
data: purchasesByItemsData,
|
||||||
query: filter,
|
query: filter,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import TenancyService from '@/services/Tenancy/TenancyService';
|
|||||||
import SalesByItems from './SalesByItems';
|
import SalesByItems from './SalesByItems';
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
import { SalesByItemsMeta } from './SalesByItemsMeta';
|
import { SalesByItemsMeta } from './SalesByItemsMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SalesByItemsReportService {
|
export class SalesByItemsReportService {
|
||||||
@@ -14,6 +16,9 @@ export class SalesByItemsReportService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private salesByItemsMeta: SalesByItemsMeta;
|
private salesByItemsMeta: SalesByItemsMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults balance sheet filter query.
|
* Defaults balance sheet filter query.
|
||||||
* @return {IBalanceSheetQuery}
|
* @return {IBalanceSheetQuery}
|
||||||
@@ -89,6 +94,12 @@ export class SalesByItemsReportService {
|
|||||||
// Retrieve the sales by items meta.
|
// Retrieve the sales by items meta.
|
||||||
const meta = await this.salesByItemsMeta.meta(tenantId, query);
|
const meta = await this.salesByItemsMeta.meta(tenantId, query);
|
||||||
|
|
||||||
|
// Triggers `onSalesByItemViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.reports.onSalesByItemViewed, {
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: salesByItemsData,
|
data: salesByItemsData,
|
||||||
query: filter,
|
query: filter,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import Ledger from '@/services/Accounting/Ledger';
|
|||||||
import TransactionsByCustomersRepository from './TransactionsByCustomersRepository';
|
import TransactionsByCustomersRepository from './TransactionsByCustomersRepository';
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
import { TransactionsByCustomersMeta } from './TransactionsByCustomersMeta';
|
import { TransactionsByCustomersMeta } from './TransactionsByCustomersMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
export class TransactionsByCustomersSheet
|
export class TransactionsByCustomersSheet
|
||||||
implements ITransactionsByCustomersService
|
implements ITransactionsByCustomersService
|
||||||
@@ -26,6 +28,9 @@ export class TransactionsByCustomersSheet
|
|||||||
@Inject()
|
@Inject()
|
||||||
private transactionsByCustomersMeta: TransactionsByCustomersMeta;
|
private transactionsByCustomersMeta: TransactionsByCustomersMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults balance sheet filter query.
|
* Defaults balance sheet filter query.
|
||||||
* @return {ICustomerBalanceSummaryQuery}
|
* @return {ICustomerBalanceSummaryQuery}
|
||||||
@@ -166,6 +171,15 @@ export class TransactionsByCustomersSheet
|
|||||||
|
|
||||||
const meta = await this.transactionsByCustomersMeta.meta(tenantId, filter);
|
const meta = await this.transactionsByCustomersMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onCustomerTransactionsViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.reports.onCustomerTransactionsViewed,
|
||||||
|
{
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: reportInstance.reportData(),
|
data: reportInstance.reportData(),
|
||||||
query: filter,
|
query: filter,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import Ledger from '@/services/Accounting/Ledger';
|
|||||||
import TransactionsByVendorRepository from './TransactionsByVendorRepository';
|
import TransactionsByVendorRepository from './TransactionsByVendorRepository';
|
||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
import { TransactionsByVendorMeta } from './TransactionsByVendorMeta';
|
import { TransactionsByVendorMeta } from './TransactionsByVendorMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
export class TransactionsByVendorsInjectable
|
export class TransactionsByVendorsInjectable
|
||||||
implements ITransactionsByVendorsService
|
implements ITransactionsByVendorsService
|
||||||
@@ -26,6 +28,9 @@ export class TransactionsByVendorsInjectable
|
|||||||
@Inject()
|
@Inject()
|
||||||
private transactionsByVendorMeta: TransactionsByVendorMeta;
|
private transactionsByVendorMeta: TransactionsByVendorMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults balance sheet filter query.
|
* Defaults balance sheet filter query.
|
||||||
* @return {IVendorBalanceSummaryQuery}
|
* @return {IVendorBalanceSummaryQuery}
|
||||||
@@ -171,6 +176,15 @@ export class TransactionsByVendorsInjectable
|
|||||||
);
|
);
|
||||||
const meta = await this.transactionsByVendorMeta.meta(tenantId, filter);
|
const meta = await this.transactionsByVendorMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onVendorTransactionsViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.reports.onVendorTransactionsViewed,
|
||||||
|
{
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: reportInstance.reportData(),
|
data: reportInstance.reportData(),
|
||||||
query: filter,
|
query: filter,
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import FinancialSheet from '../FinancialSheet';
|
|||||||
import { Tenant } from '@/system/models';
|
import { Tenant } from '@/system/models';
|
||||||
import { TrialBalanceSheetRepository } from './TrialBalanceSheetRepository';
|
import { TrialBalanceSheetRepository } from './TrialBalanceSheetRepository';
|
||||||
import { TrialBalanceSheetMeta } from './TrialBalanceSheetMeta';
|
import { TrialBalanceSheetMeta } from './TrialBalanceSheetMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class TrialBalanceSheetService extends FinancialSheet {
|
export default class TrialBalanceSheetService extends FinancialSheet {
|
||||||
@@ -16,6 +18,9 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private trialBalanceSheetMetaService: TrialBalanceSheetMeta;
|
private trialBalanceSheetMetaService: TrialBalanceSheetMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults trial balance sheet filter query.
|
* Defaults trial balance sheet filter query.
|
||||||
* @return {IBalanceSheetQuery}
|
* @return {IBalanceSheetQuery}
|
||||||
@@ -81,6 +86,15 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
|||||||
// Trial balance sheet meta.
|
// Trial balance sheet meta.
|
||||||
const meta = await this.trialBalanceSheetMetaService.meta(tenantId, filter);
|
const meta = await this.trialBalanceSheetMetaService.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onTrialBalanceSheetViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.reports.onTrialBalanceSheetView,
|
||||||
|
{
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: trialBalanceSheetData,
|
data: trialBalanceSheetData,
|
||||||
query: filter,
|
query: filter,
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import { Tenant } from '@/system/models';
|
|||||||
import { JournalSheetMeta } from '../JournalSheet/JournalSheetMeta';
|
import { JournalSheetMeta } from '../JournalSheet/JournalSheetMeta';
|
||||||
|
|
||||||
import { VendorBalanceSummaryMeta } from './VendorBalanceSummaryMeta';
|
import { VendorBalanceSummaryMeta } from './VendorBalanceSummaryMeta';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
export class VendorBalanceSummaryService
|
export class VendorBalanceSummaryService
|
||||||
implements IVendorBalanceSummaryService
|
implements IVendorBalanceSummaryService
|
||||||
@@ -28,6 +30,9 @@ export class VendorBalanceSummaryService
|
|||||||
@Inject()
|
@Inject()
|
||||||
private vendorBalanceSummaryMeta: VendorBalanceSummaryMeta;
|
private vendorBalanceSummaryMeta: VendorBalanceSummaryMeta;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defaults balance sheet filter query.
|
* Defaults balance sheet filter query.
|
||||||
* @return {IVendorBalanceSummaryQuery}
|
* @return {IVendorBalanceSummaryQuery}
|
||||||
@@ -107,10 +112,19 @@ export class VendorBalanceSummaryService
|
|||||||
// Retrieve the vendor balance summary meta.
|
// Retrieve the vendor balance summary meta.
|
||||||
const meta = await this.vendorBalanceSummaryMeta.meta(tenantId, filter);
|
const meta = await this.vendorBalanceSummaryMeta.meta(tenantId, filter);
|
||||||
|
|
||||||
|
// Triggers `onVendorBalanceSummaryViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.reports.onVendorBalanceSummaryViewed,
|
||||||
|
{
|
||||||
|
tenantId,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: reportInstance.reportData(),
|
data: reportInstance.reportData(),
|
||||||
query: filter,
|
query: filter,
|
||||||
meta
|
meta,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,22 +23,24 @@ export class ContactMailNotification {
|
|||||||
public async getDefaultMailOptions(
|
public async getDefaultMailOptions(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
customerId: number
|
customerId: number
|
||||||
): Promise<Pick<CommonMailOptions, 'to' | 'from'>> {
|
): Promise<
|
||||||
|
Pick<CommonMailOptions, 'to' | 'from' | 'toOptions' | 'fromOptions'>
|
||||||
|
> {
|
||||||
const { Customer } = this.tenancy.models(tenantId);
|
const { Customer } = this.tenancy.models(tenantId);
|
||||||
const customer = await Customer.query()
|
const customer = await Customer.query()
|
||||||
.findById(customerId)
|
.findById(customerId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const toAddresses = customer.contactAddresses;
|
const toOptions = customer.contactAddresses;
|
||||||
const fromAddresses = await this.mailTenancy.senders(tenantId);
|
const fromOptions = await this.mailTenancy.senders(tenantId);
|
||||||
|
|
||||||
const toAddress = toAddresses.find((a) => a.primary);
|
const toAddress = toOptions.find((a) => a.primary);
|
||||||
const fromAddress = fromAddresses.find((a) => a.primary);
|
const fromAddress = fromOptions.find((a) => a.primary);
|
||||||
|
|
||||||
const to = toAddress?.mail ? castArray(toAddress?.mail) : [];
|
const to = toAddress?.mail ? castArray(toAddress?.mail) : [];
|
||||||
const from = fromAddress?.mail ? castArray(fromAddress?.mail) : [];
|
const from = fromAddress?.mail ? castArray(fromAddress?.mail) : [];
|
||||||
|
|
||||||
return { to, from };
|
return { to, from, toOptions, fromOptions };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,7 +48,7 @@ export class ContactMailNotification {
|
|||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @returns {Promise<CommonMailOptions>}
|
* @returns {Promise<CommonMailOptions>}
|
||||||
*/
|
*/
|
||||||
public async parseMailOptions(
|
public async formatMailOptions(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
mailOptions: CommonMailOptions,
|
mailOptions: CommonMailOptions,
|
||||||
formatterArgs?: Record<string, any>
|
formatterArgs?: Record<string, any>
|
||||||
|
|||||||
@@ -44,3 +44,13 @@ export function validateRequiredMailOptions(
|
|||||||
throw new ServiceError(ERRORS.MAIL_BODY_NOT_FOUND);
|
throw new ServiceError(ERRORS.MAIL_BODY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const mergeAndValidateMailOptions = (
|
||||||
|
mailOptions: CommonMailOptions,
|
||||||
|
overridedOptions: Partial<CommonMailOptions>
|
||||||
|
): CommonMailOptions => {
|
||||||
|
const parsedMessageOptions = parseMailOptions(mailOptions, overridedOptions);
|
||||||
|
validateRequiredMailOptions(parsedMessageOptions);
|
||||||
|
|
||||||
|
return parsedMessageOptions;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { difference, isEmpty } from 'lodash';
|
import { difference, isEmpty, round, sumBy } from 'lodash';
|
||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { ServiceError } from '@/exceptions';
|
import { ServiceError } from '@/exceptions';
|
||||||
import {
|
import {
|
||||||
@@ -23,20 +23,18 @@ export class CommandManualJournalValidators {
|
|||||||
* @param {IManualJournalDTO} manualJournalDTO
|
* @param {IManualJournalDTO} manualJournalDTO
|
||||||
*/
|
*/
|
||||||
public valdiateCreditDebitTotalEquals(manualJournalDTO: IManualJournalDTO) {
|
public valdiateCreditDebitTotalEquals(manualJournalDTO: IManualJournalDTO) {
|
||||||
let totalCredit = 0;
|
const totalCredit = round(
|
||||||
let totalDebit = 0;
|
sumBy(manualJournalDTO.entries, (entry) => entry.credit || 0),
|
||||||
|
2
|
||||||
manualJournalDTO.entries.forEach((entry) => {
|
);
|
||||||
if (entry.credit > 0) {
|
const totalDebit = round(
|
||||||
totalCredit += entry.credit;
|
sumBy(manualJournalDTO.entries, (entry) => entry.debit || 0),
|
||||||
}
|
2
|
||||||
if (entry.debit > 0) {
|
);
|
||||||
totalDebit += entry.debit;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (totalCredit <= 0 || totalDebit <= 0) {
|
if (totalCredit <= 0 || totalDebit <= 0) {
|
||||||
throw new ServiceError(ERRORS.CREDIT_DEBIT_NOT_EQUAL_ZERO);
|
throw new ServiceError(ERRORS.CREDIT_DEBIT_NOT_EQUAL_ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalCredit !== totalDebit) {
|
if (totalCredit !== totalDebit) {
|
||||||
throw new ServiceError(ERRORS.CREDIT_DEBIT_NOT_EQUAL);
|
throw new ServiceError(ERRORS.CREDIT_DEBIT_NOT_EQUAL);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||||
import { getTransactionTypeLabel } from '@/utils/transactions-types';
|
import { getTransactionTypeLabel } from '@/utils/transactions-types';
|
||||||
import { getUploadedObjectUri } from '../Attachments/utils';
|
|
||||||
|
|
||||||
export class GetPdfTemplateTransformer extends Transformer {
|
export class GetPdfTemplateTransformer extends Transformer {
|
||||||
/**
|
/**
|
||||||
* Includeded attributes.
|
* Included attributes.
|
||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
public includeAttributes = (): string[] => {
|
public includeAttributes = (): string[] => {
|
||||||
@@ -44,20 +43,10 @@ export class GetPdfTemplateTransformer extends Transformer {
|
|||||||
|
|
||||||
class GetPdfTemplateAttributesTransformer extends Transformer {
|
class GetPdfTemplateAttributesTransformer extends Transformer {
|
||||||
/**
|
/**
|
||||||
* Includeded attributes.
|
* Included attributes.
|
||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
public includeAttributes = (): string[] => {
|
public includeAttributes = (): string[] => {
|
||||||
return ['companyLogoUri'];
|
return [];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the company logo uri.
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
protected companyLogoUri(template) {
|
|
||||||
return template.companyLogoKey
|
|
||||||
? getUploadedObjectUri(template.companyLogoKey)
|
|
||||||
: '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ export interface ICreateInvoicePdfTemplateDTO {
|
|||||||
showStatement?: boolean;
|
showStatement?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface CommonOrganizationBrandingAttributes {
|
export interface CommonOrganizationBrandingAttributes {
|
||||||
companyName?: string;
|
companyName?: string;
|
||||||
primaryColor?: string;
|
primaryColor?: string;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
import { SaleEstimateTransfromer } from './SaleEstimateTransformer';
|
import { SaleEstimateTransfromer } from './SaleEstimateTransformer';
|
||||||
import { SaleEstimateValidators } from './SaleEstimateValidators';
|
import { SaleEstimateValidators } from './SaleEstimateValidators';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class GetSaleEstimate {
|
export class GetSaleEstimate {
|
||||||
@@ -15,6 +17,9 @@ export class GetSaleEstimate {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private validators: SaleEstimateValidators;
|
private validators: SaleEstimateValidators;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the estimate details with associated entries.
|
* Retrieve the estimate details with associated entries.
|
||||||
* @async
|
* @async
|
||||||
@@ -35,10 +40,18 @@ export class GetSaleEstimate {
|
|||||||
this.validators.validateEstimateExistance(estimate);
|
this.validators.validateEstimateExistance(estimate);
|
||||||
|
|
||||||
// Transformes sale estimate model to POJO.
|
// Transformes sale estimate model to POJO.
|
||||||
return this.transformer.transform(
|
const transformed = await this.transformer.transform(
|
||||||
tenantId,
|
tenantId,
|
||||||
estimate,
|
estimate,
|
||||||
new SaleEstimateTransfromer()
|
new SaleEstimateTransfromer()
|
||||||
);
|
);
|
||||||
|
const eventPayload = { tenantId, saleEstimateId: estimateId };
|
||||||
|
|
||||||
|
// Triggers `onSaleEstimateViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleEstimate.onViewed,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
|
return transformed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import {
|
|||||||
SaleEstimateMailOptionsDTO,
|
SaleEstimateMailOptionsDTO,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
||||||
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
|
import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils';
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
|
import { transformEstimateToMailDataArgs } from './utils';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SendSaleEstimateMail {
|
export class SendSaleEstimateMail {
|
||||||
@@ -65,23 +66,17 @@ export class SendSaleEstimateMail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formates the text of the mail.
|
* Formate the text of the mail.
|
||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {number} estimateId - Estimate id.
|
* @param {number} estimateId - Estimate id.
|
||||||
* @returns {Promise<Record<string, any>>}
|
* @returns {Promise<Record<string, any>>}
|
||||||
*/
|
*/
|
||||||
public formatterData = async (tenantId: number, estimateId: number) => {
|
public formatterArgs = async (tenantId: number, estimateId: number) => {
|
||||||
const estimate = await this.getSaleEstimateService.getEstimate(
|
const estimate = await this.getSaleEstimateService.getEstimate(
|
||||||
tenantId,
|
tenantId,
|
||||||
estimateId
|
estimateId
|
||||||
);
|
);
|
||||||
return {
|
return transformEstimateToMailDataArgs(estimate);
|
||||||
CustomerName: estimate.customer.displayName,
|
|
||||||
EstimateNumber: estimate.estimateNumber,
|
|
||||||
EstimateDate: estimate.formattedEstimateDate,
|
|
||||||
EstimateAmount: estimate.formattedAmount,
|
|
||||||
EstimateExpirationDate: estimate.formattedExpirationDate,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,7 +87,9 @@ export class SendSaleEstimateMail {
|
|||||||
*/
|
*/
|
||||||
public getMailOptions = async (
|
public getMailOptions = async (
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
saleEstimateId: number
|
saleEstimateId: number,
|
||||||
|
defaultSubject: string = DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT,
|
||||||
|
defaultMessage: string = DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT
|
||||||
): Promise<SaleEstimateMailOptions> => {
|
): Promise<SaleEstimateMailOptions> => {
|
||||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
@@ -100,22 +97,44 @@ export class SendSaleEstimateMail {
|
|||||||
.findById(saleEstimateId)
|
.findById(saleEstimateId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const formatterData = await this.formatterData(tenantId, saleEstimateId);
|
const formatArgs = await this.formatterArgs(tenantId, saleEstimateId);
|
||||||
|
|
||||||
const mailOptions = await this.contactMailNotification.getMailOptions(
|
const mailOptions =
|
||||||
|
await this.contactMailNotification.getDefaultMailOptions(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleEstimate.customerId,
|
saleEstimate.customerId
|
||||||
DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT,
|
|
||||||
DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT,
|
|
||||||
formatterData
|
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
...mailOptions,
|
...mailOptions,
|
||||||
data: formatterData,
|
message: defaultMessage,
|
||||||
|
subject: defaultSubject,
|
||||||
attachEstimate: true,
|
attachEstimate: true,
|
||||||
|
formatArgs,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the given mail options.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} saleEstimateId
|
||||||
|
* @param {SaleEstimateMailOptions} mailOptions
|
||||||
|
* @returns {Promise<SaleEstimateMailOptions>}
|
||||||
|
*/
|
||||||
|
public formatMailOptions = async (
|
||||||
|
tenantId: number,
|
||||||
|
saleEstimateId: number,
|
||||||
|
mailOptions: SaleEstimateMailOptions
|
||||||
|
): Promise<SaleEstimateMailOptions> => {
|
||||||
|
const formatterArgs = await this.formatterArgs(tenantId, saleEstimateId);
|
||||||
|
const formattedOptions =
|
||||||
|
await this.contactMailNotification.formatMailOptions(
|
||||||
|
tenantId,
|
||||||
|
mailOptions,
|
||||||
|
formatterArgs
|
||||||
|
);
|
||||||
|
return { ...formattedOptions };
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the mail notification of the given sale estimate.
|
* Sends the mail notification of the given sale estimate.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -133,27 +152,54 @@ export class SendSaleEstimateMail {
|
|||||||
saleEstimateId
|
saleEstimateId
|
||||||
);
|
);
|
||||||
// Overrides and validates the given mail options.
|
// Overrides and validates the given mail options.
|
||||||
const messageOpts = parseAndValidateMailOptions(
|
const parsedMessageOptions = mergeAndValidateMailOptions(
|
||||||
localMessageOpts,
|
localMessageOpts,
|
||||||
messageOptions
|
messageOptions
|
||||||
|
) as SaleEstimateMailOptions;
|
||||||
|
|
||||||
|
const formattedOptions = await this.formatMailOptions(
|
||||||
|
tenantId,
|
||||||
|
saleEstimateId,
|
||||||
|
parsedMessageOptions
|
||||||
);
|
);
|
||||||
const mail = new Mail()
|
const mail = new Mail()
|
||||||
.setSubject(messageOpts.subject)
|
.setSubject(formattedOptions.subject)
|
||||||
.setTo(messageOpts.to)
|
.setTo(formattedOptions.to)
|
||||||
.setContent(messageOpts.body);
|
.setCC(formattedOptions.cc)
|
||||||
|
.setBCC(formattedOptions.bcc)
|
||||||
|
.setContent(formattedOptions.message);
|
||||||
|
|
||||||
|
// Attaches the estimate pdf to the mail.
|
||||||
|
if (formattedOptions.attachEstimate) {
|
||||||
|
// Retrieves the estimate pdf and attaches it to the mail.
|
||||||
|
const [estimatePdfBuffer, estimateFilename] =
|
||||||
|
await this.estimatePdf.getSaleEstimatePdf(tenantId, saleEstimateId);
|
||||||
|
|
||||||
if (messageOpts.attachEstimate) {
|
|
||||||
const estimatePdfBuffer = await this.estimatePdf.getSaleEstimatePdf(
|
|
||||||
tenantId,
|
|
||||||
saleEstimateId
|
|
||||||
);
|
|
||||||
mail.setAttachments([
|
mail.setAttachments([
|
||||||
{
|
{
|
||||||
filename: messageOpts.data?.EstimateNumber || 'estimate.pdf',
|
filename: `${estimateFilename}.pdf`,
|
||||||
content: estimatePdfBuffer,
|
content: estimatePdfBuffer,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const eventPayload = {
|
||||||
|
tenantId,
|
||||||
|
saleEstimateId,
|
||||||
|
messageOptions,
|
||||||
|
formattedOptions,
|
||||||
|
};
|
||||||
|
// Triggers `onSaleEstimateMailSend` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleEstimate.onMailSend,
|
||||||
|
eventPayload as ISaleEstimateMailPresendEvent
|
||||||
|
);
|
||||||
await mail.send();
|
await mail.send();
|
||||||
|
|
||||||
|
// Triggers `onSaleEstimateMailSent` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleEstimate.onMailSent,
|
||||||
|
eventPayload as ISaleEstimateMailPresendEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,3 +22,13 @@ export const transformEstimateToPdfTemplate = (
|
|||||||
customerAddress: contactAddressTextFormat(estimate.customer),
|
customerAddress: contactAddressTextFormat(estimate.customer),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const transformEstimateToMailDataArgs = (estimate: any) => {
|
||||||
|
return {
|
||||||
|
'Customer Name': estimate.customer.displayName,
|
||||||
|
'Estimate Number': estimate.estimateNumber,
|
||||||
|
'Estimate Date': estimate.formattedEstimateDate,
|
||||||
|
'Estimate Amount': estimate.formattedAmount,
|
||||||
|
'Estimate Expiration Date': estimate.formattedExpirationDate,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -32,15 +32,14 @@ export class GenerateShareLink {
|
|||||||
*/
|
*/
|
||||||
async generatePaymentLink(
|
async generatePaymentLink(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
transactionId: number,
|
saleInvoiceId: number,
|
||||||
transactionType: string,
|
|
||||||
publicity: string = 'private',
|
publicity: string = 'private',
|
||||||
expiryTime: string = ''
|
expiryTime: string = ''
|
||||||
) {
|
) {
|
||||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const foundInvoice = await SaleInvoice.query()
|
const foundInvoice = await SaleInvoice.query()
|
||||||
.findById(transactionId)
|
.findById(saleInvoiceId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
// Generate unique uuid for sharable link.
|
// Generate unique uuid for sharable link.
|
||||||
@@ -48,8 +47,7 @@ export class GenerateShareLink {
|
|||||||
|
|
||||||
const commonEventPayload = {
|
const commonEventPayload = {
|
||||||
tenantId,
|
tenantId,
|
||||||
transactionId,
|
saleInvoiceId,
|
||||||
transactionType,
|
|
||||||
publicity,
|
publicity,
|
||||||
expiryTime,
|
expiryTime,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export class GetInvoicePaymentMail {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the mail template attributes of the given invoice.
|
* Retrieves the mail template attributes of the given invoice.
|
||||||
|
* Invoice template attributes are composed of the invoice and branding template attributes.
|
||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {number} invoiceId - Invoice id.
|
* @param {number} invoiceId - Invoice id.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export class GetInvoiceMailTemplateAttributesTransformer extends Transformer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public companyLogoUri(): string {
|
public companyLogoUri(): string {
|
||||||
return this.options.brandingTemplate?.attributes?.companyLogoUri;
|
return this.options.brandingTemplate?.companyLogoUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public companyName(): string {
|
public companyName(): string {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export class GetSaleInvoiceMailState {
|
|||||||
/**
|
/**
|
||||||
* Retrieves the invoice mail state of the given sale invoice.
|
* Retrieves the invoice mail state of the given sale invoice.
|
||||||
* Invoice mail state includes the mail options, branding attributes and the invoice details.
|
* Invoice mail state includes the mail options, branding attributes and the invoice details.
|
||||||
*
|
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} saleInvoiceId
|
* @param {number} saleInvoiceId
|
||||||
* @returns {Promise<SaleInvoiceMailState>}
|
* @returns {Promise<SaleInvoiceMailState>}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
|
||||||
import { SaleInvoiceTransformer } from './SaleInvoiceTransformer';
|
import { SaleInvoiceTransformer } from './SaleInvoiceTransformer';
|
||||||
import { ItemEntryTransformer } from './ItemEntryTransformer';
|
import { ItemEntryTransformer } from './ItemEntryTransformer';
|
||||||
|
|
||||||
@@ -11,6 +10,10 @@ export class GetSaleInvoiceMailStateTransformer extends SaleInvoiceTransformer {
|
|||||||
return ['*'];
|
return ['*'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Included attributes.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
public includeAttributes = (): string[] => {
|
public includeAttributes = (): string[] => {
|
||||||
return [
|
return [
|
||||||
'invoiceDate',
|
'invoiceDate',
|
||||||
@@ -36,17 +39,39 @@ export class GetSaleInvoiceMailStateTransformer extends SaleInvoiceTransformer {
|
|||||||
'companyLogoUri',
|
'companyLogoUri',
|
||||||
|
|
||||||
'primaryColor',
|
'primaryColor',
|
||||||
|
|
||||||
|
'customerName',
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the customer name of the invoice.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected customerName = (invoice) => {
|
||||||
|
return invoice.customer.displayName;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the company name.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
protected companyName = () => {
|
protected companyName = () => {
|
||||||
return this.context.organization.name;
|
return this.context.organization.name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the company logo uri.
|
||||||
|
* @returns {string | null}
|
||||||
|
*/
|
||||||
protected companyLogoUri = (invoice) => {
|
protected companyLogoUri = (invoice) => {
|
||||||
return invoice.pdfTemplate?.attributes?.companyLogoUri;
|
return invoice.pdfTemplate?.companyLogoUri;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the primary color.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
protected primaryColor = (invoice) => {
|
protected primaryColor = (invoice) => {
|
||||||
return invoice.pdfTemplate?.attributes?.primaryColor;
|
return invoice.pdfTemplate?.attributes?.primaryColor;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,8 +33,12 @@ export class SaleEstimatePdfTemplate {
|
|||||||
...defaultEstimatePdfBrandingAttributes,
|
...defaultEstimatePdfBrandingAttributes,
|
||||||
...commonOrgBrandingAttrs,
|
...commonOrgBrandingAttrs,
|
||||||
};
|
};
|
||||||
|
const brandingTemplateAttrs = {
|
||||||
|
...template.attributes,
|
||||||
|
companyLogoUri: template.companyLogoUri,
|
||||||
|
};
|
||||||
const attributes = mergePdfTemplateWithDefaultAttributes(
|
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||||
template.attributes,
|
brandingTemplateAttrs,
|
||||||
orgainizationBrandingAttrs
|
orgainizationBrandingAttrs
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { renderInvoicePaperTemplateHtml } from '@bigcapital/pdf-templates';
|
||||||
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
||||||
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
||||||
import { GetSaleInvoice } from './GetSaleInvoice';
|
import { GetSaleInvoice } from './GetSaleInvoice';
|
||||||
@@ -8,6 +9,7 @@ import { InvoicePdfTemplateAttributes } from '@/interfaces';
|
|||||||
import { SaleInvoicePdfTemplate } from './SaleInvoicePdfTemplate';
|
import { SaleInvoicePdfTemplate } from './SaleInvoicePdfTemplate';
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
|
import { renderInvoicePaymentEmail } from '@bigcapital/email-components';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleInvoicePdf {
|
export class SaleInvoicePdf {
|
||||||
@@ -17,9 +19,6 @@ export class SaleInvoicePdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private chromiumlyTenancy: ChromiumlyTenancy;
|
private chromiumlyTenancy: ChromiumlyTenancy;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private templateInjectable: TemplateInjectable;
|
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private getInvoiceService: GetSaleInvoice;
|
private getInvoiceService: GetSaleInvoice;
|
||||||
|
|
||||||
@@ -29,6 +28,25 @@ export class SaleInvoicePdf {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private eventPublisher: EventPublisher;
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve sale invoice html content.
|
||||||
|
* @param {number} tenantId - Tenant Id.
|
||||||
|
* @param {ISaleInvoice} saleInvoice -
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
public async saleInvoiceHtml(
|
||||||
|
tenantId: number,
|
||||||
|
invoiceId: number
|
||||||
|
): Promise<string> {
|
||||||
|
const brandingAttributes = await this.getInvoiceBrandingAttributes(
|
||||||
|
tenantId,
|
||||||
|
invoiceId
|
||||||
|
);
|
||||||
|
return renderInvoicePaperTemplateHtml({
|
||||||
|
...brandingAttributes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice pdf content.
|
* Retrieve sale invoice pdf content.
|
||||||
* @param {number} tenantId - Tenant Id.
|
* @param {number} tenantId - Tenant Id.
|
||||||
@@ -41,15 +59,8 @@ export class SaleInvoicePdf {
|
|||||||
): Promise<[Buffer, string]> {
|
): Promise<[Buffer, string]> {
|
||||||
const filename = await this.getInvoicePdfFilename(tenantId, invoiceId);
|
const filename = await this.getInvoicePdfFilename(tenantId, invoiceId);
|
||||||
|
|
||||||
const brandingAttributes = await this.getInvoiceBrandingAttributes(
|
const htmlContent = await this.saleInvoiceHtml(tenantId, invoiceId);
|
||||||
tenantId,
|
|
||||||
invoiceId
|
|
||||||
);
|
|
||||||
const htmlContent = await this.templateInjectable.render(
|
|
||||||
tenantId,
|
|
||||||
'modules/invoice-standard',
|
|
||||||
brandingAttributes
|
|
||||||
);
|
|
||||||
// Converts the given html content to pdf document.
|
// Converts the given html content to pdf document.
|
||||||
const buffer = await this.chromiumlyTenancy.convertHtmlContent(
|
const buffer = await this.chromiumlyTenancy.convertHtmlContent(
|
||||||
tenantId,
|
tenantId,
|
||||||
|
|||||||
@@ -32,8 +32,12 @@ export class SaleInvoicePdfTemplate {
|
|||||||
...defaultInvoicePdfTemplateAttributes,
|
...defaultInvoicePdfTemplateAttributes,
|
||||||
...commonOrgBrandingAttrs,
|
...commonOrgBrandingAttrs,
|
||||||
};
|
};
|
||||||
|
const brandingTemplateAttrs = {
|
||||||
|
...template.attributes,
|
||||||
|
companyLogoUri: template.companyLogoUri,
|
||||||
|
};
|
||||||
const attributes = mergePdfTemplateWithDefaultAttributes(
|
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||||
template.attributes,
|
brandingTemplateAttrs,
|
||||||
organizationBrandingAttrs
|
organizationBrandingAttrs
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -28,9 +28,7 @@ import { GetInvoicePaymentsService } from './GetInvoicePaymentsService';
|
|||||||
import { SaleInvoiceNotifyBySms } from './SaleInvoiceNotifyBySms';
|
import { SaleInvoiceNotifyBySms } from './SaleInvoiceNotifyBySms';
|
||||||
import { SendInvoiceMailReminder } from './SendSaleInvoiceMailReminder';
|
import { SendInvoiceMailReminder } from './SendSaleInvoiceMailReminder';
|
||||||
import { SendSaleInvoiceMail } from './SendSaleInvoiceMail';
|
import { SendSaleInvoiceMail } from './SendSaleInvoiceMail';
|
||||||
import { GetSaleInvoiceMailReminder } from './GetSaleInvoiceMailReminder';
|
|
||||||
import { GetSaleInvoiceState } from './GetSaleInvoiceState';
|
import { GetSaleInvoiceState } from './GetSaleInvoiceState';
|
||||||
import { GetSaleInvoiceBrandTemplate } from './GetSaleInvoiceBrandTemplate';
|
|
||||||
import { GetSaleInvoiceMailState } from './GetSaleInvoiceMailState';
|
import { GetSaleInvoiceMailState } from './GetSaleInvoiceMailState';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -275,6 +273,19 @@ export class SaleInvoiceApplication {
|
|||||||
return this.pdfSaleInvoiceService.saleInvoicePdf(tenantId, saleInvoiceId);
|
return this.pdfSaleInvoiceService.saleInvoicePdf(tenantId, saleInvoiceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the html content of the given sale invoice.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} saleInvoiceId
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
public saleInvoiceHtml(
|
||||||
|
tenantId: number,
|
||||||
|
saleInvoiceId: number
|
||||||
|
): Promise<string> {
|
||||||
|
return this.pdfSaleInvoiceService.saleInvoiceHtml(tenantId, saleInvoiceId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -366,7 +377,10 @@ export class SaleInvoiceApplication {
|
|||||||
* @param {number} saleInvoiceid
|
* @param {number} saleInvoiceid
|
||||||
* @returns {Promise<SaleInvoiceMailState>}
|
* @returns {Promise<SaleInvoiceMailState>}
|
||||||
*/
|
*/
|
||||||
public getSaleInvoiceMailState(tenantId: number, saleInvoiceid: number) {
|
public getSaleInvoiceMailState(
|
||||||
|
tenantId: number,
|
||||||
|
saleInvoiceid: number
|
||||||
|
): Promise<SaleInvoiceMailState> {
|
||||||
return this.getSaleInvoiceMailStateService.getInvoiceMailState(
|
return this.getSaleInvoiceMailStateService.getInvoiceMailState(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceid
|
saleInvoiceid
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
DEFAULT_INVOICE_MAIL_SUBJECT,
|
DEFAULT_INVOICE_MAIL_SUBJECT,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { GetInvoicePaymentMail } from './GetInvoicePaymentMail';
|
import { GetInvoicePaymentMail } from './GetInvoicePaymentMail';
|
||||||
|
import { GenerateShareLink } from './GenerateeInvoicePaymentLink';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SendSaleInvoiceMailCommon {
|
export class SendSaleInvoiceMailCommon {
|
||||||
@@ -23,6 +24,9 @@ export class SendSaleInvoiceMailCommon {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private getInvoicePaymentMail: GetInvoicePaymentMail;
|
private getInvoicePaymentMail: GetInvoicePaymentMail;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private generatePaymentLinkService: GenerateShareLink;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the mail options.
|
* Retrieves the mail options.
|
||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
@@ -75,20 +79,32 @@ export class SendSaleInvoiceMailCommon {
|
|||||||
tenantId,
|
tenantId,
|
||||||
invoiceId
|
invoiceId
|
||||||
);
|
);
|
||||||
const parsedOptions = await this.contactMailNotification.parseMailOptions(
|
const formattedOptions =
|
||||||
|
await this.contactMailNotification.formatMailOptions(
|
||||||
tenantId,
|
tenantId,
|
||||||
mailOptions,
|
mailOptions,
|
||||||
formatterArgs
|
formatterArgs
|
||||||
);
|
);
|
||||||
|
// Generates the a new payment link for the given invoice.
|
||||||
|
const paymentLink =
|
||||||
|
await this.generatePaymentLinkService.generatePaymentLink(
|
||||||
|
tenantId,
|
||||||
|
invoiceId,
|
||||||
|
'public'
|
||||||
|
);
|
||||||
const message = await this.getInvoicePaymentMail.getMailTemplate(
|
const message = await this.getInvoicePaymentMail.getMailTemplate(
|
||||||
tenantId,
|
tenantId,
|
||||||
invoiceId,
|
invoiceId,
|
||||||
{
|
{
|
||||||
// # Invoice message
|
// # Invoice message
|
||||||
invoiceMessage: parsedOptions.message,
|
invoiceMessage: formattedOptions.message,
|
||||||
|
preview: formattedOptions.message,
|
||||||
|
|
||||||
|
// # Payment link
|
||||||
|
viewInvoiceButtonUrl: paymentLink.link,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return { ...parsedOptions, message };
|
return { ...formattedOptions, message };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,13 +127,13 @@ export class SendSaleInvoiceMailCommon {
|
|||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
...commonArgs,
|
...commonArgs,
|
||||||
['Customer Name']: invoice.customer.displayName,
|
'Customer Name': invoice.customer.displayName,
|
||||||
['Invoice Number']: invoice.invoiceNo,
|
'Invoice Number': invoice.invoiceNo,
|
||||||
['Invoice DueAmount']: invoice.dueAmountFormatted,
|
'Invoice Due Amount': invoice.dueAmountFormatted,
|
||||||
['Invoice DueDate']: invoice.dueDateFormatted,
|
'Invoice Due Date': invoice.dueDateFormatted,
|
||||||
['Invoice Date']: invoice.invoiceDateFormatted,
|
'Invoice Date': invoice.invoiceDateFormatted,
|
||||||
['Invoice Amount']: invoice.totalFormatted,
|
'Invoice Amount': invoice.totalFormatted,
|
||||||
['Overdue Days']: invoice.overdueDays,
|
'Overdue Days': invoice.overdueDays,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import Mail from '@/lib/Mail';
|
import Mail from '@/lib/Mail';
|
||||||
import { ISaleInvoiceMailSend, SendInvoiceMailDTO } from '@/interfaces';
|
import {
|
||||||
|
ISaleInvoiceMailSend,
|
||||||
|
SaleInvoiceMailOptions,
|
||||||
|
SendInvoiceMailDTO,
|
||||||
|
} from '@/interfaces';
|
||||||
import { SaleInvoicePdf } from './SaleInvoicePdf';
|
import { SaleInvoicePdf } from './SaleInvoicePdf';
|
||||||
import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon';
|
import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon';
|
||||||
import {
|
import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils';
|
||||||
parseMailOptions,
|
|
||||||
validateRequiredMailOptions,
|
|
||||||
} from '@/services/MailNotification/utils';
|
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@@ -18,12 +19,12 @@ export class SendSaleInvoiceMail {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private invoiceMail: SendSaleInvoiceMailCommon;
|
private invoiceMail: SendSaleInvoiceMailCommon;
|
||||||
|
|
||||||
@Inject('agenda')
|
|
||||||
private agenda: any;
|
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private eventPublisher: EventPublisher;
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
|
@Inject('agenda')
|
||||||
|
private agenda: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the invoice mail of the given sale invoice.
|
* Sends the invoice mail of the given sale invoice.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -50,6 +51,34 @@ export class SendSaleInvoiceMail {
|
|||||||
} as ISaleInvoiceMailSend);
|
} as ISaleInvoiceMailSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the formatted mail options.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} saleInvoiceId
|
||||||
|
* @param {SendInvoiceMailDTO} messageOptions
|
||||||
|
* @returns {Promise<SaleInvoiceMailOptions>}
|
||||||
|
*/
|
||||||
|
async getFormattedMailOptions(
|
||||||
|
tenantId: number,
|
||||||
|
saleInvoiceId: number,
|
||||||
|
messageOptions: SendInvoiceMailDTO
|
||||||
|
): Promise<SaleInvoiceMailOptions> {
|
||||||
|
const defaultMessageOptions = await this.invoiceMail.getInvoiceMailOptions(
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceId
|
||||||
|
);
|
||||||
|
// Merges message options with default options and parses the options values.
|
||||||
|
const parsedMessageOptions = mergeAndValidateMailOptions(
|
||||||
|
defaultMessageOptions,
|
||||||
|
messageOptions
|
||||||
|
);
|
||||||
|
return this.invoiceMail.formatInvoiceMailOptions(
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceId,
|
||||||
|
parsedMessageOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers the mail invoice.
|
* Triggers the mail invoice.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -62,27 +91,16 @@ export class SendSaleInvoiceMail {
|
|||||||
saleInvoiceId: number,
|
saleInvoiceId: number,
|
||||||
messageOptions: SendInvoiceMailDTO
|
messageOptions: SendInvoiceMailDTO
|
||||||
) {
|
) {
|
||||||
const defaultMessageOptions = await this.invoiceMail.getInvoiceMailOptions(
|
const formattedMessageOptions = await this.getFormattedMailOptions(
|
||||||
tenantId,
|
|
||||||
saleInvoiceId
|
|
||||||
);
|
|
||||||
// Merges message options with default options and parses the options values.
|
|
||||||
const parsedMessageOptions = parseMailOptions(
|
|
||||||
defaultMessageOptions,
|
|
||||||
messageOptions
|
|
||||||
);
|
|
||||||
// Validates the required mail options.
|
|
||||||
validateRequiredMailOptions(parsedMessageOptions);
|
|
||||||
|
|
||||||
const formattedMessageOptions =
|
|
||||||
await this.invoiceMail.formatInvoiceMailOptions(
|
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId,
|
saleInvoiceId,
|
||||||
parsedMessageOptions
|
messageOptions
|
||||||
);
|
);
|
||||||
const mail = new Mail()
|
const mail = new Mail()
|
||||||
.setSubject(formattedMessageOptions.subject)
|
.setSubject(formattedMessageOptions.subject)
|
||||||
.setTo(formattedMessageOptions.to)
|
.setTo(formattedMessageOptions.to)
|
||||||
|
.setCC(formattedMessageOptions.cc)
|
||||||
|
.setBCC(formattedMessageOptions.bcc)
|
||||||
.setContent(formattedMessageOptions.message);
|
.setContent(formattedMessageOptions.message);
|
||||||
|
|
||||||
// Attach invoice document.
|
// Attach invoice document.
|
||||||
@@ -95,7 +113,6 @@ export class SendSaleInvoiceMail {
|
|||||||
{ filename: `${invoiceFilename}.pdf`, content: invoicePdfBuffer },
|
{ filename: `${invoiceFilename}.pdf`, content: invoicePdfBuffer },
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventPayload = {
|
const eventPayload = {
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId,
|
saleInvoiceId,
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
|
|
||||||
export const DEFAULT_INVOICE_MAIL_SUBJECT =
|
export const DEFAULT_INVOICE_MAIL_SUBJECT =
|
||||||
'Invoice {Invoice Number} from {Company Name}';
|
'Invoice {Invoice Number} from {Company Name} for {Customer Name}';
|
||||||
export const DEFAULT_INVOICE_MAIL_CONTENT = `
|
export const DEFAULT_INVOICE_MAIL_CONTENT = `Hi {Customer Name},
|
||||||
<p>Dear {Customer Name}</p>
|
|
||||||
<p>Thank you for your business, You can view or print your invoice from attachements.</p>
|
|
||||||
<p>
|
|
||||||
Invoice <strong>#{Invoice Number}</strong><br />
|
|
||||||
Due Date : <strong>{Invoice Due Date}</strong><br />
|
|
||||||
Amount : <strong>{Invoice Amount}</strong></br />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
Here's invoice # {Invoice Number} for {Invoice Amount}
|
||||||
<i>Regards</i><br />
|
|
||||||
<i>{Company Name}</i>
|
The amount outstanding of {Invoice Due Amount} is due on {Invoice Due Date}.
|
||||||
</p>
|
|
||||||
|
From your online payment page you can print a PDF or view your outstanding bills.
|
||||||
|
|
||||||
|
If you have any questions, please let us know.
|
||||||
|
|
||||||
|
Thanks,
|
||||||
|
{Company Name}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT =
|
export const DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT =
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const transformInvoiceToPdfTemplate = (
|
|||||||
total: invoice.totalFormatted,
|
total: invoice.totalFormatted,
|
||||||
subtotal: invoice.subtotalFormatted,
|
subtotal: invoice.subtotalFormatted,
|
||||||
paymentMade: invoice.paymentAmountFormatted,
|
paymentMade: invoice.paymentAmountFormatted,
|
||||||
balanceDue: invoice.balanceAmountFormatted,
|
dueAmount: invoice.dueAmountFormatted,
|
||||||
|
|
||||||
termsConditions: invoice.termsConditions,
|
termsConditions: invoice.termsConditions,
|
||||||
statement: invoice.invoiceMessage,
|
statement: invoice.invoiceMessage,
|
||||||
|
|||||||
@@ -37,8 +37,12 @@ export class PaymentReceivedBrandingTemplate {
|
|||||||
...defaultPaymentReceivedPdfTemplateAttributes,
|
...defaultPaymentReceivedPdfTemplateAttributes,
|
||||||
...commonOrgBrandingAttrs,
|
...commonOrgBrandingAttrs,
|
||||||
};
|
};
|
||||||
|
const brandingTemplateAttrs = {
|
||||||
|
...template.attributes,
|
||||||
|
companyLogoUri: template.companyLogoUri,
|
||||||
|
};
|
||||||
const attributes = mergePdfTemplateWithDefaultAttributes(
|
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||||
template.attributes,
|
brandingTemplateAttrs,
|
||||||
organizationBrandingAttrs
|
organizationBrandingAttrs
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import {
|
|||||||
} from './constants';
|
} from './constants';
|
||||||
import { GetPaymentReceived } from './GetPaymentReceived';
|
import { GetPaymentReceived } from './GetPaymentReceived';
|
||||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
||||||
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
|
import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils';
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
|
import { transformPaymentReceivedToMailDataArgs } from './utils';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SendPaymentReceiveMailNotification {
|
export class SendPaymentReceiveMailNotification {
|
||||||
@@ -77,15 +78,19 @@ export class SendPaymentReceiveMailNotification {
|
|||||||
.findById(paymentId)
|
.findById(paymentId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const formatterData = await this.textFormatter(tenantId, paymentId);
|
const formatArgs = await this.textFormatter(tenantId, paymentId);
|
||||||
|
|
||||||
return this.contactMailNotification.getMailOptions(
|
const mailOptions =
|
||||||
|
await this.contactMailNotification.getDefaultMailOptions(
|
||||||
tenantId,
|
tenantId,
|
||||||
paymentReceive.customerId,
|
paymentReceive.customerId
|
||||||
DEFAULT_PAYMENT_MAIL_SUBJECT,
|
|
||||||
DEFAULT_PAYMENT_MAIL_CONTENT,
|
|
||||||
formatterData
|
|
||||||
);
|
);
|
||||||
|
return {
|
||||||
|
...mailOptions,
|
||||||
|
subject: DEFAULT_PAYMENT_MAIL_SUBJECT,
|
||||||
|
message: DEFAULT_PAYMENT_MAIL_CONTENT,
|
||||||
|
...formatArgs,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,19 +108,46 @@ export class SendPaymentReceiveMailNotification {
|
|||||||
tenantId,
|
tenantId,
|
||||||
invoiceId
|
invoiceId
|
||||||
);
|
);
|
||||||
return {
|
return transformPaymentReceivedToMailDataArgs(payment);
|
||||||
CustomerName: payment.customer.displayName,
|
|
||||||
PaymentNumber: payment.payment_receive_no,
|
|
||||||
PaymentDate: payment.formattedPaymentDate,
|
|
||||||
PaymentAmount: payment.formattedAmount,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the formatted mail options of the given payment receive.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} paymentReceiveId
|
||||||
|
* @param {SendInvoiceMailDTO} messageDTO
|
||||||
|
* @returns {Promise<PaymentReceiveMailOpts>}
|
||||||
|
*/
|
||||||
|
public getFormattedMailOptions = async (
|
||||||
|
tenantId: number,
|
||||||
|
paymentReceiveId: number,
|
||||||
|
messageDTO: SendInvoiceMailDTO
|
||||||
|
) => {
|
||||||
|
const formatterArgs = await this.textFormatter(tenantId, paymentReceiveId);
|
||||||
|
|
||||||
|
// Default message options.
|
||||||
|
const defaultMessageOpts = await this.getMailOptions(
|
||||||
|
tenantId,
|
||||||
|
paymentReceiveId
|
||||||
|
);
|
||||||
|
// Parsed message opts with default options.
|
||||||
|
const parsedMessageOpts = mergeAndValidateMailOptions(
|
||||||
|
defaultMessageOpts,
|
||||||
|
messageDTO
|
||||||
|
);
|
||||||
|
// Formats the message options.
|
||||||
|
return this.contactMailNotification.formatMailOptions(
|
||||||
|
tenantId,
|
||||||
|
parsedMessageOpts,
|
||||||
|
formatterArgs
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers the mail invoice.
|
* Triggers the mail invoice.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} saleInvoiceId
|
* @param {number} saleInvoiceId - Invoice id.
|
||||||
* @param {SendInvoiceMailDTO} messageDTO
|
* @param {SendInvoiceMailDTO} messageDTO - Message options.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public async sendMail(
|
public async sendMail(
|
||||||
@@ -123,19 +155,35 @@ export class SendPaymentReceiveMailNotification {
|
|||||||
paymentReceiveId: number,
|
paymentReceiveId: number,
|
||||||
messageDTO: SendInvoiceMailDTO
|
messageDTO: SendInvoiceMailDTO
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const defaultMessageOpts = await this.getMailOptions(
|
// Retrieves the formatted mail options.
|
||||||
|
const formattedMessageOptions = await this.getFormattedMailOptions(
|
||||||
tenantId,
|
tenantId,
|
||||||
paymentReceiveId
|
paymentReceiveId,
|
||||||
);
|
|
||||||
// Parsed message opts with default options.
|
|
||||||
const parsedMessageOpts = parseAndValidateMailOptions(
|
|
||||||
defaultMessageOpts,
|
|
||||||
messageDTO
|
messageDTO
|
||||||
);
|
);
|
||||||
await new Mail()
|
const mail = new Mail()
|
||||||
.setSubject(parsedMessageOpts.subject)
|
.setSubject(formattedMessageOptions.subject)
|
||||||
.setTo(parsedMessageOpts.to)
|
.setTo(formattedMessageOptions.to)
|
||||||
.setContent(parsedMessageOpts.body)
|
.setCC(formattedMessageOptions.cc)
|
||||||
.send();
|
.setBCC(formattedMessageOptions.bcc)
|
||||||
|
.setContent(formattedMessageOptions.message);
|
||||||
|
|
||||||
|
const eventPayload = {
|
||||||
|
tenantId,
|
||||||
|
paymentReceiveId,
|
||||||
|
messageOptions: formattedMessageOptions,
|
||||||
|
};
|
||||||
|
// Triggers `onPaymentReceiveMailSend` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.paymentReceive.onMailSend,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
|
await mail.send();
|
||||||
|
|
||||||
|
// Triggers `onPaymentReceiveMailSent` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.paymentReceive.onMailSent,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export const DEFAULT_PAYMENT_MAIL_SUBJECT = 'Payment Received by {CompanyName}';
|
export const DEFAULT_PAYMENT_MAIL_SUBJECT =
|
||||||
|
'Payment Received for {Customer Name} from {Company Name}';
|
||||||
export const DEFAULT_PAYMENT_MAIL_CONTENT = `
|
export const DEFAULT_PAYMENT_MAIL_CONTENT = `
|
||||||
<p>Dear {Customer Name}</p>
|
<p>Dear {Customer Name}</p>
|
||||||
<p>Thank you for your payment. It was a pleasure doing business with you. We look forward to work together again!</p>
|
<p>Thank you for your payment. It was a pleasure doing business with you. We look forward to work together again!</p>
|
||||||
|
|||||||
@@ -21,3 +21,12 @@ export const transformPaymentReceivedToPdfTemplate = (
|
|||||||
customerAddress: contactAddressTextFormat(payment.customer),
|
customerAddress: contactAddressTextFormat(payment.customer),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const transformPaymentReceivedToMailDataArgs = (payment: any) => {
|
||||||
|
return {
|
||||||
|
'Customer Name': payment.customer.displayName,
|
||||||
|
'Payment Number': payment.paymentReceiveNo,
|
||||||
|
'Payment Date': payment.formattedPaymentDate,
|
||||||
|
'Payment Amount': payment.formattedAmount,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -37,8 +37,12 @@ export class SaleReceiptBrandingTemplate {
|
|||||||
...defaultSaleReceiptBrandingAttributes,
|
...defaultSaleReceiptBrandingAttributes,
|
||||||
...commonOrgBrandingAttrs,
|
...commonOrgBrandingAttrs,
|
||||||
};
|
};
|
||||||
|
const brandingTemplateAttrs = {
|
||||||
|
...template.attributes,
|
||||||
|
companyLogoUri: template.companyLogoUri,
|
||||||
|
};
|
||||||
const attributes = mergePdfTemplateWithDefaultAttributes(
|
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||||
template.attributes,
|
brandingTemplateAttrs,
|
||||||
organizationBrandingAttrs
|
organizationBrandingAttrs
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import {
|
|||||||
SaleReceiptMailOptsDTO,
|
SaleReceiptMailOptsDTO,
|
||||||
} from '@/interfaces';
|
} from '@/interfaces';
|
||||||
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
|
||||||
import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
|
import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils';
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
|
import { transformReceiptToMailDataArgs } from './utils';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SaleReceiptMailNotification {
|
export class SaleReceiptMailNotification {
|
||||||
@@ -79,18 +80,19 @@ export class SaleReceiptMailNotification {
|
|||||||
.findById(saleReceiptId)
|
.findById(saleReceiptId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const formattedData = await this.textFormatter(tenantId, saleReceiptId);
|
const formatArgs = await this.textFormatterArgs(tenantId, saleReceiptId);
|
||||||
|
|
||||||
const mailOpts = await this.contactMailNotification.getMailOptions(
|
const mailOptions =
|
||||||
|
await this.contactMailNotification.getDefaultMailOptions(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleReceipt.customerId,
|
saleReceipt.customerId
|
||||||
DEFAULT_RECEIPT_MAIL_SUBJECT,
|
|
||||||
DEFAULT_RECEIPT_MAIL_CONTENT,
|
|
||||||
formattedData
|
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
...mailOpts,
|
...mailOptions,
|
||||||
|
message: DEFAULT_RECEIPT_MAIL_CONTENT,
|
||||||
|
subject: DEFAULT_RECEIPT_MAIL_SUBJECT,
|
||||||
attachReceipt: true,
|
attachReceipt: true,
|
||||||
|
formatArgs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +103,7 @@ export class SaleReceiptMailNotification {
|
|||||||
* @param {string} text - The given text.
|
* @param {string} text - The given text.
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
public textFormatter = async (
|
public textFormatterArgs = async (
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
receiptId: number
|
receiptId: number
|
||||||
): Promise<Record<string, string>> => {
|
): Promise<Record<string, string>> => {
|
||||||
@@ -109,19 +111,66 @@ export class SaleReceiptMailNotification {
|
|||||||
tenantId,
|
tenantId,
|
||||||
receiptId
|
receiptId
|
||||||
);
|
);
|
||||||
return {
|
return transformReceiptToMailDataArgs(receipt);
|
||||||
CustomerName: receipt.customer.displayName,
|
|
||||||
ReceiptNumber: receipt.receiptNumber,
|
|
||||||
ReceiptDate: receipt.formattedReceiptDate,
|
|
||||||
ReceiptAmount: receipt.formattedAmount,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the mail options of the given sale receipt.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} receiptId
|
||||||
|
* @param {SaleReceiptMailOpts} mailOptions
|
||||||
|
* @returns {Promise<SaleReceiptMailOpts>}
|
||||||
|
*/
|
||||||
|
public async formatEstimateMailOptions(
|
||||||
|
tenantId: number,
|
||||||
|
receiptId: number,
|
||||||
|
mailOptions: SaleReceiptMailOpts
|
||||||
|
): Promise<SaleReceiptMailOpts> {
|
||||||
|
const formatterArgs = await this.textFormatterArgs(tenantId, receiptId);
|
||||||
|
const formattedOptions =
|
||||||
|
(await this.contactMailNotification.formatMailOptions(
|
||||||
|
tenantId,
|
||||||
|
mailOptions,
|
||||||
|
formatterArgs
|
||||||
|
)) as SaleReceiptMailOpts;
|
||||||
|
return formattedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the formatted mail options of the given sale receipt.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} saleReceiptId
|
||||||
|
* @param {SaleReceiptMailOptsDTO} messageOpts
|
||||||
|
* @returns {Promise<SaleReceiptMailOpts>}
|
||||||
|
*/
|
||||||
|
public getFormatMailOptions = async (
|
||||||
|
tenantId: number,
|
||||||
|
saleReceiptId: number,
|
||||||
|
messageOpts: SaleReceiptMailOptsDTO
|
||||||
|
): Promise<SaleReceiptMailOpts> => {
|
||||||
|
const defaultMessageOptions = await this.getMailOptions(
|
||||||
|
tenantId,
|
||||||
|
saleReceiptId
|
||||||
|
);
|
||||||
|
// Merges message opts with default options.
|
||||||
|
const parsedMessageOpts = mergeAndValidateMailOptions(
|
||||||
|
defaultMessageOptions,
|
||||||
|
messageOpts
|
||||||
|
) as SaleReceiptMailOpts;
|
||||||
|
|
||||||
|
// Formats the message options.
|
||||||
|
return this.formatEstimateMailOptions(
|
||||||
|
tenantId,
|
||||||
|
saleReceiptId,
|
||||||
|
parsedMessageOpts
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers the mail notification of the given sale receipt.
|
* Triggers the mail notification of the given sale receipt.
|
||||||
* @param {number} tenantId - Tenant id.
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {number} saleReceiptId - Sale receipt id.
|
* @param {number} saleReceiptId - Sale receipt id.
|
||||||
* @param {SaleReceiptMailOpts} messageDTO - Overrided message options.
|
* @param {SaleReceiptMailOpts} messageDTO - message options.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public async sendMail(
|
public async sendMail(
|
||||||
@@ -129,30 +178,43 @@ export class SaleReceiptMailNotification {
|
|||||||
saleReceiptId: number,
|
saleReceiptId: number,
|
||||||
messageOpts: SaleReceiptMailOptsDTO
|
messageOpts: SaleReceiptMailOptsDTO
|
||||||
) {
|
) {
|
||||||
const defaultMessageOpts = await this.getMailOptions(
|
// Formats the message options.
|
||||||
|
const formattedMessageOptions = await this.getFormatMailOptions(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleReceiptId
|
saleReceiptId,
|
||||||
);
|
|
||||||
// Merges message opts with default options.
|
|
||||||
const parsedMessageOpts = parseAndValidateMailOptions(
|
|
||||||
defaultMessageOpts,
|
|
||||||
messageOpts
|
messageOpts
|
||||||
);
|
);
|
||||||
const mail = new Mail()
|
const mail = new Mail()
|
||||||
.setSubject(parsedMessageOpts.subject)
|
.setSubject(formattedMessageOptions.subject)
|
||||||
.setTo(parsedMessageOpts.to)
|
.setTo(formattedMessageOptions.to)
|
||||||
.setContent(parsedMessageOpts.body);
|
.setCC(formattedMessageOptions.cc)
|
||||||
|
.setBCC(formattedMessageOptions.bcc)
|
||||||
|
.setContent(formattedMessageOptions.message);
|
||||||
|
|
||||||
if (parsedMessageOpts.attachReceipt) {
|
// Attaches the receipt pdf document.
|
||||||
|
if (formattedMessageOptions.attachReceipt) {
|
||||||
// Retrieves document buffer of the receipt pdf document.
|
// Retrieves document buffer of the receipt pdf document.
|
||||||
const receiptPdfBuffer = await this.receiptPdfService.saleReceiptPdf(
|
const [receiptPdfBuffer, filename] =
|
||||||
tenantId,
|
await this.receiptPdfService.saleReceiptPdf(tenantId, saleReceiptId);
|
||||||
saleReceiptId
|
|
||||||
);
|
|
||||||
mail.setAttachments([
|
mail.setAttachments([
|
||||||
{ filename: 'receipt.pdf', content: receiptPdfBuffer },
|
{ filename: `${filename}.pdf`, content: receiptPdfBuffer },
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
const eventPayload = {
|
||||||
|
tenantId,
|
||||||
|
saleReceiptId,
|
||||||
|
messageOptions: {},
|
||||||
|
};
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleReceipt.onMailSend,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
await mail.send();
|
await mail.send();
|
||||||
|
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleReceipt.onMailSent,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ export class SaleReceiptsPdf {
|
|||||||
* @param {number} saleInvoiceId -
|
* @param {number} saleInvoiceId -
|
||||||
* @returns {Promise<Buffer>}
|
* @returns {Promise<Buffer>}
|
||||||
*/
|
*/
|
||||||
public async saleReceiptPdf(tenantId: number, saleReceiptId: number) {
|
public async saleReceiptPdf(
|
||||||
|
tenantId: number,
|
||||||
|
saleReceiptId: number
|
||||||
|
): Promise<[Buffer, string]> {
|
||||||
const filename = await this.getSaleReceiptFilename(tenantId, saleReceiptId);
|
const filename = await this.getSaleReceiptFilename(tenantId, saleReceiptId);
|
||||||
|
|
||||||
const brandingAttributes = await this.getReceiptBrandingAttributes(
|
const brandingAttributes = await this.getReceiptBrandingAttributes(
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { ISaleReceipt, ISaleReceiptBrandingTemplateAttributes } from "@/interfaces";
|
import {
|
||||||
import { contactAddressTextFormat } from "@/utils/address-text-format";
|
ISaleReceipt,
|
||||||
|
ISaleReceiptBrandingTemplateAttributes,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import { contactAddressTextFormat } from '@/utils/address-text-format';
|
||||||
|
|
||||||
|
export const transformReceiptToBrandingTemplateAttributes = (
|
||||||
|
saleReceipt: ISaleReceipt
|
||||||
export const transformReceiptToBrandingTemplateAttributes = (saleReceipt: ISaleReceipt): Partial<ISaleReceiptBrandingTemplateAttributes> => {
|
): Partial<ISaleReceiptBrandingTemplateAttributes> => {
|
||||||
return {
|
return {
|
||||||
total: saleReceipt.formattedAmount,
|
total: saleReceipt.formattedAmount,
|
||||||
subtotal: saleReceipt.formattedSubtotal,
|
subtotal: saleReceipt.formattedSubtotal,
|
||||||
@@ -18,4 +21,13 @@ export const transformReceiptToBrandingTemplateAttributes = (saleReceipt: ISaleR
|
|||||||
receiptDate: saleReceipt.formattedReceiptDate,
|
receiptDate: saleReceipt.formattedReceiptDate,
|
||||||
customerAddress: contactAddressTextFormat(saleReceipt.customer),
|
customerAddress: contactAddressTextFormat(saleReceipt.customer),
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export const transformReceiptToMailDataArgs = (saleReceipt: any) => {
|
||||||
|
return {
|
||||||
|
'Customer Name': saleReceipt.customer.displayName,
|
||||||
|
'Receipt Number': saleReceipt.receiptNumber,
|
||||||
|
'Receipt Date': saleReceipt.formattedReceiptDate,
|
||||||
|
'Receipt Amount': saleReceipt.formattedAmount,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ export default {
|
|||||||
* Sales estimates service.
|
* Sales estimates service.
|
||||||
*/
|
*/
|
||||||
saleEstimate: {
|
saleEstimate: {
|
||||||
|
onViewed: 'onSaleEstimateViewed',
|
||||||
onPdfViewed: 'onSaleEstimatePdfViewed',
|
onPdfViewed: 'onSaleEstimatePdfViewed',
|
||||||
|
|
||||||
onCreating: 'onSaleEstimateCreating',
|
onCreating: 'onSaleEstimateCreating',
|
||||||
@@ -212,7 +213,7 @@ export default {
|
|||||||
|
|
||||||
onPreMailSend: 'onSaleEstimatePreMailSend',
|
onPreMailSend: 'onSaleEstimatePreMailSend',
|
||||||
onMailSend: 'onSaleEstimateMailSend',
|
onMailSend: 'onSaleEstimateMailSend',
|
||||||
onMailSent: 'onSaleEstimateMailSend',
|
onMailSent: 'onSaleEstimateMailSent',
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -751,4 +752,23 @@ export default {
|
|||||||
onCheckoutSessionCompleted: 'onStripeCheckoutSessionCompleted',
|
onCheckoutSessionCompleted: 'onStripeCheckoutSessionCompleted',
|
||||||
onAccountUpdated: 'onStripeAccountUpdated',
|
onAccountUpdated: 'onStripeAccountUpdated',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Reports
|
||||||
|
reports: {
|
||||||
|
onBalanceSheetViewed: 'onBalanceSheetViewed',
|
||||||
|
onTrialBalanceSheetView: 'onTrialBalanceSheetViewed',
|
||||||
|
onProfitLossSheetViewed: 'onProfitLossSheetViewed',
|
||||||
|
onCashflowStatementViewed: 'onCashflowStatementViewed',
|
||||||
|
onGeneralLedgerViewed: 'onGeneralLedgerViewed',
|
||||||
|
onJournalViewed: 'onJounralViewed',
|
||||||
|
onReceivableAgingViewed: 'onReceivableAgingViewed',
|
||||||
|
onPayableAgingViewed: 'onPayableAgingViewed',
|
||||||
|
onCustomerBalanceSummaryViewed: 'onInventoryValuationViewed',
|
||||||
|
onVendorBalanceSummaryViewed: 'onVendorBalanceSummaryViewed',
|
||||||
|
onInventoryValuationViewed: 'onCustomerBalanceSummaryViewed',
|
||||||
|
onCustomerTransactionsViewed: 'onCustomerTransactionsViewed',
|
||||||
|
onVendorTransactionsViewed: 'onVendorTransactionsViewed',
|
||||||
|
onSalesByItemViewed: 'onSalesByItemViewed',
|
||||||
|
onPurchasesByItemViewed: 'onPurchasesByItemViewed',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bigcapital/utils": "*",
|
"@bigcapital/utils": "*",
|
||||||
"@blueprintjs-formik/core": "^0.3.6",
|
"@bigcapital/pdf-templates": "*",
|
||||||
|
"@blueprintjs-formik/core": "^0.3.7",
|
||||||
"@blueprintjs-formik/datetime": "^0.3.7",
|
"@blueprintjs-formik/datetime": "^0.3.7",
|
||||||
"@blueprintjs-formik/select": "^0.3.5",
|
"@blueprintjs-formik/select": "^0.3.5",
|
||||||
"@blueprintjs/colors": "4.1.19",
|
"@blueprintjs/colors": "4.1.19",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import { BrandingTemplatesDrawer } from '@/containers/BrandingTemplates/Branding
|
|||||||
|
|
||||||
import { DRAWERS } from '@/constants/drawers';
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
import { InvoiceSendMailDrawer } from '@/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailDrawer';
|
import { InvoiceSendMailDrawer } from '@/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailDrawer';
|
||||||
|
import { NewSubscriptionDrawer } from '@/containers/Subscriptions/drawers/NewSubscriptionDrawer/NewSubscriptionDrawer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drawers container of the dashboard.
|
* Drawers container of the dashboard.
|
||||||
@@ -72,6 +73,7 @@ export default function DrawersContainer() {
|
|||||||
<TaxRateDetailsDrawer name={DRAWERS.TAX_RATE_DETAILS} />
|
<TaxRateDetailsDrawer name={DRAWERS.TAX_RATE_DETAILS} />
|
||||||
<CategorizeTransactionDrawer name={DRAWERS.CATEGORIZE_TRANSACTION} />
|
<CategorizeTransactionDrawer name={DRAWERS.CATEGORIZE_TRANSACTION} />
|
||||||
<ChangeSubscriptionPlanDrawer name={DRAWERS.CHANGE_SUBSCARIPTION_PLAN} />
|
<ChangeSubscriptionPlanDrawer name={DRAWERS.CHANGE_SUBSCARIPTION_PLAN} />
|
||||||
|
<NewSubscriptionDrawer name={DRAWERS.NEW_SUBSCRIPTION_PLANS} />
|
||||||
<InvoiceCustomizeDrawer name={DRAWERS.INVOICE_CUSTOMIZE} />
|
<InvoiceCustomizeDrawer name={DRAWERS.INVOICE_CUSTOMIZE} />
|
||||||
<EstimateCustomizeDrawer name={DRAWERS.ESTIMATE_CUSTOMIZE} />
|
<EstimateCustomizeDrawer name={DRAWERS.ESTIMATE_CUSTOMIZE} />
|
||||||
<ReceiptCustomizeDrawer name={DRAWERS.RECEIPT_CUSTOMIZE} />
|
<ReceiptCustomizeDrawer name={DRAWERS.RECEIPT_CUSTOMIZE} />
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export enum DRAWERS {
|
|||||||
TAX_RATE_DETAILS = 'tax-rate-detail-drawer',
|
TAX_RATE_DETAILS = 'tax-rate-detail-drawer',
|
||||||
CATEGORIZE_TRANSACTION = 'categorize-transaction',
|
CATEGORIZE_TRANSACTION = 'categorize-transaction',
|
||||||
CHANGE_SUBSCARIPTION_PLAN = 'change-subscription-plan',
|
CHANGE_SUBSCARIPTION_PLAN = 'change-subscription-plan',
|
||||||
|
NEW_SUBSCRIPTION_PLANS = 'NEW_SUBSCRIPTION_PLANS',
|
||||||
INVOICE_CUSTOMIZE = 'INVOICE_CUSTOMIZE',
|
INVOICE_CUSTOMIZE = 'INVOICE_CUSTOMIZE',
|
||||||
ESTIMATE_CUSTOMIZE = 'ESTIMATE_CUSTOMIZE',
|
ESTIMATE_CUSTOMIZE = 'ESTIMATE_CUSTOMIZE',
|
||||||
PAYMENT_RECEIPT_CUSTOMIZE = 'PAYMENT_RECEIPT_CUSTOMIZE',
|
PAYMENT_RECEIPT_CUSTOMIZE = 'PAYMENT_RECEIPT_CUSTOMIZE',
|
||||||
@@ -34,5 +35,5 @@ export enum DRAWERS {
|
|||||||
BRANDING_TEMPLATES = 'BRANDING_TEMPLATES',
|
BRANDING_TEMPLATES = 'BRANDING_TEMPLATES',
|
||||||
PAYMENT_INVOICE_PREVIEW = 'PAYMENT_INVOICE_PREVIEW',
|
PAYMENT_INVOICE_PREVIEW = 'PAYMENT_INVOICE_PREVIEW',
|
||||||
STRIPE_PAYMENT_INTEGRATION_EDIT = 'STRIPE_PAYMENT_INTEGRATION_EDIT',
|
STRIPE_PAYMENT_INTEGRATION_EDIT = 'STRIPE_PAYMENT_INTEGRATION_EDIT',
|
||||||
INVOICE_SEND_MAIL = 'INVOICE_SEND_MAIL'
|
INVOICE_SEND_MAIL = 'INVOICE_SEND_MAIL',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Formik, Form } from 'formik';
|
|||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { isEmpty, omit } from 'lodash';
|
import { sumBy, round, isEmpty, omit } from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
@@ -88,15 +88,17 @@ function MakeJournalEntriesForm({
|
|||||||
const entries = values.entries.filter(
|
const entries = values.entries.filter(
|
||||||
(entry) => entry.debit || entry.credit,
|
(entry) => entry.debit || entry.credit,
|
||||||
);
|
);
|
||||||
|
// Updated getTotal function using lodash
|
||||||
const getTotal = (type = 'credit') => {
|
const getTotal = (type = 'credit') => {
|
||||||
return entries.reduce((total, item) => {
|
return round(
|
||||||
return item[type] ? item[type] + total : total;
|
sumBy(entries, (entry) => parseFloat(entry[type] || 0)),
|
||||||
}, 0);
|
2,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
const totalCredit = getTotal('credit');
|
const totalCredit = getTotal('credit');
|
||||||
const totalDebit = getTotal('debit');
|
const totalDebit = getTotal('debit');
|
||||||
|
|
||||||
// Validate the total credit should be eqials total debit.
|
// Validate the total credit should be equals total debit.
|
||||||
if (totalCredit !== totalDebit) {
|
if (totalCredit !== totalDebit) {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: intl.get('should_total_of_credit_and_debit_be_equal'),
|
message: intl.get('should_total_of_credit_and_debit_be_equal'),
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ function LoginFooterLinks() {
|
|||||||
)}
|
)}
|
||||||
<AuthFooterLink>
|
<AuthFooterLink>
|
||||||
<Link to={'/auth/send_reset_password'}>
|
<Link to={'/auth/send_reset_password'}>
|
||||||
<T id={'forget_my_password'} />
|
<T id={'forgot_my_password'} />
|
||||||
</Link>
|
</Link>
|
||||||
</AuthFooterLink>
|
</AuthFooterLink>
|
||||||
</AuthFooterLinks>
|
</AuthFooterLinks>
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ function RegisterFooterLinks() {
|
|||||||
|
|
||||||
<AuthFooterLink>
|
<AuthFooterLink>
|
||||||
<Link to={'/auth/send_reset_password'}>
|
<Link to={'/auth/send_reset_password'}>
|
||||||
<T id={'forget_my_password'} />
|
<T id={'forgot_my_password'} />
|
||||||
</Link>
|
</Link>
|
||||||
</AuthFooterLink>
|
</AuthFooterLink>
|
||||||
</AuthFooterLinks>
|
</AuthFooterLinks>
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export const useBrandingTemplateFormInitialValues = <
|
|||||||
|
|
||||||
const brandingAttributes = {
|
const brandingAttributes = {
|
||||||
templateName: pdfTemplate?.templateName,
|
templateName: pdfTemplate?.templateName,
|
||||||
|
companyLogoUri: pdfTemplate?.companyLogoUri,
|
||||||
...pdfTemplate?.attributes,
|
...pdfTemplate?.attributes,
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
@@ -56,14 +57,16 @@ export const useBrandingTemplateFormInitialValues = <
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useBrandingState = (state?: Partial<BrandingState>): BrandingState => {
|
export const useBrandingState = (
|
||||||
|
state?: Partial<BrandingState>,
|
||||||
|
): BrandingState => {
|
||||||
const { brandingTemplateState } = useBrandingTemplateBoot();
|
const { brandingTemplateState } = useBrandingTemplateBoot();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...brandingTemplateState,
|
...brandingTemplateState,
|
||||||
...state
|
...state,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getCustomizeDrawerNameFromResource = (resource: string) => {
|
export const getCustomizeDrawerNameFromResource = (resource: string) => {
|
||||||
const pairs = {
|
const pairs = {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ function InvoiceDetailActionsBar({
|
|||||||
openAlert,
|
openAlert,
|
||||||
|
|
||||||
// #withDrawerActions
|
// #withDrawerActions
|
||||||
|
openDrawer,
|
||||||
closeDrawer,
|
closeDrawer,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -102,8 +103,9 @@ function InvoiceDetailActionsBar({
|
|||||||
openAlert('cancel-bad-debt', { invoiceId });
|
openAlert('cancel-bad-debt', { invoiceId });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// handle send mail button click.
|
||||||
const handleMailInvoice = () => {
|
const handleMailInvoice = () => {
|
||||||
openDialog(DialogsName.InvoiceMail, { invoiceId });
|
openDrawer(DRAWERS.INVOICE_SEND_MAIL, { invoiceId });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleShareButtonClick = () => {
|
const handleShareButtonClick = () => {
|
||||||
|
|||||||
@@ -117,7 +117,6 @@ export function InvoicePaymentPage({
|
|||||||
|
|
||||||
// # Copyright
|
// # Copyright
|
||||||
copyrightText = `© 2024 Bigcapital Technology, Inc. <br /> All rights reserved.`,
|
copyrightText = `© 2024 Bigcapital Technology, Inc. <br /> All rights reserved.`,
|
||||||
|
|
||||||
classNames,
|
classNames,
|
||||||
}: PaymentPageProps) {
|
}: PaymentPageProps) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
|
||||||
width: 600px;
|
width: 600px;
|
||||||
// margin: 40px auto;
|
|
||||||
color: #222;
|
color: #222;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export function PaymentPortal() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className={styles.root}>
|
<Box className={styles.root} my={'40px'} mx={'auto'}>
|
||||||
<Stack spacing={0} className={styles.body}>
|
<Stack spacing={0} className={styles.body}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Group spacing={10}>
|
<Group spacing={10}>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ function EstimateMailDialogFormRoot({
|
|||||||
closeDialog,
|
closeDialog,
|
||||||
}) {
|
}) {
|
||||||
const { mutateAsync: sendEstimateMail } = useSendSaleEstimateMail();
|
const { mutateAsync: sendEstimateMail } = useSendSaleEstimateMail();
|
||||||
const { mailOptions, saleEstimateId, redirectToEstimatesList } =
|
const { mailOptions, saleEstimateId, } =
|
||||||
useEstimateMailDialogBoot();
|
useEstimateMailDialogBoot();
|
||||||
|
|
||||||
const initialValues = transformMailFormToInitialValues(
|
const initialValues = transformMailFormToInitialValues(
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ export function EstimateMailDialogFormContent({
|
|||||||
<Form>
|
<Form>
|
||||||
<div className={Classes.DIALOG_BODY}>
|
<div className={Classes.DIALOG_BODY}>
|
||||||
<MailNotificationForm
|
<MailNotificationForm
|
||||||
fromAddresses={mailOptions.from_addresses}
|
fromAddresses={mailOptions.from_options}
|
||||||
toAddresses={mailOptions.to_addresses}
|
toAddresses={mailOptions.to_options}
|
||||||
/>
|
/>
|
||||||
<AttachFormGroup name={'attachEstimate'} inline>
|
<AttachFormGroup name={'attachEstimate'} inline>
|
||||||
<FSwitch name={'attachEstimate'} label={'Attach Estimate'} />
|
<FSwitch name={'attachEstimate'} label={'Attach Estimate'} />
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ export function InvoiceMailReceipt({
|
|||||||
h="90px"
|
h="90px"
|
||||||
w="90px"
|
w="90px"
|
||||||
mx="auto"
|
mx="auto"
|
||||||
|
borderRadius="3px"
|
||||||
backgroundRepeat="no-repeat"
|
backgroundRepeat="no-repeat"
|
||||||
backgroundPosition="center center"
|
backgroundPosition="center center"
|
||||||
backgroundSize="contain"
|
backgroundSize="contain"
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
import { useMemo } from 'react';
|
|
||||||
import { css } from '@emotion/css';
|
|
||||||
import { Box, } from '@/components';
|
|
||||||
import { InvoiceMailReceiptPreview } from '../InvoiceCustomize/InvoiceMailReceiptPreview';
|
|
||||||
import { useInvoiceSendMailBoot } from './InvoiceSendMailContentBoot';
|
|
||||||
import { InvoiceSendMailPreviewWithHeader } from './InvoiceSendMailHeaderPreview';
|
|
||||||
import { useSendInvoiceMailMessage } from './_hooks';
|
|
||||||
|
|
||||||
export function InvoiceMailReceiptPreviewConneceted() {
|
|
||||||
const mailMessage = useSendInvoiceMailMessage();
|
|
||||||
const { invoiceMailState } = useInvoiceSendMailBoot();
|
|
||||||
|
|
||||||
const items = useMemo(
|
|
||||||
() =>
|
|
||||||
invoiceMailState?.entries?.map((entry: any) => ({
|
|
||||||
quantity: entry.quantity,
|
|
||||||
total: entry.totalFormatted,
|
|
||||||
label: entry.name,
|
|
||||||
})),
|
|
||||||
[invoiceMailState?.entries],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<InvoiceSendMailPreviewWithHeader>
|
|
||||||
<Box px={4} pt={8} pb={16}>
|
|
||||||
<InvoiceMailReceiptPreview
|
|
||||||
companyName={invoiceMailState?.companyName}
|
|
||||||
// companyLogoUri={invoiceMailState?.companyLogoUri}
|
|
||||||
|
|
||||||
primaryColor={invoiceMailState?.primaryColor}
|
|
||||||
total={invoiceMailState?.totalFormatted}
|
|
||||||
dueDate={invoiceMailState?.dueDateFormatted}
|
|
||||||
dueAmount={invoiceMailState?.dueAmountFormatted}
|
|
||||||
|
|
||||||
invoiceNumber={invoiceMailState?.invoiceNo}
|
|
||||||
items={items}
|
|
||||||
message={mailMessage}
|
|
||||||
className={css`
|
|
||||||
margin: 0 auto;
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</InvoiceSendMailPreviewWithHeader>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { useMemo, ComponentType } from 'react';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { Box } from '@/components';
|
||||||
|
import {
|
||||||
|
InvoiceMailReceiptPreview,
|
||||||
|
InvoiceMailReceiptPreviewProps,
|
||||||
|
} from '../InvoiceCustomize/InvoiceMailReceiptPreview';
|
||||||
|
import { useInvoiceSendMailBoot } from './InvoiceSendMailContentBoot';
|
||||||
|
import { InvoiceSendMailPreviewWithHeader } from './InvoiceSendMailHeaderPreview';
|
||||||
|
import { useSendInvoiceMailMessage } from './_hooks';
|
||||||
|
|
||||||
|
export function InvoiceMailReceiptPreviewConnected() {
|
||||||
|
return (
|
||||||
|
<InvoiceSendMailPreviewWithHeader>
|
||||||
|
<Box px={4} pt={8} pb={16}>
|
||||||
|
<InvoiceMailReceiptPreviewWithProps
|
||||||
|
className={css`
|
||||||
|
margin: 0 auto;
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</InvoiceSendMailPreviewWithHeader>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects props from invoice mail state into the InvoiceMailReceiptPreview component.
|
||||||
|
*/
|
||||||
|
const withInvoiceMailReceiptPreviewProps = <
|
||||||
|
P extends InvoiceMailReceiptPreviewProps,
|
||||||
|
>(
|
||||||
|
WrappedComponent: ComponentType<P & InvoiceMailReceiptPreviewProps>,
|
||||||
|
) => {
|
||||||
|
return function WithInvoiceMailReceiptPreviewProps(props: P) {
|
||||||
|
const message = useSendInvoiceMailMessage();
|
||||||
|
const { invoiceMailState } = useInvoiceSendMailBoot();
|
||||||
|
|
||||||
|
const items = useMemo(
|
||||||
|
() =>
|
||||||
|
invoiceMailState?.entries?.map((entry: any) => ({
|
||||||
|
quantity: entry.quantity,
|
||||||
|
total: entry.totalFormatted,
|
||||||
|
label: entry.name,
|
||||||
|
})),
|
||||||
|
[invoiceMailState?.entries],
|
||||||
|
);
|
||||||
|
|
||||||
|
const mailReceiptPreviewProps = {
|
||||||
|
companyName: invoiceMailState?.companyName,
|
||||||
|
companyLogoUri: invoiceMailState?.companyLogoUri,
|
||||||
|
primaryColor: invoiceMailState?.primaryColor,
|
||||||
|
total: invoiceMailState?.totalFormatted,
|
||||||
|
dueDate: invoiceMailState?.dueDateFormatted,
|
||||||
|
dueAmount: invoiceMailState?.dueAmountFormatted,
|
||||||
|
invoiceNumber: invoiceMailState?.invoiceNo,
|
||||||
|
items,
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
return <WrappedComponent {...mailReceiptPreviewProps} {...props} />;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InvoiceMailReceiptPreviewWithProps =
|
||||||
|
withInvoiceMailReceiptPreviewProps(InvoiceMailReceiptPreview);
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
import { Spinner } from '@blueprintjs/core';
|
import { Spinner } from '@blueprintjs/core';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { Drawer, DrawerSuspense } from '@/components';
|
import { Drawer, DrawerSuspense } from '@/components';
|
||||||
import withDrawers from '@/containers/Drawer/withDrawers';
|
import withDrawers from '@/containers/Drawer/withDrawers';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const InvoiceSendMailContent = React.lazy(() =>
|
const InvoiceSendMailContent = React.lazy(() =>
|
||||||
import('./InvoiceSendMailContent').then((module) => ({
|
import('./InvoiceSendMailContent').then((module) => ({
|
||||||
|
|||||||
@@ -18,9 +18,14 @@ import {
|
|||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
import { useDrawerActions } from '@/hooks/state';
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import { useInvoiceMailItems, useSendInvoiceFormatArgsOptions } from './_hooks';
|
import { useInvoiceMailItems, useSendInvoiceFormatArgsOptions } from './_hooks';
|
||||||
|
import { InvoiceSendMailFormValues } from './_types';
|
||||||
|
|
||||||
// Create new account renderer.
|
// Create new account renderer.
|
||||||
const createNewItemRenderer = (query, active, handleClick) => {
|
const createNewItemRenderer = (
|
||||||
|
query: string,
|
||||||
|
active: boolean,
|
||||||
|
handleClick: React.MouseEventHandler<HTMLElement>,
|
||||||
|
) => {
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon="add"
|
icon="add"
|
||||||
@@ -32,7 +37,7 @@ const createNewItemRenderer = (query, active, handleClick) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create new item from the given query string.
|
// Create new item from the given query string.
|
||||||
const createNewItemFromQuery = (name) => ({ name });
|
const createNewItemFromQuery = (text: string): SelectOptionProps => ({ text });
|
||||||
|
|
||||||
const styleEmailButton = css`
|
const styleEmailButton = css`
|
||||||
&.bp4-button.bp4-small {
|
&.bp4-button.bp4-small {
|
||||||
@@ -62,19 +67,19 @@ export function InvoiceSendMailFields() {
|
|||||||
const [showCCField, setShowCCField] = useState<boolean>(false);
|
const [showCCField, setShowCCField] = useState<boolean>(false);
|
||||||
const [showBccField, setShowBccField] = useState<boolean>(false);
|
const [showBccField, setShowBccField] = useState<boolean>(false);
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
const { values, setFieldValue } =
|
||||||
const { values, setFieldValue } = useFormikContext();
|
useFormikContext<InvoiceSendMailFormValues>();
|
||||||
const items = useInvoiceMailItems();
|
const items = useInvoiceMailItems();
|
||||||
const argsOptions = useSendInvoiceFormatArgsOptions();
|
const argsOptions = useSendInvoiceFormatArgsOptions();
|
||||||
|
|
||||||
const handleClickCcBtn = (event) => {
|
const handleClickCcBtn = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
setShowCCField(true);
|
setShowCCField(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClickBccBtn = (event) => {
|
const handleClickBccBtn = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
@@ -82,17 +87,19 @@ export function InvoiceSendMailFields() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateToItemSelect = (value: SelectOptionProps) => {
|
const handleCreateToItemSelect = (value: SelectOptionProps) => {
|
||||||
setFieldValue('to', [...values?.to, value?.name]);
|
setFieldValue('to', [...values?.to, value?.text]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateCcItemSelect = (value: SelectOptionProps) => {
|
const handleCreateCcItemSelect = (value: SelectOptionProps) => {
|
||||||
setFieldValue('cc', [...values?.cc, value?.name]);
|
setFieldValue('cc', [...values?.cc, value?.text]);
|
||||||
};
|
|
||||||
const handleCreateBccItemSelect = (value: SelectOptionProps) => {
|
|
||||||
setFieldValue('bcc', [...values?.bcc, value?.name]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const rightElementsToField = useMemo(() => (
|
const handleCreateBccItemSelect = (value: SelectOptionProps) => {
|
||||||
|
setFieldValue('bcc', [...values?.bcc, value?.text]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const rightElementsToField = useMemo(
|
||||||
|
() => (
|
||||||
<Group
|
<Group
|
||||||
spacing={0}
|
spacing={0}
|
||||||
paddingRight={'7px'}
|
paddingRight={'7px'}
|
||||||
@@ -118,9 +125,12 @@ export function InvoiceSendMailFields() {
|
|||||||
BCC
|
BCC
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
), []);
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const handleTextareaChange = useCallback((item: SelectOptionProps) => {
|
const handleTextareaChange = useCallback(
|
||||||
|
(item: SelectOptionProps) => {
|
||||||
const textarea = textareaRef.current;
|
const textarea = textareaRef.current;
|
||||||
if (!textarea) return;
|
if (!textarea) return;
|
||||||
|
|
||||||
@@ -139,12 +149,22 @@ export function InvoiceSendMailFields() {
|
|||||||
selectionStart + insertText.length;
|
selectionStart + insertText.length;
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
}, 0);
|
}, 0);
|
||||||
}, [setFieldValue]);
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTagInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
// Prevent the form from submitting when the user presses the Enter key
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
bg="white"
|
bg="white"
|
||||||
flex={'1'}
|
flex={'1'}
|
||||||
|
maxWidth="720px"
|
||||||
maxHeight="100%"
|
maxHeight="100%"
|
||||||
spacing={0}
|
spacing={0}
|
||||||
borderRight="1px solid #dcdcdd"
|
borderRight="1px solid #dcdcdd"
|
||||||
@@ -161,6 +181,9 @@ export function InvoiceSendMailFields() {
|
|||||||
tagProps: { round: true, minimal: true, large: true },
|
tagProps: { round: true, minimal: true, large: true },
|
||||||
rightElement: rightElementsToField,
|
rightElement: rightElementsToField,
|
||||||
large: true,
|
large: true,
|
||||||
|
inputProps: {
|
||||||
|
onKeyDown: handleTagInputKeyDown,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
createNewItemRenderer={createNewItemRenderer}
|
createNewItemRenderer={createNewItemRenderer}
|
||||||
createNewItemFromQuery={createNewItemFromQuery}
|
createNewItemFromQuery={createNewItemFromQuery}
|
||||||
@@ -179,6 +202,9 @@ export function InvoiceSendMailFields() {
|
|||||||
tagInputProps={{
|
tagInputProps={{
|
||||||
tagProps: { round: true, minimal: true, large: true },
|
tagProps: { round: true, minimal: true, large: true },
|
||||||
large: true,
|
large: true,
|
||||||
|
inputProps: {
|
||||||
|
onKeyDown: handleTagInputKeyDown,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
createNewItemRenderer={createNewItemRenderer}
|
createNewItemRenderer={createNewItemRenderer}
|
||||||
createNewItemFromQuery={createNewItemFromQuery}
|
createNewItemFromQuery={createNewItemFromQuery}
|
||||||
@@ -198,6 +224,9 @@ export function InvoiceSendMailFields() {
|
|||||||
tagInputProps={{
|
tagInputProps={{
|
||||||
tagProps: { round: true, minimal: true, large: true },
|
tagProps: { round: true, minimal: true, large: true },
|
||||||
large: true,
|
large: true,
|
||||||
|
inputProps: {
|
||||||
|
onKeyDown: handleTagInputKeyDown,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
createNewItemRenderer={createNewItemRenderer}
|
createNewItemRenderer={createNewItemRenderer}
|
||||||
createNewItemFromQuery={createNewItemFromQuery}
|
createNewItemFromQuery={createNewItemFromQuery}
|
||||||
@@ -231,8 +260,11 @@ export function InvoiceSendMailFields() {
|
|||||||
fill: false,
|
fill: false,
|
||||||
position: Position.BOTTOM_LEFT,
|
position: Position.BOTTOM_LEFT,
|
||||||
minimal: true,
|
minimal: true,
|
||||||
|
inputProps: {
|
||||||
|
onKeyDown: handleTagInputKeyDown,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
input={({ activeItem, text, label, value }) => (
|
input={() => (
|
||||||
<Button
|
<Button
|
||||||
minimal
|
minimal
|
||||||
rightIcon={
|
rightIcon={
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ export const InvoiceSendMailFormSchema = Yup.object().shape({
|
|||||||
subject: Yup.string().required('Subject is required'),
|
subject: Yup.string().required('Subject is required'),
|
||||||
message: Yup.string().required('Message is required'),
|
message: Yup.string().required('Message is required'),
|
||||||
to: Yup.array()
|
to: Yup.array()
|
||||||
.of(Yup.string().email('Invalid email'))
|
.of(Yup.string().email('Invalid email address'))
|
||||||
.required('To address is required'),
|
.required('To address is required'),
|
||||||
cc: Yup.array().of(Yup.string().email('Invalid email')),
|
cc: Yup.array().of(Yup.string().email('Invalid email address')),
|
||||||
bcc: Yup.array().of(Yup.string().email('Invalid email')),
|
bcc: Yup.array().of(Yup.string().email('Invalid email address')),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export function InvoiceSendMailHeaderPreview() {
|
|||||||
const mailSubject = useSendInvoiceMailSubject();
|
const mailSubject = useSendInvoiceMailSubject();
|
||||||
const { invoiceMailState } = useInvoiceSendMailBoot();
|
const { invoiceMailState } = useInvoiceSendMailBoot();
|
||||||
const toAddresses = useMailHeaderToAddresses();
|
const toAddresses = useMailHeaderToAddresses();
|
||||||
|
const fromAddresses = useMailHeaderFromAddresses();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
@@ -44,14 +45,12 @@ export function InvoiceSendMailHeaderPreview() {
|
|||||||
|
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<Group spacing={2}>
|
<Group spacing={2}>
|
||||||
<Box fontWeight={600}>Ahmed </Box>
|
<Box fontWeight={600}>{invoiceMailState?.companyName} </Box>
|
||||||
<Box color={'#738091'}>
|
<Box color={'#738091'}>{fromAddresses}</Box>
|
||||||
<messaging-service@post.bigcapital.app>
|
|
||||||
</Box>
|
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Box fontSize={'sm'} color={'#738091'}>
|
<Box fontSize={'sm'} color={'#738091'}>
|
||||||
Reply to: {invoiceMailState?.companyName} {toAddresses};
|
Send to: {invoiceMailState?.customerName} {toAddresses};
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -80,3 +79,13 @@ export const useMailHeaderToAddresses = () => {
|
|||||||
|
|
||||||
return useMemo(() => to?.map((email) => '<' + email + '>').join(' '), [to]);
|
return useMemo(() => to?.map((email) => '<' + email + '>').join(' '), [to]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useMailHeaderFromAddresses = () => {
|
||||||
|
const { invoiceMailState } = useInvoiceSendMailBoot();
|
||||||
|
const from = invoiceMailState?.from;
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => from?.map((email) => '<' + email + '>').join(', '),
|
||||||
|
[from],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { css } from '@emotion/css';
|
|||||||
import { Tab, Tabs } from '@blueprintjs/core';
|
import { Tab, Tabs } from '@blueprintjs/core';
|
||||||
import { Stack } from '@/components';
|
import { Stack } from '@/components';
|
||||||
|
|
||||||
const InvoiceMailReceiptPreviewConneceted = lazy(() =>
|
const InvoiceMailReceiptPreviewConnected = lazy(() =>
|
||||||
import('./InvoiceMailReceiptPreviewConnected.').then((module) => ({
|
import('./InvoiceMailReceiptPreviewConnected').then((module) => ({
|
||||||
default: module.InvoiceMailReceiptPreviewConneceted,
|
default: module.InvoiceMailReceiptPreviewConnected,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
const InvoiceSendPdfPreviewConnected = lazy(() =>
|
const InvoiceSendPdfPreviewConnected = lazy(() =>
|
||||||
@@ -51,7 +51,7 @@ export function InvoiceSendMailPreview() {
|
|||||||
title={'Payment page'}
|
title={'Payment page'}
|
||||||
panel={
|
panel={
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<InvoiceMailReceiptPreviewConneceted />
|
<InvoiceMailReceiptPreviewConnected />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,18 +1,41 @@
|
|||||||
|
import { Spinner } from '@blueprintjs/core';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { Box } from '@/components';
|
import { Stack } from '@/components';
|
||||||
import { InvoicePaperTemplate } from '../InvoiceCustomize/InvoicePaperTemplate';
|
|
||||||
import { InvoiceSendMailPreviewWithHeader } from './InvoiceSendMailHeaderPreview';
|
import { InvoiceSendMailPreviewWithHeader } from './InvoiceSendMailHeaderPreview';
|
||||||
|
import { useInvoiceHtml } from '@/hooks/query';
|
||||||
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
|
|
||||||
export function InvoiceSendPdfPreviewConnected() {
|
export function InvoiceSendPdfPreviewConnected() {
|
||||||
return (
|
return (
|
||||||
<InvoiceSendMailPreviewWithHeader>
|
<InvoiceSendMailPreviewWithHeader>
|
||||||
<Box px={4} py={6}>
|
<Stack px={4} py={6}>
|
||||||
<InvoicePaperTemplate
|
<InvoiceSendPdfPreviewIframe />
|
||||||
className={css`
|
</Stack>
|
||||||
margin: 0 auto;
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</InvoiceSendMailPreviewWithHeader>
|
</InvoiceSendMailPreviewWithHeader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function InvoiceSendPdfPreviewIframe() {
|
||||||
|
const { payload } = useDrawerContext();
|
||||||
|
const { data, isLoading } = useInvoiceHtml(payload?.invoiceId);
|
||||||
|
|
||||||
|
if (isLoading && data) {
|
||||||
|
return <Spinner size={20} />;
|
||||||
|
}
|
||||||
|
const iframeSrcDoc = data?.htmlContent;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<iframe
|
||||||
|
title={'invoice-pdf-preview'}
|
||||||
|
srcDoc={iframeSrcDoc}
|
||||||
|
className={css`
|
||||||
|
height: 1123px;
|
||||||
|
width: 794px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.05);
|
||||||
|
margin: 0 auto;
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ interface PaymentMailDialogBootProps {
|
|||||||
*/
|
*/
|
||||||
function PaymentMailDialogBoot({
|
function PaymentMailDialogBoot({
|
||||||
paymentReceiveId,
|
paymentReceiveId,
|
||||||
|
redirectToPaymentsList,
|
||||||
...props
|
...props
|
||||||
}: PaymentMailDialogBootProps) {
|
}: PaymentMailDialogBootProps) {
|
||||||
const { data: mailOptions, isLoading: isMailOptionsLoading } =
|
const { data: mailOptions, isLoading: isMailOptionsLoading } =
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ export function PaymentMailDialogFormContent({
|
|||||||
<Form>
|
<Form>
|
||||||
<div className={Classes.DIALOG_BODY}>
|
<div className={Classes.DIALOG_BODY}>
|
||||||
<MailNotificationForm
|
<MailNotificationForm
|
||||||
fromAddresses={mailOptions.from_addresses}
|
fromAddresses={mailOptions.from_options}
|
||||||
toAddresses={mailOptions.to_addresses}
|
toAddresses={mailOptions.to_options}
|
||||||
/>
|
/>
|
||||||
<AttachFormGroup name={'attachPayment'} inline>
|
<AttachFormGroup name={'attachPayment'} inline>
|
||||||
<FSwitch name={'attachPayment'} label={'Attach Payment'} />
|
<FSwitch name={'attachPayment'} label={'Attach Payment'} />
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ export function ReceiptMailDialogFormContent({
|
|||||||
<Form>
|
<Form>
|
||||||
<div className={Classes.DIALOG_BODY}>
|
<div className={Classes.DIALOG_BODY}>
|
||||||
<MailNotificationForm
|
<MailNotificationForm
|
||||||
fromAddresses={mailOptions.from_addresses}
|
fromAddresses={mailOptions.from_options}
|
||||||
toAddresses={mailOptions.to_addresses}
|
toAddresses={mailOptions.to_options}
|
||||||
/>
|
/>
|
||||||
<AttachFormGroup name={'attachReceipt:'} inline>
|
<AttachFormGroup name={'attachReceipt:'} inline>
|
||||||
<FSwitch name={'attachReceipt:'} label={'Attach Receipt'} />
|
<FSwitch name={'attachReceipt:'} label={'Attach Receipt'} />
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export function MailNotificationForm({
|
|||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
</HeaderBox>
|
</HeaderBox>
|
||||||
|
|
||||||
<MailMessageEditor name={'body'} />
|
<MailMessageEditor name={'message'} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const initialMailNotificationValues = {
|
|||||||
from: [],
|
from: [],
|
||||||
to: [],
|
to: [],
|
||||||
subject: '',
|
subject: '',
|
||||||
body: '',
|
message: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface MailNotificationFormValues {
|
export interface MailNotificationFormValues {
|
||||||
@@ -26,7 +26,7 @@ export const transformMailFormToRequest = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transformes the mail options response values to form initial values.
|
* Transforms the mail options response values to form initial values.
|
||||||
* @param {any} mailOptions
|
* @param {any} mailOptions
|
||||||
* @param {MailNotificationFormValues} initialValues
|
* @param {MailNotificationFormValues} initialValues
|
||||||
* @returns {MailNotificationFormValues}
|
* @returns {MailNotificationFormValues}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import clsx from 'classnames';
|
import clsx from 'classnames';
|
||||||
import { includes } from 'lodash';
|
import { includes } from 'lodash';
|
||||||
import { Box, Group, Stack } from '@/components';
|
|
||||||
import { Button, Card, Classes, Intent, Text } from '@blueprintjs/core';
|
import { Button, Card, Classes, Intent, Text } from '@blueprintjs/core';
|
||||||
|
import { Box, Group, Stack } from '@/components';
|
||||||
import withAlertActions from '../Alert/withAlertActions';
|
import withAlertActions from '../Alert/withAlertActions';
|
||||||
import styles from './BillingSubscription.module.scss';
|
import styles from './BillingSubscription.module.scss';
|
||||||
import withDrawerActions from '../Drawer/withDrawerActions';
|
import withDrawerActions from '../Drawer/withDrawerActions';
|
||||||
@@ -18,12 +18,11 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
|
|||||||
if (!mainSubscription) {
|
if (!mainSubscription) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// Handle cancel subscription button click.
|
||||||
const handleCancelSubBtnClick = () => {
|
const handleCancelSubBtnClick = () => {
|
||||||
openAlert('cancel-main-subscription');
|
openAlert('cancel-main-subscription');
|
||||||
};
|
};
|
||||||
const handleResumeSubBtnClick = () => {
|
// Handle update payment method button click.
|
||||||
openAlert('resume-main-subscription');
|
|
||||||
};
|
|
||||||
const handleUpdatePaymentMethod = () => {
|
const handleUpdatePaymentMethod = () => {
|
||||||
window.LemonSqueezy.Url.Open(
|
window.LemonSqueezy.Url.Open(
|
||||||
mainSubscription.lemonUrls?.updatePaymentMethod,
|
mainSubscription.lemonUrls?.updatePaymentMethod,
|
||||||
@@ -33,6 +32,10 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
|
|||||||
const handleUpgradeBtnClick = () => {
|
const handleUpgradeBtnClick = () => {
|
||||||
openDrawer(DRAWERS.CHANGE_SUBSCARIPTION_PLAN);
|
openDrawer(DRAWERS.CHANGE_SUBSCARIPTION_PLAN);
|
||||||
};
|
};
|
||||||
|
// Handles renew the expired subscription.
|
||||||
|
const handleNewSubscriptionBtnClick = () => {
|
||||||
|
openDrawer(DRAWERS.NEW_SUBSCRIPTION_PLANS);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={styles.root}>
|
<Card className={styles.root}>
|
||||||
@@ -66,6 +69,19 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Stack align="flex-start" spacing={8} className={styles.actions}>
|
<Stack align="flex-start" spacing={8} className={styles.actions}>
|
||||||
|
{mainSubscription.canceled && (
|
||||||
|
<Button
|
||||||
|
minimal
|
||||||
|
small
|
||||||
|
inline
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
onClick={handleNewSubscriptionBtnClick}
|
||||||
|
>
|
||||||
|
Renew Subscription
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!mainSubscription.canceled && (
|
||||||
|
<>
|
||||||
<Button
|
<Button
|
||||||
minimal
|
minimal
|
||||||
small
|
small
|
||||||
@@ -76,18 +92,6 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
|
|||||||
Upgrade the Plan
|
Upgrade the Plan
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{mainSubscription.canceled && (
|
|
||||||
<Button
|
|
||||||
minimal
|
|
||||||
small
|
|
||||||
inline
|
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
onClick={handleResumeSubBtnClick}
|
|
||||||
>
|
|
||||||
Resume Subscription
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{!mainSubscription.canceled && (
|
|
||||||
<Button
|
<Button
|
||||||
minimal
|
minimal
|
||||||
small
|
small
|
||||||
@@ -97,7 +101,7 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
|
|||||||
>
|
>
|
||||||
Cancel Subscription
|
Cancel Subscription
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
minimal
|
minimal
|
||||||
small
|
small
|
||||||
@@ -107,9 +111,11 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
|
|||||||
>
|
>
|
||||||
Change Payment Method
|
Change Payment Method
|
||||||
</Button>
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Group position={'apart'} style={{ marginTop: 'auto' }}>
|
<Group position={'apart'} mt={'auto'}>
|
||||||
<Group spacing={4}>
|
<Group spacing={4}>
|
||||||
<Text className={styles.priceAmount}>
|
<Text className={styles.priceAmount}>
|
||||||
{mainSubscription.planPriceFormatted}
|
{mainSubscription.planPriceFormatted}
|
||||||
@@ -130,10 +136,10 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
|
|||||||
{mainSubscription.canceled && (
|
{mainSubscription.canceled && (
|
||||||
<Button
|
<Button
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
onClick={handleResumeSubBtnClick}
|
onClick={handleNewSubscriptionBtnClick}
|
||||||
className={styles.subscribeButton}
|
className={styles.subscribeButton}
|
||||||
>
|
>
|
||||||
Resume Subscription
|
Renew Subscription
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Box } from "@/components";
|
||||||
|
import { SubscriptionPlansPeriodSwitcher } from "@/containers/Setup/SetupSubscription/SubscriptionPlansPeriodSwitcher";
|
||||||
|
import { Callout, Classes } from "@blueprintjs/core";
|
||||||
|
import { NewSubscriptionPlans } from "./NewSubscriptionPlans";
|
||||||
|
|
||||||
|
|
||||||
|
export function NewSubscriptionContent() {
|
||||||
|
return (
|
||||||
|
<Box className={Classes.DRAWER_BODY}>
|
||||||
|
<Box
|
||||||
|
maxWidth={1024}
|
||||||
|
margin="0 auto"
|
||||||
|
padding={'50px 20px 80px'}
|
||||||
|
>
|
||||||
|
<Callout style={{ marginBottom: '2rem' }} icon={null}>
|
||||||
|
Simple plans. Simple prices. Only pay for what you really need. All
|
||||||
|
plans come with award-winning 24/7 customer support. Prices do not
|
||||||
|
include applicable taxes.
|
||||||
|
</Callout>
|
||||||
|
|
||||||
|
<SubscriptionPlansPeriodSwitcher />
|
||||||
|
<NewSubscriptionPlans />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React, { lazy } from 'react';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import { Position } from '@blueprintjs/core';
|
||||||
|
import { Drawer, DrawerHeaderContent, DrawerSuspense } from '@/components';
|
||||||
|
import withDrawers from '@/containers/Drawer/withDrawers';
|
||||||
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
|
||||||
|
const NewSubscriptionContent = lazy(() =>
|
||||||
|
import('./NewSubscriptionContent').then((module) => ({
|
||||||
|
default: module.NewSubscriptionContent,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
function NewSubscriptionDrawerRoot({
|
||||||
|
name,
|
||||||
|
// #withDrawer
|
||||||
|
isOpen,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Drawer
|
||||||
|
isOpen={isOpen}
|
||||||
|
name={name}
|
||||||
|
size={'calc(100% - 5px)'}
|
||||||
|
position={Position.BOTTOM}
|
||||||
|
>
|
||||||
|
<DrawerSuspense>
|
||||||
|
<DrawerHeaderContent
|
||||||
|
name={DRAWERS.NEW_SUBSCRIPTION_PLANS}
|
||||||
|
title={'Renew Subscription Plan'}
|
||||||
|
/>
|
||||||
|
<NewSubscriptionContent />
|
||||||
|
</DrawerSuspense>
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NewSubscriptionDrawer = R.compose(withDrawers())(
|
||||||
|
NewSubscriptionDrawerRoot,
|
||||||
|
);
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import { Intent } from '@blueprintjs/core';
|
||||||
|
import { AppToaster, Group } from '@/components';
|
||||||
|
import { SubscriptionPlan } from '../../component/SubscriptionPlan';
|
||||||
|
import { SubscriptionPlansPeriod } from '@/store/plans/plans.reducer';
|
||||||
|
import { useSubscriptionPlans } from '@/hooks/constants/useSubscriptionPlans';
|
||||||
|
import { withSubscriptionPlanMapper } from '../../component/withSubscriptionPlanMapper';
|
||||||
|
import { useGetLemonSqueezyCheckout } from '@/hooks/query';
|
||||||
|
|
||||||
|
export function NewSubscriptionPlans() {
|
||||||
|
const subscriptionPlans = useSubscriptionPlans();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group spacing={14} noWrap align="stretch">
|
||||||
|
{subscriptionPlans.map((plan, index) => (
|
||||||
|
<NewSubscriptionPlanMapped plan={plan} />
|
||||||
|
))}
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NewSubscriptionPlanMapped = R.compose(
|
||||||
|
withSubscriptionPlanMapper,
|
||||||
|
)(
|
||||||
|
({
|
||||||
|
monthlyVariantId,
|
||||||
|
annuallyVariantId,
|
||||||
|
plansPeriod,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
const { mutateAsync: getLemonCheckout, isLoading } =
|
||||||
|
useGetLemonSqueezyCheckout();
|
||||||
|
|
||||||
|
// Handles the subscribe button click.
|
||||||
|
const handleSubscribe = () => {
|
||||||
|
const variantId =
|
||||||
|
plansPeriod === SubscriptionPlansPeriod.Monthly
|
||||||
|
? monthlyVariantId
|
||||||
|
: annuallyVariantId;
|
||||||
|
|
||||||
|
getLemonCheckout({ variantId })
|
||||||
|
.then((res) => {
|
||||||
|
const checkoutUrl = res.data.data.attributes.url;
|
||||||
|
window.LemonSqueezy.Url.Open(checkoutUrl);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'Something went wrong!',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<SubscriptionPlan
|
||||||
|
{...props}
|
||||||
|
onSubscribe={handleSubscribe}
|
||||||
|
subscribeButtonProps={{ loading: isLoading }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -201,6 +201,34 @@ export function usePdfInvoice(invoiceId) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GetInvoiceHtmlResponse {
|
||||||
|
htmlContent: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieves the invoice html content.
|
||||||
|
* @param {number} invoiceId
|
||||||
|
* @param {UseQueryOptions<GetInvoiceHtmlResponse>} options
|
||||||
|
* @returns {UseQueryResult<GetInvoiceHtmlResponse>}
|
||||||
|
*/
|
||||||
|
export const useInvoiceHtml = (
|
||||||
|
invoiceId: number,
|
||||||
|
options?: UseQueryOptions<GetInvoiceHtmlResponse>,
|
||||||
|
): UseQueryResult<GetInvoiceHtmlResponse> => {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useQuery<GetInvoiceHtmlResponse>(
|
||||||
|
['SALE_INVOICE_HTML', invoiceId],
|
||||||
|
() =>
|
||||||
|
apiRequest
|
||||||
|
.get(`sales/invoices/${invoiceId}`, {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json+html',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => transformToCamelCase(res.data)),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve due invoices of the given customer id.
|
* Retrieve due invoices of the given customer id.
|
||||||
* @param {number} customerId - Customer id.
|
* @param {number} customerId - Customer id.
|
||||||
@@ -372,6 +400,9 @@ export function useSendSaleInvoiceMail(
|
|||||||
// --------------------------------------
|
// --------------------------------------
|
||||||
export interface GetSaleInvoiceDefaultOptionsResponse {
|
export interface GetSaleInvoiceDefaultOptionsResponse {
|
||||||
companyName: string;
|
companyName: string;
|
||||||
|
companyLogoUri: string;
|
||||||
|
|
||||||
|
customerName: string;
|
||||||
|
|
||||||
dueDate: string;
|
dueDate: string;
|
||||||
dueDateFormatted: string;
|
dueDateFormatted: string;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export interface GetPdfTemplateValues {}
|
|||||||
|
|
||||||
export interface GetPdfTemplateResponse {
|
export interface GetPdfTemplateResponse {
|
||||||
templateName: string;
|
templateName: string;
|
||||||
|
companyLogoUri?: string | null;
|
||||||
attributes: Record<string, any>;
|
attributes: Record<string, any>;
|
||||||
predefined: boolean;
|
predefined: boolean;
|
||||||
default: boolean;
|
default: boolean;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
"to_date": "الى تاريخ",
|
"to_date": "الى تاريخ",
|
||||||
"report_date_range": "تقرير نطاق التاريخ",
|
"report_date_range": "تقرير نطاق التاريخ",
|
||||||
"log_in": "تسجيل الدخول",
|
"log_in": "تسجيل الدخول",
|
||||||
"forget_my_password": "نسيت كلمة المرور الخاصة بي",
|
"forgot_my_password": "نسيت كلمة المرور الخاصة بي",
|
||||||
"keep_me_logged_in": "تذكرني",
|
"keep_me_logged_in": "تذكرني",
|
||||||
"dont_have_an_account": "ليس لديك حساب؟",
|
"dont_have_an_account": "ليس لديك حساب؟",
|
||||||
"sign_up": "تسجيل",
|
"sign_up": "تسجيل",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user