mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-03 09:37:19 +01:00
fix: address dashboard and finance review findings
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
85c8b8f72a
commit
f63b9e3243
12 files changed
+145
-45
No files matched your search
@@ -0,0 +1,29 @@
|
||||
import { Command } from "commander";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { registerCostCommands } from "../commands/client/cost.js";
|
||||
|
||||
function program() {
|
||||
const result = new Command().exitOverride().configureOutput({ writeOut: () => {}, writeErr: () => {} });
|
||||
registerCostCommands(result);
|
||||
return result;
|
||||
}
|
||||
const context = ["--company-id", "test-company", "--api-base", "http://paperclip.test", "--api-key", "test-key", "--json"];
|
||||
afterEach(() => { vi.unstubAllGlobals(); vi.restoreAllMocks(); });
|
||||
describe("cost report date options", () => {
|
||||
it.each([["cost", "summary", "costs/summary"], ["finance", "events", "costs/finance-events"]])("forwards bounds and all-time for %s %s", async (group, name, path) => {
|
||||
const fetch = vi.fn().mockImplementation(() => Promise.resolve(Response.json({})));
|
||||
vi.stubGlobal("fetch", fetch);
|
||||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
await program().parseAsync([group, name, ...context, "--all-time"], { from: "user" });
|
||||
expect(fetch.mock.calls[0][0]).toBe(`http://paperclip.test/api/companies/test-company/${path}?period=all`);
|
||||
await program().parseAsync([group, name, ...context, "--from", "2026-09-01", "--to", "2026-09-02"], { from: "user" });
|
||||
expect(fetch.mock.calls[1][0]).toBe(`http://paperclip.test/api/companies/test-company/${path}?from=2026-09-01&to=2026-09-02`);
|
||||
});
|
||||
it.each([["accounting", "health"], ["accounting", "inspect"], ["accounting", "invoices"], ["budget", "overview"], ["cost", "window-spend"], ["cost", "quota-windows"]])("rejects unsupported date flags for %s %s", async (group, name) => {
|
||||
const fetch = vi.fn(); vi.stubGlobal("fetch", fetch);
|
||||
for (const args of [["--all-time"], ["--from", "2026-09-01"], ["--to", "2026-09-02"]]) {
|
||||
await expect(program().parseAsync([group, name, ...context, ...args], { from: "user" })).rejects.toThrow("unknown option");
|
||||
}
|
||||
expect(fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -36,7 +36,7 @@ export function registerCostCommands(program: Command): void {
|
||||
["window-spend", "costs/window-spend"],
|
||||
["quota-windows", "costs/quota-windows"],
|
||||
] as const) {
|
||||
addCompanyGet(cost, name, `Get ${name} cost data`, path);
|
||||
addCompanyGet(cost, name, `Get ${name} cost data`, path, name !== "window-spend" && name !== "quota-windows");
|
||||
}
|
||||
|
||||
addCommonClientOptions(
|
||||
@@ -80,10 +80,10 @@ export function registerCostCommands(program: Command): void {
|
||||
|
||||
const finance = program.command("finance").description("Finance event and summary operations");
|
||||
addCompanyPostJson(finance, "event:create", "Record a finance event", "finance-events");
|
||||
addCompanyGet(finance, "events", "List finance events", "costs/finance-events");
|
||||
addCompanyGet(finance, "summary", "Get finance summary", "costs/finance-summary");
|
||||
addCompanyGet(finance, "by-biller", "Get finance summary by biller", "costs/finance-by-biller");
|
||||
addCompanyGet(finance, "by-kind", "Get finance summary by kind", "costs/finance-by-kind");
|
||||
addCompanyGet(finance, "events", "List finance events", "costs/finance-events", true);
|
||||
addCompanyGet(finance, "summary", "Get finance summary", "costs/finance-summary", true);
|
||||
addCompanyGet(finance, "by-biller", "Get finance summary by biller", "costs/finance-by-biller", true);
|
||||
addCompanyGet(finance, "by-kind", "Get finance summary by kind", "costs/finance-by-kind", true);
|
||||
|
||||
const budget = program.command("budget").description("Budget policy and incident operations");
|
||||
addCompanyGet(budget, "overview", "Get budget overview", "budgets/overview");
|
||||
@@ -147,16 +147,16 @@ export function registerCostCommands(program: Command): void {
|
||||
);
|
||||
}
|
||||
|
||||
function addCompanyGet(parent: Command, name: string, description: string, path: string): void {
|
||||
addCommonClientOptions(
|
||||
parent
|
||||
.command(name)
|
||||
.description(description)
|
||||
.option("-C, --company-id <id>", "Company ID")
|
||||
function addCompanyGet(parent: Command, name: string, description: string, path: string, dateRange = false): void {
|
||||
const command = parent.command(name).description(description).option("-C, --company-id <id>", "Company ID");
|
||||
if (dateRange) {
|
||||
command
|
||||
.option("--from <iso-date>", "Inclusive start date (default: current UTC month)")
|
||||
.option("--to <iso-date>", "Inclusive end date")
|
||||
.option("--all-time", "Report all recorded history")
|
||||
.action(async (opts: CompanyOptions) => {
|
||||
.option("--all-time", "Report all recorded history");
|
||||
}
|
||||
addCommonClientOptions(
|
||||
command.action(async (opts: CompanyOptions) => {
|
||||
try {
|
||||
const ctx = resolveCommandContext(opts, { requireCompany: true });
|
||||
const params = new URLSearchParams();
|
||||
|
||||
Reference in new issue
Block a user