@stamhoofd/models 2.19.0 → 2.20.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 (48) hide show
  1. package/dist/src/models/Member.js.map +1 -1
  2. package/dist/src/models/MemberResponsibilityRecord.d.ts +5 -0
  3. package/dist/src/models/MemberResponsibilityRecord.d.ts.map +1 -1
  4. package/dist/src/models/MemberResponsibilityRecord.js +16 -1
  5. package/dist/src/models/MemberResponsibilityRecord.js.map +1 -1
  6. package/dist/src/models/Organization.d.ts +2 -0
  7. package/dist/src/models/Organization.d.ts.map +1 -1
  8. package/dist/src/models/Organization.js +19 -3
  9. package/dist/src/models/Organization.js.map +1 -1
  10. package/dist/src/models/RegisterCode.d.ts +0 -10
  11. package/dist/src/models/RegisterCode.d.ts.map +1 -1
  12. package/dist/src/models/RegisterCode.js +0 -86
  13. package/dist/src/models/RegisterCode.js.map +1 -1
  14. package/dist/src/models/STCredit.d.ts +0 -7
  15. package/dist/src/models/STCredit.d.ts.map +1 -1
  16. package/dist/src/models/STCredit.js +0 -69
  17. package/dist/src/models/STCredit.js.map +1 -1
  18. package/dist/src/models/STInvoice.d.ts +2 -20
  19. package/dist/src/models/STInvoice.d.ts.map +1 -1
  20. package/dist/src/models/STInvoice.js +0 -364
  21. package/dist/src/models/STInvoice.js.map +1 -1
  22. package/dist/src/models/STPendingInvoice.d.ts +1 -21
  23. package/dist/src/models/STPendingInvoice.d.ts.map +1 -1
  24. package/dist/src/models/STPendingInvoice.js +0 -213
  25. package/dist/src/models/STPendingInvoice.js.map +1 -1
  26. package/dist/src/models/UsedRegisterCode.d.ts +0 -5
  27. package/dist/src/models/UsedRegisterCode.d.ts.map +1 -1
  28. package/dist/src/models/UsedRegisterCode.js +0 -101
  29. package/dist/src/models/UsedRegisterCode.js.map +1 -1
  30. package/package.json +2 -2
  31. package/src/models/Member.ts +5 -5
  32. package/src/models/MemberResponsibilityRecord.ts +21 -2
  33. package/src/models/Organization.ts +23 -3
  34. package/src/models/RegisterCode.ts +0 -95
  35. package/src/models/STCredit.ts +0 -82
  36. package/src/models/STInvoice.ts +2 -433
  37. package/src/models/STPendingInvoice.ts +2 -247
  38. package/src/models/UsedRegisterCode.ts +0 -115
  39. package/dist/src/helpers/InvoiceBuilder.d.ts +0 -29
  40. package/dist/src/helpers/InvoiceBuilder.d.ts.map +0 -1
  41. package/dist/src/helpers/InvoiceBuilder.js +0 -407
  42. package/dist/src/helpers/InvoiceBuilder.js.map +0 -1
  43. package/dist/src/helpers/InvoiceBuilder.test.d.ts +0 -2
  44. package/dist/src/helpers/InvoiceBuilder.test.d.ts.map +0 -1
  45. package/dist/src/helpers/InvoiceBuilder.test.js +0 -52
  46. package/dist/src/helpers/InvoiceBuilder.test.js.map +0 -1
  47. package/src/helpers/InvoiceBuilder.test.ts +0 -57
  48. package/src/helpers/InvoiceBuilder.ts +0 -501
@@ -1,8 +1,6 @@
1
1
  import { column, Model } from "@simonbackx/simple-database";
2
- import { STInvoiceItem } from "@stamhoofd/structures";
3
2
  import { v4 as uuidv4 } from "uuid";
4
3
 
5
- import { STInvoice } from "./";
6
4
 
7
5
  export class STCredit extends Model {
8
6
  static table = "stamhoofd_credits";
@@ -51,84 +49,4 @@ export class STCredit extends Model {
51
49
  skipUpdate: true
52
50
  })
53
51
  updatedAt: Date
54
-
55
- static async getForOrganization(organizationId: string) {
56
- return await STCredit.where({ organizationId }, {
57
- sort: [{
58
- column: "createdAt",
59
- direction: "DESC"
60
- }]
61
- })
62
- }
63
-
64
- static async getBalance(organizationId: string) {
65
- const now = new Date()
66
- const credits = await this.getForOrganization(organizationId)
67
- credits.reverse()
68
- let balance = 0
69
- let balanceTransactions = 0
70
-
71
- for (const credit of credits) {
72
- if (credit.expireAt !== null && credit.expireAt <= now) {
73
- continue
74
- }
75
- // TODO: we can expire credits here
76
- balance += credit.change
77
- if (balance < 0) {
78
- // This is needed to make deleting credit and expiring credit work.
79
- // At no point in time, the credits can get negative.
80
- // E.g. Getting credits, using them, and later expiring 'getting the credits' won't have impact on future credits
81
- balance = 0
82
- }
83
-
84
- if (credit.allowTransactions || credit.change < 0) {
85
- balanceTransactions += credit.change
86
-
87
- // No point in time we can have more balance for transactions
88
- balanceTransactions = Math.min(Math.max(balanceTransactions, 0), balance)
89
- }
90
- }
91
-
92
- return {balance: balance - balanceTransactions, balanceTransactions}
93
- }
94
-
95
- static async applyCredits(organizationId: string, invoice: STInvoice, dryRun: boolean) {
96
- // Apply credits
97
- const {balance, balanceTransactions} = await STCredit.getBalance(organizationId)
98
- if (balance > 0 || balanceTransactions > 0) {
99
- // Loop all items where you can use credits for
100
- const maxCredits = invoice.meta.items.filter(i => i.canUseCredits).reduce((price, item) => price + item.price, 0)
101
- let applyValue = Math.min(maxCredits, balance)
102
-
103
- if (balanceTransactions > 0) {
104
- // Can apply to all items
105
- const maxTransactionsCredits = invoice.meta.items.reduce((price, item) => price + item.price, 0) - applyValue
106
- applyValue += Math.min(maxTransactionsCredits, balanceTransactions)
107
- }
108
-
109
- if (applyValue > 0) {
110
- invoice.meta.items.push(STInvoiceItem.create({
111
- name: "Gebruikt tegoed",
112
- unitPrice: -applyValue,
113
- amount: 1,
114
- date: new Date()
115
- }))
116
-
117
- if (!dryRun) {
118
- const credit = new STCredit()
119
- credit.organizationId = organizationId
120
- credit.change = -applyValue
121
- credit.description = "Tijdelijk vrijgehouden voor lopende betaling"
122
-
123
- // Reserve for one week (direct debit can take a while)
124
- credit.expireAt = new Date()
125
- credit.expireAt.setDate(credit.expireAt.getDate() + 7)
126
- credit.expireAt.setMilliseconds(0)
127
-
128
- await credit.save()
129
- invoice.creditId = credit.id
130
- }
131
- }
132
- }
133
- }
134
52
  }
@@ -1,12 +1,8 @@
1
1
  import { column, ManyToOneRelation, Model } from "@simonbackx/simple-database";
2
- import { Email } from "@stamhoofd/email";
3
- import { QueueHandler } from "@stamhoofd/queues";
4
- import { calculateVATPercentage, Payment as PaymentStruct, PaymentMethod, STBillingStatus, STCredit as STCreditStruct, STInvoice as STInvoiceStruct, STInvoiceItem, STInvoiceMeta, STPackage as STPackageStruct, STPackageType, STPendingInvoice as STPendingInvoiceStruct } from '@stamhoofd/structures';
5
- import { Sorter } from "@stamhoofd/utility";
2
+ import { STInvoiceMeta } from '@stamhoofd/structures';
6
3
  import { v4 as uuidv4 } from "uuid";
7
4
 
8
- import { InvoiceBuilder } from "../helpers/InvoiceBuilder";
9
- import { Organization, Payment, STCredit, STPackage, STPendingInvoice, UsedRegisterCode } from './';
5
+ import { Organization, Payment } from './';
10
6
 
11
7
  export class STInvoice extends Model {
12
8
  static table = "stamhoofd_invoices";
@@ -79,431 +75,4 @@ export class STInvoice extends Model {
79
75
 
80
76
  static organization = new ManyToOneRelation(Organization, "organization");
81
77
  static payment = new ManyToOneRelation(Payment, "payment");
82
-
83
- static createFor(organization: Organization): STInvoice {
84
- const invoice = new STInvoice()
85
- invoice.organizationId = organization.id
86
-
87
- const date = new Date()
88
- invoice.meta = STInvoiceMeta.create({
89
- date,
90
- companyName: organization.meta.companyName ?? organization.name,
91
- companyContact: organization.privateMeta.billingContact ?? "",
92
- companyAddress: organization.meta.companyAddress ?? organization.address,
93
- companyVATNumber: organization.meta.VATNumber,
94
- companyNumber: organization.meta.companyNumber,
95
- VATPercentage: calculateVATPercentage(organization.meta.companyAddress ?? organization.address, organization.meta.VATNumber)
96
- })
97
-
98
- return invoice
99
- }
100
-
101
- async getStructure() {
102
- let payment: Payment | undefined
103
- if (this.paymentId) {
104
- payment = await Payment.getByID(this.paymentId)
105
- }
106
- return STInvoiceStruct.create(Object.assign({}, this, {
107
- payment: payment ? PaymentStruct.create(payment) : null
108
- }))
109
- }
110
-
111
- async getPackages(): Promise<STPackage[]> {
112
- const ids = new Set<string>()
113
-
114
- for (const item of this.meta.items) {
115
- if (item.package) {
116
- ids.add(item.package.id)
117
- }
118
- }
119
-
120
- if (ids.size > 0) {
121
- const packages = await STPackage.getByIDs(...ids)
122
-
123
- if (packages.length !== ids.size) {
124
- console.warn("Invoice contains invalid package ids "+this.id)
125
- }
126
- return packages
127
- }
128
-
129
- return []
130
- }
131
-
132
- async getOrganizationActivePackages(): Promise<STPackage[]> {
133
- return this.organizationId ? (await STPackage.getForOrganization(this.organizationId)) : []
134
- }
135
-
136
- async activatePackages(paymentSucceeded = false) {
137
- const packages = await this.getPackages()
138
-
139
- // Search for all packages and activate them if needed (might be possible that they are already validated)
140
- for (const p of packages) {
141
- // It is possible that the meta of the package has changed in the previous loop call (in pack.activate), so we need to refetch it otherwise we get 'meta' conflicts
142
- const pack = (await STPackage.getByID(p.id)) ?? p
143
- console.log("Activating package "+pack.id)
144
-
145
- // We'll never have multiple invoices for the same package that are awaiting payments
146
- if (paymentSucceeded) {
147
- pack.meta.firstFailedPayment = null;
148
- pack.meta.paymentFailedCount = 0;
149
- }
150
-
151
- await pack.activate()
152
-
153
- // Activate doesn't save always, so save if needed:
154
- await pack.save()
155
-
156
- // Deactivate demo packages
157
- const remove: STPackageType[] = []
158
- if (pack.meta.type === STPackageType.Members) {
159
- // Remove demo
160
- remove.push(STPackageType.TrialMembers)
161
- }
162
-
163
- if (pack.meta.type === STPackageType.SingleWebshop || pack.meta.type === STPackageType.Webshops) {
164
- // Remove demo
165
- remove.push(STPackageType.TrialWebshops)
166
- }
167
-
168
- if (remove.length > 0 && this.organizationId) {
169
- // Get all packages
170
- const all = await STPackage.getForOrganization(this.organizationId)
171
- for (const pack of all) {
172
- if (remove.includes(pack.meta.type)) {
173
- console.log("Disabling demo package "+pack.id+" because package is bought.")
174
- // Stop
175
- pack.removeAt = new Date()
176
- pack.removeAt.setTime(pack.removeAt.getTime() - 1000)
177
- await pack.save()
178
- }
179
- }
180
- }
181
- }
182
- }
183
-
184
- /**
185
- * WARNING: only call this in the correct queue!
186
- */
187
- async markPaid({sendEmail} = { sendEmail: true }) {
188
- // Schule on the queue because we are modifying pending invoices
189
- if (this.paidAt !== null) {
190
- return
191
- }
192
-
193
- this.paidAt = new Date()
194
-
195
- if (this.meta.priceWithoutVAT > 0) {
196
- // Only assign a number if it was an non empty invoice
197
- await this.assignNextNumber()
198
- }
199
-
200
- if (this.creditId !== null) {
201
- const credit = await STCredit.getByID(this.creditId)
202
- if (credit) {
203
- // This credit was used to pay this invoice (partially)
204
- credit.description = this.number !== null ? "Factuur "+this.number : "Betaling "+this.id
205
- credit.expireAt = null;
206
- await credit.save()
207
- }
208
- }
209
-
210
- const packages = await this.getPackages()
211
-
212
- // Increase paid amounts and paid prices
213
- for (const item of this.meta.items) {
214
- if (item.package) {
215
- const pack = packages.find(p => p.id === item.package?.id)
216
-
217
- if (pack) {
218
- // Increase paid amount
219
- pack.meta.paidPrice += item.price
220
- pack.meta.paidAmount += item.amount
221
- await pack.save();
222
- }
223
- }
224
- }
225
-
226
- // Search for all packages and activate them if needed (might be possible that they are already validated)
227
- await this.activatePackages(true)
228
-
229
- if (packages.length === 0) {
230
- // Mark payments succeeded
231
- const orgPackages = await this.getOrganizationActivePackages()
232
- for (const p of orgPackages) {
233
- if (p.meta.firstFailedPayment) {
234
- const pack = (await STPackage.getByID(p.id)) ?? p
235
- pack.meta.firstFailedPayment = null;
236
- pack.meta.paymentFailedCount = 0;
237
- await pack.save()
238
- }
239
- }
240
- }
241
-
242
- // If needed: remove the invoice items from the pending invoice
243
- if (this.organizationId) {
244
- const pendingInvoice = await STPendingInvoice.getForOrganization(this.organizationId)
245
- if (pendingInvoice) {
246
- // Remove all invoice items that were paid
247
- const newItems: STInvoiceItem[] = []
248
- for (const item of pendingInvoice.meta.items) {
249
- const found = this.meta.items.find(i => i.id === item.id)
250
- if (!found) {
251
- newItems.push(item)
252
- } else {
253
- console.log("Removed invoice item "+item.id+" from pending invoice "+pendingInvoice.id)
254
- }
255
- }
256
- pendingInvoice.meta.items = newItems
257
-
258
- if (pendingInvoice.invoiceId === this.id) {
259
- // Unlock the pending invoice: we allow new invoices to get created
260
- pendingInvoice.invoiceId = null
261
- }
262
- await pendingInvoice.save()
263
- }
264
-
265
- // Force regeneration of organization meta data
266
- await STPackage.updateOrganizationPackages(this.organizationId)
267
- }
268
-
269
- if (this.number !== null && !this.meta.pdf) {
270
- await this.generatePdf()
271
- }
272
-
273
- if (this.organizationId && this.meta.pdf && this.number !== null && sendEmail) {
274
- const organization = await Organization.getByID(this.organizationId)
275
- if (organization) {
276
- const invoicingTo = await organization.getInvoicingToEmails()
277
-
278
- if (invoicingTo) {
279
- // Send the e-mail
280
- Email.sendInternal({
281
- to: invoicingTo,
282
- bcc: "simon@stamhoofd.be",
283
- subject: "Stamhoofd Factuur " + this.number + " voor " + organization.name,
284
- text: "Dag "+organization.name+", \n\nBedankt voor jullie vertrouwen in Stamhoofd. In bijlage vinden jullie factuur "+ this.number +" voor jullie administratie. Deze werd al betaald, je hoeft dus geen actie meer te ondernemen. Neem gerust contact met ons op (via "+organization.i18n.$t("shared.emails.general")+") als je denkt dat er iets fout is gegaan of als je nog bijkomende vragen zou hebben.\n\nMet vriendelijke groeten,\nStamhoofd\n\n",
285
- attachments: [
286
- {
287
- filename: "factuur-"+this.number+".pdf",
288
- href: this.meta.pdf.getPublicPath(),
289
- contentType: "application/pdf"
290
- }
291
- ]
292
- }, organization.i18n)
293
- }
294
- }
295
- }
296
-
297
- // Reward if we have an open register code
298
- if (this.meta.priceWithVAT >= 100 && this.organizationId) {
299
- // We spend some money
300
- const code = await UsedRegisterCode.getFor(this.organizationId)
301
- if (code && !code.creditId) {
302
- console.log("Rewarding code "+code.id+" for payment")
303
-
304
- // Deze code werd nog niet beloond
305
- await code.reward()
306
- }
307
- }
308
-
309
- // todo: link member_membership items to this newly created invoice
310
- }
311
-
312
- async assignNextNumber() {
313
- return await QueueHandler.schedule("billing/invoice-numbers", async () => {
314
- // Get clone
315
- const refreshed = await STInvoice.getByID(this.id)
316
- if (!refreshed || refreshed.number !== null) {
317
- return
318
- }
319
- // Removed the cache because not working across multiple instances
320
- // if (STInvoice.numberCache) {
321
- // this.number = STInvoice.numberCache + 1
322
- // await this.save()
323
- //
324
- // // If save succeeded: increase it
325
- // STInvoice.numberCache++;
326
- // return
327
- // }
328
- const lastInvoice = await STInvoice.where({ number: { value: null, sign: "!=" }}, { sort: [{ column: "number", direction: "DESC"}], limit: 1 })
329
- STInvoice.numberCache = lastInvoice[0]?.number ?? 0
330
-
331
- this.number = STInvoice.numberCache + 1
332
- this.meta.date = new Date()
333
- await this.save()
334
-
335
- // If save succeeded: increase it
336
- STInvoice.numberCache++;
337
- return
338
- })
339
- }
340
-
341
- async generatePdf() {
342
- const builder = new InvoiceBuilder(this)
343
- this.meta.pdf = await builder.build()
344
- await this.save()
345
- }
346
-
347
- /**
348
- * WARNGING: only call this method in the correct queue!
349
- */
350
- async markFailed(payment: Payment, markFailed = true) {
351
- console.log("Mark invoice as failed", this.id)
352
-
353
- const packages = await this.getPackages()
354
-
355
- if (markFailed && (payment.method === PaymentMethod.DirectDebit || payment.method === PaymentMethod.Transfer)) {
356
- // Only mark failed payments for background payments
357
- for (const pack of packages) {
358
- console.log("Marking package with failed payment "+pack.id)
359
-
360
- pack.meta.firstFailedPayment = pack.meta.firstFailedPayment ?? new Date()
361
- pack.meta.paymentFailedCount++;
362
- await pack.save()
363
-
364
- }
365
-
366
- if (packages.length == 0) {
367
- // Mark all active packages as failed
368
- const activePackages = await this.getOrganizationActivePackages()
369
- for (const pack of activePackages) {
370
- console.log("Marking package with failed payment "+pack.id)
371
-
372
- pack.meta.firstFailedPayment = pack.meta.firstFailedPayment ?? new Date()
373
- pack.meta.paymentFailedCount++;
374
- await pack.save()
375
- }
376
- }
377
- }
378
-
379
-
380
- if (this.organizationId) {
381
- const pendingInvoice = await STPendingInvoice.getForOrganization(this.organizationId)
382
- if (pendingInvoice && pendingInvoice.invoiceId === this.id) {
383
- pendingInvoice.invoiceId = null
384
-
385
- // Also update the packages in the pending invoice itself
386
- for (const item of pendingInvoice.meta.items) {
387
- const pack = item.package
388
- if (pack) {
389
- const pp = packages.find(p => p.id === pack.id)
390
- if (pp) {
391
- console.log("Updated package "+pp.id+" in pending invoice")
392
- // Update reference to include new failed counts
393
- item.package = STPackageStruct.create(pp)
394
- }
395
- }
396
- }
397
-
398
- await pendingInvoice.save()
399
- }
400
-
401
- // Force regeneration of organization meta data
402
- await STPackage.updateOrganizationPackages(this.organizationId)
403
- }
404
-
405
- if (this.creditId !== null) {
406
- const credit = await STCredit.getByID(this.creditId)
407
- if (credit && (credit.expireAt === null || credit.expireAt > new Date())) {
408
- // Expire usage (do not delete to keep the relation for debugging and recovery)
409
- credit.expireAt = new Date(new Date().getTime() - 1000);
410
- await credit.save()
411
- }
412
- }
413
-
414
- if (markFailed && this.organizationId && payment.method === PaymentMethod.DirectDebit) {
415
- const organization = await Organization.getByID(this.organizationId)
416
- if (organization) {
417
- const invoicingTo = await organization.getInvoicingToEmails()
418
-
419
- if (invoicingTo) {
420
- // Send the e-mail
421
- Email.sendInternal({
422
- to: invoicingTo,
423
- bcc: "simon@stamhoofd.be",
424
- subject: "Betaling mislukt voor "+organization.name,
425
- text: "Dag "+organization.name+", \n\nDe automatische betaling via domiciliëring van jullie openstaande bedrag is mislukt (zie daarvoor onze vorige e-mail). Kijk even na wat er fout ging en betaal het openstaande bedrag manueel om te vermijden dat bepaalde diensten tijdelijk worden uitgeschakeld. Betalen kan via Stamhoofd > Instellingen > Facturen en betalingen > Openstaand bedrag > Afrekenen. Neem gerust contact met ons op als je bijkomende vragen hebt.\n\nMet vriendelijke groeten,\nStamhoofd\n\n",
426
- }, organization.i18n)
427
- }
428
- }
429
- }
430
-
431
- if (markFailed && this.organizationId && payment.method === PaymentMethod.Transfer) {
432
- const organization = await Organization.getByID(this.organizationId)
433
- if (organization) {
434
- const invoicingTo = await organization.getInvoicingToEmails()
435
-
436
- if (invoicingTo) {
437
- // Send the e-mail
438
- Email.sendInternal({
439
- to: invoicingTo,
440
- bcc: "simon@stamhoofd.be",
441
- subject: "Betaling mislukt voor "+organization.name,
442
- text: "Dag "+organization.name+", \n\nBij nazicht blijkt dat we geen overschrijving hebben ontvangen voor jullie aankoop. Kijk even na wat er fout ging en betaal het openstaande bedrag om te vermijden dat de diensten worden uitgeschakeld. Betalen kan via Stamhoofd > Instellingen > Facturen en betalingen > Openstaand bedrag > Afrekenen. Neem gerust contact met ons op als je bijkomende vragen hebt.\n\nMet vriendelijke groeten,\nStamhoofd\n\n",
443
- }, organization.i18n)
444
- }
445
- }
446
- }
447
- }
448
-
449
-
450
- static async getBillingStatus(organization: Organization, hideExpired = true): Promise<STBillingStatus> {
451
- // Get all packages
452
- const packages = hideExpired ? (await STPackage.getForOrganization(organization.id)) : (await STPackage.getForOrganizationIncludingExpired(organization.id))
453
-
454
- // GEt all invoices
455
- const invoices = await STInvoice.where({ organizationId: organization.id, number: { sign: "!=", value: null }})
456
-
457
- // Get the pending invoice if it exists
458
- const pendingInvoice = await STPendingInvoice.getForOrganization(organization.id)
459
-
460
- // Generate temporary pending invoice items for the current state without adding them IRL
461
- let notYetCreatedItems: STInvoiceItem[] = []
462
- try {
463
- notYetCreatedItems = await STPendingInvoice.createItems(organization.id, pendingInvoice)
464
- } catch (e) {
465
- console.error(e)
466
- }
467
- const pendingInvoiceStruct = pendingInvoice ? STPendingInvoiceStruct.create(pendingInvoice) : (notYetCreatedItems.length > 0 ? STPendingInvoiceStruct.create({
468
- meta: STInvoiceMeta.create({
469
- companyName: organization.meta.companyName ?? organization.name,
470
- companyContact: organization.privateMeta.billingContact ?? "",
471
- companyAddress: organization.meta.companyAddress ?? organization.address,
472
- companyVATNumber: organization.meta.VATNumber,
473
- VATPercentage: calculateVATPercentage(organization.meta.companyAddress ?? organization.address, organization.meta.VATNumber)
474
- })
475
- }) : null)
476
-
477
- if (notYetCreatedItems.length > 0 && pendingInvoiceStruct) {
478
- pendingInvoiceStruct.meta.items.push(...notYetCreatedItems)
479
- }
480
-
481
- if (pendingInvoiceStruct) {
482
- // Compress
483
- pendingInvoiceStruct.meta.items = STInvoiceItem.compress(pendingInvoiceStruct.meta.items)
484
- }
485
-
486
-
487
- if (pendingInvoice?.invoiceId && pendingInvoiceStruct) {
488
- const invoice = await STInvoice.getByID(pendingInvoice?.invoiceId)
489
- if (invoice) {
490
- pendingInvoiceStruct.invoice = await invoice.getStructure()
491
- }
492
- }
493
- invoices.sort((a, b) => Sorter.byDateValue(a.createdAt, b.createdAt))
494
-
495
- const invoiceStructures: STInvoiceStruct[] = []
496
- for (const invoice of invoices) {
497
- invoiceStructures.push(await invoice.getStructure())
498
- }
499
-
500
- const credits = await STCredit.getForOrganization(organization.id)
501
-
502
- return STBillingStatus.create({
503
- packages: packages.map(pack => STPackageStruct.create(pack)),
504
- invoices: invoiceStructures,
505
- pendingInvoice: pendingInvoiceStruct,
506
- credits: credits.map(c => STCreditStruct.create(c))
507
- });
508
- }
509
78
  }