mirror of
https://github.com/Sea-Haven-Industries/payments-dashboard.git
synced 2026-09-30 06:33:11 +00:00
- Add standalone test script to validate sandbox API connectivity for check management and account info endpoints - Update seed-bank-status to persist amount, issueDate, and method fields - Add data/ to gitignore
172 lines
5.6 KiB
JavaScript
172 lines
5.6 KiB
JavaScript
/**
|
|
* Seed bank status data from BoA exports into DynamoDB.
|
|
*
|
|
* Usage:
|
|
* node scripts/seed-bank-status.js \
|
|
* --cleared "Bank Export.csv" \
|
|
* --outstanding "PrintIssuesForInquiry.csv" \
|
|
* --issued "Positive Pay Import original.csv"
|
|
*
|
|
* All flags are optional — supply whichever files you have.
|
|
*
|
|
* Updates existing payment records:
|
|
* - status: overwritten to "Cleared" | "Outstanding" | "Issued"
|
|
* - paid_date: date the check cleared (from Bank Export)
|
|
*/
|
|
|
|
import { readFileSync } from "fs";
|
|
import { parse } from "csv-parse/sync";
|
|
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
import { DynamoDBDocumentClient, UpdateCommand } from "@aws-sdk/lib-dynamodb";
|
|
|
|
const TABLE_NAME = "PaymentsDashboard";
|
|
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient());
|
|
|
|
function parseArgs() {
|
|
const args = process.argv.slice(2);
|
|
const result = {};
|
|
for (let i = 0; i < args.length; i += 2) {
|
|
const flag = args[i].replace(/^--/, "");
|
|
result[flag] = args[i + 1];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function readCSV(filePath) {
|
|
const text = readFileSync(filePath, "utf-8");
|
|
return parse(text, { columns: true, skip_empty_lines: true, trim: true });
|
|
}
|
|
|
|
// Normalize short dates like "1/27/26" → "2026-01-27"
|
|
function toISODate(dateStr) {
|
|
if (!dateStr) return null;
|
|
dateStr = dateStr.trim();
|
|
|
|
// Already ISO-ish: 04/13/2026
|
|
let parts = dateStr.split("/");
|
|
if (parts.length === 3) {
|
|
let [mm, dd, yyyy] = parts;
|
|
if (yyyy.length === 2) yyyy = `20${yyyy}`;
|
|
return `${yyyy}-${mm.padStart(2, "0")}-${dd.padStart(2, "0")}`;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async function updateStatus(checkNumber, status, { paidDate, amount, issueDate, method } = {}) {
|
|
const pk = `payment#${checkNumber}`;
|
|
const expr = ["#status = :s"];
|
|
const names = { "#status": "status" };
|
|
const values = { ":s": status };
|
|
|
|
if (paidDate) {
|
|
expr.push("paid_date = :pd");
|
|
values[":pd"] = paidDate;
|
|
}
|
|
if (amount != null) {
|
|
expr.push("amount_usd = :amt");
|
|
values[":amt"] = amount;
|
|
}
|
|
if (issueDate) {
|
|
expr.push("send_payment_on = :sd");
|
|
values[":sd"] = issueDate;
|
|
}
|
|
if (checkNumber) {
|
|
expr.push("check_number = :cn");
|
|
values[":cn"] = checkNumber;
|
|
}
|
|
if (method) {
|
|
expr.push("#method = :m");
|
|
names["#method"] = "method";
|
|
values[":m"] = method;
|
|
}
|
|
|
|
await ddb.send(
|
|
new UpdateCommand({
|
|
TableName: TABLE_NAME,
|
|
Key: { pk },
|
|
UpdateExpression: `SET ${expr.join(", ")}`,
|
|
ExpressionAttributeNames: names,
|
|
ExpressionAttributeValues: values,
|
|
})
|
|
);
|
|
}
|
|
|
|
async function main() {
|
|
const flags = parseArgs();
|
|
const statusMap = new Map(); // checkNumber → { status, paidDate }
|
|
|
|
// 1. Positive Pay Import (lowest priority) — "Issued"
|
|
// Format: check_number, date, ?, ?, amount (no headers)
|
|
if (flags.issued) {
|
|
console.log(`Reading positive pay import: ${flags.issued}`);
|
|
const text = readFileSync(flags.issued, "utf-8");
|
|
const rows = parse(text, { skip_empty_lines: true, trim: true });
|
|
for (const row of rows) {
|
|
const checkNumber = row[0]?.trim();
|
|
if (!checkNumber) continue;
|
|
const amount = parseFloat(String(row[4] || "0").replace(/,/g, ""));
|
|
const issueDate = row[1]?.trim() || null;
|
|
statusMap.set(checkNumber, { status: "Issued", paidDate: null, amount: isNaN(amount) ? 0 : amount, issueDate, method: "Check" });
|
|
}
|
|
console.log(` ${rows.length} positive pay entries`);
|
|
}
|
|
|
|
// 2. Outstanding Issues (medium priority) — "Outstanding"
|
|
if (flags.outstanding) {
|
|
console.log(`Reading outstanding issues: ${flags.outstanding}`);
|
|
const rows = readCSV(flags.outstanding);
|
|
for (const row of rows) {
|
|
const checkNumber = (row["Check Number"] || "").trim();
|
|
if (!checkNumber) continue;
|
|
const amount = parseFloat(String(row["Amount"] || "0").replace(/,/g, ""));
|
|
const rawDate = (row["Issue Date"] || "").trim();
|
|
// Normalize 2-digit year to 4-digit: "12/10/24" → "12/10/2024"
|
|
let issueDate = rawDate || null;
|
|
if (rawDate) {
|
|
const parts = rawDate.split("/");
|
|
if (parts.length === 3 && parts[2].length === 2) {
|
|
parts[2] = `20${parts[2]}`;
|
|
issueDate = parts.join("/");
|
|
}
|
|
}
|
|
statusMap.set(checkNumber, { status: "Outstanding", paidDate: null, amount: isNaN(amount) ? 0 : amount, issueDate, method: "Check" });
|
|
}
|
|
console.log(` ${rows.length} outstanding entries`);
|
|
}
|
|
|
|
// 3. Bank Export / Cleared (highest priority) — "Cleared"
|
|
// Duplicates exist (same check appears twice); just take the first paid date.
|
|
if (flags.cleared) {
|
|
console.log(`Reading bank export (cleared): ${flags.cleared}`);
|
|
const rows = readCSV(flags.cleared);
|
|
const seen = new Set();
|
|
let deduped = 0;
|
|
for (const row of rows) {
|
|
const checkNumber = (row["Check Number"] || "").trim();
|
|
if (!checkNumber) continue;
|
|
if (seen.has(checkNumber)) {
|
|
deduped++;
|
|
continue;
|
|
}
|
|
seen.add(checkNumber);
|
|
const paidDate = toISODate(row["Paid Date"] || row["CD Volume Number"] || "");
|
|
const amount = parseFloat(String(row["Amount"] || "0").replace(/,/g, ""));
|
|
statusMap.set(checkNumber, { status: "Cleared", paidDate, amount: isNaN(amount) ? 0 : amount });
|
|
}
|
|
console.log(` ${seen.size} unique cleared checks (${deduped} duplicates skipped)`);
|
|
}
|
|
|
|
// Write to DynamoDB
|
|
let count = 0;
|
|
for (const [checkNumber, { status, paidDate, amount, issueDate, method }] of statusMap) {
|
|
await updateStatus(checkNumber, status, { paidDate, amount, issueDate, method });
|
|
count++;
|
|
}
|
|
|
|
console.log(`\nDone — ${count} payments updated with bank status.`);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|