@stamhoofd/backend 2.138.2 → 2.140.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/crons/fake-settlements.test.ts +165 -0
- package/src/crons/fake-settlements.ts +117 -0
- package/src/crons/index.ts +1 -0
- package/src/endpoints/auth/CreateTokenEndpoint.ts +5 -2
- package/src/endpoints/auth/ForgotPasswordEndpoint.test.ts +45 -0
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +2 -23
- package/src/endpoints/auth/MFA.security.test.ts +76 -1
- package/src/endpoints/auth/MFA.test.ts +187 -3
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +15 -1
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemBreakdownEndpoint.test.ts +543 -0
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemBreakdownEndpoint.ts +78 -0
- package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemEndpoint.ts +3 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentBreakdownEndpoint.test.ts +1095 -0
- package/src/endpoints/organization/dashboard/payments/GetPaymentBreakdownEndpoint.ts +111 -0
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +3 -79
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +3 -1
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +1 -1
- package/src/excel-loaders/balance-item-payments.ts +145 -0
- package/src/excel-loaders/index.ts +1 -0
- package/src/excel-loaders/payments.ts +45 -37
- package/src/helpers/StripeInvoicer.ts +1 -1
- package/src/helpers/TwoFactorHelper.ts +91 -9
- package/src/helpers/breakdownRelations.ts +48 -0
- package/src/helpers/getNextPageRequest.ts +24 -0
- package/src/helpers/streamForBreakdown.test.ts +107 -0
- package/src/helpers/streamForBreakdown.ts +104 -0
- package/src/services/DocumentRenderService.test.ts +72 -1
- package/src/services/PasswordForgotService.ts +31 -1
- package/src/services/PaymentService.ts +9 -1
- package/src/services/SSOService.ts +14 -1
- package/src/sql-filters/balance-item-payments-root.ts +48 -0
- package/src/sql-filters/balance-items.ts +55 -0
- package/src/sql-filters/orders.ts +6 -2
- package/src/sql-filters/payment-settlement.test.ts +68 -0
- package/src/sql-filters/payment-settlement.ts +43 -0
- package/src/sql-filters/payments.ts +93 -31
- package/src/sql-sorters/balance-item-payments.ts +32 -0
- package/tests/filters/orders.test.ts +635 -0
- package/tests/helpers/ExportSlice.ts +71 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { compileToSQLFilter } from '@stamhoofd/sql';
|
|
2
|
+
import { PaymentMethod, PaymentProvider, PaymentStatus, Settlement } from '@stamhoofd/structures';
|
|
3
|
+
import { toBalanceItemFilter } from '@stamhoofd/structures/breakdown/breakdownFilters.js';
|
|
4
|
+
import type { SettleablePayment } from '@stamhoofd/structures/PaymentSettlement.js';
|
|
5
|
+
import { ACCOUNT_DEDUCTIONS_ID, FAILED_PAYMENT_ID, getPaymentSettlement, PENDING_PAYMENT_ID } from '@stamhoofd/structures/PaymentSettlement.js';
|
|
6
|
+
import { balanceItemFilterCompilers } from './balance-items.js';
|
|
7
|
+
import { paymentFilterCompilers } from './payments.js';
|
|
8
|
+
|
|
9
|
+
describe('paymentSettlementFilterCompilers', () => {
|
|
10
|
+
const settlement = Settlement.create({
|
|
11
|
+
id: 'settlement-1',
|
|
12
|
+
reference: 'ST-2026-01',
|
|
13
|
+
settledAt: new Date(2026, 0, 15),
|
|
14
|
+
amount: 100_00,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* At least one payment for every group getPaymentSettlement can hand out, so a group that gets a new
|
|
19
|
+
* field in its filter is covered here.
|
|
20
|
+
*/
|
|
21
|
+
const payments: SettleablePayment[] = [
|
|
22
|
+
// Money that never arrived
|
|
23
|
+
{ method: PaymentMethod.Bancontact, provider: PaymentProvider.Stripe, settlement: null, status: PaymentStatus.Pending },
|
|
24
|
+
{ method: PaymentMethod.Bancontact, provider: PaymentProvider.Stripe, settlement: null, status: PaymentStatus.Failed },
|
|
25
|
+
// Paid out, and waiting to be paid out
|
|
26
|
+
{ method: PaymentMethod.Bancontact, provider: PaymentProvider.Stripe, settlement, status: PaymentStatus.Succeeded },
|
|
27
|
+
{ method: PaymentMethod.Bancontact, provider: PaymentProvider.Stripe, settlement: null, status: PaymentStatus.Succeeded },
|
|
28
|
+
// Online, but from a provider that tells us nothing about its payouts
|
|
29
|
+
{ method: PaymentMethod.Payconiq, provider: PaymentProvider.Payconiq, settlement: null, status: PaymentStatus.Succeeded },
|
|
30
|
+
{ method: PaymentMethod.CreditCard, provider: null, settlement: null, status: PaymentStatus.Succeeded },
|
|
31
|
+
// Never online
|
|
32
|
+
{ method: PaymentMethod.Transfer, provider: null, settlement: null, status: PaymentStatus.Succeeded },
|
|
33
|
+
{ method: PaymentMethod.PointOfSale, provider: null, settlement: null, status: PaymentStatus.Succeeded },
|
|
34
|
+
{ method: PaymentMethod.AccountDeductions, provider: null, settlement: null, status: PaymentStatus.Succeeded },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const groups = payments.map(payment => getPaymentSettlement(payment));
|
|
38
|
+
|
|
39
|
+
test('every payout group can be compiled against the payments table', async () => {
|
|
40
|
+
for (const group of groups) {
|
|
41
|
+
await expect(compileToSQLFilter(group.filter, paymentFilterCompilers)).resolves.toBeDefined();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('every payout group survives being asked about the balance items it paid for', async () => {
|
|
46
|
+
// A balance item doesn't carry how it was paid, so the payout tab of a balance item breakdown
|
|
47
|
+
// selects the items through their payments. A field that only the payments table knows about
|
|
48
|
+
// would only break there, at runtime.
|
|
49
|
+
for (const group of groups) {
|
|
50
|
+
await expect(compileToSQLFilter(toBalanceItemFilter(group.filter), balanceItemFilterCompilers)).resolves.toBeDefined();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('covers every kind of payout group', () => {
|
|
55
|
+
// Guards the fixtures above: a new kind of group has to be added here before it is covered
|
|
56
|
+
expect([...new Set(groups.map(group => group.id))].sort()).toEqual([
|
|
57
|
+
ACCOUNT_DEDUCTIONS_ID,
|
|
58
|
+
FAILED_PAYMENT_ID,
|
|
59
|
+
'no-payout-info-none',
|
|
60
|
+
'no-payout-info-' + PaymentProvider.Payconiq,
|
|
61
|
+
'not-settled',
|
|
62
|
+
// Every method that brings money in outside a provider shares one group
|
|
63
|
+
'offline',
|
|
64
|
+
PENDING_PAYMENT_ID,
|
|
65
|
+
'settlement-' + PaymentProvider.Stripe + '-' + settlement.reference + '-' + settlement.settledAt.getTime(),
|
|
66
|
+
].sort());
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { SQLFilterDefinitions } from '@stamhoofd/sql';
|
|
2
|
+
import { baseSQLFilterCompilers, createColumnFilter, SQL, SQLValueType } from '@stamhoofd/sql';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* How a payment is selected by the payout it was part of (see PaymentSettlementGroup).
|
|
6
|
+
*
|
|
7
|
+
* Shared by the payments filters and by the balance items filters, which reach the same columns through
|
|
8
|
+
* the payments that paid for them, so both select exactly the same payments.
|
|
9
|
+
*
|
|
10
|
+
* All expressions are qualified with the payments table so they keep working in a subquery that joins
|
|
11
|
+
* payments (see balance-items.ts).
|
|
12
|
+
*/
|
|
13
|
+
export const paymentSettlementFilterCompilers: SQLFilterDefinitions = {
|
|
14
|
+
...baseSQLFilterCompilers,
|
|
15
|
+
method: createColumnFilter({
|
|
16
|
+
expression: SQL.column('payments', 'method'),
|
|
17
|
+
type: SQLValueType.String,
|
|
18
|
+
nullable: false,
|
|
19
|
+
}),
|
|
20
|
+
provider: createColumnFilter({
|
|
21
|
+
expression: SQL.column('payments', 'provider'),
|
|
22
|
+
type: SQLValueType.String,
|
|
23
|
+
nullable: true,
|
|
24
|
+
}),
|
|
25
|
+
status: createColumnFilter({
|
|
26
|
+
expression: SQL.column('payments', 'status'),
|
|
27
|
+
type: SQLValueType.String,
|
|
28
|
+
nullable: false,
|
|
29
|
+
}),
|
|
30
|
+
settlement: {
|
|
31
|
+
...baseSQLFilterCompilers,
|
|
32
|
+
reference: createColumnFilter({
|
|
33
|
+
expression: SQL.jsonExtract(SQL.column('payments', 'settlement'), '$.value.reference'),
|
|
34
|
+
type: SQLValueType.JSONString,
|
|
35
|
+
nullable: true,
|
|
36
|
+
}),
|
|
37
|
+
settledAt: createColumnFilter({
|
|
38
|
+
expression: SQL.jsonExtract(SQL.column('payments', 'settlement'), '$.value.settledAt'),
|
|
39
|
+
type: SQLValueType.JSONDate,
|
|
40
|
+
nullable: true,
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -1,41 +1,39 @@
|
|
|
1
1
|
import { Payment } from '@stamhoofd/models';
|
|
2
2
|
import type { SQLFilterDefinitions } from '@stamhoofd/sql';
|
|
3
3
|
import { baseSQLFilterCompilers, createColumnFilter, createExistsFilter, createJoinedRelationFilter, SQL, SQLCast, SQLConcat, SQLJsonUnquote, SQLScalar, SQLValueType } from '@stamhoofd/sql';
|
|
4
|
+
import type { StamhoofdFilter } from '@stamhoofd/structures';
|
|
5
|
+
import { TransferSettings } from '@stamhoofd/structures';
|
|
4
6
|
import { balanceItemPaymentsCompilers } from './balance-item-payments.js';
|
|
5
7
|
import { organizationFilterCompilers } from './organizations.js';
|
|
8
|
+
import { paymentSettlementFilterCompilers } from './payment-settlement.js';
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Defines how to filter payments in the database from StamhoofdFilter objects
|
|
12
|
+
*
|
|
13
|
+
* All expressions are qualified with the payments table so they keep working when payments is joined
|
|
14
|
+
* into another query instead of being selected from (see balance-item-payments-root.ts).
|
|
9
15
|
*/
|
|
10
16
|
export const paymentFilterCompilers: SQLFilterDefinitions = {
|
|
11
17
|
...baseSQLFilterCompilers,
|
|
18
|
+
// method, provider, status and settlement
|
|
19
|
+
...paymentSettlementFilterCompilers,
|
|
12
20
|
id: createColumnFilter({
|
|
13
|
-
expression: SQL.column('id'),
|
|
14
|
-
type: SQLValueType.String,
|
|
15
|
-
nullable: false,
|
|
16
|
-
}),
|
|
17
|
-
method: createColumnFilter({
|
|
18
|
-
expression: SQL.column(Payment.table, 'method'),
|
|
21
|
+
expression: SQL.column(Payment.table, 'id'),
|
|
19
22
|
type: SQLValueType.String,
|
|
20
23
|
nullable: false,
|
|
21
24
|
}),
|
|
22
25
|
type: createColumnFilter({
|
|
23
|
-
expression: SQL.column('type'),
|
|
24
|
-
type: SQLValueType.String,
|
|
25
|
-
nullable: false,
|
|
26
|
-
}),
|
|
27
|
-
status: createColumnFilter({
|
|
28
|
-
expression: SQL.column(Payment.table, 'status'),
|
|
26
|
+
expression: SQL.column(Payment.table, 'type'),
|
|
29
27
|
type: SQLValueType.String,
|
|
30
28
|
nullable: false,
|
|
31
29
|
}),
|
|
32
30
|
organizationId: createColumnFilter({
|
|
33
|
-
expression: SQL.column('organizationId'),
|
|
31
|
+
expression: SQL.column(Payment.table, 'organizationId'),
|
|
34
32
|
type: SQLValueType.String,
|
|
35
33
|
nullable: true,
|
|
36
34
|
}),
|
|
37
35
|
payingOrganizationId: createColumnFilter({
|
|
38
|
-
expression: SQL.column('payingOrganizationId'),
|
|
36
|
+
expression: SQL.column(Payment.table, 'payingOrganizationId'),
|
|
39
37
|
type: SQLValueType.String,
|
|
40
38
|
nullable: true,
|
|
41
39
|
}),
|
|
@@ -44,22 +42,22 @@ export const paymentFilterCompilers: SQLFilterDefinitions = {
|
|
|
44
42
|
organizationFilterCompilers,
|
|
45
43
|
),
|
|
46
44
|
invoiceId: createColumnFilter({
|
|
47
|
-
expression: SQL.column('invoiceId'),
|
|
45
|
+
expression: SQL.column(Payment.table, 'invoiceId'),
|
|
48
46
|
type: SQLValueType.String,
|
|
49
47
|
nullable: true,
|
|
50
48
|
}),
|
|
51
49
|
createdAt: createColumnFilter({
|
|
52
|
-
expression: SQL.column('createdAt'),
|
|
50
|
+
expression: SQL.column(Payment.table, 'createdAt'),
|
|
53
51
|
type: SQLValueType.Datetime,
|
|
54
52
|
nullable: false,
|
|
55
53
|
}),
|
|
56
54
|
updatedAt: createColumnFilter({
|
|
57
|
-
expression: SQL.column('updatedAt'),
|
|
55
|
+
expression: SQL.column(Payment.table, 'updatedAt'),
|
|
58
56
|
type: SQLValueType.Datetime,
|
|
59
57
|
nullable: false,
|
|
60
58
|
}),
|
|
61
59
|
paidAt: createColumnFilter({
|
|
62
|
-
expression: SQL.column('paidAt'),
|
|
60
|
+
expression: SQL.column(Payment.table, 'paidAt'),
|
|
63
61
|
type: SQLValueType.Datetime,
|
|
64
62
|
nullable: true,
|
|
65
63
|
}),
|
|
@@ -68,44 +66,73 @@ export const paymentFilterCompilers: SQLFilterDefinitions = {
|
|
|
68
66
|
type: SQLValueType.Number,
|
|
69
67
|
nullable: false,
|
|
70
68
|
}),
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
|
|
70
|
+
stripeAccountId: createColumnFilter({
|
|
71
|
+
expression: SQL.column(Payment.table, 'stripeAccountId'),
|
|
73
72
|
type: SQLValueType.String,
|
|
74
73
|
nullable: true,
|
|
75
74
|
}),
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* What a payment rounded away, which belongs to the payment as a whole instead of to one of the
|
|
78
|
+
* things it paid for (see PaymentBreakdownBuilder.addRounding).
|
|
79
|
+
*/
|
|
80
|
+
roundingAmount: createColumnFilter({
|
|
81
|
+
expression: SQL.column(Payment.table, 'roundingAmount'),
|
|
82
|
+
type: SQLValueType.Number,
|
|
83
|
+
nullable: false,
|
|
84
|
+
}),
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The account a transfer was made to. Used to narrow a breakdown down to the money that arrived on
|
|
88
|
+
* one account (see PaymentBreakdown).
|
|
89
|
+
*/
|
|
90
|
+
transferSettings: {
|
|
91
|
+
...baseSQLFilterCompilers,
|
|
92
|
+
iban: createColumnFilter({
|
|
93
|
+
expression: SQL.jsonExtract(SQL.column(Payment.table, 'transferSettings'), '$.value.iban'),
|
|
94
|
+
type: SQLValueType.JSONString,
|
|
95
|
+
nullable: true,
|
|
96
|
+
}),
|
|
97
|
+
creditor: createColumnFilter({
|
|
98
|
+
expression: SQL.jsonExtract(SQL.column(Payment.table, 'transferSettings'), '$.value.creditor'),
|
|
99
|
+
type: SQLValueType.JSONString,
|
|
100
|
+
nullable: true,
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
76
103
|
transferDescription: createColumnFilter({
|
|
77
104
|
expression: SQL.column(Payment.table, 'transferDescription'),
|
|
78
105
|
type: SQLValueType.String,
|
|
79
106
|
nullable: true,
|
|
80
107
|
}),
|
|
81
108
|
hasInvoice: createColumnFilter({
|
|
82
|
-
expression: SQL.isNull(SQL.column('invoiceId')),
|
|
109
|
+
expression: SQL.isNull(SQL.column(Payment.table, 'invoiceId')),
|
|
83
110
|
type: SQLValueType.Boolean,
|
|
84
111
|
nullable: false,
|
|
85
112
|
}),
|
|
86
113
|
customer: {
|
|
87
114
|
...baseSQLFilterCompilers,
|
|
88
115
|
email: createColumnFilter({
|
|
89
|
-
expression: SQL.jsonExtract(SQL.column('customer'), '$.value.email'),
|
|
116
|
+
expression: SQL.jsonExtract(SQL.column(Payment.table, 'customer'), '$.value.email'),
|
|
90
117
|
type: SQLValueType.JSONString,
|
|
91
118
|
nullable: true,
|
|
92
119
|
}),
|
|
93
120
|
firstName: createColumnFilter({
|
|
94
|
-
expression: SQL.jsonExtract(SQL.column('customer'), '$.value.firstName'),
|
|
121
|
+
expression: SQL.jsonExtract(SQL.column(Payment.table, 'customer'), '$.value.firstName'),
|
|
95
122
|
type: SQLValueType.JSONString,
|
|
96
123
|
nullable: true,
|
|
97
124
|
}),
|
|
98
125
|
lastName: createColumnFilter({
|
|
99
|
-
expression: SQL.jsonExtract(SQL.column('customer'), '$.value.lastName'),
|
|
126
|
+
expression: SQL.jsonExtract(SQL.column(Payment.table, 'customer'), '$.value.lastName'),
|
|
100
127
|
type: SQLValueType.JSONString,
|
|
101
128
|
nullable: true,
|
|
102
129
|
}),
|
|
103
130
|
name: createColumnFilter({
|
|
104
131
|
expression: new SQLCast(
|
|
105
132
|
new SQLConcat(
|
|
106
|
-
new SQLJsonUnquote(SQL.jsonExtract(SQL.column('customer'), '$.value.firstName')),
|
|
133
|
+
new SQLJsonUnquote(SQL.jsonExtract(SQL.column(Payment.table, 'customer'), '$.value.firstName')),
|
|
107
134
|
new SQLScalar(' '),
|
|
108
|
-
new SQLJsonUnquote(SQL.jsonExtract(SQL.column('customer'), '$.value.lastName')),
|
|
135
|
+
new SQLJsonUnquote(SQL.jsonExtract(SQL.column(Payment.table, 'customer'), '$.value.lastName')),
|
|
109
136
|
),
|
|
110
137
|
'CHAR',
|
|
111
138
|
),
|
|
@@ -115,22 +142,22 @@ export const paymentFilterCompilers: SQLFilterDefinitions = {
|
|
|
115
142
|
company: {
|
|
116
143
|
...baseSQLFilterCompilers,
|
|
117
144
|
name: createColumnFilter({
|
|
118
|
-
expression: SQL.jsonExtract(SQL.column('customer'), '$.value.company.name'),
|
|
145
|
+
expression: SQL.jsonExtract(SQL.column(Payment.table, 'customer'), '$.value.company.name'),
|
|
119
146
|
type: SQLValueType.JSONString,
|
|
120
147
|
nullable: true,
|
|
121
148
|
}),
|
|
122
149
|
VATNumber: createColumnFilter({
|
|
123
|
-
expression: SQL.jsonExtract(SQL.column('customer'), '$.value.company.VATNumber'),
|
|
150
|
+
expression: SQL.jsonExtract(SQL.column(Payment.table, 'customer'), '$.value.company.VATNumber'),
|
|
124
151
|
type: SQLValueType.JSONString,
|
|
125
152
|
nullable: true,
|
|
126
153
|
}),
|
|
127
154
|
companyNumber: createColumnFilter({
|
|
128
|
-
expression: SQL.jsonExtract(SQL.column('customer'), '$.value.company.companyNumber'),
|
|
155
|
+
expression: SQL.jsonExtract(SQL.column(Payment.table, 'customer'), '$.value.company.companyNumber'),
|
|
129
156
|
type: SQLValueType.JSONString,
|
|
130
157
|
nullable: true,
|
|
131
158
|
}),
|
|
132
159
|
administrationEmail: createColumnFilter({
|
|
133
|
-
expression: SQL.jsonExtract(SQL.column('customer'), '$.value.company.administrationEmail'),
|
|
160
|
+
expression: SQL.jsonExtract(SQL.column(Payment.table, 'customer'), '$.value.company.administrationEmail'),
|
|
134
161
|
type: SQLValueType.JSONString,
|
|
135
162
|
nullable: true,
|
|
136
163
|
}),
|
|
@@ -148,9 +175,44 @@ export const paymentFilterCompilers: SQLFilterDefinitions = {
|
|
|
148
175
|
SQL.column('balance_item_payments', 'balanceItemId'),
|
|
149
176
|
),
|
|
150
177
|
).where(
|
|
151
|
-
SQL.column('paymentId'),
|
|
178
|
+
SQL.column('balance_item_payments', 'paymentId'),
|
|
152
179
|
SQL.column('payments', 'id'),
|
|
153
180
|
),
|
|
154
181
|
balanceItemPaymentsCompilers,
|
|
155
182
|
),
|
|
156
183
|
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* What a search term typed in a list of payments selects, as a filter on the payments table.
|
|
187
|
+
*
|
|
188
|
+
* Shared by everything that lists or exports payments, so a search always selects the same payments no
|
|
189
|
+
* matter which of those it went through.
|
|
190
|
+
*/
|
|
191
|
+
export function getPaymentSearchFilter(search: string): StamhoofdFilter {
|
|
192
|
+
const transferDescription = search.replaceAll('+', '').replaceAll('/', '');
|
|
193
|
+
|
|
194
|
+
if (transferDescription.length === '562100153542'.length && !isNaN(parseInt(transferDescription))) {
|
|
195
|
+
// A structured transfer reference is only ever meant to find that one payment
|
|
196
|
+
return {
|
|
197
|
+
transferDescription: TransferSettings.structureOGM(transferDescription),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (search.includes('@')) {
|
|
202
|
+
return {
|
|
203
|
+
$or: [
|
|
204
|
+
{ customer: { email: { $contains: search } } },
|
|
205
|
+
{ customer: { company: { administrationEmail: { $contains: search } } } },
|
|
206
|
+
],
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
$or: [
|
|
212
|
+
{ customer: { name: { $contains: search } } },
|
|
213
|
+
{ customer: { company: { name: { $contains: search } } } },
|
|
214
|
+
{ balanceItemPayments: { $elemMatch: { balanceItem: { description: { $contains: search } } } } },
|
|
215
|
+
{ transferDescription: { $contains: search } },
|
|
216
|
+
],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { BalanceItemPayment } from '@stamhoofd/models';
|
|
2
|
+
import type { SQLOrderByDirection, SQLSortDefinitions } from '@stamhoofd/sql';
|
|
3
|
+
import { SQL, SQLOrderBy } from '@stamhoofd/sql';
|
|
4
|
+
import { Formatter } from '@stamhoofd/utility';
|
|
5
|
+
|
|
6
|
+
export const balanceItemPaymentSorters: SQLSortDefinitions<BalanceItemPayment> = {
|
|
7
|
+
// WARNING! TEST NEW SORTERS THOROUGHLY! See balanceItemSorters for why sorting on anything that is
|
|
8
|
+
// not 1:1 with a column breaks pagination.
|
|
9
|
+
|
|
10
|
+
id: {
|
|
11
|
+
getValue(a) {
|
|
12
|
+
return a.id;
|
|
13
|
+
},
|
|
14
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
15
|
+
return new SQLOrderBy({
|
|
16
|
+
column: SQL.column('balance_item_payments', 'id'),
|
|
17
|
+
direction,
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
createdAt: {
|
|
22
|
+
getValue(a) {
|
|
23
|
+
return Formatter.dateTimeIso(a.createdAt, 'UTC');
|
|
24
|
+
},
|
|
25
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
26
|
+
return new SQLOrderBy({
|
|
27
|
+
column: SQL.column('balance_item_payments', 'createdAt'),
|
|
28
|
+
direction,
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
};
|