The manager dashboard "Approved This Month" card only counted POs whose current status is MGR_APPROVED, so approvals that had already moved on to payment, delivery, or closure dropped out of the count. Managers could not see what happened to the POs they approved this month. - Count every PO whose `approvedAt` falls in the current month across all post-approval statuses (MGR_APPROVED → ... → CLOSED). `approvedAt` is set once at approval and persists, so it is the correct anchor. - Introduce a shared `POST_APPROVAL_STATUSES` constant (includes the previously-omitted PARTIALLY_CLOSED). This also fixes Total Approved Spend and the vessel/monthly breakdowns, which were silently dropping partially-received POs. - Make the card a link into /history with an approval-date filter applied (?approvedFrom=<startOfMonth>) so a click shows the full set with each PO's current status, as requested. - Add `approvedFrom`/`approvedTo` filtering to the history page, its filter UI, and the reports export route so the deep-link and exports stay in sync. Scope note: the count remains org-wide, consistent with every other card on the manager dashboard. Adds an integration test covering the moved-on case and the date window. Fixes #32
105 lines
4.1 KiB
TypeScript
105 lines
4.1 KiB
TypeScript
/**
|
|
* Integration test for the manager dashboard "Approved This Month" card.
|
|
*
|
|
* Regression: the card previously counted only POs *currently* in MGR_APPROVED,
|
|
* so POs approved this month that had moved on to payment/delivery/closure were
|
|
* dropped from the count. The card must count every PO approved this month
|
|
* regardless of its current (post-approval) status, and the same approval-date
|
|
* window must be reproducible on the /history page (where the card links to).
|
|
*/
|
|
import { describe, it, expect, beforeAll, afterEach } from "vitest";
|
|
|
|
import { db } from "@/lib/db";
|
|
import { POST_APPROVAL_STATUSES } from "@/lib/utils";
|
|
import { deletePosByTitle } from "./helpers";
|
|
import type { POStatus } from "@prisma/client";
|
|
|
|
const PREFIX = "INTTEST_APPROVED_MONTH_";
|
|
|
|
let submitterId: string;
|
|
let vesselId: string;
|
|
let accountId: string;
|
|
|
|
const now = new Date();
|
|
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
const midThisMonth = new Date(now.getFullYear(), now.getMonth(), 15, 12, 0, 0);
|
|
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 15, 12, 0, 0);
|
|
|
|
beforeAll(async () => {
|
|
// Resolve any existing cost-centre / account / user from the test DB rather
|
|
// than relying on dev-seed fixtures (the test DB is a production mirror).
|
|
const [user, vessel, account] = await Promise.all([
|
|
db.user.findFirstOrThrow({ where: { role: "MANAGER" } }),
|
|
db.vessel.findFirstOrThrow(),
|
|
db.account.findFirstOrThrow(),
|
|
]);
|
|
submitterId = user.id;
|
|
vesselId = vessel.id;
|
|
accountId = account.id;
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await deletePosByTitle(PREFIX);
|
|
});
|
|
|
|
let seq = 0;
|
|
async function makePo(opts: { title: string; status: POStatus; approvedAt: Date | null }) {
|
|
seq += 1;
|
|
return db.purchaseOrder.create({
|
|
data: {
|
|
poNumber: `${PREFIX}${Date.now()}_${seq}`,
|
|
title: opts.title,
|
|
status: opts.status,
|
|
totalAmount: 1000,
|
|
approvedAt: opts.approvedAt,
|
|
submitterId,
|
|
vesselId,
|
|
accountId,
|
|
},
|
|
});
|
|
}
|
|
|
|
/** Mirrors the dashboard "Approved This Month" query. */
|
|
function approvedThisMonthWhere() {
|
|
return {
|
|
title: { startsWith: PREFIX },
|
|
status: { in: [...POST_APPROVAL_STATUSES] },
|
|
approvedAt: { gte: startOfMonth },
|
|
};
|
|
}
|
|
|
|
describe("Approved This Month count", () => {
|
|
it("counts POs approved this month across every post-approval status", async () => {
|
|
await makePo({ title: `${PREFIX}approved`, status: "MGR_APPROVED", approvedAt: midThisMonth });
|
|
await makePo({ title: `${PREFIX}sent`, status: "SENT_FOR_PAYMENT", approvedAt: midThisMonth });
|
|
await makePo({ title: `${PREFIX}partpaid`, status: "PARTIALLY_PAID", approvedAt: midThisMonth });
|
|
await makePo({ title: `${PREFIX}paid`, status: "PAID_DELIVERED", approvedAt: midThisMonth });
|
|
await makePo({ title: `${PREFIX}partclosed`, status: "PARTIALLY_CLOSED", approvedAt: midThisMonth });
|
|
await makePo({ title: `${PREFIX}closed`, status: "CLOSED", approvedAt: midThisMonth });
|
|
|
|
const count = await db.purchaseOrder.count({ where: approvedThisMonthWhere() });
|
|
expect(count).toBe(6);
|
|
});
|
|
|
|
it("excludes POs approved in a previous month and POs never approved", async () => {
|
|
await makePo({ title: `${PREFIX}closed_lastmonth`, status: "CLOSED", approvedAt: lastMonth });
|
|
await makePo({ title: `${PREFIX}awaiting`, status: "MGR_REVIEW", approvedAt: null });
|
|
await makePo({ title: `${PREFIX}closed_thismonth`, status: "CLOSED", approvedAt: midThisMonth });
|
|
|
|
const count = await db.purchaseOrder.count({ where: approvedThisMonthWhere() });
|
|
expect(count).toBe(1);
|
|
});
|
|
|
|
it("would have missed moved-on POs under the old MGR_APPROVED-only filter", async () => {
|
|
// A PO approved this month that has since closed — the case from issue #32.
|
|
await makePo({ title: `${PREFIX}moved_on`, status: "CLOSED", approvedAt: midThisMonth });
|
|
|
|
const oldCount = await db.purchaseOrder.count({
|
|
where: { title: { startsWith: PREFIX }, status: "MGR_APPROVED", approvedAt: { gte: startOfMonth } },
|
|
});
|
|
const newCount = await db.purchaseOrder.count({ where: approvedThisMonthWhere() });
|
|
|
|
expect(oldCount).toBe(0);
|
|
expect(newCount).toBe(1);
|
|
});
|
|
});
|