Spaces:
Runtime error
Runtime error
Bansari Akhani
commited on
Commit
·
3a65c87
1
Parent(s):
fa3491c
cetae audit logs on invoice update and sync to pw
Browse files
src/controllers/invoice/invoice.controller.ts
CHANGED
|
@@ -10,9 +10,8 @@ import { FindOptions, Op } from "sequelize";
|
|
| 10 |
import PwPortfolio from "../../models/pwPortfolio";
|
| 11 |
import PwBuilding from "../../models/pwBuildings";
|
| 12 |
import PwUnit from "../../models/pwUnits";
|
| 13 |
-
import { fetchGLAccountById } from "../../shared/services/propertyware.service";
|
| 14 |
-
import { formatDate } from "../../utils/dataUtils";
|
| 15 |
import { logger } from '../../utils/logger';
|
|
|
|
| 16 |
|
| 17 |
|
| 18 |
export const createInvoice = async (req: Request, res: Response) => {
|
|
@@ -24,7 +23,7 @@ export const createInvoice = async (req: Request, res: Response) => {
|
|
| 24 |
|
| 25 |
try {
|
| 26 |
const uploadResults = await uploadInvoice(files);
|
| 27 |
-
|
| 28 |
res.status(201).json({ message: "Files uploaded successfully, processing invoices in the background" });
|
| 29 |
|
| 30 |
// Process the rest of the operations in the background
|
|
@@ -133,7 +132,7 @@ const buildInvoiceWhereClause = (filter: Record<string, any>): any => {
|
|
| 133 |
}
|
| 134 |
|
| 135 |
if (filter.reference_number) {
|
| 136 |
-
whereClause.reference_number = { [Op.like]: `%${filter.reference_number}%` };
|
| 137 |
}
|
| 138 |
|
| 139 |
if (filter.invoice_date) {
|
|
@@ -191,16 +190,16 @@ export const getAllInvoices = async (req: Request, res: Response): Promise<Respo
|
|
| 191 |
const filter = req.query.filter as Record<string, any>;
|
| 192 |
|
| 193 |
const allowedSortColumns = [
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
|
| 205 |
const whereClause = buildInvoiceWhereClause(filter);
|
| 206 |
|
|
@@ -221,7 +220,8 @@ export const getAllInvoices = async (req: Request, res: Response): Promise<Respo
|
|
| 221 |
}
|
| 222 |
|
| 223 |
const [invoices, totalInvoices] = await Promise.all([
|
| 224 |
-
Invoice.findAll({
|
|
|
|
| 225 |
include: [
|
| 226 |
{ model: User,
|
| 227 |
as: 'uploadedBy',
|
|
@@ -250,38 +250,52 @@ export const getAllInvoices = async (req: Request, res: Response): Promise<Respo
|
|
| 250 |
}
|
| 251 |
};
|
| 252 |
|
| 253 |
-
|
| 254 |
-
|
| 255 |
export const getInvoiceById = async (req: Request, res: Response): Promise<Response> => {
|
| 256 |
try {
|
| 257 |
const { id } = req.params;
|
| 258 |
-
const invoice = await Invoice.findOne({
|
| 259 |
-
{
|
| 260 |
-
|
|
|
|
|
|
|
| 261 |
|
| 262 |
if (!invoice) {
|
| 263 |
return res.status(404).json({ error: "Invoice not found" });
|
| 264 |
}
|
| 265 |
-
|
| 266 |
const invoiceDetailsPromises = invoice.InvoiceDetails.map(async (invoiceDetails) => {
|
| 267 |
-
|
| 268 |
return {
|
| 269 |
amount: invoiceDetails.amount,
|
| 270 |
expenseAccount: invoiceDetails.pw_gl_account_id,
|
| 271 |
-
location: {portfolioId
|
| 272 |
description: invoiceDetails.description || ''
|
| 273 |
};
|
| 274 |
});
|
| 275 |
-
|
| 276 |
const billSplit = await Promise.all(invoiceDetailsPromises);
|
| 277 |
-
|
| 278 |
-
return res.status(200).json({invoice: invoice.dataValues, billSplit
|
| 279 |
} catch (error) {
|
| 280 |
logger.error("Error fetching invoice:", error);
|
| 281 |
return res.status(500).json({ error: "Error fetching invoice details" });
|
| 282 |
}
|
| 283 |
};
|
| 284 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
|
| 286 |
export const updateInvoice = async (req: Request, res: Response): Promise<Response> => {
|
| 287 |
const { id } = req.params;
|
|
@@ -313,8 +327,18 @@ export const updateInvoice = async (req: Request, res: Response): Promise<Respon
|
|
| 313 |
uploaded_by: invoice.uploaded_by,
|
| 314 |
};
|
| 315 |
|
|
|
|
| 316 |
const invoiceUpdate = await existingInvoice.update(updatedInvoiceData);
|
| 317 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
// Delete existing InvoiceDetails
|
| 319 |
await InvoiceDetail.destroy({ where: { invoice_id: id } });
|
| 320 |
|
|
|
|
| 10 |
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 |
|
| 16 |
|
| 17 |
export const createInvoice = async (req: Request, res: Response) => {
|
|
|
|
| 23 |
|
| 24 |
try {
|
| 25 |
const uploadResults = await uploadInvoice(files);
|
| 26 |
+
|
| 27 |
res.status(201).json({ message: "Files uploaded successfully, processing invoices in the background" });
|
| 28 |
|
| 29 |
// Process the rest of the operations in the background
|
|
|
|
| 132 |
}
|
| 133 |
|
| 134 |
if (filter.reference_number) {
|
| 135 |
+
whereClause.reference_number = { [Op.like]: `%${filter.reference_number}%` };
|
| 136 |
}
|
| 137 |
|
| 138 |
if (filter.invoice_date) {
|
|
|
|
| 190 |
const filter = req.query.filter as Record<string, any>;
|
| 191 |
|
| 192 |
const allowedSortColumns = [
|
| 193 |
+
'id',
|
| 194 |
+
'invoice_date',
|
| 195 |
+
'total',
|
| 196 |
+
'amount_paid',
|
| 197 |
+
'due_date',
|
| 198 |
+
'pw_work_order_id',
|
| 199 |
+
'pw_vendor_id',
|
| 200 |
+
'uploaded_by',
|
| 201 |
+
'created_at'
|
| 202 |
+
];
|
| 203 |
|
| 204 |
const whereClause = buildInvoiceWhereClause(filter);
|
| 205 |
|
|
|
|
| 220 |
}
|
| 221 |
|
| 222 |
const [invoices, totalInvoices] = await Promise.all([
|
| 223 |
+
Invoice.findAll({
|
| 224 |
+
...options,
|
| 225 |
include: [
|
| 226 |
{ model: User,
|
| 227 |
as: 'uploadedBy',
|
|
|
|
| 250 |
}
|
| 251 |
};
|
| 252 |
|
|
|
|
|
|
|
| 253 |
export const getInvoiceById = async (req: Request, res: Response): Promise<Response> => {
|
| 254 |
try {
|
| 255 |
const { id } = req.params;
|
| 256 |
+
const invoice = await Invoice.findOne({
|
| 257 |
+
where: { id: id }, include: [
|
| 258 |
+
{ model: InvoiceDetail }
|
| 259 |
+
]
|
| 260 |
+
});
|
| 261 |
|
| 262 |
if (!invoice) {
|
| 263 |
return res.status(404).json({ error: "Invoice not found" });
|
| 264 |
}
|
| 265 |
+
|
| 266 |
const invoiceDetailsPromises = invoice.InvoiceDetails.map(async (invoiceDetails) => {
|
| 267 |
+
|
| 268 |
return {
|
| 269 |
amount: invoiceDetails.amount,
|
| 270 |
expenseAccount: invoiceDetails.pw_gl_account_id,
|
| 271 |
+
location: { portfolioId: invoiceDetails.pw_portfolio_id, buildingId: invoiceDetails.pw_building_id, unitId: invoiceDetails.pw_unit_id },
|
| 272 |
description: invoiceDetails.description || ''
|
| 273 |
};
|
| 274 |
});
|
| 275 |
+
|
| 276 |
const billSplit = await Promise.all(invoiceDetailsPromises);
|
| 277 |
+
|
| 278 |
+
return res.status(200).json({ invoice: invoice.dataValues, billSplit: billSplit });
|
| 279 |
} catch (error) {
|
| 280 |
logger.error("Error fetching invoice:", error);
|
| 281 |
return res.status(500).json({ error: "Error fetching invoice details" });
|
| 282 |
}
|
| 283 |
};
|
| 284 |
|
| 285 |
+
function getUpdatedFields(original: any, updated: any): string {
|
| 286 |
+
const changes: string[] = [];
|
| 287 |
+
const skipKeys = ['InvoiceDetails', 'created_at', 'updated_at','deleted_at'];
|
| 288 |
+
for (const key in original) {
|
| 289 |
+
if (skipKeys.includes(key))
|
| 290 |
+
continue;
|
| 291 |
+
console.log(original[key] !== updated[key], key, original[key], updated[key] );
|
| 292 |
+
if (original[key] !== updated[key]) {
|
| 293 |
+
changes.push(`${key}: ${original[key]} -> ${updated[key]}`);
|
| 294 |
+
}
|
| 295 |
+
}
|
| 296 |
+
return changes.join(', ');
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
|
| 300 |
export const updateInvoice = async (req: Request, res: Response): Promise<Response> => {
|
| 301 |
const { id } = req.params;
|
|
|
|
| 327 |
uploaded_by: invoice.uploaded_by,
|
| 328 |
};
|
| 329 |
|
| 330 |
+
const originalInvoiceData = { ...existingInvoice.dataValues };
|
| 331 |
const invoiceUpdate = await existingInvoice.update(updatedInvoiceData);
|
| 332 |
|
| 333 |
+
const updatedFields = getUpdatedFields(originalInvoiceData, invoiceUpdate.dataValues);
|
| 334 |
+
const auditLogData = {
|
| 335 |
+
invoice_id: invoice.id,
|
| 336 |
+
action_by: 1, // replace with the actual user ID performing the action
|
| 337 |
+
action: 'Update Invoice',
|
| 338 |
+
details: updatedFields
|
| 339 |
+
};
|
| 340 |
+
|
| 341 |
+
await AuditLog.create(auditLogData);
|
| 342 |
// Delete existing InvoiceDetails
|
| 343 |
await InvoiceDetail.destroy({ where: { invoice_id: id } });
|
| 344 |
|
src/controllers/propertyware/bills.controller.ts
CHANGED
|
@@ -24,7 +24,7 @@ export const syncInvoices = async (req: Request, res: Response) => {
|
|
| 24 |
// Map the invoices to the bill request format
|
| 25 |
invoices.forEach(async (invoice) => {
|
| 26 |
const bill = {
|
| 27 |
-
billDate: formatDate(invoice.invoice_date),
|
| 28 |
billSplits: invoice.InvoiceDetails.map(detail => ({
|
| 29 |
amount: detail.amount as number,
|
| 30 |
glAccountID: detail.pw_gl_account_id,
|
|
@@ -34,24 +34,22 @@ export const syncInvoices = async (req: Request, res: Response) => {
|
|
| 34 |
paid: false,
|
| 35 |
unitID: detail.pw_unit_id
|
| 36 |
})),
|
| 37 |
-
dueDate: invoice.due_date ? formatDate(invoice.due_date) : null,
|
| 38 |
vendorID: invoice.pw_vendor_id?.toString(),
|
| 39 |
billNumber: invoice.invoice_number,
|
| 40 |
comments: invoice.description,
|
| 41 |
refNo: invoice.reference_number,
|
| 42 |
terms: invoice.term,
|
| 43 |
-
workOrderID:
|
| 44 |
};
|
| 45 |
try {
|
| 46 |
const response = await createBill(bill);
|
| 47 |
console.log(response.id);
|
| 48 |
|
| 49 |
// add audit log for Update Invoice
|
| 50 |
-
// add entry to error log
|
| 51 |
-
const auditLogData = { invoice_id: invoice.id as number, action_by: 1, action: 'Invoice sync to PW', details: `sync Successfull. PW invoice id : ${response.id}`};
|
| 52 |
-
await AuditLog.create(auditLogData);
|
| 53 |
|
| 54 |
-
|
|
|
|
| 55 |
|
| 56 |
logger.info(`Bill for invoice ${bill.refNo} synced successfully`);
|
| 57 |
} catch (error) {
|
|
|
|
| 24 |
// Map the invoices to the bill request format
|
| 25 |
invoices.forEach(async (invoice) => {
|
| 26 |
const bill = {
|
| 27 |
+
billDate: formatDate(invoice.invoice_date),
|
| 28 |
billSplits: invoice.InvoiceDetails.map(detail => ({
|
| 29 |
amount: detail.amount as number,
|
| 30 |
glAccountID: detail.pw_gl_account_id,
|
|
|
|
| 34 |
paid: false,
|
| 35 |
unitID: detail.pw_unit_id
|
| 36 |
})),
|
| 37 |
+
dueDate: invoice.due_date ? formatDate(invoice.due_date) : null,
|
| 38 |
vendorID: invoice.pw_vendor_id?.toString(),
|
| 39 |
billNumber: invoice.invoice_number,
|
| 40 |
comments: invoice.description,
|
| 41 |
refNo: invoice.reference_number,
|
| 42 |
terms: invoice.term,
|
| 43 |
+
workOrderID: null, // FIXME: check if work order id is valid from pwoprtyware workorders
|
| 44 |
};
|
| 45 |
try {
|
| 46 |
const response = await createBill(bill);
|
| 47 |
console.log(response.id);
|
| 48 |
|
| 49 |
// add audit log for Update Invoice
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
+
const auditLogData = { invoice_id: invoice.id as number, action_by: 1, action: 'Invoice sync to PW', details: `Bill for invoice ${bill.refNo} synced successfully. PW invoice id : ${response.id}`};
|
| 52 |
+
await AuditLog.create(auditLogData);
|
| 53 |
|
| 54 |
logger.info(`Bill for invoice ${bill.refNo} synced successfully`);
|
| 55 |
} catch (error) {
|