Bansari Akhani commited on
Commit
1421ecc
·
1 Parent(s): b52eb95

check if workorder id parsed from ai sevice exists in PW

Browse files
src/controllers/invoice/invoice.controller.ts CHANGED
@@ -11,9 +11,9 @@ import PwPortfolio from "../../models/pwPortfolio";
11
  import PwBuilding from "../../models/pwBuildings";
12
  import PwUnit from "../../models/pwUnits";
13
  import { logger } from '../../utils/logger';
14
- import AuditLog from "../../models/auditLogs";
15
  import { createAuditLog } from "../auditLog.controller";
16
-
 
17
 
18
  export const createInvoice = async (req: Request, res: Response) => {
19
  const files = req.files as Express.Multer.File[];
@@ -29,101 +29,150 @@ export const createInvoice = async (req: Request, res: Response) => {
29
 
30
  // Process the rest of the operations in the background
31
  const createInvoicePromises = files.map(async (file, index) => {
32
- const formData = new FormData();
33
- formData.append("file", file.buffer, file.originalname);
34
-
35
- // Send the file URLs to the AI service API for parsing
36
- const aiServiceResponse = await parseInvoice(formData);
37
- const aiServiceData = aiServiceResponse[0];
38
-
39
- const totalAmount = aiServiceData.billSplits.reduce(
40
- (total: number, split: { amount: number }) => total + split.amount,
41
- 0
42
- );
43
- const invoiceDate = new Date(aiServiceData.billDate);
44
- const dueDate = new Date(aiServiceData.dueDate);
45
- let invoiceRecord = {
46
- reference_number: aiServiceData.refNo,
47
- invoice_number: aiServiceData.billNumber,
48
- vendor_name: aiServiceData.vendor_name,
49
- pw_vendor_id: aiServiceData.vendor_id,
50
- invoice_date: invoiceDate,
51
- due_date: dueDate,
52
- total: totalAmount,
53
- description: aiServiceData.description,
54
- status: "Pending",
55
- amount_paid: 0,
56
- term: aiServiceData.terms,
57
- pw_work_order_id: aiServiceData.workOrderID,
58
- filename: file.originalname,
59
- pdf_url: (await uploadResults[index]).Location,
60
- uploaded_by: 1, // TODO : replace with logged in user when user module is implemented
61
- };
62
-
63
- const result = await Invoice.create(invoiceRecord);
64
-
65
- const invoiceDetailsPromises = aiServiceData.billSplits.map(async (details: { portfolio: any; building: any; unit: any; glAccount: any; amount: any; description: any; }) => {
66
- let portfolioId = null;
67
- let buildingId = null;
68
- let unitId = null;
69
-
70
- if (details.portfolio) {
71
- const portfolioData = await PwPortfolio.findOne({
72
- where: {
73
- name: details.portfolio,
74
- },
75
- });
76
- if (portfolioData) {
77
- portfolioId = portfolioData.id;
78
  }
79
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- if (details.building) {
82
- const buildingData = await PwBuilding.findOne({
83
- where: {
84
- name: details.building,
85
- },
86
- });
87
- if (buildingData) {
88
- buildingId = buildingData.id;
 
 
 
 
 
 
 
 
89
  }
90
- }
91
 
92
- if (details.unit) {
93
- const unitData = await PwUnit.findOne({
94
- where: {
95
- name: details.unit,
96
- },
97
- });
98
- if (unitData) {
99
- unitId = unitData.id;
 
100
  }
101
- }
102
 
103
- return {
104
- invoice_id: result.id,
105
- pw_portfolio_id: portfolioId,
106
- pw_building_id: buildingId,
107
- pw_unit_id: unitId,
108
- pw_gl_account_id: details.glAccount,
109
- amount: details.amount,
110
- description: details.description,
111
- };
112
- });
113
 
114
- const invoiceDetails = await Promise.all(invoiceDetailsPromises);
115
- await InvoiceDetail.bulkCreate(invoiceDetails);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  });
117
 
118
- await Promise.all(createInvoicePromises);
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
- logger.info("Invoices processed successfully in the background");
121
  } catch (error) {
122
- logger.error("Error processing invoices in the background: ", error);
 
 
 
 
 
 
 
 
123
  }
124
  };
125
 
126
 
 
 
127
  const buildInvoiceWhereClause = (filter: Record<string, any>): any => {
128
  const whereClause: any = {};
129
 
 
11
  import PwBuilding from "../../models/pwBuildings";
12
  import PwUnit from "../../models/pwUnits";
13
  import { logger } from '../../utils/logger';
 
14
  import { createAuditLog } from "../auditLog.controller";
15
+ import ErrorLog from "../../models/errorLog";
16
+ import { fetchWorkorderById } from "../../shared/services/propertyware.service";
17
 
18
  export const createInvoice = async (req: Request, res: Response) => {
19
  const files = req.files as Express.Multer.File[];
 
29
 
30
  // Process the rest of the operations in the background
31
  const createInvoicePromises = files.map(async (file, index) => {
32
+ try {
33
+ const formData = new FormData();
34
+ formData.append("file", file.buffer, file.originalname);
35
+
36
+
37
+ // Send the file URLs to the AI service API for parsing
38
+ const aiServiceResponse = await parseInvoice(formData);
39
+ const aiServiceData = aiServiceResponse[0];
40
+
41
+ console.log(aiServiceData);
42
+ // validate if parsed workorderID exists in propertyware workorders
43
+ if (aiServiceData.workOrderID){
44
+ try {
45
+ const PWWorkorderDetails = await fetchWorkorderById(aiServiceData.workOrderID);
46
+ console.log(PWWorkorderDetails);
47
+ } catch (error) {
48
+ console.log('workore error : ', error)
49
+ aiServiceData.workOrderID = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
  }
52
+ console.log(aiServiceData);
53
+
54
+ const totalAmount = aiServiceData.billSplits.reduce(
55
+ (total: number, split: { amount: number }) => total + split.amount,
56
+ 0
57
+ );
58
+ const invoiceDate = new Date(aiServiceData.billDate);
59
+ const dueDate = new Date(aiServiceData.dueDate);
60
+ let invoiceRecord = {
61
+ reference_number: aiServiceData.refNo,
62
+ invoice_number: aiServiceData.billNumber,
63
+ vendor_name: aiServiceData.vendor_name,
64
+ pw_vendor_id: aiServiceData.vendor_id,
65
+ invoice_date: invoiceDate,
66
+ due_date: dueDate,
67
+ total: totalAmount,
68
+ description: aiServiceData.description,
69
+ status: "Pending",
70
+ amount_paid: 0,
71
+ term: aiServiceData.terms,
72
+ pw_work_order_id: aiServiceData.workOrderID,
73
+ filename: file.originalname,
74
+ pdf_url: (await uploadResults[index]).Location,
75
+ uploaded_by: 1, // TODO : replace with logged in user when user module is implemented
76
+ };
77
 
78
+ const result = await Invoice.create(invoiceRecord);
79
+
80
+ const invoiceDetailsPromises = aiServiceData.billSplits.map(async (details: { portfolio: any; building: any; unit: any; glAccount: any; amount: any; description: any; }) => {
81
+ let portfolioId = null;
82
+ let buildingId = null;
83
+ let unitId = null;
84
+
85
+ if (details.portfolio) {
86
+ const portfolioData = await PwPortfolio.findOne({
87
+ where: {
88
+ name: details.portfolio,
89
+ },
90
+ });
91
+ if (portfolioData) {
92
+ portfolioId = portfolioData.id;
93
+ }
94
  }
 
95
 
96
+ if (details.building) {
97
+ const buildingData = await PwBuilding.findOne({
98
+ where: {
99
+ name: details.building,
100
+ },
101
+ });
102
+ if (buildingData) {
103
+ buildingId = buildingData.id;
104
+ }
105
  }
 
106
 
107
+ if (details.unit) {
108
+ const unitData = await PwUnit.findOne({
109
+ where: {
110
+ name: details.unit,
111
+ },
112
+ });
113
+ if (unitData) {
114
+ unitId = unitData.id;
115
+ }
116
+ }
117
 
118
+ return {
119
+ invoice_id: result.id,
120
+ pw_portfolio_id: portfolioId,
121
+ pw_building_id: buildingId,
122
+ pw_unit_id: unitId,
123
+ pw_gl_account_id: details.glAccount,
124
+ amount: details.amount,
125
+ description: details.description,
126
+ };
127
+ });
128
+
129
+ const invoiceDetails = await Promise.all(invoiceDetailsPromises);
130
+ await InvoiceDetail.bulkCreate(invoiceDetails);
131
+ } catch (error) {
132
+ if (error instanceof Error) {
133
+ logger.error(`Error creating invoice for file ${file.originalname}: ${error}`);
134
+ // Add entry to error log
135
+ const errorLogData = { error_type: 'Invoice Create Failed', error_details: `Error creating invoice for file ${file.originalname}: ${error.message}` };
136
+ await ErrorLog.create(errorLogData);
137
+ } else {
138
+ logger.error(`Unknown error creating invoice for file ${file.originalname}`, error);
139
+ const errorLogData = { error_type: 'Invoice Create Failed', error_details: `Unknown error creating invoice for file ${file.originalname}: ${JSON.stringify(error)}` };
140
+ await ErrorLog.create(errorLogData);
141
+ }
142
+ }
143
  });
144
 
145
+ try {
146
+ await Promise.all(createInvoicePromises);
147
+ logger.info("Invoices processed successfully in the background");
148
+ } catch (error) {
149
+ if (error instanceof Error) {
150
+ logger.error(`Error processing invoices in the background: ${error}`);
151
+ const errorLogData = { error_type: 'Invoice Process Failed', error_details: `Error processing invoices in the background: ${error.message}` };
152
+ await ErrorLog.create(errorLogData);
153
+ } else {
154
+ logger.error('Unknown error processing invoices in the background', error);
155
+ const errorLogData = { error_type: 'Invoice Process Failed', error_details: `Unknown error processing invoices in the background: ${JSON.stringify(error)}` };
156
+ await ErrorLog.create(errorLogData);
157
+ }
158
+ }
159
 
 
160
  } catch (error) {
161
+ if (error instanceof Error) {
162
+ logger.error(`Error uploading invoices: ${error.message}`);
163
+ const errorLogData = { error_type: 'Invoice Upload Failed', error_details: `Error uploading invoices: ${error.message}` };
164
+ await ErrorLog.create(errorLogData);
165
+ } else {
166
+ logger.error('Unknown error uploading invoices', error);
167
+ const errorLogData = { error_type: 'Invoice Upload Failed', error_details: `Unknown error uploading invoices: ${JSON.stringify(error)}` };
168
+ await ErrorLog.create(errorLogData);
169
+ }
170
  }
171
  };
172
 
173
 
174
+
175
+
176
  const buildInvoiceWhereClause = (filter: Record<string, any>): any => {
177
  const whereClause: any = {};
178
 
src/controllers/propertyware/bills.controller.ts CHANGED
@@ -4,9 +4,7 @@ import Invoice from '../../models/invoice';
4
  import InvoiceDetail from '../../models/invoicedetail';
5
  import { createBill } from '../../shared/services/propertyware.service';
6
  import { formatDate } from '../../utils/dataUtils';
7
- import AuditLog from '../../models/auditLogs';
8
  import ErrorLog from './../../models/errorLog';
9
- import { AxiosError } from 'axios';
10
  import { createAuditLog } from '../auditLog.controller';
11
 
12
  export const syncInvoices = async (req: Request, res: Response) => {
@@ -41,7 +39,7 @@ export const syncInvoices = async (req: Request, res: Response) => {
41
  comments: invoice.description,
42
  refNo: invoice.reference_number,
43
  terms: invoice.term,
44
- workOrderID: null, // FIXME: check if work order id is valid from pwoprtyware workorders
45
  };
46
  try {
47
  const response = await createBill(bill);
 
4
  import InvoiceDetail from '../../models/invoicedetail';
5
  import { createBill } from '../../shared/services/propertyware.service';
6
  import { formatDate } from '../../utils/dataUtils';
 
7
  import ErrorLog from './../../models/errorLog';
 
8
  import { createAuditLog } from '../auditLog.controller';
9
 
10
  export const syncInvoices = async (req: Request, res: Response) => {
 
39
  comments: invoice.description,
40
  refNo: invoice.reference_number,
41
  terms: invoice.term,
42
+ workOrderID: invoice.pw_work_order_id,
43
  };
44
  try {
45
  const response = await createBill(bill);
src/models/errorLog.ts CHANGED
@@ -10,9 +10,9 @@ import { ErrorLogInterface } from '../shared/interfaces/errorLog.interface';
10
  import Invoice from './invoice';
11
 
12
 
13
- class ErrorLog extends Model<InferAttributes<ErrorLog>, InferCreationAttributes<ErrorLog>>implements ErrorLogInterface {
14
  declare id?: CreationOptional<number>;
15
- declare invoice_id: number;
16
  declare error_type: string;
17
  declare error_details: string;
18
  }
@@ -52,4 +52,4 @@ ErrorLog.init(
52
 
53
  ErrorLog.belongsTo(Invoice, { foreignKey: 'invoice_id' });
54
 
55
- export default ErrorLog
 
10
  import Invoice from './invoice';
11
 
12
 
13
+ class ErrorLog extends Model<ErrorLogInterface>implements ErrorLogInterface {
14
  declare id?: CreationOptional<number>;
15
+ declare invoice_id?: number;
16
  declare error_type: string;
17
  declare error_details: string;
18
  }
 
52
 
53
  ErrorLog.belongsTo(Invoice, { foreignKey: 'invoice_id' });
54
 
55
+ export default ErrorLog
src/shared/interfaces/errorLog.interface.ts CHANGED
@@ -1,6 +1,6 @@
1
  export interface ErrorLogInterface {
2
  id?: number,
3
- invoice_id: number,
4
  error_type: string,
5
  error_details: string,
6
  }
 
1
  export interface ErrorLogInterface {
2
  id?: number,
3
+ invoice_id?: number,
4
  error_type: string,
5
  error_details: string,
6
  }
src/shared/services/propertyware.service.ts CHANGED
@@ -21,7 +21,7 @@ export const fetchPortfolio = async (params = []) => {
21
  };
22
 
23
  export const fetchPortfolioById = async (portfolioId: number) => {
24
- const response = await apiClientPW.get(`/portfolios/` + portfolioId);
25
  return response.data;
26
  };
27
 
@@ -31,7 +31,7 @@ export const fetchBuildings = async (params: Record<string, string> = {} ) => {
31
  };
32
 
33
  export const fetchBuildingsById = async (buildingId: number) => {
34
- const response = await apiClientPW.get(`/portfolios/` + buildingId);
35
  return response.data;
36
  };
37
 
@@ -41,7 +41,12 @@ export const fetchUnits = async (params: Record<string, string> = {}) => {
41
  };
42
 
43
  export const fetchUnitsById = async (unitId: number) => {
44
- const response = await apiClientPW.get(`/units/` + unitId);
 
 
 
 
 
45
  return response.data;
46
  };
47
 
 
21
  };
22
 
23
  export const fetchPortfolioById = async (portfolioId: number) => {
24
+ const response = await apiClientPW.get(`/portfolios/${portfolioId}`);
25
  return response.data;
26
  };
27
 
 
31
  };
32
 
33
  export const fetchBuildingsById = async (buildingId: number) => {
34
+ const response = await apiClientPW.get(`/portfolios/${buildingId}`);
35
  return response.data;
36
  };
37
 
 
41
  };
42
 
43
  export const fetchUnitsById = async (unitId: number) => {
44
+ const response = await apiClientPW.get(`/units/${unitId}`);
45
+ return response.data;
46
+ };
47
+
48
+ export const fetchWorkorderById = async (workOrderID: number) => {
49
+ const response = await apiClientPW.get(`/workorders/${workOrderID}`);
50
  return response.data;
51
  };
52