@stamhoofd/backend 2.39.0 → 2.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/eslint.config.mjs +5 -0
  2. package/index.ts +81 -74
  3. package/jest.config.cjs +10 -0
  4. package/migrations.ts +16 -14
  5. package/package.json +11 -11
  6. package/src/crons/clear-excel-cache.test.ts +48 -50
  7. package/src/crons/clear-excel-cache.ts +18 -18
  8. package/src/crons/setup-steps.ts +2 -2
  9. package/src/crons.ts +325 -306
  10. package/src/decoders/StringArrayDecoder.ts +7 -7
  11. package/src/decoders/StringNullableDecoder.ts +1 -2
  12. package/src/email-recipient-loaders/members.ts +22 -22
  13. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +8 -9
  14. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +39 -40
  15. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +8 -8
  16. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +44 -45
  17. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +58 -57
  18. package/src/endpoints/auth/CreateAdminEndpoint.ts +48 -45
  19. package/src/endpoints/auth/CreateTokenEndpoint.test.ts +31 -31
  20. package/src/endpoints/auth/CreateTokenEndpoint.ts +146 -147
  21. package/src/endpoints/auth/DeleteTokenEndpoint.ts +7 -7
  22. package/src/endpoints/auth/DeleteUserEndpoint.ts +15 -15
  23. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +17 -18
  24. package/src/endpoints/auth/GetOtherUserEndpoint.ts +9 -10
  25. package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -35
  26. package/src/endpoints/auth/GetUserEndpoint.ts +5 -6
  27. package/src/endpoints/auth/PatchApiUserEndpoint.ts +35 -33
  28. package/src/endpoints/auth/PatchUserEndpoint.ts +55 -52
  29. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +9 -9
  30. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +8 -8
  31. package/src/endpoints/auth/SignupEndpoint.ts +37 -36
  32. package/src/endpoints/auth/VerifyEmailEndpoint.ts +29 -28
  33. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +33 -33
  34. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +7 -7
  35. package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +37 -37
  36. package/src/endpoints/global/email/CreateEmailEndpoint.ts +30 -30
  37. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +13 -13
  38. package/src/endpoints/global/email/GetEmailEndpoint.ts +13 -13
  39. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +16 -16
  40. package/src/endpoints/global/email/PatchEmailEndpoint.ts +25 -25
  41. package/src/endpoints/global/events/GetEventsEndpoint.ts +43 -44
  42. package/src/endpoints/global/events/PatchEventsEndpoint.ts +127 -172
  43. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +49 -50
  44. package/src/endpoints/global/files/GetFileCache.ts +13 -13
  45. package/src/endpoints/global/files/UploadFile.ts +51 -54
  46. package/src/endpoints/global/files/UploadImage.ts +53 -53
  47. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +25 -25
  48. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +24 -23
  49. package/src/endpoints/global/members/GetMembersCountEndpoint.ts +8 -8
  50. package/src/endpoints/global/members/GetMembersEndpoint.ts +105 -102
  51. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +240 -239
  52. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +12 -14
  53. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +32 -33
  54. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +48 -57
  55. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +21 -22
  56. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +28 -28
  57. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +18 -18
  58. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +20 -20
  59. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +17 -17
  60. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +81 -75
  61. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +14 -14
  62. package/src/endpoints/global/platform/GetPlatformEnpoint.ts +11 -11
  63. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +71 -68
  64. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +27 -27
  65. package/src/endpoints/global/registration/GetUserBillingStatusEndpoint.ts +30 -30
  66. package/src/endpoints/global/registration/GetUserDetailedBillingStatusEndpoint.ts +34 -34
  67. package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +26 -26
  68. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +12 -12
  69. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +90 -90
  70. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +118 -121
  71. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +362 -350
  72. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +8 -9
  73. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +21 -21
  74. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +65 -65
  75. package/src/endpoints/organization/dashboard/billing/GetOrganizationBillingStatusEndpoint.ts +9 -9
  76. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedBillingStatusEndpoint.ts +14 -14
  77. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +17 -17
  78. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +21 -21
  79. package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +15 -15
  80. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +52 -52
  81. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +37 -37
  82. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +14 -14
  83. package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +113 -112
  84. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +29 -29
  85. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +48 -47
  86. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +22 -21
  87. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +13 -14
  88. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +12 -13
  89. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +24 -24
  90. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +10 -12
  91. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +14 -14
  92. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +13 -13
  93. package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +12 -12
  94. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +120 -124
  95. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +172 -173
  96. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +88 -89
  97. package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +12 -12
  98. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +17 -17
  99. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +8 -8
  100. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +66 -67
  101. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +47 -47
  102. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +93 -91
  103. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +16 -17
  104. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +170 -167
  105. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +25 -24
  106. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +22 -23
  107. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +22 -22
  108. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +17 -18
  109. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +8 -9
  110. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +17 -18
  111. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -15
  112. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +19 -19
  113. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +19 -19
  114. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +14 -14
  115. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +12 -12
  116. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +103 -100
  117. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +11 -12
  118. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +15 -15
  119. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +14 -14
  120. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +14 -14
  121. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +23 -23
  122. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +54 -52
  123. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +84 -81
  124. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +120 -111
  125. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +24 -24
  126. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +18 -18
  127. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +141 -130
  128. package/src/endpoints/organization/shared/GetDocumentHtml.ts +25 -25
  129. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +18 -18
  130. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +36 -37
  131. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +9 -9
  132. package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +11 -11
  133. package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +28 -27
  134. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +20 -20
  135. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +22 -22
  136. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -14
  137. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +57 -56
  138. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +65 -66
  139. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +18 -17
  140. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +124 -128
  141. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +154 -145
  142. package/src/excel-loaders/members.ts +102 -103
  143. package/src/excel-loaders/payments.ts +155 -156
  144. package/src/helpers/AddressValidator.test.ts +32 -32
  145. package/src/helpers/AddressValidator.ts +128 -122
  146. package/src/helpers/AdminPermissionChecker.ts +339 -236
  147. package/src/helpers/AuthenticatedStructures.ts +233 -134
  148. package/src/helpers/BuckarooHelper.ts +134 -134
  149. package/src/helpers/CheckSettlements.ts +94 -88
  150. package/src/helpers/Context.ts +87 -86
  151. package/src/helpers/CookieHelper.ts +23 -22
  152. package/src/helpers/EmailResumer.ts +10 -10
  153. package/src/helpers/FileCache.ts +62 -62
  154. package/src/helpers/ForwardHandler.test.ts +122 -124
  155. package/src/helpers/ForwardHandler.ts +76 -70
  156. package/src/helpers/MemberUserSyncer.ts +101 -96
  157. package/src/helpers/MembershipCharger.ts +69 -69
  158. package/src/helpers/MembershipHelper.ts +11 -12
  159. package/src/helpers/OpenIDConnectHelper.ts +85 -82
  160. package/src/helpers/PeriodHelper.ts +65 -70
  161. package/src/helpers/StripeHelper.ts +146 -137
  162. package/src/helpers/StripePayoutChecker.ts +51 -52
  163. package/src/helpers/ViesHelper.ts +46 -44
  164. package/src/helpers/fetchToAsyncIterator.ts +14 -14
  165. package/src/helpers/xlsxAddressTransformerColumnFactory.ts +50 -52
  166. package/src/middleware/ContextMiddleware.ts +5 -5
  167. package/src/migrations/1646578856-validate-addresses.ts +6 -9
  168. package/src/seeds/0000000000-example.ts +3 -5
  169. package/src/seeds/1715028563-user-permissions.ts +16 -18
  170. package/src/seeds/1722256498-group-update-occupancy.ts +12 -12
  171. package/src/seeds/1722344162-sync-member-users.ts +14 -15
  172. package/src/seeds/1722344162-update-membership.ts +6 -6
  173. package/src/seeds/1726055544-balance-item-paid.ts +4 -4
  174. package/src/seeds/1726055545-balance-item-pending.ts +4 -4
  175. package/src/seeds/1726494419-update-cached-outstanding-balance.ts +16 -16
  176. package/src/seeds/1726494420-update-cached-outstanding-balance-from-items.ts +12 -12
  177. package/src/seeds/1726572303-schedule-stock-updates.ts +12 -12
  178. package/src/seeds/1726847064-setup-steps.ts +16 -0
  179. package/src/sql-filters/balance-item-payments.ts +7 -7
  180. package/src/sql-filters/events.ts +14 -14
  181. package/src/sql-filters/members.ts +96 -96
  182. package/src/sql-filters/organizations.ts +139 -75
  183. package/src/sql-filters/payments.ts +28 -28
  184. package/src/sql-filters/registrations.ts +14 -14
  185. package/src/sql-sorters/events.ts +25 -25
  186. package/src/sql-sorters/members.ts +26 -26
  187. package/src/sql-sorters/organizations.ts +36 -36
  188. package/src/sql-sorters/payments.ts +26 -26
  189. package/tests/e2e/stock.test.ts +616 -621
  190. package/tests/e2e/tickets.test.ts +255 -260
  191. package/tests/helpers/StripeMocker.ts +177 -179
  192. package/tests/helpers/TestServer.ts +9 -9
  193. package/tests/jest.global.setup.ts +14 -13
  194. package/tests/jest.setup.ts +33 -32
  195. package/.eslintrc.js +0 -61
  196. package/jest.config.js +0 -11
  197. package/src/helpers/SetupStepsUpdater.ts +0 -359
  198. package/src/seeds/1724076679-setup-steps.ts +0 -16
@@ -1,16 +1,16 @@
1
1
  import { ArrayDecoder, AutoEncoderPatchType, Data, Decoder, PatchableArray, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder } from '@simonbackx/simple-encoding';
2
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
3
- import { SimpleError } from "@simonbackx/simple-errors";
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
+ import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { BalanceItem, BalanceItemPayment, Order, Payment, Token, Webshop } from '@stamhoofd/models';
5
5
  import { QueueHandler } from '@stamhoofd/queues';
6
- import { BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, OrderStatus, PaymentMethod, PaymentStatus, PermissionLevel, PrivateOrder, PrivatePayment,Webshop as WebshopStruct } from "@stamhoofd/structures";
6
+ import { BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, OrderStatus, PaymentMethod, PaymentStatus, PermissionLevel, PrivateOrder, PrivatePayment, Webshop as WebshopStruct } from '@stamhoofd/structures';
7
7
 
8
8
  import { Context } from '../../../../helpers/Context';
9
9
 
10
10
  type Params = { id: string };
11
11
  type Query = undefined;
12
- type Body = AutoEncoderPatchType<PrivateOrder>[] | PatchableArrayAutoEncoder<PrivateOrder>
13
- type ResponseBody = PrivateOrder[]
12
+ type Body = AutoEncoderPatchType<PrivateOrder>[] | PatchableArrayAutoEncoder<PrivateOrder>;
13
+ type ResponseBody = PrivateOrder[];
14
14
 
15
15
  class VersionSpecificDecoder<A, B> implements Decoder<A | B> {
16
16
  oldDecoder: Decoder<A>;
@@ -25,13 +25,13 @@ class VersionSpecificDecoder<A, B> implements Decoder<A | B> {
25
25
 
26
26
  decode(data: Data): A | B {
27
27
  // Set the version of the decoding context of "data"
28
- const v = data.context.version
28
+ const v = data.context.version;
29
29
 
30
30
  if (v >= this.version) {
31
31
  return this.newerDecoder.decode(data);
32
32
  }
33
33
 
34
- return this.oldDecoder.decode(data);
34
+ return this.oldDecoder.decode(data);
35
35
  }
36
36
  }
37
37
 
@@ -41,15 +41,15 @@ export class PatchWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Re
41
41
  new ArrayDecoder(PrivateOrder.patchType() as Decoder<AutoEncoderPatchType<PrivateOrder>>),
42
42
  159,
43
43
  // After or at version 159, accept a patchable array
44
- new PatchableArrayDecoder(PrivateOrder as Decoder<PrivateOrder>, PrivateOrder.patchType() as Decoder<AutoEncoderPatchType<PrivateOrder>>, StringDecoder)
44
+ new PatchableArrayDecoder(PrivateOrder as Decoder<PrivateOrder>, PrivateOrder.patchType() as Decoder<AutoEncoderPatchType<PrivateOrder>>, StringDecoder),
45
45
  );
46
46
 
47
47
  protected doesMatch(request: Request): [true, Params] | [false] {
48
- if (request.method != "PATCH") {
48
+ if (request.method !== 'PATCH') {
49
49
  return [false];
50
50
  }
51
51
 
52
- const params = Endpoint.parseParameters(request.url, "/webshop/@id/orders", { id: String });
52
+ const params = Endpoint.parseParameters(request.url, '/webshop/@id/orders', { id: String });
53
53
 
54
54
  if (params) {
55
55
  return [true, params as Params];
@@ -59,22 +59,23 @@ export class PatchWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Re
59
59
 
60
60
  async handle(request: DecodedRequest<Params, Query, Body>) {
61
61
  const organization = await Context.setOrganizationScope();
62
- await Context.authenticate()
62
+ await Context.authenticate();
63
63
 
64
64
  // Fast throw first (more in depth checking for patches later)
65
65
  if (!await Context.auth.hasSomeAccess(organization.id)) {
66
- throw Context.auth.error()
66
+ throw Context.auth.error();
67
67
  }
68
68
 
69
- let body: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray()
69
+ let body: PatchableArrayAutoEncoder<PrivateOrder> = new PatchableArray();
70
70
 
71
71
  // Migrate old syntax
72
72
  if (Array.isArray(request.body)) {
73
73
  for (const p of request.body) {
74
74
  body.addPatch(p);
75
75
  }
76
- } else {
77
- body = request.body
76
+ }
77
+ else {
78
+ body = request.body;
78
79
  }
79
80
 
80
81
  if (body.changes.length == 0) {
@@ -82,100 +83,103 @@ export class PatchWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Re
82
83
  }
83
84
 
84
85
  // Need to happen in the queue because we are updating the webshop stock
85
- const orders = await QueueHandler.schedule("webshop-stock/"+request.params.id, async () => {
86
- const webshop = await Webshop.getByID(request.params.id)
86
+ const orders = await QueueHandler.schedule('webshop-stock/' + request.params.id, async () => {
87
+ const webshop = await Webshop.getByID(request.params.id);
87
88
  if (!webshop || !await Context.auth.canAccessWebshop(webshop, PermissionLevel.Write)) {
88
- throw Context.auth.notFoundOrNoAccess()
89
+ throw Context.auth.notFoundOrNoAccess();
89
90
  }
90
91
 
91
- const orders = body.getPatches().length > 0 ? await Order.where({
92
- webshopId: webshop.id,
93
- id: {
94
- sign: "IN",
95
- value: body.getPatches().map(o => o.id)
96
- }
97
- }) : []
92
+ const orders = body.getPatches().length > 0
93
+ ? await Order.where({
94
+ webshopId: webshop.id,
95
+ id: {
96
+ sign: 'IN',
97
+ value: body.getPatches().map(o => o.id),
98
+ },
99
+ })
100
+ : [];
98
101
 
99
102
  // We use a getter because we need to have an up to date webshop struct
100
103
  // otherwise we won't validate orders on the latest webshop with the latest stock information
101
104
  const webshopGetter = {
102
105
  get struct() {
103
106
  return WebshopStruct.create(webshop);
104
- }
105
- }
107
+ },
108
+ };
106
109
 
107
110
  // TODO: handle order creation here
108
111
  for (const put of body.getPuts()) {
109
- const struct = put.put
110
- const model = new Order()
111
- model.webshopId = webshop.id
112
- model.organizationId = webshop.organizationId
113
- model.status = struct.status
114
- model.data = struct.data
112
+ const struct = put.put;
113
+ const model = new Order();
114
+ model.webshopId = webshop.id;
115
+ model.organizationId = webshop.organizationId;
116
+ model.status = struct.status;
117
+ model.data = struct.data;
115
118
 
116
119
  // For now, we don't invalidate tickets, because they will get invalidated at scan time (the order status is checked)
117
- // This allows you to revalidate a ticket without needing to generate a new one (e.g. when accidentally canceling an order)
120
+ // This allows you to revalidate a ticket without needing to generate a new one (e.g. when accidentally canceling an order)
118
121
  // -> the user doesn't need to download the ticket again
119
122
  // + added benefit: we can inform the user that the ticket was canceled, instead of throwing an 'invalid ticket' error
120
123
 
121
124
  if (model.status === OrderStatus.Deleted) {
122
- model.data.removePersonalData()
125
+ model.data.removePersonalData();
123
126
  }
124
127
 
125
- const order = model.setRelation(Order.webshop, webshop.setRelation(Webshop.organization, organization))
128
+ const order = model.setRelation(Order.webshop, webshop.setRelation(Webshop.organization, organization));
126
129
 
127
130
  // TODO: validate before updating stock
128
131
  order.data.validate(webshopGetter.struct, organization.meta, request.i18n, true);
129
-
132
+
130
133
  try {
131
- await order.updateStock()
132
- const totalPrice = order.data.totalPrice
134
+ await order.updateStock();
135
+ const totalPrice = order.data.totalPrice;
133
136
 
134
137
  if (totalPrice == 0) {
135
138
  // Force unknown payment method
136
- order.data.paymentMethod = PaymentMethod.Unknown
139
+ order.data.paymentMethod = PaymentMethod.Unknown;
137
140
 
138
141
  // Mark this order as paid
139
- await order.markPaid(null, organization, webshop)
140
- await order.save()
141
- } else {
142
- const payment = new Payment()
143
- payment.organizationId = organization.id
144
- payment.method = struct.data.paymentMethod
145
- payment.status = PaymentStatus.Created
146
- payment.price = totalPrice
147
- payment.paidAt = null
142
+ await order.markPaid(null, organization, webshop);
143
+ await order.save();
144
+ }
145
+ else {
146
+ const payment = new Payment();
147
+ payment.organizationId = organization.id;
148
+ payment.method = struct.data.paymentMethod;
149
+ payment.status = PaymentStatus.Created;
150
+ payment.price = totalPrice;
151
+ payment.paidAt = null;
148
152
 
149
153
  // Determine the payment provider (always null because no online payments here)
150
- payment.provider = null
154
+ payment.provider = null;
151
155
 
152
- await payment.save()
156
+ await payment.save();
153
157
 
154
- order.paymentId = payment.id
155
- order.setRelation(Order.payment, payment)
158
+ order.paymentId = payment.id;
159
+ order.setRelation(Order.payment, payment);
156
160
 
157
161
  // Create balance item
158
162
  const balanceItem = new BalanceItem();
159
163
  balanceItem.orderId = order.id;
160
164
  balanceItem.type = BalanceItemType.Order;
161
- balanceItem.unitPrice = totalPrice
162
- balanceItem.description = webshop.meta.name
163
- balanceItem.pricePaid = 0
165
+ balanceItem.unitPrice = totalPrice;
166
+ balanceItem.description = webshop.meta.name;
167
+ balanceItem.pricePaid = 0;
164
168
  balanceItem.organizationId = organization.id;
165
169
  balanceItem.status = BalanceItemStatus.Pending;
166
170
  balanceItem.relations = new Map([
167
171
  [
168
- BalanceItemRelationType.Webshop,
172
+ BalanceItemRelationType.Webshop,
169
173
  BalanceItemRelation.create({
170
174
  id: webshop.id,
171
175
  name: webshop.meta.name,
172
- })
173
- ]
174
- ])
176
+ }),
177
+ ],
178
+ ]);
175
179
  await balanceItem.save();
176
180
 
177
181
  // Create one balance item payment to pay it in one payment
178
- const balanceItemPayment = new BalanceItemPayment()
182
+ const balanceItemPayment = new BalanceItemPayment();
179
183
  balanceItemPayment.balanceItemId = balanceItem.id;
180
184
  balanceItemPayment.paymentId = payment.id;
181
185
  balanceItemPayment.organizationId = organization.id;
@@ -183,50 +187,53 @@ export class PatchWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Re
183
187
  await balanceItemPayment.save();
184
188
 
185
189
  if (payment.method == PaymentMethod.Transfer) {
186
- await order.markValid(payment, [])
187
- await payment.save()
188
- await order.save()
189
- } else if (payment.method == PaymentMethod.PointOfSale) {
190
+ await order.markValid(payment, []);
191
+ await payment.save();
192
+ await order.save();
193
+ }
194
+ else if (payment.method == PaymentMethod.PointOfSale) {
190
195
  // Not really paid, but needed to create the tickets if needed
191
- await order.markPaid(payment, organization, webshop)
192
- await payment.save()
193
- await order.save()
194
- } else {
195
- throw new Error("Unsupported payment method")
196
+ await order.markPaid(payment, organization, webshop);
197
+ await payment.save();
198
+ await order.save();
199
+ }
200
+ else {
201
+ throw new Error('Unsupported payment method');
196
202
  }
197
203
 
198
- balanceItem.description = order.generateBalanceDescription(webshop)
199
- await balanceItem.save()
204
+ balanceItem.description = order.generateBalanceDescription(webshop);
205
+ await balanceItem.save();
200
206
  }
201
- } catch (e) {
202
- await order.deleteOrderBecauseOfCreationError()
207
+ }
208
+ catch (e) {
209
+ await order.deleteOrderBecauseOfCreationError();
203
210
  throw e;
204
211
  }
205
-
206
- orders.push(order)
212
+
213
+ orders.push(order);
207
214
  }
208
215
 
209
216
  for (const patch of body.getPatches()) {
210
- const model = orders.find(p => p.id == patch.id)
217
+ const model = orders.find(p => p.id == patch.id);
211
218
  if (!model) {
212
219
  throw new SimpleError({
213
- code: "not_found",
214
- message: "Order with id "+patch.id+" does not exist"
215
- })
220
+ code: 'not_found',
221
+ message: 'Order with id ' + patch.id + ' does not exist',
222
+ });
216
223
  }
217
224
  const previousToPay = model.totalToPay;
218
- const previousStatus = model.status
225
+ const previousStatus = model.status;
219
226
 
220
- model.status = patch.status ?? model.status
227
+ model.status = patch.status ?? model.status;
221
228
 
222
229
  // For now, we don't invalidate tickets, because they will get invalidated at scan time (the order status is checked)
223
- // This allows you to revalidate a ticket without needing to generate a new one (e.g. when accidentally canceling an order)
230
+ // This allows you to revalidate a ticket without needing to generate a new one (e.g. when accidentally canceling an order)
224
231
  // -> the user doesn't need to download the ticket again
225
232
  // + added benefit: we can inform the user that the ticket was canceled, instead of throwing an 'invalid ticket' error
226
233
 
227
- const previousData = model.data.clone()
234
+ const previousData = model.data.clone();
228
235
  if (patch.data) {
229
- model.data.patchOrPut(patch.data)
236
+ model.data.patchOrPut(patch.data);
230
237
 
231
238
  if (model.status !== OrderStatus.Deleted) {
232
239
  // Make sure all data is up to date and validated (= possible corrections happen here too)
@@ -235,59 +242,61 @@ export class PatchWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Re
235
242
  }
236
243
 
237
244
  if (model.status === OrderStatus.Deleted) {
238
- model.data.removePersonalData()
245
+ model.data.removePersonalData();
239
246
  }
240
247
 
241
248
  if (model.status === OrderStatus.Deleted || model.status === OrderStatus.Canceled) {
242
- model.markUpdated()
249
+ model.markUpdated();
243
250
  // Cancel payment if still pending
244
- await BalanceItem.deleteForDeletedOrders([model.id])
245
- } else {
251
+ await BalanceItem.deleteForDeletedOrders([model.id]);
252
+ }
253
+ else {
246
254
  if (previousStatus === OrderStatus.Canceled || previousStatus === OrderStatus.Deleted) {
247
- model.markUpdated()
255
+ model.markUpdated();
248
256
  // Undo deletion
249
- await BalanceItem.undoForDeletedOrders([model.id])
257
+ await BalanceItem.undoForDeletedOrders([model.id]);
250
258
  }
251
259
  }
252
260
 
253
261
  // Update balance item prices for this order if price has changed
254
262
  if (previousToPay !== model.totalToPay) {
255
- const items = await BalanceItem.where({ orderId: model.id })
263
+ const items = await BalanceItem.where({ orderId: model.id });
256
264
  if (items.length >= 1) {
257
- model.markUpdated()
258
- items[0].unitPrice = model.totalToPay
259
- items[0].description = model.generateBalanceDescription(webshop)
265
+ model.markUpdated();
266
+ items[0].unitPrice = model.totalToPay;
267
+ items[0].description = model.generateBalanceDescription(webshop);
260
268
  items[0].updateStatus();
261
- await items[0].save()
269
+ await items[0].save();
262
270
 
263
271
  // Zero out the other items
264
- const otherItems = items.slice(1)
265
- await BalanceItem.deleteItems(otherItems)
266
- } else if (items.length === 0
267
- && model.totalToPay > 0) {
268
- model.markUpdated()
272
+ const otherItems = items.slice(1);
273
+ await BalanceItem.deleteItems(otherItems);
274
+ }
275
+ else if (items.length === 0
276
+ && model.totalToPay > 0) {
277
+ model.markUpdated();
269
278
  const balanceItem = new BalanceItem();
270
279
  balanceItem.orderId = model.id;
271
- balanceItem.unitPrice = model.totalToPay
272
- balanceItem.description = model.generateBalanceDescription(webshop)
273
- balanceItem.pricePaid = 0
280
+ balanceItem.unitPrice = model.totalToPay;
281
+ balanceItem.description = model.generateBalanceDescription(webshop);
282
+ balanceItem.pricePaid = 0;
274
283
  balanceItem.organizationId = organization.id;
275
284
  balanceItem.status = BalanceItemStatus.Pending;
276
285
  await balanceItem.save();
277
286
  }
278
287
  }
279
288
 
280
- await model.save()
281
- await model.setRelation(Order.webshop, webshop).updateStock(previousData)
282
- await model.setRelation(Order.webshop, webshop).updateTickets()
289
+ await model.save();
290
+ await model.setRelation(Order.webshop, webshop).updateStock(previousData);
291
+ await model.setRelation(Order.webshop, webshop).updateTickets();
283
292
  }
284
293
 
285
- const mapped = orders.map(order => order.setRelation(Order.webshop, webshop))
286
- return mapped
287
- })
294
+ const mapped = orders.map(order => order.setRelation(Order.webshop, webshop));
295
+ return mapped;
296
+ });
288
297
 
289
298
  return new Response(
290
- await Order.getPrivateStructures(orders)
299
+ await Order.getPrivateStructures(orders),
291
300
  );
292
301
  }
293
302
  }
@@ -1,25 +1,25 @@
1
1
  import { ArrayDecoder, AutoEncoderPatchType, Decoder } from '@simonbackx/simple-encoding';
2
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
3
- import { SimpleError, SimpleErrors } from "@simonbackx/simple-errors";
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
+ import { SimpleError, SimpleErrors } from '@simonbackx/simple-errors';
4
4
  import { Ticket, Token, Webshop } from '@stamhoofd/models';
5
- import { PermissionLevel, TicketPrivate } from "@stamhoofd/structures";
5
+ import { PermissionLevel, TicketPrivate } from '@stamhoofd/structures';
6
6
 
7
7
  import { Context } from '../../../../helpers/Context';
8
8
 
9
9
  type Params = { id: string };
10
10
  type Query = undefined;
11
- type Body = AutoEncoderPatchType<TicketPrivate>[]
12
- type ResponseBody = TicketPrivate[]
11
+ type Body = AutoEncoderPatchType<TicketPrivate>[];
12
+ type ResponseBody = TicketPrivate[];
13
13
 
14
14
  export class PatchWebshopTicketsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
15
- bodyDecoder = new ArrayDecoder(TicketPrivate.patchType() as Decoder<AutoEncoderPatchType<TicketPrivate>>)
15
+ bodyDecoder = new ArrayDecoder(TicketPrivate.patchType() as Decoder<AutoEncoderPatchType<TicketPrivate>>);
16
16
 
17
17
  protected doesMatch(request: Request): [true, Params] | [false] {
18
- if (request.method != "PATCH") {
18
+ if (request.method !== 'PATCH') {
19
19
  return [false];
20
20
  }
21
21
 
22
- const params = Endpoint.parseParameters(request.url, "/webshop/@id/tickets/private", { id: String });
22
+ const params = Endpoint.parseParameters(request.url, '/webshop/@id/tickets/private', { id: String });
23
23
 
24
24
  if (params) {
25
25
  return [true, params as Params];
@@ -29,52 +29,52 @@ export class PatchWebshopTicketsEndpoint extends Endpoint<Params, Query, Body, R
29
29
 
30
30
  async handle(request: DecodedRequest<Params, Query, Body>) {
31
31
  const organization = await Context.setOrganizationScope();
32
- await Context.authenticate()
32
+ await Context.authenticate();
33
33
 
34
34
  // Fast throw first (more in depth checking for patches later)
35
35
  if (!await Context.auth.hasSomeAccess(organization.id)) {
36
- throw Context.auth.error()
36
+ throw Context.auth.error();
37
37
  }
38
38
 
39
39
  if (request.body.length == 0) {
40
40
  return new Response([]);
41
41
  }
42
42
 
43
- const webshop = await Webshop.getByID(request.params.id)
43
+ const webshop = await Webshop.getByID(request.params.id);
44
44
  if (!webshop || !await Context.auth.canAccessWebshopTickets(webshop, PermissionLevel.Write)) {
45
- throw Context.auth.notFoundOrNoAccess("Je hebt geen toegang om tickets te wijzigen van deze webshop")
45
+ throw Context.auth.notFoundOrNoAccess('Je hebt geen toegang om tickets te wijzigen van deze webshop');
46
46
  }
47
47
 
48
- const tickets: Ticket[] = []
49
- const errors = new SimpleErrors()
48
+ const tickets: Ticket[] = [];
49
+ const errors = new SimpleErrors();
50
50
 
51
51
  for (const patch of request.body) {
52
- const model = await Ticket.getByID(patch.id)
52
+ const model = await Ticket.getByID(patch.id);
53
53
  if (!model || model.webshopId !== webshop.id) {
54
54
  errors.addError(new SimpleError({
55
- code: "ticket_not_found",
55
+ code: 'ticket_not_found',
56
56
  field: patch.id,
57
- message: "Ticket with id "+patch.id+" does not exist"
58
- }))
59
- continue
57
+ message: 'Ticket with id ' + patch.id + ' does not exist',
58
+ }));
59
+ continue;
60
60
  }
61
61
 
62
62
  if (patch.scannedAt !== undefined) {
63
- model.scannedAt = patch.scannedAt
63
+ model.scannedAt = patch.scannedAt;
64
64
  }
65
65
 
66
66
  if (patch.scannedBy !== undefined) {
67
- model.scannedBy = patch.scannedBy
67
+ model.scannedBy = patch.scannedBy;
68
68
  }
69
69
 
70
- await model.save()
70
+ await model.save();
71
71
 
72
- tickets.push(model)
72
+ tickets.push(model);
73
73
  }
74
74
 
75
75
  errors.throwIfNotEmpty();
76
76
  return new Response(
77
- tickets.map(ticket => TicketPrivate.create(ticket))
77
+ tickets.map(ticket => TicketPrivate.create(ticket)),
78
78
  );
79
79
  }
80
80
  }
@@ -1,10 +1,10 @@
1
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
1
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
2
  import { SimpleError } from '@simonbackx/simple-errors';
3
3
  import { Token, Webshop } from '@stamhoofd/models';
4
4
  import { QueueHandler } from '@stamhoofd/queues';
5
- import { PermissionLevel, PrivateWebshop, WebshopPrivateMetaData } from "@stamhoofd/structures";
5
+ import { PermissionLevel, PrivateWebshop, WebshopPrivateMetaData } from '@stamhoofd/structures';
6
6
 
7
- import { Context } from "../../../../helpers/Context";
7
+ import { Context } from '../../../../helpers/Context';
8
8
 
9
9
  type Params = { id: string };
10
10
  type Query = undefined;
@@ -16,13 +16,12 @@ type ResponseBody = PrivateWebshop;
16
16
  */
17
17
 
18
18
  export class VerifyWebshopDomainEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
19
-
20
19
  protected doesMatch(request: Request): [true, Params] | [false] {
21
- if (request.method != "POST") {
20
+ if (request.method !== 'POST') {
22
21
  return [false];
23
22
  }
24
23
 
25
- const params = Endpoint.parseParameters(request.url, "/webshop/@id/verify-domain", { id: String });
24
+ const params = Endpoint.parseParameters(request.url, '/webshop/@id/verify-domain', { id: String });
26
25
 
27
26
  if (params) {
28
27
  return [true, params as Params];
@@ -32,28 +31,29 @@ export class VerifyWebshopDomainEndpoint extends Endpoint<Params, Query, Body, R
32
31
 
33
32
  async handle(request: DecodedRequest<Params, Query, Body>) {
34
33
  const organization = await Context.setOrganizationScope();
35
- await Context.authenticate()
34
+ await Context.authenticate();
36
35
 
37
36
  // Fast throw first (more in depth checking for patches later)
38
37
  if (!await Context.auth.hasSomeAccess(organization.id)) {
39
- throw Context.auth.error()
38
+ throw Context.auth.error();
40
39
  }
41
40
 
42
- return await QueueHandler.schedule("webshop-stock/"+request.params.id, async () => {
43
- const webshop = await Webshop.getByID(request.params.id)
41
+ return await QueueHandler.schedule('webshop-stock/' + request.params.id, async () => {
42
+ const webshop = await Webshop.getByID(request.params.id);
44
43
  if (!webshop || !await Context.auth.canAccessWebshop(webshop, PermissionLevel.Full)) {
45
- throw Context.auth.notFoundOrNoAccess()
44
+ throw Context.auth.notFoundOrNoAccess();
46
45
  }
47
-
46
+
48
47
  if (webshop.domain !== null) {
49
- webshop.privateMeta.dnsRecords = WebshopPrivateMetaData.buildDNSRecords(webshop.domain)
50
- await webshop.updateDNSRecords()
51
- } else {
52
- webshop.privateMeta.dnsRecords = []
53
- webshop.meta.domainActive = false
48
+ webshop.privateMeta.dnsRecords = WebshopPrivateMetaData.buildDNSRecords(webshop.domain);
49
+ await webshop.updateDNSRecords();
50
+ }
51
+ else {
52
+ webshop.privateMeta.dnsRecords = [];
53
+ webshop.meta.domainActive = false;
54
54
  }
55
55
 
56
- await webshop.save()
56
+ await webshop.save();
57
57
  return new Response(PrivateWebshop.create(webshop));
58
58
  });
59
59
  }