feat: 待签合同支持登记线下签署日期

- 线下手签合同:展开后可点击「登记签署日期」选择日期并保存
  保存后合同从待签列表移除(signDate 已填写)
- 电子签合同:签署日期由电签系统自动回写,不可手动修改
- 后端新增 POST /esign/sign-date 接口
  电子签合同拒绝手动修改签署日期

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
selfrelease
2026-08-16 12:13:07 +08:00
parent 79145b47b9
commit 040c5a3ab9
8 changed files with 344 additions and 50 deletions
+12 -4
View File
@@ -10,6 +10,7 @@ import {
generatePayslipFromBatches,
prePayrollCheck,
} from '../services/payroll.service'
import { isInProbation } from '../services/contract.service'
// RFC 5987 编码中文文件名
function contentDisposition(filename: string): string {
@@ -352,8 +353,12 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
where: { employeeId_month: { employeeId: emp.id, month } },
})
if (emp.contracts?.[0]?.probationSalary && new Date(emp.contracts[0].startDate) > new Date(Date.now() - 365 * 24 * 60 * 60 * 1000)) {
baseSalary = emp.contracts[0].probationSalary
// 按批次月份判定是否仍在试用期(试用期结束日 = 合同开始日 + 试用期月数)
// 试用期且 probationSalary > 0 → 用试用期工资;否则用转正工资
const batchMonthEnd = new Date(`${month}-28T23:59:59`) // 月末近似
const latestContract = emp.contracts?.[0]
if (isInProbation(latestContract, batchMonthEnd) && latestContract.probationSalary > 0) {
baseSalary = latestContract.probationSalary
} else if (emp.monthlySalary) {
try { baseSalary = Number(decrypt(emp.monthlySalary)) || 0 } catch { baseSalary = Number(emp.monthlySalary) || 0 }
}
@@ -607,8 +612,11 @@ router.post('/batches/:batchId/employees', async (req: AuthRequest, res: Respons
if (!emp) continue
let baseSalary = 0
if (emp.contracts?.[0]?.probationSalary && new Date(emp.contracts[0].startDate) > new Date(Date.now() - 365 * 24 * 60 * 60 * 1000)) {
baseSalary = emp.contracts[0].probationSalary
// 按批次月份判定是否仍在试用期
const batchMonthEnd = new Date(`${batch.month}-28T23:59:59`)
const latestContract = emp.contracts?.[0]
if (isInProbation(latestContract, batchMonthEnd) && latestContract.probationSalary > 0) {
baseSalary = latestContract.probationSalary
} else if (emp.monthlySalary) {
try { baseSalary = Number(decrypt(emp.monthlySalary)) || 0 } catch { baseSalary = Number(emp.monthlySalary) || 0 }
}