ledgerflow-backend/lambdas/settings/index.js
Adam Moussa 59127d5ab8 Initial commit — LedgerFlow backend
Lambda-based serverless backend with Google SSO, purchase orders,
invoices, and X12 810 EDI generation for Amazon Payee Central.
Includes bill-to/ship-to address support from Coupa purchase-orders table.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 18:19:38 -04:00

60 lines
2.7 KiB
JavaScript

// lambdas/settings/index.js
// User settings CRUD — stores config (dynamo, edi, company) and invCounter per user
const { GetCommand, PutCommand } = require("@aws-sdk/lib-dynamodb");
const { getDocClient, TABLES, ok, notFound, badRequest, serverError, parseBody, handler } = require("@ledgerflow/shared");
const TABLE = () => TABLES.SETTINGS;
// Default config shape (mirrors frontend defaults)
const DEFAULT_CONFIG = {
dynamo: { region: "us-east-1", table: "", key: "", secret: "", fields: { poNumber: "po_number", vendor: "vendor_name", amount: "total_amount", status: "status", issueDate: "issue_date", dueDate: "due_date" }, connected: false },
edi: { region: "us-east-1", partnershipId: "", key: "", secret: "", senderId: "", receiverId: "", transformerId: "", connected: false },
company: { name: "", taxId: "", terms: "Net 30", currency: "USD", invPrefix: "INV-", taxRate: 0 },
};
async function getSettings(event, user) {
const doc = getDocClient();
const result = await doc.send(new GetCommand({ TableName: TABLE(), Key: { userId: user.userId } }));
if (!result.Item) {
return ok({ config: DEFAULT_CONFIG, invCounter: 1 });
}
return ok({ config: result.Item.config, invCounter: result.Item.invCounter });
}
async function putSettings(event, user) {
const body = parseBody(event);
if (!body.config && body.invCounter === undefined) {
return badRequest("Request body must include config and/or invCounter");
}
const doc = getDocClient();
// Merge with existing settings so partial updates work
const existing = await doc.send(new GetCommand({ TableName: TABLE(), Key: { userId: user.userId } }));
const current = existing.Item || { config: DEFAULT_CONFIG, invCounter: 1 };
const item = {
userId: user.userId,
config: body.config !== undefined ? body.config : current.config,
invCounter: body.invCounter !== undefined ? body.invCounter : current.invCounter,
updatedAt: new Date().toISOString(),
};
await doc.send(new PutCommand({ TableName: TABLE(), Item: item }));
return ok({ config: item.config, invCounter: item.invCounter });
}
// ── Router ────────────────────────────────────────────────────────────────────
exports.handler = handler(async (event, context, user) => {
if (!user) return { statusCode: 401, body: "Unauthorized" };
const method = event.requestContext?.http?.method || event.httpMethod;
switch (method) {
case "GET": return getSettings(event, user);
case "PUT": return putSettings(event, user);
default: return badRequest(`Method ${method} not allowed on /settings`);
}
});