Spaces:
Runtime error
Runtime error
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 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 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 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
}
|
| 90 |
-
}
|
| 91 |
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
|
|
|
| 100 |
}
|
| 101 |
-
}
|
| 102 |
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
|
| 114 |
-
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
});
|
| 117 |
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
-
logger.info("Invoices processed successfully in the background");
|
| 121 |
} catch (error) {
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|
| 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<
|
| 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/
|
| 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/
|
| 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/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|