erp-retail-backend/src/modules/cash/validation/cash.schema.ts

161 lines
4.7 KiB
TypeScript

import { z } from 'zod';
import {
uuidSchema,
moneySchema,
paginationSchema,
} from '../../../shared/validation/common.schema';
// Enums
export const movementTypeEnum = z.enum([
'cash_in',
'cash_out',
'deposit',
'withdrawal',
'adjustment',
'opening',
'closing',
]);
export const movementReasonEnum = z.enum([
'change_fund',
'petty_cash',
'bank_deposit',
'supplier_payment',
'expense',
'salary_advance',
'correction',
'other',
]);
export const movementStatusEnum = z.enum([
'pending',
'approved',
'rejected',
'cancelled',
]);
export const closingTypeEnum = z.enum(['shift', 'daily', 'weekly', 'monthly']);
export const closingStatusEnum = z.enum([
'in_progress',
'pending_review',
'approved',
'rejected',
'reconciled',
]);
export const denominationTypeEnum = z.enum(['bill', 'coin']);
// ==================== MOVEMENT SCHEMAS ====================
// Create movement schema
export const createMovementSchema = z.object({
branchId: uuidSchema,
registerId: uuidSchema,
sessionId: uuidSchema,
type: movementTypeEnum,
reason: movementReasonEnum.optional(),
amount: moneySchema.positive('Amount must be positive'),
description: z.string().min(1, 'Description is required').max(500),
referenceType: z.string().max(50).optional(),
referenceId: uuidSchema.optional(),
referenceNumber: z.string().max(50).optional(),
requiresApproval: z.boolean().optional(),
recipientName: z.string().max(200).optional(),
recipientId: uuidSchema.optional(),
accountId: uuidSchema.optional(),
bankAccount: z.string().max(50).optional(),
depositSlipNumber: z.string().max(50).optional(),
notes: z.string().max(1000).optional(),
metadata: z.record(z.any()).optional(),
});
// Approve/reject movement schema
export const movementActionSchema = z.object({
reason: z.string().max(255).optional(),
});
// List movements query schema
export const listMovementsQuerySchema = paginationSchema.extend({
branchId: uuidSchema.optional(),
sessionId: uuidSchema.optional(),
registerId: uuidSchema.optional(),
type: movementTypeEnum.optional(),
reason: movementReasonEnum.optional(),
status: movementStatusEnum.optional(),
startDate: z.coerce.date().optional(),
endDate: z.coerce.date().optional(),
});
// ==================== CLOSING SCHEMAS ====================
// Create closing schema
export const createClosingSchema = z.object({
branchId: uuidSchema,
registerId: uuidSchema.optional(),
sessionId: uuidSchema.optional(),
type: closingTypeEnum,
periodStart: z.coerce.date(),
periodEnd: z.coerce.date(),
openingBalance: moneySchema.default(0),
closingNotes: z.string().max(1000).optional(),
}).refine(data => data.periodStart <= data.periodEnd, {
message: 'Period start must be before or equal to period end',
path: ['periodEnd'],
});
// Denomination count schema
export const denominationCountSchema = z.object({
type: denominationTypeEnum,
denomination: z.number().positive(),
quantity: z.number().int().min(0),
});
// Submit cash count schema
export const submitCashCountSchema = z.object({
denominations: z.array(denominationCountSchema).min(1, 'At least one denomination is required'),
});
// Submit payment counts schema
export const submitPaymentCountsSchema = z.object({
cash: moneySchema.optional(),
card: moneySchema.default(0),
transfer: moneySchema.default(0),
other: moneySchema.default(0),
});
// Closing action schema (approve/reject)
export const closingActionSchema = z.object({
notes: z.string().max(1000).optional(),
});
// Reject closing schema (requires notes)
export const rejectClosingSchema = z.object({
notes: z.string().min(1, 'Rejection reason is required').max(1000),
});
// Reconcile closing schema
export const reconcileClosingSchema = z.object({
depositAmount: moneySchema.positive('Deposit amount must be positive'),
depositReference: z.string().min(1).max(100),
depositDate: z.coerce.date(),
});
// List closings query schema
export const listClosingsQuerySchema = paginationSchema.extend({
branchId: uuidSchema.optional(),
sessionId: uuidSchema.optional(),
status: closingStatusEnum.optional(),
type: closingTypeEnum.optional(),
startDate: z.coerce.date().optional(),
endDate: z.coerce.date().optional(),
});
// Types
export type CreateMovementInput = z.infer<typeof createMovementSchema>;
export type ListMovementsQuery = z.infer<typeof listMovementsQuerySchema>;
export type CreateClosingInput = z.infer<typeof createClosingSchema>;
export type SubmitCashCountInput = z.infer<typeof submitCashCountSchema>;
export type SubmitPaymentCountsInput = z.infer<typeof submitPaymentCountsSchema>;
export type ReconcileClosingInput = z.infer<typeof reconcileClosingSchema>;
export type ListClosingsQuery = z.infer<typeof listClosingsQuerySchema>;