@stamhoofd/backend 2.55.2 → 2.57.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/index.ts +4 -0
- package/package.json +12 -11
- package/src/crons.ts +4 -3
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +150 -0
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +27 -3
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +27 -9
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +2 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +17 -2
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +17 -1
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +12 -9
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +10 -1
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +5 -3
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +21 -1
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +4 -306
- package/src/helpers/AdminPermissionChecker.ts +102 -1
- package/src/helpers/AuthenticatedStructures.ts +46 -2
- package/src/helpers/EmailResumer.ts +8 -3
- package/src/seeds/1732117645-move-rrn.ts +77 -0
- package/src/services/AuditLogService.ts +232 -0
- package/src/services/BalanceItemPaymentService.ts +45 -0
- package/src/services/BalanceItemService.ts +88 -0
- package/src/services/GroupService.ts +13 -0
- package/src/services/PaymentService.ts +308 -0
- package/src/services/RegistrationService.ts +78 -0
- package/src/services/explainPatch.ts +639 -0
- package/src/sql-filters/audit-logs.ts +10 -0
- package/src/sql-sorters/audit-logs.ts +35 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Migration } from '@simonbackx/simple-database';
|
|
2
|
+
import { Member } from '@stamhoofd/models';
|
|
3
|
+
import { MemberUserSyncer } from '../helpers/MemberUserSyncer';
|
|
4
|
+
import { logger } from '@simonbackx/simple-logging';
|
|
5
|
+
|
|
6
|
+
export default new Migration(async () => {
|
|
7
|
+
if (STAMHOOFD.environment == 'test') {
|
|
8
|
+
console.log('skipped in tests');
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (STAMHOOFD.userMode !== 'platform') {
|
|
13
|
+
console.log('skipped seed update-membership because usermode not platform');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
process.stdout.write('\n');
|
|
18
|
+
let c = 0;
|
|
19
|
+
let id: string = '';
|
|
20
|
+
|
|
21
|
+
await logger.setContext({ tags: ['silent-seed', 'seed'] }, async () => {
|
|
22
|
+
while (true) {
|
|
23
|
+
const rawMembers = await Member.where({
|
|
24
|
+
id: {
|
|
25
|
+
value: id,
|
|
26
|
+
sign: '>',
|
|
27
|
+
},
|
|
28
|
+
}, { limit: 500, sort: ['id'] });
|
|
29
|
+
|
|
30
|
+
if (rawMembers.length === 0) {
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const promises: Promise<any>[] = [];
|
|
35
|
+
|
|
36
|
+
for (const member of rawMembers) {
|
|
37
|
+
promises.push((async () => {
|
|
38
|
+
const idR = '2b7d8f1c-ce67-4612-880b-fb1fb19affbb';
|
|
39
|
+
const valR = member.details.recordAnswers.get(idR);
|
|
40
|
+
|
|
41
|
+
const idRS = 'd381acce-9603-4246-af62-f3ea5292eec7';
|
|
42
|
+
const valRS = member.details.recordAnswers.get(idRS);
|
|
43
|
+
|
|
44
|
+
let save = false;
|
|
45
|
+
|
|
46
|
+
if (valR && !member.details.nationalRegisterNumber) {
|
|
47
|
+
member.details.nationalRegisterNumber = valR.stringValue;
|
|
48
|
+
save = true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (valRS && member.details.parents.length > 0 && !member.details.parents.find(p => p.nationalRegisterNumber)) {
|
|
52
|
+
member.details.parents[0].nationalRegisterNumber = valRS.stringValue;
|
|
53
|
+
save = true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (save) {
|
|
57
|
+
await member.save();
|
|
58
|
+
}
|
|
59
|
+
c++;
|
|
60
|
+
|
|
61
|
+
if (c % 1000 === 0) {
|
|
62
|
+
process.stdout.write('.');
|
|
63
|
+
}
|
|
64
|
+
if (c % 10000 === 0) {
|
|
65
|
+
process.stdout.write('\n');
|
|
66
|
+
}
|
|
67
|
+
})());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
await Promise.all(promises);
|
|
71
|
+
id = rawMembers[rawMembers.length - 1].id;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Do something here
|
|
76
|
+
return Promise.resolve();
|
|
77
|
+
});
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { AutoEncoder, AutoEncoderPatchType } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { AuditLog, Group, Member, Organization, Registration, Event } from '@stamhoofd/models';
|
|
3
|
+
import { AuditLogReplacement, AuditLogReplacementType, AuditLogType, GroupType, MemberDetails, OrganizationMetaData, OrganizationPrivateMetaData, PlatformConfig, PlatformPrivateConfig } from '@stamhoofd/structures';
|
|
4
|
+
import { Context } from '../helpers/Context';
|
|
5
|
+
import { explainPatch } from './explainPatch';
|
|
6
|
+
|
|
7
|
+
export type MemberAddedAuditOptions = {
|
|
8
|
+
type: AuditLogType.MemberAdded;
|
|
9
|
+
member: Member;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type MemberEditedAuditOptions = {
|
|
13
|
+
type: AuditLogType.MemberEdited;
|
|
14
|
+
member: Member;
|
|
15
|
+
oldMemberDetails: MemberDetails;
|
|
16
|
+
memberDetailsPatch: AutoEncoderPatchType<MemberDetails>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type MemberRegisteredAuditOptions = {
|
|
20
|
+
type: AuditLogType.MemberRegistered | AuditLogType.MemberUnregistered;
|
|
21
|
+
member: Member;
|
|
22
|
+
group: Group;
|
|
23
|
+
registration: Registration;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type PlatformConfigChangeAuditOptions = {
|
|
27
|
+
type: AuditLogType.PlatformSettingsChanged;
|
|
28
|
+
} & ({
|
|
29
|
+
oldConfig: PlatformPrivateConfig;
|
|
30
|
+
patch: PlatformPrivateConfig | AutoEncoderPatchType<PlatformPrivateConfig>;
|
|
31
|
+
} | {
|
|
32
|
+
oldConfig: PlatformConfig;
|
|
33
|
+
patch: PlatformConfig | AutoEncoderPatchType<PlatformConfig>;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type OrganizationConfigChangeAuditOptions = {
|
|
37
|
+
type: AuditLogType.OrganizationSettingsChanged;
|
|
38
|
+
organization: Organization;
|
|
39
|
+
} & ({
|
|
40
|
+
oldMeta: OrganizationMetaData;
|
|
41
|
+
patch: OrganizationMetaData | AutoEncoderPatchType<OrganizationMetaData>;
|
|
42
|
+
} | {
|
|
43
|
+
oldMeta: OrganizationPrivateMetaData;
|
|
44
|
+
patch: OrganizationPrivateMetaData | AutoEncoderPatchType<OrganizationPrivateMetaData>;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export type EventAuditOptions = {
|
|
48
|
+
type: AuditLogType.EventAdded | AuditLogType.EventEdited | AuditLogType.EventDeleted;
|
|
49
|
+
event: Event;
|
|
50
|
+
oldData?: AutoEncoder;
|
|
51
|
+
patch?: AutoEncoder | AutoEncoderPatchType<AutoEncoder>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type GroupAuditOptions = {
|
|
55
|
+
type: AuditLogType.GroupAdded | AuditLogType.GroupEdited | AuditLogType.GroupDeleted;
|
|
56
|
+
group: Group;
|
|
57
|
+
oldData?: AutoEncoder;
|
|
58
|
+
patch?: AutoEncoder | AutoEncoderPatchType<AutoEncoder>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type AuditLogOptions = GroupAuditOptions | EventAuditOptions | MemberAddedAuditOptions | MemberEditedAuditOptions | MemberRegisteredAuditOptions | PlatformConfigChangeAuditOptions | OrganizationConfigChangeAuditOptions;
|
|
62
|
+
|
|
63
|
+
export const AuditLogService = {
|
|
64
|
+
async log(options: AuditLogOptions) {
|
|
65
|
+
try {
|
|
66
|
+
const userId = Context.optionalAuth?.user?.id ?? null;
|
|
67
|
+
const organizationId = Context.organization?.id ?? null;
|
|
68
|
+
|
|
69
|
+
const model = new AuditLog();
|
|
70
|
+
|
|
71
|
+
model.type = options.type;
|
|
72
|
+
model.userId = userId;
|
|
73
|
+
model.organizationId = organizationId;
|
|
74
|
+
|
|
75
|
+
if (options.type === AuditLogType.MemberRegistered) {
|
|
76
|
+
this.fillForMemberRegistered(model, options);
|
|
77
|
+
}
|
|
78
|
+
else if (options.type === AuditLogType.MemberUnregistered) {
|
|
79
|
+
this.fillForMemberRegistered(model, options);
|
|
80
|
+
}
|
|
81
|
+
else if (options.type === AuditLogType.MemberEdited) {
|
|
82
|
+
this.fillForMemberEdited(model, options);
|
|
83
|
+
}
|
|
84
|
+
else if (options.type === AuditLogType.MemberAdded) {
|
|
85
|
+
this.fillForMemberAdded(model, options);
|
|
86
|
+
}
|
|
87
|
+
else if (options.type === AuditLogType.PlatformSettingsChanged) {
|
|
88
|
+
this.fillForPlatformConfig(model, options);
|
|
89
|
+
}
|
|
90
|
+
else if (options.type === AuditLogType.OrganizationSettingsChanged) {
|
|
91
|
+
this.fillForOrganizationConfig(model, options);
|
|
92
|
+
}
|
|
93
|
+
else if (options.type === AuditLogType.EventAdded || options.type === AuditLogType.EventEdited || options.type === AuditLogType.EventDeleted) {
|
|
94
|
+
this.fillForEvent(model, options);
|
|
95
|
+
}
|
|
96
|
+
else if (options.type === AuditLogType.GroupAdded || options.type === AuditLogType.GroupEdited || options.type === AuditLogType.GroupDeleted) {
|
|
97
|
+
this.fillForGroup(model, options);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// In the future we might group these saves together in one query to improve performance
|
|
101
|
+
await model.save();
|
|
102
|
+
|
|
103
|
+
console.log('Audit log', model.id, options);
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
console.error('Failed to save log', options, e);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
fillForMemberRegistered(model: AuditLog, options: MemberRegisteredAuditOptions) {
|
|
111
|
+
model.objectId = options.member.id;
|
|
112
|
+
model.replacements = new Map([
|
|
113
|
+
['m', AuditLogReplacement.create({
|
|
114
|
+
id: options.member.id,
|
|
115
|
+
value: options.member.details.name,
|
|
116
|
+
type: AuditLogReplacementType.Member,
|
|
117
|
+
})],
|
|
118
|
+
['g', AuditLogReplacement.create({
|
|
119
|
+
id: options.group.id,
|
|
120
|
+
value: options.group.settings.name,
|
|
121
|
+
type: AuditLogReplacementType.Group,
|
|
122
|
+
})],
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
const registrationStructure = options.registration.setRelation(Registration.group, options.group).getStructure();
|
|
126
|
+
if (registrationStructure.description) {
|
|
127
|
+
model.description = registrationStructure.description;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
fillForMemberEdited(model: AuditLog, options: MemberEditedAuditOptions) {
|
|
132
|
+
model.objectId = options.member.id;
|
|
133
|
+
|
|
134
|
+
model.replacements = new Map([
|
|
135
|
+
['m', AuditLogReplacement.create({
|
|
136
|
+
id: options.member.id,
|
|
137
|
+
value: options.member.details.name,
|
|
138
|
+
type: AuditLogReplacementType.Member,
|
|
139
|
+
})],
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
// Generate changes list
|
|
143
|
+
model.patchList = explainPatch(options.oldMemberDetails, options.memberDetailsPatch);
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
fillForMemberAdded(model: AuditLog, options: MemberAddedAuditOptions) {
|
|
147
|
+
model.objectId = options.member.id;
|
|
148
|
+
|
|
149
|
+
model.replacements = new Map([
|
|
150
|
+
['m', AuditLogReplacement.create({
|
|
151
|
+
id: options.member.id,
|
|
152
|
+
value: options.member.details.name,
|
|
153
|
+
type: AuditLogReplacementType.Member,
|
|
154
|
+
})],
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
// Generate changes list
|
|
158
|
+
model.patchList = explainPatch(null, options.member.details);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
fillForPlatformConfig(model: AuditLog, options: PlatformConfigChangeAuditOptions) {
|
|
162
|
+
model.objectId = null;
|
|
163
|
+
|
|
164
|
+
// Generate changes list
|
|
165
|
+
model.patchList = explainPatch(options.oldConfig, options.patch);
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
fillForOrganizationConfig(model: AuditLog, options: OrganizationConfigChangeAuditOptions) {
|
|
169
|
+
model.objectId = options.organization.id;
|
|
170
|
+
model.organizationId = options.organization.id;
|
|
171
|
+
|
|
172
|
+
model.replacements = new Map([
|
|
173
|
+
['o', AuditLogReplacement.create({
|
|
174
|
+
id: options.organization.id,
|
|
175
|
+
value: options.organization.name,
|
|
176
|
+
type: AuditLogReplacementType.Organization,
|
|
177
|
+
})],
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
// Generate changes list
|
|
181
|
+
model.patchList = explainPatch(options.oldMeta, options.patch);
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
fillForEvent(model: AuditLog, options: EventAuditOptions) {
|
|
185
|
+
model.objectId = options.event.id;
|
|
186
|
+
|
|
187
|
+
if (options.patch) {
|
|
188
|
+
// Generate changes list
|
|
189
|
+
model.patchList = explainPatch(options.oldData ?? null, options.patch);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
model.replacements = new Map([
|
|
193
|
+
['e', AuditLogReplacement.create({
|
|
194
|
+
id: options.event.id,
|
|
195
|
+
value: options.event.name,
|
|
196
|
+
type: AuditLogReplacementType.Event,
|
|
197
|
+
})],
|
|
198
|
+
]);
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
fillForGroup(model: AuditLog, options: GroupAuditOptions) {
|
|
202
|
+
model.objectId = options.group.id;
|
|
203
|
+
|
|
204
|
+
if (options.patch) {
|
|
205
|
+
// Generate changes list
|
|
206
|
+
model.patchList = explainPatch(options.oldData ?? null, options.patch);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (options.group.type === GroupType.WaitingList) {
|
|
210
|
+
// Change event type
|
|
211
|
+
switch (options.type) {
|
|
212
|
+
case AuditLogType.GroupAdded:
|
|
213
|
+
model.type = AuditLogType.WaitingListAdded;
|
|
214
|
+
break;
|
|
215
|
+
case AuditLogType.GroupEdited:
|
|
216
|
+
model.type = AuditLogType.WaitingListEdited;
|
|
217
|
+
break;
|
|
218
|
+
case AuditLogType.GroupDeleted:
|
|
219
|
+
model.type = AuditLogType.WaitingListDeleted;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
model.replacements = new Map([
|
|
225
|
+
['g', AuditLogReplacement.create({
|
|
226
|
+
id: options.group.id,
|
|
227
|
+
value: options.group.settings.name,
|
|
228
|
+
type: AuditLogReplacementType.Group,
|
|
229
|
+
})],
|
|
230
|
+
]);
|
|
231
|
+
},
|
|
232
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ManyToOneRelation } from '@simonbackx/simple-database';
|
|
2
|
+
import { BalanceItemPayment, Organization } from '@stamhoofd/models';
|
|
3
|
+
import { BalanceItemStatus } from '@stamhoofd/structures';
|
|
4
|
+
import { BalanceItemService } from './BalanceItemService';
|
|
5
|
+
|
|
6
|
+
type Loaded<T> = (T) extends ManyToOneRelation<infer Key, infer Model> ? Record<Key, Model> : never;
|
|
7
|
+
|
|
8
|
+
export const BalanceItemPaymentService = {
|
|
9
|
+
async markPaid(balanceItemPayment: BalanceItemPayment & Loaded<typeof BalanceItemPayment.balanceItem> & Loaded<typeof BalanceItemPayment.payment>, organization: Organization) {
|
|
10
|
+
// Update cached amountPaid of the balance item (balanceItemPayment will get overwritten later, but we need it to calculate the status)
|
|
11
|
+
balanceItemPayment.balanceItem.pricePaid += balanceItemPayment.price;
|
|
12
|
+
|
|
13
|
+
// Update status
|
|
14
|
+
const old = balanceItemPayment.balanceItem.status;
|
|
15
|
+
balanceItemPayment.balanceItem.updateStatus();
|
|
16
|
+
await balanceItemPayment.balanceItem.save();
|
|
17
|
+
|
|
18
|
+
// Do logic of balance item
|
|
19
|
+
if (balanceItemPayment.balanceItem.status === BalanceItemStatus.Paid && old !== BalanceItemStatus.Paid) {
|
|
20
|
+
// Only call markPaid once (if it wasn't (partially) paid before)
|
|
21
|
+
await BalanceItemService.markPaid(balanceItemPayment.balanceItem, balanceItemPayment.payment, organization);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
await BalanceItemService.markUpdated(balanceItemPayment.balanceItem, balanceItemPayment.payment, organization);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Call balanceItemPayment once a earlier succeeded payment is no longer succeeded
|
|
30
|
+
*/
|
|
31
|
+
async undoPaid(balanceItemPayment: BalanceItemPayment & Loaded<typeof BalanceItemPayment.balanceItem> & Loaded<typeof BalanceItemPayment.payment>, organization: Organization) {
|
|
32
|
+
await BalanceItemService.undoPaid(balanceItemPayment.balanceItem, balanceItemPayment.payment, organization);
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
async markFailed(balanceItemPayment: BalanceItemPayment & Loaded<typeof BalanceItemPayment.balanceItem> & Loaded<typeof BalanceItemPayment.payment>, organization: Organization) {
|
|
36
|
+
// Do logic of balance item
|
|
37
|
+
await BalanceItemService.markFailed(balanceItemPayment.balanceItem, balanceItemPayment.payment, organization);
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
async undoFailed(balanceItemPayment: BalanceItemPayment & Loaded<typeof BalanceItemPayment.balanceItem> & Loaded<typeof BalanceItemPayment.payment>, organization: Organization) {
|
|
41
|
+
// Reactivate deleted items
|
|
42
|
+
await BalanceItemService.undoFailed(balanceItemPayment.balanceItem, balanceItemPayment.payment, organization);
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { BalanceItem, Order, Organization, Payment, Webshop } from '@stamhoofd/models';
|
|
2
|
+
import { BalanceItemStatus, OrderStatus } from '@stamhoofd/structures';
|
|
3
|
+
import { RegistrationService } from './RegistrationService';
|
|
4
|
+
|
|
5
|
+
export const BalanceItemService = {
|
|
6
|
+
async markPaid(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
|
|
7
|
+
if (balanceItem.status === BalanceItemStatus.Hidden) {
|
|
8
|
+
await BalanceItem.reactivateItems([balanceItem]);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// status and pricePaid changes are handled inside balanceitempayment
|
|
12
|
+
if (balanceItem.dependingBalanceItemId) {
|
|
13
|
+
const depending = await BalanceItem.getByID(balanceItem.dependingBalanceItemId);
|
|
14
|
+
if (depending && depending.status === BalanceItemStatus.Hidden) {
|
|
15
|
+
await BalanceItem.reactivateItems([depending]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// If registration
|
|
20
|
+
if (balanceItem.registrationId) {
|
|
21
|
+
await RegistrationService.markValid(balanceItem.registrationId);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// If order
|
|
25
|
+
if (balanceItem.orderId) {
|
|
26
|
+
const order = await Order.getByID(balanceItem.orderId);
|
|
27
|
+
if (order) {
|
|
28
|
+
await order.markPaid(payment, organization);
|
|
29
|
+
|
|
30
|
+
// Save number in balance description
|
|
31
|
+
if (order.number !== null) {
|
|
32
|
+
const webshop = await Webshop.getByID(order.webshopId);
|
|
33
|
+
|
|
34
|
+
if (webshop) {
|
|
35
|
+
balanceItem.description = order.generateBalanceDescription(webshop);
|
|
36
|
+
await balanceItem.save();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
async markUpdated(balanceItem: BalanceItem, payment: Payment, organization: Organization) {
|
|
44
|
+
// For orders: mark order as changed (so they are refetched in front ends)
|
|
45
|
+
if (balanceItem.orderId) {
|
|
46
|
+
const order = await Order.getByID(balanceItem.orderId);
|
|
47
|
+
if (order) {
|
|
48
|
+
await order.paymentChanged(payment, organization);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async undoPaid(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
|
|
54
|
+
// If order
|
|
55
|
+
if (balanceItem.orderId) {
|
|
56
|
+
const order = await Order.getByID(balanceItem.orderId);
|
|
57
|
+
if (order) {
|
|
58
|
+
await order.undoPaid(payment, organization);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
async markFailed(balanceItem: BalanceItem, payment: Payment, organization: Organization) {
|
|
64
|
+
// If order
|
|
65
|
+
if (balanceItem.orderId) {
|
|
66
|
+
const order = await Order.getByID(balanceItem.orderId);
|
|
67
|
+
if (order) {
|
|
68
|
+
await order.onPaymentFailed(payment, organization);
|
|
69
|
+
|
|
70
|
+
if (order.status === OrderStatus.Deleted) {
|
|
71
|
+
balanceItem.status = BalanceItemStatus.Hidden;
|
|
72
|
+
await balanceItem.save();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
async undoFailed(balanceItem: BalanceItem, payment: Payment, organization: Organization) {
|
|
79
|
+
// If order
|
|
80
|
+
if (balanceItem.orderId) {
|
|
81
|
+
const order = await Order.getByID(balanceItem.orderId);
|
|
82
|
+
if (order) {
|
|
83
|
+
await order.undoPaymentFailed(payment, organization);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Group } from '@stamhoofd/models';
|
|
2
|
+
|
|
3
|
+
export const GroupService = {
|
|
4
|
+
async updateOccupancy(groupId: string) {
|
|
5
|
+
const group = await Group.getByID(groupId);
|
|
6
|
+
if (group) {
|
|
7
|
+
// todo: implementation should move to the service
|
|
8
|
+
await group.updateOccupancy();
|
|
9
|
+
await group.save();
|
|
10
|
+
}
|
|
11
|
+
return group;
|
|
12
|
+
},
|
|
13
|
+
};
|