Bansari Akhani commited on
Commit
178b3d8
·
1 Parent(s): 1c7eff7

invoice approval

Browse files
src/controllers/invoice/invoice.controller.ts CHANGED
@@ -16,6 +16,8 @@ import { fetchWorkorderById } from "../../shared/services/propertyware.service";
16
  import { logInvoiceAction } from "../invoiceActivityLogs.controller";
17
  import InvoiceApproval from "../../models/invoiceApproval";
18
  import Role from "../../models/roles";
 
 
19
 
20
  export const createInvoice = async (req: Request, res: Response) => {
21
  const files = req.files as Express.Multer.File[];
@@ -445,9 +447,9 @@ export const updateInvoice = async (req: Request, res: Response): Promise<Respon
445
  uploaded_by: invoice.uploaded_by,
446
  };
447
 
448
- const invoiceUpdate = await updateInvoiceData(existingInvoice.id as number, updatedInvoiceData, userId);
449
 
450
- const invoiceDetails = await updateInvoiceDetails(existingInvoice.id as number, billSplit, userId);
451
 
452
  const updatedInvoice = await Invoice.findByPk(id, {
453
  include: [{ model: InvoiceDetail }],
@@ -479,23 +481,18 @@ export const deleteInvoice = async (req: Request, res: Response): Promise<Respon
479
  }
480
  };
481
 
482
- // Interface for approval data
483
- interface ApprovalData {
484
- invoiceId: number;
485
- userId: number;
486
- comment?: string;
487
- }
488
-
489
  // Function to approve invoice
490
- export const approveInvoice = async (req: Request, res: Response): Promise<void> => {
491
- const { invoiceId, userId = 1, comment = '' }: ApprovalData = req.body;
 
 
492
 
493
  try {
494
  const invoice = await Invoice.findOne({
495
- where: { id: invoiceId },
496
  include: [{ model: InvoiceDetail }]
497
  });
498
-
499
  if (!invoice) {
500
  res.status(404).json({ error: 'Invoice not found' });
501
  return;
@@ -509,55 +506,61 @@ export const approveInvoice = async (req: Request, res: Response): Promise<void>
509
  res.status(400).json({ error: 'Invoice cannot be approved without property address' });
510
  return;
511
  }
 
 
 
 
 
 
 
 
 
 
512
 
513
- const user = await User.findByPk(1, {
514
- include: [{ model: Role }],
515
- });
516
- if (!user) {
517
- res.status(400).json({ error: 'Invalid User' });
518
- return;
519
- }
520
 
521
- const role = await Role.findByPk(user.id);
522
- if (!role) {
523
- res.status(400).json({ error: 'Invalid approval role ID' });
524
- return;
525
- }
526
- const approvalRoleId = role.id;
527
 
528
- if (invoice.total < 1500) {
529
- if (role.name === 'PM' || role.name === 'Accounting Supervisor') {
530
- await approveAndCreateRecord(invoice.id as number, userId, approvalRoleId, comment);
531
-
532
- invoice.status = 'Approved';
533
- await invoice.save();
534
-
535
- res.status(200).json({ message: 'Invoice approved' });
536
- } else {
537
- res.status(403).json({ error: 'Only Property Manager or Accounting Supervisor can approve this invoice' });
538
- }
539
- } else {
540
- if (role.name === 'PM') {
541
- await approveAndCreateRecord(invoice.id as number, userId, approvalRoleId, comment);
542
 
543
- invoice.status = 'PM Approved';
544
- await invoice.save();
545
-
546
- res.status(200).json({ message: 'Invoice approved by PM' });
547
-
548
- } else if (role.name === 'Accounting Supervisor' && invoice.status === 'PM Approved') {
549
- await approveAndCreateRecord(invoice.id as number, userId, approvalRoleId, comment);
550
-
551
- invoice.status = 'Approved';
552
- await invoice.save();
553
-
554
- res.status(200).json({ message: 'Invoice approved by Accounting Supervisor' });
555
-
556
  } else {
557
- res.status(403).json({ error: 'Invoice needs to be approved by Property Manager first' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  }
 
 
 
559
  }
560
  } catch (error) {
 
561
  res.status(500).json({ error: error });
562
  }
563
  };
@@ -569,11 +572,11 @@ const approveAndCreateRecord = async (
569
  comment: string
570
  ): Promise<void> => {
571
  await InvoiceApproval.create({
572
- invoiceId: invoiceId,
573
- approvedBy: userId,
574
- approvalRoleId: approvalRoleId,
575
  comment,
576
- createdAt: new Date()
577
  });
578
  };
579
 
 
16
  import { logInvoiceAction } from "../invoiceActivityLogs.controller";
17
  import InvoiceApproval from "../../models/invoiceApproval";
18
  import Role from "../../models/roles";
19
+ import { AuthenticatedRequest } from "shared/interfaces/user.interface";
20
+ import { ApprovalData } from "shared/interfaces/invoice.interface";
21
 
22
  export const createInvoice = async (req: Request, res: Response) => {
23
  const files = req.files as Express.Multer.File[];
 
447
  uploaded_by: invoice.uploaded_by,
448
  };
449
 
450
+ await updateInvoiceData(existingInvoice.id as number, updatedInvoiceData, userId);
451
 
452
+ await updateInvoiceDetails(existingInvoice.id as number, billSplit, userId);
453
 
454
  const updatedInvoice = await Invoice.findByPk(id, {
455
  include: [{ model: InvoiceDetail }],
 
481
  }
482
  };
483
 
 
 
 
 
 
 
 
484
  // Function to approve invoice
485
+ export const approveInvoice = async (req: AuthenticatedRequest, res: Response): Promise<void> => {
486
+ const { id } = req.params;
487
+ const user = req.user;
488
+ const { comment } = req.body;
489
 
490
  try {
491
  const invoice = await Invoice.findOne({
492
+ where: { id: id },
493
  include: [{ model: InvoiceDetail }]
494
  });
495
+
496
  if (!invoice) {
497
  res.status(404).json({ error: 'Invoice not found' });
498
  return;
 
506
  res.status(400).json({ error: 'Invoice cannot be approved without property address' });
507
  return;
508
  }
509
+ let roleData;
510
+ if (user && typeof user !== 'string') {
511
+
512
+ const userData = await User.findByPk(user?.id, {
513
+ include: [{ model: Role, as: 'role' }],
514
+ });
515
+ if (!userData) {
516
+ res.status(400).json({ error: 'Invalid User' });
517
+ return;
518
+ }
519
 
520
+ roleData = await Role.findByPk(userData.role_id);
521
+ if (!roleData) {
522
+ res.status(400).json({ error: 'Invalid approval role ID' });
523
+ return;
524
+ }
 
 
525
 
526
+ if (invoice.total < 1500) {
527
+ if (roleData.name === 'Property Manager' || roleData.name === 'Accounting Supervisor') {
528
+ await approveAndCreateRecord(invoice.id as number, userData.id, roleData.id, comment);
 
 
 
529
 
530
+ invoice.status = 'Approved';
531
+ await invoice.save();
 
 
 
 
 
 
 
 
 
 
 
 
532
 
533
+ res.status(200).json({ message: 'Invoice approved' });
534
+ } else {
535
+ res.status(403).json({ error: 'Only Property Manager or Accounting Supervisor can approve this invoice' });
536
+ }
 
 
 
 
 
 
 
 
 
537
  } else {
538
+ if (roleData.name === 'Property Manager') {
539
+ await approveAndCreateRecord(invoice.id as number, userData.id, roleData.id, comment);
540
+
541
+ invoice.status = 'PM Approved';
542
+ await invoice.save();
543
+
544
+ res.status(200).json({ message: 'Invoice approved by PM' });
545
+
546
+ } else if (roleData.name === 'Accounting Supervisor' && invoice.status === 'PM Approved') {
547
+ await approveAndCreateRecord(invoice.id as number, userData.id, roleData.id, comment);
548
+
549
+ invoice.status = 'Approved';
550
+ await invoice.save();
551
+
552
+ res.status(200).json({ message: 'Invoice approved by Accounting Supervisor' });
553
+
554
+ } else {
555
+ res.status(403).json({ error: 'Invoice needs to be approved by Property Manager first' });
556
+ }
557
  }
558
+ } else {
559
+ res.status(400).json({ error: 'Invalid User' });
560
+ return;
561
  }
562
  } catch (error) {
563
+ console.log(error);
564
  res.status(500).json({ error: error });
565
  }
566
  };
 
572
  comment: string
573
  ): Promise<void> => {
574
  await InvoiceApproval.create({
575
+ invoice_id: invoiceId,
576
+ approved_by: userId,
577
+ approval_role_id: approvalRoleId,
578
  comment,
579
+ created_at: new Date()
580
  });
581
  };
582
 
src/middlewares/authMiddleware.ts CHANGED
@@ -1,13 +1,17 @@
1
- import { Request, Response, NextFunction } from 'express';
2
  import { verifyToken } from '../utils/jwtUtils';
3
- const jwtMiddleware = (req: Request, res: Response, next: NextFunction) => {
 
 
4
  const token = req.header('authorization')?.split(' ')[1];
5
  if (!token) {
6
  return res.status(401).json({ error: 'Unauthorized' });
7
  }
8
 
9
  try {
10
- const decoded = verifyToken(token);
 
 
11
  next();
12
  } catch (error) {
13
  return res.status(401).json({ error: 'Unauthorized' });
@@ -15,4 +19,4 @@ const jwtMiddleware = (req: Request, res: Response, next: NextFunction) => {
15
 
16
  }
17
 
18
- export {jwtMiddleware}
 
1
+ import { Response, NextFunction } from 'express';
2
  import { verifyToken } from '../utils/jwtUtils';
3
+ import { AuthenticatedRequest } from '../shared/interfaces/user.interface';
4
+
5
+ const jwtMiddleware = (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
6
  const token = req.header('authorization')?.split(' ')[1];
7
  if (!token) {
8
  return res.status(401).json({ error: 'Unauthorized' });
9
  }
10
 
11
  try {
12
+ const user = verifyToken(token);
13
+ console.log("Loagged in User ", user);
14
+ req.user = user;
15
  next();
16
  } catch (error) {
17
  return res.status(401).json({ error: 'Unauthorized' });
 
19
 
20
  }
21
 
22
+ export {jwtMiddleware}
src/models/invoiceApproval.ts CHANGED
@@ -6,11 +6,11 @@ import { sequelize } from './index';
6
  class InvoiceApproval extends Model<InvoiceApprovalAttributes>
7
  implements InvoiceApprovalAttributes {
8
  public id?: number;
9
- public invoiceId!: number;
10
- public approvedBy!: number;
11
- public approvalRoleId!: number;
12
  public comment?: string;
13
- public createdAt?: Date;
14
 
15
  }
16
 
@@ -21,7 +21,7 @@ InvoiceApproval.init(
21
  autoIncrement: true,
22
  primaryKey: true,
23
  },
24
- invoiceId: {
25
  type: DataTypes.INTEGER,
26
  allowNull: false,
27
  references: {
@@ -31,11 +31,11 @@ InvoiceApproval.init(
31
  onUpdate: 'CASCADE',
32
  onDelete: 'CASCADE',
33
  },
34
- approvedBy: {
35
  type: DataTypes.INTEGER,
36
  allowNull: false,
37
  },
38
- approvalRoleId: {
39
  type: DataTypes.INTEGER,
40
  allowNull: false,
41
  },
@@ -43,7 +43,7 @@ InvoiceApproval.init(
43
  type: DataTypes.TEXT,
44
  allowNull: true,
45
  },
46
- createdAt: {
47
  type: DataTypes.DATE,
48
  defaultValue: DataTypes.NOW,
49
  },
 
6
  class InvoiceApproval extends Model<InvoiceApprovalAttributes>
7
  implements InvoiceApprovalAttributes {
8
  public id?: number;
9
+ public invoice_id!: number;
10
+ public approved_by!: number;
11
+ public approval_role_id!: number;
12
  public comment?: string;
13
+ public created_at?: Date;
14
 
15
  }
16
 
 
21
  autoIncrement: true,
22
  primaryKey: true,
23
  },
24
+ invoice_id: {
25
  type: DataTypes.INTEGER,
26
  allowNull: false,
27
  references: {
 
31
  onUpdate: 'CASCADE',
32
  onDelete: 'CASCADE',
33
  },
34
+ approved_by: {
35
  type: DataTypes.INTEGER,
36
  allowNull: false,
37
  },
38
+ approval_role_id: {
39
  type: DataTypes.INTEGER,
40
  allowNull: false,
41
  },
 
43
  type: DataTypes.TEXT,
44
  allowNull: true,
45
  },
46
+ created_at: {
47
  type: DataTypes.DATE,
48
  defaultValue: DataTypes.NOW,
49
  },
src/routes/invoice.routes.ts CHANGED
@@ -12,30 +12,6 @@ import {
12
  } from '../controllers/invoice/invoice.controller';
13
 
14
  const invoiceRouter = express.Router();
15
-
16
- /**
17
- * @swagger
18
- * /api/invoices/{id}/approve:
19
- * post:
20
- * summary: Approve Invoice
21
- * tags: [Invoices]
22
- * parameters:
23
- * - in: path
24
- * name: id
25
- * schema:
26
- * type: integer
27
- * required: true
28
- * description: Invoice ID
29
- * responses:
30
- * 200:
31
- * description: Invoices approved successfully
32
- * 400:
33
- * description: Bad Request
34
- * 500:
35
- * description: Error approving invoices
36
- */
37
- invoiceRouter.post("/:id/approve", [], approveInvoice);
38
-
39
  invoiceRouter.use(jwtMiddleware);
40
 
41
  /**
@@ -366,4 +342,28 @@ invoiceRouter.put('/:id', updateInvoice);
366
  */
367
  invoiceRouter.delete('/:id', deleteInvoice);
368
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  export default invoiceRouter;
 
12
  } from '../controllers/invoice/invoice.controller';
13
 
14
  const invoiceRouter = express.Router();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  invoiceRouter.use(jwtMiddleware);
16
 
17
  /**
 
342
  */
343
  invoiceRouter.delete('/:id', deleteInvoice);
344
 
345
+
346
+ /**
347
+ * @swagger
348
+ * /api/invoices/{id}/approve:
349
+ * post:
350
+ * summary: Approve Invoice
351
+ * tags: [Invoices]
352
+ * parameters:
353
+ * - in: path
354
+ * name: id
355
+ * schema:
356
+ * type: integer
357
+ * required: true
358
+ * description: Invoice ID
359
+ * responses:
360
+ * 200:
361
+ * description: Invoices approved successfully
362
+ * 400:
363
+ * description: Bad Request
364
+ * 500:
365
+ * description: Error approving invoices
366
+ */
367
+ invoiceRouter.post("/:id/approve", [], approveInvoice);
368
+
369
  export default invoiceRouter;
src/shared/interfaces/invoice.interface.ts CHANGED
@@ -17,3 +17,9 @@ export interface InvoiceInterface {
17
  status: string;
18
  uploaded_by: number;
19
  }
 
 
 
 
 
 
 
17
  status: string;
18
  uploaded_by: number;
19
  }
20
+
21
+ export interface ApprovalData {
22
+ invoiceId: string;
23
+ userId: number;
24
+ comment?: string;
25
+ }
src/shared/interfaces/invoiceApproval.interface.ts CHANGED
@@ -1,8 +1,8 @@
1
  export interface InvoiceApprovalAttributes {
2
  id?: number;
3
- invoiceId: number;
4
- approvedBy: number;
5
- approvalRoleId: number;
6
  comment?: string;
7
- createdAt?: Date;
8
  }
 
1
  export interface InvoiceApprovalAttributes {
2
  id?: number;
3
+ invoice_id: number;
4
+ approved_by: number;
5
+ approval_role_id: number;
6
  comment?: string;
7
+ created_at?: Date;
8
  }
src/shared/interfaces/user.interface.ts CHANGED
@@ -1,3 +1,5 @@
 
 
1
  export interface UserInterface {
2
  id?: number;
3
  name: string;
@@ -6,3 +8,7 @@ export interface UserInterface {
6
  status: string;
7
  password: string;
8
  }
 
 
 
 
 
1
+ import { JwtPayload } from 'jsonwebtoken';
2
+ import { Request } from 'express';
3
  export interface UserInterface {
4
  id?: number;
5
  name: string;
 
8
  status: string;
9
  password: string;
10
  }
11
+
12
+ export interface AuthenticatedRequest extends Request {
13
+ user?: string | JwtPayload;
14
+ }