@stamhoofd/backend 2.127.0 → 2.127.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamhoofd/backend",
3
- "version": "2.127.0",
3
+ "version": "2.127.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
@@ -57,20 +57,20 @@
57
57
  "@simonbackx/simple-endpoints": "1.21.1",
58
58
  "@simonbackx/simple-errors": "1.5.0",
59
59
  "@simonbackx/simple-logging": "1.0.1",
60
- "@stamhoofd/backend-env": "2.127.0",
61
- "@stamhoofd/backend-i18n": "2.127.0",
62
- "@stamhoofd/backend-middleware": "2.127.0",
63
- "@stamhoofd/crons": "2.127.0",
64
- "@stamhoofd/email": "2.127.0",
65
- "@stamhoofd/excel-writer": "2.127.0",
66
- "@stamhoofd/logging": "2.127.0",
67
- "@stamhoofd/models": "2.127.0",
68
- "@stamhoofd/object-differ": "2.127.0",
69
- "@stamhoofd/queues": "2.127.0",
70
- "@stamhoofd/sql": "2.127.0",
71
- "@stamhoofd/structures": "2.127.0",
72
- "@stamhoofd/types": "2.127.0",
73
- "@stamhoofd/utility": "2.127.0",
60
+ "@stamhoofd/backend-env": "2.127.1",
61
+ "@stamhoofd/backend-i18n": "2.127.1",
62
+ "@stamhoofd/backend-middleware": "2.127.1",
63
+ "@stamhoofd/crons": "2.127.1",
64
+ "@stamhoofd/email": "2.127.1",
65
+ "@stamhoofd/excel-writer": "2.127.1",
66
+ "@stamhoofd/logging": "2.127.1",
67
+ "@stamhoofd/models": "2.127.1",
68
+ "@stamhoofd/object-differ": "2.127.1",
69
+ "@stamhoofd/queues": "2.127.1",
70
+ "@stamhoofd/sql": "2.127.1",
71
+ "@stamhoofd/structures": "2.127.1",
72
+ "@stamhoofd/types": "2.127.1",
73
+ "@stamhoofd/utility": "2.127.1",
74
74
  "archiver": "7.0.1",
75
75
  "axios": "1.16.0",
76
76
  "base-x": "3.0.11",
@@ -91,7 +91,7 @@
91
91
  "stripe": "16.12.0"
92
92
  },
93
93
  "devDependencies": {
94
- "@stamhoofd/test-utils": "2.127.0",
94
+ "@stamhoofd/test-utils": "2.127.1",
95
95
  "@types/cookie": "0.6.0",
96
96
  "@types/luxon": "3.7.1",
97
97
  "@types/mailparser": "3.4.6",
@@ -107,5 +107,5 @@
107
107
  "publishConfig": {
108
108
  "access": "public"
109
109
  },
110
- "gitHead": "c3c61a7728293f5fe7b4b639f95b3cc79166265f"
110
+ "gitHead": "0e8db3ce693701ff334894c2e9e60d9a51c7286e"
111
111
  }
@@ -44,6 +44,6 @@ async function createStripeInvoices() {
44
44
  const invoicer = new StripeInvoicer({
45
45
  secretKey: STAMHOOFD.STRIPE_SECRET_KEY,
46
46
  });
47
- await invoicer.generateAllInvoices(membershipOrganization);
47
+ await invoicer.generateAllInvoices(membershipOrganization, { forceLast: Formatter.dateIso(today) === '2026-07-03' });
48
48
  lastStripeInvoice = new Date();
49
49
  }
@@ -8,10 +8,10 @@ import { QueueHandler } from '@stamhoofd/queues';
8
8
  import { AuditLogSource, BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, OrderStatus, PaymentMethod, PaymentStatus, PermissionLevel, PrivateOrder, TranslatedString, Webshop as WebshopStruct, WebshopTicketType } from '@stamhoofd/structures';
9
9
 
10
10
  import { Context } from '../../../../helpers/Context.js';
11
- import { AuditLogService } from '../../../../services/AuditLogService.js';
12
- import { shouldReserveUitpasNumbers, UitpasService } from '../../../../services/uitpas/UitpasService.js';
13
11
  import { ServiceFeeHelper } from '../../../../helpers/ServiceFeeHelper.js';
12
+ import { AuditLogService } from '../../../../services/AuditLogService.js';
14
13
  import { PaymentService } from '../../../../services/PaymentService.js';
14
+ import { shouldReserveUitpasNumbers, UitpasService } from '../../../../services/uitpas/UitpasService.js';
15
15
 
16
16
  type Params = { id: string };
17
17
  type Query = undefined;
@@ -153,6 +153,16 @@ export class PatchWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Re
153
153
  const payment = new Payment();
154
154
  payment.organizationId = organization.id;
155
155
  payment.method = struct.data.paymentMethod;
156
+
157
+ // quick throw to prevent payment from being saved
158
+ if (payment.method === PaymentMethod.Transfer && !webshop.meta.paymentConfiguration.transferSettings.iban) {
159
+ throw new SimpleError({
160
+ code: 'missing_iban',
161
+ message: 'Missing IBAN',
162
+ human: $t(`%w2`),
163
+ });
164
+ }
165
+
156
166
  payment.status = PaymentStatus.Created;
157
167
  payment.price = totalPrice;
158
168
  PaymentService.roundPayment(payment);
@@ -7,6 +7,7 @@ import { Formatter } from '@stamhoofd/utility';
7
7
  import Stripe from 'stripe';
8
8
  import { PaymentService } from '../services/PaymentService.js';
9
9
  import { VATService } from '../services/VATService.js';
10
+ import { SQL } from '@stamhoofd/sql';
10
11
 
11
12
  export class ApplicationFeeDetails {
12
13
  transferFee = 0;
@@ -138,7 +139,13 @@ export class StripeReportInvoicer {
138
139
  }
139
140
 
140
141
  static async hasPayment(sellingOrganization: Organization, reference: string) {
141
- return !!await Payment.select().where('organizationId', sellingOrganization.id).where('reference', reference).where('status', PaymentStatus.Succeeded).first(false);
142
+ return !!await Payment.select()
143
+ .where('organizationId', sellingOrganization.id)
144
+ .where('reference', reference)
145
+ .where('status', PaymentStatus.Succeeded)
146
+ .where('method', PaymentMethod.AccountDeductions)
147
+ .where('provider', PaymentProvider.Stripe)
148
+ .first(false);
142
149
  }
143
150
 
144
151
  async generateInvoice(sellingOrganization: Organization, reference: string, accountId: string, applicationFee: ApplicationFeeDetails) {
@@ -175,7 +182,14 @@ export class StripeReportInvoicer {
175
182
  const existingPayments = await Payment.select()
176
183
  .where('organizationId', sellingOrganization.id)
177
184
  .where('payingOrganizationId', organization.id)
185
+ .where(
186
+ // required because the same organization can have multiple stripe accounts. Null = legacy
187
+ SQL.where('stripeAccountId', stripeAccount.id)
188
+ .or('stripeAccountId', null),
189
+ )
178
190
  .where('reference', reference)
191
+ .where('method', PaymentMethod.AccountDeductions)
192
+ .where('provider', PaymentProvider.Stripe)
179
193
  .where('status', PaymentStatus.Succeeded)
180
194
  .fetch();
181
195
 
@@ -320,7 +334,7 @@ export class StripeInvoicer {
320
334
  }
321
335
 
322
336
  // Loops all months until all invoices are generated
323
- async generateAllInvoices(sellingOrganization: Organization, options?: { force?: boolean; start?: Date }) {
337
+ async generateAllInvoices(sellingOrganization: Organization, options?: { force?: boolean; start?: Date; forceLast?: boolean }) {
324
338
  const startMonth = options?.start ?? new Date(2026, 0, 1);
325
339
  const stopAt = new Date(Date.now() + (STAMHOOFD.environment === 'production' ? (-60 * 60 * 24 * 1000) : (60 * 60 * 24 * 1000 * 31))); // One day margin before creating invoices
326
340
  let currentMonth = new Date(startMonth);
@@ -332,8 +346,11 @@ export class StripeInvoicer {
332
346
  // Stop
333
347
  break;
334
348
  }
335
- await this.generateInvoices(sellingOrganization, currentMonth, options);
336
- currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1);
349
+ const nextMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1);
350
+ const { end: endNext } = StripeInvoicer.getMonthUnixStartEnd(currentMonth);
351
+ const isLastMonth = endNext >= stopAt.getTime() / 1000;
352
+ await this.generateInvoices(sellingOrganization, currentMonth, { ...options, force: (isLastMonth && !!options?.forceLast) || !!options?.force });
353
+ currentMonth = nextMonth;
337
354
  }
338
355
  }
339
356