ztechno_core 0.0.125 → 0.0.127
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/core/mail_service.js +1 -0
- package/lib/core/types/mail_types.d.ts +1 -0
- package/lib/mollie/orm/invoice_item_templates_orm.d.ts +12 -0
- package/lib/mollie/orm/invoice_item_templates_orm.js +86 -0
- package/lib/mollie/orm/invoices_orm.d.ts +1 -0
- package/lib/mollie/orm/invoices_orm.js +16 -4
- package/lib/mollie/services/invoice_service.d.ts +23 -0
- package/lib/mollie/services/invoice_service.js +83 -2
- package/lib/mollie/types/mollie_types.d.ts +14 -1
- package/package.json +1 -1
package/lib/core/mail_service.js
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ZSQLService } from '../..';
|
|
2
|
+
import { ZOrm } from '../../core/orm/orm';
|
|
3
|
+
import { ZInvoiceItemTemplate } from '../types/mollie_types';
|
|
4
|
+
export declare class InvoiceItemTemplatesOrm extends ZOrm {
|
|
5
|
+
constructor(opt: { sqlService: ZSQLService; alias?: string });
|
|
6
|
+
create(template: Omit<ZInvoiceItemTemplate, 'id' | 'created_at' | 'updated_at'>): Promise<ZInvoiceItemTemplate>;
|
|
7
|
+
findById(id: number): Promise<ZInvoiceItemTemplate>;
|
|
8
|
+
findAll(): Promise<ZInvoiceItemTemplate[]>;
|
|
9
|
+
update(id: number, template: Partial<Omit<ZInvoiceItemTemplate, 'id' | 'created_at' | 'updated_at'>>): Promise<void>;
|
|
10
|
+
delete(id: number): Promise<void>;
|
|
11
|
+
createTable(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
|
+
exports.InvoiceItemTemplatesOrm = void 0;
|
|
4
|
+
const orm_1 = require('../../core/orm/orm');
|
|
5
|
+
class InvoiceItemTemplatesOrm extends orm_1.ZOrm {
|
|
6
|
+
constructor(opt) {
|
|
7
|
+
super({ sqlService: opt.sqlService, alias: opt.alias ?? 'mollie_invoice_item_templates' });
|
|
8
|
+
}
|
|
9
|
+
async create(template) {
|
|
10
|
+
const res = await this.sqlService.query(
|
|
11
|
+
/*SQL*/ `
|
|
12
|
+
INSERT INTO \`${this.alias}\`
|
|
13
|
+
(name, item_type, description, quantity, unit_price, vat_rate, sort_order)
|
|
14
|
+
VALUES
|
|
15
|
+
(:name, :item_type, :description, :quantity, :unit_price, :vat_rate, :sort_order)
|
|
16
|
+
`,
|
|
17
|
+
{
|
|
18
|
+
name: template.name,
|
|
19
|
+
item_type: template.item_type ?? 'service',
|
|
20
|
+
description: template.description,
|
|
21
|
+
quantity: template.quantity,
|
|
22
|
+
unit_price: template.unit_price,
|
|
23
|
+
vat_rate: template.vat_rate,
|
|
24
|
+
sort_order: template.sort_order ?? 0,
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
const insertId = res.insertId;
|
|
28
|
+
return await this.findById(insertId);
|
|
29
|
+
}
|
|
30
|
+
async findById(id) {
|
|
31
|
+
const res = await this.sqlService.exec({
|
|
32
|
+
query: `SELECT * FROM \`${this.alias}\` WHERE id=:id LIMIT 1`,
|
|
33
|
+
params: { id },
|
|
34
|
+
});
|
|
35
|
+
return res[0];
|
|
36
|
+
}
|
|
37
|
+
async findAll() {
|
|
38
|
+
return await this.sqlService.exec({
|
|
39
|
+
query: `SELECT * FROM \`${this.alias}\` ORDER BY sort_order, name`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async update(id, template) {
|
|
43
|
+
const sets = [];
|
|
44
|
+
const params = { id };
|
|
45
|
+
const fields = ['name', 'item_type', 'description', 'quantity', 'unit_price', 'vat_rate', 'sort_order'];
|
|
46
|
+
for (const field of fields) {
|
|
47
|
+
if (template[field] !== undefined) {
|
|
48
|
+
sets.push(`${field}=:${field}`);
|
|
49
|
+
params[field] = template[field];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (sets.length === 0) return;
|
|
53
|
+
sets.push('updated_at=NOW()');
|
|
54
|
+
await this.sqlService.query(
|
|
55
|
+
/*SQL*/ `
|
|
56
|
+
UPDATE \`${this.alias}\` SET ${sets.join(', ')} WHERE id=:id
|
|
57
|
+
`,
|
|
58
|
+
params,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
async delete(id) {
|
|
62
|
+
await this.sqlService.query(
|
|
63
|
+
/*SQL*/ `
|
|
64
|
+
DELETE FROM \`${this.alias}\` WHERE id=:id
|
|
65
|
+
`,
|
|
66
|
+
{ id },
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
async createTable() {
|
|
70
|
+
await this.sqlService.query(/*SQL*/ `
|
|
71
|
+
CREATE TABLE IF NOT EXISTS \`${this.alias}\` (
|
|
72
|
+
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
73
|
+
name VARCHAR(100) NOT NULL,
|
|
74
|
+
item_type ENUM('service','subsidy') NOT NULL DEFAULT 'service',
|
|
75
|
+
description VARCHAR(255) NOT NULL,
|
|
76
|
+
quantity DECIMAL(10,2) NOT NULL DEFAULT 1,
|
|
77
|
+
unit_price DECIMAL(12,2) NOT NULL,
|
|
78
|
+
vat_rate DECIMAL(5,2) NOT NULL DEFAULT 0.00,
|
|
79
|
+
sort_order SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
|
80
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
81
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
82
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
|
83
|
+
`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.InvoiceItemTemplatesOrm = InvoiceItemTemplatesOrm;
|
|
@@ -10,9 +10,9 @@ class InvoicesOrm extends orm_1.ZOrm {
|
|
|
10
10
|
const res = await this.sqlService.query(
|
|
11
11
|
/*SQL*/ `
|
|
12
12
|
INSERT INTO \`${this.alias}\`
|
|
13
|
-
(invoice_number, customer_id, subscription_id, subscription_period_start, subscription_period_end, mollie_customer_id, mollie_payment_id, pay_token_hash, pay_token_expires_at, pay_token_finalized_at, status, amount_due, amount_paid, currency, description, payment_terms, due_date, issued_at, paid_at, checkout_url, metadata)
|
|
13
|
+
(invoice_number, customer_id, subscription_id, subscription_period_start, subscription_period_end, mollie_customer_id, mollie_payment_id, pay_token_hash, pay_token_expires_at, pay_token_finalized_at, status, amount_due, amount_paid, currency, description, payment_terms, due_date, issued_at, paid_at, checkout_url, times_sent, metadata)
|
|
14
14
|
VALUES
|
|
15
|
-
(:invoice_number, :customer_id, :subscription_id, :subscription_period_start, :subscription_period_end, :mollie_customer_id, :mollie_payment_id, :pay_token_hash, :pay_token_expires_at, :pay_token_finalized_at, :status, :amount_due, :amount_paid, :currency, :description, :payment_terms, :due_date, :issued_at, :paid_at, :checkout_url, :metadata)
|
|
15
|
+
(:invoice_number, :customer_id, :subscription_id, :subscription_period_start, :subscription_period_end, :mollie_customer_id, :mollie_payment_id, :pay_token_hash, :pay_token_expires_at, :pay_token_finalized_at, :status, :amount_due, :amount_paid, :currency, :description, :payment_terms, :due_date, :issued_at, :paid_at, :checkout_url, :times_sent, :metadata)
|
|
16
16
|
ON DUPLICATE KEY UPDATE
|
|
17
17
|
subscription_id=VALUES(subscription_id),
|
|
18
18
|
subscription_period_start=VALUES(subscription_period_start),
|
|
@@ -32,10 +32,11 @@ class InvoicesOrm extends orm_1.ZOrm {
|
|
|
32
32
|
issued_at=VALUES(issued_at),
|
|
33
33
|
paid_at=VALUES(paid_at),
|
|
34
34
|
checkout_url=VALUES(checkout_url),
|
|
35
|
+
times_sent=VALUES(times_sent),
|
|
35
36
|
metadata=VALUES(metadata),
|
|
36
37
|
updated_at=NOW()
|
|
37
38
|
`,
|
|
38
|
-
invoice,
|
|
39
|
+
{ ...invoice, times_sent: invoice.times_sent ?? 0 },
|
|
39
40
|
);
|
|
40
41
|
return res;
|
|
41
42
|
}
|
|
@@ -140,6 +141,16 @@ class InvoicesOrm extends orm_1.ZOrm {
|
|
|
140
141
|
{ id },
|
|
141
142
|
);
|
|
142
143
|
}
|
|
144
|
+
async incrementTimesSent(id) {
|
|
145
|
+
await this.sqlService.query(
|
|
146
|
+
/*SQL*/ `
|
|
147
|
+
UPDATE \`${this.alias}\`
|
|
148
|
+
SET times_sent = times_sent + 1, updated_at=NOW()
|
|
149
|
+
WHERE id=:id
|
|
150
|
+
`,
|
|
151
|
+
{ id },
|
|
152
|
+
);
|
|
153
|
+
}
|
|
143
154
|
async createTable() {
|
|
144
155
|
await this.sqlService.query(/*SQL*/ `
|
|
145
156
|
CREATE TABLE IF NOT EXISTS \`${this.alias}\` (
|
|
@@ -154,7 +165,7 @@ class InvoicesOrm extends orm_1.ZOrm {
|
|
|
154
165
|
pay_token_hash CHAR(64),
|
|
155
166
|
pay_token_expires_at DATETIME,
|
|
156
167
|
pay_token_finalized_at DATETIME,
|
|
157
|
-
status ENUM('draft','pending','paid','failed','canceled','expired','refunded') NOT NULL DEFAULT 'draft',
|
|
168
|
+
status ENUM('draft','pending','paid','failed','canceled','expired','refunded','archived') NOT NULL DEFAULT 'draft',
|
|
158
169
|
amount_due DECIMAL(12,2) NOT NULL,
|
|
159
170
|
amount_paid DECIMAL(12,2) NOT NULL DEFAULT 0,
|
|
160
171
|
currency CHAR(3) NOT NULL DEFAULT 'EUR',
|
|
@@ -164,6 +175,7 @@ class InvoicesOrm extends orm_1.ZOrm {
|
|
|
164
175
|
issued_at DATETIME,
|
|
165
176
|
paid_at DATETIME,
|
|
166
177
|
checkout_url VARCHAR(512),
|
|
178
|
+
times_sent INT NOT NULL DEFAULT 0,
|
|
167
179
|
metadata JSON NULL,
|
|
168
180
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
169
181
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
@@ -11,12 +11,14 @@ import {
|
|
|
11
11
|
CreateInvoiceOverrides,
|
|
12
12
|
ZIssuedPayToken,
|
|
13
13
|
ZPayResolveResult,
|
|
14
|
+
ZInvoiceItemTemplate,
|
|
14
15
|
} from '../types/mollie_types';
|
|
15
16
|
export declare class InvoiceService {
|
|
16
17
|
private opt;
|
|
17
18
|
private invoicesOrm;
|
|
18
19
|
private itemsOrm;
|
|
19
20
|
private paymentsOrm;
|
|
21
|
+
private templateOrm;
|
|
20
22
|
private subscriptionsOrm;
|
|
21
23
|
private subscriptionItemsOrm;
|
|
22
24
|
private payTokenSecret;
|
|
@@ -40,6 +42,8 @@ export declare class InvoiceService {
|
|
|
40
42
|
private ensurePayTokenSchema;
|
|
41
43
|
private ensureSubscriptionInvoiceSchema;
|
|
42
44
|
private ensureSubsidyItemTypeSchema;
|
|
45
|
+
private ensureSentCountSchema;
|
|
46
|
+
private ensureArchivedStatusSchema;
|
|
43
47
|
private ensureInvoicePaymentSchema;
|
|
44
48
|
private generateInvoiceNumber;
|
|
45
49
|
private getWebhookUrl;
|
|
@@ -69,6 +73,24 @@ export declare class InvoiceService {
|
|
|
69
73
|
listInvoices(): Promise<ZInvoice[]>;
|
|
70
74
|
getInvoiceById(invoiceId: number): Promise<ZInvoice | undefined>;
|
|
71
75
|
listPayments(invoice_id: number): Promise<ZInvoicePayment[]>;
|
|
76
|
+
archiveInvoice(invoiceId: number): Promise<ZInvoice>;
|
|
77
|
+
duplicateInvoice(
|
|
78
|
+
sourceInvoiceId: number,
|
|
79
|
+
customerId: number,
|
|
80
|
+
): Promise<{
|
|
81
|
+
invoice: ZInvoice;
|
|
82
|
+
pay: ZIssuedPayToken;
|
|
83
|
+
}>;
|
|
84
|
+
createItemTemplate(
|
|
85
|
+
template: Omit<ZInvoiceItemTemplate, 'id' | 'created_at' | 'updated_at'>,
|
|
86
|
+
): Promise<ZInvoiceItemTemplate>;
|
|
87
|
+
listItemTemplates(): Promise<ZInvoiceItemTemplate[]>;
|
|
88
|
+
getItemTemplate(id: number): Promise<ZInvoiceItemTemplate>;
|
|
89
|
+
updateItemTemplate(
|
|
90
|
+
id: number,
|
|
91
|
+
template: Partial<Omit<ZInvoiceItemTemplate, 'id' | 'created_at' | 'updated_at'>>,
|
|
92
|
+
): Promise<ZInvoiceItemTemplate>;
|
|
93
|
+
deleteItemTemplate(id: number): Promise<void>;
|
|
72
94
|
createInvoiceWithPayment(input: CreateInvoiceInput): Promise<{
|
|
73
95
|
invoice: ZInvoice;
|
|
74
96
|
checkoutUrl: string;
|
|
@@ -96,6 +118,7 @@ export declare class InvoiceService {
|
|
|
96
118
|
recipient?: string,
|
|
97
119
|
opt?: {
|
|
98
120
|
mode?: 'invoice' | 'receipt';
|
|
121
|
+
ccOwner?: boolean;
|
|
99
122
|
},
|
|
100
123
|
): Promise<{
|
|
101
124
|
invoice_number: string;
|
|
@@ -11,6 +11,7 @@ const path_1 = __importDefault(require('path'));
|
|
|
11
11
|
const fs_1 = __importDefault(require('fs'));
|
|
12
12
|
const crypto_1 = __importDefault(require('crypto'));
|
|
13
13
|
const invoice_items_orm_1 = require('../orm/invoice_items_orm');
|
|
14
|
+
const invoice_item_templates_orm_1 = require('../orm/invoice_item_templates_orm');
|
|
14
15
|
const invoice_payments_orm_1 = require('../orm/invoice_payments_orm');
|
|
15
16
|
const invoices_orm_1 = require('../orm/invoices_orm');
|
|
16
17
|
const subscription_items_orm_1 = require('../orm/subscription_items_orm');
|
|
@@ -31,6 +32,7 @@ class InvoiceService {
|
|
|
31
32
|
this.invoicesOrm = new invoices_orm_1.InvoicesOrm({ sqlService: opt.sqlService });
|
|
32
33
|
this.itemsOrm = new invoice_items_orm_1.InvoiceItemsOrm({ sqlService: opt.sqlService });
|
|
33
34
|
this.paymentsOrm = new invoice_payments_orm_1.InvoicePaymentsOrm({ sqlService: opt.sqlService });
|
|
35
|
+
this.templateOrm = new invoice_item_templates_orm_1.InvoiceItemTemplatesOrm({ sqlService: opt.sqlService });
|
|
34
36
|
this.subscriptionsOrm = new subscriptions_orm_1.SubscriptionsOrm({ sqlService: opt.sqlService });
|
|
35
37
|
this.subscriptionItemsOrm = new subscription_items_orm_1.SubscriptionItemsOrm({ sqlService: opt.sqlService });
|
|
36
38
|
this.mailService = opt.mailService;
|
|
@@ -41,10 +43,13 @@ class InvoiceService {
|
|
|
41
43
|
await this.invoicesOrm.ensureTableExists();
|
|
42
44
|
await this.itemsOrm.ensureTableExists();
|
|
43
45
|
await this.paymentsOrm.ensureTableExists();
|
|
46
|
+
await this.templateOrm.ensureTableExists();
|
|
44
47
|
await this.ensurePayTokenSchema();
|
|
45
48
|
await this.ensureSubscriptionInvoiceSchema();
|
|
46
49
|
await this.ensureInvoicePaymentSchema();
|
|
47
50
|
await this.ensureSubsidyItemTypeSchema();
|
|
51
|
+
await this.ensureSentCountSchema();
|
|
52
|
+
await this.ensureArchivedStatusSchema();
|
|
48
53
|
}
|
|
49
54
|
async ensurePayTokenSchema() {
|
|
50
55
|
const table = this.invoicesOrm.alias;
|
|
@@ -104,6 +109,31 @@ class InvoiceService {
|
|
|
104
109
|
);
|
|
105
110
|
}
|
|
106
111
|
}
|
|
112
|
+
async ensureSentCountSchema() {
|
|
113
|
+
const table = this.invoicesOrm.alias;
|
|
114
|
+
const rows = await this.opt.sqlService.exec({
|
|
115
|
+
query: `SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=:schema AND TABLE_NAME=:tableName AND COLUMN_NAME='times_sent' LIMIT 1`,
|
|
116
|
+
params: { schema: this.opt.sqlService.database, tableName: table },
|
|
117
|
+
});
|
|
118
|
+
if (!rows?.[0]) {
|
|
119
|
+
await this.opt.sqlService.query(
|
|
120
|
+
`ALTER TABLE \`${table}\` ADD COLUMN times_sent INT NOT NULL DEFAULT 0 AFTER checkout_url`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async ensureArchivedStatusSchema() {
|
|
125
|
+
const table = this.invoicesOrm.alias;
|
|
126
|
+
const rows = await this.opt.sqlService.exec({
|
|
127
|
+
query: `SELECT COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=:schema AND TABLE_NAME=:tableName AND COLUMN_NAME='status' LIMIT 1`,
|
|
128
|
+
params: { schema: this.opt.sqlService.database, tableName: table },
|
|
129
|
+
});
|
|
130
|
+
const colType = rows?.[0]?.COLUMN_TYPE ?? '';
|
|
131
|
+
if (colType && !colType.includes('archived')) {
|
|
132
|
+
await this.opt.sqlService.query(
|
|
133
|
+
`ALTER TABLE \`${table}\` MODIFY COLUMN status ENUM('draft','pending','paid','failed','canceled','expired','refunded','archived') NOT NULL DEFAULT 'draft'`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
107
137
|
async ensureInvoicePaymentSchema() {
|
|
108
138
|
const table = this.paymentsOrm.alias;
|
|
109
139
|
const ensureColumn = async (column, sqlType) => {
|
|
@@ -389,6 +419,54 @@ class InvoiceService {
|
|
|
389
419
|
async listPayments(invoice_id) {
|
|
390
420
|
return await this.paymentsOrm.findByInvoice(invoice_id);
|
|
391
421
|
}
|
|
422
|
+
// ==================== Archive ====================
|
|
423
|
+
async archiveInvoice(invoiceId) {
|
|
424
|
+
const invoice = await this.invoicesOrm.findById(invoiceId);
|
|
425
|
+
if (!invoice) {
|
|
426
|
+
throw new Error(`Invoice ${invoiceId} not found`);
|
|
427
|
+
}
|
|
428
|
+
if (invoice.status === 'paid') {
|
|
429
|
+
throw new Error(`Cannot archive a paid invoice (${invoice.invoice_number})`);
|
|
430
|
+
}
|
|
431
|
+
if (invoice.status === 'archived') {
|
|
432
|
+
return invoice;
|
|
433
|
+
}
|
|
434
|
+
await this.invoicesOrm.updateStatus(invoiceId, 'archived');
|
|
435
|
+
return await this.invoicesOrm.findById(invoiceId);
|
|
436
|
+
}
|
|
437
|
+
// ==================== Duplicate ====================
|
|
438
|
+
async duplicateInvoice(sourceInvoiceId, customerId) {
|
|
439
|
+
const items = await this.itemsOrm.findByInvoice(sourceInvoiceId);
|
|
440
|
+
if (items.length === 0) {
|
|
441
|
+
throw new Error(`Source invoice ${sourceInvoiceId} has no items to duplicate`);
|
|
442
|
+
}
|
|
443
|
+
const mappedItems = items.map((it) => ({
|
|
444
|
+
description: it.description,
|
|
445
|
+
quantity: it.quantity,
|
|
446
|
+
unit_price: it.unit_price,
|
|
447
|
+
vat_rate: it.vat_rate,
|
|
448
|
+
item_type: it.item_type,
|
|
449
|
+
sort_order: it.sort_order,
|
|
450
|
+
}));
|
|
451
|
+
return await this.createInvoiceDraft({ customer_id: customerId, items: mappedItems });
|
|
452
|
+
}
|
|
453
|
+
// ==================== Item Templates ====================
|
|
454
|
+
async createItemTemplate(template) {
|
|
455
|
+
return await this.templateOrm.create(template);
|
|
456
|
+
}
|
|
457
|
+
async listItemTemplates() {
|
|
458
|
+
return await this.templateOrm.findAll();
|
|
459
|
+
}
|
|
460
|
+
async getItemTemplate(id) {
|
|
461
|
+
return await this.templateOrm.findById(id);
|
|
462
|
+
}
|
|
463
|
+
async updateItemTemplate(id, template) {
|
|
464
|
+
await this.templateOrm.update(id, template);
|
|
465
|
+
return await this.templateOrm.findById(id);
|
|
466
|
+
}
|
|
467
|
+
async deleteItemTemplate(id) {
|
|
468
|
+
await this.templateOrm.delete(id);
|
|
469
|
+
}
|
|
392
470
|
async createInvoiceWithPayment(input) {
|
|
393
471
|
const draft = await this.createInvoiceDraft(input);
|
|
394
472
|
const payment = await this.createMolliePaymentForInvoice(draft.invoice);
|
|
@@ -571,8 +649,8 @@ class InvoiceService {
|
|
|
571
649
|
subscription_period_end: (0, orm_1.formatDatetime)(periodEnd),
|
|
572
650
|
});
|
|
573
651
|
}
|
|
574
|
-
if (
|
|
575
|
-
await this.sendInvoiceEmail(invoice.id, undefined, { mode: 'receipt' });
|
|
652
|
+
if (mappedStatus === 'paid' && (!hadInvoice || previousStatus !== 'paid')) {
|
|
653
|
+
await this.sendInvoiceEmail(invoice.id, undefined, { mode: 'receipt', ccOwner: true });
|
|
576
654
|
}
|
|
577
655
|
if (
|
|
578
656
|
subscription &&
|
|
@@ -874,9 +952,11 @@ class InvoiceService {
|
|
|
874
952
|
const content = isReceipt
|
|
875
953
|
? `<br>Beste ${customer.name},<br><br>We hebben uw betaling ontvangen. De factuur vindt u in de bijlage.<br><br>Dank u wel, ${cfg.company.companyShort}<br>`
|
|
876
954
|
: `<br>Beste ${customer.name},<br><br>U vindt uw factuur in de bijlage.<br><br><a href="${pay?.payUrl}" style="display:inline-block;padding:10px 16px;background:#0d6efd;color:#fff;text-decoration:none;border-radius:6px;">Betaal nu</a><br><br>Dank u wel, ${cfg.company.companyShort}<br>`;
|
|
955
|
+
const cc = isReceipt ? undefined : opt?.ccOwner ? cfg.contact.contactQuote : undefined;
|
|
877
956
|
await this.mailService.sendAdvanced({
|
|
878
957
|
from: `${cfg.company.company} <${cfg.contact.contact}>`,
|
|
879
958
|
recipient: to,
|
|
959
|
+
cc,
|
|
880
960
|
subject,
|
|
881
961
|
template: 'template.html',
|
|
882
962
|
inject: {
|
|
@@ -893,6 +973,7 @@ class InvoiceService {
|
|
|
893
973
|
},
|
|
894
974
|
],
|
|
895
975
|
});
|
|
976
|
+
await this.invoicesOrm.incrementTimesSent(invoiceId);
|
|
896
977
|
return {
|
|
897
978
|
invoice_number: invoice.invoice_number,
|
|
898
979
|
recipient: to,
|
|
@@ -53,7 +53,7 @@ export type CreateInvoiceOverrides = {
|
|
|
53
53
|
mollie_payment_id?: string | null;
|
|
54
54
|
checkout_url?: string | null;
|
|
55
55
|
};
|
|
56
|
-
export type ZInvoiceStatus = 'draft' | 'pending' | 'paid' | 'failed' | 'canceled' | 'expired' | 'refunded';
|
|
56
|
+
export type ZInvoiceStatus = 'draft' | 'pending' | 'paid' | 'failed' | 'canceled' | 'expired' | 'refunded' | 'archived';
|
|
57
57
|
export type ZInvoice = {
|
|
58
58
|
id?: number;
|
|
59
59
|
invoice_number: string;
|
|
@@ -76,6 +76,7 @@ export type ZInvoice = {
|
|
|
76
76
|
issued_at?: string | null;
|
|
77
77
|
paid_at?: string | null;
|
|
78
78
|
checkout_url?: string | null;
|
|
79
|
+
times_sent?: number;
|
|
79
80
|
metadata?: any;
|
|
80
81
|
created_at?: string | Date;
|
|
81
82
|
updated_at?: string | Date;
|
|
@@ -145,6 +146,18 @@ export type ZSubscription = {
|
|
|
145
146
|
created_at?: string | Date;
|
|
146
147
|
updated_at?: string | Date;
|
|
147
148
|
};
|
|
149
|
+
export type ZInvoiceItemTemplate = {
|
|
150
|
+
id?: number;
|
|
151
|
+
name: string;
|
|
152
|
+
item_type?: ZInvoiceItemType;
|
|
153
|
+
description: string;
|
|
154
|
+
quantity: number;
|
|
155
|
+
unit_price: number;
|
|
156
|
+
vat_rate: number;
|
|
157
|
+
sort_order?: number;
|
|
158
|
+
created_at?: string | Date;
|
|
159
|
+
updated_at?: string | Date;
|
|
160
|
+
};
|
|
148
161
|
export type ZIssuedPayToken = {
|
|
149
162
|
token: string;
|
|
150
163
|
expiresAt: string;
|