@stamhoofd/models 2.19.0 ā 2.20.0
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/dist/src/models/Member.js.map +1 -1
- package/dist/src/models/MemberResponsibilityRecord.d.ts +5 -0
- package/dist/src/models/MemberResponsibilityRecord.d.ts.map +1 -1
- package/dist/src/models/MemberResponsibilityRecord.js +16 -1
- package/dist/src/models/MemberResponsibilityRecord.js.map +1 -1
- package/dist/src/models/Organization.d.ts +2 -0
- package/dist/src/models/Organization.d.ts.map +1 -1
- package/dist/src/models/Organization.js +19 -3
- package/dist/src/models/Organization.js.map +1 -1
- package/dist/src/models/RegisterCode.d.ts +0 -10
- package/dist/src/models/RegisterCode.d.ts.map +1 -1
- package/dist/src/models/RegisterCode.js +0 -86
- package/dist/src/models/RegisterCode.js.map +1 -1
- package/dist/src/models/STCredit.d.ts +0 -7
- package/dist/src/models/STCredit.d.ts.map +1 -1
- package/dist/src/models/STCredit.js +0 -69
- package/dist/src/models/STCredit.js.map +1 -1
- package/dist/src/models/STInvoice.d.ts +2 -20
- package/dist/src/models/STInvoice.d.ts.map +1 -1
- package/dist/src/models/STInvoice.js +0 -364
- package/dist/src/models/STInvoice.js.map +1 -1
- package/dist/src/models/STPendingInvoice.d.ts +1 -21
- package/dist/src/models/STPendingInvoice.d.ts.map +1 -1
- package/dist/src/models/STPendingInvoice.js +0 -213
- package/dist/src/models/STPendingInvoice.js.map +1 -1
- package/dist/src/models/UsedRegisterCode.d.ts +0 -5
- package/dist/src/models/UsedRegisterCode.d.ts.map +1 -1
- package/dist/src/models/UsedRegisterCode.js +0 -101
- package/dist/src/models/UsedRegisterCode.js.map +1 -1
- package/package.json +2 -2
- package/src/models/Member.ts +5 -5
- package/src/models/MemberResponsibilityRecord.ts +21 -2
- package/src/models/Organization.ts +23 -3
- package/src/models/RegisterCode.ts +0 -95
- package/src/models/STCredit.ts +0 -82
- package/src/models/STInvoice.ts +2 -433
- package/src/models/STPendingInvoice.ts +2 -247
- package/src/models/UsedRegisterCode.ts +0 -115
- package/dist/src/helpers/InvoiceBuilder.d.ts +0 -29
- package/dist/src/helpers/InvoiceBuilder.d.ts.map +0 -1
- package/dist/src/helpers/InvoiceBuilder.js +0 -407
- package/dist/src/helpers/InvoiceBuilder.js.map +0 -1
- package/dist/src/helpers/InvoiceBuilder.test.d.ts +0 -2
- package/dist/src/helpers/InvoiceBuilder.test.d.ts.map +0 -1
- package/dist/src/helpers/InvoiceBuilder.test.js +0 -52
- package/dist/src/helpers/InvoiceBuilder.test.js.map +0 -1
- package/src/helpers/InvoiceBuilder.test.ts +0 -57
- package/src/helpers/InvoiceBuilder.ts +0 -501
|
@@ -2,14 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.STPendingInvoice = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const api_client_1 = require("@mollie/api-client");
|
|
6
5
|
const simple_database_1 = require("@simonbackx/simple-database");
|
|
7
|
-
const simple_errors_1 = require("@simonbackx/simple-errors");
|
|
8
|
-
const email_1 = require("@stamhoofd/email");
|
|
9
6
|
const structures_1 = require("@stamhoofd/structures");
|
|
10
|
-
const utility_1 = require("@stamhoofd/utility");
|
|
11
7
|
const uuid_1 = require("uuid");
|
|
12
|
-
const InvoiceBuilder_1 = require("../helpers/InvoiceBuilder");
|
|
13
8
|
const _1 = require("./");
|
|
14
9
|
/**
|
|
15
10
|
* Things that should get paid, but are not yet invoiced yet because:
|
|
@@ -34,214 +29,6 @@ class STPendingInvoice extends simple_database_1.Model {
|
|
|
34
29
|
createdAt;
|
|
35
30
|
updatedAt;
|
|
36
31
|
static organization = new simple_database_1.ManyToOneRelation(_1.Organization, "organization");
|
|
37
|
-
static async getForOrganization(organizationId) {
|
|
38
|
-
const invoices = await STPendingInvoice.where({ organizationId });
|
|
39
|
-
return invoices[0] ?? undefined;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Always run this in the queue!
|
|
43
|
-
*/
|
|
44
|
-
static async addItems(organization, invoiceItems) {
|
|
45
|
-
// Get the pending invoice if it exists
|
|
46
|
-
const pendingInvoice = await STPendingInvoice.getForOrganization(organization.id);
|
|
47
|
-
return await this.addItemsTo(pendingInvoice, organization, invoiceItems);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Always run this in the queue!
|
|
51
|
-
*/
|
|
52
|
-
static async addItemsTo(pendingInvoice, organization, invoiceItems) {
|
|
53
|
-
if (invoiceItems.length > 0) {
|
|
54
|
-
if (!pendingInvoice) {
|
|
55
|
-
// Create one
|
|
56
|
-
pendingInvoice = new STPendingInvoice();
|
|
57
|
-
pendingInvoice.organizationId = organization.id;
|
|
58
|
-
pendingInvoice.meta = structures_1.STInvoiceMeta.create({
|
|
59
|
-
companyName: organization.meta.companyName ?? organization.name,
|
|
60
|
-
companyContact: organization.privateMeta.billingContact ?? "",
|
|
61
|
-
companyAddress: organization.meta.companyAddress ?? organization.address,
|
|
62
|
-
companyVATNumber: organization.meta.VATNumber,
|
|
63
|
-
VATPercentage: (0, structures_1.calculateVATPercentage)(organization.meta.companyAddress ?? organization.address, organization.meta.VATNumber)
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
pendingInvoice.meta.items.push(...invoiceItems);
|
|
67
|
-
// Can we compress
|
|
68
|
-
if (pendingInvoice.invoiceId === null) {
|
|
69
|
-
console.log("Compressing pending invoice items " + pendingInvoice.id);
|
|
70
|
-
pendingInvoice.meta.items = structures_1.STInvoiceItem.compress(pendingInvoice.meta.items);
|
|
71
|
-
}
|
|
72
|
-
await pendingInvoice.save();
|
|
73
|
-
}
|
|
74
|
-
return pendingInvoice;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Always run this in the queue!
|
|
78
|
-
*/
|
|
79
|
-
static async addAutomaticItems(organization) {
|
|
80
|
-
// Get the pending invoice if it exists
|
|
81
|
-
const pendingInvoice = await STPendingInvoice.getForOrganization(organization.id);
|
|
82
|
-
// Generate temporary pending invoice items for the current state without adding them IRL
|
|
83
|
-
const notYetCreatedItems = await STPendingInvoice.createItems(organization.id, pendingInvoice);
|
|
84
|
-
return await this.addItemsTo(pendingInvoice, organization, notYetCreatedItems);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* This method checks all the packages of the given organization and will return
|
|
88
|
-
* new invoice items that should get charged. You'll need to add them to
|
|
89
|
-
* the pending invoice yourself (always in a queue!)
|
|
90
|
-
*/
|
|
91
|
-
static async createItems(organizationId, pendingInvoice) {
|
|
92
|
-
const packages = await _1.STPackage.getForOrganization(organizationId);
|
|
93
|
-
// Always use midnight as a reference time (because this method should always return the same values if it called multiple times on the same day)
|
|
94
|
-
const today = new Date();
|
|
95
|
-
today.setHours(0, 0, 0, 0);
|
|
96
|
-
// But use now as reference for activation detection
|
|
97
|
-
const now = new Date();
|
|
98
|
-
let membersCount = null;
|
|
99
|
-
const pendingItems = [];
|
|
100
|
-
for (const pack of packages) {
|
|
101
|
-
if (pack.meta.startDate > now) {
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
if (pack.meta.pricingType === structures_1.STPricingType.PerMember && (pack.validUntil === null || pack.validUntil >= today)) {
|
|
105
|
-
if (membersCount === null) {
|
|
106
|
-
membersCount = await _1.Registration.getActiveMembers(organizationId);
|
|
107
|
-
}
|
|
108
|
-
// Calculate the items that are already pending and remove them
|
|
109
|
-
const pendingCount = pendingInvoice ? pendingInvoice.meta.items.reduce((c, item) => c + ((item.package && item.package.id === pack.id) ? item.amount : 0), 0) : 0;
|
|
110
|
-
const item = structures_1.STInvoiceItem.fromPackage(structures_1.STPackage.create(pack), membersCount, pendingCount, today);
|
|
111
|
-
if (item.price > 0) {
|
|
112
|
-
console.log('Adding item to pending invoice', item);
|
|
113
|
-
pendingItems.push(item);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
else if ((pack.validUntil === null || pack.validUntil >= today) && pack.meta.paidAmount < pack.meta.minimumAmount) {
|
|
117
|
-
// Check if paid amount matches at least one
|
|
118
|
-
const pendingCount = pendingInvoice ? pendingInvoice.meta.items.reduce((c, item) => c + ((item.package && item.package.id === pack.id) ? item.amount : 0), 0) : 0;
|
|
119
|
-
const item = structures_1.STInvoiceItem.fromPackage(structures_1.STPackage.create(pack), 0, pendingCount, today);
|
|
120
|
-
if (item.price > 0) {
|
|
121
|
-
console.log('Adding item to pending invoice', item);
|
|
122
|
-
pendingItems.push(item);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
// Add all payments that are not yet charged
|
|
127
|
-
return pendingItems;
|
|
128
|
-
}
|
|
129
|
-
static async charge(organization) {
|
|
130
|
-
const pendingInvoice = await this.getForOrganization(organization.id);
|
|
131
|
-
if (!pendingInvoice || pendingInvoice.meta.priceWithVAT === 0) {
|
|
132
|
-
throw new simple_errors_1.SimpleError({
|
|
133
|
-
code: "no_pending_invoice",
|
|
134
|
-
message: "No pending invoice",
|
|
135
|
-
human: "Je kan op dit moment niet afrekenen omdat er geen openstaand bedrag is."
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
if (pendingInvoice.invoiceId !== null) {
|
|
139
|
-
throw new simple_errors_1.SimpleError({
|
|
140
|
-
code: "payment_pending",
|
|
141
|
-
message: "Payment pending",
|
|
142
|
-
human: "Er is momenteel al een afrekening in behandeling (dit kan 3 werkdagen duren), wacht enkele dagen voor je het opnieuw probeert."
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
if (organization.serverMeta.mollieCustomerId === undefined) {
|
|
146
|
-
throw new simple_errors_1.SimpleError({
|
|
147
|
-
code: "no_mollie_customer",
|
|
148
|
-
message: "No connected mollie customer",
|
|
149
|
-
human: "Er is nog geen domiciliƫring of automatische afrekening ingesteld"
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
// Step 1: create the invoice
|
|
153
|
-
const invoice = _1.STInvoice.createFor(organization);
|
|
154
|
-
invoice.meta.items = pendingInvoice.meta.items.slice(); // make a copy (needed to prevent mutating pending invoice and invoice at the same time)
|
|
155
|
-
if (invoice.meta.priceWithVAT == 0) {
|
|
156
|
-
throw new simple_errors_1.SimpleError({
|
|
157
|
-
code: "no_pending_invoice",
|
|
158
|
-
message: "No pending invoice",
|
|
159
|
-
human: "Je kan op dit moment niet afrekenen omdat er geen openstaand bedrag is."
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
await _1.STCredit.applyCredits(organization.id, invoice, false);
|
|
163
|
-
const price = invoice.meta.priceWithVAT;
|
|
164
|
-
// Create payment
|
|
165
|
-
const payment = new _1.Payment();
|
|
166
|
-
payment.organizationId = null;
|
|
167
|
-
payment.method = structures_1.PaymentMethod.DirectDebit;
|
|
168
|
-
payment.status = structures_1.PaymentStatus.Created;
|
|
169
|
-
payment.price = price;
|
|
170
|
-
payment.paidAt = null;
|
|
171
|
-
payment.provider = structures_1.PaymentProvider.Mollie;
|
|
172
|
-
await payment.save();
|
|
173
|
-
invoice.paymentId = payment.id;
|
|
174
|
-
invoice.setRelation(_1.STInvoice.payment, payment);
|
|
175
|
-
await invoice.save();
|
|
176
|
-
const description = "Stamhoofd - " + invoice.id;
|
|
177
|
-
if (price <= 0) {
|
|
178
|
-
// Needs to happen before markPaid! (because the pending invoice will get modified)
|
|
179
|
-
pendingInvoice.invoiceId = invoice.id;
|
|
180
|
-
await pendingInvoice.save();
|
|
181
|
-
await invoice.markPaid();
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
// Mollie payment is required
|
|
185
|
-
const apiKey = STAMHOOFD.MOLLIE_API_KEY;
|
|
186
|
-
if (!apiKey) {
|
|
187
|
-
throw new simple_errors_1.SimpleError({
|
|
188
|
-
code: "",
|
|
189
|
-
message: "Betalingen zijn tijdelijk onbeschikbaar"
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
const mollieClient = (0, api_client_1.createMollieClient)({ apiKey });
|
|
193
|
-
const molliePayment = await mollieClient.payments.create({
|
|
194
|
-
amount: {
|
|
195
|
-
currency: 'EUR',
|
|
196
|
-
value: (price / 100).toFixed(2)
|
|
197
|
-
},
|
|
198
|
-
//method: molliePaymentMethod.directdebit,
|
|
199
|
-
description,
|
|
200
|
-
customerId: organization.serverMeta.mollieCustomerId,
|
|
201
|
-
sequenceType: api_client_1.SequenceType.recurring,
|
|
202
|
-
redirectUrl: "https://" + STAMHOOFD.domains.dashboard + '/settings/billing/payment?id=' + encodeURIComponent(payment.id),
|
|
203
|
-
webhookUrl: 'https://' + STAMHOOFD.domains.api + "/v" + structures_1.Version + "/billing/payments/" + encodeURIComponent(payment.id) + "?exchange=true",
|
|
204
|
-
metadata: {
|
|
205
|
-
invoiceId: invoice.id,
|
|
206
|
-
paymentId: payment.id,
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
console.log(molliePayment);
|
|
210
|
-
if (molliePayment.method === 'creditcard') {
|
|
211
|
-
console.log("Corrected payment method to creditcard");
|
|
212
|
-
payment.method = structures_1.PaymentMethod.CreditCard;
|
|
213
|
-
await payment.save();
|
|
214
|
-
}
|
|
215
|
-
// Save payment
|
|
216
|
-
const dbPayment = new _1.MolliePayment();
|
|
217
|
-
dbPayment.paymentId = payment.id;
|
|
218
|
-
dbPayment.mollieId = molliePayment.id;
|
|
219
|
-
await dbPayment.save();
|
|
220
|
-
// Only if all went okay
|
|
221
|
-
pendingInvoice.invoiceId = invoice.id;
|
|
222
|
-
await pendingInvoice.save();
|
|
223
|
-
const invoicingTo = await organization.getInvoicingToEmails();
|
|
224
|
-
if (invoicingTo && payment.method === structures_1.PaymentMethod.DirectDebit) {
|
|
225
|
-
// Generate a temporary PDF file
|
|
226
|
-
const builder = new InvoiceBuilder_1.InvoiceBuilder(invoice);
|
|
227
|
-
const pdf = await builder.build();
|
|
228
|
-
// Send the e-mail
|
|
229
|
-
email_1.Email.sendInternal({
|
|
230
|
-
to: invoicingTo,
|
|
231
|
-
bcc: "simon@stamhoofd.be",
|
|
232
|
-
subject: "Pro-forma factuur voor " + organization.name,
|
|
233
|
-
text: "Dag " + organization.name + ", \n\nBedankt voor jullie vertrouwen in Stamhoofd. Jullie hebben momenteel een openstaand bedrag van " + utility_1.Formatter.price(price) + " in Stamhoofd (zie bijlage). Zoals eerder aangegeven zal dit via domiciliƫring worden aangerekend (op dezelfde bankrekening waarmee de vorige betaling is gedaan). Hiervoor hoeven jullie dus niets te doen. Kijk eventueel na of er voldoende geld op jullie rekening staat. De betaling zal ƩƩn van de komende drie werkdagen plaatsvinden. Wil je het rekeningnummer voor de volgende betaling wijzigen? Dan kan je de stappen volgen op https://" + organization.marketingDomain + "/docs/bankrekening-domiciliering-wijzigen/.\n\nNa betaling ontvangen jullie een definitieve factuur. Je kan in Stamhoofd altijd het openstaande bedrag nakijken bij Instellingen > Facturen en betalingen. Neem gerust contact met ons op (via " + organization.i18n.$t("shared.emails.general") + ") als je denkt dat er iets fout is gegaan of als je nog bijkomende vragen zou hebben.\n\nMet vriendelijke groeten,\nStamhoofd\n\n",
|
|
234
|
-
attachments: [
|
|
235
|
-
{
|
|
236
|
-
filename: "pro-forma-factuur.pdf",
|
|
237
|
-
href: pdf.getPublicPath(),
|
|
238
|
-
contentType: "application/pdf"
|
|
239
|
-
}
|
|
240
|
-
]
|
|
241
|
-
}, organization.i18n);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
32
|
}
|
|
246
33
|
exports.STPendingInvoice = STPendingInvoice;
|
|
247
34
|
tslib_1.__decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"STPendingInvoice.js","sourceRoot":"","sources":["../../../src/models/STPendingInvoice.ts"],"names":[],"mappings":";;;;AAAA,
|
|
1
|
+
{"version":3,"file":"STPendingInvoice.js","sourceRoot":"","sources":["../../../src/models/STPendingInvoice.ts"],"names":[],"mappings":";;;;AAAA,iEAA+E;AAC/E,sDAAsD;AACtD,+BAAoC;AAEpC,yBAAkC;AAElC;;;;;;;;;;GAUG;AACH,MAAa,gBAAiB,SAAQ,uBAAK;IACvC,MAAM,CAAC,KAAK,GAAG,4BAA4B,CAAC;IAE5C,UAAU;IAMV,EAAE,CAAU;IAGZ,cAAc,CAAgB;IAG9B,IAAI,CAAe;IAEnB,wEAAwE;IACxE,sGAAsG;IAEtG,SAAS,GAAkB,IAAI,CAAA;IAY/B,SAAS,CAAM;IAUf,SAAS,CAAM;IAEf,MAAM,CAAC,YAAY,GAAG,IAAI,mCAAiB,CAAC,eAAY,EAAE,cAAc,CAAC,CAAC;;AA5C9E,4CA6CC;AApCG;IALC,IAAA,wBAAM,EAAC;QACJ,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC,KAAK;YAC3C,OAAO,KAAK,IAAI,IAAA,SAAM,GAAE,CAAC;QAC7B,CAAC;KACJ,CAAC;4CACU;AAGZ;IADC,IAAA,wBAAM,EAAC,EAAE,UAAU,EAAE,gBAAgB,CAAC,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;wDACxD;AAG9B;IADC,IAAA,wBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,0BAAa,EAAE,CAAC;8CAC9B;AAKnB;IADC,IAAA,wBAAM,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;mDACZ;AAY/B;IAVC,IAAA,wBAAM,EAAC;QACJ,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,GAAS;YAClC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACpB,OAAO,GAAG,CAAC;YACf,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAA;YACvB,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;YACvB,OAAO,IAAI,CAAA;QACf,CAAC;KACJ,CAAC;mDACa;AAUf;IARC,IAAA,wBAAM,EAAC;QACJ,IAAI,EAAE,UAAU,EAAE,UAAU;YACxB,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAA;YACvB,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;YACvB,OAAO,IAAI,CAAA;QACf,CAAC;QACD,UAAU,EAAE,IAAI;KACnB,CAAC;mDACa"}
|
|
@@ -13,10 +13,5 @@ export declare class UsedRegisterCode extends Model {
|
|
|
13
13
|
creditId: string | null;
|
|
14
14
|
createdAt: Date;
|
|
15
15
|
updatedAt: Date;
|
|
16
|
-
static getFor(organizationId: string): Promise<UsedRegisterCode | undefined>;
|
|
17
|
-
reward(): Promise<void>;
|
|
18
|
-
static getAll(code: string): Promise<UsedRegisterCode[]>;
|
|
19
|
-
static getUsed(code: string): Promise<UsedRegisterCode[]>;
|
|
20
|
-
static getUsedCount(code: string): Promise<number>;
|
|
21
16
|
}
|
|
22
17
|
//# sourceMappingURL=UsedRegisterCode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UsedRegisterCode.d.ts","sourceRoot":"","sources":["../../../src/models/UsedRegisterCode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"UsedRegisterCode.d.ts","sourceRoot":"","sources":["../../../src/models/UsedRegisterCode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAI5D,qBAAa,gBAAiB,SAAQ,KAAK;IACvC,MAAM,CAAC,KAAK,SAAyB;IAOrC,EAAE,EAAG,MAAM,CAAC;IAGZ,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IAEH,cAAc,EAAE,MAAM,CAAC;IAEvB;;OAEG;IAEH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAY/B,SAAS,EAAE,IAAI,CAAA;IAUf,SAAS,EAAE,IAAI,CAAA;CAClB"}
|
|
@@ -3,12 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.UsedRegisterCode = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const simple_database_1 = require("@simonbackx/simple-database");
|
|
6
|
-
const email_1 = require("@stamhoofd/email");
|
|
7
|
-
const utility_1 = require("@stamhoofd/utility");
|
|
8
6
|
const uuid_1 = require("uuid");
|
|
9
|
-
const _1 = require("./");
|
|
10
|
-
const structures_1 = require("@stamhoofd/structures");
|
|
11
|
-
const queues_1 = require("@stamhoofd/queues");
|
|
12
7
|
class UsedRegisterCode extends simple_database_1.Model {
|
|
13
8
|
static table = "used_register_codes";
|
|
14
9
|
id;
|
|
@@ -23,102 +18,6 @@ class UsedRegisterCode extends simple_database_1.Model {
|
|
|
23
18
|
creditId = null;
|
|
24
19
|
createdAt;
|
|
25
20
|
updatedAt;
|
|
26
|
-
static async getFor(organizationId) {
|
|
27
|
-
const code = await this.where({ organizationId }, { limit: 1 });
|
|
28
|
-
return code[0] ?? undefined;
|
|
29
|
-
}
|
|
30
|
-
async reward() {
|
|
31
|
-
if (this.creditId) {
|
|
32
|
-
// Already received
|
|
33
|
-
console.error("Already rewarded for used register code " + this.id);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
const code = await _1.RegisterCode.getByID(this.code);
|
|
37
|
-
if (!code || !code.organizationId) {
|
|
38
|
-
console.error("Couldn't find code " + this.code + " for used register code " + this.id);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
const organization = await _1.Organization.getByID(this.organizationId);
|
|
42
|
-
if (!organization) {
|
|
43
|
-
console.error("Couldn't find organization with id " + this.organizationId + " for used register code " + this.id);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const receivingOrganization = await _1.Organization.getByID(code.organizationId);
|
|
47
|
-
if (!receivingOrganization) {
|
|
48
|
-
console.error("Couldn't find receiving organization with id " + code.organizationId + " for used register code " + this.id);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
const usedCount = await UsedRegisterCode.getUsedCount(this.code) + 1;
|
|
52
|
-
const credit = new _1.STCredit();
|
|
53
|
-
credit.organizationId = code.organizationId;
|
|
54
|
-
credit.change = code.invoiceValue ? 0 : Math.min(100 * 100, usedCount * 10 * 100);
|
|
55
|
-
credit.description = organization.name + " doorverwezen š";
|
|
56
|
-
// Expire in one year (will get extended for every purchase or activation)
|
|
57
|
-
credit.expireAt = new Date();
|
|
58
|
-
credit.expireAt.setFullYear(credit.expireAt.getFullYear() + 1);
|
|
59
|
-
credit.expireAt.setMilliseconds(0);
|
|
60
|
-
await credit.save();
|
|
61
|
-
this.creditId = credit.id;
|
|
62
|
-
await this.save();
|
|
63
|
-
if (code.invoiceValue) {
|
|
64
|
-
const name = "Aankoop Stamhoofd voor " + organization.name;
|
|
65
|
-
const item = structures_1.STInvoiceItem.create({
|
|
66
|
-
name,
|
|
67
|
-
description: "Via doorverwijzingscode",
|
|
68
|
-
amount: 1,
|
|
69
|
-
unitPrice: code.invoiceValue,
|
|
70
|
-
canUseCredits: false
|
|
71
|
-
});
|
|
72
|
-
console.log("Scheduling code charge for ", code);
|
|
73
|
-
await queues_1.QueueHandler.schedule("billing/invoices-" + receivingOrganization.id, async () => {
|
|
74
|
-
await _1.STPendingInvoice.addItems(receivingOrganization, [item]);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
const admins = await receivingOrganization.getAdminToEmails();
|
|
78
|
-
if (admins) {
|
|
79
|
-
if (code.invoiceValue) {
|
|
80
|
-
email_1.Email.sendInternal({
|
|
81
|
-
to: admins,
|
|
82
|
-
bcc: "simon@stamhoofd.be",
|
|
83
|
-
subject: `${organization.name} heeft jullie tegoed gebruikt`,
|
|
84
|
-
text: "Dag " + receivingOrganization.name + ",\n\n" + organization.name + " had jullie doorverwijzingslink gebruikt om zich op Stamhoofd te registreren, en nu hebben ze dit ook gebruikt. Zoals afgesproken wordt hiervoor " + utility_1.Formatter.price(code.invoiceValue) + " aangerekend via jullie openstaand saldo in jullie Stamhoofd account."
|
|
85
|
-
+ "\n\nā Stamhoofd"
|
|
86
|
-
}, organization.i18n);
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
// Delay email until everything is validated and saved
|
|
90
|
-
email_1.Email.sendInternal({
|
|
91
|
-
to: admins,
|
|
92
|
-
bcc: "simon@stamhoofd.be",
|
|
93
|
-
subject: "Je hebt " + utility_1.Formatter.price(credit.change) + " tegoed ontvangen š°",
|
|
94
|
-
text: "Dag " + receivingOrganization.name + ",\n\nGeweldig nieuws! " + organization.name + " had jullie doorverwijzingslink gebruikt om zich op Stamhoofd te registreren, en nu hebben ze ook voor het eerst minstens ƩƩn euro uitgegeven. Daardoor ontvangen jullie " + utility_1.Formatter.price(credit.change) + " tegoed voor Stamhoofd (zie daarvoor Stamhoofd > Instellingen). "
|
|
95
|
-
+ (credit.change <= 90 * 100 ? ("Bij de volgende vereniging ontvangen jullie nog meer: " + utility_1.Formatter.price(credit.change + 10 * 100) + ". ") : "")
|
|
96
|
-
+ (credit.change <= 80 * 100 ? ("En dat blijft oplopen tot ⬠100,00 per vereniging die je aanbrengt š ") : "")
|
|
97
|
-
+ "Doe zo verder! Lees zeker onze tips na om nog een groter bedrag te verzamelen š\n\nā Stamhoofd"
|
|
98
|
-
}, organization.i18n);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
static async getAll(code) {
|
|
103
|
-
const used = await UsedRegisterCode.where({
|
|
104
|
-
code
|
|
105
|
-
});
|
|
106
|
-
return used;
|
|
107
|
-
}
|
|
108
|
-
static async getUsed(code) {
|
|
109
|
-
const used = await UsedRegisterCode.where({
|
|
110
|
-
code,
|
|
111
|
-
creditId: {
|
|
112
|
-
value: null,
|
|
113
|
-
sign: "!="
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
return used;
|
|
117
|
-
}
|
|
118
|
-
static async getUsedCount(code) {
|
|
119
|
-
const used = await this.getUsed(code);
|
|
120
|
-
return used.length;
|
|
121
|
-
}
|
|
122
21
|
}
|
|
123
22
|
exports.UsedRegisterCode = UsedRegisterCode;
|
|
124
23
|
tslib_1.__decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UsedRegisterCode.js","sourceRoot":"","sources":["../../../src/models/UsedRegisterCode.ts"],"names":[],"mappings":";;;;AAAA,iEAA4D;AAC5D
|
|
1
|
+
{"version":3,"file":"UsedRegisterCode.js","sourceRoot":"","sources":["../../../src/models/UsedRegisterCode.ts"],"names":[],"mappings":";;;;AAAA,iEAA4D;AAC5D,+BAAoC;AAGpC,MAAa,gBAAiB,SAAQ,uBAAK;IACvC,MAAM,CAAC,KAAK,GAAG,qBAAqB,CAAC;IAOrC,EAAE,CAAU;IAGZ,IAAI,CAAS;IAEb;;OAEG;IAEH,cAAc,CAAS;IAEvB;;OAEG;IAEH,QAAQ,GAAkB,IAAI,CAAC;IAY/B,SAAS,CAAM;IAUf,SAAS,CAAM;;AA7CnB,4CA8CC;AAtCG;IALC,IAAA,wBAAM,EAAC;QACJ,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC,KAAK;YAC3C,OAAO,KAAK,IAAI,IAAA,SAAM,GAAE,CAAC;QAC7B,CAAC;KACJ,CAAC;4CACU;AAGZ;IADC,IAAA,wBAAM,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;8CACd;AAMb;IADC,IAAA,wBAAM,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;wDACJ;AAMvB;IADC,IAAA,wBAAM,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;kDACZ;AAY/B;IAVC,IAAA,wBAAM,EAAC;QACJ,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,GAAS;YAClC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACpB,OAAO,GAAG,CAAC;YACf,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAA;YACvB,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;YACvB,OAAO,IAAI,CAAA;QACf,CAAC;KACJ,CAAC;mDACa;AAUf;IARC,IAAA,wBAAM,EAAC;QACJ,IAAI,EAAE,UAAU,EAAE,UAAU;YACxB,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAA;YACvB,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;YACvB,OAAO,IAAI,CAAA;QACf,CAAC;QACD,UAAU,EAAE,IAAI;KACnB,CAAC;mDACa"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/models",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.0",
|
|
4
4
|
"main": "./dist/src/index.js",
|
|
5
5
|
"types": "./dist/src/index.d.ts",
|
|
6
6
|
"license": "UNLICENCED",
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
"publishConfig": {
|
|
30
30
|
"access": "public"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "946c0ddcaa7cdc9b769d4dc9083a517fe7ad19f8"
|
|
33
33
|
}
|
package/src/models/Member.ts
CHANGED
|
@@ -359,11 +359,11 @@ export class Member extends Model {
|
|
|
359
359
|
.from(Member.table)
|
|
360
360
|
.join(
|
|
361
361
|
SQL
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
362
|
+
.leftJoin('_members_users')
|
|
363
|
+
.where(
|
|
364
|
+
SQL.column('_members_users', 'membersId'),
|
|
365
|
+
SQL.column(Member.table, 'id'),
|
|
366
|
+
)
|
|
367
367
|
).where(
|
|
368
368
|
SQL.column('_members_users', 'usersId'),
|
|
369
369
|
user.id,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { column, Model } from '@simonbackx/simple-database';
|
|
2
|
-
import {
|
|
1
|
+
import { column, Model, SQLResultNamespacedRow } from '@simonbackx/simple-database';
|
|
2
|
+
import { SQL, SQLSelect } from '@stamhoofd/sql';
|
|
3
3
|
import { MemberResponsibilityRecord as MemberResponsibilityRecordStruct } from '@stamhoofd/structures';
|
|
4
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
5
|
|
|
5
6
|
export class MemberResponsibilityRecord extends Model {
|
|
6
7
|
static table = "member_responsibility_records"
|
|
@@ -43,4 +44,22 @@ export class MemberResponsibilityRecord extends Model {
|
|
|
43
44
|
getStructure() {
|
|
44
45
|
return MemberResponsibilityRecordStruct.create(this)
|
|
45
46
|
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Experimental: needs to move to library
|
|
50
|
+
*/
|
|
51
|
+
static select() {
|
|
52
|
+
const transformer = (row: SQLResultNamespacedRow): MemberResponsibilityRecord => {
|
|
53
|
+
const d = (this as typeof MemberResponsibilityRecord & typeof Model).fromRow(row[this.table] as any) as MemberResponsibilityRecord|undefined
|
|
54
|
+
|
|
55
|
+
if (!d) {
|
|
56
|
+
throw new Error("MemberResponsibilityRecord not found")
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return d;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const select = new SQLSelect(transformer, SQL.wildcard())
|
|
63
|
+
return select.from(SQL.table(this.table))
|
|
64
|
+
}
|
|
46
65
|
}
|
|
@@ -220,6 +220,14 @@ export class Organization extends Model {
|
|
|
220
220
|
message: "No organization known for host " + host,
|
|
221
221
|
});
|
|
222
222
|
}
|
|
223
|
+
|
|
224
|
+
if (!organization.active) {
|
|
225
|
+
throw new SimpleError({
|
|
226
|
+
code: "archived",
|
|
227
|
+
message: "This organization is archived",
|
|
228
|
+
human: 'Deze groep is gearchiveerd'
|
|
229
|
+
});
|
|
230
|
+
}
|
|
223
231
|
return organization;
|
|
224
232
|
}
|
|
225
233
|
|
|
@@ -264,7 +272,13 @@ export class Organization extends Model {
|
|
|
264
272
|
return this.id+"." + defaultDomain;
|
|
265
273
|
}
|
|
266
274
|
|
|
275
|
+
private _cachedPeriod?: OrganizationRegistrationPeriod
|
|
276
|
+
|
|
267
277
|
async getPeriod() {
|
|
278
|
+
if (this._cachedPeriod) {
|
|
279
|
+
return this._cachedPeriod;
|
|
280
|
+
}
|
|
281
|
+
|
|
268
282
|
const oPeriods = await OrganizationRegistrationPeriod.where({ periodId: this.periodId, organizationId: this.id }, {limit: 1})
|
|
269
283
|
|
|
270
284
|
let oPeriod: OrganizationRegistrationPeriod;
|
|
@@ -289,12 +303,14 @@ export class Organization extends Model {
|
|
|
289
303
|
oPeriod = oPeriods[0];
|
|
290
304
|
}
|
|
291
305
|
|
|
306
|
+
this._cachedPeriod = oPeriod
|
|
292
307
|
return oPeriod
|
|
293
308
|
}
|
|
294
309
|
|
|
295
310
|
getBaseStructure(): OrganizationStruct {
|
|
296
311
|
return OrganizationStruct.create({
|
|
297
312
|
id: this.id,
|
|
313
|
+
active: this.active,
|
|
298
314
|
name: this.name,
|
|
299
315
|
meta: this.meta,
|
|
300
316
|
address: this.address,
|
|
@@ -792,13 +808,17 @@ export class Organization extends Model {
|
|
|
792
808
|
return await this.getAdminToEmails()
|
|
793
809
|
}
|
|
794
810
|
|
|
811
|
+
async getAdmins() {
|
|
812
|
+
// Circular reference fix
|
|
813
|
+
const User = (await import('./User')).User;
|
|
814
|
+
return await User.getAdmins([this.id], {verified: true});
|
|
815
|
+
}
|
|
816
|
+
|
|
795
817
|
/**
|
|
796
818
|
* These email addresess are private
|
|
797
819
|
*/
|
|
798
820
|
async getFullAdmins() {
|
|
799
|
-
|
|
800
|
-
const User = (await import('./User')).User;
|
|
801
|
-
const admins = await User.getAdmins([this.id], {verified: true})
|
|
821
|
+
const admins = await this.getAdmins();
|
|
802
822
|
|
|
803
823
|
// Only full access
|
|
804
824
|
return admins.filter(a => a.permissions && a.permissions.forOrganization(this)?.hasFullAccess())
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { column, Model } from "@simonbackx/simple-database";
|
|
2
|
-
import { SimpleError } from "@simonbackx/simple-errors";
|
|
3
|
-
import { EmailInterfaceBase } from "@stamhoofd/email";
|
|
4
2
|
import basex from "base-x";
|
|
5
3
|
import crypto from "crypto";
|
|
6
4
|
|
|
7
|
-
import { Organization } from "./Organization";
|
|
8
|
-
import { STCredit } from "./STCredit";
|
|
9
|
-
import { UsedRegisterCode } from "./UsedRegisterCode";
|
|
10
5
|
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
|
11
6
|
const bs58 = basex(ALPHABET)
|
|
12
7
|
|
|
@@ -71,94 +66,4 @@ export class RegisterCode extends Model {
|
|
|
71
66
|
async generateCode() {
|
|
72
67
|
this.code = bs58.encode(await randomBytes(8)).toUpperCase();
|
|
73
68
|
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Prepares a register code for usage.
|
|
77
|
-
* Returns the models to save and the emails to send when succesful
|
|
78
|
-
*/
|
|
79
|
-
static async applyRegisterCode(organization: Organization, codeString: string): Promise<{models: Model[], emails: EmailInterfaceBase[]}> {
|
|
80
|
-
const alreadyUsed = await UsedRegisterCode.getFor(organization.id)
|
|
81
|
-
if (alreadyUsed) {
|
|
82
|
-
throw new SimpleError({
|
|
83
|
-
code: "invalid_field",
|
|
84
|
-
message: "Invalid register code",
|
|
85
|
-
human: "Je hebt al een doorverwijzingscode gebruikt",
|
|
86
|
-
field: "registerCode",
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const code = await RegisterCode.getByID(codeString)
|
|
91
|
-
if (!code) {
|
|
92
|
-
throw new SimpleError({
|
|
93
|
-
code: "invalid_field",
|
|
94
|
-
message: "Invalid register code",
|
|
95
|
-
human: "De doorverwijzingscode die je hebt opgegeven is niet langer geldig",
|
|
96
|
-
field: "registerCode",
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const otherOrganization = code.organizationId ? await Organization.getByID(code.organizationId) : undefined
|
|
101
|
-
|
|
102
|
-
const delayEmails: EmailInterfaceBase[] = []
|
|
103
|
-
let credit: STCredit | undefined = undefined
|
|
104
|
-
let usedCode: UsedRegisterCode | undefined = undefined
|
|
105
|
-
|
|
106
|
-
if (code.value > 0) {
|
|
107
|
-
// Create initial credit
|
|
108
|
-
credit = new STCredit()
|
|
109
|
-
credit.organizationId = organization.id
|
|
110
|
-
credit.change = code.value
|
|
111
|
-
credit.description = otherOrganization ? ("Tegoed gekregen van "+otherOrganization.name) : code.description
|
|
112
|
-
|
|
113
|
-
// Expire in one year (will get extended for every purchase or activation)
|
|
114
|
-
credit.expireAt = new Date()
|
|
115
|
-
credit.expireAt.setFullYear(credit.expireAt.getFullYear() + 1)
|
|
116
|
-
credit.expireAt.setMilliseconds(0)
|
|
117
|
-
|
|
118
|
-
// Save later
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (otherOrganization) {
|
|
122
|
-
const admins = await otherOrganization.getAdminToEmails()
|
|
123
|
-
if (admins) {
|
|
124
|
-
if (code.invoiceValue) {
|
|
125
|
-
// Delay email until everything is validated and saved
|
|
126
|
-
delayEmails.push({
|
|
127
|
-
to: admins,
|
|
128
|
-
bcc: "simon@stamhoofd.be",
|
|
129
|
-
subject: organization.name+" heeft jullie doorverwijzingslink gebruikt š„³",
|
|
130
|
-
type: "transactional",
|
|
131
|
-
text: "Dag "+otherOrganization.name+",\n\nGoed nieuws! "+organization.name+" heeft jullie doorverwijzingslink gebruikt om zich op Stamhoofd te registreren.\n\nā Stamhoofd"
|
|
132
|
-
})
|
|
133
|
-
} else {
|
|
134
|
-
// Delay email until everything is validated and saved
|
|
135
|
-
delayEmails.push({
|
|
136
|
-
to: admins,
|
|
137
|
-
bcc: "simon@stamhoofd.be",
|
|
138
|
-
subject: organization.name+" heeft jullie doorverwijzingslink gebruikt š„³",
|
|
139
|
-
type: "transactional",
|
|
140
|
-
text: "Dag "+otherOrganization.name+",\n\nGoed nieuws! "+organization.name+" heeft jullie doorverwijzingslink gebruikt om zich op Stamhoofd te registreren. Als zij minstens 1 euro op Stamhoofd uitgeven ontvangen jullie een tegoed dat kan oplopen tot 100 euro per vereniging (zie daarvoor Stamhoofd > Instellingen). Lees zeker onze tips na om nog een groter bedrag te verzamelen š\n\nā Stamhoofd"
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
} else {
|
|
145
|
-
delayEmails.push({
|
|
146
|
-
to: 'hallo@stamhoofd.be',
|
|
147
|
-
subject: organization.name+" heeft jullie doorverwijzingslink gebruikt š„³",
|
|
148
|
-
type: "transactional",
|
|
149
|
-
text: "Dag Stamhoofd,\n\nGoed nieuws! "+organization.name+" heeft jullie doorverwijzingslink "+code.code+" gebruikt om zich op Stamhoofd te registreren. \n\nā Stamhoofd"
|
|
150
|
-
})
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Save that we used this code (so we can reward the other organization)
|
|
154
|
-
usedCode = new UsedRegisterCode()
|
|
155
|
-
usedCode.organizationId = organization.id
|
|
156
|
-
usedCode.code = code.code
|
|
157
|
-
|
|
158
|
-
// Save later
|
|
159
|
-
return {
|
|
160
|
-
models: credit ? [credit, usedCode] : [usedCode],
|
|
161
|
-
emails: delayEmails
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
69
|
}
|