From ee46203f92967e5eed677e6db62887b468ee8ebe Mon Sep 17 00:00:00 2001 From: 27942 Date: Thu, 8 Jan 2026 18:24:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=A4=A7=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- User/views.py | 58 ++- .../migrations/0011_bonuschange_submitter.py | 18 + finance/models.py | 1 + finance/views.py | 302 ++++++++++++-- 系统变更需求14-16接口文档.md | 372 ++++++++++++++++++ 5 files changed, 717 insertions(+), 34 deletions(-) create mode 100644 finance/migrations/0011_bonuschange_submitter.py create mode 100644 系统变更需求14-16接口文档.md diff --git a/User/views.py b/User/views.py index b5ea172..272595f 100644 --- a/User/views.py +++ b/User/views.py @@ -982,17 +982,63 @@ class approvalProcessing(APIView): if type == "报销申请": try: - user = Reimbursement.objects.get(id=approval.user_id, is_deleted=False) + reimbursement = Reimbursement.objects.get(id=approval.user_id, is_deleted=False) except Reimbursement.DoesNotExist: return Response({'status': 'error', 'message': '报销申请记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND) + if state == "已通过": - approval.state = "已通过" - user.state = "已通过" + # 多级审批逻辑:检查是否还有下一个审批人 + # personincharge字段存储所有审批人(用逗号分隔),当前审批人是第一个 + # 从审批内容中提取所有审批人列表 + approvers_list = [] + if "审批流程:" in approval.content: + # 从内容中提取审批人列表 + try: + flow_part = approval.content.split("审批流程:")[1].split("(按顺序审批)")[0] + approvers_list = [a.strip() for a in flow_part.split(',') if a.strip()] + except: + # 如果解析失败,使用personincharge作为单个审批人 + approvers_list = [approval.personincharge] + else: + # 如果没有审批流程信息,使用personincharge作为单个审批人 + approvers_list = [approval.personincharge] + + current_approver = approval.personincharge + + # 找到当前审批人在列表中的位置 + try: + current_index = approvers_list.index(current_approver) + except ValueError: + # 如果找不到,说明是单个审批人或格式不对,直接完成 + current_index = 0 + + # 检查是否还有下一个审批人 + if current_index < len(approvers_list) - 1: + # 还有下一个审批人,流转到下一个 + next_approver = approvers_list[current_index + 1] + approval.personincharge = next_approver + approval.state = '审核中' # 继续审核中 + approval.content = approval.content.replace( + f"当前审批人:{current_approver}", + f"当前审批人:{next_approver}" + ) if f"当前审批人:{current_approver}" in approval.content else approval.content + f",当前审批人:{next_approver}" + approval.save(update_fields=['state', 'personincharge', 'content']) + reimbursement.state = "审核中" + reimbursement.save(update_fields=['state']) + else: + # 最后一个审批人,审批通过后抄送财务 + approval.personincharge = "财务" + approval.state = "已通过" + approval.content = approval.content + ",所有审批人已通过,已抄送财务" + approval.save(update_fields=['state', 'personincharge', 'content']) + reimbursement.state = "待财务处理" + reimbursement.save(update_fields=['state']) else: + # 审批不通过 approval.state = "未通过" - user.state = "未通过" - approval.save(update_fields=['state']) - user.save(update_fields=['state']) + reimbursement.state = "未通过" + approval.save(update_fields=['state']) + reimbursement.save(update_fields=['state']) if type == "工资/奖金变更": try: diff --git a/finance/migrations/0011_bonuschange_submitter.py b/finance/migrations/0011_bonuschange_submitter.py new file mode 100644 index 0000000..0402aa9 --- /dev/null +++ b/finance/migrations/0011_bonuschange_submitter.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.25 on 2026-01-08 10:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('finance', '0010_add_is_deleted_fields'), + ] + + operations = [ + migrations.AddField( + model_name='bonuschange', + name='submitter', + field=models.CharField(blank=True, max_length=100, null=True), + ), + ] diff --git a/finance/models.py b/finance/models.py index 2373339..78965f9 100644 --- a/finance/models.py +++ b/finance/models.py @@ -68,4 +68,5 @@ class BonusChange(models.Model): Instructions = models.TextField() # 调整说明 times = models.CharField(max_length=100) # 时间 state = models.CharField(max_length=100) # 状态 + submitter = models.CharField(max_length=100, null=True, blank=True) # 提交人 is_deleted = models.BooleanField(default=False) # 软删除标记 \ No newline at end of file diff --git a/finance/views.py b/finance/views.py index fed1aaf..4e62913 100644 --- a/finance/views.py +++ b/finance/views.py @@ -831,6 +831,7 @@ class loan(APIView): def post(self, request, *args, **kwargs): """ 调账申请 + 优化后:不需要指定审批人,直接抄送财务(数据来源案件管理) :param request: :param args: :param kwargs: @@ -841,20 +842,59 @@ class loan(APIView): CustomerID = request.data.get('CustomerID') amount = request.data.get('amount') situation = request.data.get('situation') - personincharge = request.data.get('personincharge') + case_id = request.data.get('case_id') # 案件ID(可选,用于关联案件) token = request.META.get('token') - if not all([times, ContractNo, amount, situation,CustomerID,personincharge]): - return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST) + + # 移除personincharge必填验证 + if not all([times, ContractNo, amount, situation, CustomerID]): + missing_params = [] + if not times: + missing_params.append('times(收款日期)') + if not ContractNo: + missing_params.append('ContractNo(合同号)') + if not CustomerID: + missing_params.append('CustomerID(客户名称)') + if not amount: + missing_params.append('amount(收款金额)') + if not situation: + missing_params.append('situation(情况说明)') + return Response({ + 'status': 'error', + 'message': f'缺少必填参数: {", ".join(missing_params)}', + 'code': 1 + }, status=status.HTTP_400_BAD_REQUEST) + from datetime import datetime now = datetime.now() - - # 格式化日期为字符串,格式为 YYYY-MM-DD date_string = now.strftime("%Y-%m-%d") + try: user = User.objects.get(token=token, is_deleted=False) except User.DoesNotExist: return Response({'status': 'error', 'message': '用户不存在或已被删除', 'code': 1}, status=status.HTTP_401_UNAUTHORIZED) + # 如果提供了案件ID,从案件表获取相关信息(可选) + from business.models import ProjectRegistration + case_info = None + if case_id: + try: + case = ProjectRegistration.objects.get(id=case_id, is_deleted=False) + case_info = { + 'id': case.id, + 'ContractNo': case.ContractNo, + 'responsiblefor': case.responsiblefor, + 'type': case.type + } + # 如果合同号未提供,从案件表同步 + if not ContractNo: + ContractNo = case.ContractNo + except ProjectRegistration.DoesNotExist: + return Response({ + 'status': 'error', + 'message': '案件不存在或已被删除', + 'code': 1 + }, status=status.HTTP_404_NOT_FOUND) + acc = Accounts.objects.create( times=times, ContractNo=ContractNo, @@ -863,18 +903,62 @@ class loan(APIView): situation=situation, submit=user.username, submit_tiem=date_string, - state="审核中" + state="待财务处理" # 直接设为待财务处理 ) + + # 构建审批内容,包含案件信息(如果提供了) + content_parts = [ + f"{user.username}在{times}提交了调账申请", + f"合同编号:{ContractNo}", + f"客户名称:{CustomerID}", + f"收入金额:{amount}", + f"情况说明:{situation}" + ] + if case_info: + content_parts.append(f"案件类型:{case_info['type']}") + content_parts.append(f"案件负责人:{case_info['responsiblefor']}") + content = ",".join(content_parts) + + # 直接抄送财务,不需要审批人 Approval.objects.create( title=user.username + "提交调账申请", - content=user.username + "在" + times + "提交了调账申请,合同编号:" + ContractNo + ",客户名称:" + CustomerID + "收入金额:" + amount, + content=content, times=date_string, - personincharge=format_personincharge(personincharge, is_department=False), # 审批员用户名 - state='审核中', + personincharge="财务", # 直接抄送财务 + state='已抄送财务', # 直接标记为已抄送财务 type="调账申请", user_id=acc.id ) - return Response({'message': '插入成功' ,'code': 0}, status=status.HTTP_200_OK) + + # 记录操作日志 + new_data = { + 'id': acc.id, + 'ContractNo': acc.ContractNo, + 'CustomerID': acc.CustomerID, + 'amount': acc.amount, + 'case_id': case_id if case_id else None + } + log_operation( + request=request, + operation_type='CREATE', + module='Finance', + action='新增调账申请', + target_type='Accounts', + target_id=acc.id, + target_name=f'{acc.ContractNo} - {acc.CustomerID}', + new_data=new_data, + remark=f'新增调账申请:合同号 {acc.ContractNo},金额 {acc.amount},已直接抄送财务' + ) + + return Response({ + 'message': '提交成功', + 'code': 0, + 'data': { + 'id': acc.id, + 'ContractNo': acc.ContractNo, + 'state': acc.state + } + }, status=status.HTTP_200_OK) class loandisplay(APIView): def post(self, request, *args, **kwargs): @@ -1160,7 +1244,10 @@ class DeletePayment(APIView): class reimbursement(APIView): def post(self, request, *args, **kwargs): """ - 报销 + 报销申请 + 优化后:根据团队类型判断是否需要审批 + - 个人团队(personal/独立律师):不触发审批,直接抄送财务 + - 团队(team/团队律师):需要多级审批,按顺序依次审批,最后抄送财务 :param request: :param args: :param kwargs: @@ -1171,30 +1258,148 @@ class reimbursement(APIView): reason = request.data.get('reason') amount = request.data.get('amount') FeeDescription = request.data.get('FeeDescription') - personincharge = request.data.get('personincharge') - if not all([person,times, reason, amount, FeeDescription, personincharge]): - return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST) + approvers = request.data.get('approvers') # 审批人列表(数组,仅团队类型需要) + token = request.META.get('token') + + if not all([person, times, reason, amount, FeeDescription]): + missing_params = [] + if not person: + missing_params.append('person(报销人)') + if not times: + missing_params.append('times(报销日期)') + if not reason: + missing_params.append('reason(报销理由)') + if not amount: + missing_params.append('amount(报销金额)') + if not FeeDescription: + missing_params.append('FeeDescription(费用说明)') + return Response({ + 'status': 'error', + 'message': f'缺少必填参数: {", ".join(missing_params)}', + 'code': 1 + }, status=status.HTTP_400_BAD_REQUEST) + + # 获取报销人的团队信息 + try: + submitter_user = User.objects.get(username=person, is_deleted=False) + except User.DoesNotExist: + return Response({ + 'status': 'error', + 'message': f'报销人"{person}"不存在或已被删除', + 'code': 1 + }, status=status.HTTP_404_NOT_FOUND) + + from User.models import Team + team_name = submitter_user.team + team = None + if team_name: + try: + team = Team.objects.get(name=team_name, is_deleted=False) + except Team.DoesNotExist: + pass + from datetime import datetime now = datetime.now() + date_string = now.strftime("%Y-%m-%d") + reim = Reimbursement.objects.create( person=person, times=times, reason=reason, amount=amount, FeeDescription=FeeDescription, - submit_tiem=now.strftime("%Y-%m-%d"), - state="审核中" + submit_tiem=date_string, + state="审核中" if (team and team.team_type == 'team') else "待财务处理" ) - Approval.objects.create( - title=person + "报销申请", - content=person + "在" + times + "提交了报销申请,报销理由:" + reason + ",付款金额:" + amount + ",付款日期:" + times + ",费用说明:" + FeeDescription, - times=times, - personincharge=format_personincharge(personincharge, is_department=False), # 审批员用户名 - state='审核中', - type="报销申请", - user_id=reim.id + + # 根据团队类型判断是否需要审批 + if team and team.team_type == 'personal': + # 个人团队(独立律师):不触发审批,直接抄送财务 + Approval.objects.create( + title=person + "报销申请", + content=f"{person}在{times}提交了报销申请,报销理由:{reason},付款金额:{amount},付款日期:{times},费用说明:{FeeDescription}", + times=date_string, + personincharge="财务", # 直接抄送财务 + state='已抄送财务', # 直接标记为已抄送财务 + type="报销申请", + user_id=reim.id + ) + reim.state = "待财务处理" + reim.save(update_fields=['state']) + else: + # 团队类型(团队律师):需要多级审批 + if not approvers: + return Response({ + 'status': 'error', + 'message': '团队类型需要指定审批人,请提供审批人列表', + 'code': 1 + }, status=status.HTTP_400_BAD_REQUEST) + + # 验证审批人列表 + if not isinstance(approvers, list) or len(approvers) == 0: + return Response({ + 'status': 'error', + 'message': '审批人列表不能为空,请提供至少一个审批人', + 'code': 1 + }, status=status.HTTP_400_BAD_REQUEST) + + # 验证所有审批人是否存在 + valid_approvers = [] + for approver_name in approvers: + try: + approver = User.objects.get(username=approver_name, is_deleted=False) + valid_approvers.append(approver_name) + except User.DoesNotExist: + return Response({ + 'status': 'error', + 'message': f'审批人"{approver_name}"不存在或已被删除', + 'code': 1 + }, status=status.HTTP_404_NOT_FOUND) + + # 创建审批记录,personincharge存储当前审批人(第一个) + # 所有审批人信息存储在content中,用于后续流转 + all_approvers_str = ','.join(valid_approvers) + current_approver = valid_approvers[0] # 第一个审批人 + + Approval.objects.create( + title=person + "报销申请", + content=f"{person}在{times}提交了报销申请,报销理由:{reason},付款金额:{amount},付款日期:{times},费用说明:{FeeDescription}。审批流程:{all_approvers_str}(按顺序审批),当前审批人:{current_approver}", + times=date_string, + personincharge=current_approver, # 当前审批人(第一个) + state='审核中', + type="报销申请", + user_id=reim.id + ) + + # 记录操作日志 + new_data = { + 'id': reim.id, + 'person': reim.person, + 'amount': reim.amount, + 'team_type': team.team_type if team else 'unknown', + 'approvers': approvers if (team and team.team_type == 'team') else None + } + log_operation( + request=request, + operation_type='CREATE', + module='Finance', + action='新增报销申请', + target_type='Reimbursement', + target_id=reim.id, + target_name=f'{reim.person} - {reim.amount}', + new_data=new_data, + remark=f'新增报销申请:{reim.person},金额 {reim.amount},团队类型 {team.team_type if team else "未知"}' ) - return Response({'message': '插入成功', 'code': 0}, status=status.HTTP_200_OK) + + return Response({ + 'message': '提交成功', + 'code': 0, + 'data': { + 'id': reim.id, + 'state': reim.state, + 'needs_approval': team is None or team.team_type != 'personal' + } + }, status=status.HTTP_200_OK) class reimbursementdetail(APIView): def post(self, request, *args, **kwargs): @@ -1312,6 +1517,7 @@ class Change(APIView): def post(self, request, *args, **kwargs): """ 工资/奖金变更 + 优化后:增加提交人字段,明确是谁提交的 :param request: :param args: :param kwargs: @@ -1321,9 +1527,20 @@ class Change(APIView): type = request.data.get('type') Instructions = request.data.get('Instructions') personincharge = request.data.get('personincharge') + token = request.META.get('token') if not all([username, type, Instructions]): return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST) + + # 获取提交人信息 + submitter = '未知用户' + try: + user = User.objects.get(token=token, is_deleted=False) + submitter = user.username + except User.DoesNotExist: + # 如果token无效,使用username作为提交人(兼容旧逻辑) + submitter = username + from datetime import datetime now = datetime.now() bonus = BonusChange.objects.create( @@ -1331,18 +1548,46 @@ class Change(APIView): type=type, Instructions=Instructions, times=now.strftime("%Y-%m-%d"), - state="审核中" + state="审核中", + submitter=submitter # 记录提交人 ) Approval.objects.create( title=username + "工资/奖金变更", - content=username + "在" + now.strftime('%Y-%m-%d"') + "提交了工资/奖金变更,类型:" + type + ",调整说明:" + Instructions, + content=f"{submitter}在{now.strftime('%Y-%m-%d')}提交了工资/奖金变更,类型:{type},调整说明:{Instructions}", times=now.strftime("%Y-%m-%d"), personincharge=format_personincharge(personincharge, is_department=False), # 审批员用户名 state='审核中', type="工资/奖金变更", user_id=bonus.id ) - return Response({'message': '插入成功', 'code': 0}, status=status.HTTP_200_OK) + + # 记录操作日志 + new_data = { + 'id': bonus.id, + 'username': bonus.username, + 'type': bonus.type, + 'submitter': bonus.submitter + } + log_operation( + request=request, + operation_type='CREATE', + module='Finance', + action='新增工资/奖金变更', + target_type='BonusChange', + target_id=bonus.id, + target_name=f'{bonus.username} - {bonus.type}', + new_data=new_data, + remark=f'新增工资/奖金变更:{bonus.username},类型 {bonus.type},提交人 {bonus.submitter}' + ) + + return Response({ + 'message': '插入成功', + 'code': 0, + 'data': { + 'id': bonus.id, + 'submitter': bonus.submitter + } + }, status=status.HTTP_200_OK) class ChangeDetail(APIView): def post(self, request, *args, **kwargs): @@ -1377,6 +1622,7 @@ class ChangeDetail(APIView): "type": info.type, "Instructions": info.Instructions, "username": info.username, + "submitter": info.submitter if info.submitter else info.username, # 提交人,如果没有则使用username } data.append(itme) return Response({'message': '展示成功', "total": total, 'data': data, 'code': 0}, status=status.HTTP_200_OK) diff --git a/系统变更需求14-16接口文档.md b/系统变更需求14-16接口文档.md new file mode 100644 index 0000000..977814a --- /dev/null +++ b/系统变更需求14-16接口文档.md @@ -0,0 +1,372 @@ +# 系统变更需求14-16接口文档 + +## 需求概述 + +本文档包含以下三个需求的接口优化说明: +- **需求14**:调账申请不需要指定审批人直接抄送财务(数据来源案件管理) +- **需求15**:报销审批功能修改,支持多级审批和团队类型判断 +- **需求16**:工资奖金变更增加提交人字段 + +--- + +## 需求14:调账申请优化 + +### 接口信息 + +- **URL**:`/finance/loan` +- **方法**:POST +- **功能**:调账申请 + +### 优化内容 + +1. **移除审批人参数**:不再需要指定 `personincharge` +2. **直接抄送财务**:创建审批记录时,`personincharge` 设为"财务",状态设为"已抄送财务" +3. **支持案件管理**:可选参数 `case_id`,从案件表获取相关信息 + +### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| times | string | 是 | 收款日期 | +| ContractNo | string | 是 | 合同号 | +| CustomerID | string | 是 | 客户名称 | +| amount | string | 是 | 收款金额 | +| situation | string | 是 | 情况说明 | +| case_id | int | 否 | 案件ID(可选,用于关联案件) | +| token | string | 是 | 用户Token | + +### 请求示例 + +```json +POST /finance/loan +{ + "times": "2024-01-15", + "ContractNo": "HT2024001", + "CustomerID": "XX公司", + "amount": "100000", + "situation": "调账说明", + "case_id": 1 +} +``` + +### 响应示例 + +```json +{ + "message": "提交成功", + "code": 0, + "data": { + "id": 123, + "ContractNo": "HT2024001", + "state": "待财务处理" + } +} +``` + +### 审批记录 + +- **personincharge**:`"财务"` +- **state**:`"已抄送财务"` +- **说明**:不需要审批流程,直接抄送财务处理 + +--- + +## 需求15:报销审批优化 + +### 接口信息 + +- **URL**:`/finance/reimbursement` +- **方法**:POST +- **功能**:报销申请 + +### 优化内容 + +1. **根据团队类型判断是否需要审批**: + - 个人团队(personal/独立律师):不触发审批,直接抄送财务 + - 团队(team/团队律师):需要多级审批,按顺序依次审批 + +2. **多级审批流程**: + - 支持多个审批人按顺序审批 + - 第一个审批人审批通过后,自动流转到下一个 + - 最后一个审批人审批通过后,抄送财务 + +### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| person | string | 是 | 报销人 | +| times | string | 是 | 报销日期 | +| reason | string | 是 | 报销理由 | +| amount | string | 是 | 报销金额 | +| FeeDescription | string | 是 | 费用说明 | +| approvers | array | 条件必填 | 审批人列表(仅团队类型必填) | + +### 请求示例 + +#### 个人团队(不需要审批人) + +```json +POST /finance/reimbursement +{ + "person": "张三", + "times": "2024-01-15", + "reason": "差旅费", + "amount": "5000", + "FeeDescription": "出差住宿和交通费用" +} +``` + +#### 团队(需要审批人列表) + +```json +POST /finance/reimbursement +{ + "person": "李四", + "times": "2024-01-15", + "reason": "差旅费", + "amount": "5000", + "FeeDescription": "出差住宿和交通费用", + "approvers": ["王五", "赵六", "孙七"] +} +``` + +**说明**: +- `approvers` 是数组,按顺序排列 +- 第一个审批人先审批,通过后流转到第二个,以此类推 +- 最后一个审批人审批通过后,抄送财务 + +### 响应示例 + +```json +{ + "message": "提交成功", + "code": 0, + "data": { + "id": 123, + "state": "审核中", + "needs_approval": true + } +} +``` + +### 审批流程 + +#### 个人团队流程 +``` +提交报销申请 + ↓ +直接抄送财务 + ↓ +状态:待财务处理 +``` + +#### 团队流程(多级审批) +``` +提交报销申请 + ↓ +创建审批记录(当前审批人:第一个) + ↓ +第一个审批人审批通过 + ↓ +流转到第二个审批人 + ↓ +第二个审批人审批通过 + ↓ +流转到第三个审批人 + ↓ +...(依次流转) + ↓ +最后一个审批人审批通过 + ↓ +抄送财务 + ↓ +状态:待财务处理 +``` + +### 审批处理接口 + +- **URL**:`/user/approval_processing` +- **方法**:POST + +#### 审批请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | int | 是 | 审批记录ID | +| type | string | 是 | 审批类型("报销申请") | +| state | string | 是 | 审批状态("已通过"或"未通过") | + +#### 审批请求示例 + +```json +POST /user/approval_processing +{ + "id": 123, + "type": "报销申请", + "state": "已通过" +} +``` + +#### 审批流转逻辑 + +1. **审批通过**: + - 检查是否还有下一个审批人 + - 如果有:更新 `personincharge` 为下一个审批人,状态保持"审核中" + - 如果没有:更新 `personincharge` 为"财务",状态改为"已通过",报销申请状态改为"待财务处理" + +2. **审批不通过**: + - 审批状态改为"未通过" + - 报销申请状态改为"未通过" + - 终止审批流程 + +### 错误响应 + +#### 团队类型未提供审批人 +```json +{ + "status": "error", + "message": "团队类型需要指定审批人,请提供审批人列表", + "code": 1 +} +``` + +#### 审批人列表为空 +```json +{ + "status": "error", + "message": "审批人列表不能为空,请提供至少一个审批人", + "code": 1 +} +``` + +#### 审批人不存在 +```json +{ + "status": "error", + "message": "审批人\"王五\"不存在或已被删除", + "code": 1 +} +``` + +--- + +## 需求16:工资奖金变更优化 + +### 接口信息 + +- **URL**:`/finance/change` +- **方法**:POST +- **功能**:工资/奖金变更 + +### 优化内容 + +1. **新增提交人字段**:在 `BonusChange` 模型中添加 `submitter` 字段 +2. **自动记录提交人**:从Token获取当前登录用户,自动记录为提交人 +3. **返回提交人信息**:在查询接口中返回提交人字段 + +### 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| username | string | 是 | 用户名(变更对象) | +| type | string | 是 | 变更类型 | +| Instructions | string | 是 | 调整说明 | +| personincharge | string | 是 | 审批人 | +| token | string | 是 | 用户Token(用于获取提交人) | + +### 请求示例 + +```json +POST /finance/change +{ + "username": "张三", + "type": "工资调整", + "Instructions": "根据绩效考核调整工资", + "personincharge": "李四" +} +``` + +### 响应示例 + +```json +{ + "message": "插入成功", + "code": 0, + "data": { + "id": 123, + "submitter": "王五" + } +} +``` + +### 查询接口 + +- **URL**:`/finance/ChangeDetail` +- **方法**:POST + +#### 响应示例 + +```json +{ + "message": "展示成功", + "total": 10, + "data": [ + { + "id": 1, + "times": "2024-01-15", + "type": "工资调整", + "Instructions": "根据绩效考核调整工资", + "username": "张三", + "submitter": "王五" + } + ], + "code": 0 +} +``` + +**说明**: +- `submitter` 字段显示是谁提交的变更申请 +- 如果 `submitter` 为空,则使用 `username` 作为提交人(兼容旧数据) + +### 模型变更 + +在 `BonusChange` 模型中新增字段: + +```python +submitter = models.CharField(max_length=100, null=True, blank=True) # 提交人 +``` + +**注意**:需要执行数据库迁移: +```bash +python manage.py makemigrations +python manage.py migrate +``` + +--- + +## 总结 + +### 需求14:调账申请 +- ✅ 移除审批人参数 +- ✅ 直接抄送财务 +- ✅ 支持案件管理数据来源 + +### 需求15:报销审批 +- ✅ 根据团队类型判断是否需要审批 +- ✅ 个人团队:直接抄送财务 +- ✅ 团队:支持多级审批,按顺序流转 +- ✅ 最后一个审批人审批通过后抄送财务 + +### 需求16:工资奖金变更 +- ✅ 新增提交人字段 +- ✅ 自动记录提交人 +- ✅ 查询接口返回提交人信息 + +--- + +## 更新日志 + +### v1.0.0 (2024-01-XX) +- ✅ 需求14:调账申请优化完成 +- ✅ 需求15:报销审批多级审批功能完成 +- ✅ 需求16:工资奖金变更提交人字段完成