Version: 1.0
Last Updated: June 6, 2025
Status: Production Ready
This document provides a detailed description of each Mongoose data model used in the ClaimBot application, found within the src/models/ directory.
- All models include
createdAtandupdatedAttimestamps by default due to{ timestamps: true }in their schema options, unless otherwise specified forAuditLog. mongoose.models.ModelName || mongoose.model<IModelName>('ModelName', Schema)is a common pattern to prevent model recompilation issues in Next.js development.
File: src/models/AdminTripTemplate.ts
Interface: IAdminTripTemplate
Purpose: Represents trip templates defined by administrators, available globally for users to select when creating claims.
_id(ObjectId): Unique identifier for the template.origin(Location): Embedded object defining the starting point.address(String, required): Formatted address of the origin.lat(Number, required): Latitude of the origin.lng(Number, required): Longitude of the origin.
destination(Location): Embedded object defining the end point.address(String, required): Formatted address of the destination.lat(Number, required): Latitude of the destination.lng(Number, required): Longitude of the destination.
roundTrip(Boolean, required): Indicates if the template represents a round trip.label(String, required): A user-friendly name or label for this template (e.g., "Office to Client X").
None explicit, but origin and destination use the Location type from src/types/location.ts.
required for all fields and sub-fields.
None explicitly defined in this model file.
File: src/models/AuditLog.ts
Interface: IAuditLog
Purpose: Records significant actions performed within the system for auditing and tracking purposes.
userId(ObjectId, ref: 'User', required): The user who performed the action.action(String, required): A string identifying the action performed (e.g., 'created_claim', 'updated_user_roles').target(Object, required): Information about the entity that was affected.collection(String, required): The name of the MongoDB collection of the target document.documentId(ObjectId, required): The_idof the affected document in the target collection.
details(String, optional): Additional details or context about the action.timestamp(Date, required, default:Date.now): When the action occurred. (Note:timestamps: trueis not used here,timestampis explicitly defined).
userId refers to the User model.
required for userId, action, target.collection, target.documentId, timestamp.
None explicitly defined.
File: src/models/Claim.ts
Interface: IClaim
Purpose: Represents an expense claim submitted by a user.
userId(ObjectId, ref: 'User', required): The user who submitted the claim.date(Date, required): The date the expense was incurred.project(String, optional): Associated project for the claim.description(String, optional): Description of the claim.tripMode(String, enum: ['default', 'custom'], default: 'default'): Mode of the trip ('default' likely from office, 'custom' for user-defined origin).roundTrip(Boolean, optional): Whether the trip was a round trip.origin(String, optional): Textual description of the origin (used iforiginLocationnot set).destination(String, optional): Textual description of the destination.originLocation(Object, optional): Structured location data for the origin.lat(Number)lng(Number)formatted_address(String)
destinationLocation(Object, optional): Structured location data for the destination.lat(Number)lng(Number)formatted_address(String)
calculatedMileage(Number, optional): Mileage calculated by the system (e.g., via Google Maps).expenses(Object, optional): Nested object detailing various expense types.mileage(Number, optional): Claimed mileage amount (distinct fromcalculatedMileagewhich might be system-generated distance).toll(Number, optional)petrol(Number, optional)meal(Number, optional)others(Number, optional)
mileageRate(Number, optional): The mileage rate applied at the time of claim calculation.totalClaim(Number, optional): The total calculated amount for the claim.attachments(Array of ObjectId, ref: 'File'): List of file IDs linked to this claim.status(String, required, enum: ['draft', 'submitted', 'approved', 'rejected', 'paid'], default: 'draft'): Current status of the claim.submittedAt(Date, optional): Timestamp when the claim was submitted.approvedBy(ObjectId, ref: 'User', optional): The user who approved/rejected the claim.approvedAt(Date, optional): Timestamp of approval/rejection.remarks(String, optional): Remarks from the approver.
userId and approvedBy refer to User. attachments refers to File.
required and enum constraints.
None explicitly defined.
- draft - Initial state, can be edited by submitter
- submitted - Submitted for approval, awaiting review
- approved - Approved by manager/admin, ready for payment
- rejected - Rejected by approver with remarks
- paid - Payment processed (updated by finance)
File: src/models/File.ts
Interface: IFile
Purpose: Stores metadata about uploaded files, typically attachments for claims or overtime requests.
filename(String, required): Original name of the uploaded file.mimetype(String, optional): MIME type of the file.path(String, required): Server-side path where the file is stored (e.g.,/uploads/filename.ext).uploadedBy(ObjectId, ref: 'User', required): The user who uploaded the file.linkedTo(Object, required): Information linking the file to another document.collection(String, required, enum: ['claims', 'overtime']): The collection name of the linked document.documentId(ObjectId, required): The_idof the document in thelinkedTo.collection.
uploadedAt(Date): Automatically added bytimestamps: trueascreatedAt. The interface explicitly listsuploadedAt, but the schema usestimestamps. This might be a slight mismatch oruploadedAtis an alias forcreatedAt.
uploadedBy refers to User. linkedTo provides a polymorphic-like association.
required and enum constraints.
None explicitly defined.
File: src/models/LocationTemplate.ts
Interface: ILocationTemplate
Purpose: Represents individual named locations (points of interest) defined by administrators, likely used as predefined origins/destinations.
name(String, required, trim, maxlength: 100): User-friendly name for the location (e.g., "Main Office", "Client HQ Site A").address(String, required, trim, maxlength: 500): Full address of the location.lat(Number, required, min: -90, max: 90): Latitude.lng(Number, required, min: -180, max: 180): Longitude.
None.
required, trim, maxlength, min/max for coordinates.
{ name: 1 }{ lat: 1, lng: 1 }{ lat: 1, lng: 1 }, { name: 'location_2d' }(Geospatial index).
File: src/models/Overtime.ts
Interface: IOvertime
Purpose: Represents an overtime request submitted by a user.
userId(ObjectId, ref: 'User', required): The user submitting the request.date(Date, required): Date the overtime was worked.startTime(String, required, format HH:MM): Start time of overtime.endTime(String, required, format HH:MM): End time of overtime.reason(String, required): Justification for the overtime.hoursWorked(Number, optional): Calculated hours of overtime.rateMultiplier(Number, optional): Multiplier applied to the hourly rate (e.g., 1.5, 2.0).hourlyRate(Number, optional): The user's hourly rate at the time of calculation.totalPayout(Number, optional): Total calculated overtime pay.attachments(Array of ObjectId, ref: 'File'): List of file IDs linked to this request.status(String, required, enum: ['submitted', 'approved', 'rejected', 'paid'], default: 'submitted'): Current status of the request.approvedBy(ObjectId, ref: 'User', optional): The user who approved/rejected the request.approvedAt(Date, optional): Timestamp of approval/rejection.remarks(String, optional): Remarks from the approver.
userId and approvedBy refer to User. attachments refers to File.
required and enum constraints.
None explicitly defined.
- submitted - Initial state after submission, awaiting approval
- approved - Approved by manager/admin, ready for payment
- rejected - Rejected by approver with remarks
- paid - Payment processed (updated by finance)
- Maximum 18 hours of overtime per month per user
- Weekday overtime must start after 8 PM
- User's salary must be verified before submitting overtime
- Rate multiplier determined by day type and user designation
File: src/models/RateConfig.ts
Interface: IRateConfig
Purpose: Stores configuration for different rates, such as mileage rates or overtime multipliers.
type(String, required, enum: ['mileage', 'overtime_multiplier']): The type of rate being configured.value(Number, optional): The rate value itself (e.g., rate per km for 'mileage').condition(Object, optional): Conditions for applying an 'overtime_multiplier'.dayType(String, optional): E.g., "weekday", "weekend", "public_holiday".designation(String, optional): User designation this multiplier applies to.
multiplier(Number, optional): The overtime multiplier value (e.g., 1.5, 2.0).effectiveDate(Date, required): The date from which this rate configuration is effective.
None.
required and enum constraints.
None explicitly defined.
// Mileage rate configuration
{
type: 'mileage',
value: 0.5, // RM 0.50 per km
effectiveDate: new Date('2025-01-01')
}
// Overtime multiplier for weekends
{
type: 'overtime_multiplier',
multiplier: 2.0, // Double pay
condition: {
dayType: 'weekend'
},
effectiveDate: new Date('2025-01-01')
}File: src/models/SavedTripTemplate.ts
Interface: ISavedTripTemplate
Purpose: Represents trip templates saved by individual users for their personal convenience.
_id(ObjectId): Unique identifier for the template.userId(String, required): The Clerk User ID of the user who saved this template. (Note: Changed from ObjectId to String in the model to align with Clerk ID format).origin(Location): Embedded object for the starting point.address(String, required): Formatted address.lat(Number, required): Latitude.lng(Number, required): Longitude.
destination(Location): Embedded object for the end point.address(String, required): Formatted address.lat(Number, required): Latitude.lng(Number, required): Longitude.
roundTrip(Boolean, required): Indicates if it's a round trip.label(String, required): User-defined label for this template.
userId links to a user (conceptually, via Clerk ID). origin and destination use the Location type from src/types/location.ts.
required for all fields and sub-fields.
None explicitly defined.
File: src/models/User.ts
Interface: IUser
Purpose: Represents an application user, linking to Clerk authentication and storing application-specific user data.
clerkId(String, required, unique): The user's ID from Clerk.name(String, optional): User's full name.email(String, required, unique): User's email address.department(String, optional): User's department.designation(String, optional): User's job designation/title.roles(Array of String, enum: UserRole, required, default: ['staff']): List of roles assigned to the user.monthlySalary(Number, optional): User's monthly salary.hourlyRate(Number, optional): User's hourly rate (if applicable).salaryVerificationStatus(String, enum: ['pending', 'verified', 'rejected'], optional): Status of their salary verification.salarySubmittedAt(Date, optional): When salary info was last submitted.salaryVerifiedAt(Date, optional): When salary info was last verified.salaryVerifiedBy(String, optional): Clerk ID of the admin who verified the salary.lastSalaryReviewYear(Number, optional): The year the user last had their salary reviewed/submitted.monthlyOvertimeHours(Map, of: Number, default: {}): Stores total overtime hours per month (key: "YYYY-MM", value: hours).isActive(Boolean, default: true): Whether the user account is active.
hasRole(role: UserRole): boolean: Checks if the user has a specific role (superadmin always true).hasAnyRole(roles: UserRole[]): boolean: Checks if the user has any of the specified roles (superadmin always true).
canReviewSalary(Boolean, get): Computed based onlastSalaryReviewYearand current year. True iflastSalaryReviewYearis not set or is less than the current year.
pre('save'): Ensures the 'staff' role is always included in therolesarray before saving.
Implicitly linked to Claim, Overtime, AuditLog, File via userId or uploadedBy fields in those models.
required, unique, enum constraints.
clerkId and email are unique.
type UserRole = 'staff' | 'manager' | 'finance' | 'admin' | 'superadmin';- not_submitted - Initial state, no salary information provided
- pending - Salary submitted, awaiting verification
- verified - Salary verified by manager/admin
- rejected - Salary rejected, needs resubmission
erDiagram
User ||--o{ Claim : submits
User ||--o{ Overtime : submits
User ||--o{ File : uploads
User ||--o{ AuditLog : performs
User ||--o{ SavedTripTemplate : creates
Claim ||--o{ File : "has attachments"
Overtime ||--o{ File : "has attachments"
User ||--o| User : "approves (approvedBy)"
RateConfig ||--o{ Overtime : "determines rate"
RateConfig ||--o{ Claim : "determines mileage rate"
LocationTemplate }o--o{ Claim : "used in"
AdminTripTemplate }o--o{ Claim : "used in"
SavedTripTemplate }o--o{ Claim : "used in"
// Find users by role
const admins = await User.find({ roles: { $in: ['admin', 'superadmin'] } });
// Find users with pending salary verification
const pendingSalary = await User.find({ salaryVerificationStatus: 'pending' });// Find user's pending submissions
const pendingClaims = await Claim.find({
userId: userId,
status: { $in: ['draft', 'submitted'] }
});
// Find submissions requiring approval
const needsApproval = await Claim.find({ status: 'submitted' })
.populate('userId', 'name email department');// Find all actions by a user
const userActions = await AuditLog.find({ userId })
.sort({ timestamp: -1 })
.limit(50);
// Find actions on a specific document
const documentHistory = await AuditLog.find({
'target.collection': 'claims',
'target.documentId': claimId
});- RBAC and Authentication - User roles and permissions
- API Reference - How to interact with these models via API
- System Architecture - Overall system design
- Workflow Guides - Business process documentation
Next Steps: Review the API Reference to understand how to interact with these data models through the application's REST API.