@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
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { ManyToOneRelation } from '@simonbackx/simple-database';
|
|
2
2
|
import { column, Database } from '@simonbackx/simple-database';
|
|
3
|
-
import type {I18n} from '@stamhoofd/backend-i18n';
|
|
3
|
+
import type { I18n } from '@stamhoofd/backend-i18n';
|
|
4
4
|
import { QueryableModel } from '@stamhoofd/sql';
|
|
5
5
|
import basex from 'base-x';
|
|
6
6
|
import crypto from 'crypto';
|
|
7
7
|
|
|
8
|
-
import type {Organization} from './Organization.js';
|
|
8
|
+
import type { Organization } from './Organization.js';
|
|
9
9
|
import { User } from './User.js';
|
|
10
10
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
11
|
+
import { getAppHost } from '@stamhoofd/structures';
|
|
12
|
+
import { Platform } from './Platform.js';
|
|
11
13
|
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
12
14
|
const bs58 = basex(ALPHABET);
|
|
13
15
|
|
|
@@ -110,8 +112,7 @@ export class PasswordToken extends QueryableModel {
|
|
|
110
112
|
|
|
111
113
|
if (validUntil) {
|
|
112
114
|
token.validUntil = new Date(validUntil);
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
+
} else {
|
|
115
116
|
token.validUntil = new Date();
|
|
116
117
|
token.validUntil.setTime(token.validUntil.getTime() + 3 * 3600 * 1000);
|
|
117
118
|
}
|
|
@@ -122,29 +123,28 @@ export class PasswordToken extends QueryableModel {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
static async getPasswordRecoveryUrl(user: User, organization: Organization | null, i18n: I18n, validUntil?: Date) {
|
|
125
|
-
if (user.organizationId !== null && ((user.organizationId ?? null) !== (organization?.id ?? null))) {
|
|
126
|
-
throw new Error('Unexpected mismatch in organization id for PasswordToken');
|
|
127
|
-
}
|
|
128
126
|
// Send an e-mail to say you already have an account + follow password forgot flow
|
|
129
127
|
const token = await PasswordToken.createToken(user, validUntil);
|
|
128
|
+
return token.getPasswordRecoveryUrl(organization, i18n, user);
|
|
129
|
+
}
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Build the password recovery url for this (already created) token.
|
|
133
|
+
* Pass the user to avoid an extra query when it is already loaded.
|
|
134
|
+
*/
|
|
135
|
+
async getPasswordRecoveryUrl(organization: Organization | null, i18n: I18n, user?: User) {
|
|
136
|
+
const tokenUser = user ?? await User.getByID(this.userId);
|
|
137
|
+
if (!tokenUser) {
|
|
138
|
+
throw new Error('PasswordToken without a valid user');
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
if (tokenUser.organizationId !== null && ((tokenUser.organizationId ?? null) !== (organization?.id ?? null))) {
|
|
142
|
+
throw new Error('Unexpected mismatch in organization id for PasswordToken');
|
|
143
|
+
}
|
|
143
144
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return Promise.resolve(host + '/login' + '?email=' + encodeURIComponent(user.email) + '&hasAccount=' + (user.hasAccount() ? 1 : 0));
|
|
145
|
+
const hasOrganizationPermissions = organization ? tokenUser.permissions?.forOrganization(organization, await Platform.getSharedStruct())?.isEmpty === false : false;
|
|
146
|
+
const host = 'https://' + getAppHost(hasOrganizationPermissions ? 'dashboard' : 'registration', organization, hasOrganizationPermissions, i18n);
|
|
147
|
+
return host + '/reset-password?token=' + encodeURIComponent(this.token);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
static async clearFor(userId: string) {
|
package/src/models/Payment.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { column } from '@simonbackx/simple-database';
|
|
2
|
-
import
|
|
3
|
-
import { BalanceItemDetailed, BalanceItemPaymentDetailed, BaseOrganization, PaymentCustomer, PaymentGeneral, PaymentProvider, PaymentType, Settlement, TransferSettings } from '@stamhoofd/structures';
|
|
2
|
+
import { BalanceItemDetailed, BalanceItemPaymentDetailed, BaseOrganization, PaymentCustomer, PaymentGeneral, PaymentMethod, PaymentProvider, PaymentStatus, PaymentType, Settlement, TransferSettings } from '@stamhoofd/structures';
|
|
4
3
|
import { Formatter } from '@stamhoofd/utility';
|
|
5
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
6
5
|
|
|
7
|
-
import { QueryableModel } from '@stamhoofd/sql';
|
|
6
|
+
import { QueryableModel, SQL } from '@stamhoofd/sql';
|
|
7
|
+
import { CreateMandateSettings } from '@stamhoofd/structures/checkout/CreateMandateSettings.js';
|
|
8
8
|
import type { BalanceItem } from './BalanceItem.js';
|
|
9
9
|
import type { BalanceItemPayment } from './BalanceItemPayment.js';
|
|
10
10
|
import { Organization } from './Organization.js';
|
|
@@ -52,6 +52,19 @@ export class Payment extends QueryableModel {
|
|
|
52
52
|
@column({ type: 'string', nullable: true })
|
|
53
53
|
payingUserId: string | null = null;
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* External ID of the mandate used for this payment
|
|
57
|
+
*/
|
|
58
|
+
@column({ type: 'string', nullable: true })
|
|
59
|
+
mandateId: string | null = null;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Link to related payment that was reversed.
|
|
63
|
+
* Used for refunds and chargebacks
|
|
64
|
+
*/
|
|
65
|
+
@column({ type: 'string', nullable: true })
|
|
66
|
+
reversingPaymentId: string | null = null;
|
|
67
|
+
|
|
55
68
|
/**
|
|
56
69
|
* @deprecated
|
|
57
70
|
*/
|
|
@@ -75,19 +88,30 @@ export class Payment extends QueryableModel {
|
|
|
75
88
|
@column({ type: 'string', nullable: true })
|
|
76
89
|
stripeAccountId: string | null = null;
|
|
77
90
|
|
|
91
|
+
@column({ type: 'json', decoder: CreateMandateSettings, nullable: true })
|
|
92
|
+
createMandate: CreateMandateSettings | null = null;
|
|
93
|
+
|
|
78
94
|
/**
|
|
79
95
|
* Total price
|
|
80
96
|
*/
|
|
81
97
|
@column({ type: 'integer' })
|
|
82
98
|
price: number;
|
|
83
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Total price refunded or charged back
|
|
102
|
+
*/
|
|
103
|
+
@column({ type: 'integer' })
|
|
104
|
+
refundedAmount = 0;
|
|
105
|
+
|
|
84
106
|
/**
|
|
85
107
|
* The difference between the sum of the balance item payments price and the price of the payment, caused by rounding to 1 cent.
|
|
86
108
|
* This cannot be >= 100 (= 0,01 euro) or <= -100 (=-0,01 euro)
|
|
87
109
|
*
|
|
110
|
+
* For understanding the sign of the value, regard it as an extra balance item to the payment.
|
|
111
|
+
*
|
|
88
112
|
* Just like all prices, this price is stored per ten thousand (1 = 0,0001 ). Storing smaller units is not possible because even in balance items, the price to pay cannot be smaller than 0,0001 euro
|
|
89
113
|
*
|
|
90
|
-
* E.g. total price to pay is 0,242 because of VAT, then we round this to 0,24. The roundingAmount will be -0,002 in this case.
|
|
114
|
+
* E.g. total price to pay is 0,242 because of VAT, then we round this to 0,24. The roundingAmount will be -0,002 in this case, because all the items (0,242) - 0,002 = 0,24.
|
|
91
115
|
*/
|
|
92
116
|
@column({ type: 'integer' })
|
|
93
117
|
roundingAmount = 0;
|
|
@@ -122,6 +146,20 @@ export class Payment extends QueryableModel {
|
|
|
122
146
|
@column({ type: 'integer' })
|
|
123
147
|
serviceFeeManualCharged = 0;
|
|
124
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Transfer fee - if we need to charge this for a certain provider
|
|
151
|
+
*
|
|
152
|
+
* This EXCLUDES VAT
|
|
153
|
+
*/
|
|
154
|
+
@column({ type: 'integer' })
|
|
155
|
+
transferFeeManual = 0;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Part of the transferFeeManual, that has been invoiced (added to outstanding balance)
|
|
159
|
+
*/
|
|
160
|
+
@column({ type: 'integer' })
|
|
161
|
+
transferFeeManualCharged = 0;
|
|
162
|
+
|
|
125
163
|
/**
|
|
126
164
|
* Included in the total price
|
|
127
165
|
*/
|
|
@@ -137,6 +175,12 @@ export class Payment extends QueryableModel {
|
|
|
137
175
|
@column({ type: 'string', nullable: true })
|
|
138
176
|
invoiceId: string | null = null;
|
|
139
177
|
|
|
178
|
+
/**
|
|
179
|
+
* In case the payment was initiated by an admin or not.
|
|
180
|
+
*/
|
|
181
|
+
@column({ type: 'string', nullable: true })
|
|
182
|
+
adminUserId: string | null = null;
|
|
183
|
+
|
|
140
184
|
@column({
|
|
141
185
|
type: 'datetime', beforeSave(old?: any) {
|
|
142
186
|
if (old !== undefined) {
|
|
@@ -177,11 +221,24 @@ export class Payment extends QueryableModel {
|
|
|
177
221
|
@column({ type: 'string', nullable: true })
|
|
178
222
|
ibanName: string | null = null;
|
|
179
223
|
|
|
224
|
+
get canChangeStatus() {
|
|
225
|
+
return this.price !== 0 && (this.method === PaymentMethod.Transfer || this.method === PaymentMethod.PointOfSale || this.method === PaymentMethod.Unknown);
|
|
226
|
+
}
|
|
227
|
+
|
|
180
228
|
generateDescription(organization: Organization, reference: string, replacements: { [key: string]: string } = {}) {
|
|
181
229
|
const settings = this.transferSettings ?? organization.meta.transferSettings;
|
|
182
230
|
this.transferDescription = settings.generateDescription(reference, organization.address.country, replacements);
|
|
183
231
|
}
|
|
184
232
|
|
|
233
|
+
async updateRefundedAmount() {
|
|
234
|
+
this.refundedAmount = await Payment.select()
|
|
235
|
+
.where('organizationId', this.organizationId)
|
|
236
|
+
.where('reversingPaymentId', this.id)
|
|
237
|
+
.where('status', PaymentStatus.Succeeded)
|
|
238
|
+
.sum(SQL.column('price'));
|
|
239
|
+
await this.save();
|
|
240
|
+
}
|
|
241
|
+
|
|
185
242
|
static roundPrice(price: number) {
|
|
186
243
|
return Math.round(price / 100) * 100;
|
|
187
244
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { ManyToOneRelation } from '@simonbackx/simple-database';
|
|
2
2
|
import { column } from '@simonbackx/simple-database';
|
|
3
|
-
import type { RecordAnswer} from '@stamhoofd/structures';
|
|
4
|
-
import { AppliedRegistrationDiscount, GroupPrice,
|
|
3
|
+
import type { RecordAnswer } from '@stamhoofd/structures';
|
|
4
|
+
import { AppliedRegistrationDiscount, GroupPrice, RecordAnswerMapDecoder, RegisterItemOption, Registration as RegistrationStructure, StockReservation } from '@stamhoofd/structures';
|
|
5
5
|
import { v4 as uuidv4 } from 'uuid';
|
|
6
6
|
|
|
7
7
|
import { ArrayDecoder, MapDecoder, StringDecoder } from '@simonbackx/simple-encoding';
|
|
8
8
|
import { QueryableModel } from '@stamhoofd/sql';
|
|
9
|
-
import type {Group} from './Group.js';
|
|
9
|
+
import type { Group } from './Group.js';
|
|
10
10
|
|
|
11
11
|
export class Registration extends QueryableModel {
|
|
12
12
|
static table = 'registrations';
|
|
@@ -39,7 +39,7 @@ export class Registration extends QueryableModel {
|
|
|
39
39
|
@column({ type: 'json', decoder: new ArrayDecoder(RegisterItemOption) })
|
|
40
40
|
options: RegisterItemOption[] = [];
|
|
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
|
/**
|
|
@@ -117,6 +117,7 @@ export class Registration extends QueryableModel {
|
|
|
117
117
|
waitingList = false;
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
|
+
* @deprecated - use RegistrationInvitation instead
|
|
120
121
|
* When a registration is on the waiting list or is invite only, set this to true to allow the user to
|
|
121
122
|
* register normally.
|
|
122
123
|
*/
|
|
@@ -165,6 +166,6 @@ export class Registration extends QueryableModel {
|
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
shouldIncludeStock() {
|
|
168
|
-
return (this.registeredAt !== null && this.deactivatedAt === null) ||
|
|
169
|
+
return (this.registeredAt !== null && this.deactivatedAt === null) || (this.reservedUntil && this.reservedUntil > new Date());
|
|
169
170
|
}
|
|
170
171
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { column } from '@simonbackx/simple-database';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
|
|
4
|
+
import { QueryableModel } from '@stamhoofd/sql';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Invitation to register for a group. If an invitation exists the member can always register even if he does not meet the requirements of the group.
|
|
8
|
+
* Used for allowing members who are on a waiting list to register for a group.
|
|
9
|
+
*/
|
|
10
|
+
export class RegistrationInvitation extends QueryableModel {
|
|
11
|
+
static table = 'registration_invitations';
|
|
12
|
+
|
|
13
|
+
@column({
|
|
14
|
+
primary: true, type: 'string', beforeSave(value) {
|
|
15
|
+
return value ?? uuidv4();
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
id: string;
|
|
19
|
+
|
|
20
|
+
@column({ type: 'string' })
|
|
21
|
+
groupId: string;
|
|
22
|
+
|
|
23
|
+
@column({ type: 'string' })
|
|
24
|
+
memberId: string;
|
|
25
|
+
|
|
26
|
+
@column({ type: 'string' })
|
|
27
|
+
organizationId: string;
|
|
28
|
+
|
|
29
|
+
@column({
|
|
30
|
+
type: 'datetime', beforeSave(old?: any) {
|
|
31
|
+
if (old !== undefined) {
|
|
32
|
+
return old;
|
|
33
|
+
}
|
|
34
|
+
const date = new Date();
|
|
35
|
+
date.setMilliseconds(0);
|
|
36
|
+
return date;
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
createdAt: Date;
|
|
40
|
+
}
|
package/src/models/STCredit.ts
CHANGED
|
@@ -49,4 +49,36 @@ export class STCredit extends QueryableModel {
|
|
|
49
49
|
skipUpdate: true,
|
|
50
50
|
})
|
|
51
51
|
updatedAt: Date;
|
|
52
|
+
|
|
53
|
+
static async getForOrganization(organizationId: string) {
|
|
54
|
+
return await STCredit.where({ organizationId }, {
|
|
55
|
+
sort: [{
|
|
56
|
+
column: 'createdAt',
|
|
57
|
+
direction: 'DESC',
|
|
58
|
+
}],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static async getBalance(organizationId: string) {
|
|
63
|
+
const now = new Date();
|
|
64
|
+
const credits = await this.getForOrganization(organizationId);
|
|
65
|
+
credits.reverse();
|
|
66
|
+
let balance = 0;
|
|
67
|
+
|
|
68
|
+
for (const credit of credits) {
|
|
69
|
+
if (credit.expireAt !== null && credit.expireAt <= now) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
// TODO: we can expire credits here
|
|
73
|
+
balance += credit.change;
|
|
74
|
+
if (balance < 0) {
|
|
75
|
+
// This is needed to make deleting credit and expiring credit work.
|
|
76
|
+
// At no point in time, the credits can get negative.
|
|
77
|
+
// E.g. Getting credits, using them, and later expiring 'getting the credits' won't have impact on future credits
|
|
78
|
+
balance = 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { balance: balance };
|
|
83
|
+
}
|
|
52
84
|
}
|
package/src/models/STInvoice.ts
CHANGED
|
@@ -20,12 +20,14 @@ export class STInvoice extends QueryableModel {
|
|
|
20
20
|
id!: string;
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
+
* @deprecated
|
|
23
24
|
* Organization that made the invoice. Can be null if the organization was deleted and for the migration from V1 -> V2
|
|
24
25
|
*/
|
|
25
26
|
@column({ type: 'string', nullable: true })
|
|
26
|
-
organizationId: string | null;
|
|
27
|
+
protected organizationId: string | null;
|
|
27
28
|
|
|
28
29
|
/**
|
|
30
|
+
*
|
|
29
31
|
* Organization that is associated with this invoice (can be null if deleted or unknown)
|
|
30
32
|
*/
|
|
31
33
|
@column({ type: 'string', nullable: true })
|
|
@@ -56,6 +58,12 @@ export class STInvoice extends QueryableModel {
|
|
|
56
58
|
@column({ type: 'datetime', nullable: true })
|
|
57
59
|
paidAt: Date | null = null;
|
|
58
60
|
|
|
61
|
+
/**
|
|
62
|
+
* If a invoice was refunded because of a cancellation, we store the negative invoice id here
|
|
63
|
+
*/
|
|
64
|
+
@column({ type: 'string', nullable: true })
|
|
65
|
+
negativeInvoiceId: string | null = null;
|
|
66
|
+
|
|
59
67
|
@column({
|
|
60
68
|
type: 'datetime', beforeSave(old?: any) {
|
|
61
69
|
if (old !== undefined) {
|
|
@@ -117,14 +125,12 @@ export class STInvoice extends QueryableModel {
|
|
|
117
125
|
cardLabel: ('cardLabel' in details ? details.cardLabel : null),
|
|
118
126
|
}),
|
|
119
127
|
}));
|
|
120
|
-
}
|
|
121
|
-
catch (e) {
|
|
128
|
+
} catch (e) {
|
|
122
129
|
console.error(e);
|
|
123
130
|
}
|
|
124
131
|
}
|
|
125
132
|
}
|
|
126
|
-
}
|
|
127
|
-
catch (e) {
|
|
133
|
+
} catch (e) {
|
|
128
134
|
console.error(e);
|
|
129
135
|
}
|
|
130
136
|
return mandates;
|
package/src/models/STPackage.ts
CHANGED
|
@@ -65,6 +65,19 @@ export class STPackage extends QueryableModel {
|
|
|
65
65
|
@column({ type: 'datetime', nullable: true })
|
|
66
66
|
lastEmailAt: Date | null = null;
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Helper to handle edge cases where validUntil is null but removeAt is set
|
|
70
|
+
*/
|
|
71
|
+
get endDate() {
|
|
72
|
+
if (!this.removeAt) {
|
|
73
|
+
return this.validUntil;
|
|
74
|
+
}
|
|
75
|
+
if (!this.validUntil) {
|
|
76
|
+
return this.removeAt;
|
|
77
|
+
}
|
|
78
|
+
return new Date(Math.min(this.validUntil.getTime(), this.removeAt.getTime()));
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
async activate() {
|
|
69
82
|
if (this.validAt !== null) {
|
|
70
83
|
return;
|
|
@@ -150,8 +163,7 @@ export class STPackage extends QueryableModel {
|
|
|
150
163
|
pack.meta.pricingType = STPricingType.Fixed;
|
|
151
164
|
pack.validUntil = null;
|
|
152
165
|
pack.removeAt = null;
|
|
153
|
-
}
|
|
154
|
-
else if (pack.meta.type === STPackageType.Members) {
|
|
166
|
+
} else if (pack.meta.type === STPackageType.Members) {
|
|
155
167
|
pack.meta.serviceFeeFixed = 0;
|
|
156
168
|
pack.meta.serviceFeePercentage = 0;
|
|
157
169
|
pack.meta.serviceFeeMinimum = 0;
|
|
@@ -165,8 +177,6 @@ export class STPackage extends QueryableModel {
|
|
|
165
177
|
}
|
|
166
178
|
|
|
167
179
|
createStatus(): STPackageStatus {
|
|
168
|
-
// TODO: if payment failed: temporary set valid until to 2 weeks after last/first failed payment
|
|
169
|
-
|
|
170
180
|
return STPackageStatus.create({
|
|
171
181
|
startDate: this.meta.startDate,
|
|
172
182
|
validUntil: this.validUntil,
|
|
@@ -205,20 +215,16 @@ export class STPackage extends QueryableModel {
|
|
|
205
215
|
if (this.meta.type === STPackageType.Members) {
|
|
206
216
|
type = EmailTemplateType.MembersExpirationReminder;
|
|
207
217
|
allowDays = 32;
|
|
208
|
-
}
|
|
209
|
-
else if (this.meta.type === STPackageType.Webshops) {
|
|
218
|
+
} else if (this.meta.type === STPackageType.Webshops) {
|
|
210
219
|
type = EmailTemplateType.WebshopsExpirationReminder;
|
|
211
220
|
allowDays = 32;
|
|
212
|
-
}
|
|
213
|
-
else if (this.meta.type === STPackageType.SingleWebshop) {
|
|
221
|
+
} else if (this.meta.type === STPackageType.SingleWebshop) {
|
|
214
222
|
type = EmailTemplateType.SingleWebshopExpirationReminder;
|
|
215
223
|
allowDays = 7;
|
|
216
|
-
}
|
|
217
|
-
else if (this.meta.type === STPackageType.TrialMembers) {
|
|
224
|
+
} else if (this.meta.type === STPackageType.TrialMembers) {
|
|
218
225
|
type = EmailTemplateType.TrialMembersExpirationReminder;
|
|
219
226
|
allowDays = 3;
|
|
220
|
-
}
|
|
221
|
-
else if (this.meta.type === STPackageType.TrialWebshops) {
|
|
227
|
+
} else if (this.meta.type === STPackageType.TrialWebshops) {
|
|
222
228
|
type = EmailTemplateType.TrialWebshopsExpirationReminder;
|
|
223
229
|
allowDays = 3;
|
|
224
230
|
}
|
|
@@ -237,8 +243,7 @@ export class STPackage extends QueryableModel {
|
|
|
237
243
|
});
|
|
238
244
|
}
|
|
239
245
|
this.lastEmailAt = new Date();
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
246
|
+
} else {
|
|
242
247
|
console.log('Skip sending expiration email for ' + this.id + ' (no type)');
|
|
243
248
|
}
|
|
244
249
|
|
|
@@ -25,6 +25,13 @@ export class UsedRegisterCode extends QueryableModel {
|
|
|
25
25
|
* Set if this has been rewarded
|
|
26
26
|
*/
|
|
27
27
|
@column({ type: 'string', nullable: true })
|
|
28
|
+
balanceItemId: string | null = null;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated Migrated to balanceItemId
|
|
32
|
+
* Set if this has been rewarded
|
|
33
|
+
*/
|
|
34
|
+
@column({ type: 'string', nullable: true })
|
|
28
35
|
creditId: string | null = null;
|
|
29
36
|
|
|
30
37
|
@column({
|
|
@@ -48,4 +55,31 @@ export class UsedRegisterCode extends QueryableModel {
|
|
|
48
55
|
skipUpdate: true,
|
|
49
56
|
})
|
|
50
57
|
updatedAt: Date;
|
|
58
|
+
|
|
59
|
+
static async getFor(organizationId: string): Promise<UsedRegisterCode | undefined> {
|
|
60
|
+
const code = await this.where({ organizationId }, { limit: 1 });
|
|
61
|
+
return code[0] ?? undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static async getAll(code: string) {
|
|
65
|
+
const used = await UsedRegisterCode.where({
|
|
66
|
+
code,
|
|
67
|
+
});
|
|
68
|
+
return used;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static async getUsed(code: string) {
|
|
72
|
+
const used = await UsedRegisterCode.select()
|
|
73
|
+
.where('code', code)
|
|
74
|
+
.andWhere('balanceItemId', '!=', null)
|
|
75
|
+
.fetch();
|
|
76
|
+
return used;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static async getUsedCount(code: string) {
|
|
80
|
+
return await UsedRegisterCode.select()
|
|
81
|
+
.where('code', code)
|
|
82
|
+
.andWhere('balanceItemId', '!=', null)
|
|
83
|
+
.count();
|
|
84
|
+
}
|
|
51
85
|
}
|
package/src/models/User.ts
CHANGED
|
@@ -141,20 +141,19 @@ export class User extends QueryableModel {
|
|
|
141
141
|
return global;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
static async getAdmins(organizationId: string, options?: { verified?: boolean }) {
|
|
144
|
+
static async getAdmins(organizationId: string | string[], options?: { verified?: boolean }) {
|
|
145
145
|
if (STAMHOOFD.userMode === 'platform') {
|
|
146
146
|
// Custom implementation
|
|
147
147
|
const q = User.select()
|
|
148
148
|
.where('organizationId', null)
|
|
149
149
|
.where('permissions', '!=', null)
|
|
150
150
|
.where(
|
|
151
|
-
SQL.
|
|
152
|
-
SQL.jsonExtract(SQL.column('permissions'), '$.value.organizationPermissions'),
|
|
153
|
-
|
|
154
|
-
true,
|
|
151
|
+
SQL.jsonOverlaps(
|
|
152
|
+
SQL.jsonKeys(SQL.jsonExtract(SQL.column('permissions'), '$.value.organizationPermissions')),
|
|
153
|
+
SQL.cast(SQL.scalar(JSON.stringify(Array.isArray(organizationId) ? organizationId : [organizationId])), 'JSON'),
|
|
155
154
|
),
|
|
156
|
-
'
|
|
157
|
-
|
|
155
|
+
'=',
|
|
156
|
+
1,
|
|
158
157
|
)
|
|
159
158
|
;
|
|
160
159
|
|
package/src/models/_relations.ts
CHANGED
|
@@ -22,6 +22,10 @@ import { Group } from './Group.js';
|
|
|
22
22
|
import { Registration } from './Registration.js';
|
|
23
23
|
import { Token } from './Token.js';
|
|
24
24
|
import { PasswordToken } from './PasswordToken.js';
|
|
25
|
+
import { City } from './addresses/City.js';
|
|
26
|
+
import { Province } from './addresses/Province.js';
|
|
27
|
+
import { PostalCode } from './addresses/PostalCode.js';
|
|
28
|
+
import { Street } from './addresses/Street.js';
|
|
25
29
|
|
|
26
30
|
if (User === undefined) {
|
|
27
31
|
throw new Error('Unexpected missing User');
|
|
@@ -131,3 +135,28 @@ if (!PasswordToken.relations) {
|
|
|
131
135
|
PasswordToken.user = new ManyToOneRelation(User, 'user');
|
|
132
136
|
PasswordToken.user.foreignKey = 'userId';
|
|
133
137
|
PasswordToken.relations.push(PasswordToken.user);
|
|
138
|
+
|
|
139
|
+
if (!City.relations) {
|
|
140
|
+
City.relations = [];
|
|
141
|
+
}
|
|
142
|
+
City.parentCity = new ManyToOneRelation(City, 'parentCity');
|
|
143
|
+
City.parentCity.foreignKey = 'parentCityId';
|
|
144
|
+
City.relations.push(City.parentCity);
|
|
145
|
+
|
|
146
|
+
City.province = new ManyToOneRelation(Province, 'province');
|
|
147
|
+
City.province.foreignKey = 'provinceId';
|
|
148
|
+
City.relations.push(City.province);
|
|
149
|
+
|
|
150
|
+
if (!PostalCode.relations) {
|
|
151
|
+
PostalCode.relations = [];
|
|
152
|
+
}
|
|
153
|
+
PostalCode.city = new ManyToOneRelation(City, 'city');
|
|
154
|
+
PostalCode.city.foreignKey = 'cityId';
|
|
155
|
+
PostalCode.relations.push(PostalCode.city);
|
|
156
|
+
|
|
157
|
+
if (!Street.relations) {
|
|
158
|
+
Street.relations = [];
|
|
159
|
+
}
|
|
160
|
+
Street.city = new ManyToOneRelation(City, 'city');
|
|
161
|
+
Street.city.foreignKey = 'cityId';
|
|
162
|
+
Street.relations.push(Street.city);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { column
|
|
1
|
+
import { column } from '@simonbackx/simple-database';
|
|
2
|
+
import type { ManyToOneRelation } from '@simonbackx/simple-database';
|
|
2
3
|
import type { Country } from '@stamhoofd/types/Country';
|
|
3
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
4
5
|
|
|
5
6
|
import { QueryableModel } from '@stamhoofd/sql';
|
|
6
|
-
import { Province } from './Province.js';
|
|
7
|
+
import type { Province } from './Province.js';
|
|
7
8
|
|
|
8
9
|
export class City extends QueryableModel {
|
|
9
10
|
static table = 'cities';
|
|
@@ -18,15 +19,16 @@ export class City extends QueryableModel {
|
|
|
18
19
|
@column({ type: 'string' })
|
|
19
20
|
name: string;
|
|
20
21
|
|
|
21
|
-
@column({ type: 'string'
|
|
22
|
+
@column({ type: 'string' })
|
|
22
23
|
provinceId: string;
|
|
23
24
|
|
|
24
|
-
@column({ type: 'string',
|
|
25
|
+
@column({ type: 'string', nullable: true })
|
|
25
26
|
parentCityId: string | null = null;
|
|
26
27
|
|
|
27
28
|
@column({ type: 'string' })
|
|
28
29
|
country: Country;
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
static
|
|
31
|
+
// Relations are wired up in _relations.ts (after the classes are defined) to avoid circular references.
|
|
32
|
+
static parentCity: ManyToOneRelation<'parentCity', City>;
|
|
33
|
+
static province: ManyToOneRelation<'province', Province>;
|
|
32
34
|
}
|
|
@@ -3,6 +3,7 @@ import { Country } from '@stamhoofd/types/Country';
|
|
|
3
3
|
import { City } from './City.js';
|
|
4
4
|
import { PostalCode } from './PostalCode.js';
|
|
5
5
|
import { Province } from './Province.js';
|
|
6
|
+
import '../_relations.js';
|
|
6
7
|
|
|
7
8
|
describe('Model.PostalCode', () => {
|
|
8
9
|
let oostVlaanderen!: Province;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { column, Database
|
|
1
|
+
import { column, Database } from '@simonbackx/simple-database';
|
|
2
|
+
import type { ManyToOneRelation } from '@simonbackx/simple-database';
|
|
2
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
3
4
|
import { QueryableModel } from '@stamhoofd/sql';
|
|
4
5
|
import { Country } from '@stamhoofd/types/Country';
|
|
@@ -20,13 +21,14 @@ export class PostalCode extends QueryableModel {
|
|
|
20
21
|
@column({ type: 'string' })
|
|
21
22
|
postalCode: string;
|
|
22
23
|
|
|
23
|
-
@column({ type: 'string'
|
|
24
|
+
@column({ type: 'string' })
|
|
24
25
|
cityId: string;
|
|
25
26
|
|
|
26
27
|
@column({ type: 'string' })
|
|
27
28
|
country: Country;
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
// Relation is wired up in _relations.ts (after the classes are defined) to avoid circular references.
|
|
31
|
+
static city: ManyToOneRelation<'city', City>;
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* Search for a given city via it's postal code and country, collecting information about the city (id), parentCity (id) and province (id)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { column
|
|
1
|
+
import { column } from '@simonbackx/simple-database';
|
|
2
|
+
import type { ManyToOneRelation } from '@simonbackx/simple-database';
|
|
2
3
|
import { QueryableModel } from '@stamhoofd/sql';
|
|
3
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
4
5
|
|
|
5
|
-
import { City } from './City.js';
|
|
6
|
+
import type { City } from './City.js';
|
|
6
7
|
|
|
7
8
|
export class Street extends QueryableModel {
|
|
8
9
|
static table = 'streets';
|
|
@@ -17,8 +18,9 @@ export class Street extends QueryableModel {
|
|
|
17
18
|
@column({ type: 'string' })
|
|
18
19
|
name: string;
|
|
19
20
|
|
|
20
|
-
@column({ type: 'string'
|
|
21
|
+
@column({ type: 'string' })
|
|
21
22
|
cityId: string;
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
// Relation is wired up in _relations.ts (after the classes are defined) to avoid circular references.
|
|
25
|
+
static city: ManyToOneRelation<'city', City>;
|
|
24
26
|
}
|
package/src/models/index.ts
CHANGED