@stamhoofd/models 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/assets/Metropolis-Black.woff2 +0 -0
- package/dist/src/assets/Metropolis-BlackItalic.woff2 +0 -0
- package/dist/src/assets/Metropolis-Bold.woff2 +0 -0
- package/dist/src/assets/Metropolis-BoldItalic.woff2 +0 -0
- package/dist/src/assets/Metropolis-ExtraBold.woff2 +0 -0
- package/dist/src/assets/Metropolis-ExtraBoldItalic.woff2 +0 -0
- package/dist/src/assets/Metropolis-ExtraLight.woff2 +0 -0
- package/dist/src/assets/Metropolis-ExtraLightItalic.woff2 +0 -0
- package/dist/src/assets/Metropolis-Light.woff2 +0 -0
- package/dist/src/assets/Metropolis-LightItalic.woff2 +0 -0
- package/dist/src/assets/Metropolis-Medium.woff2 +0 -0
- package/dist/src/assets/Metropolis-MediumItalic.woff2 +0 -0
- package/dist/src/assets/Metropolis-Regular.woff2 +0 -0
- package/dist/src/assets/Metropolis-RegularItalic.woff2 +0 -0
- package/dist/src/assets/Metropolis-SemiBold.woff2 +0 -0
- package/dist/src/assets/Metropolis-SemiBoldItalic.woff2 +0 -0
- package/dist/src/assets/Metropolis-Thin.woff2 +0 -0
- package/dist/src/assets/Metropolis-ThinItalic.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-Black.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-BlackItalic.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-Bold.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-BoldItalic.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-ExtraBold.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-ExtraBoldItalic.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-ExtraLight.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-ExtraLightItalic.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-Light.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-LightItalic.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-Medium.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-MediumItalic.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-Regular.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-RegularItalic.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-SemiBold.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-SemiBoldItalic.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-Thin.woff2 +0 -0
- package/dist/src/assets/assets/Metropolis-ThinItalic.woff2 +0 -0
- package/dist/src/assets/assets/logo.png +0 -0
- package/dist/src/assets/logo.png +0 -0
- package/dist/src/factories/AddressFactory.d.ts +10 -0
- package/dist/src/factories/AddressFactory.d.ts.map +1 -0
- package/dist/src/factories/AddressFactory.js +42 -0
- package/dist/src/factories/AddressFactory.js.map +1 -0
- package/dist/src/factories/EmergencyContactFactory.d.ts +9 -0
- package/dist/src/factories/EmergencyContactFactory.d.ts.map +1 -0
- package/dist/src/factories/EmergencyContactFactory.js +42 -0
- package/dist/src/factories/EmergencyContactFactory.js.map +1 -0
- package/dist/src/factories/GroupFactory.d.ts +19 -0
- package/dist/src/factories/GroupFactory.d.ts.map +1 -0
- package/dist/src/factories/GroupFactory.js +52 -0
- package/dist/src/factories/GroupFactory.js.map +1 -0
- package/dist/src/factories/MemberFactory.d.ts +16 -0
- package/dist/src/factories/MemberFactory.d.ts.map +1 -0
- package/dist/src/factories/MemberFactory.js +98 -0
- package/dist/src/factories/MemberFactory.js.map +1 -0
- package/dist/src/factories/OrganizationFactory.d.ts +16 -0
- package/dist/src/factories/OrganizationFactory.d.ts.map +1 -0
- package/dist/src/factories/OrganizationFactory.js +40 -0
- package/dist/src/factories/OrganizationFactory.js.map +1 -0
- package/dist/src/factories/ParentFactory.d.ts +10 -0
- package/dist/src/factories/ParentFactory.d.ts.map +1 -0
- package/dist/src/factories/ParentFactory.js +44 -0
- package/dist/src/factories/ParentFactory.js.map +1 -0
- package/dist/src/factories/RecordFactory.d.ts +10 -0
- package/dist/src/factories/RecordFactory.d.ts.map +1 -0
- package/dist/src/factories/RecordFactory.js +14 -0
- package/dist/src/factories/RecordFactory.js.map +1 -0
- package/dist/src/factories/RegisterCodeFactory.d.ts +18 -0
- package/dist/src/factories/RegisterCodeFactory.d.ts.map +1 -0
- package/dist/src/factories/RegisterCodeFactory.js +19 -0
- package/dist/src/factories/RegisterCodeFactory.js.map +1 -0
- package/dist/src/factories/RegistrationFactory.d.ts +14 -0
- package/dist/src/factories/RegistrationFactory.d.ts.map +1 -0
- package/dist/src/factories/RegistrationFactory.js +26 -0
- package/dist/src/factories/RegistrationFactory.js.map +1 -0
- package/dist/src/factories/UserFactory.d.ts +21 -0
- package/dist/src/factories/UserFactory.d.ts.map +1 -0
- package/dist/src/factories/UserFactory.js +41 -0
- package/dist/src/factories/UserFactory.js.map +1 -0
- package/dist/src/factories/WebshopFactory.d.ts +16 -0
- package/dist/src/factories/WebshopFactory.d.ts.map +1 -0
- package/dist/src/factories/WebshopFactory.js +35 -0
- package/dist/src/factories/WebshopFactory.js.map +1 -0
- package/dist/src/helpers/DNSValidator.d.ts +6 -0
- package/dist/src/helpers/DNSValidator.d.ts.map +1 -0
- package/dist/src/helpers/DNSValidator.js +144 -0
- package/dist/src/helpers/DNSValidator.js.map +1 -0
- package/dist/src/helpers/EmailBuilder.d.ts +22 -0
- package/dist/src/helpers/EmailBuilder.d.ts.map +1 -0
- package/dist/src/helpers/EmailBuilder.js +100 -0
- package/dist/src/helpers/EmailBuilder.js.map +1 -0
- package/dist/src/helpers/GroupBuilder.d.ts +9 -0
- package/dist/src/helpers/GroupBuilder.d.ts.map +1 -0
- package/dist/src/helpers/GroupBuilder.js +382 -0
- package/dist/src/helpers/GroupBuilder.js.map +1 -0
- package/dist/src/helpers/Handlebars.d.ts +2 -0
- package/dist/src/helpers/Handlebars.d.ts.map +1 -0
- package/dist/src/helpers/Handlebars.js +192 -0
- package/dist/src/helpers/Handlebars.js.map +1 -0
- package/dist/src/helpers/InvoiceBuilder.d.ts +29 -0
- package/dist/src/helpers/InvoiceBuilder.d.ts.map +1 -0
- package/dist/src/helpers/InvoiceBuilder.js +406 -0
- package/dist/src/helpers/InvoiceBuilder.js.map +1 -0
- package/dist/src/helpers/InvoiceBuilder.test.d.ts +2 -0
- package/dist/src/helpers/InvoiceBuilder.test.d.ts.map +1 -0
- package/dist/src/helpers/InvoiceBuilder.test.js +52 -0
- package/dist/src/helpers/InvoiceBuilder.test.js.map +1 -0
- package/dist/src/helpers/RateLimiter.d.ts +27 -0
- package/dist/src/helpers/RateLimiter.d.ts.map +1 -0
- package/dist/src/helpers/RateLimiter.js +57 -0
- package/dist/src/helpers/RateLimiter.js.map +1 -0
- package/dist/src/helpers/WebshopCounter.d.ts +6 -0
- package/dist/src/helpers/WebshopCounter.d.ts.map +1 -0
- package/dist/src/helpers/WebshopCounter.js +36 -0
- package/dist/src/helpers/WebshopCounter.js.map +1 -0
- package/dist/src/helpers/WebshopCounter.test.d.ts +2 -0
- package/dist/src/helpers/WebshopCounter.test.d.ts.map +1 -0
- package/dist/src/helpers/WebshopCounter.test.js +17 -0
- package/dist/src/helpers/WebshopCounter.test.js.map +1 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +23 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/migrations/1593773929-create-initial-tables.sql +634 -0
- package/dist/src/migrations/1605261999-gemeenten-tmp.sql +2820 -0
- package/dist/src/migrations/1605262045-import-postcodes.d.ts +16 -0
- package/dist/src/migrations/1605262045-import-postcodes.d.ts.map +1 -0
- package/dist/src/migrations/1605262045-import-postcodes.js +116 -0
- package/dist/src/migrations/1605262045-import-postcodes.js.map +1 -0
- package/dist/src/migrations/1605262046-import-postcodes-nl.d.ts +4 -0
- package/dist/src/migrations/1605262046-import-postcodes-nl.d.ts.map +1 -0
- package/dist/src/migrations/1605262046-import-postcodes-nl.js +83 -0
- package/dist/src/migrations/1605262046-import-postcodes-nl.js.map +1 -0
- package/dist/src/migrations/1605279038-drop-gemeenten-tmp.sql +1 -0
- package/dist/src/migrations/1648392491-default-templates.sql +9 -0
- package/dist/src/migrations/1651245707-default-templates-reminders.sql +6 -0
- package/dist/src/migrations/1708607340-tickets-deleted-at.sql +1 -0
- package/dist/src/migrations/1710459176-register-code-invoices.sql +3 -0
- package/dist/src/migrations/1712158247-discount-codes.sql +17 -0
- package/dist/src/migrations/1713178665-drop-invites.sql +1 -0
- package/dist/src/migrations/1713178666-drop-keychain.sql +1 -0
- package/dist/src/migrations/1713178667-drop-challenges.sql +1 -0
- package/dist/src/migrations/1713178668-drop-user-keys.sql +7 -0
- package/dist/src/migrations/1713178669-drop-organization-key.sql +2 -0
- package/dist/src/migrations/data/postcodes/nl/Drenthe +291 -0
- package/dist/src/migrations/data/postcodes/nl/Flevoland +107 -0
- package/dist/src/migrations/data/postcodes/nl/Friesland +518 -0
- package/dist/src/migrations/data/postcodes/nl/Gelderland +601 -0
- package/dist/src/migrations/data/postcodes/nl/Groningen +279 -0
- package/dist/src/migrations/data/postcodes/nl/Limburg +324 -0
- package/dist/src/migrations/data/postcodes/nl/Noord-Brabant +620 -0
- package/dist/src/migrations/data/postcodes/nl/Noord-Holland +566 -0
- package/dist/src/migrations/data/postcodes/nl/Overrijsel +344 -0
- package/dist/src/migrations/data/postcodes/nl/Utrecht +278 -0
- package/dist/src/migrations/data/postcodes/nl/Zeeland +179 -0
- package/dist/src/migrations/data/postcodes/nl/Zuid-Holland +662 -0
- package/dist/src/models/BalanceItem.d.ts +57 -0
- package/dist/src/models/BalanceItem.d.ts.map +1 -0
- package/dist/src/models/BalanceItem.js +346 -0
- package/dist/src/models/BalanceItem.js.map +1 -0
- package/dist/src/models/BalanceItemPayment.d.ts +31 -0
- package/dist/src/models/BalanceItemPayment.d.ts.map +1 -0
- package/dist/src/models/BalanceItemPayment.js +101 -0
- package/dist/src/models/BalanceItemPayment.js.map +1 -0
- package/dist/src/models/BuckarooPayment.d.ts +8 -0
- package/dist/src/models/BuckarooPayment.d.ts.map +1 -0
- package/dist/src/models/BuckarooPayment.js +24 -0
- package/dist/src/models/BuckarooPayment.js.map +1 -0
- package/dist/src/models/Document.d.ts +44 -0
- package/dist/src/models/Document.d.ts.map +1 -0
- package/dist/src/models/Document.js +194 -0
- package/dist/src/models/Document.js.map +1 -0
- package/dist/src/models/DocumentTemplate.d.ts +45 -0
- package/dist/src/models/DocumentTemplate.d.ts.map +1 -0
- package/dist/src/models/DocumentTemplate.js +533 -0
- package/dist/src/models/DocumentTemplate.js.map +1 -0
- package/dist/src/models/EmailTemplate.d.ts +22 -0
- package/dist/src/models/EmailTemplate.d.ts.map +1 -0
- package/dist/src/models/EmailTemplate.js +70 -0
- package/dist/src/models/EmailTemplate.js.map +1 -0
- package/dist/src/models/EmailVerificationCode.d.ts +60 -0
- package/dist/src/models/EmailVerificationCode.d.ts.map +1 -0
- package/dist/src/models/EmailVerificationCode.js +307 -0
- package/dist/src/models/EmailVerificationCode.js.map +1 -0
- package/dist/src/models/Group.d.ts +36 -0
- package/dist/src/models/Group.d.ts.map +1 -0
- package/dist/src/models/Group.js +231 -0
- package/dist/src/models/Group.js.map +1 -0
- package/dist/src/models/Image.d.ts +12 -0
- package/dist/src/models/Image.d.ts.map +1 -0
- package/dist/src/models/Image.js +137 -0
- package/dist/src/models/Image.js.map +1 -0
- package/dist/src/models/Member.d.ts +66 -0
- package/dist/src/models/Member.d.ts.map +1 -0
- package/dist/src/models/Member.js +309 -0
- package/dist/src/models/Member.js.map +1 -0
- package/dist/src/models/MemberResponsibilityRecord.d.ts +11 -0
- package/dist/src/models/MemberResponsibilityRecord.d.ts.map +1 -0
- package/dist/src/models/MemberResponsibilityRecord.js +47 -0
- package/dist/src/models/MemberResponsibilityRecord.js.map +1 -0
- package/dist/src/models/MolliePayment.d.ts +8 -0
- package/dist/src/models/MolliePayment.d.ts.map +1 -0
- package/dist/src/models/MolliePayment.js +24 -0
- package/dist/src/models/MolliePayment.js.map +1 -0
- package/dist/src/models/MollieToken.d.ts +45 -0
- package/dist/src/models/MollieToken.d.ts.map +1 -0
- package/dist/src/models/MollieToken.js +333 -0
- package/dist/src/models/MollieToken.js.map +1 -0
- package/dist/src/models/OneTimeToken.d.ts +38 -0
- package/dist/src/models/OneTimeToken.d.ts.map +1 -0
- package/dist/src/models/OneTimeToken.js +126 -0
- package/dist/src/models/OneTimeToken.js.map +1 -0
- package/dist/src/models/Order.d.ts +99 -0
- package/dist/src/models/Order.d.ts.map +1 -0
- package/dist/src/models/Order.js +912 -0
- package/dist/src/models/Order.js.map +1 -0
- package/dist/src/models/Organization.d.ts +119 -0
- package/dist/src/models/Organization.d.ts.map +1 -0
- package/dist/src/models/Organization.js +900 -0
- package/dist/src/models/Organization.js.map +1 -0
- package/dist/src/models/OrganizationRegistrationPeriod.d.ts +14 -0
- package/dist/src/models/OrganizationRegistrationPeriod.d.ts.map +1 -0
- package/dist/src/models/OrganizationRegistrationPeriod.js +62 -0
- package/dist/src/models/OrganizationRegistrationPeriod.js.map +1 -0
- package/dist/src/models/PasswordToken.d.ts +29 -0
- package/dist/src/models/PasswordToken.d.ts.map +1 -0
- package/dist/src/models/PasswordToken.js +118 -0
- package/dist/src/models/PasswordToken.js.map +1 -0
- package/dist/src/models/PayconiqPayment.d.ts +18 -0
- package/dist/src/models/PayconiqPayment.d.ts.map +1 -0
- package/dist/src/models/PayconiqPayment.js +216 -0
- package/dist/src/models/PayconiqPayment.js.map +1 -0
- package/dist/src/models/Payment.d.ts +62 -0
- package/dist/src/models/Payment.d.ts.map +1 -0
- package/dist/src/models/Payment.js +199 -0
- package/dist/src/models/Payment.js.map +1 -0
- package/dist/src/models/Platform.d.ts +15 -0
- package/dist/src/models/Platform.d.ts.map +1 -0
- package/dist/src/models/Platform.js +77 -0
- package/dist/src/models/Platform.js.map +1 -0
- package/dist/src/models/RegisterCode.d.ts +27 -0
- package/dist/src/models/RegisterCode.d.ts.map +1 -0
- package/dist/src/models/RegisterCode.js +162 -0
- package/dist/src/models/RegisterCode.js.map +1 -0
- package/dist/src/models/Registration.d.ts +47 -0
- package/dist/src/models/Registration.d.ts.map +1 -0
- package/dist/src/models/Registration.js +369 -0
- package/dist/src/models/Registration.js.map +1 -0
- package/dist/src/models/RegistrationPeriod.d.ts +15 -0
- package/dist/src/models/RegistrationPeriod.d.ts.map +1 -0
- package/dist/src/models/RegistrationPeriod.js +64 -0
- package/dist/src/models/RegistrationPeriod.js.map +1 -0
- package/dist/src/models/STCredit.d.ts +20 -0
- package/dist/src/models/STCredit.d.ts.map +1 -0
- package/dist/src/models/STCredit.js +129 -0
- package/dist/src/models/STCredit.js.map +1 -0
- package/dist/src/models/STInvoice.d.ts +51 -0
- package/dist/src/models/STInvoice.d.ts.map +1 -0
- package/dist/src/models/STInvoice.js +453 -0
- package/dist/src/models/STInvoice.js.map +1 -0
- package/dist/src/models/STPackage.d.ts +36 -0
- package/dist/src/models/STPackage.d.ts.map +1 -0
- package/dist/src/models/STPackage.js +300 -0
- package/dist/src/models/STPackage.js.map +1 -0
- package/dist/src/models/STPendingInvoice.d.ts +45 -0
- package/dist/src/models/STPendingInvoice.d.ts.map +1 -0
- package/dist/src/models/STPendingInvoice.js +284 -0
- package/dist/src/models/STPendingInvoice.js.map +1 -0
- package/dist/src/models/StripeAccount.d.ts +17 -0
- package/dist/src/models/StripeAccount.d.ts.map +1 -0
- package/dist/src/models/StripeAccount.js +81 -0
- package/dist/src/models/StripeAccount.js.map +1 -0
- package/dist/src/models/StripeCheckoutSession.d.ts +9 -0
- package/dist/src/models/StripeCheckoutSession.d.ts.map +1 -0
- package/dist/src/models/StripeCheckoutSession.js +31 -0
- package/dist/src/models/StripeCheckoutSession.js.map +1 -0
- package/dist/src/models/StripePaymentIntent.d.ts +9 -0
- package/dist/src/models/StripePaymentIntent.d.ts.map +1 -0
- package/dist/src/models/StripePaymentIntent.js +31 -0
- package/dist/src/models/StripePaymentIntent.js.map +1 -0
- package/dist/src/models/Ticket.d.ts +61 -0
- package/dist/src/models/Ticket.d.ts.map +1 -0
- package/dist/src/models/Ticket.js +143 -0
- package/dist/src/models/Ticket.js.map +1 -0
- package/dist/src/models/Token.d.ts +49 -0
- package/dist/src/models/Token.d.ts.map +1 -0
- package/dist/src/models/Token.js +218 -0
- package/dist/src/models/Token.js.map +1 -0
- package/dist/src/models/Token.test.d.ts +2 -0
- package/dist/src/models/Token.test.d.ts.map +1 -0
- package/dist/src/models/Token.test.js +60 -0
- package/dist/src/models/Token.test.js.map +1 -0
- package/dist/src/models/UsedRegisterCode.d.ts +22 -0
- package/dist/src/models/UsedRegisterCode.d.ts.map +1 -0
- package/dist/src/models/UsedRegisterCode.js +158 -0
- package/dist/src/models/UsedRegisterCode.js.map +1 -0
- package/dist/src/models/User.d.ts +55 -0
- package/dist/src/models/User.d.ts.map +1 -0
- package/dist/src/models/User.js +314 -0
- package/dist/src/models/User.js.map +1 -0
- package/dist/src/models/UserPermissions.d.ts +15 -0
- package/dist/src/models/UserPermissions.d.ts.map +1 -0
- package/dist/src/models/UserPermissions.js +57 -0
- package/dist/src/models/UserPermissions.js.map +1 -0
- package/dist/src/models/Webshop.d.ts +44 -0
- package/dist/src/models/Webshop.d.ts.map +1 -0
- package/dist/src/models/Webshop.js +184 -0
- package/dist/src/models/Webshop.js.map +1 -0
- package/dist/src/models/WebshopDiscountCode.d.ts +18 -0
- package/dist/src/models/WebshopDiscountCode.d.ts.map +1 -0
- package/dist/src/models/WebshopDiscountCode.js +88 -0
- package/dist/src/models/WebshopDiscountCode.js.map +1 -0
- package/dist/src/models/addresses/City.d.ts +14 -0
- package/dist/src/models/addresses/City.d.ts.map +1 -0
- package/dist/src/models/addresses/City.js +37 -0
- package/dist/src/models/addresses/City.js.map +1 -0
- package/dist/src/models/addresses/PostalCode.d.ts +20 -0
- package/dist/src/models/addresses/PostalCode.d.ts.map +1 -0
- package/dist/src/models/addresses/PostalCode.js +138 -0
- package/dist/src/models/addresses/PostalCode.js.map +1 -0
- package/dist/src/models/addresses/PostalCode.test.d.ts +2 -0
- package/dist/src/models/addresses/PostalCode.test.d.ts.map +1 -0
- package/dist/src/models/addresses/PostalCode.test.js +98 -0
- package/dist/src/models/addresses/PostalCode.test.js.map +1 -0
- package/dist/src/models/addresses/Province.d.ts +9 -0
- package/dist/src/models/addresses/Province.d.ts.map +1 -0
- package/dist/src/models/addresses/Province.js +24 -0
- package/dist/src/models/addresses/Province.js.map +1 -0
- package/dist/src/models/addresses/Street.d.ts +10 -0
- package/dist/src/models/addresses/Street.d.ts.map +1 -0
- package/dist/src/models/addresses/Street.js +26 -0
- package/dist/src/models/addresses/Street.js.map +1 -0
- package/dist/src/models/index.d.ts +37 -0
- package/dist/src/models/index.d.ts.map +1 -0
- package/dist/src/models/index.js +53 -0
- package/dist/src/models/index.js.map +1 -0
- package/dist/src/structures/OrganizationServerMetaData.d.ts +43 -0
- package/dist/src/structures/OrganizationServerMetaData.d.ts.map +1 -0
- package/dist/src/structures/OrganizationServerMetaData.js +128 -0
- package/dist/src/structures/OrganizationServerMetaData.js.map +1 -0
- package/dist/tests/jest.global.setup.d.ts +3 -0
- package/dist/tests/jest.global.setup.d.ts.map +1 -0
- package/dist/tests/jest.global.setup.js +20 -0
- package/dist/tests/jest.global.setup.js.map +1 -0
- package/dist/tests/jest.setup.d.ts +2 -0
- package/dist/tests/jest.setup.d.ts.map +1 -0
- package/dist/tests/jest.setup.js +15 -0
- package/dist/tests/jest.setup.js.map +1 -0
- package/package.json +30 -0
- package/src/assets/Metropolis-Black.woff2 +0 -0
- package/src/assets/Metropolis-BlackItalic.woff2 +0 -0
- package/src/assets/Metropolis-Bold.woff2 +0 -0
- package/src/assets/Metropolis-BoldItalic.woff2 +0 -0
- package/src/assets/Metropolis-ExtraBold.woff2 +0 -0
- package/src/assets/Metropolis-ExtraBoldItalic.woff2 +0 -0
- package/src/assets/Metropolis-ExtraLight.woff2 +0 -0
- package/src/assets/Metropolis-ExtraLightItalic.woff2 +0 -0
- package/src/assets/Metropolis-Light.woff2 +0 -0
- package/src/assets/Metropolis-LightItalic.woff2 +0 -0
- package/src/assets/Metropolis-Medium.woff2 +0 -0
- package/src/assets/Metropolis-MediumItalic.woff2 +0 -0
- package/src/assets/Metropolis-Regular.woff2 +0 -0
- package/src/assets/Metropolis-RegularItalic.woff2 +0 -0
- package/src/assets/Metropolis-SemiBold.woff2 +0 -0
- package/src/assets/Metropolis-SemiBoldItalic.woff2 +0 -0
- package/src/assets/Metropolis-Thin.woff2 +0 -0
- package/src/assets/Metropolis-ThinItalic.woff2 +0 -0
- package/src/assets/logo.png +0 -0
- package/src/factories/AddressFactory.ts +42 -0
- package/src/factories/EmergencyContactFactory.ts +43 -0
- package/src/factories/GroupFactory.ts +66 -0
- package/src/factories/MemberFactory.ts +122 -0
- package/src/factories/OrganizationFactory.ts +45 -0
- package/src/factories/ParentFactory.ts +49 -0
- package/src/factories/RecordFactory.ts +12 -0
- package/src/factories/RegisterCodeFactory.ts +25 -0
- package/src/factories/RegistrationFactory.ts +32 -0
- package/src/factories/UserFactory.ts +66 -0
- package/src/factories/WebshopFactory.ts +43 -0
- package/src/helpers/DNSValidator.ts +153 -0
- package/src/helpers/EmailBuilder.ts +127 -0
- package/src/helpers/GroupBuilder.ts +438 -0
- package/src/helpers/Handlebars.ts +203 -0
- package/src/helpers/InvoiceBuilder.test.ts +57 -0
- package/src/helpers/InvoiceBuilder.ts +501 -0
- package/src/helpers/RateLimiter.ts +75 -0
- package/src/helpers/WebshopCounter.test.ts +16 -0
- package/src/helpers/WebshopCounter.ts +36 -0
- package/src/index.ts +21 -0
- package/src/migrations/1593773929-create-initial-tables.sql +634 -0
- package/src/migrations/1605261999-gemeenten-tmp.sql +2820 -0
- package/src/migrations/1605262045-import-postcodes.ts +132 -0
- package/src/migrations/1605262046-import-postcodes-nl.ts +97 -0
- package/src/migrations/1605279038-drop-gemeenten-tmp.sql +1 -0
- package/src/migrations/1648392491-default-templates.sql +9 -0
- package/src/migrations/1651245707-default-templates-reminders.sql +6 -0
- package/src/migrations/1708607340-tickets-deleted-at.sql +1 -0
- package/src/migrations/1710459176-register-code-invoices.sql +3 -0
- package/src/migrations/1712158247-discount-codes.sql +17 -0
- package/src/migrations/1713178665-drop-invites.sql +1 -0
- package/src/migrations/1713178666-drop-keychain.sql +1 -0
- package/src/migrations/1713178667-drop-challenges.sql +1 -0
- package/src/migrations/1713178668-drop-user-keys.sql +7 -0
- package/src/migrations/1713178669-drop-organization-key.sql +2 -0
- package/src/migrations/1714985451-user-nullable-organization-id.sql +2 -0
- package/src/migrations/1714985452-email-verification-code-nullable-organization-id.sql +2 -0
- package/src/migrations/1714985453-user-organization-permissions.sql +2 -0
- package/src/migrations/1714985454-user-organization-permissions.sql +2 -0
- package/src/migrations/1715079362-platform.sql +6 -0
- package/src/migrations/1715181649-registrations-organization-id.sql +2 -0
- package/src/migrations/1715181650-registrations-organization-id-fill.sql +1 -0
- package/src/migrations/1715181651-registrations-organization-id-drop-null.sql +2 -0
- package/src/migrations/1716117067-members-nullable-organization-id.sql +2 -0
- package/src/migrations/1719567581-registration-periods.sql +13 -0
- package/src/migrations/1719567582-organization-registration-periods.sql +13 -0
- package/src/migrations/1719567881-organization-periodId.sql +2 -0
- package/src/migrations/1719567882-groups-periodId.sql +2 -0
- package/src/migrations/1719567883-platform-periodId.sql +2 -0
- package/src/migrations/1719568079-default-period.sql +2 -0
- package/src/migrations/1719568080-set-default-period-platform.sql +1 -0
- package/src/migrations/1719568081-set-default-period-organizations.sql +1 -0
- package/src/migrations/1719568082-set-default-period-groups.sql +1 -0
- package/src/migrations/1719580828-registrations-periodId.sql +2 -0
- package/src/migrations/1719580829-set-default-period-registrations.sql +1 -0
- package/src/migrations/data/postcodes/nl/Drenthe +291 -0
- package/src/migrations/data/postcodes/nl/Flevoland +107 -0
- package/src/migrations/data/postcodes/nl/Friesland +518 -0
- package/src/migrations/data/postcodes/nl/Gelderland +601 -0
- package/src/migrations/data/postcodes/nl/Groningen +279 -0
- package/src/migrations/data/postcodes/nl/Limburg +324 -0
- package/src/migrations/data/postcodes/nl/Noord-Brabant +620 -0
- package/src/migrations/data/postcodes/nl/Noord-Holland +566 -0
- package/src/migrations/data/postcodes/nl/Overrijsel +344 -0
- package/src/migrations/data/postcodes/nl/Utrecht +278 -0
- package/src/migrations/data/postcodes/nl/Zeeland +179 -0
- package/src/migrations/data/postcodes/nl/Zuid-Holland +662 -0
- package/src/models/BalanceItem.ts +392 -0
- package/src/models/BalanceItemPayment.ts +106 -0
- package/src/models/BuckarooPayment.ts +19 -0
- package/src/models/Document.ts +203 -0
- package/src/models/DocumentTemplate.ts +583 -0
- package/src/models/EmailTemplate.ts +64 -0
- package/src/models/EmailVerificationCode.ts +352 -0
- package/src/models/Group.ts +293 -0
- package/src/models/Image.ts +147 -0
- package/src/models/Member.ts +386 -0
- package/src/models/MemberResponsibilityRecord.ts +39 -0
- package/src/models/MolliePayment.ts +19 -0
- package/src/models/MollieToken.ts +369 -0
- package/src/models/OneTimeToken.ts +131 -0
- package/src/models/Order.ts +1030 -0
- package/src/models/Organization.ts +1085 -0
- package/src/models/OrganizationRegistrationPeriod.ts +54 -0
- package/src/models/PasswordToken.ts +139 -0
- package/src/models/PayconiqPayment.ts +241 -0
- package/src/models/Payment.ts +216 -0
- package/src/models/Platform.ts +76 -0
- package/src/models/RegisterCode.ts +164 -0
- package/src/models/Registration.ts +405 -0
- package/src/models/RegistrationPeriod.ts +55 -0
- package/src/models/STCredit.ts +134 -0
- package/src/models/STInvoice.ts +507 -0
- package/src/models/STPackage.ts +324 -0
- package/src/models/STPendingInvoice.ts +308 -0
- package/src/models/StripeAccount.ts +71 -0
- package/src/models/StripeCheckoutSession.ts +22 -0
- package/src/models/StripePaymentIntent.ts +22 -0
- package/src/models/Ticket.ts +145 -0
- package/src/models/Token.test.ts +69 -0
- package/src/models/Token.ts +269 -0
- package/src/models/UsedRegisterCode.ts +166 -0
- package/src/models/User.ts +445 -0
- package/src/models/UserPermissions.ts +54 -0
- package/src/models/Webshop.ts +206 -0
- package/src/models/WebshopDiscountCode.ts +81 -0
- package/src/models/addresses/City.ts +31 -0
- package/src/models/addresses/PostalCode.test.ts +117 -0
- package/src/models/addresses/PostalCode.ts +164 -0
- package/src/models/addresses/Province.ts +20 -0
- package/src/models/addresses/Street.ts +25 -0
- package/src/models/index.ts +49 -0
- package/src/structures/OrganizationServerMetaData.ts +117 -0
|
@@ -0,0 +1,1030 @@
|
|
|
1
|
+
import { column, ManyToOneRelation, Model } from "@simonbackx/simple-database";
|
|
2
|
+
import { SimpleError } from "@simonbackx/simple-errors";
|
|
3
|
+
import { Email } from '@stamhoofd/email';
|
|
4
|
+
import { QueueHandler } from "@stamhoofd/queues";
|
|
5
|
+
import { BalanceItemPaymentWithPrivatePayment,BalanceItemWithPayments, BalanceItemWithPrivatePayments, EmailTemplateType, MemberBalanceItemPayment, Order as OrderStruct, OrderData, OrderStatus, Payment as PaymentStruct, PaymentMethod, PrivateOrder, PrivatePayment, ProductType, Recipient, Replacement, WebshopPreview, WebshopStatus, WebshopTicketType, WebshopTimeSlot } from '@stamhoofd/structures';
|
|
6
|
+
import { Formatter } from "@stamhoofd/utility";
|
|
7
|
+
import { v4 as uuidv4 } from "uuid";
|
|
8
|
+
|
|
9
|
+
import { getEmailBuilder } from "../helpers/EmailBuilder";
|
|
10
|
+
import { WebshopCounter } from '../helpers/WebshopCounter';
|
|
11
|
+
import { BalanceItem, EmailTemplate, Organization, Payment, Ticket, Webshop, WebshopDiscountCode } from './';
|
|
12
|
+
|
|
13
|
+
export class Order extends Model {
|
|
14
|
+
static table = "webshop_orders";
|
|
15
|
+
|
|
16
|
+
// Columns
|
|
17
|
+
@column({
|
|
18
|
+
primary: true, type: "string", beforeSave(value) {
|
|
19
|
+
return value ?? uuidv4();
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
id!: string;
|
|
23
|
+
|
|
24
|
+
@column({ foreignKey: Order.organization, type: "string" })
|
|
25
|
+
organizationId: string;
|
|
26
|
+
|
|
27
|
+
@column({ foreignKey: Order.webshop, type: "string" })
|
|
28
|
+
webshopId: string;
|
|
29
|
+
|
|
30
|
+
@column({ type: "string", nullable: true, foreignKey: Order.payment })
|
|
31
|
+
paymentId: string | null = null
|
|
32
|
+
|
|
33
|
+
@column({ type: "string", nullable: true })
|
|
34
|
+
userId: string | null = null
|
|
35
|
+
|
|
36
|
+
@column({ type: "json", decoder: OrderData })
|
|
37
|
+
data: OrderData
|
|
38
|
+
|
|
39
|
+
@column({ type: "integer", nullable: true })
|
|
40
|
+
number: number | null = null
|
|
41
|
+
|
|
42
|
+
@column({
|
|
43
|
+
type: "datetime", beforeSave(old?: any) {
|
|
44
|
+
if (old !== undefined) {
|
|
45
|
+
return old;
|
|
46
|
+
}
|
|
47
|
+
const date = new Date()
|
|
48
|
+
date.setMilliseconds(0)
|
|
49
|
+
return date
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
createdAt: Date
|
|
53
|
+
|
|
54
|
+
@column({
|
|
55
|
+
type: "datetime", beforeSave() {
|
|
56
|
+
const date = new Date()
|
|
57
|
+
date.setMilliseconds(0)
|
|
58
|
+
return date
|
|
59
|
+
},
|
|
60
|
+
skipUpdate: true
|
|
61
|
+
})
|
|
62
|
+
updatedAt: Date
|
|
63
|
+
|
|
64
|
+
@column({ type: "datetime", nullable: true })
|
|
65
|
+
validAt: Date | null = null
|
|
66
|
+
|
|
67
|
+
@column({ type: "string" })
|
|
68
|
+
status = OrderStatus.Created
|
|
69
|
+
|
|
70
|
+
static webshop = new ManyToOneRelation(Webshop, "webshop");
|
|
71
|
+
static payment = new ManyToOneRelation(Payment, "payment");
|
|
72
|
+
static organization = new ManyToOneRelation(Organization, "organization");
|
|
73
|
+
|
|
74
|
+
getTransferReplacements(): { [key: string]: string } {
|
|
75
|
+
return {
|
|
76
|
+
nr: this.number?.toString() ?? "",
|
|
77
|
+
email: this.data.customer.email ?? '',
|
|
78
|
+
phone: this.data.customer.phone ?? '',
|
|
79
|
+
name: this.data.customer.name ?? '',
|
|
80
|
+
naam: this.data.customer.name ?? '',
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getUrl(this: Order & { webshop: Webshop & { organization: Organization } }) {
|
|
85
|
+
// Country locales are disabled on webshops (always the same country). But we need to add the language if it isn't the same as the organization default language
|
|
86
|
+
let locale = ""
|
|
87
|
+
if (this.data.consumerLanguage != this.webshop.organization.i18n.language) {
|
|
88
|
+
locale = "/"+this.data.consumerLanguage
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return "https://"+this.webshop.getHost()+locale+"/order/"+this.id
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
generateBalanceDescription(webshop: Webshop) {
|
|
95
|
+
if (!this.number) {
|
|
96
|
+
return 'Bestelling - ' + webshop.meta.name
|
|
97
|
+
}
|
|
98
|
+
return 'Bestelling #' + this.number.toString() + ' - ' + webshop.meta.name
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Fetch an order
|
|
103
|
+
*/
|
|
104
|
+
static async getForPayment(organizationId: string | null, paymentId: string): Promise<Order | undefined> {
|
|
105
|
+
const order = (await this.where({ paymentId }, { limit: 1 }))[0]
|
|
106
|
+
if (!order) {
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
if (organizationId !== null && order.organizationId !== organizationId) {
|
|
110
|
+
// Security check
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
return order
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
shouldIncludeStock() {
|
|
117
|
+
return this.status !== OrderStatus.Canceled && this.status !== OrderStatus.Deleted
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async shouldCreateTickets() {
|
|
121
|
+
if (this.status === OrderStatus.Canceled || this.status === OrderStatus.Deleted || this.id === undefined || this.id === null) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (this.data.paymentMethod === PaymentMethod.PointOfSale || this.data.paymentMethod === PaymentMethod.Unknown || this.data.totalPrice === 0) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Check we paid at least 1 cent
|
|
130
|
+
const allBalanceItems = await BalanceItem.where({ orderId: this.id })
|
|
131
|
+
if (allBalanceItems.find(b => b.pricePaid > 0)) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get totalToPay() {
|
|
139
|
+
if (this.status === OrderStatus.Canceled || this.status === OrderStatus.Deleted) {
|
|
140
|
+
return 0
|
|
141
|
+
}
|
|
142
|
+
return this.data.totalPrice
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async undoPaymentFailed(this: Order, _payment: Payment | null, _organization: Organization) {
|
|
146
|
+
if (this.status !== OrderStatus.Deleted && this.status !== OrderStatus.Canceled) {
|
|
147
|
+
this.markUpdated()
|
|
148
|
+
await this.save()
|
|
149
|
+
|
|
150
|
+
const webshop = await Webshop.getByID(this.webshopId);
|
|
151
|
+
if (webshop) {
|
|
152
|
+
await this.setRelation(Order.webshop, webshop).updateTickets()
|
|
153
|
+
}
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.log('Undoing payment failed for order '+this.id)
|
|
158
|
+
this.markUpdated()
|
|
159
|
+
this.status = OrderStatus.Created
|
|
160
|
+
await this.save()
|
|
161
|
+
|
|
162
|
+
const webshop = await QueueHandler.schedule("webshop-stock/"+this.webshopId, async () => {
|
|
163
|
+
// Fetch webshop inside queue to make sure the stock is up to date
|
|
164
|
+
const webshop = await Webshop.getByID(this.webshopId);
|
|
165
|
+
if (!webshop) {
|
|
166
|
+
// ignore for now
|
|
167
|
+
console.error("Missing webshop with id "+this.webshopId+" in webshop stock queue for order "+this.id)
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
await this.setRelation(Order.webshop, webshop).updateStock() // readd reserved stock
|
|
172
|
+
return webshop;
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
if (webshop) {
|
|
176
|
+
await this.setRelation(Order.webshop, webshop).updateTickets()
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async deleteOrderBecauseOfCreationError(this: Order) {
|
|
181
|
+
this.status = OrderStatus.Deleted
|
|
182
|
+
await this.save()
|
|
183
|
+
|
|
184
|
+
const webshop = await QueueHandler.schedule("webshop-stock/"+this.webshopId, async () => {
|
|
185
|
+
// Fetch webshop inside queue to make sure the stock is up to date
|
|
186
|
+
const webshop = await Webshop.getByID(this.webshopId);
|
|
187
|
+
if (!webshop) {
|
|
188
|
+
// ignore for now
|
|
189
|
+
console.error("Missing webshop with id "+this.webshopId+" in webshop stock queue for order "+this.id)
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await this.setRelation(Order.webshop, webshop).updateStock() // readd reserved stock
|
|
194
|
+
return webshop;
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
if (webshop) {
|
|
198
|
+
await this.setRelation(Order.webshop, webshop).updateTickets()
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async onPaymentFailed(this: Order, payment: Payment | null, organization: Organization) {
|
|
203
|
+
if (this.shouldIncludeStock()) {
|
|
204
|
+
this.markUpdated()
|
|
205
|
+
if (this.number === null) {
|
|
206
|
+
this.status = OrderStatus.Deleted
|
|
207
|
+
}
|
|
208
|
+
await this.save()
|
|
209
|
+
|
|
210
|
+
const webshop = await QueueHandler.schedule("webshop-stock/"+this.webshopId, async () => {
|
|
211
|
+
// Fetch webshop inside queue to make sure the stock is up to date
|
|
212
|
+
const webshop = await Webshop.getByID(this.webshopId);
|
|
213
|
+
if (!webshop) {
|
|
214
|
+
// ignore for now
|
|
215
|
+
console.error("Missing webshop with id "+this.webshopId+" in webshop stock queue for order "+this.id)
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
await this.setRelation(Order.webshop, webshop).updateStock() // remove reserved stock
|
|
220
|
+
return webshop
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
if (webshop) {
|
|
224
|
+
await this.setRelation(Order.webshop, webshop).updateTickets()
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Send an email if the payment failed after 15 minutes being pending
|
|
228
|
+
// const difference = new Date().getTime() - this.createdAt.getTime()
|
|
229
|
+
// if (difference > 1000 * 60 * 30 && difference < 1000 * 60 * 60 * 24) {
|
|
230
|
+
// if (payment && payment.method !== PaymentMethod.Transfer && payment.method !== PaymentMethod.PointOfSale) {
|
|
231
|
+
// console.log('Marked order '+this.id+' as payment failed after ' + (difference / 1000 / 60).toFixed(1) + ' mins. Sending email.')
|
|
232
|
+
// const webshop = await Webshop.getByID(this.webshopId)
|
|
233
|
+
// if (!webshop) {
|
|
234
|
+
// console.error("Missing organization or webshop for order "+this.id)
|
|
235
|
+
// return
|
|
236
|
+
// }
|
|
237
|
+
// const { from, replyTo } = organization.getEmail(webshop.privateMeta.defaultEmailId, true)
|
|
238
|
+
// await this.setRelation(Order.webshop, webshop.setRelation(Order.organization, organization)).sendEmailTemplate({
|
|
239
|
+
// type: EmailTemplateType.OrderOnlinePaymentFailed,
|
|
240
|
+
// from,
|
|
241
|
+
// replyTo
|
|
242
|
+
// })
|
|
243
|
+
// } else {
|
|
244
|
+
// console.log('Marked order '+this.id+' as payment failed after ' + (difference / 1000 / 60).toFixed(1) + ' mins. Payment method not matching.')
|
|
245
|
+
// }
|
|
246
|
+
// } else {
|
|
247
|
+
// console.log('Marked order '+this.id+' as payment failed after ' + (difference / 1000 / 60).toFixed(1) + ' mins. Not sending email.')
|
|
248
|
+
// }
|
|
249
|
+
} else {
|
|
250
|
+
this.markUpdated()
|
|
251
|
+
await this.save()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Adds or removes the order to the stock of the webshop (if it wasn't already included). If amounts were changed, only those
|
|
259
|
+
* changes will get added
|
|
260
|
+
* Should always happen in the webshop-stock queue to prevent multiple webshop writes at the same time
|
|
261
|
+
* + in combination with validation and reading the webshop
|
|
262
|
+
*/
|
|
263
|
+
async updateStock(this: Order & { webshop: Webshop }, previousData: OrderData | null = null) {
|
|
264
|
+
// Previous data?
|
|
265
|
+
|
|
266
|
+
// Add or delete this order from the stock?
|
|
267
|
+
const add = this.shouldIncludeStock()
|
|
268
|
+
|
|
269
|
+
let changed = false
|
|
270
|
+
const discountCodeUsageMap: Map<string, number> = new Map()
|
|
271
|
+
|
|
272
|
+
if (previousData !== null) {
|
|
273
|
+
// Remove stock from old items without modifying old data
|
|
274
|
+
for (const item of previousData.cart.items) {
|
|
275
|
+
const product = this.webshop.products.find(p => p.id === item.product.id)
|
|
276
|
+
if (product) {
|
|
277
|
+
if (item.reservedAmount > 0) {
|
|
278
|
+
product.usedStock -= item.reservedAmount
|
|
279
|
+
if (product.usedStock < 0) {
|
|
280
|
+
product.usedStock = 0
|
|
281
|
+
}
|
|
282
|
+
changed = true
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Product price
|
|
286
|
+
for (const [priceId, amount] of item.reservedPrices) {
|
|
287
|
+
const productPrice = product.prices.find(p => p.id === priceId)
|
|
288
|
+
if (productPrice) {
|
|
289
|
+
productPrice.usedStock -= amount
|
|
290
|
+
if (productPrice.usedStock < 0) {
|
|
291
|
+
productPrice.usedStock = 0
|
|
292
|
+
}
|
|
293
|
+
changed = true
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Options
|
|
298
|
+
for (const [optionId, amount] of item.reservedOptions) {
|
|
299
|
+
const option = product.optionMenus.flatMap(om => om.options).find(o => o.id === optionId)
|
|
300
|
+
if (option) {
|
|
301
|
+
option.usedStock -= amount
|
|
302
|
+
if (option.usedStock < 0) {
|
|
303
|
+
option.usedStock = 0
|
|
304
|
+
}
|
|
305
|
+
changed = true
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Seats
|
|
310
|
+
if (item.reservedSeats.length > 0) {
|
|
311
|
+
for (const seat of item.reservedSeats) {
|
|
312
|
+
// Only remove each seat once
|
|
313
|
+
const reservedIndex = product.reservedSeats.findIndex(s => s.equals(seat))
|
|
314
|
+
if (reservedIndex !== -1) {
|
|
315
|
+
product.reservedSeats.splice(reservedIndex, 1)
|
|
316
|
+
changed = true
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
item.reservedSeats = [];
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
for (const code of previousData.discountCodes) {
|
|
325
|
+
if (code.reserved) {
|
|
326
|
+
// Remove usage
|
|
327
|
+
code.reserved = false;
|
|
328
|
+
discountCodeUsageMap.set(code.id, (discountCodeUsageMap.get(code.id) ?? 0) - 1)
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
for (const item of this.data.cart.items) {
|
|
334
|
+
if (previousData !== null) {
|
|
335
|
+
// If we have previousData, we already removed the stock from the old items, so reservedAmount is always zero
|
|
336
|
+
item.reservedAmount = 0
|
|
337
|
+
item.reservedPrices = new Map()
|
|
338
|
+
item.reservedOptions = new Map()
|
|
339
|
+
changed = true
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const difference = add ? (item.amount - item.reservedAmount) : -item.reservedAmount
|
|
343
|
+
if (difference !== 0) {
|
|
344
|
+
const product = this.webshop.products.find(p => p.id === item.product.id)
|
|
345
|
+
if (product) {
|
|
346
|
+
product.usedStock += difference
|
|
347
|
+
if (product.usedStock < 0) {
|
|
348
|
+
product.usedStock = 0
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Keep track that we included this order in the stock already
|
|
352
|
+
item.reservedAmount += difference
|
|
353
|
+
changed = true
|
|
354
|
+
|
|
355
|
+
const productPrice = product.prices.find(p => p.id === item.productPrice.id)
|
|
356
|
+
if (productPrice) {
|
|
357
|
+
productPrice.usedStock += difference
|
|
358
|
+
if (productPrice.usedStock < 0) {
|
|
359
|
+
productPrice.usedStock = 0
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Keep track that we included this order in the stock already
|
|
363
|
+
item.reservedPrices.set(productPrice.id, (item.reservedPrices.get(productPrice.id) ?? 0) + difference)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Options
|
|
367
|
+
for (const cartItemOption of item.options) {
|
|
368
|
+
const option = product.optionMenus.find(om => om.id === cartItemOption.optionMenu.id)?.options.find(o => o.id === cartItemOption.option.id)
|
|
369
|
+
if (option) {
|
|
370
|
+
option.usedStock += difference
|
|
371
|
+
if (option.usedStock < 0) {
|
|
372
|
+
option.usedStock = 0
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Keep track that we included this order in the stock already
|
|
376
|
+
item.reservedOptions.set(option.id, (item.reservedOptions.get(option.id) ?? 0) + difference)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (previousData !== null && previousData.timeSlot) {
|
|
384
|
+
// Changed timeslot. Remove all reserved ones
|
|
385
|
+
const ps = previousData.timeSlot
|
|
386
|
+
const timeSlot = this.webshop.meta.checkoutMethods.flatMap(m => m.timeSlots).flatMap(t => t.timeSlots).find(t => t.id === ps.id)
|
|
387
|
+
if (timeSlot) {
|
|
388
|
+
// Remove any reserved stock
|
|
389
|
+
const updated = Order.updateTimeSlotStock(timeSlot, this.data, false)
|
|
390
|
+
changed = changed || updated
|
|
391
|
+
} else {
|
|
392
|
+
this.data.reservedOrder = false
|
|
393
|
+
this.data.reservedPersons = 0
|
|
394
|
+
changed = true
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (this.data.timeSlot !== null) {
|
|
399
|
+
const s = this.data.timeSlot
|
|
400
|
+
const timeSlot = this.webshop.meta.checkoutMethods.flatMap(m => m.timeSlots).flatMap(t => t.timeSlots).find(t => t.id === s.id)
|
|
401
|
+
|
|
402
|
+
if (timeSlot) {
|
|
403
|
+
const updated = Order.updateTimeSlotStock(timeSlot, this.data, add)
|
|
404
|
+
changed = changed || updated
|
|
405
|
+
} else {
|
|
406
|
+
console.error("Missing timeslot "+s.id+" in webshop "+this.webshopId)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Seats
|
|
411
|
+
for (const item of this.data.cart.items) {
|
|
412
|
+
if (item.seats.length > 0) {
|
|
413
|
+
const product = this.webshop.products.find(p => p.id === item.product.id)
|
|
414
|
+
if (product) {
|
|
415
|
+
if (previousData !== null) {
|
|
416
|
+
// Already removed
|
|
417
|
+
item.reservedSeats = []
|
|
418
|
+
changed = true
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// First remove all (but only once!), to avoid duplicates
|
|
422
|
+
for (const seat of item.reservedSeats) {
|
|
423
|
+
const reservedIndex = product.reservedSeats.findIndex(s => s.equals(seat))
|
|
424
|
+
if (reservedIndex !== -1) {
|
|
425
|
+
product.reservedSeats.splice(reservedIndex, 1)
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// First remove all (but only once!), to avoid duplicates
|
|
430
|
+
if (add) {
|
|
431
|
+
for (const seat of item.seats) {
|
|
432
|
+
product.reservedSeats.push(seat)
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
changed = true
|
|
436
|
+
item.reservedSeats = add ? item.seats : []
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Discount codes
|
|
442
|
+
for (const code of this.data.discountCodes) {
|
|
443
|
+
if (previousData !== null) {
|
|
444
|
+
code.reserved = false;
|
|
445
|
+
changed = true
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (code.reserved && !add) {
|
|
449
|
+
// Remove usage
|
|
450
|
+
code.reserved = false;
|
|
451
|
+
discountCodeUsageMap.set(code.id, (discountCodeUsageMap.get(code.id) ?? 0) - 1)
|
|
452
|
+
changed = true
|
|
453
|
+
} else if (!code.reserved && add) {
|
|
454
|
+
code.reserved = true;
|
|
455
|
+
discountCodeUsageMap.set(code.id, (discountCodeUsageMap.get(code.id) ?? 0) + 1)
|
|
456
|
+
changed = true
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
for (const [id, amount] of discountCodeUsageMap) {
|
|
461
|
+
if (amount !== 0) {
|
|
462
|
+
const code = await WebshopDiscountCode.getByID(id);
|
|
463
|
+
if (code) {
|
|
464
|
+
code.usageCount += amount;
|
|
465
|
+
await code.save()
|
|
466
|
+
changed = true
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (changed) {
|
|
472
|
+
await this.webshop.save()
|
|
473
|
+
await this.save()
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
private static updateTimeSlotStock(timeSlot: WebshopTimeSlot, data: OrderData, add: boolean) {
|
|
478
|
+
let changed = false
|
|
479
|
+
if (data.reservedOrder !== add) {
|
|
480
|
+
data.reservedOrder = add
|
|
481
|
+
timeSlot.usedOrders += add ? 1 : -1
|
|
482
|
+
if (timeSlot.usedOrders < 0) {
|
|
483
|
+
timeSlot.usedOrders = 0
|
|
484
|
+
}
|
|
485
|
+
changed = true
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const personDifference = (add ? data.cart.persons : 0) - data.reservedPersons
|
|
489
|
+
|
|
490
|
+
if (personDifference !== 0) {
|
|
491
|
+
timeSlot.usedPersons += personDifference
|
|
492
|
+
if (timeSlot.usedPersons < 0) {
|
|
493
|
+
timeSlot.usedPersons = 0
|
|
494
|
+
}
|
|
495
|
+
data.reservedPersons += personDifference
|
|
496
|
+
changed = true
|
|
497
|
+
}
|
|
498
|
+
return changed
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async updateTickets(this: Order & { webshop: Webshop }): Promise<{ tickets: Ticket[], didCreateTickets: boolean }> {
|
|
502
|
+
const webshop = this.webshop
|
|
503
|
+
|
|
504
|
+
if (webshop.meta.ticketType !== WebshopTicketType.Tickets && webshop.meta.ticketType !== WebshopTicketType.SingleTicket) {
|
|
505
|
+
return { tickets: [], didCreateTickets: false };
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const existingTickets = !this.id ? [] : await Ticket.where({ orderId: this.id })
|
|
509
|
+
|
|
510
|
+
if (!(await this.shouldCreateTickets())) {
|
|
511
|
+
// Delete all existing tickets
|
|
512
|
+
for (const ticket of existingTickets) {
|
|
513
|
+
if (ticket.isDeleted) {
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
console.log("Deleting ticket for order "+this.id+", ticket id = "+ticket.id)
|
|
517
|
+
await ticket.softDelete();
|
|
518
|
+
}
|
|
519
|
+
return { tickets: [], didCreateTickets: false }
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Create tickets if needed (we might already be valid in case of transfer payments)
|
|
523
|
+
let tickets: Ticket[] = []
|
|
524
|
+
|
|
525
|
+
if (webshop.meta.ticketType === WebshopTicketType.Tickets) {
|
|
526
|
+
const ticketMap = new Map<string, number>()
|
|
527
|
+
|
|
528
|
+
for (const item of this.data.cart.items) {
|
|
529
|
+
if (item.product.type === ProductType.Ticket || item.product.type === ProductType.Voucher) {
|
|
530
|
+
const offset = ticketMap.get(item.product.id) ?? 0
|
|
531
|
+
|
|
532
|
+
// Separate ticket if multiple amounts
|
|
533
|
+
for (let index = 0; index < item.amount; index++) {
|
|
534
|
+
const ticket = new Ticket()
|
|
535
|
+
ticket.orderId = this.id
|
|
536
|
+
ticket.itemId = item.id
|
|
537
|
+
ticket.organizationId = this.organizationId
|
|
538
|
+
ticket.webshopId = this.webshopId
|
|
539
|
+
|
|
540
|
+
// Relative index for items with same properties
|
|
541
|
+
ticket.index = offset + index + 1
|
|
542
|
+
ticket.total = item.getTotalAmount(this.data.cart)
|
|
543
|
+
|
|
544
|
+
// Seat
|
|
545
|
+
ticket.seat = item.seats.length > 0 ? item.seats[index] : null
|
|
546
|
+
ticket.originalSeat = ticket.seat
|
|
547
|
+
|
|
548
|
+
// Do not save yet
|
|
549
|
+
tickets.push(ticket)
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
ticketMap.set(item.product.id, offset + item.amount)
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
} else {
|
|
556
|
+
// Create a shared ticket for the whole order
|
|
557
|
+
const ticket = new Ticket()
|
|
558
|
+
ticket.orderId = this.id
|
|
559
|
+
ticket.itemId = null
|
|
560
|
+
ticket.organizationId = this.organizationId
|
|
561
|
+
ticket.webshopId = this.webshopId
|
|
562
|
+
|
|
563
|
+
// Relative index for items with same properties
|
|
564
|
+
ticket.index = 1
|
|
565
|
+
ticket.total = 1
|
|
566
|
+
|
|
567
|
+
// Do not save yet
|
|
568
|
+
tickets.push(ticket)
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
let didCreateTickets = false
|
|
572
|
+
|
|
573
|
+
if (!this.id) {
|
|
574
|
+
await this.save()
|
|
575
|
+
for (const ticket of tickets) {
|
|
576
|
+
ticket.orderId = this.id
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// First check if we already have tickets
|
|
581
|
+
const mergedTickets: Ticket[] = []
|
|
582
|
+
|
|
583
|
+
// First try to keep existing tickets
|
|
584
|
+
for (const existing of existingTickets) {
|
|
585
|
+
if (existing.originalSeat) {
|
|
586
|
+
const index = tickets.findIndex(t => t.seat && t.seat.equals(existing.originalSeat!))
|
|
587
|
+
if (index !== -1) {
|
|
588
|
+
const ticket = tickets[index]
|
|
589
|
+
tickets.splice(index, 1);
|
|
590
|
+
existing.itemId = ticket.itemId;
|
|
591
|
+
existing.index = ticket.index;
|
|
592
|
+
existing.total = ticket.total
|
|
593
|
+
existing.seat = ticket.seat
|
|
594
|
+
existing.deletedAt = null;
|
|
595
|
+
mergedTickets.push(existing)
|
|
596
|
+
}
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
const index = tickets.findIndex(ticket => existing.itemId === ticket.itemId && ticket.index === existing.index)
|
|
600
|
+
if (index !== -1) {
|
|
601
|
+
const ticket = tickets[index]
|
|
602
|
+
tickets.splice(index, 1);
|
|
603
|
+
existing.itemId = ticket.itemId;
|
|
604
|
+
existing.index = ticket.index;
|
|
605
|
+
existing.total = ticket.total
|
|
606
|
+
existing.seat = ticket.seat
|
|
607
|
+
existing.deletedAt = null;
|
|
608
|
+
mergedTickets.push(existing)
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Try to reuse tickets instead of recreating if no match is found
|
|
613
|
+
for (const existing of existingTickets) {
|
|
614
|
+
if (mergedTickets.find(m => m.id === existing.id)) {
|
|
615
|
+
// Already mapped
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const index = tickets.findIndex(ticket => existing.itemId === ticket.itemId)
|
|
620
|
+
if (index !== -1) {
|
|
621
|
+
const ticket = tickets[index]
|
|
622
|
+
tickets.splice(index, 1);
|
|
623
|
+
existing.itemId = ticket.itemId;
|
|
624
|
+
existing.index = ticket.index;
|
|
625
|
+
existing.total = ticket.total
|
|
626
|
+
existing.seat = ticket.seat
|
|
627
|
+
existing.deletedAt = null;
|
|
628
|
+
mergedTickets.push(existing)
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Remaining tickets that were not reused
|
|
633
|
+
for (const ticket of tickets) {
|
|
634
|
+
didCreateTickets = true
|
|
635
|
+
mergedTickets.push(ticket)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
tickets = mergedTickets
|
|
639
|
+
|
|
640
|
+
// Wait to save them all
|
|
641
|
+
console.log("Saving merged tickets for order "+this.id)
|
|
642
|
+
await Promise.all(tickets.map((ticket) => ticket.save()))
|
|
643
|
+
|
|
644
|
+
// Delete others
|
|
645
|
+
for (const existing of existingTickets) {
|
|
646
|
+
if (!mergedTickets.find(m => m.id === existing.id)) {
|
|
647
|
+
if (existing.isDeleted) {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
console.log("Deleting ticket for order "+this.id+", ticket id = "+existing.id)
|
|
651
|
+
await existing.softDelete()
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
return { tickets, didCreateTickets }
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
markUpdated() {
|
|
659
|
+
this.updatedAt = new Date()
|
|
660
|
+
|
|
661
|
+
// Also save if this is the only saved property
|
|
662
|
+
this.forceSaveProperty("updatedAt")
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* A payment changed the total paid amount
|
|
667
|
+
*/
|
|
668
|
+
async paymentChanged(this: Order, payment: Payment | null, organization: Organization, knownWebshop?: Webshop) {
|
|
669
|
+
this.markUpdated()
|
|
670
|
+
await this.save()
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
async undoPaid(this: Order, payment: Payment | null, organization: Organization, knownWebshop?: Webshop) {
|
|
674
|
+
this.markUpdated()
|
|
675
|
+
await this.save()
|
|
676
|
+
|
|
677
|
+
const webshop = (knownWebshop ?? (await Webshop.getByID(this.webshopId)))?.setRelation(Webshop.organization, organization);
|
|
678
|
+
if (!webshop) {
|
|
679
|
+
console.error("Missing webshop for order "+this.id)
|
|
680
|
+
return
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
await this.setRelation(Order.webshop, webshop).updateTickets()
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Only call this once! Make sure you use the queues correctly
|
|
688
|
+
*/
|
|
689
|
+
async markPaid(this: Order, payment: Payment | null, organization: Organization, knownWebshop?: Webshop) {
|
|
690
|
+
console.log("Marking order "+this.id+" as paid")
|
|
691
|
+
this.markUpdated()
|
|
692
|
+
await this.save()
|
|
693
|
+
const webshop = (knownWebshop ?? (await Webshop.getByID(this.webshopId)))?.setRelation(Webshop.organization, organization);
|
|
694
|
+
if (!webshop) {
|
|
695
|
+
console.error("Missing webshop for order "+this.id)
|
|
696
|
+
return
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (this.status === OrderStatus.Deleted) {
|
|
700
|
+
await this.undoPaymentFailed(payment, organization)
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const { tickets, didCreateTickets } = await this.setRelation(Order.webshop, webshop).updateTickets()
|
|
704
|
+
|
|
705
|
+
// Needs to happen before validation, because we can include the tickets in the validation that way
|
|
706
|
+
if (this.validAt === null) {
|
|
707
|
+
await this.setRelation(Order.webshop, webshop).markValid(payment, tickets)
|
|
708
|
+
} else {
|
|
709
|
+
if (!this.data.shouldSendPaymentUpdates) {
|
|
710
|
+
console.log("Skip sending paid email for order "+this.id)
|
|
711
|
+
return
|
|
712
|
+
}
|
|
713
|
+
if (this.data.customer.email.length > 0){
|
|
714
|
+
if (didCreateTickets) {
|
|
715
|
+
await this.setRelation(Order.webshop, webshop).sendTickets()
|
|
716
|
+
} else {
|
|
717
|
+
if (payment && payment.method === PaymentMethod.Transfer) {
|
|
718
|
+
await this.setRelation(Order.webshop, webshop).sendPaidMail()
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
async sendPaidMail(this: Order & { webshop: Webshop & { organization: Organization } }) {
|
|
726
|
+
const organization = this.webshop.organization
|
|
727
|
+
const { from, replyTo } = organization.getEmail(this.webshop.privateMeta.defaultEmailId, true)
|
|
728
|
+
|
|
729
|
+
await this.sendEmailTemplate({
|
|
730
|
+
type: EmailTemplateType.OrderReceivedTransfer,
|
|
731
|
+
from,
|
|
732
|
+
replyTo
|
|
733
|
+
})
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
async sendTickets(this: Order & { webshop: Webshop & { organization: Organization } }) {
|
|
737
|
+
const organization = this.webshop.organization
|
|
738
|
+
const { from, replyTo } = organization.getEmail(this.webshop.privateMeta.defaultEmailId, true)
|
|
739
|
+
|
|
740
|
+
await this.sendEmailTemplate({
|
|
741
|
+
type: EmailTemplateType.TicketsReceivedTransfer,
|
|
742
|
+
from,
|
|
743
|
+
replyTo
|
|
744
|
+
})
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
getStructureWithoutPayment() {
|
|
748
|
+
return OrderStruct.create({
|
|
749
|
+
id: this.id,
|
|
750
|
+
webshopId: this.webshopId,
|
|
751
|
+
number: this.number,
|
|
752
|
+
data: this.data,
|
|
753
|
+
status: this.status,
|
|
754
|
+
balanceItems: [],
|
|
755
|
+
createdAt: this.createdAt,
|
|
756
|
+
updatedAt: this.updatedAt,
|
|
757
|
+
validAt: this.validAt,
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
static async getStructures(orders: Order[]): Promise<OrderStruct[]> {
|
|
762
|
+
if (orders.length === 0) {
|
|
763
|
+
return []
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Balance items
|
|
767
|
+
const allBalanceItems = await BalanceItem.where({ orderId: {
|
|
768
|
+
sign: "IN",
|
|
769
|
+
value: orders.map(o => o.id)
|
|
770
|
+
} })
|
|
771
|
+
|
|
772
|
+
const {payments, balanceItemPayments} = await BalanceItem.loadPayments(allBalanceItems)
|
|
773
|
+
|
|
774
|
+
const structures: OrderStruct[] = []
|
|
775
|
+
for (const order of orders) {
|
|
776
|
+
const balanceItems = allBalanceItems.filter(b => b.orderId === order.id)
|
|
777
|
+
|
|
778
|
+
const balanceItemStructures = balanceItems.map((balanceItem) => {
|
|
779
|
+
return BalanceItemWithPayments.create({
|
|
780
|
+
...balanceItem,
|
|
781
|
+
payments: balanceItemPayments.filter(b => b.balanceItemId === balanceItem.id).map(balanceItemPayment => {
|
|
782
|
+
const payment = payments.find(pp => pp.id === balanceItemPayment.paymentId)!
|
|
783
|
+
return MemberBalanceItemPayment.create({
|
|
784
|
+
...balanceItemPayment,
|
|
785
|
+
payment: PaymentStruct.create(payment)
|
|
786
|
+
})
|
|
787
|
+
})
|
|
788
|
+
});
|
|
789
|
+
})
|
|
790
|
+
|
|
791
|
+
structures.push(
|
|
792
|
+
OrderStruct.create({
|
|
793
|
+
...order,
|
|
794
|
+
balanceItems: balanceItemStructures,
|
|
795
|
+
// Compatibility
|
|
796
|
+
payment: balanceItemStructures[0]?.payments[0]?.payment ?? null
|
|
797
|
+
})
|
|
798
|
+
)
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
return structures
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
static async getPrivateStructures(orders: Order[]): Promise<PrivateOrder[]> {
|
|
805
|
+
if (orders.length === 0) {
|
|
806
|
+
return []
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// Balance items
|
|
810
|
+
const allBalanceItems = await BalanceItem.where({ orderId: {
|
|
811
|
+
sign: "IN",
|
|
812
|
+
value: orders.map(o => o.id)
|
|
813
|
+
} })
|
|
814
|
+
|
|
815
|
+
const {payments, balanceItemPayments} = await BalanceItem.loadPayments(allBalanceItems)
|
|
816
|
+
|
|
817
|
+
const structures: PrivateOrder[] = []
|
|
818
|
+
for (const order of orders) {
|
|
819
|
+
const balanceItems = allBalanceItems.filter(b => b.orderId === order.id)
|
|
820
|
+
|
|
821
|
+
const balanceItemStructures = balanceItems.map((balanceItem) => {
|
|
822
|
+
return BalanceItemWithPrivatePayments.create({
|
|
823
|
+
...balanceItem,
|
|
824
|
+
payments: balanceItemPayments.filter(b => b.balanceItemId === balanceItem.id).map(balanceItemPayment => {
|
|
825
|
+
const payment = payments.find(pp => pp.id === balanceItemPayment.paymentId)!
|
|
826
|
+
return BalanceItemPaymentWithPrivatePayment.create({
|
|
827
|
+
...balanceItemPayment,
|
|
828
|
+
payment: PrivatePayment.create(payment)
|
|
829
|
+
})
|
|
830
|
+
})
|
|
831
|
+
});
|
|
832
|
+
})
|
|
833
|
+
|
|
834
|
+
structures.push(
|
|
835
|
+
PrivateOrder.create({
|
|
836
|
+
...order,
|
|
837
|
+
balanceItems: balanceItemStructures,
|
|
838
|
+
// Compatibility
|
|
839
|
+
payment: balanceItemStructures[0]?.payments[0]?.payment ?? null
|
|
840
|
+
})
|
|
841
|
+
)
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
return structures
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
async getStructure() {
|
|
849
|
+
return (await Order.getStructures([this]))[0]
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
async sendEmailTemplate(this: Order & { webshop: Webshop & { organization: Organization } }, data: {
|
|
853
|
+
type: EmailTemplateType,
|
|
854
|
+
from: string,
|
|
855
|
+
replyTo?: string,
|
|
856
|
+
to?: Recipient,
|
|
857
|
+
}) {
|
|
858
|
+
// Never send an email for archived webshops
|
|
859
|
+
if (this.webshop.meta.status === WebshopStatus.Archived) {
|
|
860
|
+
return
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// First fetch template
|
|
864
|
+
let templates = (await EmailTemplate.where({ type: data.type, webshopId: this.webshop.id }))
|
|
865
|
+
|
|
866
|
+
if (templates.length == 0) {
|
|
867
|
+
templates = (await EmailTemplate.where({ type: data.type, organizationId: null }))
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (templates.length == 0) {
|
|
871
|
+
console.error("Could not find email template for type "+data.type)
|
|
872
|
+
return
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
const template = templates[0]
|
|
876
|
+
|
|
877
|
+
let recipient = (await this.getStructure()).getRecipient(
|
|
878
|
+
await this.webshop.organization.getStructure({emptyGroups: true}),
|
|
879
|
+
WebshopPreview.create(this.webshop)
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
if (data.to) {
|
|
883
|
+
// Clear first and last name
|
|
884
|
+
recipient.firstName = null;
|
|
885
|
+
recipient.lastName = null;
|
|
886
|
+
recipient.replacements = recipient.replacements.filter(r => !['firstName', 'lastName'].includes(r.token))
|
|
887
|
+
data.to.merge(recipient);
|
|
888
|
+
recipient = data.to
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// Create e-mail builder
|
|
892
|
+
const builder = await getEmailBuilder(this.webshop.organization, {
|
|
893
|
+
recipients: [recipient],
|
|
894
|
+
subject: template.subject,
|
|
895
|
+
html: template.html,
|
|
896
|
+
// text: template.text,
|
|
897
|
+
from: data.from,
|
|
898
|
+
replyTo: data.replyTo,
|
|
899
|
+
type: 'transactional',
|
|
900
|
+
defaultReplacements: this.webshop.meta.getEmailReplacements()
|
|
901
|
+
})
|
|
902
|
+
|
|
903
|
+
Email.schedule(builder)
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* WARNING: this should always run inside a queue so it only runs once for the same orde
|
|
908
|
+
* Include any tickets that are generated and should be included in the e-mail
|
|
909
|
+
*/
|
|
910
|
+
async markValid(this: Order & { webshop: Webshop & { organization: Organization } }, payment: Payment | null, tickets: Ticket[]) {
|
|
911
|
+
const webshop = this.webshop
|
|
912
|
+
const organization = webshop.organization
|
|
913
|
+
|
|
914
|
+
console.log("Marking as valid: order "+this.id)
|
|
915
|
+
const wasValid = this.validAt !== null
|
|
916
|
+
|
|
917
|
+
if (wasValid) {
|
|
918
|
+
console.warn("Warning: already validated an order")
|
|
919
|
+
return
|
|
920
|
+
}
|
|
921
|
+
this.validAt = new Date() // will get flattened AFTER calculations
|
|
922
|
+
this.validAt.setMilliseconds(0)
|
|
923
|
+
this.number = await WebshopCounter.getNextNumber(this.webshopId, this.webshop.privateMeta.numberingType)
|
|
924
|
+
|
|
925
|
+
if (payment && !Order.payment.isLoaded(this)) {
|
|
926
|
+
this.setRelation(Order.payment, payment)
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// Now we have a number, update the payment
|
|
930
|
+
if (payment && payment.method === PaymentMethod.Transfer) {
|
|
931
|
+
// Only now we can update the transfer description, since we need the order number as a reference
|
|
932
|
+
payment.transferSettings = webshop.meta.transferSettings.fillMissing(organization.mappedTransferSettings)
|
|
933
|
+
|
|
934
|
+
if (!payment.transferSettings.iban) {
|
|
935
|
+
throw new SimpleError({
|
|
936
|
+
code: "missing_iban",
|
|
937
|
+
message: "Missing IBAN",
|
|
938
|
+
human: "Er is geen rekeningnummer ingesteld voor overschrijvingen. Contacteer de beheerder."
|
|
939
|
+
})
|
|
940
|
+
}
|
|
941
|
+
payment.generateDescription(organization, this.number.toString(), this.getTransferReplacements())
|
|
942
|
+
await payment.save();
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
await this.save()
|
|
946
|
+
|
|
947
|
+
if (this.data.customer.email.length > 0) {
|
|
948
|
+
const webshop = this.webshop
|
|
949
|
+
const organization = webshop.organization
|
|
950
|
+
|
|
951
|
+
const { from, replyTo } = organization.getEmail(webshop.privateMeta.defaultEmailId, true)
|
|
952
|
+
|
|
953
|
+
if (tickets.length > 0) {
|
|
954
|
+
// Also send a copy
|
|
955
|
+
if (payment && payment.method === PaymentMethod.PointOfSale) {
|
|
956
|
+
await this.sendEmailTemplate({
|
|
957
|
+
type: EmailTemplateType.TicketsConfirmationPOS,
|
|
958
|
+
from,
|
|
959
|
+
replyTo
|
|
960
|
+
})
|
|
961
|
+
} else {
|
|
962
|
+
await this.sendEmailTemplate({
|
|
963
|
+
type: EmailTemplateType.TicketsConfirmation,
|
|
964
|
+
from,
|
|
965
|
+
replyTo
|
|
966
|
+
})
|
|
967
|
+
}
|
|
968
|
+
} else {
|
|
969
|
+
if (this.webshop.meta.ticketType === WebshopTicketType.None) {
|
|
970
|
+
|
|
971
|
+
if (payment && payment.method === PaymentMethod.Transfer) {
|
|
972
|
+
// Also send a copy
|
|
973
|
+
await this.sendEmailTemplate({
|
|
974
|
+
type: EmailTemplateType.OrderConfirmationTransfer,
|
|
975
|
+
from,
|
|
976
|
+
replyTo
|
|
977
|
+
})
|
|
978
|
+
} else if (payment && payment.method === PaymentMethod.PointOfSale) {
|
|
979
|
+
await this.sendEmailTemplate({
|
|
980
|
+
type: EmailTemplateType.OrderConfirmationPOS,
|
|
981
|
+
from,
|
|
982
|
+
replyTo
|
|
983
|
+
})
|
|
984
|
+
} else {
|
|
985
|
+
// Also send a copy
|
|
986
|
+
await this.sendEmailTemplate({
|
|
987
|
+
type: EmailTemplateType.OrderConfirmationOnline,
|
|
988
|
+
from,
|
|
989
|
+
replyTo
|
|
990
|
+
})
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
} else {
|
|
994
|
+
await this.sendEmailTemplate({
|
|
995
|
+
type: EmailTemplateType.TicketsConfirmationTransfer,
|
|
996
|
+
from,
|
|
997
|
+
replyTo
|
|
998
|
+
})
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
if (this.webshop.privateMeta.notificationEmails) {
|
|
1004
|
+
const webshop = this.webshop
|
|
1005
|
+
const organization = webshop.organization
|
|
1006
|
+
const { from, replyTo } = organization.getEmail(webshop.privateMeta.defaultEmailId, true)
|
|
1007
|
+
const i18n = organization.i18n;
|
|
1008
|
+
|
|
1009
|
+
const webshopDashboardUrl = "https://"+(STAMHOOFD.domains.dashboard ?? "stamhoofd.app")+"/"+i18n.locale + '/webshops/'+Formatter.slug(webshop.meta.name)+'/orders';
|
|
1010
|
+
|
|
1011
|
+
// Send an email to all these notification emails
|
|
1012
|
+
for (const email of this.webshop.privateMeta.notificationEmails) {
|
|
1013
|
+
await this.sendEmailTemplate({
|
|
1014
|
+
type: EmailTemplateType.OrderNotification,
|
|
1015
|
+
from,
|
|
1016
|
+
replyTo,
|
|
1017
|
+
to: Recipient.create({
|
|
1018
|
+
email,
|
|
1019
|
+
replacements: [
|
|
1020
|
+
Replacement.create({
|
|
1021
|
+
token: 'orderUrl',
|
|
1022
|
+
value: webshopDashboardUrl
|
|
1023
|
+
})
|
|
1024
|
+
]
|
|
1025
|
+
})
|
|
1026
|
+
})
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|