fix: refactor paymentStatus to improve error handling and response structure
Deploy Application / deploy (push) Successful in 38s
Details
Deploy Application / deploy (push) Successful in 38s
Details
This commit is contained in:
parent
a19933a06e
commit
e09b2517e1
|
|
@ -82,20 +82,11 @@ function mapRowToRvOpenitem(row: any) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class SyncController {
|
// Function to process a single database sync
|
||||||
static async syncRvOpenitem(req: Request, res: Response) {
|
async function processDatabaseSync(database: any) {
|
||||||
try {
|
|
||||||
const databases = await db.database.findMany({
|
|
||||||
where: {
|
|
||||||
is_synced: "Y",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// sinkron per db satu per satu (each database work is wrapped in a transaction)
|
|
||||||
let totalInserted = 0;
|
|
||||||
let totalUpdated = 0;
|
|
||||||
for (const database of databases) {
|
|
||||||
const pool = await getPoolForDbId(database.db_id);
|
const pool = await getPoolForDbId(database.db_id);
|
||||||
const q = "SELECT * FROM rv_openitem";
|
const q = "SELECT * FROM rv_openitem";
|
||||||
|
|
||||||
// Set timeout to 1 hour for sync operations
|
// Set timeout to 1 hour for sync operations
|
||||||
const client = await pool.connect();
|
const client = await pool.connect();
|
||||||
let result;
|
let result;
|
||||||
|
|
@ -104,53 +95,83 @@ export default class SyncController {
|
||||||
} finally {
|
} finally {
|
||||||
client.release();
|
client.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
const rows = result.rows || [];
|
const rows = result.rows || [];
|
||||||
|
console.log(
|
||||||
|
`Syncing rv_openitem for database ${database.name} (${database.db_id}): fetched ${rows.length} rows`
|
||||||
|
);
|
||||||
|
|
||||||
const freshDB = await db.rv_openitem.count({
|
const freshDB = await db.rv_openitem.count({
|
||||||
where: { db_id: database.db_id },
|
where: { db_id: database.db_id },
|
||||||
});
|
});
|
||||||
// perform upserts in smaller transactions (per-batch) to avoid long-lived transactions
|
|
||||||
|
let dbInserted = 0;
|
||||||
|
let dbUpdated = 0;
|
||||||
const batchSize = 200;
|
const batchSize = 200;
|
||||||
const transactions = [];
|
|
||||||
|
// Process in batches to avoid memory issues
|
||||||
for (let i = 0; i < rows.length; i += batchSize) {
|
for (let i = 0; i < rows.length; i += batchSize) {
|
||||||
const batch = rows.slice(i, i + batchSize);
|
const batch = rows.slice(i, i + batchSize);
|
||||||
const ops = batch.map((r: any) => mapRowToRvOpenitem(r));
|
const ops = batch.map((r: any) => mapRowToRvOpenitem(r));
|
||||||
for (const op of ops) {
|
|
||||||
|
let transactions = [];
|
||||||
|
|
||||||
if (freshDB === 0) {
|
if (freshDB === 0) {
|
||||||
transactions.push(
|
// Fresh DB - just insert all records
|
||||||
|
transactions = ops.map((op) =>
|
||||||
db.rv_openitem.create({
|
db.rv_openitem.create({
|
||||||
data: { ...op, db_id: database.db_id },
|
data: { ...op, db_id: database.db_id },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
totalInserted++;
|
dbInserted += ops.length;
|
||||||
} else {
|
} else {
|
||||||
const existing = await db.rv_openitem.findFirst({
|
// Check existing records in bulk instead of one by one
|
||||||
where: { c_invoice_id: op.c_invoice_id },
|
const invoiceIds = ops.map((op) => op.c_invoice_id).filter(Boolean);
|
||||||
|
const existingRecords = await db.rv_openitem.findMany({
|
||||||
|
where: {
|
||||||
|
c_invoice_id: { in: invoiceIds },
|
||||||
|
db_id: database.db_id,
|
||||||
|
},
|
||||||
|
select: { id: true, c_invoice_id: true },
|
||||||
});
|
});
|
||||||
if (existing) {
|
|
||||||
|
const existingMap = new Map(
|
||||||
|
existingRecords.map((record) => [record.c_invoice_id, record.id])
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const op of ops) {
|
||||||
|
const existingId = existingMap.get(op.c_invoice_id);
|
||||||
|
if (existingId) {
|
||||||
transactions.push(
|
transactions.push(
|
||||||
db.rv_openitem.update({
|
db.rv_openitem.update({
|
||||||
where: { id: existing.id },
|
where: { id: existingId },
|
||||||
data: op,
|
data: op,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
totalUpdated++;
|
dbUpdated++;
|
||||||
} else {
|
} else {
|
||||||
transactions.push(
|
transactions.push(
|
||||||
db.rv_openitem.create({
|
db.rv_openitem.create({
|
||||||
data: { ...op, db_id: database.db_id },
|
data: { ...op, db_id: database.db_id },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
totalInserted++;
|
dbInserted++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup: mark local records as not paid if they no longer exist in source
|
// Execute batch transaction
|
||||||
|
if (transactions.length > 0) {
|
||||||
|
await db.$transaction(transactions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup: mark local records as not paid if they no longer exist in source
|
||||||
if (freshDB > 0) {
|
if (freshDB > 0) {
|
||||||
const existingIds = rows
|
const existingIds = rows
|
||||||
.map((r: any) => r.c_invoice_id)
|
.map((r: any) => r.c_invoice_id)
|
||||||
.filter((v: any) => v !== undefined && v !== null);
|
.filter((v: any) => v !== undefined && v !== null);
|
||||||
|
|
||||||
if (existingIds.length > 0) {
|
if (existingIds.length > 0) {
|
||||||
const toUpdate = await db.rv_openitem.findMany({
|
const toUpdate = await db.rv_openitem.findMany({
|
||||||
where: {
|
where: {
|
||||||
|
|
@ -158,19 +179,46 @@ export default class SyncController {
|
||||||
c_invoice_id: { notIn: existingIds },
|
c_invoice_id: { notIn: existingIds },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
for (const item of toUpdate) {
|
|
||||||
transactions.push(
|
if (toUpdate.length > 0) {
|
||||||
|
const updateTransactions = toUpdate.map((item) =>
|
||||||
db.rv_openitem.update({
|
db.rv_openitem.update({
|
||||||
where: { id: item.id },
|
where: { id: item.id },
|
||||||
data: { is_pay: "N" },
|
data: { is_pay: "N" },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
await db.$transaction(updateTransactions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (transactions.length > 0) await db.$transaction(transactions);
|
|
||||||
|
return { inserted: dbInserted, updated: dbUpdated };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default class SyncController {
|
||||||
|
static async syncRvOpenitem(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const databases = await db.database.findMany({
|
||||||
|
where: {
|
||||||
|
is_synced: "Y",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Process all databases in parallel using Promise.all
|
||||||
|
const results = await Promise.all(
|
||||||
|
databases.map((database) => processDatabaseSync(database))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sum up results from all databases
|
||||||
|
const totalInserted = results.reduce(
|
||||||
|
(sum, result) => sum + result.inserted,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const totalUpdated = results.reduce(
|
||||||
|
(sum, result) => sum + result.updated,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
status: "success",
|
status: "success",
|
||||||
inserted: totalInserted,
|
inserted: totalInserted,
|
||||||
|
|
|
||||||
|
|
@ -446,9 +446,7 @@ export async function paymentStatus(req: Request, res: Response) {
|
||||||
const where: any = {};
|
const where: any = {};
|
||||||
if (startdate || enddate) {
|
if (startdate || enddate) {
|
||||||
if (!startdate || !enddate) {
|
if (!startdate || !enddate) {
|
||||||
return res
|
return res.status(400).json({
|
||||||
.status(400)
|
|
||||||
.json({
|
|
||||||
error: "startdate and enddate are required when filtering by date",
|
error: "startdate and enddate are required when filtering by date",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -489,21 +487,12 @@ export async function paymentStatus(req: Request, res: Response) {
|
||||||
include: { transactions: true },
|
include: { transactions: true },
|
||||||
orderBy: { created_at: "desc" },
|
orderBy: { created_at: "desc" },
|
||||||
});
|
});
|
||||||
|
if (rows.length === 0) {
|
||||||
|
return res
|
||||||
|
.status(404)
|
||||||
|
.json({ responseCode: "404", responseMessage: "no payment found" });
|
||||||
|
}
|
||||||
return res.json({ count: rows.length, data: rows });
|
return res.json({ count: rows.length, data: rows });
|
||||||
|
|
||||||
const result = rows.map((r: any) => ({
|
|
||||||
id: r.id,
|
|
||||||
transaction_id: r.transaction_id,
|
|
||||||
amount: r.amount,
|
|
||||||
documentno: r.documentno,
|
|
||||||
c_invoice_id: r.c_invoice_id,
|
|
||||||
created_at: r.created_at,
|
|
||||||
tenant_id: r.tenant_id,
|
|
||||||
transaction_status: r.transactions ? r.transactions.status : null,
|
|
||||||
transaction_reference: r.transactions ? r.transactions.referenceNo : null,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return res.json({ count: result.length, data: result });
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("paymentStatus error", err);
|
console.error("paymentStatus error", err);
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue