fix(05-06): add Collection and Remittance CASL subjects, update routes

- Add Collection and Remittance to AppSubjects union type
- Grant OFFICE_STAFF manage:Collection and manage:Remittance
- Grant COLLECTOR create/read:Collection and create/read:Remittance
- Update all 8 collection/remittance route handlers from Subscriber to
  their dedicated CASL subjects (Collection or Remittance)
- Update JSDoc comments in route files to reflect new subject names

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
kevin-asprec
2026-03-05 18:28:07 +08:00
parent a0ea504af3
commit a59a246fdc
8 changed files with 27 additions and 16 deletions

View File

@@ -1,12 +1,12 @@
/**
* GET /api/collections/[id] — Get a single collection with allocations
* GET /api/collections/[id] — Get a single collection with allocations (requires read:Collection)
*/
import { NextRequest, NextResponse } from "next/server";
import { withPermission } from "@/lib/middleware/authorize";
import { withTenantContext } from "@/lib/prisma-tenant";
export function GET(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
return withPermission("read", "Subscriber")(
return withPermission("read", "Collection")(
async (_req: NextRequest, { user }) => {
if (!user.tenantId) {
return NextResponse.json(

View File

@@ -1,5 +1,5 @@
/**
* POST /api/collections/[id]/void — Void a collection (reversing JE)
* POST /api/collections/[id]/void — Void a collection (requires update:Collection)
*/
import { NextRequest, NextResponse } from "next/server";
import { withPermission } from "@/lib/middleware/authorize";
@@ -7,7 +7,7 @@ import { withTenantContext } from "@/lib/prisma-tenant";
import { voidCollection } from "@/lib/services/collector-service";
export function POST(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
return withPermission("update", "Subscriber")(
return withPermission("update", "Collection")(
async (_req: NextRequest, { user }) => {
if (!user.tenantId) {
return NextResponse.json(

View File

@@ -1,13 +1,13 @@
/**
* POST /api/collections — Record a new cash collection
* GET /api/collections — Get collection history (filtered by subscriberId or collectorId)
* POST /api/collections — Record a new cash collection (requires create:Collection)
* GET /api/collections — Get collection history (requires read:Collection)
*/
import { NextRequest, NextResponse } from "next/server";
import { withPermission } from "@/lib/middleware/authorize";
import { withTenantContext } from "@/lib/prisma-tenant";
import { recordCollection, getCollectionHistory } from "@/lib/services/collector-service";
export const POST = withPermission("create", "Subscriber")(
export const POST = withPermission("create", "Collection")(
async (req: NextRequest, { user }) => {
if (!user.tenantId) {
return NextResponse.json(
@@ -53,7 +53,7 @@ export const POST = withPermission("create", "Subscriber")(
}
);
export const GET = withPermission("read", "Subscriber")(
export const GET = withPermission("read", "Collection")(
async (req: NextRequest, { user }) => {
if (!user.tenantId) {
return NextResponse.json(

View File

@@ -1,5 +1,5 @@
/**
* POST /api/remittances/[id]/verify — Verify a remittance (office staff counts total)
* POST /api/remittances/[id]/verify — Verify a remittance (requires update:Remittance)
*/
import { NextRequest, NextResponse } from "next/server";
import { withPermission } from "@/lib/middleware/authorize";
@@ -7,7 +7,7 @@ import { withTenantContext } from "@/lib/prisma-tenant";
import { verifyRemittance } from "@/lib/services/remittance-service";
export function POST(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
return withPermission("update", "Subscriber")(
return withPermission("update", "Remittance")(
async (innerReq: NextRequest, { user }) => {
if (!user.tenantId) {
return NextResponse.json(

View File

@@ -1,6 +1,6 @@
/**
* POST /api/remittances — Create a new remittance (collector declares total)
* GET /api/remittances — List remittances with optional filtering
* POST /api/remittances — Create a new remittance (requires create:Remittance)
* GET /api/remittances — List remittances with optional filtering (requires read:Remittance)
*/
import { NextRequest, NextResponse } from "next/server";
import { withPermission } from "@/lib/middleware/authorize";
@@ -8,7 +8,7 @@ import { withTenantContext } from "@/lib/prisma-tenant";
import { createRemittance, listRemittances } from "@/lib/services/remittance-service";
import { RemittanceStatus } from "@prisma/client";
export const POST = withPermission("create", "Subscriber")(
export const POST = withPermission("create", "Remittance")(
async (req: NextRequest, { user }) => {
if (!user.tenantId) {
return NextResponse.json(
@@ -50,7 +50,7 @@ export const POST = withPermission("create", "Subscriber")(
}
);
export const GET = withPermission("read", "Subscriber")(
export const GET = withPermission("read", "Remittance")(
async (req: NextRequest, { user }) => {
if (!user.tenantId) {
return NextResponse.json(

View File

@@ -1,5 +1,5 @@
/**
* GET /api/reports/collections — Daily collection summary report
* GET /api/reports/collections — Daily collection summary report (requires read:Collection)
*
* Query params:
* date — ISO date string (defaults to today)
@@ -10,7 +10,7 @@ import { withPermission } from "@/lib/middleware/authorize";
import { withTenantContext } from "@/lib/prisma-tenant";
import { getDailyCollectionSummary, getCollectorCollectionDetail } from "@/lib/services/collection-report-service";
export const GET = withPermission("read", "Subscriber")(
export const GET = withPermission("read", "Collection")(
async (req: NextRequest, { user }) => {
if (!user.tenantId) {
return NextResponse.json(

View File

@@ -66,6 +66,9 @@ export function definePermissionsFor(
can("manage", "Expense");
// Vendor management (CRUD)
can("manage", "Vendor");
// Collection and remittance management
can("manage", "Collection");
can("manage", "Remittance");
// Job type rates (read-only for office staff — admin configures rates)
can("read", "JobTypeRate");
// View financial reports (read-only)
@@ -88,6 +91,12 @@ export function definePermissionsFor(
can("create", "Payment");
// View payment history
can("read", "Payment");
// Can create and view collections
can("create", "Collection");
can("read", "Collection");
// Can create and view remittances
can("create", "Remittance");
can("read", "Remittance");
// NOTE: No explicit cannot() needed — Collector simply has no rules for
// Invoice, User management, or Reports. Absence of a rule = no access.
break;

View File

@@ -13,6 +13,8 @@ export type AppSubjects =
| "Subscriber"
| "Invoice"
| "Payment"
| "Collection"
| "Remittance"
| "Zone"
| "Ticket"
| "JobOrder"