fix: 年度价值报告风险量化按劳动法设定法定上限
- 单条风险estimatedLoss封顶:双倍工资≤11个月(第82条)、经济补偿N≤12个月月薪≤社平3倍(第47条)、违法解除2N≤24个月(第87条) - 年度规避损失按30%规避率打折(非每项风险都演变为实际仲裁) - ROI上限9999% - 前端计算说明同步更新
This commit is contained in:
@@ -15,8 +15,15 @@ function safeDecryptSalary(encrypted: string): number {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 社平工资3倍封顶(参照上海2024年度社平工资约12307元/月,3倍≈36921元)
|
||||
* 《劳动合同法》第47条:月工资高于社平3倍的按3倍计,且补偿年限最高12年
|
||||
*/
|
||||
const SOCIAL_AVG_SALARY_3X = 36921
|
||||
|
||||
/**
|
||||
* 风险量化:根据风险类型和员工数据估算预估损失金额
|
||||
* 依据《劳动合同法》《劳动法》设定法定上限
|
||||
* @returns { estimatedLoss, lossRange, deadline }
|
||||
*/
|
||||
function estimateRiskCost(
|
||||
@@ -24,21 +31,26 @@ function estimateRiskCost(
|
||||
employee?: { hireDate: Date; monthlySalary?: string } | null,
|
||||
): { estimatedLoss: number; lossRange: [number, number]; deadline?: Date } {
|
||||
const today = new Date()
|
||||
const salary = employee ? safeDecryptSalary(employee.monthlySalary || '0') : 0
|
||||
const rawSalary = employee ? safeDecryptSalary(employee.monthlySalary || '0') : 0
|
||||
// 第47条:高收入者月薪封顶为社平3倍
|
||||
const salary = Math.min(rawSalary, SOCIAL_AVG_SALARY_3X)
|
||||
const workYears = employee
|
||||
? Math.max(0, (today.getFullYear() - employee.hireDate.getFullYear()) +
|
||||
(today.getMonth() - employee.hireDate.getMonth()) / 12)
|
||||
: 0
|
||||
const n = Math.max(0.5, Math.round(workYears * 2) / 2) // 经济补偿月数,不满半年算0.5
|
||||
// 经济补偿月数:每满1年1个月,不满半年算0.5个月,第47条上限12年
|
||||
const n = Math.min(12, Math.max(0.5, Math.round(workYears * 2) / 2))
|
||||
|
||||
// 根据风险标题关键词判断类型并估算
|
||||
const title = risk.title
|
||||
|
||||
// 未签合同(>30天):双倍工资
|
||||
// 《劳动合同法》第82条:超过1个月不满1年未签书面合同,每月支付2倍工资
|
||||
// 超过1年视为已订立无固定期限合同,不再适用双倍工资,故最多罚11个月
|
||||
if (title.includes('未签合同') && title.includes('天')) {
|
||||
const daysMatch = title.match(/(\d+)天/)
|
||||
const days = daysMatch ? parseInt(daysMatch[1]) : 0
|
||||
const months = Math.ceil(days / 30)
|
||||
const months = Math.min(11, Math.ceil(days / 30)) // 法定上限11个月
|
||||
const loss = salary * months
|
||||
return {
|
||||
estimatedLoss: loss,
|
||||
@@ -48,8 +60,9 @@ function estimateRiskCost(
|
||||
}
|
||||
|
||||
// 合同到期未续签:经济补偿 N × 月薪
|
||||
// 《劳动合同法》第47条:N≤12个月,月薪≤社平3倍
|
||||
if (title.includes('到期') && title.includes('未续签')) {
|
||||
const loss = salary * n
|
||||
const loss = salary * n // n已封顶12
|
||||
return {
|
||||
estimatedLoss: loss,
|
||||
lossRange: [loss, loss * 2], // 可能被认定为违法解除 2N
|
||||
@@ -79,8 +92,9 @@ function estimateRiskCost(
|
||||
}
|
||||
|
||||
// 三期/工伤/医疗期:违法解除风险 2N
|
||||
// 《劳动合同法》第87条:违法解除按第47条标准的2倍赔偿,2N≤24个月
|
||||
if (title.includes('孕期') || title.includes('哺乳期') || title.includes('工伤') || title.includes('医疗期')) {
|
||||
const loss = salary * n * 2
|
||||
const loss = salary * n * 2 // n已封顶12,故2N≤24
|
||||
return {
|
||||
estimatedLoss: loss,
|
||||
lossRange: [loss, loss * 2],
|
||||
@@ -1609,9 +1623,11 @@ export async function getAnnualValueReport(orgId: string, year: number) {
|
||||
}))
|
||||
|
||||
// 价值计算
|
||||
// 1. 规避损失 = 已解决风险的预估损失之和
|
||||
// 1. 规避损失 = 已解决风险的预估损失之和 × 0.3(规避率30%,非每项风险都会演变为实际仲裁)
|
||||
// 2. 节约工时 = AI 查询 * 0.5h + 合同审查 * 1h + 算薪批次 * 2h + 制度公示 * 1h
|
||||
// 3. 节约成本 = 节约工时 * 100 元/h(HR 平均时薪)
|
||||
const RISK_AVOIDANCE_RATE = 0.3 // 规避率:约30%的未处理风险会演变为实际劳动争议
|
||||
const adjustedLossAvoided = Math.round(lossAvoided * RISK_AVOIDANCE_RATE)
|
||||
const aiTimeSaved = aiConversations * 0.5 + aiReviews * 1.0
|
||||
const payrollTimeSaved = payrollBatches * 2.0
|
||||
const policyTimeSaved = policiesPublished * 1.0
|
||||
@@ -1619,12 +1635,13 @@ export async function getAnnualValueReport(orgId: string, year: number) {
|
||||
const timeSaved = Math.round(aiTimeSaved + payrollTimeSaved + policyTimeSaved + contractTimeSaved)
|
||||
const costSaved = timeSaved * 100
|
||||
|
||||
// 总价值 = 规避损失 + 节约成本
|
||||
const totalValue = lossAvoided + costSaved
|
||||
// 总价值 = 规避损失(打折后) + 节约成本
|
||||
const totalValue = adjustedLossAvoided + costSaved
|
||||
|
||||
// ROI = 总价值 / 系统成本(假设年费 12000 元)
|
||||
// ROI = 总价值 / 系统成本(年费 12000 元),上限 9999%
|
||||
const systemCost = 12000
|
||||
const roi = systemCost > 0 ? Math.round((totalValue / systemCost) * 100) : 0
|
||||
const rawRoi = systemCost > 0 ? Math.round((totalValue / systemCost) * 100) : 0
|
||||
const roi = Math.min(rawRoi, 9999)
|
||||
|
||||
const metrics = {
|
||||
risksResolved,
|
||||
|
||||
@@ -226,7 +226,7 @@ export default function AnnualValueReport() {
|
||||
<div className="space-y-1 text-xs text-gray-600">
|
||||
<div className="flex items-start gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-safe mt-1 flex-shrink-0" />
|
||||
<span><b>规避损失</b>:已解决风险的预估损失金额之和(基于风险量化模型)</span>
|
||||
<span><b>规避损失</b>:已解决风险的预估损失之和 × 30%规避率(依据《劳动合同法》量化模型,单条上限按法定标准封顶)</span>
|
||||
</div>
|
||||
<div className="flex items-start gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-blue-500 mt-1 flex-shrink-0" />
|
||||
@@ -238,11 +238,15 @@ export default function AnnualValueReport() {
|
||||
</div>
|
||||
<div className="flex items-start gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-amber-500 mt-1 flex-shrink-0" />
|
||||
<span><b>总价值</b>:规避损失 + 节约成本</span>
|
||||
<span><b>总价值</b>:规避损失(打折后) + 节约成本</span>
|
||||
</div>
|
||||
<div className="flex items-start gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-gray-400 mt-1 flex-shrink-0" />
|
||||
<span><b>ROI</b>:总价值 ÷ 系统年费(¥12,000)× 100%</span>
|
||||
<span><b>ROI</b>:总价值 ÷ 系统年费(¥12,000)× 100%,上限 9999%</span>
|
||||
</div>
|
||||
<div className="flex items-start gap-1.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-gray-300 mt-1 flex-shrink-0" />
|
||||
<span><b>法定上限</b>:双倍工资≤11个月(第82条)、经济补偿N≤12个月月薪≤社平3倍(第47条)、违法解除2N≤24个月(第87条)</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user