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 {
static async syncRvOpenitem(req: Request, res: Response) {
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) {
// Function to process a single database sync
async function processDatabaseSync(database: any) {
const pool = await getPoolForDbId(database.db_id);
const q = "SELECT * FROM rv_openitem";
// Set timeout to 1 hour for sync operations
const client = await pool.connect();
let result;
@ -104,53 +95,83 @@ export default class SyncController {
} finally {
client.release();
}
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({
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 transactions = [];
// Process in batches to avoid memory issues
for (let i = 0; i < rows.length; i += batchSize) {
const batch = rows.slice(i, i + batchSize);
const ops = batch.map((r: any) => mapRowToRvOpenitem(r));
for (const op of ops) {
let transactions = [];
if (freshDB === 0) {
transactions.push(
// Fresh DB - just insert all records
transactions = ops.map((op) =>
db.rv_openitem.create({
data: { ...op, db_id: database.db_id },
})
);
totalInserted++;
dbInserted += ops.length;
} else {
const existing = await db.rv_openitem.findFirst({
where: { c_invoice_id: op.c_invoice_id },
// Check existing records in bulk instead of one by one
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(
db.rv_openitem.update({
where: { id: existing.id },
where: { id: existingId },
data: op,
})
);
totalUpdated++;
dbUpdated++;
} else {
transactions.push(
db.rv_openitem.create({
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) {
const existingIds = rows
.map((r: any) => r.c_invoice_id)
.filter((v: any) => v !== undefined && v !== null);
if (existingIds.length > 0) {
const toUpdate = await db.rv_openitem.findMany({
where: {
@ -158,18 +179,45 @@ export default class SyncController {
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({
where: { id: item.id },
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({
status: "success",

View File

@ -446,9 +446,7 @@ export async function paymentStatus(req: Request, res: Response) {
const where: any = {};
if (startdate || enddate) {
if (!startdate || !enddate) {
return res
.status(400)
.json({
return res.status(400).json({
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 },
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 });
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) {
console.error("paymentStatus error", err);
return res.status(500).json({