feat: 批量获取操作跨批次重复检查 + 加班费改为获取 + 排除预入职

后端:
- 5个批量获取接口(加班费/绩效/奖金/违纪/考勤)加 force 参数
- 非force模式下检查同月其他批次是否已有非零值,有则返回 needConfirm
- 加班费接口改用 calcBatchEntry 重算(含税/社保),不再手动算 totalPay
- 批次创建用当前日期判断是否已入职,排除预入职人员

前端:
- 5个 mutation 支持 force 参数,needConfirm 时弹确认框
- 加班费按钮文字从'导入加班费'改为'获取加班费'
This commit is contained in:
freedakgmail
2026-08-19 08:04:37 +08:00
parent 519d14d623
commit 8388672c40
4 changed files with 235 additions and 41 deletions
+49 -19
View File
@@ -562,25 +562,37 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
})
const importOvertimeMutation = useMutation({
mutationFn: () => payrollApi.importOvertimeToBatch(batchId),
onSuccess: (res: any) => {
mutationFn: (force?: boolean) => payrollApi.importOvertimeToBatch(batchId, force),
onSuccess: async (res: any) => {
if (res.data?.needConfirm) {
if (await confirm({ title: '跨批次重复提醒', message: res.data.message, variant: 'primary' })) {
importOvertimeMutation.mutate(true)
}
return
}
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
queryClient.invalidateQueries({ queryKey: ['overtime-records'] })
if (res.data?.imported > 0) {
toast.success(`成功导入 ${res.data.imported} 条加班费记录`)
toast.success(`成功获取 ${res.data.imported} 条加班费记录`)
} else {
toast.info(res.data?.message || '没有可导入的加班记录')
toast.info(res.data?.message || '没有可获取的加班记录')
}
},
onError: () => {
toast.error('导入失败,请重试')
toast.error('获取失败,请重试')
},
})
const fetchBonusMutation = useMutation({
mutationFn: () => payrollApi.fetchBonusToBatch(batchId),
onSuccess: (res: any) => {
mutationFn: (force?: boolean) => payrollApi.fetchBonusToBatch(batchId, force),
onSuccess: async (res: any) => {
if (res.data?.needConfirm) {
if (await confirm({ title: '跨批次重复提醒', message: res.data.message, variant: 'primary' })) {
fetchBonusMutation.mutate(true)
}
return
}
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
if (res.data?.filled > 0) {
@@ -595,8 +607,14 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
})
const fetchPerformanceMutation = useMutation({
mutationFn: () => payrollApi.fetchPerformanceToBatch(batchId),
onSuccess: (res: any) => {
mutationFn: (force?: boolean) => payrollApi.fetchPerformanceToBatch(batchId, force),
onSuccess: async (res: any) => {
if (res.data?.needConfirm) {
if (await confirm({ title: '跨批次重复提醒', message: res.data.message, variant: 'primary' })) {
fetchPerformanceMutation.mutate(true)
}
return
}
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
if (res.data?.filled > 0) {
@@ -611,8 +629,14 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
})
const fetchDisciplinaryMutation = useMutation({
mutationFn: () => payrollApi.fetchDisciplinaryToBatch(batchId),
onSuccess: (res: any) => {
mutationFn: (force?: boolean) => payrollApi.fetchDisciplinaryToBatch(batchId, force),
onSuccess: async (res: any) => {
if (res.data?.needConfirm) {
if (await confirm({ title: '跨批次重复提醒', message: res.data.message, variant: 'primary' })) {
fetchDisciplinaryMutation.mutate(true)
}
return
}
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
if (res.data?.filled > 0) {
@@ -627,8 +651,14 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
})
const fetchAttendanceDeductionMutation = useMutation({
mutationFn: () => payrollApi.fetchAttendanceDeductionToBatch(batchId),
onSuccess: (res: any) => {
mutationFn: (force?: boolean) => payrollApi.fetchAttendanceDeductionToBatch(batchId, force),
onSuccess: async (res: any) => {
if (res.data?.needConfirm) {
if (await confirm({ title: '跨批次重复提醒', message: res.data.message, variant: 'primary' })) {
fetchAttendanceDeductionMutation.mutate(true)
}
return
}
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
if (res.data?.filled > 0) {
@@ -854,16 +884,16 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
<Button
variant="secondary"
size="sm"
onClick={() => importOvertimeMutation.mutate()}
onClick={() => importOvertimeMutation.mutate(undefined)}
disabled={importOvertimeMutation.isPending}
>
<Calculator className="w-4 h-4 mr-1" />
{importOvertimeMutation.isPending ? '导入中...' : '导入加班费'}
{importOvertimeMutation.isPending ? '获取中...' : '获取加班费'}
</Button>
<Button
variant="secondary"
size="sm"
onClick={() => fetchBonusMutation.mutate()}
onClick={() => fetchBonusMutation.mutate(undefined)}
disabled={fetchBonusMutation.isPending || isArchived}
title="从提成奖金模块按月拉取填充奖金字段"
>
@@ -873,7 +903,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
<Button
variant="secondary"
size="sm"
onClick={() => fetchPerformanceMutation.mutate()}
onClick={() => fetchPerformanceMutation.mutate(undefined)}
disabled={fetchPerformanceMutation.isPending || isArchived}
title="按考核等级系数计算绩效工资(A=1.2/B=1.0/C=0.8/D=0.6),无考核记录按1.0"
>
@@ -883,7 +913,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
<Button
variant="secondary"
size="sm"
onClick={() => fetchDisciplinaryMutation.mutate()}
onClick={() => fetchDisciplinaryMutation.mutate(undefined)}
disabled={fetchDisciplinaryMutation.isPending || isArchived}
title="按批次月份从违纪记录中汇总扣款金额(action=DEDUCTION"
>
@@ -893,7 +923,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
<Button
variant="secondary"
size="sm"
onClick={() => fetchAttendanceDeductionMutation.mutate()}
onClick={() => fetchAttendanceDeductionMutation.mutate(undefined)}
disabled={fetchAttendanceDeductionMutation.isPending || isArchived}
title="按批次月份从考勤确认中获取扣款金额(需先在考勤确认中填写)"
>