@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.
Files changed (481) hide show
  1. package/dist/src/assets/Metropolis-Black.woff2 +0 -0
  2. package/dist/src/assets/Metropolis-BlackItalic.woff2 +0 -0
  3. package/dist/src/assets/Metropolis-Bold.woff2 +0 -0
  4. package/dist/src/assets/Metropolis-BoldItalic.woff2 +0 -0
  5. package/dist/src/assets/Metropolis-ExtraBold.woff2 +0 -0
  6. package/dist/src/assets/Metropolis-ExtraBoldItalic.woff2 +0 -0
  7. package/dist/src/assets/Metropolis-ExtraLight.woff2 +0 -0
  8. package/dist/src/assets/Metropolis-ExtraLightItalic.woff2 +0 -0
  9. package/dist/src/assets/Metropolis-Light.woff2 +0 -0
  10. package/dist/src/assets/Metropolis-LightItalic.woff2 +0 -0
  11. package/dist/src/assets/Metropolis-Medium.woff2 +0 -0
  12. package/dist/src/assets/Metropolis-MediumItalic.woff2 +0 -0
  13. package/dist/src/assets/Metropolis-Regular.woff2 +0 -0
  14. package/dist/src/assets/Metropolis-RegularItalic.woff2 +0 -0
  15. package/dist/src/assets/Metropolis-SemiBold.woff2 +0 -0
  16. package/dist/src/assets/Metropolis-SemiBoldItalic.woff2 +0 -0
  17. package/dist/src/assets/Metropolis-Thin.woff2 +0 -0
  18. package/dist/src/assets/Metropolis-ThinItalic.woff2 +0 -0
  19. package/dist/src/assets/assets/Metropolis-Black.woff2 +0 -0
  20. package/dist/src/assets/assets/Metropolis-BlackItalic.woff2 +0 -0
  21. package/dist/src/assets/assets/Metropolis-Bold.woff2 +0 -0
  22. package/dist/src/assets/assets/Metropolis-BoldItalic.woff2 +0 -0
  23. package/dist/src/assets/assets/Metropolis-ExtraBold.woff2 +0 -0
  24. package/dist/src/assets/assets/Metropolis-ExtraBoldItalic.woff2 +0 -0
  25. package/dist/src/assets/assets/Metropolis-ExtraLight.woff2 +0 -0
  26. package/dist/src/assets/assets/Metropolis-ExtraLightItalic.woff2 +0 -0
  27. package/dist/src/assets/assets/Metropolis-Light.woff2 +0 -0
  28. package/dist/src/assets/assets/Metropolis-LightItalic.woff2 +0 -0
  29. package/dist/src/assets/assets/Metropolis-Medium.woff2 +0 -0
  30. package/dist/src/assets/assets/Metropolis-MediumItalic.woff2 +0 -0
  31. package/dist/src/assets/assets/Metropolis-Regular.woff2 +0 -0
  32. package/dist/src/assets/assets/Metropolis-RegularItalic.woff2 +0 -0
  33. package/dist/src/assets/assets/Metropolis-SemiBold.woff2 +0 -0
  34. package/dist/src/assets/assets/Metropolis-SemiBoldItalic.woff2 +0 -0
  35. package/dist/src/assets/assets/Metropolis-Thin.woff2 +0 -0
  36. package/dist/src/assets/assets/Metropolis-ThinItalic.woff2 +0 -0
  37. package/dist/src/assets/assets/logo.png +0 -0
  38. package/dist/src/assets/logo.png +0 -0
  39. package/dist/src/factories/AddressFactory.d.ts +10 -0
  40. package/dist/src/factories/AddressFactory.d.ts.map +1 -0
  41. package/dist/src/factories/AddressFactory.js +42 -0
  42. package/dist/src/factories/AddressFactory.js.map +1 -0
  43. package/dist/src/factories/EmergencyContactFactory.d.ts +9 -0
  44. package/dist/src/factories/EmergencyContactFactory.d.ts.map +1 -0
  45. package/dist/src/factories/EmergencyContactFactory.js +42 -0
  46. package/dist/src/factories/EmergencyContactFactory.js.map +1 -0
  47. package/dist/src/factories/GroupFactory.d.ts +19 -0
  48. package/dist/src/factories/GroupFactory.d.ts.map +1 -0
  49. package/dist/src/factories/GroupFactory.js +52 -0
  50. package/dist/src/factories/GroupFactory.js.map +1 -0
  51. package/dist/src/factories/MemberFactory.d.ts +16 -0
  52. package/dist/src/factories/MemberFactory.d.ts.map +1 -0
  53. package/dist/src/factories/MemberFactory.js +98 -0
  54. package/dist/src/factories/MemberFactory.js.map +1 -0
  55. package/dist/src/factories/OrganizationFactory.d.ts +16 -0
  56. package/dist/src/factories/OrganizationFactory.d.ts.map +1 -0
  57. package/dist/src/factories/OrganizationFactory.js +40 -0
  58. package/dist/src/factories/OrganizationFactory.js.map +1 -0
  59. package/dist/src/factories/ParentFactory.d.ts +10 -0
  60. package/dist/src/factories/ParentFactory.d.ts.map +1 -0
  61. package/dist/src/factories/ParentFactory.js +44 -0
  62. package/dist/src/factories/ParentFactory.js.map +1 -0
  63. package/dist/src/factories/RecordFactory.d.ts +10 -0
  64. package/dist/src/factories/RecordFactory.d.ts.map +1 -0
  65. package/dist/src/factories/RecordFactory.js +14 -0
  66. package/dist/src/factories/RecordFactory.js.map +1 -0
  67. package/dist/src/factories/RegisterCodeFactory.d.ts +18 -0
  68. package/dist/src/factories/RegisterCodeFactory.d.ts.map +1 -0
  69. package/dist/src/factories/RegisterCodeFactory.js +19 -0
  70. package/dist/src/factories/RegisterCodeFactory.js.map +1 -0
  71. package/dist/src/factories/RegistrationFactory.d.ts +14 -0
  72. package/dist/src/factories/RegistrationFactory.d.ts.map +1 -0
  73. package/dist/src/factories/RegistrationFactory.js +26 -0
  74. package/dist/src/factories/RegistrationFactory.js.map +1 -0
  75. package/dist/src/factories/UserFactory.d.ts +21 -0
  76. package/dist/src/factories/UserFactory.d.ts.map +1 -0
  77. package/dist/src/factories/UserFactory.js +41 -0
  78. package/dist/src/factories/UserFactory.js.map +1 -0
  79. package/dist/src/factories/WebshopFactory.d.ts +16 -0
  80. package/dist/src/factories/WebshopFactory.d.ts.map +1 -0
  81. package/dist/src/factories/WebshopFactory.js +35 -0
  82. package/dist/src/factories/WebshopFactory.js.map +1 -0
  83. package/dist/src/helpers/DNSValidator.d.ts +6 -0
  84. package/dist/src/helpers/DNSValidator.d.ts.map +1 -0
  85. package/dist/src/helpers/DNSValidator.js +144 -0
  86. package/dist/src/helpers/DNSValidator.js.map +1 -0
  87. package/dist/src/helpers/EmailBuilder.d.ts +22 -0
  88. package/dist/src/helpers/EmailBuilder.d.ts.map +1 -0
  89. package/dist/src/helpers/EmailBuilder.js +100 -0
  90. package/dist/src/helpers/EmailBuilder.js.map +1 -0
  91. package/dist/src/helpers/GroupBuilder.d.ts +9 -0
  92. package/dist/src/helpers/GroupBuilder.d.ts.map +1 -0
  93. package/dist/src/helpers/GroupBuilder.js +382 -0
  94. package/dist/src/helpers/GroupBuilder.js.map +1 -0
  95. package/dist/src/helpers/Handlebars.d.ts +2 -0
  96. package/dist/src/helpers/Handlebars.d.ts.map +1 -0
  97. package/dist/src/helpers/Handlebars.js +192 -0
  98. package/dist/src/helpers/Handlebars.js.map +1 -0
  99. package/dist/src/helpers/InvoiceBuilder.d.ts +29 -0
  100. package/dist/src/helpers/InvoiceBuilder.d.ts.map +1 -0
  101. package/dist/src/helpers/InvoiceBuilder.js +406 -0
  102. package/dist/src/helpers/InvoiceBuilder.js.map +1 -0
  103. package/dist/src/helpers/InvoiceBuilder.test.d.ts +2 -0
  104. package/dist/src/helpers/InvoiceBuilder.test.d.ts.map +1 -0
  105. package/dist/src/helpers/InvoiceBuilder.test.js +52 -0
  106. package/dist/src/helpers/InvoiceBuilder.test.js.map +1 -0
  107. package/dist/src/helpers/RateLimiter.d.ts +27 -0
  108. package/dist/src/helpers/RateLimiter.d.ts.map +1 -0
  109. package/dist/src/helpers/RateLimiter.js +57 -0
  110. package/dist/src/helpers/RateLimiter.js.map +1 -0
  111. package/dist/src/helpers/WebshopCounter.d.ts +6 -0
  112. package/dist/src/helpers/WebshopCounter.d.ts.map +1 -0
  113. package/dist/src/helpers/WebshopCounter.js +36 -0
  114. package/dist/src/helpers/WebshopCounter.js.map +1 -0
  115. package/dist/src/helpers/WebshopCounter.test.d.ts +2 -0
  116. package/dist/src/helpers/WebshopCounter.test.d.ts.map +1 -0
  117. package/dist/src/helpers/WebshopCounter.test.js +17 -0
  118. package/dist/src/helpers/WebshopCounter.test.js.map +1 -0
  119. package/dist/src/index.d.ts +18 -0
  120. package/dist/src/index.d.ts.map +1 -0
  121. package/dist/src/index.js +23 -0
  122. package/dist/src/index.js.map +1 -0
  123. package/dist/src/migrations/1593773929-create-initial-tables.sql +634 -0
  124. package/dist/src/migrations/1605261999-gemeenten-tmp.sql +2820 -0
  125. package/dist/src/migrations/1605262045-import-postcodes.d.ts +16 -0
  126. package/dist/src/migrations/1605262045-import-postcodes.d.ts.map +1 -0
  127. package/dist/src/migrations/1605262045-import-postcodes.js +116 -0
  128. package/dist/src/migrations/1605262045-import-postcodes.js.map +1 -0
  129. package/dist/src/migrations/1605262046-import-postcodes-nl.d.ts +4 -0
  130. package/dist/src/migrations/1605262046-import-postcodes-nl.d.ts.map +1 -0
  131. package/dist/src/migrations/1605262046-import-postcodes-nl.js +83 -0
  132. package/dist/src/migrations/1605262046-import-postcodes-nl.js.map +1 -0
  133. package/dist/src/migrations/1605279038-drop-gemeenten-tmp.sql +1 -0
  134. package/dist/src/migrations/1648392491-default-templates.sql +9 -0
  135. package/dist/src/migrations/1651245707-default-templates-reminders.sql +6 -0
  136. package/dist/src/migrations/1708607340-tickets-deleted-at.sql +1 -0
  137. package/dist/src/migrations/1710459176-register-code-invoices.sql +3 -0
  138. package/dist/src/migrations/1712158247-discount-codes.sql +17 -0
  139. package/dist/src/migrations/1713178665-drop-invites.sql +1 -0
  140. package/dist/src/migrations/1713178666-drop-keychain.sql +1 -0
  141. package/dist/src/migrations/1713178667-drop-challenges.sql +1 -0
  142. package/dist/src/migrations/1713178668-drop-user-keys.sql +7 -0
  143. package/dist/src/migrations/1713178669-drop-organization-key.sql +2 -0
  144. package/dist/src/migrations/data/postcodes/nl/Drenthe +291 -0
  145. package/dist/src/migrations/data/postcodes/nl/Flevoland +107 -0
  146. package/dist/src/migrations/data/postcodes/nl/Friesland +518 -0
  147. package/dist/src/migrations/data/postcodes/nl/Gelderland +601 -0
  148. package/dist/src/migrations/data/postcodes/nl/Groningen +279 -0
  149. package/dist/src/migrations/data/postcodes/nl/Limburg +324 -0
  150. package/dist/src/migrations/data/postcodes/nl/Noord-Brabant +620 -0
  151. package/dist/src/migrations/data/postcodes/nl/Noord-Holland +566 -0
  152. package/dist/src/migrations/data/postcodes/nl/Overrijsel +344 -0
  153. package/dist/src/migrations/data/postcodes/nl/Utrecht +278 -0
  154. package/dist/src/migrations/data/postcodes/nl/Zeeland +179 -0
  155. package/dist/src/migrations/data/postcodes/nl/Zuid-Holland +662 -0
  156. package/dist/src/models/BalanceItem.d.ts +57 -0
  157. package/dist/src/models/BalanceItem.d.ts.map +1 -0
  158. package/dist/src/models/BalanceItem.js +346 -0
  159. package/dist/src/models/BalanceItem.js.map +1 -0
  160. package/dist/src/models/BalanceItemPayment.d.ts +31 -0
  161. package/dist/src/models/BalanceItemPayment.d.ts.map +1 -0
  162. package/dist/src/models/BalanceItemPayment.js +101 -0
  163. package/dist/src/models/BalanceItemPayment.js.map +1 -0
  164. package/dist/src/models/BuckarooPayment.d.ts +8 -0
  165. package/dist/src/models/BuckarooPayment.d.ts.map +1 -0
  166. package/dist/src/models/BuckarooPayment.js +24 -0
  167. package/dist/src/models/BuckarooPayment.js.map +1 -0
  168. package/dist/src/models/Document.d.ts +44 -0
  169. package/dist/src/models/Document.d.ts.map +1 -0
  170. package/dist/src/models/Document.js +194 -0
  171. package/dist/src/models/Document.js.map +1 -0
  172. package/dist/src/models/DocumentTemplate.d.ts +45 -0
  173. package/dist/src/models/DocumentTemplate.d.ts.map +1 -0
  174. package/dist/src/models/DocumentTemplate.js +533 -0
  175. package/dist/src/models/DocumentTemplate.js.map +1 -0
  176. package/dist/src/models/EmailTemplate.d.ts +22 -0
  177. package/dist/src/models/EmailTemplate.d.ts.map +1 -0
  178. package/dist/src/models/EmailTemplate.js +70 -0
  179. package/dist/src/models/EmailTemplate.js.map +1 -0
  180. package/dist/src/models/EmailVerificationCode.d.ts +60 -0
  181. package/dist/src/models/EmailVerificationCode.d.ts.map +1 -0
  182. package/dist/src/models/EmailVerificationCode.js +307 -0
  183. package/dist/src/models/EmailVerificationCode.js.map +1 -0
  184. package/dist/src/models/Group.d.ts +36 -0
  185. package/dist/src/models/Group.d.ts.map +1 -0
  186. package/dist/src/models/Group.js +231 -0
  187. package/dist/src/models/Group.js.map +1 -0
  188. package/dist/src/models/Image.d.ts +12 -0
  189. package/dist/src/models/Image.d.ts.map +1 -0
  190. package/dist/src/models/Image.js +137 -0
  191. package/dist/src/models/Image.js.map +1 -0
  192. package/dist/src/models/Member.d.ts +66 -0
  193. package/dist/src/models/Member.d.ts.map +1 -0
  194. package/dist/src/models/Member.js +309 -0
  195. package/dist/src/models/Member.js.map +1 -0
  196. package/dist/src/models/MemberResponsibilityRecord.d.ts +11 -0
  197. package/dist/src/models/MemberResponsibilityRecord.d.ts.map +1 -0
  198. package/dist/src/models/MemberResponsibilityRecord.js +47 -0
  199. package/dist/src/models/MemberResponsibilityRecord.js.map +1 -0
  200. package/dist/src/models/MolliePayment.d.ts +8 -0
  201. package/dist/src/models/MolliePayment.d.ts.map +1 -0
  202. package/dist/src/models/MolliePayment.js +24 -0
  203. package/dist/src/models/MolliePayment.js.map +1 -0
  204. package/dist/src/models/MollieToken.d.ts +45 -0
  205. package/dist/src/models/MollieToken.d.ts.map +1 -0
  206. package/dist/src/models/MollieToken.js +333 -0
  207. package/dist/src/models/MollieToken.js.map +1 -0
  208. package/dist/src/models/OneTimeToken.d.ts +38 -0
  209. package/dist/src/models/OneTimeToken.d.ts.map +1 -0
  210. package/dist/src/models/OneTimeToken.js +126 -0
  211. package/dist/src/models/OneTimeToken.js.map +1 -0
  212. package/dist/src/models/Order.d.ts +99 -0
  213. package/dist/src/models/Order.d.ts.map +1 -0
  214. package/dist/src/models/Order.js +912 -0
  215. package/dist/src/models/Order.js.map +1 -0
  216. package/dist/src/models/Organization.d.ts +119 -0
  217. package/dist/src/models/Organization.d.ts.map +1 -0
  218. package/dist/src/models/Organization.js +900 -0
  219. package/dist/src/models/Organization.js.map +1 -0
  220. package/dist/src/models/OrganizationRegistrationPeriod.d.ts +14 -0
  221. package/dist/src/models/OrganizationRegistrationPeriod.d.ts.map +1 -0
  222. package/dist/src/models/OrganizationRegistrationPeriod.js +62 -0
  223. package/dist/src/models/OrganizationRegistrationPeriod.js.map +1 -0
  224. package/dist/src/models/PasswordToken.d.ts +29 -0
  225. package/dist/src/models/PasswordToken.d.ts.map +1 -0
  226. package/dist/src/models/PasswordToken.js +118 -0
  227. package/dist/src/models/PasswordToken.js.map +1 -0
  228. package/dist/src/models/PayconiqPayment.d.ts +18 -0
  229. package/dist/src/models/PayconiqPayment.d.ts.map +1 -0
  230. package/dist/src/models/PayconiqPayment.js +216 -0
  231. package/dist/src/models/PayconiqPayment.js.map +1 -0
  232. package/dist/src/models/Payment.d.ts +62 -0
  233. package/dist/src/models/Payment.d.ts.map +1 -0
  234. package/dist/src/models/Payment.js +199 -0
  235. package/dist/src/models/Payment.js.map +1 -0
  236. package/dist/src/models/Platform.d.ts +15 -0
  237. package/dist/src/models/Platform.d.ts.map +1 -0
  238. package/dist/src/models/Platform.js +77 -0
  239. package/dist/src/models/Platform.js.map +1 -0
  240. package/dist/src/models/RegisterCode.d.ts +27 -0
  241. package/dist/src/models/RegisterCode.d.ts.map +1 -0
  242. package/dist/src/models/RegisterCode.js +162 -0
  243. package/dist/src/models/RegisterCode.js.map +1 -0
  244. package/dist/src/models/Registration.d.ts +47 -0
  245. package/dist/src/models/Registration.d.ts.map +1 -0
  246. package/dist/src/models/Registration.js +369 -0
  247. package/dist/src/models/Registration.js.map +1 -0
  248. package/dist/src/models/RegistrationPeriod.d.ts +15 -0
  249. package/dist/src/models/RegistrationPeriod.d.ts.map +1 -0
  250. package/dist/src/models/RegistrationPeriod.js +64 -0
  251. package/dist/src/models/RegistrationPeriod.js.map +1 -0
  252. package/dist/src/models/STCredit.d.ts +20 -0
  253. package/dist/src/models/STCredit.d.ts.map +1 -0
  254. package/dist/src/models/STCredit.js +129 -0
  255. package/dist/src/models/STCredit.js.map +1 -0
  256. package/dist/src/models/STInvoice.d.ts +51 -0
  257. package/dist/src/models/STInvoice.d.ts.map +1 -0
  258. package/dist/src/models/STInvoice.js +453 -0
  259. package/dist/src/models/STInvoice.js.map +1 -0
  260. package/dist/src/models/STPackage.d.ts +36 -0
  261. package/dist/src/models/STPackage.d.ts.map +1 -0
  262. package/dist/src/models/STPackage.js +300 -0
  263. package/dist/src/models/STPackage.js.map +1 -0
  264. package/dist/src/models/STPendingInvoice.d.ts +45 -0
  265. package/dist/src/models/STPendingInvoice.d.ts.map +1 -0
  266. package/dist/src/models/STPendingInvoice.js +284 -0
  267. package/dist/src/models/STPendingInvoice.js.map +1 -0
  268. package/dist/src/models/StripeAccount.d.ts +17 -0
  269. package/dist/src/models/StripeAccount.d.ts.map +1 -0
  270. package/dist/src/models/StripeAccount.js +81 -0
  271. package/dist/src/models/StripeAccount.js.map +1 -0
  272. package/dist/src/models/StripeCheckoutSession.d.ts +9 -0
  273. package/dist/src/models/StripeCheckoutSession.d.ts.map +1 -0
  274. package/dist/src/models/StripeCheckoutSession.js +31 -0
  275. package/dist/src/models/StripeCheckoutSession.js.map +1 -0
  276. package/dist/src/models/StripePaymentIntent.d.ts +9 -0
  277. package/dist/src/models/StripePaymentIntent.d.ts.map +1 -0
  278. package/dist/src/models/StripePaymentIntent.js +31 -0
  279. package/dist/src/models/StripePaymentIntent.js.map +1 -0
  280. package/dist/src/models/Ticket.d.ts +61 -0
  281. package/dist/src/models/Ticket.d.ts.map +1 -0
  282. package/dist/src/models/Ticket.js +143 -0
  283. package/dist/src/models/Ticket.js.map +1 -0
  284. package/dist/src/models/Token.d.ts +49 -0
  285. package/dist/src/models/Token.d.ts.map +1 -0
  286. package/dist/src/models/Token.js +218 -0
  287. package/dist/src/models/Token.js.map +1 -0
  288. package/dist/src/models/Token.test.d.ts +2 -0
  289. package/dist/src/models/Token.test.d.ts.map +1 -0
  290. package/dist/src/models/Token.test.js +60 -0
  291. package/dist/src/models/Token.test.js.map +1 -0
  292. package/dist/src/models/UsedRegisterCode.d.ts +22 -0
  293. package/dist/src/models/UsedRegisterCode.d.ts.map +1 -0
  294. package/dist/src/models/UsedRegisterCode.js +158 -0
  295. package/dist/src/models/UsedRegisterCode.js.map +1 -0
  296. package/dist/src/models/User.d.ts +55 -0
  297. package/dist/src/models/User.d.ts.map +1 -0
  298. package/dist/src/models/User.js +314 -0
  299. package/dist/src/models/User.js.map +1 -0
  300. package/dist/src/models/UserPermissions.d.ts +15 -0
  301. package/dist/src/models/UserPermissions.d.ts.map +1 -0
  302. package/dist/src/models/UserPermissions.js +57 -0
  303. package/dist/src/models/UserPermissions.js.map +1 -0
  304. package/dist/src/models/Webshop.d.ts +44 -0
  305. package/dist/src/models/Webshop.d.ts.map +1 -0
  306. package/dist/src/models/Webshop.js +184 -0
  307. package/dist/src/models/Webshop.js.map +1 -0
  308. package/dist/src/models/WebshopDiscountCode.d.ts +18 -0
  309. package/dist/src/models/WebshopDiscountCode.d.ts.map +1 -0
  310. package/dist/src/models/WebshopDiscountCode.js +88 -0
  311. package/dist/src/models/WebshopDiscountCode.js.map +1 -0
  312. package/dist/src/models/addresses/City.d.ts +14 -0
  313. package/dist/src/models/addresses/City.d.ts.map +1 -0
  314. package/dist/src/models/addresses/City.js +37 -0
  315. package/dist/src/models/addresses/City.js.map +1 -0
  316. package/dist/src/models/addresses/PostalCode.d.ts +20 -0
  317. package/dist/src/models/addresses/PostalCode.d.ts.map +1 -0
  318. package/dist/src/models/addresses/PostalCode.js +138 -0
  319. package/dist/src/models/addresses/PostalCode.js.map +1 -0
  320. package/dist/src/models/addresses/PostalCode.test.d.ts +2 -0
  321. package/dist/src/models/addresses/PostalCode.test.d.ts.map +1 -0
  322. package/dist/src/models/addresses/PostalCode.test.js +98 -0
  323. package/dist/src/models/addresses/PostalCode.test.js.map +1 -0
  324. package/dist/src/models/addresses/Province.d.ts +9 -0
  325. package/dist/src/models/addresses/Province.d.ts.map +1 -0
  326. package/dist/src/models/addresses/Province.js +24 -0
  327. package/dist/src/models/addresses/Province.js.map +1 -0
  328. package/dist/src/models/addresses/Street.d.ts +10 -0
  329. package/dist/src/models/addresses/Street.d.ts.map +1 -0
  330. package/dist/src/models/addresses/Street.js +26 -0
  331. package/dist/src/models/addresses/Street.js.map +1 -0
  332. package/dist/src/models/index.d.ts +37 -0
  333. package/dist/src/models/index.d.ts.map +1 -0
  334. package/dist/src/models/index.js +53 -0
  335. package/dist/src/models/index.js.map +1 -0
  336. package/dist/src/structures/OrganizationServerMetaData.d.ts +43 -0
  337. package/dist/src/structures/OrganizationServerMetaData.d.ts.map +1 -0
  338. package/dist/src/structures/OrganizationServerMetaData.js +128 -0
  339. package/dist/src/structures/OrganizationServerMetaData.js.map +1 -0
  340. package/dist/tests/jest.global.setup.d.ts +3 -0
  341. package/dist/tests/jest.global.setup.d.ts.map +1 -0
  342. package/dist/tests/jest.global.setup.js +20 -0
  343. package/dist/tests/jest.global.setup.js.map +1 -0
  344. package/dist/tests/jest.setup.d.ts +2 -0
  345. package/dist/tests/jest.setup.d.ts.map +1 -0
  346. package/dist/tests/jest.setup.js +15 -0
  347. package/dist/tests/jest.setup.js.map +1 -0
  348. package/package.json +30 -0
  349. package/src/assets/Metropolis-Black.woff2 +0 -0
  350. package/src/assets/Metropolis-BlackItalic.woff2 +0 -0
  351. package/src/assets/Metropolis-Bold.woff2 +0 -0
  352. package/src/assets/Metropolis-BoldItalic.woff2 +0 -0
  353. package/src/assets/Metropolis-ExtraBold.woff2 +0 -0
  354. package/src/assets/Metropolis-ExtraBoldItalic.woff2 +0 -0
  355. package/src/assets/Metropolis-ExtraLight.woff2 +0 -0
  356. package/src/assets/Metropolis-ExtraLightItalic.woff2 +0 -0
  357. package/src/assets/Metropolis-Light.woff2 +0 -0
  358. package/src/assets/Metropolis-LightItalic.woff2 +0 -0
  359. package/src/assets/Metropolis-Medium.woff2 +0 -0
  360. package/src/assets/Metropolis-MediumItalic.woff2 +0 -0
  361. package/src/assets/Metropolis-Regular.woff2 +0 -0
  362. package/src/assets/Metropolis-RegularItalic.woff2 +0 -0
  363. package/src/assets/Metropolis-SemiBold.woff2 +0 -0
  364. package/src/assets/Metropolis-SemiBoldItalic.woff2 +0 -0
  365. package/src/assets/Metropolis-Thin.woff2 +0 -0
  366. package/src/assets/Metropolis-ThinItalic.woff2 +0 -0
  367. package/src/assets/logo.png +0 -0
  368. package/src/factories/AddressFactory.ts +42 -0
  369. package/src/factories/EmergencyContactFactory.ts +43 -0
  370. package/src/factories/GroupFactory.ts +66 -0
  371. package/src/factories/MemberFactory.ts +122 -0
  372. package/src/factories/OrganizationFactory.ts +45 -0
  373. package/src/factories/ParentFactory.ts +49 -0
  374. package/src/factories/RecordFactory.ts +12 -0
  375. package/src/factories/RegisterCodeFactory.ts +25 -0
  376. package/src/factories/RegistrationFactory.ts +32 -0
  377. package/src/factories/UserFactory.ts +66 -0
  378. package/src/factories/WebshopFactory.ts +43 -0
  379. package/src/helpers/DNSValidator.ts +153 -0
  380. package/src/helpers/EmailBuilder.ts +127 -0
  381. package/src/helpers/GroupBuilder.ts +438 -0
  382. package/src/helpers/Handlebars.ts +203 -0
  383. package/src/helpers/InvoiceBuilder.test.ts +57 -0
  384. package/src/helpers/InvoiceBuilder.ts +501 -0
  385. package/src/helpers/RateLimiter.ts +75 -0
  386. package/src/helpers/WebshopCounter.test.ts +16 -0
  387. package/src/helpers/WebshopCounter.ts +36 -0
  388. package/src/index.ts +21 -0
  389. package/src/migrations/1593773929-create-initial-tables.sql +634 -0
  390. package/src/migrations/1605261999-gemeenten-tmp.sql +2820 -0
  391. package/src/migrations/1605262045-import-postcodes.ts +132 -0
  392. package/src/migrations/1605262046-import-postcodes-nl.ts +97 -0
  393. package/src/migrations/1605279038-drop-gemeenten-tmp.sql +1 -0
  394. package/src/migrations/1648392491-default-templates.sql +9 -0
  395. package/src/migrations/1651245707-default-templates-reminders.sql +6 -0
  396. package/src/migrations/1708607340-tickets-deleted-at.sql +1 -0
  397. package/src/migrations/1710459176-register-code-invoices.sql +3 -0
  398. package/src/migrations/1712158247-discount-codes.sql +17 -0
  399. package/src/migrations/1713178665-drop-invites.sql +1 -0
  400. package/src/migrations/1713178666-drop-keychain.sql +1 -0
  401. package/src/migrations/1713178667-drop-challenges.sql +1 -0
  402. package/src/migrations/1713178668-drop-user-keys.sql +7 -0
  403. package/src/migrations/1713178669-drop-organization-key.sql +2 -0
  404. package/src/migrations/1714985451-user-nullable-organization-id.sql +2 -0
  405. package/src/migrations/1714985452-email-verification-code-nullable-organization-id.sql +2 -0
  406. package/src/migrations/1714985453-user-organization-permissions.sql +2 -0
  407. package/src/migrations/1714985454-user-organization-permissions.sql +2 -0
  408. package/src/migrations/1715079362-platform.sql +6 -0
  409. package/src/migrations/1715181649-registrations-organization-id.sql +2 -0
  410. package/src/migrations/1715181650-registrations-organization-id-fill.sql +1 -0
  411. package/src/migrations/1715181651-registrations-organization-id-drop-null.sql +2 -0
  412. package/src/migrations/1716117067-members-nullable-organization-id.sql +2 -0
  413. package/src/migrations/1719567581-registration-periods.sql +13 -0
  414. package/src/migrations/1719567582-organization-registration-periods.sql +13 -0
  415. package/src/migrations/1719567881-organization-periodId.sql +2 -0
  416. package/src/migrations/1719567882-groups-periodId.sql +2 -0
  417. package/src/migrations/1719567883-platform-periodId.sql +2 -0
  418. package/src/migrations/1719568079-default-period.sql +2 -0
  419. package/src/migrations/1719568080-set-default-period-platform.sql +1 -0
  420. package/src/migrations/1719568081-set-default-period-organizations.sql +1 -0
  421. package/src/migrations/1719568082-set-default-period-groups.sql +1 -0
  422. package/src/migrations/1719580828-registrations-periodId.sql +2 -0
  423. package/src/migrations/1719580829-set-default-period-registrations.sql +1 -0
  424. package/src/migrations/data/postcodes/nl/Drenthe +291 -0
  425. package/src/migrations/data/postcodes/nl/Flevoland +107 -0
  426. package/src/migrations/data/postcodes/nl/Friesland +518 -0
  427. package/src/migrations/data/postcodes/nl/Gelderland +601 -0
  428. package/src/migrations/data/postcodes/nl/Groningen +279 -0
  429. package/src/migrations/data/postcodes/nl/Limburg +324 -0
  430. package/src/migrations/data/postcodes/nl/Noord-Brabant +620 -0
  431. package/src/migrations/data/postcodes/nl/Noord-Holland +566 -0
  432. package/src/migrations/data/postcodes/nl/Overrijsel +344 -0
  433. package/src/migrations/data/postcodes/nl/Utrecht +278 -0
  434. package/src/migrations/data/postcodes/nl/Zeeland +179 -0
  435. package/src/migrations/data/postcodes/nl/Zuid-Holland +662 -0
  436. package/src/models/BalanceItem.ts +392 -0
  437. package/src/models/BalanceItemPayment.ts +106 -0
  438. package/src/models/BuckarooPayment.ts +19 -0
  439. package/src/models/Document.ts +203 -0
  440. package/src/models/DocumentTemplate.ts +583 -0
  441. package/src/models/EmailTemplate.ts +64 -0
  442. package/src/models/EmailVerificationCode.ts +352 -0
  443. package/src/models/Group.ts +293 -0
  444. package/src/models/Image.ts +147 -0
  445. package/src/models/Member.ts +386 -0
  446. package/src/models/MemberResponsibilityRecord.ts +39 -0
  447. package/src/models/MolliePayment.ts +19 -0
  448. package/src/models/MollieToken.ts +369 -0
  449. package/src/models/OneTimeToken.ts +131 -0
  450. package/src/models/Order.ts +1030 -0
  451. package/src/models/Organization.ts +1085 -0
  452. package/src/models/OrganizationRegistrationPeriod.ts +54 -0
  453. package/src/models/PasswordToken.ts +139 -0
  454. package/src/models/PayconiqPayment.ts +241 -0
  455. package/src/models/Payment.ts +216 -0
  456. package/src/models/Platform.ts +76 -0
  457. package/src/models/RegisterCode.ts +164 -0
  458. package/src/models/Registration.ts +405 -0
  459. package/src/models/RegistrationPeriod.ts +55 -0
  460. package/src/models/STCredit.ts +134 -0
  461. package/src/models/STInvoice.ts +507 -0
  462. package/src/models/STPackage.ts +324 -0
  463. package/src/models/STPendingInvoice.ts +308 -0
  464. package/src/models/StripeAccount.ts +71 -0
  465. package/src/models/StripeCheckoutSession.ts +22 -0
  466. package/src/models/StripePaymentIntent.ts +22 -0
  467. package/src/models/Ticket.ts +145 -0
  468. package/src/models/Token.test.ts +69 -0
  469. package/src/models/Token.ts +269 -0
  470. package/src/models/UsedRegisterCode.ts +166 -0
  471. package/src/models/User.ts +445 -0
  472. package/src/models/UserPermissions.ts +54 -0
  473. package/src/models/Webshop.ts +206 -0
  474. package/src/models/WebshopDiscountCode.ts +81 -0
  475. package/src/models/addresses/City.ts +31 -0
  476. package/src/models/addresses/PostalCode.test.ts +117 -0
  477. package/src/models/addresses/PostalCode.ts +164 -0
  478. package/src/models/addresses/Province.ts +20 -0
  479. package/src/models/addresses/Street.ts +25 -0
  480. package/src/models/index.ts +49 -0
  481. 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
+ }