feat(payments): relax batch_ref regex to allow spaces, slashes, dots, colons, etc.
The previous rule ^[A-Za-z0-9_-]{1,50}$ rejected common bank/POP
references that include punctuation [e.g. 'Bank ref 26/09/18',
'INV/2026.09:18 #3', 'POP 12345']. New rule permits letters, digits,
space, underscore, hyphen, slash, dot, comma, colon, parens,
ampersand, hash, apostrophe, plus; length 1-100. Still rejects
characters that break filesystems/URLs [e.g. < > " ? * | \ ;].
Files updated:
- app/api/payments/create/route.ts [inline regex + new error msg]
- app/api/payments/[id]/edit/route.ts [BATCH_REF_PATTERN + new error msg]
- app/unpaid/UnpaidClient.tsx [HTML pattern attribute]
- app/remittance/RemittanceClient.tsx [HTML pattern attribute]
- docs/superpowers/plans/2026-09-18-jei-payments-and-remittance.md [plan doc]
This commit is contained in:
parent
472ae5c0f0
commit
346a60cf97
5 changed files with 14 additions and 9 deletions
|
|
@ -3,7 +3,9 @@ import { revalidatePath } from "next/cache";
|
|||
import { getPb } from "../../../../lib/pb";
|
||||
|
||||
const BATCHES = "JEI_payment_batches";
|
||||
const BATCH_REF_PATTERN = /^[A-Za-z0-9_-]{1,50}$/;
|
||||
// Permissive: letters/digits/space/_-/,.,:()&#'+, length 1-100.
|
||||
// Rejects only characters that break filesystems/URLs (< > " ? * | \ ; etc).
|
||||
const BATCH_REF_PATTERN = /^[A-Za-z0-9 _\-\/.,:()&#'+]{1,100}$/;
|
||||
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
|
@ -37,7 +39,7 @@ export async function POST(
|
|||
// Validate
|
||||
if (!BATCH_REF_PATTERN.test(batchRef)) {
|
||||
return NextResponse.json(
|
||||
{ error: "batch_ref must match ^[A-Za-z0-9_-]{1,50}$" },
|
||||
{ error: "batch_ref must be 1-100 chars: letters, digits, space, _ - / . , : ( ) & # ' +" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,9 +40,11 @@ export async function POST(req: NextRequest) {
|
|||
}
|
||||
|
||||
// 2. batch_ref
|
||||
if (!/^[A-Za-z0-9_-]{1,50}$/.test(batchRef)) {
|
||||
// Permissive: letters/digits/space/_-/,.,:()&#'+, length 1-100.
|
||||
// Rejects only characters that break filesystems/URLs (< > " ? * | \ ; etc).
|
||||
if (!/^[A-Za-z0-9 _\-\/.,:()&#'+]{1,100}$/.test(batchRef)) {
|
||||
return NextResponse.json(
|
||||
{ error: "batch_ref must match ^[A-Za-z0-9_-]{1,50}$" },
|
||||
{ error: "batch_ref must be 1-100 chars: letters, digits, space, _ - / . , : ( ) & # ' +" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ export default function RemittanceClient({ batches }: { batches: BatchWithInvoic
|
|||
type="text"
|
||||
value={editForm.batch_ref}
|
||||
onChange={(e) => setEditForm({ ...editForm, batch_ref: e.target.value })}
|
||||
pattern="[A-Za-z0-9_-]{1,50}"
|
||||
pattern="[A-Za-z0-9 _\-\/.,:()&#'+]{1,100}"
|
||||
required
|
||||
className="w-full border rounded px-3 py-2 font-mono"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ export default function UnpaidClient({ initialRecords }: { initialRecords: Invoi
|
|||
type="text"
|
||||
value={batchRef}
|
||||
onChange={(e) => setBatchRef(e.target.value)}
|
||||
pattern="[A-Za-z0-9_-]{1,50}"
|
||||
pattern="[A-Za-z0-9 _\-\/.,:()&#'+]{1,100}"
|
||||
required
|
||||
className="w-full border rounded px-3 py-2 font-mono"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -378,9 +378,10 @@ export async function POST(req: NextRequest) {
|
|||
}
|
||||
|
||||
// 2. batch_ref
|
||||
if (!/^[A-Za-z0-9_-]{1,50}$/.test(batchRef)) {
|
||||
// Permissive: letters/digits/space/_-/,.,:()&#'+, length 1-100.
|
||||
if (!/^[A-Za-z0-9 _\-\/.,:()&#'+]{1,100}$/.test(batchRef)) {
|
||||
return NextResponse.json(
|
||||
{ error: "batch_ref must match ^[A-Za-z0-9_-]{1,50}$" },
|
||||
{ error: "batch_ref must be 1-100 chars: letters, digits, space, _ - / . , : ( ) & # ' +" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
|
@ -984,7 +985,7 @@ export default function UnpaidClient({ initialRecords }: { initialRecords: Invoi
|
|||
type="text"
|
||||
value={batchRef}
|
||||
onChange={(e) => setBatchRef(e.target.value)}
|
||||
pattern="[A-Za-z0-9_-]{1,50}"
|
||||
pattern="[A-Za-z0-9 _\-\/.,:()&#'+]{1,100}"
|
||||
required
|
||||
className="w-full border rounded px-3 py-2 font-mono"
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue