@stamhoofd/models 2.36.2 → 2.38.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/factories/GroupFactory.d.ts +2 -0
- package/dist/src/factories/GroupFactory.d.ts.map +1 -1
- package/dist/src/factories/GroupFactory.js +12 -0
- package/dist/src/factories/GroupFactory.js.map +1 -1
- package/dist/src/factories/OrganizationFactory.d.ts +2 -0
- package/dist/src/factories/OrganizationFactory.d.ts.map +1 -1
- package/dist/src/factories/OrganizationFactory.js +4 -0
- package/dist/src/factories/OrganizationFactory.js.map +1 -1
- package/dist/src/factories/RegistrationFactory.d.ts +3 -0
- package/dist/src/factories/RegistrationFactory.d.ts.map +1 -1
- package/dist/src/factories/RegistrationFactory.js +4 -0
- package/dist/src/factories/RegistrationFactory.js.map +1 -1
- package/dist/src/factories/RegistrationPeriodFactory.d.ts +11 -0
- package/dist/src/factories/RegistrationPeriodFactory.d.ts.map +1 -0
- package/dist/src/factories/RegistrationPeriodFactory.js +23 -0
- package/dist/src/factories/RegistrationPeriodFactory.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/migrations/1726668114-maximum-free-amount.sql +2 -0
- package/dist/src/migrations/1726668115-free-amount.sql +2 -0
- package/dist/src/migrations/1726668116-price-without-discount.sql +2 -0
- package/dist/src/models/BalanceItem.js +2 -2
- package/dist/src/models/BalanceItem.js.map +1 -1
- package/dist/src/models/CachedOutstandingBalance.d.ts.map +1 -1
- package/dist/src/models/CachedOutstandingBalance.js +6 -0
- package/dist/src/models/CachedOutstandingBalance.js.map +1 -1
- package/dist/src/models/Email.d.ts +1 -0
- package/dist/src/models/Email.d.ts.map +1 -1
- package/dist/src/models/Email.js +54 -1
- package/dist/src/models/Email.js.map +1 -1
- package/dist/src/models/Member.d.ts.map +1 -1
- package/dist/src/models/Member.js +3 -17
- package/dist/src/models/Member.js.map +1 -1
- package/dist/src/models/MemberPlatformMembership.d.ts +8 -0
- package/dist/src/models/MemberPlatformMembership.d.ts.map +1 -1
- package/dist/src/models/MemberPlatformMembership.js +64 -2
- package/dist/src/models/MemberPlatformMembership.js.map +1 -1
- package/dist/src/models/Organization.d.ts +3 -1
- package/dist/src/models/Organization.d.ts.map +1 -1
- package/dist/src/models/Organization.js +2 -2
- package/dist/src/models/Organization.js.map +1 -1
- package/dist/src/models/Platform.d.ts.map +1 -1
- package/dist/src/models/Platform.js +5 -2
- package/dist/src/models/Platform.js.map +1 -1
- package/package.json +2 -2
- package/src/factories/GroupFactory.ts +14 -2
- package/src/factories/OrganizationFactory.ts +8 -2
- package/src/factories/RegistrationFactory.ts +5 -0
- package/src/factories/RegistrationPeriodFactory.ts +23 -0
- package/src/index.ts +1 -0
- package/src/migrations/1726668114-maximum-free-amount.sql +2 -0
- package/src/migrations/1726668115-free-amount.sql +2 -0
- package/src/migrations/1726668116-price-without-discount.sql +2 -0
- package/src/models/BalanceItem.ts +2 -2
- package/src/models/CachedOutstandingBalance.ts +7 -0
- package/src/models/Email.ts +64 -2
- package/src/models/Member.ts +4 -22
- package/src/models/MemberPlatformMembership.ts +78 -6
- package/src/models/Organization.ts +2 -2
- package/src/models/Platform.ts +7 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Factory } from "@simonbackx/simple-database";
|
|
2
|
-
import {
|
|
2
|
+
import { GroupPrice, GroupSettings, OldGroupPrice, OldGroupPrices, PermissionsByRole, ReduceablePrice } from "@stamhoofd/structures";
|
|
3
3
|
|
|
4
4
|
import { Group } from "../models/Group";
|
|
5
5
|
import { Organization } from "../models/Organization";
|
|
@@ -8,13 +8,15 @@ import { OrganizationFactory } from './OrganizationFactory';
|
|
|
8
8
|
class Options {
|
|
9
9
|
organization?: Organization;
|
|
10
10
|
price?: number;
|
|
11
|
-
reducedPrice?: number
|
|
11
|
+
reducedPrice?: number;
|
|
12
|
+
stock?: number;
|
|
12
13
|
|
|
13
14
|
delayDate?: Date
|
|
14
15
|
delayPrice?: number
|
|
15
16
|
delayReducedPrice?: number
|
|
16
17
|
skipCategory?: boolean
|
|
17
18
|
permissions?: PermissionsByRole
|
|
19
|
+
maxMembers?: number | null
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export class GroupFactory extends Factory<Options, Group> {
|
|
@@ -38,6 +40,16 @@ export class GroupFactory extends Factory<Options, Group> {
|
|
|
38
40
|
})],
|
|
39
41
|
})
|
|
40
42
|
],
|
|
43
|
+
prices: [
|
|
44
|
+
GroupPrice.create({
|
|
45
|
+
price: ReduceablePrice.create({
|
|
46
|
+
price: this.options.price ?? 400,
|
|
47
|
+
reducedPrice: this.options.reducedPrice ?? null
|
|
48
|
+
}),
|
|
49
|
+
stock: this.options.stock ?? null
|
|
50
|
+
})
|
|
51
|
+
],
|
|
52
|
+
maxMembers: this.options.maxMembers === undefined ? null : this.options.maxMembers
|
|
41
53
|
})
|
|
42
54
|
|
|
43
55
|
if (this.options.delayPrice !== undefined) {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Factory } from "@simonbackx/simple-database";
|
|
2
|
-
import { Address,Country,OrganizationMetaData, OrganizationType, PermissionRoleDetailed } from "@stamhoofd/structures";
|
|
3
|
-
import { Formatter } from "@stamhoofd/utility";
|
|
2
|
+
import { Address, Country, OrganizationMetaData, OrganizationType, PermissionRoleDetailed } from "@stamhoofd/structures";
|
|
3
|
+
import { Formatter } from "@stamhoofd/utility";
|
|
4
4
|
|
|
5
5
|
import { Organization } from "../models/Organization";
|
|
6
|
+
import { RegistrationPeriod } from "../models/RegistrationPeriod";
|
|
7
|
+
import { RegistrationPeriodFactory } from "./RegistrationPeriodFactory";
|
|
6
8
|
|
|
7
9
|
class Options {
|
|
8
10
|
uri?: string;
|
|
@@ -11,6 +13,7 @@ class Options {
|
|
|
11
13
|
name?: string;
|
|
12
14
|
city?: string;
|
|
13
15
|
roles?: PermissionRoleDetailed[];
|
|
16
|
+
period?: RegistrationPeriod;
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
export class OrganizationFactory extends Factory<Options, Organization> {
|
|
@@ -35,6 +38,9 @@ export class OrganizationFactory extends Factory<Options, Organization> {
|
|
|
35
38
|
country: Country.Belgium
|
|
36
39
|
})
|
|
37
40
|
|
|
41
|
+
const period = this.options.period ?? await new RegistrationPeriodFactory({}).create();
|
|
42
|
+
organization.periodId = period.id;
|
|
43
|
+
|
|
38
44
|
if (this.options.roles) {
|
|
39
45
|
organization.privateMeta.roles = this.options.roles;
|
|
40
46
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Factory } from "@simonbackx/simple-database";
|
|
2
2
|
|
|
3
|
+
import { GroupPrice } from "@stamhoofd/structures";
|
|
3
4
|
import { Group } from "../models/Group";
|
|
4
5
|
import { Member } from '../models/Member';
|
|
5
6
|
import { Registration } from '../models/Registration';
|
|
@@ -7,6 +8,8 @@ import { Registration } from '../models/Registration';
|
|
|
7
8
|
class Options {
|
|
8
9
|
member: Member;
|
|
9
10
|
group: Group;
|
|
11
|
+
groupPrice: GroupPrice
|
|
12
|
+
price?: number
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
export class RegistrationFactory extends Factory<Options, Registration> {
|
|
@@ -18,6 +21,8 @@ export class RegistrationFactory extends Factory<Options, Registration> {
|
|
|
18
21
|
registration.organizationId = this.options.group.organizationId
|
|
19
22
|
registration.registeredAt = new Date()
|
|
20
23
|
registration.registeredAt.setMilliseconds(0)
|
|
24
|
+
registration.groupPrice = this.options.groupPrice
|
|
25
|
+
registration.price = this.options.price === undefined ? registration.groupPrice.price.price : this.options.price
|
|
21
26
|
|
|
22
27
|
await registration.save()
|
|
23
28
|
return registration;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Factory } from "@simonbackx/simple-database";
|
|
2
|
+
import { RegistrationPeriodSettings } from "@stamhoofd/structures";
|
|
3
|
+
|
|
4
|
+
import { RegistrationPeriod } from "../models";
|
|
5
|
+
|
|
6
|
+
class Options {
|
|
7
|
+
startDate?: Date;
|
|
8
|
+
endDate?: Date;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class RegistrationPeriodFactory extends Factory<Options, RegistrationPeriod> {
|
|
12
|
+
async create(): Promise<RegistrationPeriod> {
|
|
13
|
+
const period = new RegistrationPeriod();
|
|
14
|
+
|
|
15
|
+
period.organizationId = null;
|
|
16
|
+
period.startDate = this.options.startDate ?? new Date(2024,0,1,0,0,0,0);
|
|
17
|
+
period.endDate = this.options.endDate ?? new Date(2024,11,31,59,59,59,999);
|
|
18
|
+
period.settings = RegistrationPeriodSettings.create({});
|
|
19
|
+
|
|
20
|
+
await period.save();
|
|
21
|
+
return period;
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./factories/RegisterCodeFactory"
|
|
|
9
9
|
export * from "./factories/RegistrationFactory"
|
|
10
10
|
export * from "./factories/UserFactory"
|
|
11
11
|
export * from "./factories/WebshopFactory"
|
|
12
|
+
export * from "./factories/RegistrationPeriodFactory"
|
|
12
13
|
|
|
13
14
|
// Helpers
|
|
14
15
|
export * from "./helpers/EmailBuilder"
|
|
@@ -237,7 +237,7 @@ export class BalanceItem extends Model {
|
|
|
237
237
|
|
|
238
238
|
// Set other items to zero (the balance item payments keep the real price)
|
|
239
239
|
for (const item of items) {
|
|
240
|
-
needsUpdate =
|
|
240
|
+
needsUpdate = true
|
|
241
241
|
|
|
242
242
|
// Don't change status of items that are already paid or are partially paid
|
|
243
243
|
// Not using item.paidPrice, since this is cached
|
|
@@ -264,7 +264,7 @@ export class BalanceItem extends Model {
|
|
|
264
264
|
for (const item of items) {
|
|
265
265
|
if (item.status === BalanceItemStatus.Hidden) {
|
|
266
266
|
item.status = BalanceItemStatus.Pending
|
|
267
|
-
needsUpdate =
|
|
267
|
+
needsUpdate = true
|
|
268
268
|
await item.save()
|
|
269
269
|
}
|
|
270
270
|
}
|
|
@@ -154,6 +154,13 @@ export class CachedOutstandingBalance extends Model {
|
|
|
154
154
|
|
|
155
155
|
results.push([objectId, {amount, amountPending}]);
|
|
156
156
|
}
|
|
157
|
+
|
|
158
|
+
// Add missing object ids (with 0 amount, otherwise we don't reset the amounts back to zero when all the balance items are hidden)
|
|
159
|
+
for (const objectId of objectIds) {
|
|
160
|
+
if (!results.find(([id]) => id === objectId)) {
|
|
161
|
+
results.push([objectId, {amount: 0, amountPending: 0}]);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
157
164
|
|
|
158
165
|
return results;
|
|
159
166
|
}
|
package/src/models/Email.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
4
4
|
|
|
5
5
|
import { AnyDecoder, ArrayDecoder } from '@simonbackx/simple-encoding';
|
|
6
6
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
7
|
+
import { I18n } from '@stamhoofd/backend-i18n';
|
|
7
8
|
import { Email as EmailClass } from "@stamhoofd/email";
|
|
8
9
|
import { QueueHandler } from '@stamhoofd/queues';
|
|
9
10
|
import { SQL, SQLWhereSign } from '@stamhoofd/sql';
|
|
@@ -11,7 +12,6 @@ import { Formatter } from '@stamhoofd/utility';
|
|
|
11
12
|
import { getEmailBuilder } from '../helpers/EmailBuilder';
|
|
12
13
|
import { EmailRecipient } from './EmailRecipient';
|
|
13
14
|
import { Organization } from './Organization';
|
|
14
|
-
import { I18n } from '@stamhoofd/backend-i18n';
|
|
15
15
|
|
|
16
16
|
export class Email extends Model {
|
|
17
17
|
static table = "emails";
|
|
@@ -132,6 +132,46 @@ export class Email extends Model {
|
|
|
132
132
|
human: 'Vul een afzender in voor je een e-mail verstuurt'
|
|
133
133
|
})
|
|
134
134
|
}
|
|
135
|
+
|
|
136
|
+
this.validateAttachments()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
validateAttachments() {
|
|
140
|
+
// Validate attachments
|
|
141
|
+
const size = this.attachments.reduce((value: number, attachment) => {
|
|
142
|
+
return value + attachment.bytes
|
|
143
|
+
}, 0)
|
|
144
|
+
|
|
145
|
+
if (size > 9.5*1024*1024) {
|
|
146
|
+
throw new SimpleError({
|
|
147
|
+
code: "too_big_attachments",
|
|
148
|
+
message: "Too big attachments",
|
|
149
|
+
human: "Jouw bericht is te groot. Grote bijlages verstuur je beter niet via e-mail, je plaatst dan best een link naar de locatie in bv. Google Drive. De maximale grootte van een e-mail is 10MB, inclusief het bericht. Als je grote bestanden verstuurt kan je ze ook proberen te verkleinen.",
|
|
150
|
+
field: "attachments"
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const safeContentTypes = [
|
|
155
|
+
"application/msword",
|
|
156
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
157
|
+
"application/vnd.ms-excel",
|
|
158
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
159
|
+
"application/pdf",
|
|
160
|
+
"image/jpeg",
|
|
161
|
+
"image/png",
|
|
162
|
+
"image/gif"
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
for (const attachment of this.attachments) {
|
|
166
|
+
if (attachment.contentType && !safeContentTypes.includes(attachment.contentType)) {
|
|
167
|
+
throw new SimpleError({
|
|
168
|
+
code: "content_type_not_supported",
|
|
169
|
+
message: "Content-Type not supported",
|
|
170
|
+
human: "Het bestandstype van jouw bijlage wordt niet ondersteund of is onveilig om in een e-mail te plaatsen. Overweeg om je bestand op bv. Google Drive te zetten en de link in jouw e-mail te zetten.",
|
|
171
|
+
field: "attachments"
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
}
|
|
135
175
|
}
|
|
136
176
|
|
|
137
177
|
getFromAddress() {
|
|
@@ -172,7 +212,7 @@ export class Email extends Model {
|
|
|
172
212
|
await this.save();
|
|
173
213
|
|
|
174
214
|
const id = this.id;
|
|
175
|
-
return await QueueHandler.schedule('send-email', async function () {
|
|
215
|
+
return await QueueHandler.schedule('send-email', async function (this: unknown) {
|
|
176
216
|
let upToDate = await Email.getByID(id);
|
|
177
217
|
if (!upToDate) {
|
|
178
218
|
throw new SimpleError({
|
|
@@ -253,6 +293,27 @@ export class Email extends Model {
|
|
|
253
293
|
const batchSize = 100;
|
|
254
294
|
const recipientsSet = new Set<string>();
|
|
255
295
|
|
|
296
|
+
const attachments = upToDate.attachments.map((attachment, index) => {
|
|
297
|
+
let filename = "bijlage-"+index;
|
|
298
|
+
|
|
299
|
+
if (attachment.contentType == "application/pdf") {
|
|
300
|
+
// tmp solution for pdf only
|
|
301
|
+
filename += ".pdf"
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Correct file name if needed
|
|
305
|
+
if (attachment.filename) {
|
|
306
|
+
filename = attachment.filename.toLowerCase().replace(/[^a-z0-9.]+/g, "-").replace(/^-+/, "").replace(/-+$/, "")
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
filename: filename,
|
|
311
|
+
content: attachment.content,
|
|
312
|
+
contentType: attachment.contentType ?? undefined,
|
|
313
|
+
encoding: "base64"
|
|
314
|
+
}
|
|
315
|
+
})
|
|
316
|
+
|
|
256
317
|
// eslint-disable-next-line no-constant-condition
|
|
257
318
|
while (true) {
|
|
258
319
|
const data = await SQL.select()
|
|
@@ -315,6 +376,7 @@ export class Email extends Model {
|
|
|
315
376
|
subject: upToDate.subject!,
|
|
316
377
|
html: upToDate.html!,
|
|
317
378
|
type: "broadcast",
|
|
379
|
+
attachments,
|
|
318
380
|
callback(error: Error|null ) {
|
|
319
381
|
callback(error).catch(console.error)
|
|
320
382
|
},
|
package/src/models/Member.ts
CHANGED
|
@@ -498,18 +498,9 @@ export class Member extends Model {
|
|
|
498
498
|
|
|
499
499
|
if (activeMembershipsUndeletable.length) {
|
|
500
500
|
// Skip automatic additions
|
|
501
|
-
for (const m of activeMembershipsUndeletable) {
|
|
502
|
-
const beforePrice = m.price
|
|
503
|
-
|
|
501
|
+
for (const m of activeMembershipsUndeletable) {
|
|
504
502
|
await m.calculatePrice(me)
|
|
505
|
-
|
|
506
|
-
if (beforePrice !== m.price) {
|
|
507
|
-
await m.save()
|
|
508
|
-
|
|
509
|
-
console.log('Updated price for membership: ' + me.id + ' - membership ' + m.id)
|
|
510
|
-
} else if (!silent) {
|
|
511
|
-
console.log('Skipping automatic membership for: ' + me.id, ' - already has active memberships - no price change')
|
|
512
|
-
}
|
|
503
|
+
await m.save()
|
|
513
504
|
}
|
|
514
505
|
return
|
|
515
506
|
}
|
|
@@ -541,18 +532,9 @@ export class Member extends Model {
|
|
|
541
532
|
// Check if already have the same membership
|
|
542
533
|
for (const m of activeMemberships) {
|
|
543
534
|
if (m.membershipTypeId === cheapestMembership.membership.id) {
|
|
544
|
-
// Update the price of this active membership (could have changed)
|
|
545
|
-
const beforePrice = m.price
|
|
546
|
-
|
|
535
|
+
// Update the price of this active membership (could have changed)
|
|
547
536
|
await m.calculatePrice(me)
|
|
548
|
-
|
|
549
|
-
if (beforePrice !== m.price) {
|
|
550
|
-
await m.save()
|
|
551
|
-
|
|
552
|
-
console.log('Updated price for membership: ' + me.id + ' - membership ' + m.id)
|
|
553
|
-
} else if (!silent) {
|
|
554
|
-
console.log('Skipping automatic membership for: ' + me.id, ' - already has this membership - no price change')
|
|
555
|
-
}
|
|
537
|
+
await m.save()
|
|
556
538
|
return
|
|
557
539
|
}
|
|
558
540
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { column, Model, SQLResultNamespacedRow } from "@simonbackx/simple-database";
|
|
3
3
|
import { SimpleError } from "@simonbackx/simple-errors";
|
|
4
|
-
import { SQL, SQLSelect } from "@stamhoofd/sql";
|
|
4
|
+
import { SQL, SQLSelect, SQLWhereSign } from "@stamhoofd/sql";
|
|
5
5
|
import { PlatformMembershipTypeBehaviour } from "@stamhoofd/structures";
|
|
6
6
|
import { Formatter } from "@stamhoofd/utility";
|
|
7
7
|
import { v4 as uuidv4 } from "uuid";
|
|
@@ -48,6 +48,19 @@ export class MemberPlatformMembership extends Model {
|
|
|
48
48
|
@column({ type: "integer" })
|
|
49
49
|
price = 0;
|
|
50
50
|
|
|
51
|
+
@column({ type: "integer" })
|
|
52
|
+
priceWithoutDiscount = 0;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Contains the amount of days, or either 1/0 to count the amount of 'free' days maximum could be awarded.
|
|
56
|
+
* Set to 0 when already a different 'free' membership was created, so it shouldn't count for a free day
|
|
57
|
+
*/
|
|
58
|
+
@column({ type: "integer" })
|
|
59
|
+
maximumFreeAmount = 0;
|
|
60
|
+
|
|
61
|
+
@column({ type: "integer" })
|
|
62
|
+
freeAmount = 0;
|
|
63
|
+
|
|
51
64
|
/**
|
|
52
65
|
* Whether this was added automatically by the system
|
|
53
66
|
*/
|
|
@@ -93,7 +106,7 @@ export class MemberPlatformMembership extends Model {
|
|
|
93
106
|
}
|
|
94
107
|
|
|
95
108
|
async calculatePrice(member: Member) {
|
|
96
|
-
const platform = await Platform.
|
|
109
|
+
const platform = await Platform.getSharedPrivateStruct();
|
|
97
110
|
const membershipType = platform.config.membershipTypes.find(m => m.id == this.membershipTypeId);
|
|
98
111
|
|
|
99
112
|
if (!membershipType) {
|
|
@@ -115,7 +128,7 @@ export class MemberPlatformMembership extends Model {
|
|
|
115
128
|
}
|
|
116
129
|
|
|
117
130
|
const organization = await Organization.getByID(this.organizationId);
|
|
118
|
-
if(!organization) {
|
|
131
|
+
if (!organization) {
|
|
119
132
|
throw new SimpleError({
|
|
120
133
|
//todo
|
|
121
134
|
code: 'not_found',
|
|
@@ -126,9 +139,40 @@ export class MemberPlatformMembership extends Model {
|
|
|
126
139
|
|
|
127
140
|
const tagIds = organization.meta.tags;
|
|
128
141
|
const shouldApplyReducedPrice = member.details.shouldApplyReducedPrice;
|
|
129
|
-
|
|
130
|
-
const priceConfig = periodConfig.getPriceConfigForDate(membershipType.behaviour === PlatformMembershipTypeBehaviour.Days ? this.startDate : (this.createdAt ?? new Date()));
|
|
131
142
|
|
|
143
|
+
const priceConfig = periodConfig.getPriceConfigForDate(membershipType.behaviour === PlatformMembershipTypeBehaviour.Days ? this.startDate : (this.createdAt ?? new Date()));
|
|
144
|
+
const earliestPriceConfig = periodConfig.getPriceConfigForDate(membershipType.behaviour === PlatformMembershipTypeBehaviour.Days ? new Date(1950, 0, 1) : (new Date(1950, 0, 1)));
|
|
145
|
+
|
|
146
|
+
let freeDays = 0;
|
|
147
|
+
|
|
148
|
+
const d = this.createdAt ??new Date();
|
|
149
|
+
|
|
150
|
+
if (periodConfig.amountFree) {
|
|
151
|
+
// Check if this organization has rights to free memberships
|
|
152
|
+
const alreadyUsed = await MemberPlatformMembership.select()
|
|
153
|
+
.where('organizationId', this.organizationId)
|
|
154
|
+
.where('membershipTypeId', this.membershipTypeId)
|
|
155
|
+
.where('periodId', this.periodId)
|
|
156
|
+
.where('deletedAt', null)
|
|
157
|
+
.whereNot('id', this.id)
|
|
158
|
+
.where(
|
|
159
|
+
SQL.where('createdAt', SQLWhereSign.Less, d)
|
|
160
|
+
.or(
|
|
161
|
+
SQL.where('createdAt', SQLWhereSign.Equal, d)
|
|
162
|
+
.and('id', SQLWhereSign.Less, this.id)
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
.sum(SQL.column('maximumFreeAmount'));
|
|
166
|
+
|
|
167
|
+
if (alreadyUsed < periodConfig.amountFree) {
|
|
168
|
+
freeDays = periodConfig.amountFree - alreadyUsed;
|
|
169
|
+
console.log('Free membership created for ', this.id, periodConfig.amountFree, alreadyUsed);
|
|
170
|
+
} else {
|
|
171
|
+
console.log('No free membership created for', this.id, periodConfig.amountFree, alreadyUsed);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
132
176
|
if (membershipType.behaviour === PlatformMembershipTypeBehaviour.Days) {
|
|
133
177
|
// Make sure time is equal between start and end date
|
|
134
178
|
let startBrussels = Formatter.luxon(this.startDate);
|
|
@@ -141,20 +185,48 @@ export class MemberPlatformMembership extends Model {
|
|
|
141
185
|
this.expireDate = null
|
|
142
186
|
|
|
143
187
|
const days = Math.round((this.endDate.getTime() - this.startDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
144
|
-
this.
|
|
188
|
+
this.maximumFreeAmount = days;
|
|
189
|
+
this.priceWithoutDiscount = earliestPriceConfig.calculatePrice(tagIds, false, days);
|
|
190
|
+
this.price = priceConfig.calculatePrice(tagIds, shouldApplyReducedPrice, Math.max(0, days - freeDays));
|
|
191
|
+
this.freeAmount = Math.min(days, freeDays);
|
|
145
192
|
} else {
|
|
193
|
+
this.priceWithoutDiscount = earliestPriceConfig.getBasePrice(tagIds, false);
|
|
146
194
|
this.price = priceConfig.getBasePrice(tagIds, shouldApplyReducedPrice);
|
|
147
195
|
this.startDate = periodConfig.startDate;
|
|
148
196
|
this.endDate = periodConfig.endDate;
|
|
149
197
|
this.expireDate = periodConfig.expireDate;
|
|
198
|
+
this.maximumFreeAmount = this.price > 0 ? 1 : 0;
|
|
199
|
+
this.freeAmount = 0;
|
|
200
|
+
|
|
201
|
+
if (freeDays > 0) {
|
|
202
|
+
this.price = 0;
|
|
203
|
+
this.freeAmount = 1;
|
|
204
|
+
}
|
|
150
205
|
}
|
|
151
206
|
|
|
152
207
|
if (this.balanceItemId) {
|
|
208
|
+
this.maximumFreeAmount = this.freeAmount;
|
|
209
|
+
|
|
153
210
|
// Also update the balance item
|
|
154
211
|
const balanceItem = await BalanceItem.getByID(this.balanceItemId);
|
|
155
212
|
if (balanceItem) {
|
|
156
213
|
balanceItem.unitPrice = this.price;
|
|
157
214
|
await balanceItem.save();
|
|
215
|
+
|
|
216
|
+
await BalanceItem.updateOutstanding([balanceItem]);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async doDelete() {
|
|
222
|
+
this.deletedAt = new Date()
|
|
223
|
+
await this.save()
|
|
224
|
+
|
|
225
|
+
if (this.balanceItemId) {
|
|
226
|
+
// Also update the balance item
|
|
227
|
+
const balanceItem = await BalanceItem.getByID(this.balanceItemId);
|
|
228
|
+
if (balanceItem) {
|
|
229
|
+
await BalanceItem.deleteItems([balanceItem])
|
|
158
230
|
}
|
|
159
231
|
}
|
|
160
232
|
}
|
|
@@ -196,7 +196,7 @@ export class Organization extends Model {
|
|
|
196
196
|
* Get an Organization by looking at the host of a request
|
|
197
197
|
* Format is 2331c59a-0cbe-4279-871c-ea9d0474cd54.api.stamhoofd.app
|
|
198
198
|
*/
|
|
199
|
-
static async fromApiHost(host: string): Promise<Organization> {
|
|
199
|
+
static async fromApiHost(host: string, options?: {allowInactive?: boolean}): Promise<Organization> {
|
|
200
200
|
const splitted = host.split('.')
|
|
201
201
|
if (splitted.length < 2) {
|
|
202
202
|
throw new SimpleError({
|
|
@@ -213,7 +213,7 @@ export class Organization extends Model {
|
|
|
213
213
|
});
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
if (!organization.active) {
|
|
216
|
+
if (!organization.active && !options?.allowInactive) {
|
|
217
217
|
throw new SimpleError({
|
|
218
218
|
code: "archived",
|
|
219
219
|
message: "This organization is archived",
|
package/src/models/Platform.ts
CHANGED
|
@@ -77,7 +77,12 @@ export class Platform extends Model {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
async save() {
|
|
80
|
-
Platform.
|
|
81
|
-
|
|
80
|
+
Platform.clearCache()
|
|
81
|
+
const s = await super.save()
|
|
82
|
+
|
|
83
|
+
// Force update cache immediately
|
|
84
|
+
await Platform.getSharedStruct();
|
|
85
|
+
|
|
86
|
+
return s;
|
|
82
87
|
}
|
|
83
88
|
}
|