All files / lib permissions.ts

100% Statements 47/47
100% Branches 24/24
100% Functions 10/10
100% Lines 47/47

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113                                                      1x 4x 4x 4x 4x 2x 4x               1x 6x 6x 6x 6x 6x 3x 3x   1x 2x 1x 1x                         1x 6x 6x 6x 6x 6x 3x 3x               1x 2x 2x 2x 2x   1x 2x 2x         1x 2x 2x     1x 2x 2x     1x 2x 2x     1x 2x 2x  
import type { Role } from '@prisma/client';
 
// =============================================================================
// Permission rules — locked by client 2026-05-20 (consolidated single-system)
// =============================================================================
//
// One system, two roles (MASTER / STAFF).  No subdomain split.
//
// USER MANAGEMENT RULES (final, 2026-05-20):
//   - CREATE: STAFF can create only STAFF users.  MASTER can create either role.
//   - EDIT/DELETE/RESET:  STAFF can act only on themselves.
//                         MASTER can act on themselves AND on STAFF users.
//                         No peer editing (Master cannot touch another Master,
//                         Staff cannot touch another Staff).
//                         No upward editing (Staff cannot touch Master).
//
// USER SECTION restrictions (unchanged):
//   - "รับสินค้า & log" page: MASTER only.
//   - Inventory: STAFF cannot use the import flow.
//   - Transfer:  STAFF cannot use bulk-accept ("กดรับเฟสทั้งหมดที่แจ้ง").
//   - Transfer initiation: MASTER → COMPLETED immediately,
//                          STAFF → PENDING, awaits MASTER approval.
//
// =============================================================================
 
// ----- USER MANAGEMENT -----
 
export function canCreateUserWithRole(
  actorRole: Role,
  targetRole: Role,
): boolean {
  if (actorRole === 'MASTER') return true;
  return actorRole === 'STAFF' && targetRole === 'STAFF';
}
 
/**
 * Can the actor edit / delete / change-role of the target?
 *
 * Strict rules: self always allowed; downward only for MASTER → STAFF;
 * no peer (same role, different user); no upward (STAFF → MASTER).
 */
export function canManageUser(
  actor: { id: string; role: Role },
  target: { id: string; role: Role },
): boolean {
  if (actor.id === target.id) return true;
  if (actor.role === 'MASTER' && target.role === 'STAFF') return true;
  return false;
}
 
export function assignableRoles(actorRole: Role): Role[] {
  if (actorRole === 'MASTER') return ['MASTER', 'STAFF'];
  return ['STAFF'];
}
 
// ----- PASSWORD RESET -----
//
// Rules (client 2026-05-20):
//   MASTER can: reset self,  reset any STAFF.  Cannot reset another MASTER.
//   STAFF  can: reset self only.
//
// "Reset self" = change-password flow (requires current password).
// "Reset other (Master → Staff)" = force-reset flow (no current password).
// If a MASTER forgets their own password, recovery is a DBA task (no peer-reset
// path) — this is acceptable for a 10-20-user internal system.
 
export function canResetPassword(
  actor: { id: string; role: Role },
  target: { id: string; role: Role },
): boolean {
  if (actor.id === target.id) return true;            // self-reset
  if (actor.role === 'MASTER' && target.role === 'STAFF') return true; // master → staff
  return false;                                       // everything else blocked
}
 
// ----- TRANSFER WORKFLOW -----
//
// MASTER initiating a transfer: created directly as COMPLETED.
// STAFF initiating a transfer:  created as PENDING — must be approved by a MASTER.
// Only MASTER can approve / reject a pending transfer.
 
export function transferInitialStatus(
  actorRole: Role,
): 'PENDING' | 'COMPLETED' {
  return actorRole === 'MASTER' ? 'COMPLETED' : 'PENDING';
}
 
export function canApproveTransfer(role: Role): boolean {
  return role === 'MASTER';
}
 
// ----- USER SECTION -----
 
/** Can the user see the "รับสินค้า & log" menu / open /reception? */
export function canSeeReception(role: Role): boolean {
  return role === 'MASTER';
}
 
/** Inventory page: can the user use the import-product flow? */
export function canImportInventory(role: Role): boolean {
  return role === 'MASTER';
}
 
/** Transfer page: can the user press "กดรับเฟสทั้งหมดที่แจ้ง" (bulk-accept)? */
export function canBulkAcceptTransfer(role: Role): boolean {
  return role === 'MASTER';
}
 
/** Settings (reference data CRUD): MASTER only. */
export function canManageSettings(role: Role): boolean {
  return role === 'MASTER';
}