first commit
This commit is contained in:
83
backend/src/services/CalculationService.ts
Normal file
83
backend/src/services/CalculationService.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import pool from '../config/database';
|
||||
import { PerformanceLevel } from '../dao/PerformanceDAO';
|
||||
|
||||
export interface AttendanceInput {
|
||||
leaveDays: number;
|
||||
lateTimes: number;
|
||||
lackCardTimes: number;
|
||||
}
|
||||
|
||||
export interface LevelAndReward {
|
||||
level: PerformanceLevel;
|
||||
rewardPunish: string;
|
||||
}
|
||||
|
||||
export interface ConsecutiveLowScoreResult {
|
||||
consecutiveMonths: number;
|
||||
warning: 'none' | 'written_warning' | 'dismissal';
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate attendance score.
|
||||
* Base: 10 points. Deduct 5 per leave day, 2 per late/lack-card occurrence. Minimum 0.
|
||||
* Requirements: 11.1, 11.2, 11.3, 11.5
|
||||
*/
|
||||
export function calculateAttendanceScore(input: AttendanceInput): number {
|
||||
const { leaveDays, lateTimes, lackCardTimes } = input;
|
||||
const deduction = leaveDays * 5 + lateTimes * 2 + lackCardTimes * 2;
|
||||
return Math.max(0, 10 - deduction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate performance level and reward/punishment description based on total score.
|
||||
* Requirements: 5.1, 5.2, 5.3, 5.4, 5.5
|
||||
*/
|
||||
export function calculateLevelAndReward(totalScore: number): LevelAndReward {
|
||||
if (totalScore >= 90) {
|
||||
return { level: 'excellent', rewardPunish: '优秀,按公司规定给予奖励' };
|
||||
} else if (totalScore >= 80) {
|
||||
return { level: 'qualified', rewardPunish: '合格,扣除当月绩效工资100元' };
|
||||
} else if (totalScore >= 70) {
|
||||
return { level: 'qualified', rewardPunish: '合格,扣除当月绩效工资200元' };
|
||||
} else if (totalScore >= 60) {
|
||||
return { level: 'need_motivation', rewardPunish: '需激励,扣除当月绩效工资300元' };
|
||||
} else {
|
||||
return { level: 'unqualified', rewardPunish: '不合格,扣除当月绩效工资600元' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check consecutive low score warning for an employee.
|
||||
* Queries the most recent completed performance records and counts consecutive months below 60.
|
||||
* Requirements: 5.6, 5.7
|
||||
*/
|
||||
export async function checkConsecutiveLowScore(
|
||||
userId: number,
|
||||
lookbackMonths: number = 3
|
||||
): Promise<ConsecutiveLowScoreResult> {
|
||||
const [rows] = await pool.query<any[]>(
|
||||
`SELECT total_score FROM performance_month
|
||||
WHERE user_id = ? AND status = 'completed' AND total_score IS NOT NULL
|
||||
ORDER BY month DESC
|
||||
LIMIT ?`,
|
||||
[userId, lookbackMonths]
|
||||
);
|
||||
|
||||
let consecutiveMonths = 0;
|
||||
for (const row of rows) {
|
||||
if (row.total_score < 60) {
|
||||
consecutiveMonths++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let warning: ConsecutiveLowScoreResult['warning'] = 'none';
|
||||
if (consecutiveMonths >= 3) {
|
||||
warning = 'dismissal';
|
||||
} else if (consecutiveMonths >= 2) {
|
||||
warning = 'written_warning';
|
||||
}
|
||||
|
||||
return { consecutiveMonths, warning };
|
||||
}
|
||||
Reference in New Issue
Block a user