payments-dashboard/src/processPaymentCsv.js
Adam Moussa f1c2063f52 Initial SAM stack: payments CSV to Slack App Home
Two Lambda functions:
- processPaymentCsv: S3 trigger, parses CSV, stores dashboard in DynamoDB
- slackAppHome: API Gateway endpoint for Slack events, publishes Home tab view
2026-04-09 13:29:28 -04:00

74 lines
2.2 KiB
JavaScript

import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";
import { parse } from "csv-parse/sync";
const s3 = new S3Client();
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient());
const TABLE_NAME = process.env.TABLE_NAME;
export const handler = async (event) => {
const record = event.Records[0];
const bucket = record.s3.bucket.name;
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, " "));
// Download CSV from S3
const { Body } = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
const csvText = await Body.transformToString("utf-8");
// Parse CSV
const rows = parse(csvText, {
columns: true,
skip_empty_lines: true,
trim: true,
});
const normalizedRows = rows.map((row) => {
const clean = {};
for (const [k, v] of Object.entries(row)) {
clean[String(k).trim()] = typeof v === "string" ? v.trim() : v;
}
return clean;
});
// Calculate
const parseAmount = (value) => {
const num = parseFloat(String(value || "0").replace(/,/g, "").trim());
return isNaN(num) ? 0 : num;
};
const payments = normalizedRows.map((row) => ({
method: (row["Method"] || "").trim(),
payee: (row["Payee"] || "").trim(),
invoice_numbers: (row["Invoice Numbers"] || "").trim(),
send_payment_on: (row["Send Payment On"] || "").trim(),
amount_usd: parseAmount(row["Amount in USD"]),
status: (row["Status"] || "").trim(),
company_subsidiary: (row["Company/Subsidiary"] || "").trim(),
}));
const grand_total = payments.reduce((sum, p) => sum + p.amount_usd, 0);
const dashboard = {
file_name: key.split("/").pop(),
row_count: payments.length,
grand_count: payments.length,
grand_total: Number(grand_total.toFixed(2)),
payments,
last_updated: new Date().toISOString(),
};
// Save to DynamoDB
await ddb.send(
new PutCommand({
TableName: TABLE_NAME,
Item: {
pk: "current_dashboard",
...dashboard,
},
})
);
console.log(`Processed ${payments.length} payments from ${key}`);
return { statusCode: 200, body: `Processed ${payments.length} payments` };
};