@stamhoofd/models 2.120.6 → 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/RegistrationInvitationFactory.d.ts +15 -0
- package/dist/factories/RegistrationInvitationFactory.d.ts.map +1 -0
- package/dist/factories/RegistrationInvitationFactory.js +18 -0
- package/dist/factories/RegistrationInvitationFactory.js.map +1 -0
- 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/factories/index.d.ts +1 -0
- package/dist/factories/index.d.ts.map +1 -1
- package/dist/factories/index.js +1 -0
- package/dist/factories/index.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 +10 -1
- package/dist/helpers/Handlebars.js.map +1 -1
- package/dist/helpers/InvoiceCounter.d.ts +24 -0
- package/dist/helpers/InvoiceCounter.d.ts.map +1 -0
- package/dist/helpers/InvoiceCounter.js +133 -0
- package/dist/helpers/InvoiceCounter.js.map +1 -0
- 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/1763216320-bigint-balance-item-payments.sql +2 -0
- package/dist/migrations/1763216320-bigint-balance-items.sql +5 -0
- package/dist/migrations/1763216320-bigint-orders.sql +2 -0
- package/dist/migrations/1763216320-bigint-payments.sql +2 -0
- package/dist/migrations/1763216332-bigint-balance-item-price-total.sql +2 -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/1776873089-create-registration-invitations-table.sql +13 -0
- package/dist/migrations/1778657958-payments-create-mandate.sql +2 -0
- package/dist/migrations/1778657959-payments-mandate-id.sql +2 -0
- package/dist/migrations/1778796615-payments-reversing-payment-id.sql +5 -0
- package/dist/migrations/1778950642-price-invoiced.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 +14 -1
- package/dist/models/BalanceItem.d.ts.map +1 -1
- package/dist/models/BalanceItem.js +91 -41
- 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/Group.d.ts +4 -0
- package/dist/models/Group.d.ts.map +1 -1
- package/dist/models/Group.js +17 -0
- package/dist/models/Group.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 -8
- 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/MollieToken.d.ts +4 -8
- package/dist/models/MollieToken.d.ts.map +1 -1
- package/dist/models/MollieToken.js +37 -90
- package/dist/models/MollieToken.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 +30 -23
- package/dist/models/Organization.d.ts.map +1 -1
- package/dist/models/Organization.js +113 -61
- 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 +35 -3
- package/dist/models/Payment.d.ts.map +1 -1
- package/dist/models/Payment.js +66 -3
- package/dist/models/Payment.js.map +1 -1
- package/dist/models/Registration.d.ts +1 -0
- package/dist/models/Registration.d.ts.map +1 -1
- package/dist/models/Registration.js +4 -3
- package/dist/models/Registration.js.map +1 -1
- package/dist/models/RegistrationInvitation.d.ts +14 -0
- package/dist/models/RegistrationInvitation.d.ts.map +1 -0
- package/dist/models/RegistrationInvitation.js +45 -0
- package/dist/models/RegistrationInvitation.js.map +1 -0
- 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/User.d.ts +1 -1
- package/dist/models/User.d.ts.map +1 -1
- package/dist/models/User.js +1 -1
- package/dist/models/User.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 +2 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +2 -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 +41 -13
- package/src/factories/GroupFactory.ts +4 -6
- package/src/factories/OrganizationFactory.ts +12 -4
- package/src/factories/RegistrationInvitationFactory.ts +24 -0
- package/src/factories/STPackageFactory.ts +2 -2
- package/src/factories/UserFactory.ts +4 -5
- package/src/factories/index.ts +1 -0
- package/src/helpers/EmailBuilder.ts +19 -28
- package/src/helpers/Handlebars.ts +10 -1
- package/src/helpers/InvoiceCounter.test.ts +220 -0
- package/src/helpers/InvoiceCounter.ts +162 -0
- 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/1763216320-bigint-balance-item-payments.sql +2 -0
- package/src/migrations/1763216320-bigint-balance-items.sql +5 -0
- package/src/migrations/1763216320-bigint-orders.sql +2 -0
- package/src/migrations/1763216320-bigint-payments.sql +2 -0
- package/src/migrations/1763216332-bigint-balance-item-price-total.sql +2 -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/1776873089-create-registration-invitations-table.sql +13 -0
- package/src/migrations/1778657958-payments-create-mandate.sql +2 -0
- package/src/migrations/1778657959-payments-mandate-id.sql +2 -0
- package/src/migrations/1778796615-payments-reversing-payment-id.sql +5 -0
- package/src/migrations/1778950642-price-invoiced.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 +98 -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/Group.ts +24 -3
- package/src/models/Invoice.ts +10 -9
- package/src/models/MemberPlatformMembership.test.ts +70 -0
- package/src/models/MemberPlatformMembership.ts +16 -12
- package/src/models/MollieToken.ts +42 -102
- package/src/models/Order.ts +14 -26
- package/src/models/Organization.ts +143 -86
- package/src/models/PasswordToken.ts +21 -21
- package/src/models/Payment.ts +61 -4
- package/src/models/Registration.ts +6 -5
- package/src/models/RegistrationInvitation.ts +40 -0
- 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/User.ts +6 -7
- 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 +3 -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
package/src/models/Email.test.ts
CHANGED
|
@@ -896,7 +896,7 @@ describe('Model.Email', () => {
|
|
|
896
896
|
organizationId: organization.id,
|
|
897
897
|
type: BalanceItemType.Other,
|
|
898
898
|
amount: 2,
|
|
899
|
-
unitPrice:
|
|
899
|
+
unitPrice: 12_99_00,
|
|
900
900
|
description: 'Test balance item',
|
|
901
901
|
}).create();
|
|
902
902
|
await CachedBalance.updateForUsers(organization.id, [existingUser.id]);
|
|
@@ -940,12 +940,12 @@ describe('Model.Email', () => {
|
|
|
940
940
|
// Check if the table is correct
|
|
941
941
|
expect(EmailMocker.getSucceededEmail(0).html).toMatch('<table');
|
|
942
942
|
expect(EmailMocker.getSucceededEmail(0).html).toMatch('2 x '); // amount
|
|
943
|
-
expect(EmailMocker.getSucceededEmail(0).html).toMatch(Formatter.price(
|
|
943
|
+
expect(EmailMocker.getSucceededEmail(0).html).toMatch(Formatter.price(12_99_00)); // unit price
|
|
944
944
|
expect(EmailMocker.getSucceededEmail(0).html).toMatch('<td>' + expectedAmount); // total price in table
|
|
945
945
|
expect(EmailMocker.getSucceededEmail(0).html).toMatch('Test balance item'); // description
|
|
946
946
|
|
|
947
947
|
expect(EmailMocker.getSucceededEmail(0).text).toMatch('2 x '); // amount
|
|
948
|
-
expect(EmailMocker.getSucceededEmail(0).text).toMatch(Formatter.price(
|
|
948
|
+
expect(EmailMocker.getSucceededEmail(0).text).toMatch(Formatter.price(12_99_00)); // unit price
|
|
949
949
|
expect(EmailMocker.getSucceededEmail(0).text).toMatch(expectedAmount); // total price in table
|
|
950
950
|
expect(EmailMocker.getSucceededEmail(0).text?.toLowerCase()).toMatch('test balance item'); // description
|
|
951
951
|
}, 15_000);
|
package/src/models/Email.ts
CHANGED
|
@@ -23,11 +23,9 @@ type Attachment = { filename: string; path?: string; href?: string; content?: st
|
|
|
23
23
|
function errorToSimpleErrors(e: unknown) {
|
|
24
24
|
if (isSimpleErrors(e)) {
|
|
25
25
|
return e;
|
|
26
|
-
}
|
|
27
|
-
else if (isSimpleError(e)) {
|
|
26
|
+
} else if (isSimpleError(e)) {
|
|
28
27
|
return new SimpleErrors(e);
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
28
|
+
} else {
|
|
31
29
|
return new SimpleErrors(
|
|
32
30
|
new SimpleError({
|
|
33
31
|
code: 'unknown_error',
|
|
@@ -471,22 +469,22 @@ export class Email extends QueryableModel {
|
|
|
471
469
|
case 'hard-bounce': {
|
|
472
470
|
base.set('hardBouncesCount',
|
|
473
471
|
SQL.calculation(SQL.column('hardBouncesCount'))
|
|
474
|
-
.add(readDynamicSQLExpression(1))
|
|
475
|
-
|
|
472
|
+
.add(readDynamicSQLExpression(1)),
|
|
473
|
+
);
|
|
476
474
|
break;
|
|
477
475
|
}
|
|
478
476
|
case 'soft-bounce': {
|
|
479
477
|
base.set('softBouncesCount',
|
|
480
478
|
SQL.calculation(SQL.column('softBouncesCount'))
|
|
481
|
-
.add(readDynamicSQLExpression(1))
|
|
482
|
-
|
|
479
|
+
.add(readDynamicSQLExpression(1)),
|
|
480
|
+
);
|
|
483
481
|
break;
|
|
484
482
|
}
|
|
485
483
|
case 'complaint': {
|
|
486
484
|
base.set('spamComplaintsCount',
|
|
487
485
|
SQL.calculation(SQL.column('spamComplaintsCount'))
|
|
488
|
-
.add(readDynamicSQLExpression(1))
|
|
489
|
-
|
|
486
|
+
.add(readDynamicSQLExpression(1)),
|
|
487
|
+
);
|
|
490
488
|
break;
|
|
491
489
|
}
|
|
492
490
|
}
|
|
@@ -604,8 +602,7 @@ export class Email extends QueryableModel {
|
|
|
604
602
|
});
|
|
605
603
|
if (waitForSending) {
|
|
606
604
|
return await this.resumeSending();
|
|
607
|
-
}
|
|
608
|
-
else {
|
|
605
|
+
} else {
|
|
609
606
|
this.resumeSending().catch(console.error);
|
|
610
607
|
}
|
|
611
608
|
return this;
|
|
@@ -642,8 +639,7 @@ export class Email extends QueryableModel {
|
|
|
642
639
|
contentType: attachment.contentType ?? undefined,
|
|
643
640
|
encoding: 'base64',
|
|
644
641
|
});
|
|
645
|
-
}
|
|
646
|
-
else {
|
|
642
|
+
} else {
|
|
647
643
|
// Note: because we send lots of emails, we better download the file here so we can reuse it in every email instead of downloading it every time
|
|
648
644
|
const withSigned = await attachment.file!.withSignedUrl();
|
|
649
645
|
if (!withSigned || !withSigned.signedUrl) {
|
|
@@ -655,12 +651,11 @@ export class Email extends QueryableModel {
|
|
|
655
651
|
}
|
|
656
652
|
|
|
657
653
|
const filePath = withSigned.signedUrl;
|
|
658
|
-
let fileBuffer: Buffer | null
|
|
654
|
+
let fileBuffer: Buffer | null;
|
|
659
655
|
try {
|
|
660
656
|
const response = await fetch(filePath);
|
|
661
657
|
fileBuffer = Buffer.from(await response.arrayBuffer());
|
|
662
|
-
}
|
|
663
|
-
catch (e) {
|
|
658
|
+
} catch (e) {
|
|
664
659
|
throw new SimpleError({
|
|
665
660
|
code: 'attachment_not_found',
|
|
666
661
|
message: 'Attachment not found',
|
|
@@ -707,8 +702,7 @@ export class Email extends QueryableModel {
|
|
|
707
702
|
// Update repacements that have been generated
|
|
708
703
|
recipient.replacements = virtualRecipient.replacements;
|
|
709
704
|
await recipient.save();
|
|
710
|
-
}
|
|
711
|
-
else {
|
|
705
|
+
} else {
|
|
712
706
|
recipient.failCount += 1;
|
|
713
707
|
recipient.failErrorMessage = error.message;
|
|
714
708
|
if (recipient.failError) {
|
|
@@ -719,8 +713,7 @@ export class Email extends QueryableModel {
|
|
|
719
713
|
recipient.lastFailedAt = new Date();
|
|
720
714
|
await recipient.save();
|
|
721
715
|
}
|
|
722
|
-
}
|
|
723
|
-
catch (e) {
|
|
716
|
+
} catch (e) {
|
|
724
717
|
console.error(e);
|
|
725
718
|
}
|
|
726
719
|
promiseResolve();
|
|
@@ -761,8 +754,7 @@ export class Email extends QueryableModel {
|
|
|
761
754
|
console.log('Email already sent, skipping...', upToDate.id);
|
|
762
755
|
return upToDate;
|
|
763
756
|
}
|
|
764
|
-
}
|
|
765
|
-
else {
|
|
757
|
+
} else {
|
|
766
758
|
if (singleRecipientId) {
|
|
767
759
|
// Not possible
|
|
768
760
|
throw new SimpleError({
|
|
@@ -786,8 +778,7 @@ export class Email extends QueryableModel {
|
|
|
786
778
|
await upToDate.save();
|
|
787
779
|
return upToDate;
|
|
788
780
|
}
|
|
789
|
-
}
|
|
790
|
-
else if (upToDate.status !== EmailStatus.Queued) {
|
|
781
|
+
} else if (upToDate.status !== EmailStatus.Queued) {
|
|
791
782
|
console.error('Email is not queued or sending, cannot send', upToDate.id, upToDate.status);
|
|
792
783
|
return upToDate;
|
|
793
784
|
}
|
|
@@ -888,8 +879,7 @@ export class Email extends QueryableModel {
|
|
|
888
879
|
|
|
889
880
|
try {
|
|
890
881
|
await upToDate.save();
|
|
891
|
-
}
|
|
892
|
-
finally {
|
|
882
|
+
} finally {
|
|
893
883
|
isSavingStatus = false;
|
|
894
884
|
}
|
|
895
885
|
}
|
|
@@ -938,8 +928,7 @@ export class Email extends QueryableModel {
|
|
|
938
928
|
// Failed or soft-failed
|
|
939
929
|
if (recipient.failError && isSoftEmailRecipientError(recipient.failError)) {
|
|
940
930
|
softFailedCount += 1;
|
|
941
|
-
}
|
|
942
|
-
else {
|
|
931
|
+
} else {
|
|
943
932
|
failedCount += 1;
|
|
944
933
|
}
|
|
945
934
|
skipped++;
|
|
@@ -957,13 +946,11 @@ export class Email extends QueryableModel {
|
|
|
957
946
|
if (recipient.sentAt) {
|
|
958
947
|
succeededCount += 1;
|
|
959
948
|
await saveStatus();
|
|
960
|
-
}
|
|
961
|
-
else {
|
|
949
|
+
} else {
|
|
962
950
|
// Failed or soft-failed
|
|
963
951
|
if (recipient.failError && isSoftEmailRecipientError(recipient.failError)) {
|
|
964
952
|
softFailedCount += 1;
|
|
965
|
-
}
|
|
966
|
-
else {
|
|
953
|
+
} else {
|
|
967
954
|
failedCount += 1;
|
|
968
955
|
}
|
|
969
956
|
await saveStatus();
|
|
@@ -973,14 +960,12 @@ export class Email extends QueryableModel {
|
|
|
973
960
|
|
|
974
961
|
if (sendingPromises.length > 0 || skipped > 0) {
|
|
975
962
|
await Promise.all(sendingPromises);
|
|
976
|
-
}
|
|
977
|
-
else {
|
|
963
|
+
} else {
|
|
978
964
|
break;
|
|
979
965
|
}
|
|
980
966
|
}
|
|
981
967
|
}
|
|
982
|
-
}
|
|
983
|
-
catch (e) {
|
|
968
|
+
} catch (e) {
|
|
984
969
|
if (!upToDate) {
|
|
985
970
|
throw e;
|
|
986
971
|
}
|
|
@@ -1023,8 +1008,7 @@ export class Email extends QueryableModel {
|
|
|
1023
1008
|
human: $t(`%1EG`),
|
|
1024
1009
|
}),
|
|
1025
1010
|
);
|
|
1026
|
-
}
|
|
1027
|
-
else {
|
|
1011
|
+
} else {
|
|
1028
1012
|
upToDate.status = EmailStatus.Sent;
|
|
1029
1013
|
upToDate.emailErrors = null;
|
|
1030
1014
|
}
|
|
@@ -1115,8 +1099,7 @@ export class Email extends QueryableModel {
|
|
|
1115
1099
|
await upToDate.save();
|
|
1116
1100
|
|
|
1117
1101
|
console.log('Updated recipient count for email', id, 'to', count);
|
|
1118
|
-
}
|
|
1119
|
-
catch (e) {
|
|
1102
|
+
} catch (e) {
|
|
1120
1103
|
if (isAbortedError(e)) {
|
|
1121
1104
|
return;
|
|
1122
1105
|
}
|
|
@@ -1263,8 +1246,7 @@ export class Email extends QueryableModel {
|
|
|
1263
1246
|
recipient.replacements = merged;
|
|
1264
1247
|
|
|
1265
1248
|
break;
|
|
1266
|
-
}
|
|
1267
|
-
else {
|
|
1249
|
+
} else {
|
|
1268
1250
|
console.log('Could not merge duplicate email recipient', item.email, other.id, 'keeping both', other.replacements, item.replacements);
|
|
1269
1251
|
}
|
|
1270
1252
|
}
|
|
@@ -1283,8 +1265,7 @@ export class Email extends QueryableModel {
|
|
|
1283
1265
|
|
|
1284
1266
|
if (!recipient.email || duplicateOfRecipientId) {
|
|
1285
1267
|
countWithoutEmail += 1;
|
|
1286
|
-
}
|
|
1287
|
-
else {
|
|
1268
|
+
} else {
|
|
1288
1269
|
count += 1;
|
|
1289
1270
|
}
|
|
1290
1271
|
}
|
|
@@ -1299,8 +1280,7 @@ export class Email extends QueryableModel {
|
|
|
1299
1280
|
upToDate.recipientsErrors = null;
|
|
1300
1281
|
upToDate.membersCount = membersSet.size;
|
|
1301
1282
|
await upToDate.save();
|
|
1302
|
-
}
|
|
1303
|
-
catch (e: unknown) {
|
|
1283
|
+
} catch (e: unknown) {
|
|
1304
1284
|
console.error('Failed to build recipients for email', id);
|
|
1305
1285
|
console.error(e);
|
|
1306
1286
|
upToDate.recipientsStatus = EmailRecipientsStatus.NotCreated;
|
|
@@ -1381,8 +1361,7 @@ export class Email extends QueryableModel {
|
|
|
1381
1361
|
}
|
|
1382
1362
|
|
|
1383
1363
|
console.warn('No example recipient found for email', id);
|
|
1384
|
-
}
|
|
1385
|
-
catch (e) {
|
|
1364
|
+
} catch (e) {
|
|
1386
1365
|
console.error('Failed to build example recipient for email', id);
|
|
1387
1366
|
console.error(e);
|
|
1388
1367
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { column } from '@simonbackx/simple-database';
|
|
2
2
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
3
|
-
import type {I18n} from '@stamhoofd/backend-i18n';
|
|
3
|
+
import type { I18n } from '@stamhoofd/backend-i18n';
|
|
4
4
|
import { QueryableModel } from '@stamhoofd/sql';
|
|
5
|
-
import { EmailTemplateType, Recipient, Replacement } from '@stamhoofd/structures';
|
|
5
|
+
import { appToUri, EmailTemplateType, getAppHost, Recipient, Replacement } from '@stamhoofd/structures';
|
|
6
6
|
import basex from 'base-x';
|
|
7
7
|
import crypto from 'crypto';
|
|
8
8
|
import { v4 as uuidv4 } from 'uuid';
|
|
9
9
|
import { sendEmailTemplate } from '../helpers/EmailBuilder.js';
|
|
10
10
|
import { Platform } from './Platform.js';
|
|
11
|
-
import type {User} from './User.js';
|
|
12
|
-
import type {Organization} from './Organization.js';
|
|
11
|
+
import type { User } from './User.js';
|
|
12
|
+
import type { Organization } from './Organization.js';
|
|
13
13
|
|
|
14
14
|
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
15
15
|
const bs58 = basex(ALPHABET);
|
|
@@ -121,20 +121,8 @@ export class EmailVerificationCode extends QueryableModel {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
getEmailVerificationUrl(user: User, organization: Organization | null, i18n: I18n) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
host = 'https://' + (STAMHOOFD.domains.dashboard ?? 'stamhoofd.app') + '/' + i18n.locale;
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
// Add language if different than default
|
|
130
|
-
host = 'https://' + organization.getHost();
|
|
131
|
-
|
|
132
|
-
if (i18n.language !== organization.i18n.language) {
|
|
133
|
-
host += '/' + i18n.language;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return host + '/verify-email' + (user.organizationPermissions && this.organizationId ? '/' + encodeURIComponent(this.organizationId) : '') + '?code=' + encodeURIComponent(this.code) + '&token=' + encodeURIComponent(this.token);
|
|
124
|
+
const host = getAppHost('verify-email', organization, !!user.permissions, i18n);
|
|
125
|
+
return 'https://' + host + '?code=' + encodeURIComponent(this.code) + '&token=' + encodeURIComponent(this.token) + '&email=' + encodeURIComponent(this.email);
|
|
138
126
|
}
|
|
139
127
|
|
|
140
128
|
/**
|
|
@@ -283,8 +271,7 @@ export class EmailVerificationCode extends QueryableModel {
|
|
|
283
271
|
},
|
|
284
272
|
type: 'transactional',
|
|
285
273
|
});
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
274
|
+
} else {
|
|
288
275
|
await sendEmailTemplate(organization, {
|
|
289
276
|
recipients: [
|
|
290
277
|
Recipient.create({
|
|
@@ -359,8 +346,7 @@ export class EmailVerificationCode extends QueryableModel {
|
|
|
359
346
|
|
|
360
347
|
// Expire in 3 hours
|
|
361
348
|
verificationCode.expiresAt = new Date(new Date().getTime() + 1000 * 60 * 60 * 3);
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
349
|
+
} else {
|
|
364
350
|
verificationCode = verificationCodes[0];
|
|
365
351
|
|
|
366
352
|
if (verificationCode.email !== email || verificationCode.expiresAt < new Date(new Date().getTime() - 15 * 60 * 1000) || verificationCode.tries >= EmailVerificationCode.MAX_TRIES) {
|
package/src/models/Event.ts
CHANGED
|
@@ -68,6 +68,10 @@ export class Event extends QueryableModel {
|
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
get slug() {
|
|
72
|
+
return this.getStructure().slug;
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
/**
|
|
72
76
|
* @deprecated
|
|
73
77
|
*/
|
|
@@ -101,8 +105,7 @@ export class Event extends QueryableModel {
|
|
|
101
105
|
group.settings.requireOrganizationIds = [this.organizationId];
|
|
102
106
|
group.settings.requireOrganizationTags = [];
|
|
103
107
|
group.settings.requirePlatformMembershipOn = null;
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
108
|
+
} else {
|
|
106
109
|
group.settings.requireOrganizationTags = this.meta.organizationTagIds ?? [];
|
|
107
110
|
|
|
108
111
|
// Everyone can register
|
|
@@ -118,8 +121,7 @@ export class Event extends QueryableModel {
|
|
|
118
121
|
if (waitingList) {
|
|
119
122
|
if (group.settings.allowRegistrationsByOrganization) {
|
|
120
123
|
waitingList.settings.allowRegistrationsByOrganization = true;
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
124
|
+
} else {
|
|
123
125
|
waitingList.settings.allowRegistrationsByOrganization = false;
|
|
124
126
|
}
|
|
125
127
|
await this.syncGroupRequirements(waitingList);
|
|
@@ -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/Group.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { column, Database
|
|
2
|
-
import type { GroupCategory} from '@stamhoofd/structures';
|
|
1
|
+
import { column, Database } from '@simonbackx/simple-database';
|
|
2
|
+
import type { GroupCategory } from '@stamhoofd/structures';
|
|
3
3
|
import { GroupPrivateSettings, GroupSettings, GroupStatus, Group as GroupStruct, GroupType, StockReservation } from '@stamhoofd/structures';
|
|
4
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
5
|
|
|
6
6
|
import { ArrayDecoder } from '@simonbackx/simple-encoding';
|
|
7
7
|
import { QueueHandler } from '@stamhoofd/queues';
|
|
8
8
|
import { QueryableModel } from '@stamhoofd/sql';
|
|
9
|
-
import type {OrganizationRegistrationPeriod} from './OrganizationRegistrationPeriod.js';
|
|
9
|
+
import type { OrganizationRegistrationPeriod } from './OrganizationRegistrationPeriod.js';
|
|
10
10
|
import { Registration } from './Registration.js';
|
|
11
11
|
|
|
12
12
|
if (Registration === undefined) {
|
|
@@ -90,6 +90,27 @@ export class Group extends QueryableModel {
|
|
|
90
90
|
@column({ type: 'json', decoder: new ArrayDecoder(StockReservation) })
|
|
91
91
|
stockReservations: StockReservation[] = [];
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* No registrations and waiting list registrations are possible if closed
|
|
95
|
+
*/
|
|
96
|
+
get closed() {
|
|
97
|
+
if (this.status !== GroupStatus.Open) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (this.settings.notYetOpen) {
|
|
102
|
+
// Start date or pre registration date are in the future
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const now = new Date();
|
|
107
|
+
if (this.settings.registrationEndDate && this.settings.registrationEndDate < now) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
93
114
|
static async getAll(organizationId: string, periodId: string | null, active = true, types: GroupType[] = [GroupType.Membership]): Promise<Group[]> {
|
|
94
115
|
const query = Group.select()
|
|
95
116
|
.where('organizationId', organizationId);
|
package/src/models/Invoice.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { column } from '@simonbackx/simple-database';
|
|
2
|
-
import { Company, File,
|
|
2
|
+
import { Company, File, PaymentCustomer, VATSubtotal } from '@stamhoofd/structures';
|
|
3
3
|
import { v4 as uuidv4 } from 'uuid';
|
|
4
4
|
|
|
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';
|
|
@@ -109,14 +110,6 @@ export class Invoice extends QueryableModel {
|
|
|
109
110
|
@column({
|
|
110
111
|
type: 'datetime',
|
|
111
112
|
nullable: true,
|
|
112
|
-
beforeSave(old?: any) {
|
|
113
|
-
if (old !== undefined || !this.number) {
|
|
114
|
-
return old;
|
|
115
|
-
}
|
|
116
|
-
const date = new Date();
|
|
117
|
-
date.setMilliseconds(0);
|
|
118
|
-
return date;
|
|
119
|
-
},
|
|
120
113
|
})
|
|
121
114
|
invoicedAt: Date | null = null;
|
|
122
115
|
|
|
@@ -161,4 +154,12 @@ export class Invoice extends QueryableModel {
|
|
|
161
154
|
const invoicedBalanceItems = await InvoicedBalanceItem.select().where('invoiceId', invoices.map(i => i.id)).fetch();
|
|
162
155
|
return { invoicedBalanceItems };
|
|
163
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
|
+
}
|
|
164
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;
|