@stamhoofd/backend 2.17.4 → 2.18.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/package.json +5 -5
- package/src/crons/setup-steps.ts +9 -0
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +6 -14
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +66 -4
- package/src/endpoints/organization/dashboard/billing/GetBillingStatusEndpoint.ts +6 -7
- package/src/endpoints/organization/dashboard/billing/GetDetailedBillingStatusEndpoint.ts +78 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +4 -0
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +6 -2
- package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +78 -0
- package/src/excel-loaders/payments.ts +36 -4
- package/src/helpers/AuthenticatedStructures.ts +6 -7
- package/src/helpers/SetupStepsUpdater.ts +210 -0
- package/src/seeds/1724076679-setup-steps.ts +16 -0
- package/src/endpoints/admin/invoices/GetInvoicesCountEndpoint.ts +0 -47
- package/src/endpoints/admin/invoices/GetInvoicesEndpoint.ts +0 -185
- package/src/endpoints/organization/dashboard/billing/ActivatePackagesEndpoint.ts +0 -424
- package/src/endpoints/organization/dashboard/billing/DeactivatePackageEndpoint.ts +0 -67
|
@@ -1,424 +0,0 @@
|
|
|
1
|
-
import { createMollieClient, PaymentMethod as molliePaymentMethod, SequenceType } from '@mollie/api-client';
|
|
2
|
-
import { BankTransferDetails } from '@mollie/api-client/dist/types/src/data/payments/data';
|
|
3
|
-
import { ArrayDecoder, AutoEncoder, AutoEncoderPatchType, BooleanDecoder, Decoder, EnumDecoder, field, StringDecoder } from "@simonbackx/simple-encoding";
|
|
4
|
-
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
5
|
-
import { isSimpleError, isSimpleErrors, SimpleError } from "@simonbackx/simple-errors";
|
|
6
|
-
import { MolliePayment, Payment, Registration, STCredit, STInvoice, STPackage, STPendingInvoice, Token } from "@stamhoofd/models";
|
|
7
|
-
import { QueueHandler } from '@stamhoofd/queues';
|
|
8
|
-
import { Organization as OrganizationStruct, OrganizationPatch, PaymentMethod, PaymentProvider, PaymentStatus, STInvoiceItem, STInvoiceResponse, STPackage as STPackageStruct, STPackageBundle, STPackageBundleHelper, STPricingType, TransferSettings, User as UserStruct, Version } from "@stamhoofd/structures";
|
|
9
|
-
|
|
10
|
-
import { Context } from '../../../../helpers/Context';
|
|
11
|
-
|
|
12
|
-
type Params = Record<string, never>;
|
|
13
|
-
type Query = undefined;
|
|
14
|
-
type ResponseBody = STInvoiceResponse;
|
|
15
|
-
|
|
16
|
-
class Body extends AutoEncoder {
|
|
17
|
-
@field({ decoder: new ArrayDecoder(new EnumDecoder(STPackageBundle)), optional: true })
|
|
18
|
-
bundles: STPackageBundle[] = []
|
|
19
|
-
|
|
20
|
-
@field({ decoder: new ArrayDecoder(StringDecoder), optional: true })
|
|
21
|
-
renewPackageIds: string[] = []
|
|
22
|
-
|
|
23
|
-
@field({ decoder: BooleanDecoder, optional: true })
|
|
24
|
-
includePending = false
|
|
25
|
-
|
|
26
|
-
@field({ decoder: new EnumDecoder(PaymentMethod) })
|
|
27
|
-
paymentMethod: PaymentMethod
|
|
28
|
-
|
|
29
|
-
@field({ decoder: BooleanDecoder, optional: true })
|
|
30
|
-
proForma = false
|
|
31
|
-
|
|
32
|
-
@field({ decoder: OrganizationPatch, optional: true })
|
|
33
|
-
organizationPatch?: AutoEncoderPatchType<OrganizationStruct>
|
|
34
|
-
|
|
35
|
-
@field({ decoder: UserStruct.patchType(), optional: true })
|
|
36
|
-
userPatch?: AutoEncoderPatchType<UserStruct>
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class ActivatePackagesEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
40
|
-
bodyDecoder = Body as Decoder<Body>
|
|
41
|
-
|
|
42
|
-
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
43
|
-
if (request.method != "POST") {
|
|
44
|
-
return [false];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const params = Endpoint.parseParameters(request.url, "/billing/activate-packages", {});
|
|
48
|
-
|
|
49
|
-
if (params) {
|
|
50
|
-
return [true, params as Params];
|
|
51
|
-
}
|
|
52
|
-
return [false];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
56
|
-
const organization = await Context.setOrganizationScope();
|
|
57
|
-
const {user} = await Context.authenticate()
|
|
58
|
-
|
|
59
|
-
// If the user has permission, we'll also search if he has access to the organization's key
|
|
60
|
-
if (!await Context.auth.canActivatePackages(organization.id)) {
|
|
61
|
-
throw Context.auth.error()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Apply patches if needed
|
|
65
|
-
if (request.body.userPatch) {
|
|
66
|
-
user.firstName = request.body.userPatch.firstName ?? user.firstName
|
|
67
|
-
user.lastName = request.body.userPatch.lastName ?? user.lastName
|
|
68
|
-
|
|
69
|
-
if (!request.body.proForma) {
|
|
70
|
-
await user.save()
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Apply patches if needed
|
|
75
|
-
if (user.firstName && user.lastName) {
|
|
76
|
-
organization.privateMeta.billingContact = user.firstName + " " + user.lastName
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (request.body.organizationPatch) {
|
|
80
|
-
console.log("Received patch in activatePackagesEndpoint", request.body.organizationPatch)
|
|
81
|
-
if (request.body.organizationPatch.address) {
|
|
82
|
-
organization.address.patchOrPut(request.body.organizationPatch.address)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (request.body.organizationPatch.meta) {
|
|
86
|
-
organization.meta.patchOrPut(request.body.organizationPatch.meta)
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (!request.body.proForma) {
|
|
91
|
-
await organization.save();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return await QueueHandler.schedule("billing/invoices-"+organization.id, async () => {
|
|
95
|
-
const currentPackages = await STPackage.getForOrganization(organization.id)
|
|
96
|
-
|
|
97
|
-
// Create packages
|
|
98
|
-
const packages: STPackageStruct[] = [];
|
|
99
|
-
for (const bundle of request.body.bundles) {
|
|
100
|
-
// Renew after currently running packages
|
|
101
|
-
let date = new Date()
|
|
102
|
-
|
|
103
|
-
let skip = false
|
|
104
|
-
// Do we have a collision?
|
|
105
|
-
for (const currentPack of currentPackages) {
|
|
106
|
-
if (!STPackageBundleHelper.isCombineable(bundle, STPackageStruct.create(currentPack))) {
|
|
107
|
-
if (!STPackageBundleHelper.isStackable(bundle, STPackageStruct.create(currentPack))) {
|
|
108
|
-
// WE skip silently
|
|
109
|
-
console.error("Tried to activate non combineable, non stackable packages...")
|
|
110
|
-
skip = true
|
|
111
|
-
continue
|
|
112
|
-
/*throw new SimpleError({
|
|
113
|
-
code: "not_combineable",
|
|
114
|
-
message: "Het pakket dat je wilt activeren is al actief of is niet combineerbaar. Herlaad de pagina, mogelijk zie je een verouderde weergave van jouw geactiveerde pakketten."
|
|
115
|
-
})*/
|
|
116
|
-
}
|
|
117
|
-
if (currentPack.validUntil !== null) {
|
|
118
|
-
const end = currentPack.validUntil
|
|
119
|
-
if (end > date) {
|
|
120
|
-
date = end
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (skip) {
|
|
128
|
-
continue
|
|
129
|
-
}
|
|
130
|
-
packages.push(STPackageBundleHelper.getCurrentPackage(bundle, date))
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const invoice = STInvoice.createFor(organization)
|
|
134
|
-
const date = new Date()
|
|
135
|
-
|
|
136
|
-
invoice.meta.ipAddress = request.request.getIP()
|
|
137
|
-
invoice.meta.userAgent = request.headers["user-agent"] ?? null
|
|
138
|
-
|
|
139
|
-
let membersCount: number | null = null
|
|
140
|
-
|
|
141
|
-
// Save packages as models
|
|
142
|
-
const models: STPackage[] = []
|
|
143
|
-
for (const pack of packages) {
|
|
144
|
-
const model = new STPackage()
|
|
145
|
-
model.id = pack.id
|
|
146
|
-
model.meta = pack.meta
|
|
147
|
-
model.validUntil = pack.validUntil
|
|
148
|
-
model.removeAt = pack.removeAt
|
|
149
|
-
|
|
150
|
-
// Not yet valid / active (ignored until valid)
|
|
151
|
-
model.validAt = null
|
|
152
|
-
model.organizationId = organization.id
|
|
153
|
-
|
|
154
|
-
// If type is
|
|
155
|
-
let amount = 1
|
|
156
|
-
|
|
157
|
-
if (membersCount === null && pack.meta.pricingType === STPricingType.PerMember) {
|
|
158
|
-
membersCount = await Registration.getActiveMembers(organization.id)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (pack.meta.pricingType === STPricingType.PerMember) {
|
|
162
|
-
amount = membersCount ?? 1
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Add items to invoice
|
|
166
|
-
invoice.meta.items.push(
|
|
167
|
-
STInvoiceItem.fromPackage(pack, amount, 0, date)
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
if (!request.body.proForma) {
|
|
171
|
-
await model.save()
|
|
172
|
-
}
|
|
173
|
-
models.push(model)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Add renewals
|
|
177
|
-
if (request.body.renewPackageIds.length > 0) {
|
|
178
|
-
const currentPackages = await STPackage.getForOrganization(organization.id)
|
|
179
|
-
|
|
180
|
-
for (const id of request.body.renewPackageIds) {
|
|
181
|
-
const pack = currentPackages.find(c => c.id === id)
|
|
182
|
-
if (!pack) {
|
|
183
|
-
throw new SimpleError({
|
|
184
|
-
code: "not_found",
|
|
185
|
-
message: "Package not found",
|
|
186
|
-
human: "Het pakket dat je wil verlengen kan je helaas niet meer verlengen"
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Renew
|
|
191
|
-
const model = pack.createRenewed()
|
|
192
|
-
|
|
193
|
-
// If type is
|
|
194
|
-
let amount = 1
|
|
195
|
-
|
|
196
|
-
if (membersCount === null && pack.meta.pricingType === STPricingType.PerMember) {
|
|
197
|
-
membersCount = await Registration.getActiveMembers(organization.id)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (pack.meta.pricingType === STPricingType.PerMember) {
|
|
201
|
-
amount = membersCount ?? 1
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Add items to invoice
|
|
205
|
-
invoice.meta.items.push(
|
|
206
|
-
STInvoiceItem.fromPackage(STPackageStruct.create(model), amount, 0, date)
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
if (!request.body.proForma) {
|
|
210
|
-
await model.save()
|
|
211
|
-
}
|
|
212
|
-
models.push(model)
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Calculate price
|
|
217
|
-
if (invoice.meta.priceWithVAT > 0 || request.body.includePending) {
|
|
218
|
-
|
|
219
|
-
// Since we are about the pay something:
|
|
220
|
-
// also add the items that are in the pending queue
|
|
221
|
-
const pendingInvoice = await STPendingInvoice.addAutomaticItems(organization)
|
|
222
|
-
if (pendingInvoice && pendingInvoice.invoiceId === null && pendingInvoice.meta.items.length) {
|
|
223
|
-
if (!request.body.proForma) {
|
|
224
|
-
// Already generate an ID for the invoice
|
|
225
|
-
await invoice.save()
|
|
226
|
-
|
|
227
|
-
// Block usage of this pending invoice until this payment is finished (failed or succeeded)
|
|
228
|
-
pendingInvoice.invoiceId = invoice.id
|
|
229
|
-
await pendingInvoice.save()
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Add the items to our invoice
|
|
233
|
-
invoice.meta.items.push(...pendingInvoice.meta.items)
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Apply credits
|
|
238
|
-
await STCredit.applyCredits(organization.id, invoice, request.body.proForma)
|
|
239
|
-
|
|
240
|
-
const price = invoice.meta.priceWithVAT
|
|
241
|
-
|
|
242
|
-
if (price < 0) {
|
|
243
|
-
throw new Error("Unexpected negative price")
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (!request.body.proForma) {
|
|
247
|
-
// Create payment
|
|
248
|
-
const payment = new Payment()
|
|
249
|
-
payment.organizationId = null
|
|
250
|
-
payment.method = request.body.paymentMethod
|
|
251
|
-
payment.status = PaymentStatus.Created
|
|
252
|
-
payment.price = price
|
|
253
|
-
payment.paidAt = null
|
|
254
|
-
payment.provider = PaymentProvider.Mollie
|
|
255
|
-
|
|
256
|
-
// Do some quick validatiosn before creating the payment (otherwise we'll have to mark it as failed)
|
|
257
|
-
let _molliePaymentMethod: molliePaymentMethod | undefined = molliePaymentMethod.bancontact
|
|
258
|
-
let sequenceType: SequenceType | undefined = SequenceType.first
|
|
259
|
-
|
|
260
|
-
if (payment.method == PaymentMethod.iDEAL) {
|
|
261
|
-
_molliePaymentMethod = molliePaymentMethod.ideal
|
|
262
|
-
} else if (payment.method == PaymentMethod.CreditCard) {
|
|
263
|
-
_molliePaymentMethod = molliePaymentMethod.creditcard
|
|
264
|
-
} else if (payment.method == PaymentMethod.Transfer) {
|
|
265
|
-
_molliePaymentMethod = molliePaymentMethod.banktransfer
|
|
266
|
-
sequenceType = SequenceType.oneoff
|
|
267
|
-
} else if (payment.method == PaymentMethod.DirectDebit) {
|
|
268
|
-
const pendingInvoice = await STPendingInvoice.getForOrganization(organization.id)
|
|
269
|
-
|
|
270
|
-
if (pendingInvoice && pendingInvoice.invoiceId !== null && pendingInvoice.invoiceId !== invoice.id) {
|
|
271
|
-
throw new SimpleError({
|
|
272
|
-
code: "payment_pending",
|
|
273
|
-
message: "Payment pending",
|
|
274
|
-
human: "Er is momenteel al een afrekening in behandeling (dit kan 3 werkdagen duren). Probeer een andere betaalmethode."
|
|
275
|
-
})
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Use saved payment method
|
|
279
|
-
_molliePaymentMethod = undefined
|
|
280
|
-
sequenceType = SequenceType.recurring
|
|
281
|
-
|
|
282
|
-
if (!organization.serverMeta.mollieCustomerId) {
|
|
283
|
-
throw new SimpleError({
|
|
284
|
-
code: "no_mollie_customer",
|
|
285
|
-
message: "Er is geen opgeslagen betaalmethode gevonden. Probeer te betalen via een andere betaalmethode."
|
|
286
|
-
})
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
await payment.save()
|
|
291
|
-
|
|
292
|
-
invoice.paymentId = payment.id
|
|
293
|
-
invoice.setRelation(STInvoice.payment, payment)
|
|
294
|
-
|
|
295
|
-
await invoice.save()
|
|
296
|
-
|
|
297
|
-
const description = "Stamhoofd - "+invoice.id
|
|
298
|
-
|
|
299
|
-
if (price == 0) {
|
|
300
|
-
await invoice.markPaid()
|
|
301
|
-
return new Response(STInvoiceResponse.create({
|
|
302
|
-
paymentUrl: undefined,
|
|
303
|
-
invoice: await invoice.getStructure()
|
|
304
|
-
}));
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
try {
|
|
308
|
-
// Mollie payment is required
|
|
309
|
-
const apiKey = STAMHOOFD.MOLLIE_API_KEY
|
|
310
|
-
if (!apiKey) {
|
|
311
|
-
throw new SimpleError({
|
|
312
|
-
code: "",
|
|
313
|
-
message: "Betalingen zijn tijdelijk onbeschikbaar"
|
|
314
|
-
})
|
|
315
|
-
}
|
|
316
|
-
const mollieClient = createMollieClient({ apiKey });
|
|
317
|
-
let customerId = organization.serverMeta.mollieCustomerId
|
|
318
|
-
|
|
319
|
-
if (!organization.serverMeta.mollieCustomerId) {
|
|
320
|
-
if (payment.method === PaymentMethod.DirectDebit) {
|
|
321
|
-
throw new SimpleError({
|
|
322
|
-
code: "no_mollie_customer",
|
|
323
|
-
message: "Er is geen opgeslagen betaalmethode gevonden. Probeer te betalen via een andere betaalmethode."
|
|
324
|
-
})
|
|
325
|
-
}
|
|
326
|
-
const mollieCustomer = await mollieClient.customers.create({
|
|
327
|
-
name: organization.name,
|
|
328
|
-
email: user.email,
|
|
329
|
-
metadata: {
|
|
330
|
-
organizationId: organization.id,
|
|
331
|
-
userId: user.id,
|
|
332
|
-
}
|
|
333
|
-
})
|
|
334
|
-
|
|
335
|
-
customerId = mollieCustomer.id
|
|
336
|
-
organization.serverMeta.mollieCustomerId = mollieCustomer.id
|
|
337
|
-
console.log("Saving new mollie customer", mollieCustomer, "for organization", organization.id)
|
|
338
|
-
await organization.save()
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const molliePayment = await mollieClient.payments.create({
|
|
342
|
-
amount: {
|
|
343
|
-
currency: 'EUR',
|
|
344
|
-
value: (price / 100).toFixed(2)
|
|
345
|
-
},
|
|
346
|
-
method: _molliePaymentMethod,
|
|
347
|
-
description,
|
|
348
|
-
customerId,
|
|
349
|
-
sequenceType,
|
|
350
|
-
redirectUrl: "https://"+STAMHOOFD.domains.dashboard+'/settings/billing/payment?id='+encodeURIComponent(payment.id),
|
|
351
|
-
webhookUrl: 'https://'+STAMHOOFD.domains.api+"/v"+Version+"/billing/payments/"+encodeURIComponent(payment.id)+"?exchange=true",
|
|
352
|
-
metadata: {
|
|
353
|
-
invoiceId: invoice.id,
|
|
354
|
-
paymentId: payment.id,
|
|
355
|
-
},
|
|
356
|
-
billingEmail: user.email,
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
if (molliePayment.method === 'creditcard') {
|
|
360
|
-
console.log("Corrected payment method to creditcard")
|
|
361
|
-
payment.method = PaymentMethod.CreditCard
|
|
362
|
-
await payment.save();
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
console.log(molliePayment)
|
|
366
|
-
const paymentUrl = molliePayment.getCheckoutUrl() ?? undefined
|
|
367
|
-
|
|
368
|
-
// Save payment
|
|
369
|
-
const dbPayment = new MolliePayment()
|
|
370
|
-
dbPayment.paymentId = payment.id
|
|
371
|
-
dbPayment.mollieId = molliePayment.id
|
|
372
|
-
await dbPayment.save();
|
|
373
|
-
|
|
374
|
-
if ((molliePayment.details as BankTransferDetails)?.transferReference) {
|
|
375
|
-
const details = molliePayment.details as BankTransferDetails
|
|
376
|
-
payment.transferSettings = TransferSettings.create({
|
|
377
|
-
iban: details.bankAccount,
|
|
378
|
-
creditor: 'Stamhoofd',
|
|
379
|
-
})
|
|
380
|
-
payment.transferDescription = details.transferReference
|
|
381
|
-
await payment.save()
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (sequenceType === SequenceType.recurring) {
|
|
385
|
-
// Activate package
|
|
386
|
-
await invoice.activatePackages(false)
|
|
387
|
-
await STPackage.updateOrganizationPackages(organization.id)
|
|
388
|
-
|
|
389
|
-
const pendingInvoice = await STPendingInvoice.getForOrganization(organization.id)
|
|
390
|
-
if (pendingInvoice) {
|
|
391
|
-
pendingInvoice.invoiceId = invoice.id
|
|
392
|
-
await pendingInvoice.save()
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return new Response(STInvoiceResponse.create({
|
|
397
|
-
paymentUrl: paymentUrl,
|
|
398
|
-
invoice: await invoice.getStructure()
|
|
399
|
-
}));
|
|
400
|
-
} catch (e) {
|
|
401
|
-
console.error(e)
|
|
402
|
-
payment.status = PaymentStatus.Failed
|
|
403
|
-
await payment.save()
|
|
404
|
-
await invoice.markFailed(payment, false)
|
|
405
|
-
|
|
406
|
-
if (isSimpleError(e) || isSimpleErrors(e)) {
|
|
407
|
-
throw e
|
|
408
|
-
}
|
|
409
|
-
throw new SimpleError({
|
|
410
|
-
code: "payment_failed",
|
|
411
|
-
message: "Er ging iets mis bij het aanmaken van de betaling. Probeer later opnieuw of contacteer ons als het probleem zich blijft voordoen ("+request.$t("shared.emails.general")+")"
|
|
412
|
-
})
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// We don't save the invoice, just return it
|
|
417
|
-
return new Response(STInvoiceResponse.create({
|
|
418
|
-
paymentUrl: undefined,
|
|
419
|
-
invoice: await invoice.getStructure()
|
|
420
|
-
}));
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
2
|
-
import { SimpleError } from "@simonbackx/simple-errors";
|
|
3
|
-
import { STPackage, Token } from "@stamhoofd/models";
|
|
4
|
-
import { QueueHandler } from '@stamhoofd/queues';
|
|
5
|
-
|
|
6
|
-
import { Context } from "../../../../helpers/Context";
|
|
7
|
-
type Params = {id: string};
|
|
8
|
-
type Query = undefined;
|
|
9
|
-
type ResponseBody = undefined;
|
|
10
|
-
type Body = undefined
|
|
11
|
-
|
|
12
|
-
export class DeactivatePackageEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
13
|
-
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
14
|
-
if (request.method != "POST") {
|
|
15
|
-
return [false];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const params = Endpoint.parseParameters(request.url, "/billing/deactivate-package/@id", {id: String});
|
|
19
|
-
|
|
20
|
-
if (params) {
|
|
21
|
-
return [true, params as Params];
|
|
22
|
-
}
|
|
23
|
-
return [false];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
27
|
-
const organization = await Context.setOrganizationScope();
|
|
28
|
-
await Context.authenticate()
|
|
29
|
-
|
|
30
|
-
// If the user has permission, we'll also search if he has access to the organization's key
|
|
31
|
-
if (!await Context.auth.canDeactivatePackages(organization.id)) {
|
|
32
|
-
throw Context.auth.error()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
await QueueHandler.schedule("billing/invoices-"+organization.id, async () => {
|
|
36
|
-
const packages = await STPackage.getForOrganization(organization.id)
|
|
37
|
-
|
|
38
|
-
const pack = packages.find(p => p.id === request.params.id)
|
|
39
|
-
if (!pack) {
|
|
40
|
-
throw new SimpleError({
|
|
41
|
-
code: "not_found",
|
|
42
|
-
message: "Package not found",
|
|
43
|
-
human: "De functie die je wilt deactiveren is al gedeactiveerd of bestaat niet",
|
|
44
|
-
statusCode: 404
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!pack.meta.canDeactivate) {
|
|
49
|
-
throw new SimpleError({
|
|
50
|
-
code: "not_allowed",
|
|
51
|
-
message: "Can't deactivate this package",
|
|
52
|
-
human: "Je kan deze functie niet zelf deactiveren. Neem contact met ons op.",
|
|
53
|
-
})
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Deactivate
|
|
57
|
-
pack.removeAt = new Date()
|
|
58
|
-
pack.removeAt.setTime(pack.removeAt.getTime() - 1000)
|
|
59
|
-
await pack.save()
|
|
60
|
-
|
|
61
|
-
await STPackage.updateOrganizationPackages(organization.id)
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return new Response(undefined)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|