Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions models/EmployeePerk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
const mongoose = require('mongoose');

/**
* EmployeePerk Model
* Manages non-cash benefits and perquisites for employees
*/
const employeePerkSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
index: true
},
employeeId: {
type: String,
required: true,
index: true
},
employeeName: String,
perkType: {
type: String,
enum: [
'company_car',
'housing',
'meal_vouchers',
'health_insurance',
'life_insurance',
'stock_options',
'club_membership',
'phone_allowance',
'internet_allowance',
'education_allowance',
'relocation_assistance',
'other'
],
required: true
},
perkName: {
type: String,
required: true
},
description: String,
monetaryValue: {
type: Number,
default: 0
},
taxableValue: {
type: Number,
default: 0
},
isTaxable: {
type: Boolean,
default: true
},
frequency: {
type: String,
enum: ['one_time', 'monthly', 'quarterly', 'annual'],
default: 'monthly'
},
effectiveFrom: {
type: Date,
required: true
},
effectiveTo: Date,
provider: {
name: String,
contactDetails: String
},
documents: [{
documentType: String,
documentUrl: String,
uploadedAt: Date
}],
utilizationTracking: {
isTracked: {
type: Boolean,
default: false
},
utilizationLimit: Number,
utilizationUsed: {
type: Number,
default: 0
}
},
status: {
type: String,
enum: ['active', 'suspended', 'expired', 'cancelled'],
default: 'active'
},
notes: String
}, {
timestamps: true
});

// Indexes
employeePerkSchema.index({ userId: 1, employeeId: 1, status: 1 });
employeePerkSchema.index({ perkType: 1, effectiveFrom: 1 });

module.exports = mongoose.model('EmployeePerk', employeePerkSchema);
153 changes: 153 additions & 0 deletions models/PayrollRun.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
const mongoose = require('mongoose');

/**
* PayrollRun Model
* Manages batch payroll processing for a specific period
*/
const payrollEntrySchema = new mongoose.Schema({
employeeId: {
type: String,
required: true
},
employeeName: String,
salaryStructureId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'SalaryStructure'
},
earnings: [{
componentName: String,
amount: Number
}],
deductions: [{
componentName: String,
amount: Number
}],
reimbursements: [{
componentName: String,
amount: Number
}],
grossPay: {
type: Number,
required: true
},
totalDeductions: {
type: Number,
default: 0
},
netPay: {
type: Number,
required: true
},
taxDeducted: {
type: Number,
default: 0
},
professionalTax: {
type: Number,
default: 0
},
providentFund: {
type: Number,
default: 0
},
esi: {
type: Number,
default: 0
},
paymentStatus: {
type: String,
enum: ['pending', 'processed', 'failed', 'reversed'],
default: 'pending'
},
paymentDate: Date,
paymentReference: String,
remarks: String
}, { _id: false });

const payrollRunSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
index: true
},
runId: {
type: String,
unique: true,
required: true
},
payrollPeriod: {
month: {
type: Number,
required: true,
min: 1,
max: 12
},
year: {
type: Number,
required: true
}
},
periodStart: {
type: Date,
required: true
},
periodEnd: {
type: Date,
required: true
},
entries: [payrollEntrySchema],
summary: {
totalEmployees: {
type: Number,
default: 0
},
totalGrossPay: {
type: Number,
default: 0
},
totalDeductions: {
type: Number,
default: 0
},
totalNetPay: {
type: Number,
default: 0
},
totalTax: {
type: Number,
default: 0
}
},
status: {
type: String,
enum: ['draft', 'pending_approval', 'approved', 'processing', 'completed', 'failed'],
default: 'draft'
},
approvedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
approvedAt: Date,
processedAt: Date,
notes: String
}, {
timestamps: true
});

// Pre-save hook to calculate summary
payrollRunSchema.pre('save', function (next) {
this.summary.totalEmployees = this.entries.length;
this.summary.totalGrossPay = this.entries.reduce((sum, e) => sum + e.grossPay, 0);
this.summary.totalDeductions = this.entries.reduce((sum, e) => sum + e.totalDeductions, 0);
this.summary.totalNetPay = this.entries.reduce((sum, e) => sum + e.netPay, 0);
this.summary.totalTax = this.entries.reduce((sum, e) => sum + e.taxDeducted, 0);

next();
});

// Indexes
payrollRunSchema.index({ userId: 1, 'payrollPeriod.year': 1, 'payrollPeriod.month': 1 });
payrollRunSchema.index({ status: 1, createdAt: -1 });

module.exports = mongoose.model('PayrollRun', payrollRunSchema);
124 changes: 124 additions & 0 deletions models/SalaryStructure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
const mongoose = require('mongoose');

/**
* SalaryStructure Model
* Defines flexible component-based salary structure for employees
*/
const salaryComponentSchema = new mongoose.Schema({
componentName: {
type: String,
required: true
},
componentType: {
type: String,
enum: ['earning', 'deduction', 'reimbursement'],
required: true
},
calculationType: {
type: String,
enum: ['fixed', 'percentage', 'formula'],
default: 'fixed'
},
amount: {
type: Number,
default: 0
},
percentage: {
type: Number,
default: 0
},
baseComponent: {
type: String,
default: null
},
isTaxable: {
type: Boolean,
default: true
},
isStatutory: {
type: Boolean,
default: false
}
}, { _id: false });

const salaryStructureSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
index: true
},
employeeId: {
type: String,
required: true,
index: true
},
employeeName: {
type: String,
required: true
},
designation: String,
department: String,
effectiveFrom: {
type: Date,
required: true
},
effectiveTo: Date,
components: [salaryComponentSchema],
ctc: {
type: Number,
required: true
},
grossSalary: {
type: Number,
default: 0
},
netSalary: {
type: Number,
default: 0
},
paymentFrequency: {
type: String,
enum: ['monthly', 'bi-weekly', 'weekly'],
default: 'monthly'
},
bankDetails: {
accountNumber: String,
ifscCode: String,
bankName: String,
accountHolderName: String
},
taxRegime: {
type: String,
enum: ['old', 'new'],
default: 'new'
},
isActive: {
type: Boolean,
default: true
}
}, {
timestamps: true
});

// Pre-save hook to calculate gross and net salary
salaryStructureSchema.pre('save', function (next) {
const earnings = this.components
.filter(c => c.componentType === 'earning')
.reduce((sum, c) => sum + c.amount, 0);

const deductions = this.components
.filter(c => c.componentType === 'deduction')
.reduce((sum, c) => sum + c.amount, 0);

this.grossSalary = earnings;
this.netSalary = earnings - deductions;

next();
});

// Indexes
salaryStructureSchema.index({ userId: 1, employeeId: 1 });
salaryStructureSchema.index({ effectiveFrom: 1, isActive: 1 });

module.exports = mongoose.model('SalaryStructure', salaryStructureSchema);
Loading