feat(plan): per-cell record cap (3) + additive overrides

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-08 10:40:46 +02:00
parent 2cfa28dc47
commit 80dc8a5c69
3 changed files with 153 additions and 109 deletions

View File

@@ -58,6 +58,20 @@ beforeEach(async () => {
await prisma.work_plan_overrides.deleteMany({
where: { note: { contains: N } },
});
// Also clean up empty-note rows on the specific test dates used by the
// "allows an empty note" tests. These rows are not caught by the prefix
// filter above, and would cause the per-cell cap check to reject them
// once three accumulate across runs.
await prisma.work_plan_entries.deleteMany({
where: {
date_from: {
in: [
new Date("2099-07-15T00:00:00.000Z"),
new Date("2097-08-05T00:00:00.000Z"),
],
},
},
});
});
afterAll(async () => {
@@ -341,6 +355,36 @@ describe("plan.service.createEntry", () => {
expect("error" in result).toBe(true);
if ("error" in result) expect(result.status).toBe(403);
});
it("rejects a 4th entry covering a day already at the cap", async () => {
for (let i = 0; i < 3; i++) {
const r = await createEntry(
{
user_id: adminUserId,
date_from: "2099-07-20",
date_to: "2099-07-20",
category: "work",
note: `${N}e${i}`,
},
adminUserId,
false,
);
expect("data" in r).toBe(true);
}
const fourth = await createEntry(
{
user_id: adminUserId,
date_from: "2099-07-20",
date_to: "2099-07-20",
category: "work",
note: `${N}e3`,
},
adminUserId,
false,
);
expect("error" in fourth).toBe(true);
if ("error" in fourth) expect(fourth.status).toBe(400);
});
});
// ---------------------------------------------------------------------------
@@ -439,7 +483,7 @@ describe("plan.service.deleteEntry", () => {
// ---------------------------------------------------------------------------
describe("plan.service.createOverride", () => {
it("creates an override and returns { data, oldData: null, replacedData: null }", async () => {
it("creates an override and returns { data, oldData: null }", async () => {
const result = await createOverride(
{
user_id: adminUserId,
@@ -454,42 +498,45 @@ describe("plan.service.createOverride", () => {
if ("data" in result) {
expect(result.data.note).toBe(`${N}day off`);
expect(result.oldData).toBeNull();
expect(result.replacedData).toBeNull();
}
});
it("soft-deletes the existing override and reports it in replacedData", async () => {
await createOverride(
it("stacks additive overrides and caps at MAX_RECORDS_PER_CELL", async () => {
for (let i = 0; i < 3; i++) {
const r = await createOverride(
{
user_id: adminUserId,
shift_date: "2099-10-02",
category: "leave",
note: `${N}o${i}`,
},
adminUserId,
false,
);
expect("data" in r).toBe(true);
}
// A 4th record on the same day is rejected by the cap.
const fourth = await createOverride(
{
user_id: adminUserId,
shift_date: "2099-10-02",
category: "leave",
note: `${N}first`,
note: `${N}o3`,
},
adminUserId,
false,
);
const second = await createOverride(
{
expect("error" in fourth).toBe(true);
if ("error" in fourth) expect(fourth.status).toBe(400);
// All three earlier overrides remain active — no replace happened.
const active = await prisma.work_plan_overrides.findMany({
where: {
user_id: adminUserId,
shift_date: "2099-10-02",
category: "sick",
note: `${N}second`,
shift_date: new Date("2099-10-02"),
is_deleted: false,
},
adminUserId,
false,
);
expect("data" in second).toBe(true);
if ("data" in second) {
expect((second.replacedData as any)?.note).toBe(`${N}first`);
}
const all = await prisma.work_plan_overrides.findMany({
where: { user_id: adminUserId, shift_date: new Date("2099-10-02") },
});
// The first should be soft-deleted, the second active
expect(all.find((o) => o.note === `${N}first`)?.is_deleted).toBe(true);
expect(all.find((o) => o.note === `${N}second`)?.is_deleted).toBe(false);
expect(active.length).toBe(3);
});
it("rejects a past date without force", async () => {