fix: refactor paymentStatus to improve error handling and response structure
Deploy Application / deploy (push) Successful in 38s Details

This commit is contained in:
faisolavolut 2025-11-11 16:00:50 +07:00
parent a19933a06e
commit e09b2517e1
2 changed files with 135 additions and 98 deletions

View File

@ -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,

View File

@ -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({