pelagia-portal/App/tests/integration/po-tcs-discount.test.ts
Hardik c67155f5d9
All checks were successful
PR checks / checks (pull_request) Successful in 54s
PR checks / integration (pull_request) Successful in 32s
test(po): fix intent type in TCS/Discount test (updatePo uses "save")
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 14:52:53 +05:30

115 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Integration test for PO-level TCS & Discount (issue #133).
* Verifies totalAmount folds in the charges (subtotal + GST + TCS Discount),
* the absolute amounts are persisted, edits update them, and a manager line edit
* preserves them.
*/
import { vi, describe, it, expect, beforeAll, afterEach } from "vitest";
vi.mock("@/auth", () => ({ auth: vi.fn() }));
vi.mock("next/cache", () => ({ revalidatePath: vi.fn() }));
vi.mock("@/lib/notifier", () => ({ notify: vi.fn() }));
import { auth } from "@/auth";
import { db } from "@/lib/db";
import type { Role } from "@prisma/client";
import { createPo } from "@/app/(portal)/po/new/actions";
import { updatePo } from "@/app/(portal)/po/[id]/edit/actions";
import { managerEditLineItems } from "@/app/(portal)/approvals/[id]/manager-line-edit-actions";
import { makeSession, getSeedUser, getSeedVessel, getSeedAccount, makePoForm, deletePosByTitle } from "./helpers";
const PREFIX = "INTTEST_TCSDISC_";
let techId: string;
let managerId: string;
let vesselId: string;
let accountId: string;
beforeAll(async () => {
const [tech, manager, vessel, account] = await Promise.all([
getSeedUser("tech@pelagia.local"),
getSeedUser("manager@pelagia.local"),
getSeedVessel("MV Pelagia Star"),
getSeedAccount("700201"),
]);
techId = tech.id;
managerId = manager.id;
vesselId = vessel.id;
accountId = account.id;
});
afterEach(async () => {
await deletePosByTitle(PREFIX);
vi.clearAllMocks();
});
function as(userId: string, role: Role) {
vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(userId, role));
}
// One line item: 10 × ₹100 @ 18% GST ⇒ taxable 1000, GST 180, incl-GST 1180.
function form(title: string, intent: string, tcs: number, discount: number) {
const f = makePoForm({
title, vesselId, accountId, intent: "draft",
lineItems: [{ description: "Item", quantity: 10, unit: "pc", unitPrice: 100, gstRate: 0.18 }],
});
f.set("intent", intent); // create: draft/submit · edit: save
f.set("tcsAmount", String(tcs));
f.set("discountAmount", String(discount));
return f;
}
describe("PO TCS & Discount", () => {
it("folds TCS and Discount into totalAmount and stores the absolute amounts", async () => {
as(techId, "TECHNICAL");
const result = await createPo(form(`${PREFIX}Create`, "draft", 118, 100));
expect(result).not.toHaveProperty("error");
const po = await db.purchaseOrder.findUniqueOrThrow({ where: { id: (result as { id: string }).id } });
expect(Number(po.tcsAmount)).toBeCloseTo(118, 2);
expect(Number(po.discountAmount)).toBeCloseTo(100, 2);
expect(Number(po.totalAmount)).toBeCloseTo(1180 + 118 - 100, 2); // 1198
});
it("defaults to zero charges ⇒ totalAmount is just incl-GST", async () => {
as(techId, "TECHNICAL");
const result = await createPo(form(`${PREFIX}Zero`, "draft", 0, 0));
const po = await db.purchaseOrder.findUniqueOrThrow({ where: { id: (result as { id: string }).id } });
expect(Number(po.totalAmount)).toBeCloseTo(1180, 2);
expect(Number(po.tcsAmount)).toBe(0);
expect(Number(po.discountAmount)).toBe(0);
});
it("edit updates the charges and recomputes the total", async () => {
as(techId, "TECHNICAL");
const created = await createPo(form(`${PREFIX}Edit`, "draft", 0, 0));
const poId = (created as { id: string }).id;
as(techId, "TECHNICAL");
const edited = await updatePo(poId, form(`${PREFIX}Edit`, "save", 50, 30));
expect(edited).not.toHaveProperty("error");
const po = await db.purchaseOrder.findUniqueOrThrow({ where: { id: poId } });
expect(Number(po.tcsAmount)).toBeCloseTo(50, 2);
expect(Number(po.discountAmount)).toBeCloseTo(30, 2);
expect(Number(po.totalAmount)).toBeCloseTo(1180 + 50 - 30, 2); // 1200
});
it("a manager line edit preserves the PO's TCS & Discount", async () => {
as(techId, "TECHNICAL");
const created = await createPo(form(`${PREFIX}MgrEdit`, "submit", 118, 100)); // ⇒ MGR_REVIEW
const poId = (created as { id: string }).id;
as(managerId, "MANAGER");
const res = await managerEditLineItems({
poId,
// Double the quantity: incl-GST becomes 20 × 100 × 1.18 = 2360.
lineItems: [{ name: "Item", quantity: 20, unit: "pc", unitPrice: 100 }],
});
expect(res).not.toHaveProperty("error");
const po = await db.purchaseOrder.findUniqueOrThrow({ where: { id: poId } });
expect(Number(po.tcsAmount)).toBeCloseTo(118, 2);
expect(Number(po.discountAmount)).toBeCloseTo(100, 2);
expect(Number(po.totalAmount)).toBeCloseTo(2360 + 118 - 100, 2); // 2378
});
});