• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GEWIS / sudosos-backend / 23902942709

02 Apr 2026 01:31PM UTC coverage: 89.064% (-0.004%) from 89.068%
23902942709

push

github

JustSamuel
ci: fix coverage baseline

1821 of 2232 branches covered (81.59%)

Branch coverage included in aggregate %.

9288 of 10241 relevant lines covered (90.69%)

1011.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.93
/src/controller/invoice-controller.ts
1
/**
2
 *  SudoSOS back-end API service.
3
 *  Copyright (C) 2026 Study association GEWIS
4
 *
5
 *  This program is free software: you can redistribute it and/or modify
6
 *  it under the terms of the GNU Affero General Public License as published
7
 *  by the Free Software Foundation, either version 3 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU Affero General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU Affero General Public License
16
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
 *
18
 *  @license
19
 */
20

21
/**
22
 * This is the page of invoice controller.
23
 *
24
 * @module invoicing
25
 */
26

27
import log4js, { Logger } from 'log4js';
2✔
28
import { Response } from 'express';
29
import BaseController, { BaseControllerOptions } from './base-controller';
2✔
30
import Policy from './policy';
31
import { RequestWithToken } from '../middleware/token-middleware';
32
import InvoiceService, { InvoiceFilterParameters, parseInvoiceFilterParameters } from '../service/invoice-service';
2✔
33
import { parseRequestPagination, toResponse } from '../helpers/pagination';
2✔
34
import {
35
  CreateInvoiceParams,
36
  CreateInvoiceRequest,
37
  UpdateInvoiceParams,
38
  UpdateInvoiceRequest,
39
} from './request/invoice-request';
40
import { createInvoiceRequestSpec, updateInvoiceRequestSpec } from './request/validators/invoice-request-spec';
2✔
41
import { globalAsyncValidatorRegistry } from '../middleware/async-validator-registry';
2✔
42
import { asBoolean, asDate, asInvoiceState, asNumber } from '../helpers/validators';
2✔
43
import Invoice from '../entity/invoices/invoice';
2✔
44
import User, { UserType } from '../entity/user/user';
2✔
45
import { UpdateInvoiceUserRequest } from './request/user-request';
46
import InvoiceUser from '../entity/user/invoice-user';
2✔
47
import { parseInvoiceUserToResponse } from '../helpers/revision-to-response';
2✔
48
import { AppDataSource } from '../database/database';
2✔
49
import { NotImplementedError, PdfError } from '../errors';
2✔
50

51
/**
52
 * The Invoice controller.
53
 */
54
export default class InvoiceController extends BaseController {
2✔
55
  private logger: Logger = log4js.getLogger('InvoiceController');
2✔
56

57
  /**
58
    * Creates a new Invoice controller instance.
59
    * @param options - The options passed to the base controller.
60
    */
61
  public constructor(options: BaseControllerOptions) {
62
    super(options);
2✔
63
    this.logger.level = process.env.LOG_LEVEL;
2✔
64
    globalAsyncValidatorRegistry.register('CreateInvoiceRequest', createInvoiceRequestSpec);
2✔
65
    globalAsyncValidatorRegistry.register('UpdateInvoiceRequest', updateInvoiceRequestSpec, (req) => ({
4✔
66
      ...req.body,
67
      invoiceId: parseInt(req.params.id, 10),
68
      state: asInvoiceState((req.body as UpdateInvoiceRequest).state),
69
      byId: (req.body as UpdateInvoiceRequest).byId ?? req.token.user.id,
8✔
70
    }));
71
  }
72

73
  /**
74
    * @inhertidoc
75
    */
76
  getPolicy(): Policy {
77
    return {
2✔
78
      '/': {
79
        GET: {
80
          policy: async (req) => this.roleManager.can(req.token.roles, 'get', 'all', 'Invoice', ['*']),
5✔
81
          handler: this.getAllInvoices.bind(this),
82
        },
83
        POST: {
84
          body: { modelName: 'CreateInvoiceRequest' },
85
          policy: async (req) => this.roleManager.can(req.token.roles, 'create', 'all', 'Invoice', ['*']),
7✔
86
          handler: this.createInvoice.bind(this),
87
        },
88
      },
89
      '/users/:id(\\d+)': {
90
        GET: {
91
          policy: async (req) => this.roleManager.can(req.token.roles, 'get', 'all', 'Invoice', ['*']),
5✔
92
          handler: this.getSingleInvoiceUser.bind(this),
93
        },
94
        PUT : {
95
          body: { modelName: 'UpdateInvoiceUserRequest' },
96
          policy: async (req) => this.roleManager.can(req.token.roles, 'update', 'all', 'Invoice', ['*']),
6✔
97
          handler: this.updateInvoiceUser.bind(this),
98
        },
99
        DELETE: {
100
          policy: async (req) => this.roleManager.can(req.token.roles, 'delete', 'all', 'Invoice', ['*']),
3✔
101
          handler: this.deleteInvoiceUser.bind(this),
102
        },
103
      },
104
      '/:id(\\d+)': {
105
        GET: {
106
          policy: async (req) => this.roleManager.can(req.token.roles, 'get', await InvoiceController.getRelation(req), 'Invoice', ['*']),
6✔
107
          handler: this.getSingleInvoice.bind(this),
108
        },
109
        PATCH: {
110
          body: { modelName: 'UpdateInvoiceRequest' },
111
          policy: async (req) => this.roleManager.can(req.token.roles, 'update', 'all', 'Invoice', ['*']),
5✔
112
          handler: this.updateInvoice.bind(this),
113
        },
114
        DELETE: {
115
          policy: async (req) => this.roleManager.can(req.token.roles, 'delete', 'all', 'Invoice', ['*']),
2✔
116
          handler: this.deleteInvoice.bind(this),
117
        },
118
      },
119
      '/:id(\\d+)/pdf': {
120
        GET: {
121
          policy: async (req) => this.roleManager.can(req.token.roles, 'get', await InvoiceController.getRelation(req), 'Invoice', ['*']),
3✔
122
          handler: this.getInvoicePDF.bind(this),
123
        },
124
      },
125
      '/eligible-transactions': {
126
        GET: {
127
          policy: async (req) => this.roleManager.can(req.token.roles, 'get', 'all', 'Invoice', ['*']),
×
128
          handler: this.getEligibleTransactions.bind(this),
129
        },
130
      },
131
      '/drift': {
132
        GET: {
133
          policy: async (req) => this.roleManager.can(req.token.roles, 'get', 'all', 'Invoice', ['*']),
×
134
          handler: this.getDriftedInvoices.bind(this),
135
        },
136
      },
137
    };
138
  }
139

140
  /**
141
   * GET /invoices
142
   * @summary Returns all invoices in the system.
143
   * @operationId getAllInvoices
144
   * @tags invoices - Operations of the invoices controller
145
   * @security JWT
146
   * @param {integer} toId.query - Filter on Id of the debtor
147
   * @param {number} invoiceId.query - Filter on invoice ID
148
   * @param {Array<string|number>} currentState.query enum:CREATED,SENT,PAID,DELETED - Filter based on Invoice State.
149
   * @param {boolean} returnEntries.query - Boolean if invoice entries should be returned
150
   * @param {string} fromDate.query - Start date for selected invoices (inclusive)
151
   * @param {string} tillDate.query - End date for selected invoices (exclusive)
152
   * @param {string} description.query - Filter invoices by description (partial match)
153
   * @param {integer} take.query - How many entries the endpoint should return
154
   * @param {integer} skip.query - How many entries should be skipped (for pagination)
155
   * @return {PaginatedInvoiceResponse} 200 - All existing invoices
156
   * @return {string} 500 - Internal server error
157
   */
158
  public async getAllInvoices(req: RequestWithToken, res: Response): Promise<void> {
159
    const { body } = req;
4✔
160
    this.logger.trace('Get all invoices', body, 'by user', req.token.user);
4✔
161

162
    let take;
163
    let skip;
164
    let filters: InvoiceFilterParameters;
165
    try {
4✔
166
      const pagination = parseRequestPagination(req);
4✔
167
      filters = parseInvoiceFilterParameters(req);
4✔
168
      take = pagination.take;
4✔
169
      skip = pagination.skip;
4✔
170
    } catch (e) {
171
      res.status(400).send(e.message);
×
172
      return;
×
173
    }
174

175
    // Handle request
176
    try {
4✔
177
      const [invoices, count] = await new InvoiceService().getPaginatedInvoices(
4✔
178
        filters, { take, skip },
179
      );
180

181
      const records = filters.returnInvoiceEntries
4!
182
        ? InvoiceService.toArrayResponse(invoices)
183
        : InvoiceService.toArrayWithoutEntriesResponse(invoices);
184

185
      res.json(toResponse(records, count, { take, skip }));
4✔
186
    } catch (error) {
187
      this.logger.error('Could not return all invoices:', error);
×
188
      res.status(500).json('Internal server error.');
×
189
    }
190
  }
191

192
  /**
193
   * GET /invoices/{id}
194
   * @summary Returns a single invoice in the system.
195
   * @operationId getSingleInvoice
196
   * @param {integer} id.path.required - The id of the requested invoice
197
   * @tags invoices - Operations of the invoices controller
198
   * @security JWT
199
   * @param {boolean} returnEntries.query -
200
   * Boolean if invoice entries should be returned, defaults to true.
201
   * @return {InvoiceResponse} 200 - All existing invoices
202
   * @return {string} 404 - Invoice not found
203
   * @return {string} 500 - Internal server error
204
   */
205
  public async getSingleInvoice(req: RequestWithToken, res: Response): Promise<void> {
206
    const { id } = req.params;
6✔
207
    const invoiceId = parseInt(id, 10);
6✔
208
    this.logger.trace('Get invoice', invoiceId, 'by user', req.token.user);
6✔
209

210
    // Handle request
211
    try {
6✔
212
      const returnInvoiceEntries = asBoolean(req.query.returnEntries) ?? true;
6✔
213

214
      const invoices: Invoice[] = await new InvoiceService().getInvoices(
6✔
215
        { invoiceId, returnInvoiceEntries },
216
      );
217

218
      const invoice = invoices[0];
6✔
219
      if (!invoice) {
6✔
220
        res.status(404).json('Unknown invoice ID.');
1✔
221
        return;
1✔
222
      }
223
      const response = returnInvoiceEntries
5!
224
        ? InvoiceService.asInvoiceResponse(invoice)
225
        : InvoiceService.asBaseInvoiceResponse(invoice);
226

227
      res.json(response);
5✔
228
    } catch (error) {
229
      this.logger.error('Could not return invoice:', error);
×
230
      res.status(500).json('Internal server error.');
×
231
    }
232
  }
233

234
  /**
235
   * POST /invoices
236
   * @summary Adds an invoice to the system.
237
   * @operationId createInvoice
238
   * @tags invoices - Operations of the invoices controller
239
   * @security JWT
240
   * @param {CreateInvoiceRequest} request.body.required -
241
   * The invoice which should be created
242
   * @return {InvoiceResponse} 200 - The created invoice entity
243
   * @return {ValidationResponse} 400 - Validation error
244
   * @return {string} 500 - Internal server error
245
   */
246
  public async createInvoice(req: RequestWithToken, res: Response): Promise<void> {
247
    const body = req.body as CreateInvoiceRequest;
1✔
248
    this.logger.trace('Create Invoice', body, 'by user', req.token.user);
1✔
249

250
    // handle request
251
    try {
1✔
252
      const userDefinedDefaults = await new InvoiceService().getDefaultInvoiceParams(body.forId);
1✔
253

254
      // If no byId is provided we use the token user id.
255
      const params: CreateInvoiceParams = {
1✔
256
        ...userDefinedDefaults,
257
        ...body,
258
        date: body.date ? new Date(body.date) : new Date(),
1!
259
        byId: body.byId ?? req.token.user.id,
1!
260
        description: body.description ?? '',
1!
261
      };
262

263
      const invoice: Invoice = await AppDataSource.manager.transaction(async (manager) =>
1✔
264
        new InvoiceService(manager).createInvoice(params));
1✔
265
      res.json(InvoiceService.asInvoiceResponse(invoice));
1✔
266
    } catch (error) {
267
      if (error instanceof NotImplementedError) {
×
268
        res.status(501).json(error.message);
×
269
        return;
×
270
      }
271
      this.logger.error('Could not create invoice:', error);
×
272
      res.status(500).json('Internal server error.');
×
273
    }
274
  }
275

276
  /**
277
   * PATCH /invoices/{id}
278
   * @summary Adds an invoice to the system.
279
   * @operationId updateInvoice
280
   * @tags invoices - Operations of the invoices controller
281
   * @security JWT
282
   * @param {integer} id.path.required - The id of the invoice which should be updated
283
   * @param {UpdateInvoiceRequest} request.body.required -
284
   * The invoice update to process
285
   * @return {BaseInvoiceResponse} 200 - The updated invoice entity
286
   * @return {ValidationResponse} 400 - Validation error
287
   * @return {string} 500 - Internal server error
288
   */
289
  public async updateInvoice(req: RequestWithToken, res: Response): Promise<void> {
290
    const body = req.body as UpdateInvoiceRequest;
1✔
291
    const { id } = req.params;
1✔
292
    const invoiceId = parseInt(id, 10);
1✔
293
    this.logger.trace('Update Invoice', body, 'by user', req.token.user);
1✔
294

295
    try {
1✔
296
      // Default byId to token user id.
297
      const params: UpdateInvoiceParams = {
1✔
298
        ...body,
299
        invoiceId,
300
        state: asInvoiceState(body.state),
301
        byId: body.byId ?? req.token.user.id,
2✔
302
      };
303

304
      const invoice: Invoice = await AppDataSource.manager.transaction(async (manager) =>
1✔
305
        new InvoiceService(manager).updateInvoice(params));
1✔
306

307
      res.json(InvoiceService.asBaseInvoiceResponse(invoice));
1✔
308
    } catch (error) {
309
      this.logger.error('Could not update invoice:', error);
×
310
      res.status(500).json('Internal server error.');
×
311
    }
312
  }
313

314
  /**
315
   * DELETE /invoices/{id}
316
   * @summary Deletes an invoice.
317
   * @operationId deleteInvoice
318
   * @tags invoices - Operations of the invoices controller
319
   * @security JWT
320
   * @param {integer} id.path.required - The id of the invoice which should be deleted
321
   * @return {string} 404 - Invoice not found
322
   * @return 204 - Deletion success
323
   * @return {string} 500 - Internal server error
324
   */
325
  // TODO Deleting of invoices that are not of state CREATED?
326
  public async deleteInvoice(req: RequestWithToken, res: Response): Promise<void> {
327
    const { id } = req.params;
2✔
328
    const invoiceId = parseInt(id, 10);
2✔
329
    this.logger.trace('Delete Invoice', id, 'by user', req.token.user);
2✔
330

331
    try {
2✔
332
      const invoice = await AppDataSource.manager.transaction(async (manager) =>
2✔
333
        new InvoiceService(manager).deleteInvoice(invoiceId, req.token.user.id));
2✔
334
      if (!invoice) {
2✔
335
        res.status(404).json('Invoice not found.');
1✔
336
        return;
1✔
337
      }
338
      res.status(204).send();
1✔
339
    } catch (error) {
340
      this.logger.error('Could not delete invoice:', error);
×
341
      res.status(500).json('Internal server error.');
×
342
    }
343
  }
344

345
  /**
346
   * GET /invoices/{id}/pdf
347
   * @summary Get an invoice pdf.
348
   * @operationId getInvoicePdf
349
   * @tags invoices - Operations of the invoices controller
350
   * @security JWT
351
   * @param {integer} id.path.required - The id of the invoice to return
352
   * @param {boolean} force.query - Force creation of pdf
353
   * @return {string} 404 - Invoice not found
354
   * @return {string} 200 - The pdf location information.
355
   * @return {string} 500 - Internal server error
356
   */
357
  public async getInvoicePDF(req: RequestWithToken, res: Response): Promise<void> {
358
    const { id } = req.params;
2✔
359
    const invoiceId = parseInt(id, 10);
2✔
360
    this.logger.trace('Get Invoice PDF', id, 'by user', req.token.user);
2✔
361

362
    try {
2✔
363
      const invoice = await Invoice.findOne(InvoiceService.getOptions({ invoiceId, returnInvoiceEntries: true }) );
2✔
364
      if (!invoice) {
2✔
365
        res.status(404).json('Invoice not found.');
1✔
366
        return;
1✔
367
      }
368

369
      const pdf = await invoice.getOrCreatePdf(req.query.force === 'true');
1✔
370

371
      res.status(200).json({ pdf: pdf.downloadName });
1✔
372
    } catch (error) {
373
      this.logger.error('Could get invoice PDF:', error);
×
374
      if (error instanceof PdfError) {
×
375
        res.status(502).json('PDF Generator service failed.');
×
376
        return;
×
377
      }
378
      res.status(500).json('Internal server error.');
×
379
    }
380
  }
381

382
  /**
383
   * DELETE /invoices/users/{id}
384
   * @summary Delete invoice user defaults.
385
   * @operationId deleteInvoiceUser
386
   * @tags invoices - Operations of the invoices controller
387
   * @security JWT
388
   * @param {integer} id.path.required - The id of the invoice user to delete.
389
   * @return {string} 404 - Invoice User not found
390
   * @return 204 - Success
391
   * @return {string} 500 - Internal server error
392
   */
393
  public async deleteInvoiceUser(req: RequestWithToken, res: Response): Promise<void> {
394
    const { id } = req.params;
2✔
395
    const userId = parseInt(id, 10);
2✔
396
    this.logger.trace('Delete Invoice User', id, 'by user', req.token.user);
2✔
397

398
    try {
2✔
399
      const invoiceUser = await InvoiceUser.findOne({ where: { userId } });
2✔
400
      if (!invoiceUser) {
2✔
401
        res.status(404).json('Invoice User not found.');
1✔
402
        return;
1✔
403
      }
404

405
      await InvoiceUser.delete(userId);
1✔
406
      res.status(204).json();
1✔
407
    } catch (error) {
408
      this.logger.error('Could not get invoice user:', error);
×
409
      res.status(500).json('Internal server error.');
×
410
    }
411
  }
412

413
  /**
414
   * GET /invoices/users/{id}
415
   * @summary Get invoice user defaults.
416
   * @operationId getSingleInvoiceUser
417
   * @tags invoices - Operations of the invoices controller
418
   * @security JWT
419
   * @param {integer} id.path.required - The id of the invoice user to return.
420
   * @return {string} 404 - Invoice User not found
421
   * @return {string} 404 - User not found
422
   * @return {string} 400 - User is not of type INVOICE
423
   * @return {InvoiceUserResponse} 200 - The requested Invoice User
424
   * @return {string} 500 - Internal server error
425
   */
426
  public async getSingleInvoiceUser(req: RequestWithToken, res: Response): Promise<void> {
427
    const { id } = req.params;
4✔
428
    const userId = parseInt(id, 10);
4✔
429
    this.logger.trace('Get Invoice User', id, 'by user', req.token.user);
4✔
430

431
    try {
4✔
432
      const user = await User.findOne({ where: { id: userId, deleted: false } });
4✔
433
      if (!user) {
4✔
434
        res.status(404).json('User not found.');
1✔
435
        return;
1✔
436
      }
437

438
      if (user.type !== UserType.INVOICE) {
3✔
439
        res.status(400).json(`User is of type ${UserType[user.type]} and not of type INVOICE.`);
1✔
440
        return;
1✔
441
      }
442

443
      const invoiceUser = await InvoiceUser.findOne({ where: { userId }, relations: ['user'] });
2✔
444
      if (!invoiceUser) {
2✔
445
        res.status(404).json('Invoice User not found.');
1✔
446
        return;
1✔
447
      }
448

449
      res.status(200).json(parseInvoiceUserToResponse(invoiceUser));
1✔
450
    } catch (error) {
451
      this.logger.error('Could not get invoice user:', error);
×
452
      res.status(500).json('Internal server error.');
×
453
    }
454
  }
455

456
  /**
457
   * PUT /invoices/users/{id}
458
   * @summary Update or create invoice user defaults.
459
   * @operationId putInvoiceUser
460
   * @tags invoices - Operations of the invoices controller
461
   * @security JWT
462
   * @param {integer} id.path.required - The id of the user to update
463
   * @param {UpdateInvoiceUserRequest} request.body.required - The invoice user which should be updated
464
   * @return {string} 404 - User not found
465
   * @return {string} 400 - User is not of type INVOICE
466
   * @return {InvoiceUserResponse} 200 - The updated / created Invoice User
467
   * @return {string} 500 - Internal server error
468
   */
469
  public async updateInvoiceUser(req: RequestWithToken, res: Response): Promise<void> {
470
    const { id } = req.params;
5✔
471
    const body = req.body as UpdateInvoiceUserRequest;
5✔
472
    const userId = parseInt(id, 10);
5✔
473
    this.logger.trace('Update Invoice User', id, 'by user', req.token.user);
5✔
474

475
    try {
5✔
476
      const user = await User.findOne({ where: { id: userId, deleted: false } });
5✔
477
      if (!user) {
5✔
478
        res.status(404).json('User not found.');
1✔
479
        return;
1✔
480
      }
481

482
      if (!([UserType.INVOICE, UserType.ORGAN].includes(user.type))) {
4✔
483
        res.status(400).json(`User is of type ${UserType[user.type]} and not of type INVOICE or ORGAN.`);
1✔
484
        return;
1✔
485
      }
486

487
      let invoiceUser = Object.assign(new InvoiceUser(), {
3✔
488
        ...body,
489
        user,
490
      }) as InvoiceUser;
491

492
      invoiceUser = await InvoiceUser.save(invoiceUser);
3✔
493

494
      res.status(200).json(parseInvoiceUserToResponse(invoiceUser));
3✔
495
    } catch (error) {
496
      this.logger.error('Could not update invoice user:', error);
×
497
      res.status(500).json('Internal server error.');
×
498
    }
499
  }
500

501
  /**
502
   * GET /invoices/eligible-transactions
503
   * @summary Get eligible transactions for invoice creation.
504
   * @operationId getEligibleTransactions
505
   * @tags invoices - Operations of the invoices controller
506
   * @security JWT
507
   * @param {integer} forId.query.required - Filter on Id of the debtor
508
   * @param {string} fromDate.query.required - Start date for selected transactions (inclusive)
509
   * @param {string} tillDate.query - End date for selected transactions (exclusive)
510
   * @return {TransactionResponse} 200 - The eligible transactions
511
   * @return {string} 500 - Internal server error
512
   */
513
  public async getEligibleTransactions(req: RequestWithToken, res: Response): Promise<void> {
514
    this.logger.trace('Get eligible transactions for invoice creation', req.query, 'by user', req.token.user);
×
515

516
    let fromDate, tillDate;
517
    let forId;
518
    try {
×
519
      forId = asNumber(req.query.forId);
×
520
      fromDate = asDate(req.query.fromDate);
×
521
      tillDate = req.query.tillDate ? asDate(req.query.tillDate) : undefined;
×
522
    } catch (e) {
523
      res.status(400).send(e.message);
×
524
      return;
×
525
    }
526

527
    try {
×
528
      const transactions = await new InvoiceService().getEligibleTransactions({
×
529
        forId,
530
        fromDate,
531
        tillDate,
532
      });
533
      res.json(transactions);
×
534
    } catch (error) {
535
      this.logger.error('Could not get eligible transactions:', error);
×
536
      res.status(500).json('Internal server error.');
×
537
    }
538
  }
539

540

541
  /**
542
   * GET /invoices/drift
543
   * @summary Returns all invoices with transfer amount drift: for active invoices transfer != row sum; for deleted invoices transfer != credit transfer.
544
   * @operationId getInvoiceDrift
545
   * @tags invoices - Operations of the invoices controller
546
   * @security JWT
547
   * @return {Array<InvoiceDriftResponse>} 200 - All invoices with transfer amount drift
548
   * @return {string} 500 - Internal server error
549
   */
550
  public async getDriftedInvoices(req: RequestWithToken, res: Response): Promise<void> {
551
    this.logger.trace('Get drifted invoices by user', req.token.user);
×
552
    try {
×
553
      const results = await new InvoiceService().findDriftedInvoices();
×
554
      res.json(results.map(InvoiceService.asInvoiceDriftResponse));
×
555
    } catch (error) {
556
      this.logger.error('Could not return drifted invoices:', error);
×
557
      res.status(500).json('Internal server error.');
×
558
    }
559
  }
560

561
  /**
562
   * Function to determine which credentials are needed to get invoice
563
   * all if user is not connected to invoice
564
   * own if user is connected to invoice
565
   * @param req
566
   * @return whether invoice is connected to used token
567
   */
568
  static async getRelation(req: RequestWithToken): Promise<string> {
569
    const invoice: Invoice = await Invoice.findOne({ where: { id: parseInt(req.params.id, 10) }, relations: ['to'] });
9✔
570
    if (!invoice) return 'all';
9✔
571
    if (invoice.to.id === req.token.user.id) return 'own';
7✔
572
    return 'all';
6✔
573
  }
574
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc