@stamhoofd/models 2.121.0 → 2.122.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/factories/GroupFactory.d.ts.map +1 -1
- package/dist/factories/GroupFactory.js +1 -1
- package/dist/factories/GroupFactory.js.map +1 -1
- package/dist/factories/OrganizationFactory.d.ts +2 -1
- package/dist/factories/OrganizationFactory.d.ts.map +1 -1
- package/dist/factories/OrganizationFactory.js +9 -1
- package/dist/factories/OrganizationFactory.js.map +1 -1
- package/dist/factories/STPackageFactory.js.map +1 -1
- package/dist/factories/UserFactory.d.ts.map +1 -1
- package/dist/factories/UserFactory.js +2 -2
- package/dist/factories/UserFactory.js.map +1 -1
- package/dist/helpers/EmailBuilder.d.ts.map +1 -1
- package/dist/helpers/EmailBuilder.js +8 -8
- package/dist/helpers/EmailBuilder.js.map +1 -1
- package/dist/helpers/Handlebars.d.ts.map +1 -1
- package/dist/helpers/Handlebars.js +7 -1
- package/dist/helpers/Handlebars.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/migrations/1605262045-import-postcodes.d.ts.map +1 -1
- package/dist/migrations/1605262045-import-postcodes.js +58 -24
- package/dist/migrations/1605262045-import-postcodes.js.map +1 -1
- package/dist/migrations/1605262046-import-postcodes-nl.d.ts.map +1 -1
- package/dist/migrations/1605262046-import-postcodes-nl.js +54 -17
- package/dist/migrations/1605262046-import-postcodes-nl.js.map +1 -1
- package/dist/migrations/1719567881-organization-periodId.sql +2 -0
- package/dist/migrations/1719567882-groups-periodId.sql +2 -0
- package/dist/migrations/1720080975-convert-charset.d.ts +4 -0
- package/dist/migrations/1720080975-convert-charset.d.ts.map +1 -0
- package/dist/migrations/1720080975-convert-charset.js +26 -0
- package/dist/migrations/1720080975-convert-charset.js.map +1 -0
- package/dist/migrations/1720080976-convert-charset-leads.d.ts.map +1 -1
- package/dist/migrations/1720080976-convert-charset-leads.js +11 -10
- package/dist/migrations/1720080976-convert-charset-leads.js.map +1 -1
- package/dist/migrations/1721400546-users-memberId.sql +2 -0
- package/dist/migrations/1722269236-group-waitinglist-id.sql +2 -1
- package/dist/migrations/1722525785-balance-item-paying-organization-id.sql +2 -0
- package/dist/migrations/1722525787-depending-balance-item.sql +2 -0
- package/dist/migrations/1722963554-registration-group-price-and-options.sql +1 -1
- package/dist/migrations/1723652797-payments-paying-organization-id-fk.sql +2 -0
- package/dist/migrations/1733317908-added-missing-organization-fk-on-registrations.sql +2 -0
- package/dist/migrations/1733317910-paying-organization-id-fk.sql +2 -0
- package/dist/migrations/1733504881-negative-invoice-id.sql +6 -0
- package/dist/migrations/1733994455-balance-item-status-open.d.ts +4 -0
- package/dist/migrations/1733994455-balance-item-status-open.d.ts.map +1 -0
- package/dist/migrations/1733994455-balance-item-status-open.js +28 -0
- package/dist/migrations/1733994455-balance-item-status-open.js.map +1 -0
- package/dist/migrations/1769087808-corrected-invoice-user-agent.sql +2 -0
- package/dist/migrations/1769087809-payments-invoice-id.sql +2 -0
- package/dist/migrations/1772033555-balance-item-package-id.sql +2 -0
- package/dist/migrations/1778796615-payments-reversing-payment-id.sql +2 -0
- package/dist/migrations/1779443446-transfer-fees.sql +3 -0
- package/dist/migrations/1779709174-used-register-code-balance-item-id.sql +5 -0
- package/dist/migrations/1779968328-payments-admin-user-id.sql +5 -0
- package/dist/migrations/1779970611-payments-refunded-amount.sql +2 -0
- package/dist/migrations/1779972640-balance-items-failed-at.sql +2 -0
- package/dist/migrations/1780328285-document-template-locked.sql +2 -0
- package/dist/migrations/1780328286-document-locked.sql +2 -0
- package/dist/migrations/1780412083-documents-set-locked.d.ts +4 -0
- package/dist/migrations/1780412083-documents-set-locked.d.ts.map +1 -0
- package/dist/migrations/1780412083-documents-set-locked.js +14 -0
- package/dist/migrations/1780412083-documents-set-locked.js.map +1 -0
- package/dist/migrations/1780928401-v1-groups-migration-data.d.ts +4 -0
- package/dist/migrations/1780928401-v1-groups-migration-data.d.ts.map +1 -0
- package/dist/migrations/1780928401-v1-groups-migration-data.js +44 -0
- package/dist/migrations/1780928401-v1-groups-migration-data.js.map +1 -0
- package/dist/models/BalanceItem.d.ts +7 -2
- package/dist/models/BalanceItem.d.ts.map +1 -1
- package/dist/models/BalanceItem.js +41 -38
- package/dist/models/BalanceItem.js.map +1 -1
- package/dist/models/CachedBalance.d.ts +6 -1
- package/dist/models/CachedBalance.d.ts.map +1 -1
- package/dist/models/CachedBalance.js +3 -2
- package/dist/models/CachedBalance.js.map +1 -1
- package/dist/models/Document.d.ts +4 -0
- package/dist/models/Document.d.ts.map +1 -1
- package/dist/models/Document.js +26 -3
- package/dist/models/Document.js.map +1 -1
- package/dist/models/DocumentTemplate.d.ts +4 -0
- package/dist/models/DocumentTemplate.d.ts.map +1 -1
- package/dist/models/DocumentTemplate.js +37 -1
- package/dist/models/DocumentTemplate.js.map +1 -1
- package/dist/models/Email.d.ts.map +1 -1
- package/dist/models/Email.js +1 -1
- package/dist/models/Email.js.map +1 -1
- package/dist/models/EmailVerificationCode.d.ts.map +1 -1
- package/dist/models/EmailVerificationCode.js +3 -13
- package/dist/models/EmailVerificationCode.js.map +1 -1
- package/dist/models/Event.d.ts +2 -1
- package/dist/models/Event.d.ts.map +1 -1
- package/dist/models/Event.js +3 -0
- package/dist/models/Event.js.map +1 -1
- package/dist/models/EventNotification.d.ts.map +1 -1
- package/dist/models/EventNotification.js +5 -5
- package/dist/models/EventNotification.js.map +1 -1
- package/dist/models/Invoice.d.ts +1 -0
- package/dist/models/Invoice.d.ts.map +1 -1
- package/dist/models/Invoice.js +8 -0
- package/dist/models/Invoice.js.map +1 -1
- package/dist/models/MemberPlatformMembership.d.ts.map +1 -1
- package/dist/models/MemberPlatformMembership.js +9 -0
- package/dist/models/MemberPlatformMembership.js.map +1 -1
- package/dist/models/Order.d.ts.map +1 -1
- package/dist/models/Order.js +1 -0
- package/dist/models/Order.js.map +1 -1
- package/dist/models/Organization.d.ts +23 -25
- package/dist/models/Organization.d.ts.map +1 -1
- package/dist/models/Organization.js +92 -64
- package/dist/models/Organization.js.map +1 -1
- package/dist/models/PasswordToken.d.ts +5 -1
- package/dist/models/PasswordToken.d.ts.map +1 -1
- package/dist/models/PasswordToken.js +18 -17
- package/dist/models/PasswordToken.js.map +1 -1
- package/dist/models/Payment.d.ts +21 -2
- package/dist/models/Payment.d.ts.map +1 -1
- package/dist/models/Payment.js +43 -2
- package/dist/models/Payment.js.map +1 -1
- package/dist/models/Registration.d.ts.map +1 -1
- package/dist/models/Registration.js +3 -3
- package/dist/models/Registration.js.map +1 -1
- package/dist/models/STCredit.d.ts +4 -0
- package/dist/models/STCredit.d.ts.map +1 -1
- package/dist/models/STCredit.js +28 -0
- package/dist/models/STCredit.js.map +1 -1
- package/dist/models/STInvoice.d.ts +7 -1
- package/dist/models/STInvoice.d.ts.map +1 -1
- package/dist/models/STInvoice.js +9 -0
- package/dist/models/STInvoice.js.map +1 -1
- package/dist/models/STPackage.d.ts +4 -0
- package/dist/models/STPackage.d.ts.map +1 -1
- package/dist/models/STPackage.js +12 -1
- package/dist/models/STPackage.js.map +1 -1
- package/dist/models/UsedRegisterCode.d.ts +9 -0
- package/dist/models/UsedRegisterCode.d.ts.map +1 -1
- package/dist/models/UsedRegisterCode.js +31 -0
- package/dist/models/UsedRegisterCode.js.map +1 -1
- package/dist/models/_relations.js +25 -0
- package/dist/models/_relations.js.map +1 -1
- package/dist/models/addresses/City.d.ts +4 -4
- package/dist/models/addresses/City.d.ts.map +1 -1
- package/dist/models/addresses/City.js +6 -6
- package/dist/models/addresses/City.js.map +1 -1
- package/dist/models/addresses/PostalCode.d.ts +2 -2
- package/dist/models/addresses/PostalCode.d.ts.map +1 -1
- package/dist/models/addresses/PostalCode.js +4 -3
- package/dist/models/addresses/PostalCode.js.map +1 -1
- package/dist/models/addresses/Street.d.ts +3 -3
- package/dist/models/addresses/Street.d.ts.map +1 -1
- package/dist/models/addresses/Street.js +4 -4
- package/dist/models/addresses/Street.js.map +1 -1
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +1 -0
- package/dist/models/index.js.map +1 -1
- package/dist/models/v1GroupMigrationData.d.ts +22 -0
- package/dist/models/v1GroupMigrationData.d.ts.map +1 -0
- package/dist/models/v1GroupMigrationData.js +48 -0
- package/dist/models/v1GroupMigrationData.js.map +1 -0
- package/package.json +32 -13
- package/src/factories/GroupFactory.ts +4 -6
- package/src/factories/OrganizationFactory.ts +12 -4
- package/src/factories/STPackageFactory.ts +2 -2
- package/src/factories/UserFactory.ts +4 -5
- package/src/helpers/EmailBuilder.ts +19 -28
- package/src/helpers/Handlebars.ts +6 -1
- package/src/index.ts +0 -1
- package/src/migrations/1605262045-import-postcodes.ts +62 -25
- package/src/migrations/1605262046-import-postcodes-nl.ts +58 -17
- package/src/migrations/1719567881-organization-periodId.sql +2 -0
- package/src/migrations/1719567882-groups-periodId.sql +2 -0
- package/src/migrations/1720080975-convert-charset.ts +34 -0
- package/src/migrations/1720080976-convert-charset-leads.ts +16 -13
- package/src/migrations/1721400546-users-memberId.sql +2 -0
- package/src/migrations/1722269236-group-waitinglist-id.sql +2 -1
- package/src/migrations/1722525785-balance-item-paying-organization-id.sql +2 -0
- package/src/migrations/1722525787-depending-balance-item.sql +2 -0
- package/src/migrations/1722963554-registration-group-price-and-options.sql +1 -1
- package/src/migrations/1723652797-payments-paying-organization-id-fk.sql +2 -0
- package/src/migrations/1733317908-added-missing-organization-fk-on-registrations.sql +2 -0
- package/src/migrations/1733317910-paying-organization-id-fk.sql +2 -0
- package/src/migrations/1733504881-negative-invoice-id.sql +6 -0
- package/src/migrations/1733994455-balance-item-status-open.ts +30 -0
- package/src/migrations/1769087808-corrected-invoice-user-agent.sql +2 -0
- package/src/migrations/1769087809-payments-invoice-id.sql +2 -0
- package/src/migrations/1772033555-balance-item-package-id.sql +2 -0
- package/src/migrations/1778796615-payments-reversing-payment-id.sql +2 -0
- package/src/migrations/1779443446-transfer-fees.sql +3 -0
- package/src/migrations/1779709174-used-register-code-balance-item-id.sql +5 -0
- package/src/migrations/1779968328-payments-admin-user-id.sql +5 -0
- package/src/migrations/1779970611-payments-refunded-amount.sql +2 -0
- package/src/migrations/1779972640-balance-items-failed-at.sql +2 -0
- package/src/migrations/1780328285-document-template-locked.sql +2 -0
- package/src/migrations/1780328286-document-locked.sql +2 -0
- package/src/migrations/1780412083-documents-set-locked.ts +18 -0
- package/src/migrations/1780928401-v1-groups-migration-data.ts +50 -0
- package/src/models/BalanceItem.ts +44 -43
- package/src/models/CachedBalance.test.ts +46 -46
- package/src/models/CachedBalance.ts +7 -7
- package/src/models/Document.ts +34 -13
- package/src/models/DocumentTemplate.ts +56 -17
- package/src/models/Email.test.ts +3 -3
- package/src/models/Email.ts +28 -49
- package/src/models/EmailVerificationCode.ts +8 -22
- package/src/models/Event.ts +6 -4
- package/src/models/EventNotification.ts +6 -6
- package/src/models/Invoice.ts +9 -0
- package/src/models/MemberPlatformMembership.test.ts +70 -0
- package/src/models/MemberPlatformMembership.ts +16 -12
- package/src/models/Order.ts +14 -26
- package/src/models/Organization.ts +122 -93
- package/src/models/PasswordToken.ts +21 -21
- package/src/models/Payment.ts +42 -4
- package/src/models/Registration.ts +3 -3
- package/src/models/STCredit.ts +32 -0
- package/src/models/STInvoice.ts +11 -5
- package/src/models/STPackage.ts +19 -14
- package/src/models/UsedRegisterCode.ts +34 -0
- package/src/models/_relations.ts +29 -0
- package/src/models/addresses/City.ts +8 -6
- package/src/models/addresses/PostalCode.test.ts +1 -0
- package/src/models/addresses/PostalCode.ts +5 -3
- package/src/models/addresses/Street.ts +6 -4
- package/src/models/index.ts +2 -0
- package/src/models/v1GroupMigrationData.ts +43 -0
- package/dist/helpers/MemberMerger.d.ts +0 -14
- package/dist/helpers/MemberMerger.d.ts.map +0 -1
- package/dist/helpers/MemberMerger.js +0 -364
- package/dist/helpers/MemberMerger.js.map +0 -1
- package/dist/migrations/1720080975-convert-charset.sql +0 -85
- package/dist/migrations/1723202126-member-number-index.sql +0 -2
- package/dist/models/OneTimeToken.d.ts +0 -38
- package/dist/models/OneTimeToken.d.ts.map +0 -1
- package/dist/models/OneTimeToken.js +0 -125
- package/dist/models/OneTimeToken.js.map +0 -1
- package/src/helpers/MemberMerger.test.ts +0 -782
- package/src/helpers/MemberMerger.ts +0 -577
- package/src/migrations/1720080975-convert-charset.sql +0 -85
- package/src/migrations/1723202126-member-number-index.sql +0 -2
- package/src/models/OneTimeToken.ts +0 -133
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { column, ManyToManyRelation } from '@simonbackx/simple-database';
|
|
2
|
-
import {
|
|
2
|
+
import { EnumDecoder } from '@simonbackx/simple-encoding';
|
|
3
3
|
import { QueryableModel } from '@stamhoofd/sql';
|
|
4
|
-
import type { RecordAnswer} from '@stamhoofd/structures';
|
|
5
|
-
import { EventNotificationStatus,
|
|
4
|
+
import type { RecordAnswer } from '@stamhoofd/structures';
|
|
5
|
+
import { EventNotificationStatus, RecordAnswerMapDecoder } from '@stamhoofd/structures';
|
|
6
6
|
import { v4 as uuidv4 } from 'uuid';
|
|
7
7
|
import { Event } from './Event.js';
|
|
8
8
|
|
|
@@ -26,7 +26,7 @@ export class EventNotification extends QueryableModel {
|
|
|
26
26
|
@column({ type: 'datetime' })
|
|
27
27
|
endDate: Date;
|
|
28
28
|
|
|
29
|
-
@column({ type: 'string' })
|
|
29
|
+
@column({ type: 'string', decoder: new EnumDecoder(EventNotificationStatus) })
|
|
30
30
|
status = EventNotificationStatus.Draft;
|
|
31
31
|
|
|
32
32
|
/**
|
|
@@ -39,13 +39,13 @@ export class EventNotification extends QueryableModel {
|
|
|
39
39
|
@column({ type: 'string' })
|
|
40
40
|
organizationId: string;
|
|
41
41
|
|
|
42
|
-
@column({ type: 'json', decoder:
|
|
42
|
+
@column({ type: 'json', decoder: RecordAnswerMapDecoder })
|
|
43
43
|
recordAnswers: Map<string, RecordAnswer> = new Map();
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* Contains the answers of an event notification that were accepted
|
|
47
47
|
*/
|
|
48
|
-
@column({ type: 'json', decoder:
|
|
48
|
+
@column({ type: 'json', decoder: RecordAnswerMapDecoder })
|
|
49
49
|
acceptedRecordAnswers: Map<string, RecordAnswer> = new Map();
|
|
50
50
|
|
|
51
51
|
@column({ type: 'string', nullable: true })
|
package/src/models/Invoice.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
5
5
|
import { ArrayDecoder } from '@simonbackx/simple-encoding';
|
|
6
6
|
import { QueryableModel } from '@stamhoofd/sql';
|
|
7
7
|
import { InvoicedBalanceItem } from './InvoicedBalanceItem.js';
|
|
8
|
+
import { Formatter } from '@stamhoofd/utility';
|
|
8
9
|
|
|
9
10
|
export class Invoice extends QueryableModel {
|
|
10
11
|
static table = 'invoices';
|
|
@@ -153,4 +154,12 @@ export class Invoice extends QueryableModel {
|
|
|
153
154
|
const invoicedBalanceItems = await InvoicedBalanceItem.select().where('invoiceId', invoices.map(i => i.id)).fetch();
|
|
154
155
|
return { invoicedBalanceItems };
|
|
155
156
|
}
|
|
157
|
+
|
|
158
|
+
generateCustomerFilename(ext: 'pdf' | 'xml') {
|
|
159
|
+
if (!this.number || !this.invoicedAt) {
|
|
160
|
+
return this.id + '.' + ext;
|
|
161
|
+
}
|
|
162
|
+
const date = this.invoicedAt
|
|
163
|
+
return Formatter.dateIso(date) + ' - ' + (this.totalWithVAT < 0 ? $t('%1aE') : $t('%1Zw')) + ' ' + this.number + ' - ' + Formatter.fileSlug(this.seller.name) + '.' + ext;
|
|
164
|
+
}
|
|
156
165
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { PlatformMembershipType, PlatformMembershipTypeBehaviour, PlatformMembershipTypeConfig } from '@stamhoofd/structures';
|
|
2
|
+
|
|
3
|
+
import { MemberFactory } from '../factories/MemberFactory.js';
|
|
4
|
+
import { OrganizationFactory } from '../factories/OrganizationFactory.js';
|
|
5
|
+
import { RegistrationPeriodFactory } from '../factories/RegistrationPeriodFactory.js';
|
|
6
|
+
import { MemberPlatformMembership } from './MemberPlatformMembership.js';
|
|
7
|
+
import { Platform } from './Platform.js';
|
|
8
|
+
|
|
9
|
+
describe('MemberPlatformMembership', () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
vitest.useFakeTimers({ toFake: ['Date'] }).setSystemTime(new Date(2024, 4, 1, 0, 0, 0, 0));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vitest.useRealTimers();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
async function setupDaysMembership(maximumDays: number) {
|
|
19
|
+
const period = await new RegistrationPeriodFactory({
|
|
20
|
+
startDate: new Date(2024, 0, 1, 0, 0, 0, 0),
|
|
21
|
+
endDate: new Date(2024, 11, 31, 23, 59, 59, 999),
|
|
22
|
+
}).create();
|
|
23
|
+
const organization = await new OrganizationFactory({ period }).create();
|
|
24
|
+
const member = await new MemberFactory({ organization }).create();
|
|
25
|
+
const membershipType = PlatformMembershipType.create({
|
|
26
|
+
name: 'Days membership',
|
|
27
|
+
behaviour: PlatformMembershipTypeBehaviour.Days,
|
|
28
|
+
periods: new Map([
|
|
29
|
+
[period.id, PlatformMembershipTypeConfig.create({
|
|
30
|
+
startDate: period.startDate,
|
|
31
|
+
endDate: period.endDate,
|
|
32
|
+
maximumDays,
|
|
33
|
+
})],
|
|
34
|
+
]),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const platform = await Platform.getForEditing();
|
|
38
|
+
platform.periodId = period.id;
|
|
39
|
+
platform.config.membershipTypes = [membershipType];
|
|
40
|
+
await platform.save();
|
|
41
|
+
|
|
42
|
+
const membership = new MemberPlatformMembership();
|
|
43
|
+
membership.memberId = member.id;
|
|
44
|
+
membership.membershipTypeId = membershipType.id;
|
|
45
|
+
membership.organizationId = organization.id;
|
|
46
|
+
membership.periodId = period.id;
|
|
47
|
+
|
|
48
|
+
return { member, membership };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
test('allows the inclusive maximum number of days', async () => {
|
|
52
|
+
const { member, membership } = await setupDaysMembership(2);
|
|
53
|
+
membership.startDate = new Date(2024, 4, 1, 0, 0, 0, 0);
|
|
54
|
+
membership.endDate = new Date(2024, 4, 2, 0, 0, 0, 0);
|
|
55
|
+
|
|
56
|
+
await expect(membership.calculatePrice(member)).resolves.toBeUndefined();
|
|
57
|
+
expect(membership.maximumFreeAmount).toBe(2);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('rejects days memberships that exceed maximum days', async () => {
|
|
61
|
+
const { member, membership } = await setupDaysMembership(2);
|
|
62
|
+
membership.startDate = new Date(2024, 4, 1, 0, 0, 0, 0);
|
|
63
|
+
membership.endDate = new Date(2024, 4, 3, 0, 0, 0, 0);
|
|
64
|
+
|
|
65
|
+
await expect(membership.calculatePrice(member)).rejects.toMatchObject({
|
|
66
|
+
code: 'invalid_field',
|
|
67
|
+
field: 'endDate',
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -207,8 +207,7 @@ export class MemberPlatformMembership extends QueryableModel {
|
|
|
207
207
|
this.startDate = startBrussels.toJSDate();
|
|
208
208
|
this.endDate = endBrussels.toJSDate();
|
|
209
209
|
this.expireDate = null;
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
210
|
+
} else {
|
|
212
211
|
this.endDate = Formatter.luxon(periodConfig.endDate).set({ hour: 23, minute: 59, second: 59, millisecond: 0 }).toJSDate();
|
|
213
212
|
this.expireDate = periodConfig.expireDate ? Formatter.luxon(periodConfig.expireDate).set({ hour: 23, minute: 59, second: 59, millisecond: 0 }).toJSDate() : null;
|
|
214
213
|
|
|
@@ -222,8 +221,7 @@ export class MemberPlatformMembership extends QueryableModel {
|
|
|
222
221
|
startBrussels = startBrussels.set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
|
223
222
|
this.startDate = startBrussels.toJSDate();
|
|
224
223
|
}
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
224
|
+
} else {
|
|
227
225
|
// Keep the set date, but make sure it is at 0:00 in CET
|
|
228
226
|
let startBrussels = Formatter.luxon(this.startDate);
|
|
229
227
|
startBrussels = startBrussels.set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
|
@@ -248,6 +246,16 @@ export class MemberPlatformMembership extends QueryableModel {
|
|
|
248
246
|
this.endDate = Formatter.luxon(this.startDate).set({ hour: 23, minute: 59, second: 59, millisecond: 0 }).toJSDate();
|
|
249
247
|
}
|
|
250
248
|
|
|
249
|
+
const maximumEndDate = periodConfig.getMaximumEndDate(this.startDate, membershipType.behaviour);
|
|
250
|
+
if (this.endDate > maximumEndDate) {
|
|
251
|
+
throw new SimpleError({
|
|
252
|
+
code: 'invalid_field',
|
|
253
|
+
field: 'endDate',
|
|
254
|
+
message: 'End date is after the maximum allowed end date',
|
|
255
|
+
human: $t('%15C', { date: Formatter.date(maximumEndDate) }),
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
251
259
|
if (periodConfig.trialDays) {
|
|
252
260
|
// Check whether you are elegible for a trial
|
|
253
261
|
const latestTrialDate = await this.isElegibleForTrial(member);
|
|
@@ -267,12 +275,10 @@ export class MemberPlatformMembership extends QueryableModel {
|
|
|
267
275
|
}
|
|
268
276
|
|
|
269
277
|
this.trialUntil = trialUntil.toJSDate();
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
278
|
+
} else {
|
|
272
279
|
this.trialUntil = null;
|
|
273
280
|
}
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
281
|
+
} else {
|
|
276
282
|
// No trial
|
|
277
283
|
this.trialUntil = null;
|
|
278
284
|
}
|
|
@@ -347,8 +353,7 @@ export class MemberPlatformMembership extends QueryableModel {
|
|
|
347
353
|
if (alreadyUsed < periodConfig.amountFree) {
|
|
348
354
|
freeDays = periodConfig.amountFree - alreadyUsed;
|
|
349
355
|
console.log('Free membership created for ', this.id, periodConfig.amountFree, alreadyUsed);
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
356
|
+
} else {
|
|
352
357
|
console.log('No free membership created for', this.id, periodConfig.amountFree, alreadyUsed);
|
|
353
358
|
}
|
|
354
359
|
}
|
|
@@ -359,8 +364,7 @@ export class MemberPlatformMembership extends QueryableModel {
|
|
|
359
364
|
this.priceWithoutDiscount = earliestPriceConfig.calculatePrice(tagIds, false, days);
|
|
360
365
|
this.price = priceConfig.calculatePrice(tagIds, shouldApplyReducedPrice, Math.max(0, days - freeDays));
|
|
361
366
|
this.freeAmount = Math.min(days, freeDays);
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
367
|
+
} else {
|
|
364
368
|
this.priceWithoutDiscount = earliestPriceConfig.getBasePrice(tagIds, false);
|
|
365
369
|
this.price = priceConfig.getBasePrice(tagIds, shouldApplyReducedPrice);
|
|
366
370
|
this.maximumFreeAmount = this.price > 0 ? 1 : 0;
|
package/src/models/Order.ts
CHANGED
|
@@ -286,8 +286,7 @@ export class Order extends QueryableModel {
|
|
|
286
286
|
if (webshop) {
|
|
287
287
|
await this.setRelation(Order.webshop, webshop).updateTickets();
|
|
288
288
|
}
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
289
|
+
} else {
|
|
291
290
|
this.markUpdated();
|
|
292
291
|
await this.save();
|
|
293
292
|
}
|
|
@@ -427,8 +426,7 @@ export class Order extends QueryableModel {
|
|
|
427
426
|
// Remove any reserved stock
|
|
428
427
|
const updated = Order.updateTimeSlotStock(timeSlot, this.data, false);
|
|
429
428
|
changed = changed || updated;
|
|
430
|
-
}
|
|
431
|
-
else {
|
|
429
|
+
} else {
|
|
432
430
|
this.data.reservedOrder = false;
|
|
433
431
|
this.data.reservedPersons = 0;
|
|
434
432
|
changed = true;
|
|
@@ -442,8 +440,7 @@ export class Order extends QueryableModel {
|
|
|
442
440
|
if (timeSlot) {
|
|
443
441
|
const updated = Order.updateTimeSlotStock(timeSlot, this.data, add);
|
|
444
442
|
changed = changed || updated;
|
|
445
|
-
}
|
|
446
|
-
else {
|
|
443
|
+
} else {
|
|
447
444
|
console.error('Missing timeslot ' + s.id + ' in webshop ' + this.webshopId);
|
|
448
445
|
}
|
|
449
446
|
}
|
|
@@ -456,6 +453,7 @@ export class Order extends QueryableModel {
|
|
|
456
453
|
if (previousData !== null) {
|
|
457
454
|
// Already removed
|
|
458
455
|
item.reservedSeats = [];
|
|
456
|
+
// eslint-disable-next-line no-useless-assignment
|
|
459
457
|
changed = true;
|
|
460
458
|
}
|
|
461
459
|
|
|
@@ -491,8 +489,7 @@ export class Order extends QueryableModel {
|
|
|
491
489
|
code.reserved = false;
|
|
492
490
|
discountCodeUsageMap.set(code.id, (discountCodeUsageMap.get(code.id) ?? 0) - 1);
|
|
493
491
|
changed = true;
|
|
494
|
-
}
|
|
495
|
-
else if (!code.reserved && add) {
|
|
492
|
+
} else if (!code.reserved && add) {
|
|
496
493
|
code.reserved = true;
|
|
497
494
|
discountCodeUsageMap.set(code.id, (discountCodeUsageMap.get(code.id) ?? 0) + 1);
|
|
498
495
|
changed = true;
|
|
@@ -599,8 +596,7 @@ export class Order extends QueryableModel {
|
|
|
599
596
|
ticketMap.set(item.product.id, offset + item.amount);
|
|
600
597
|
}
|
|
601
598
|
}
|
|
602
|
-
}
|
|
603
|
-
else {
|
|
599
|
+
} else {
|
|
604
600
|
// Create a shared ticket for the whole order
|
|
605
601
|
const ticket = new Ticket();
|
|
606
602
|
ticket.orderId = this.id;
|
|
@@ -752,8 +748,7 @@ export class Order extends QueryableModel {
|
|
|
752
748
|
// Needs to happen before validation, because we can include the tickets in the validation that way
|
|
753
749
|
if (this.validAt === null) {
|
|
754
750
|
await this.setRelation(Order.webshop, webshop).markValid(payment, tickets);
|
|
755
|
-
}
|
|
756
|
-
else {
|
|
751
|
+
} else {
|
|
757
752
|
this.markUpdated();
|
|
758
753
|
await this.save();
|
|
759
754
|
|
|
@@ -764,8 +759,7 @@ export class Order extends QueryableModel {
|
|
|
764
759
|
if (this.data.customer.email.length > 0) {
|
|
765
760
|
if (didCreateTickets) {
|
|
766
761
|
await this.setRelation(Order.webshop, webshop).sendTickets();
|
|
767
|
-
}
|
|
768
|
-
else {
|
|
762
|
+
} else {
|
|
769
763
|
if (payment && payment.method === PaymentMethod.Transfer) {
|
|
770
764
|
await this.setRelation(Order.webshop, webshop).sendPaidMail();
|
|
771
765
|
}
|
|
@@ -976,40 +970,34 @@ export class Order extends QueryableModel {
|
|
|
976
970
|
await this.sendEmailTemplate({
|
|
977
971
|
type: EmailTemplateType.TicketsConfirmationPOS,
|
|
978
972
|
});
|
|
979
|
-
}
|
|
980
|
-
else {
|
|
973
|
+
} else {
|
|
981
974
|
await this.sendEmailTemplate({
|
|
982
975
|
type: EmailTemplateType.TicketsConfirmation,
|
|
983
976
|
});
|
|
984
977
|
}
|
|
985
|
-
}
|
|
986
|
-
else {
|
|
978
|
+
} else {
|
|
987
979
|
if (this.webshop.meta.ticketType === WebshopTicketType.None) {
|
|
988
980
|
if (payment && payment.method === PaymentMethod.Transfer) {
|
|
989
981
|
// Also send a copy
|
|
990
982
|
await this.sendEmailTemplate({
|
|
991
983
|
type: EmailTemplateType.OrderConfirmationTransfer,
|
|
992
984
|
});
|
|
993
|
-
}
|
|
994
|
-
else if (payment && payment.method === PaymentMethod.PointOfSale) {
|
|
985
|
+
} else if (payment && payment.method === PaymentMethod.PointOfSale) {
|
|
995
986
|
await this.sendEmailTemplate({
|
|
996
987
|
type: EmailTemplateType.OrderConfirmationPOS,
|
|
997
988
|
});
|
|
998
|
-
}
|
|
999
|
-
else {
|
|
989
|
+
} else {
|
|
1000
990
|
// Also send a copy
|
|
1001
991
|
await this.sendEmailTemplate({
|
|
1002
992
|
type: EmailTemplateType.OrderConfirmationOnline,
|
|
1003
993
|
});
|
|
1004
994
|
}
|
|
1005
|
-
}
|
|
1006
|
-
else {
|
|
995
|
+
} else {
|
|
1007
996
|
if (payment && payment.method === PaymentMethod.Transfer) {
|
|
1008
997
|
await this.sendEmailTemplate({
|
|
1009
998
|
type: EmailTemplateType.TicketsConfirmationTransfer,
|
|
1010
999
|
});
|
|
1011
|
-
}
|
|
1012
|
-
else {
|
|
1000
|
+
} else {
|
|
1013
1001
|
console.error('Unexpected missing tickets for order where tickets are expected');
|
|
1014
1002
|
}
|
|
1015
1003
|
}
|
|
@@ -6,19 +6,21 @@ import { I18n } from '@stamhoofd/backend-i18n/I18n';
|
|
|
6
6
|
import type { EmailInterfaceRecipient } from '@stamhoofd/email';
|
|
7
7
|
import { QueueHandler } from '@stamhoofd/queues';
|
|
8
8
|
import { QueryableModel, SQL } from '@stamhoofd/sql';
|
|
9
|
-
import type { OrganizationEmail, PrivatePaymentConfiguration } from '@stamhoofd/structures';
|
|
10
|
-
import { Address, appToUri, Company, DNSRecordStatus, EmailTemplateType, GroupType, OrganizationMetaData, OrganizationPrivateMetaData, Organization as OrganizationStruct, PaymentMethod, PaymentProvider, Recipient, Replacement, STPackageType, TransferSettings } from '@stamhoofd/structures';
|
|
11
|
-
import {
|
|
9
|
+
import type { AppType, OrganizationEmail, PrivatePaymentConfiguration } from '@stamhoofd/structures';
|
|
10
|
+
import { AccessRight, Address, appToUri, Company, DNSRecordStatus, EmailTemplateType, getAppHost, GroupType, OrganizationMetaData, OrganizationPrivateMetaData, Organization as OrganizationStruct, PaymentMethod, PaymentProvider, Recipient, Replacement, STPackageType, TransferSettings } from '@stamhoofd/structures';
|
|
11
|
+
import type { PaymentMandate } from '@stamhoofd/structures/PaymentMandate.js';
|
|
12
|
+
import { Country } from '@stamhoofd/types/Country';
|
|
13
|
+
import { Language } from '@stamhoofd/types/Language';
|
|
14
|
+
import { Formatter, Sorter } from '@stamhoofd/utility';
|
|
12
15
|
import { v4 as uuidv4 } from 'uuid';
|
|
13
16
|
import { validateDNSRecords } from '../helpers/DNSValidator.js';
|
|
14
17
|
import { OrganizationServerMetaData } from '../structures/OrganizationServerMetaData.js';
|
|
15
18
|
import { Group } from './Group.js';
|
|
16
19
|
import { OrganizationRegistrationPeriod } from './OrganizationRegistrationPeriod.js';
|
|
17
|
-
import { StripeAccount } from './StripeAccount.js';
|
|
18
|
-
import { Country } from '@stamhoofd/types/Country';
|
|
19
|
-
import { Language } from '@stamhoofd/types/Language';
|
|
20
20
|
import { Registration } from './Registration.js';
|
|
21
|
-
import
|
|
21
|
+
import { StripeAccount } from './StripeAccount.js';
|
|
22
|
+
import { Token } from './Token.js';
|
|
23
|
+
import type { User } from './User.js';
|
|
22
24
|
|
|
23
25
|
export class Organization extends QueryableModel {
|
|
24
26
|
static table = 'organizations';
|
|
@@ -211,57 +213,12 @@ export class Organization extends QueryableModel {
|
|
|
211
213
|
return organization;
|
|
212
214
|
}
|
|
213
215
|
|
|
214
|
-
/**
|
|
215
|
-
* Potentially includes a path
|
|
216
|
-
*/
|
|
217
|
-
getRegistrationHost(i18n?: { language: Language; locale: string }): string {
|
|
218
|
-
if (this.registerDomain) {
|
|
219
|
-
let d = this.registerDomain;
|
|
220
|
-
|
|
221
|
-
if (i18n && i18n.language !== this.i18n.language) {
|
|
222
|
-
d += '/' + i18n.language;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return d;
|
|
226
|
-
}
|
|
227
|
-
return this.getDefaultRegistrationHost(i18n);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
getDefaultRegistrationHost(i18n?: { language: Language; locale: string }): string {
|
|
231
|
-
if (!STAMHOOFD.domains.registration) {
|
|
232
|
-
return STAMHOOFD.domains.dashboard + '/' + (i18n?.locale ?? this.i18n.locale) + '/' + appToUri('registration') + '/' + this.uri;
|
|
233
|
-
}
|
|
234
|
-
let defaultDomain = STAMHOOFD.domains.registration[this.address.country] ?? STAMHOOFD.domains.registration[''];
|
|
235
|
-
|
|
236
|
-
if (i18n && i18n.language !== this.i18n.language) {
|
|
237
|
-
defaultDomain += '/' + i18n.language;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return this.uri + '.' + defaultDomain;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
216
|
getDashboardHost(i18n?: { language: Language; locale: string }): string {
|
|
244
|
-
return
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
get registerUrl() {
|
|
248
|
-
return 'https://' + this.getRegistrationHost();
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* @deprecated
|
|
253
|
-
* use getRegistrationHost
|
|
254
|
-
*/
|
|
255
|
-
getHost(i18n?: I18n): string {
|
|
256
|
-
return this.getRegistrationHost(i18n);
|
|
217
|
+
return getAppHost('dashboard', this, true, i18n);
|
|
257
218
|
}
|
|
258
219
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
* Use getDefaultRegistrationHost
|
|
262
|
-
*/
|
|
263
|
-
getDefaultHost(i18n?: I18n): string {
|
|
264
|
-
return this.getDefaultRegistrationHost(i18n);
|
|
220
|
+
get registerUrl(): string {
|
|
221
|
+
return 'https://' + getAppHost('registration', this, false);
|
|
265
222
|
}
|
|
266
223
|
|
|
267
224
|
get marketingDomain(): string {
|
|
@@ -303,8 +260,7 @@ export class Organization extends QueryableModel {
|
|
|
303
260
|
await created.save();
|
|
304
261
|
return created;
|
|
305
262
|
});
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
263
|
+
} else {
|
|
308
264
|
oPeriod = oPeriods[0];
|
|
309
265
|
}
|
|
310
266
|
|
|
@@ -367,8 +323,7 @@ export class Organization extends QueryableModel {
|
|
|
367
323
|
|
|
368
324
|
console.log('Did set register domain for ' + this.id + ' to ' + organization.registerDomain);
|
|
369
325
|
}
|
|
370
|
-
}
|
|
371
|
-
else {
|
|
326
|
+
} else {
|
|
372
327
|
// Clear register domain
|
|
373
328
|
if (organization.registerDomain) {
|
|
374
329
|
// We need to clear it, to prevent sending e-mails with invalid links
|
|
@@ -406,8 +361,7 @@ export class Organization extends QueryableModel {
|
|
|
406
361
|
type: EmailTemplateType.OrganizationStableDNS,
|
|
407
362
|
bcc: true,
|
|
408
363
|
});
|
|
409
|
-
}
|
|
410
|
-
else if (!wasActive && this.privateMeta.mailDomainActive && (!didSendDomainSetupMail || didSendWarning) && !organization.serverMeta.isDNSUnstable) {
|
|
364
|
+
} else if (!wasActive && this.privateMeta.mailDomainActive && (!didSendDomainSetupMail || didSendWarning) && !organization.serverMeta.isDNSUnstable) {
|
|
411
365
|
organization.serverMeta.didSendDomainSetupMail = true;
|
|
412
366
|
await organization.save();
|
|
413
367
|
|
|
@@ -415,15 +369,13 @@ export class Organization extends QueryableModel {
|
|
|
415
369
|
await this.sendEmailTemplate({
|
|
416
370
|
type: EmailTemplateType.OrganizationDNSSetupComplete,
|
|
417
371
|
});
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
372
|
+
} else {
|
|
420
373
|
await this.sendEmailTemplate({
|
|
421
374
|
type: EmailTemplateType.OrganizationValidDNS,
|
|
422
375
|
});
|
|
423
376
|
}
|
|
424
377
|
}
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
378
|
+
} else {
|
|
427
379
|
// DNS settings gone broken
|
|
428
380
|
if (organization.privateMeta.mailDomain) {
|
|
429
381
|
organization.privateMeta.pendingMailDomain = organization.privateMeta.pendingMailDomain ?? organization.privateMeta.mailDomain;
|
|
@@ -448,8 +400,7 @@ export class Organization extends QueryableModel {
|
|
|
448
400
|
type: EmailTemplateType.OrganizationUnstableDNS,
|
|
449
401
|
bcc: true,
|
|
450
402
|
});
|
|
451
|
-
}
|
|
452
|
-
else if (!organization.serverMeta.isDNSUnstable && organization.serverMeta.didSendDomainSetupMail && organization.serverMeta.DNSRecordWarningCount == 0) {
|
|
403
|
+
} else if (!organization.serverMeta.isDNSUnstable && organization.serverMeta.didSendDomainSetupMail && organization.serverMeta.DNSRecordWarningCount == 0) {
|
|
453
404
|
organization.serverMeta.DNSRecordWarningCount += 1;
|
|
454
405
|
await organization.save();
|
|
455
406
|
|
|
@@ -500,6 +451,10 @@ export class Organization extends QueryableModel {
|
|
|
500
451
|
token: 'mailDomain',
|
|
501
452
|
value: this.privateMeta.mailDomain ?? this.privateMeta.pendingMailDomain ?? '',
|
|
502
453
|
}),
|
|
454
|
+
Replacement.create({
|
|
455
|
+
token: 'organizationName',
|
|
456
|
+
value: this.name,
|
|
457
|
+
}),
|
|
503
458
|
],
|
|
504
459
|
unsubscribeType: 'marketing',
|
|
505
460
|
fromStamhoofd: true,
|
|
@@ -547,8 +502,7 @@ export class Organization extends QueryableModel {
|
|
|
547
502
|
|
|
548
503
|
await client.send(deleteCmd);
|
|
549
504
|
console.log('Deleted AWS mail idenitiy @' + this.id + ' for ' + this.privateMeta.mailDomain);
|
|
550
|
-
}
|
|
551
|
-
catch (e) {
|
|
505
|
+
} catch (e) {
|
|
552
506
|
console.error('Could not delete AWS email identitiy @' + this.id + ' for ' + this.privateMeta.mailDomain);
|
|
553
507
|
console.error(e);
|
|
554
508
|
}
|
|
@@ -615,8 +569,7 @@ export class Organization extends QueryableModel {
|
|
|
615
569
|
// Recreate it immediately
|
|
616
570
|
exists = false;
|
|
617
571
|
}
|
|
618
|
-
}
|
|
619
|
-
catch (e) {
|
|
572
|
+
} catch (e) {
|
|
620
573
|
console.error(e);
|
|
621
574
|
}
|
|
622
575
|
|
|
@@ -860,6 +813,17 @@ export class Organization extends QueryableModel {
|
|
|
860
813
|
return admins.filter(a => a.permissions && a.permissions.forOrganization(this)?.hasFullAccess());
|
|
861
814
|
}
|
|
862
815
|
|
|
816
|
+
/**
|
|
817
|
+
* These email addresess are private
|
|
818
|
+
*/
|
|
819
|
+
async getFinanceAdmins() {
|
|
820
|
+
const admins = await this.getAdmins();
|
|
821
|
+
const filtered = admins.filter(a => a.permissions && (a.permissions.forOrganization(this)?.hasFullAccess() || a.permissions.forOrganization(this)?.hasAccessRight(AccessRight.OrganizationFinanceDirector)));
|
|
822
|
+
|
|
823
|
+
// Only full access
|
|
824
|
+
return filtered;
|
|
825
|
+
}
|
|
826
|
+
|
|
863
827
|
/**
|
|
864
828
|
* These email addresess are private
|
|
865
829
|
*/
|
|
@@ -879,32 +843,90 @@ export class Organization extends QueryableModel {
|
|
|
879
843
|
return filtered.flatMap(f => f.getEmailTo());
|
|
880
844
|
}
|
|
881
845
|
|
|
846
|
+
adminsToRecipients(admins: User[]) {
|
|
847
|
+
return admins.flatMap((f) => {
|
|
848
|
+
return Recipient.create({
|
|
849
|
+
firstName: f.firstName,
|
|
850
|
+
lastName: f.lastName,
|
|
851
|
+
email: f.email,
|
|
852
|
+
replacements: [],
|
|
853
|
+
});
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
|
|
882
857
|
/**
|
|
883
858
|
* These email addresess are private
|
|
884
859
|
*/
|
|
885
860
|
async getAdminRecipients(): Promise<Recipient[]> {
|
|
886
|
-
|
|
861
|
+
const filtered = await this.getFullAdmins();
|
|
862
|
+
return this.adminsToRecipients(filtered);
|
|
863
|
+
}
|
|
887
864
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
865
|
+
/**
|
|
866
|
+
* These email addresess are private
|
|
867
|
+
*/
|
|
868
|
+
async getFinanceAdminRecipients(): Promise<Recipient[]> {
|
|
869
|
+
const filtered = await this.getFullAdmins();
|
|
870
|
+
return this.adminsToRecipients(filtered);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* These email addresess are private
|
|
875
|
+
*/
|
|
876
|
+
async getInvoicingToEmails() {
|
|
877
|
+
// Circular reference fix
|
|
878
|
+
const filtered = await this.getFinanceAdmins();
|
|
879
|
+
|
|
880
|
+
if (filtered.length > 0) {
|
|
881
|
+
return filtered.flatMap(f => f.getEmailTo()).map((recipient) => {
|
|
882
|
+
if (!recipient.name) {
|
|
883
|
+
return recipient.email;
|
|
884
|
+
}
|
|
885
|
+
const cleanedName = Formatter.emailSenderName(recipient.name);
|
|
886
|
+
if (cleanedName.length < 2) {
|
|
887
|
+
return recipient.email;
|
|
888
|
+
}
|
|
889
|
+
return '"' + cleanedName + '" <' + recipient.email + '>';
|
|
890
|
+
}).join(', ');
|
|
893
891
|
}
|
|
894
892
|
|
|
895
|
-
return
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
893
|
+
return undefined;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Returns one email for invoices. since in ubl we can only add one address.
|
|
898
|
+
* We choose the oldest user that was active in the last 3 months (otherwise the oldest user if noone was active)
|
|
899
|
+
*/
|
|
900
|
+
async getInvoicingToEmail(): Promise<string | undefined> {
|
|
901
|
+
// Circular reference fix
|
|
902
|
+
const admins = await this.getAdmins();
|
|
903
|
+
|
|
904
|
+
const tokens = await Token.select().where('userId', admins.map(a => a.id)).fetch();
|
|
905
|
+
|
|
906
|
+
// Sort by admins that were active in the last 3 months, then creation date
|
|
907
|
+
const cutoffDate = new Date(Date.now() - 1000 * 60 * 60 * 24 * 31 * 3);
|
|
908
|
+
admins.sort((a, b) => {
|
|
909
|
+
const aTokens = tokens.filter(t => t.userId === a.id);
|
|
910
|
+
const bTokens = tokens.filter(t => t.userId === b.id);
|
|
911
|
+
const aActive = !!aTokens.find(t => t.updatedAt > cutoffDate);
|
|
912
|
+
const bActive = !!bTokens.find(t => t.updatedAt > cutoffDate);
|
|
913
|
+
return Sorter.stack(
|
|
914
|
+
Sorter.byBooleanValue(aActive, bActive),
|
|
915
|
+
Sorter.byDateValue(b.createdAt, a.createdAt),
|
|
916
|
+
);
|
|
907
917
|
});
|
|
918
|
+
|
|
919
|
+
const filtered = admins.filter(a => a.verified && a.permissions && !a.email.endsWith('@stamhoofd.be') && (a.permissions.forOrganization(this)?.hasFullAccess() || a.permissions.forOrganization(this)?.hasAccessRight(AccessRight.OrganizationFinanceDirector)));
|
|
920
|
+
|
|
921
|
+
if (filtered.length > 0) {
|
|
922
|
+
return filtered.map(f => f.email)[0];
|
|
923
|
+
}
|
|
924
|
+
const filtered2 = admins.filter(a => a.verified && a.permissions && (a.permissions.forOrganization(this)?.hasFullAccess() || a.permissions.forOrganization(this)?.hasAccessRight(AccessRight.OrganizationFinanceDirector)));
|
|
925
|
+
|
|
926
|
+
if (filtered2.length > 0) {
|
|
927
|
+
return filtered2.map(f => f.email)[0];
|
|
928
|
+
}
|
|
929
|
+
return undefined;
|
|
908
930
|
}
|
|
909
931
|
|
|
910
932
|
/**
|
|
@@ -926,10 +948,10 @@ export class Organization extends QueryableModel {
|
|
|
926
948
|
if (mandate) {
|
|
927
949
|
return {
|
|
928
950
|
provider: mandate.provider,
|
|
929
|
-
stripeAccount: null
|
|
930
|
-
}
|
|
951
|
+
stripeAccount: null,
|
|
952
|
+
};
|
|
931
953
|
}
|
|
932
|
-
|
|
954
|
+
|
|
933
955
|
let stripeAccount = (config.stripeAccountId ? (await StripeAccount.getByID(config.stripeAccountId)) : null) ?? null;
|
|
934
956
|
if (stripeAccount && stripeAccount.organizationId !== this.id) {
|
|
935
957
|
console.warn('Stripe account ' + stripeAccount.id + ' is not linked to organization ' + this.id);
|
|
@@ -1016,7 +1038,7 @@ export class Organization extends QueryableModel {
|
|
|
1016
1038
|
* Assures at least one company at all times
|
|
1017
1039
|
*/
|
|
1018
1040
|
get defaultCompanies() {
|
|
1019
|
-
|
|
1041
|
+
const b = this.meta.companies.length
|
|
1020
1042
|
? this.meta.companies
|
|
1021
1043
|
: [
|
|
1022
1044
|
Company.create({
|
|
@@ -1024,5 +1046,12 @@ export class Organization extends QueryableModel {
|
|
|
1024
1046
|
address: this.address,
|
|
1025
1047
|
}),
|
|
1026
1048
|
];
|
|
1049
|
+
|
|
1050
|
+
// Missing address -> use organization address
|
|
1051
|
+
if (!b[0].address) {
|
|
1052
|
+
b[0].address = this.address;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
return b;
|
|
1027
1056
|
}
|
|
1028
1057
|
}
|