diff --git a/CLAUDE.md b/CLAUDE.md index 836cde6..c486be2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,6 +11,7 @@ src/ config.ts - Credential storage (~/.config/lucas/) api-client.ts - HTTP client with Bearer auth output.ts - JSON output helpers (success/error) + body-builder.ts - Request body builder with --no-flag unset support commands/ auth/ login.ts - Device authorization flow @@ -29,6 +30,7 @@ src/ transfers/ list.ts - List all transfers create.ts - Create transfer (from, to, amount, exchange-rate) + update.ts - Update transfer by ID delete.ts - Delete transfer by ID subscriptions/ list.ts - List all subscriptions @@ -39,6 +41,7 @@ src/ loans/ list.ts - List all loans create.ts - Create loan (name, principal, account) + update.ts - Update loan by ID pay.ts - Make a loan payment delete.ts - Delete loan by ID stats/ @@ -143,6 +146,7 @@ Login flow writes human-readable output to stderr (not JSON) since it is interac | DELETE | /api/transactions/:id | transactions delete | | GET | /api/transfers | transfers list | | POST | /api/transfers | transfers create | +| PUT | /api/transfers/:id | transfers update | | DELETE | /api/transfers/:id | transfers delete | | GET | /api/subscriptions | subscriptions list | | POST | /api/subscriptions | subscriptions create | @@ -152,6 +156,7 @@ Login flow writes human-readable output to stderr (not JSON) since it is interac | GET | /api/loans | loans list | | POST | /api/loans | loans create | | POST | /api/loans/:id/pay | loans pay | +| PUT | /api/loans/:id | loans update | | DELETE | /api/loans/:id | loans delete | | GET | /api/stats/summary | stats summary | | GET | /api/stats/monthly | stats monthly | diff --git a/README.md b/README.md index d2a6c28..c8d7066 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,11 @@ lucas accounts create \ --bank "BCP" \ --currency PEN \ --balance 1000 # Create an account -lucas accounts update --name "New Name" # Update account +lucas accounts update \ + --name "New Name" \ + --balance 5000 \ + --credit-limit 10000 \ + --is-archived true # Update account lucas accounts delete # Delete account ``` @@ -117,6 +121,12 @@ lucas transfers create \ --description "Monthly savings" \ --exchange-rate 3.72 # Create a transfer +lucas transfers update \ + --amount 1500 \ + --exchange-rate 3.75 # Update a transfer +lucas transfers update \ + --amount 1000 \ + --no-notes # Update with unset lucas transfers delete # Delete transfer ``` @@ -128,12 +138,15 @@ lucas subscriptions create \ --name "Netflix" \ --amount 44.90 \ --account-id \ + --currency PEN \ --frequency MONTHLY \ + --billing-day 1 \ --next-billing-date 2026-04-01 # Create a subscription lucas subscriptions update \ --amount 49.90 \ - --frequency YEARLY # Update subscription + --frequency YEARLY \ + --no-account-id # Update subscription (unset account) lucas subscriptions mark-paid # Mark as paid lucas subscriptions delete # Delete subscription @@ -149,10 +162,21 @@ lucas loans create \ --name "Car Loan" \ --principal 25000 \ --account-id \ + --currency PEN \ --interest-rate 12.5 \ --installments 36 \ + --first-due-date 2026-02-15 \ + --interval-unit MONTH \ + --interval-count 1 \ --start-date 2026-01-15 # Create a loan +lucas loans update \ + --name "Auto Loan" \ + --interest-rate 5.5 # Update a loan +lucas loans update \ + --no-agreed-installments \ + --no-target-payment # Update with unset + lucas loans pay --amount 750 # Make a payment lucas loans delete # Delete loan ``` @@ -180,6 +204,18 @@ lucas exchange-rate convert \ --amount 100 # Convert currencies ``` +### Unsetting Optional Fields + +Use `--no-` to clear an optional field: + +```bash +lucas subscriptions update --no-account-id # Remove linked account +lucas transactions update --no-category-id # Clear category +lucas accounts update --no-credit-limit # Remove credit limit +lucas transfers update --no-notes # Clear notes +lucas loans update --no-agreed-installments # Remove agreed installments +``` + ## AI Integration LucasApp CLI outputs structured JSON exclusively, making it a natural fit for AI agents with terminal access. diff --git a/package.json b/package.json index f0b67f1..6b17ed4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lucasapp-cli", - "version": "0.1.0", + "version": "0.2.0", "description": "LucasApp CLI - Financial data management for AI agents", "author": "StevenACZ", "license": "MIT", diff --git a/src/commands/accounts/update.ts b/src/commands/accounts/update.ts index bcf1837..ea6ab70 100644 --- a/src/commands/accounts/update.ts +++ b/src/commands/accounts/update.ts @@ -1,6 +1,7 @@ import { Command } from "commander"; import { apiRequest } from "../../lib/api-client.js"; import { output } from "../../lib/output.js"; +import { buildBody } from "../../lib/body-builder.js"; export const updateAccountCommand = new Command("update") .description("Update an account") @@ -9,13 +10,33 @@ export const updateAccountCommand = new Command("update") .option("--bank ", "Bank name") .option("--color ", "Account color") .option("--icon ", "Account icon") + .option("--balance ", "Account balance") + .option("--credit-limit ", "Credit limit") + .option("--current-debt ", "Current debt") + .option("--statement-closing-day ", "Statement closing day") + .option("--display-order ", "Display order") + .option("--excluded", "Exclude from totals") + .option("--no-excluded", "Include in totals") + .option("--is-archived", "Archive account") + .option("--no-is-archived", "Unarchive account") .action(async (id: string, opts) => { - const body: Record = {}; - if (opts.name) body.name = opts.name; - if (opts.bank) body.bank = opts.bank; - if (opts.color) body.color = opts.color; - if (opts.icon) body.icon = opts.icon; - + const body = buildBody(opts, [ + { opt: "name", body: "name" }, + { opt: "bank", body: "bank" }, + { opt: "color", body: "color" }, + { opt: "icon", body: "icon" }, + { opt: "balance", body: "balance", type: "number" }, + { opt: "creditLimit", body: "creditLimit", type: "number" }, + { opt: "currentDebt", body: "currentDebt", type: "number" }, + { + opt: "statementClosingDay", + body: "statementClosingDay", + type: "number", + }, + { opt: "displayOrder", body: "displayOrder", type: "number" }, + { opt: "excluded", body: "excluded", type: "boolean" }, + { opt: "isArchived", body: "isArchived", type: "boolean" }, + ]); const data = await apiRequest("PUT", `/api/accounts/${id}`, body); output.success(data); }); diff --git a/src/commands/loans/create.ts b/src/commands/loans/create.ts index 3d9e50a..2935858 100644 --- a/src/commands/loans/create.ts +++ b/src/commands/loans/create.ts @@ -1,25 +1,46 @@ import { Command } from "commander"; import { apiRequest } from "../../lib/api-client.js"; import { output } from "../../lib/output.js"; +import { buildBody } from "../../lib/body-builder.js"; export const createLoanCommand = new Command("create") .description("Create a new loan") .requiredOption("--name ", "Loan name") .requiredOption("--principal ", "Principal amount") - .requiredOption("--account-id ", "Account ID") + .requiredOption("--currency ", "Currency code") + .requiredOption("--first-due-date ", "First due date (YYYY-MM-DD)") + .requiredOption( + "--interval-unit ", + "Interval unit (DAY|WEEK|MONTH|YEAR)", + ) + .requiredOption("--interval-count ", "Interval count") + .option("--account-id ", "Payment account ID") + .option("--agreed-installments ", "Total installments") + .option("--target-payment ", "Target payment amount") .option("--interest-rate ", "Interest rate") - .option("--installments ", "Number of installments") - .option("--start-date ", "Start date (YYYY-MM-DD)") + .option("--interest-rate-unit ", "Rate unit (ANNUAL|MONTHLY)") + .option("--interest-enabled", "Enable interest") + .option("--late-fee-amount ", "Late fee amount") + .option("--late-fee-grace-days ", "Late fee grace days") + .option("--late-fee-enabled", "Enable late fees") .action(async (opts) => { - const body: Record = { - name: opts.name, - principal: Number(opts.principal), - accountId: opts.accountId, - }; - if (opts.interestRate) body.interestRate = Number(opts.interestRate); - if (opts.installments) body.installments = Number(opts.installments); - if (opts.startDate) body.startDate = opts.startDate; - + const body = buildBody(opts, [ + { opt: "name", body: "name" }, + { opt: "principal", body: "principal", type: "number" }, + { opt: "currency", body: "currency" }, + { opt: "firstDueDate", body: "firstDueDate" }, + { opt: "intervalUnit", body: "intervalUnit" }, + { opt: "intervalCount", body: "intervalCount", type: "number" }, + { opt: "accountId", body: "paymentAccountId" }, + { opt: "agreedInstallments", body: "agreedInstallments", type: "number" }, + { opt: "targetPayment", body: "targetPayment", type: "number" }, + { opt: "interestRate", body: "interestRate", type: "number" }, + { opt: "interestRateUnit", body: "interestRateUnit" }, + { opt: "interestEnabled", body: "interestEnabled", type: "boolean" }, + { opt: "lateFeeAmount", body: "lateFeeAmount", type: "number" }, + { opt: "lateFeeGraceDays", body: "lateFeeGraceDays", type: "number" }, + { opt: "lateFeeEnabled", body: "lateFeeEnabled", type: "boolean" }, + ]); const data = await apiRequest("POST", "/api/loans", body); output.success(data); }); diff --git a/src/commands/loans/update.ts b/src/commands/loans/update.ts new file mode 100644 index 0000000..a440338 --- /dev/null +++ b/src/commands/loans/update.ts @@ -0,0 +1,48 @@ +import { Command } from "commander"; +import { apiRequest } from "../../lib/api-client.js"; +import { output } from "../../lib/output.js"; +import { buildBody } from "../../lib/body-builder.js"; + +export const updateLoanCommand = new Command("update") + .description("Update a loan") + .argument("", "Loan ID") + .option("--name ", "Loan name") + .option("--account-id ", "Payment account ID") + .option("--is-primary", "Set as primary") + .option("--no-is-primary", "Unset primary") + .option("--is-archived", "Archive loan") + .option("--no-is-archived", "Unarchive loan") + .option("--first-due-date ", "First due date (YYYY-MM-DD)") + .option("--interval-unit ", "Interval unit (DAY|WEEK|MONTH|YEAR)") + .option("--interval-count ", "Interval count") + .option("--agreed-installments ", "Total installments") + .option("--target-payment ", "Target payment amount") + .option("--interest-rate ", "Interest rate") + .option("--interest-rate-unit ", "Rate unit (ANNUAL|MONTHLY)") + .option("--interest-enabled", "Enable interest") + .option("--no-interest-enabled", "Disable interest") + .option("--late-fee-amount ", "Late fee amount") + .option("--late-fee-grace-days ", "Late fee grace days") + .option("--late-fee-enabled", "Enable late fees") + .option("--no-late-fee-enabled", "Disable late fees") + .action(async (id, opts) => { + const body = buildBody(opts, [ + { opt: "name", body: "name" }, + { opt: "accountId", body: "paymentAccountId" }, + { opt: "isPrimary", body: "isPrimary", type: "boolean" }, + { opt: "isArchived", body: "isArchived", type: "boolean" }, + { opt: "firstDueDate", body: "firstDueDate" }, + { opt: "intervalUnit", body: "intervalUnit" }, + { opt: "intervalCount", body: "intervalCount", type: "number" }, + { opt: "agreedInstallments", body: "agreedInstallments", type: "number" }, + { opt: "targetPayment", body: "targetPayment", type: "number" }, + { opt: "interestRate", body: "interestRate", type: "number" }, + { opt: "interestRateUnit", body: "interestRateUnit" }, + { opt: "interestEnabled", body: "interestEnabled", type: "boolean" }, + { opt: "lateFeeAmount", body: "lateFeeAmount", type: "number" }, + { opt: "lateFeeGraceDays", body: "lateFeeGraceDays", type: "number" }, + { opt: "lateFeeEnabled", body: "lateFeeEnabled", type: "boolean" }, + ]); + const data = await apiRequest("PUT", `/api/loans/${id}`, body); + output.success(data); + }); diff --git a/src/commands/stats/by-category.ts b/src/commands/stats/by-category.ts index 6b963f4..3cb0a82 100644 --- a/src/commands/stats/by-category.ts +++ b/src/commands/stats/by-category.ts @@ -4,7 +4,15 @@ import { output } from "../../lib/output.js"; export const byCategoryCommand = new Command("by-category") .description("Get statistics by category") - .action(async () => { - const data = await apiRequest("GET", "/api/stats/by-category"); + .option("--currency ", "Currency code") + .action(async (opts) => { + const params: Record = {}; + if (opts.currency) params.currency = opts.currency; + const data = await apiRequest( + "GET", + "/api/stats/by-category", + undefined, + params, + ); output.success(data); }); diff --git a/src/commands/stats/monthly.ts b/src/commands/stats/monthly.ts index 3111d09..da49758 100644 --- a/src/commands/stats/monthly.ts +++ b/src/commands/stats/monthly.ts @@ -4,7 +4,17 @@ import { output } from "../../lib/output.js"; export const monthlyCommand = new Command("monthly") .description("Get monthly statistics") - .action(async () => { - const data = await apiRequest("GET", "/api/stats/monthly"); + .option("--currency ", "Currency code") + .option("--months ", "Number of months") + .action(async (opts) => { + const params: Record = {}; + if (opts.currency) params.currency = opts.currency; + if (opts.months) params.months = opts.months; + const data = await apiRequest( + "GET", + "/api/stats/monthly", + undefined, + params, + ); output.success(data); }); diff --git a/src/commands/stats/summary.ts b/src/commands/stats/summary.ts index 92a4a39..c258de5 100644 --- a/src/commands/stats/summary.ts +++ b/src/commands/stats/summary.ts @@ -4,7 +4,15 @@ import { output } from "../../lib/output.js"; export const summaryCommand = new Command("summary") .description("Get financial summary") - .action(async () => { - const data = await apiRequest("GET", "/api/stats/summary"); + .option("--currency ", "Currency code") + .action(async (opts) => { + const params: Record = {}; + if (opts.currency) params.currency = opts.currency; + const data = await apiRequest( + "GET", + "/api/stats/summary", + undefined, + params, + ); output.success(data); }); diff --git a/src/commands/subscriptions/create.ts b/src/commands/subscriptions/create.ts index 606c687..ecf0a2c 100644 --- a/src/commands/subscriptions/create.ts +++ b/src/commands/subscriptions/create.ts @@ -1,25 +1,41 @@ import { Command } from "commander"; import { apiRequest } from "../../lib/api-client.js"; import { output } from "../../lib/output.js"; +import { buildBody } from "../../lib/body-builder.js"; export const createSubscriptionCommand = new Command("create") .description("Create a new subscription") .requiredOption("--name ", "Subscription name") .requiredOption("--amount ", "Subscription amount") - .requiredOption("--account-id ", "Account ID") - .requiredOption("--frequency ", "Frequency (MONTHLY|YEARLY|WEEKLY)") - .option("--next-billing-date ", "Next billing date") + .requiredOption("--frequency ", "Frequency (MONTHLY|YEARLY)") + .requiredOption("--billing-day ", "Billing day of the month") + .option("--account-id ", "Account ID") + .option("--currency ", "Currency code") + .option("--billing-month ", "Billing month (for YEARLY)") + .option("--icon ", "Icon") + .option("--color ", "Color") + .option("--category-id ", "Category ID") + .option("--type ", "Type") + .option("--auto-record", "Enable auto-record") + .option("--start-date ", "Start date (YYYY-MM-DD)") .option("--description ", "Description") .action(async (opts) => { - const body: Record = { - name: opts.name, - amount: Number(opts.amount), - accountId: opts.accountId, - frequency: opts.frequency, - }; - if (opts.nextBillingDate) body.nextBillingDate = opts.nextBillingDate; - if (opts.description) body.description = opts.description; - + const body = buildBody(opts, [ + { opt: "name", body: "name" }, + { opt: "amount", body: "amount", type: "number" }, + { opt: "frequency", body: "frequency" }, + { opt: "billingDay", body: "billingDay", type: "number" }, + { opt: "accountId", body: "accountId" }, + { opt: "currency", body: "currency" }, + { opt: "billingMonth", body: "billingMonth", type: "number" }, + { opt: "icon", body: "icon" }, + { opt: "color", body: "color" }, + { opt: "categoryId", body: "categoryId" }, + { opt: "type", body: "type" }, + { opt: "autoRecord", body: "autoRecord", type: "boolean" }, + { opt: "startDate", body: "startDate" }, + { opt: "description", body: "description" }, + ]); const data = await apiRequest("POST", "/api/subscriptions", body); output.success(data); }); diff --git a/src/commands/subscriptions/update.ts b/src/commands/subscriptions/update.ts index 840fc4b..d71090a 100644 --- a/src/commands/subscriptions/update.ts +++ b/src/commands/subscriptions/update.ts @@ -1,23 +1,46 @@ import { Command } from "commander"; import { apiRequest } from "../../lib/api-client.js"; import { output } from "../../lib/output.js"; +import { buildBody } from "../../lib/body-builder.js"; export const updateSubscriptionCommand = new Command("update") .description("Update a subscription") .argument("", "Subscription ID") .option("--name ", "Subscription name") .option("--amount ", "Amount") - .option("--frequency ", "Frequency (MONTHLY|YEARLY|WEEKLY)") - .option("--next-billing-date ", "Next billing date") + .option("--frequency ", "Frequency (MONTHLY|YEARLY)") .option("--description ", "Description") + .option("--currency ", "Currency code") + .option("--account-id ", "Account ID") + .option("--billing-day ", "Billing day of the month") + .option("--billing-month ", "Billing month (for YEARLY)") + .option("--icon ", "Icon") + .option("--color ", "Color") + .option("--category-id ", "Category ID") + .option("--type ", "Type") + .option("--start-date ", "Start date (YYYY-MM-DD)") + .option("--auto-record", "Enable auto-record") + .option("--no-auto-record", "Disable auto-record") + .option("--is-active", "Activate subscription") + .option("--no-is-active", "Deactivate subscription") .action(async (id: string, opts) => { - const body: Record = {}; - if (opts.name) body.name = opts.name; - if (opts.amount) body.amount = Number(opts.amount); - if (opts.frequency) body.frequency = opts.frequency; - if (opts.nextBillingDate) body.nextBillingDate = opts.nextBillingDate; - if (opts.description) body.description = opts.description; - + const body = buildBody(opts, [ + { opt: "name", body: "name" }, + { opt: "amount", body: "amount", type: "number" }, + { opt: "frequency", body: "frequency" }, + { opt: "description", body: "description" }, + { opt: "currency", body: "currency" }, + { opt: "accountId", body: "accountId" }, + { opt: "billingDay", body: "billingDay", type: "number" }, + { opt: "billingMonth", body: "billingMonth", type: "number" }, + { opt: "icon", body: "icon" }, + { opt: "color", body: "color" }, + { opt: "categoryId", body: "categoryId" }, + { opt: "type", body: "type" }, + { opt: "startDate", body: "startDate" }, + { opt: "autoRecord", body: "autoRecord", type: "boolean" }, + { opt: "isActive", body: "isActive", type: "boolean" }, + ]); const data = await apiRequest("PUT", `/api/subscriptions/${id}`, body); output.success(data); }); diff --git a/src/commands/transactions/update.ts b/src/commands/transactions/update.ts index 0304d4e..c4196e6 100644 --- a/src/commands/transactions/update.ts +++ b/src/commands/transactions/update.ts @@ -1,23 +1,28 @@ import { Command } from "commander"; import { apiRequest } from "../../lib/api-client.js"; import { output } from "../../lib/output.js"; +import { buildBody } from "../../lib/body-builder.js"; export const updateTransactionCommand = new Command("update") .description("Update a transaction") .argument("", "Transaction ID") .option("--description ", "Transaction description") .option("--amount ", "Transaction amount") + .option("--type ", "Type (INCOME|EXPENSE)") + .option("--date ", "Date (YYYY-MM-DD)") .option("--category-id ", "Category ID") .option("--notes ", "Additional notes") .option("--merchant ", "Merchant name") .action(async (id: string, opts) => { - const body: Record = {}; - if (opts.description) body.description = opts.description; - if (opts.amount) body.amount = Number(opts.amount); - if (opts.categoryId) body.categoryId = opts.categoryId; - if (opts.notes) body.notes = opts.notes; - if (opts.merchant) body.merchant = opts.merchant; - + const body = buildBody(opts, [ + { opt: "description", body: "description" }, + { opt: "amount", body: "amount", type: "number" }, + { opt: "type", body: "type" }, + { opt: "date", body: "date" }, + { opt: "categoryId", body: "categoryId" }, + { opt: "notes", body: "notes" }, + { opt: "merchant", body: "merchant" }, + ]); const data = await apiRequest("PUT", `/api/transactions/${id}`, body); output.success(data); }); diff --git a/src/commands/transfers/create.ts b/src/commands/transfers/create.ts index 9466e96..b89a94f 100644 --- a/src/commands/transfers/create.ts +++ b/src/commands/transfers/create.ts @@ -1,23 +1,29 @@ import { Command } from "commander"; import { apiRequest } from "../../lib/api-client.js"; import { output } from "../../lib/output.js"; +import { buildBody } from "../../lib/body-builder.js"; export const createTransferCommand = new Command("create") .description("Create a new transfer") .requiredOption("--from-account-id ", "Source account ID") .requiredOption("--to-account-id ", "Destination account ID") .requiredOption("--amount ", "Transfer amount") + .option("--to-amount ", "Destination amount (cross-currency)") .option("--description ", "Transfer description") + .option("--date ", "Date (YYYY-MM-DD)") + .option("--notes ", "Notes") .option("--exchange-rate ", "Exchange rate") .action(async (opts) => { - const body: Record = { - fromAccountId: opts.fromAccountId, - toAccountId: opts.toAccountId, - amount: Number(opts.amount), - }; - if (opts.description) body.description = opts.description; - if (opts.exchangeRate) body.exchangeRate = Number(opts.exchangeRate); - + const body = buildBody(opts, [ + { opt: "fromAccountId", body: "fromAccountId" }, + { opt: "toAccountId", body: "toAccountId" }, + { opt: "amount", body: "amount", type: "number" }, + { opt: "toAmount", body: "toAmount", type: "number" }, + { opt: "description", body: "description" }, + { opt: "date", body: "date" }, + { opt: "notes", body: "notes" }, + { opt: "exchangeRate", body: "exchangeRate", type: "number" }, + ]); const data = await apiRequest("POST", "/api/transfers", body); output.success(data); }); diff --git a/src/commands/transfers/update.ts b/src/commands/transfers/update.ts new file mode 100644 index 0000000..1a6bf25 --- /dev/null +++ b/src/commands/transfers/update.ts @@ -0,0 +1,26 @@ +import { Command } from "commander"; +import { apiRequest } from "../../lib/api-client.js"; +import { output } from "../../lib/output.js"; +import { buildBody } from "../../lib/body-builder.js"; + +export const updateTransferCommand = new Command("update") + .description("Update a transfer") + .argument("", "Transfer ID") + .requiredOption("--amount ", "Transfer amount") + .option("--to-amount ", "Destination amount (cross-currency)") + .option("--description ", "Description") + .option("--date ", "Date (YYYY-MM-DD)") + .option("--notes ", "Notes") + .option("--exchange-rate ", "Exchange rate") + .action(async (id, opts) => { + const body = buildBody(opts, [ + { opt: "amount", body: "amount", type: "number" }, + { opt: "toAmount", body: "toAmount", type: "number" }, + { opt: "description", body: "description" }, + { opt: "date", body: "date" }, + { opt: "notes", body: "notes" }, + { opt: "exchangeRate", body: "exchangeRate", type: "number" }, + ]); + const data = await apiRequest("PUT", `/api/transfers/${id}`, body); + output.success(data); + }); diff --git a/src/index.ts b/src/index.ts index e18abfd..e8d21e5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,6 +21,7 @@ import { deleteTransactionCommand } from "./commands/transactions/delete.js"; // Transfers import { listTransfersCommand } from "./commands/transfers/list.js"; import { createTransferCommand } from "./commands/transfers/create.js"; +import { updateTransferCommand } from "./commands/transfers/update.js"; import { deleteTransferCommand } from "./commands/transfers/delete.js"; // Subscriptions @@ -33,6 +34,7 @@ import { markPaidCommand } from "./commands/subscriptions/mark-paid.js"; // Loans import { listLoansCommand } from "./commands/loans/list.js"; import { createLoanCommand } from "./commands/loans/create.js"; +import { updateLoanCommand } from "./commands/loans/update.js"; import { payLoanCommand } from "./commands/loans/pay.js"; import { deleteLoanCommand } from "./commands/loans/delete.js"; @@ -82,6 +84,7 @@ transactions.addCommand(deleteTransactionCommand); const transfers = program.command("transfers").description("Manage transfers"); transfers.addCommand(listTransfersCommand); transfers.addCommand(createTransferCommand); +transfers.addCommand(updateTransferCommand); transfers.addCommand(deleteTransferCommand); // Grupo: subscriptions @@ -98,6 +101,7 @@ subscriptions.addCommand(markPaidCommand); const loans = program.command("loans").description("Manage loans"); loans.addCommand(listLoansCommand); loans.addCommand(createLoanCommand); +loans.addCommand(updateLoanCommand); loans.addCommand(payLoanCommand); loans.addCommand(deleteLoanCommand); diff --git a/src/lib/body-builder.ts b/src/lib/body-builder.ts new file mode 100644 index 0000000..d94f72b --- /dev/null +++ b/src/lib/body-builder.ts @@ -0,0 +1,25 @@ +type FieldType = "number" | "boolean" | "string"; + +interface FieldMapping { + opt: string; + body: string; + type?: FieldType; +} + +export function buildBody( + opts: Record, + fields: FieldMapping[], +): Record { + const body: Record = {}; + for (const { opt, body: key, type } of fields) { + const val = opts[opt]; + if (val === false) { + body[key] = null; + } else if (val !== undefined) { + if (type === "number") body[key] = Number(val); + else if (type === "boolean") body[key] = true; + else body[key] = val; + } + } + return body; +}