diff --git a/User/urls.py b/User/urls.py
index 8d6dc9f..306b48d 100644
--- a/User/urls.py
+++ b/User/urls.py
@@ -1,5 +1,5 @@
from django.urls import path
-from .views import CreateUserView,LoginView,EditorialStaffView,PersonnelDetailsView,DepartmentView,PersonnelListView,AddDepartment,DeleteDepartment,Personlist,roxyExhibition,approvalProcessing,personneldisplay,DeleteUser,OperationLogView,TeamView,TeamListView,AddTeam,EditTeam,DeleteTeam,ChangePasswordView
+from .views import CreateUserView,LoginView,EditorialStaffView,PersonnelDetailsView,DepartmentView,PersonnelListView,AddDepartment,DeleteDepartment,Personlist,roxyExhibition,approvalProcessing,personneldisplay,DeleteUser,OperationLogView,TeamView,TeamListView,AddTeam,EditTeam,DeleteTeam,ChangePasswordView,ApprovalStatusCheck
urlpatterns = [
path('create-user',CreateUserView.as_view(),name='create-user'),
path('login',LoginView.as_view(),name='login'),
@@ -12,6 +12,7 @@ urlpatterns = [
path('personlist',Personlist.as_view(),name='Personlist'),
path('roxyexhibition',roxyExhibition.as_view(),name='roxyExhibition'),
path('approval_processing',approvalProcessing.as_view(),name='approval_processing'),
+ path('approval-status-check',ApprovalStatusCheck.as_view(),name='approval-status-check'),
path('personneldisplay',personneldisplay.as_view(),name='personneldisplay'),
path('deleteUser',DeleteUser.as_view(),name='deleteUser'),
path('operation-log',OperationLogView.as_view(),name='operation-log'),
diff --git a/User/views.py b/User/views.py
index 4658479..2fd76b0 100644
--- a/User/views.py
+++ b/User/views.py
@@ -1048,6 +1048,7 @@ class approvalProcessing(APIView):
def post(self, request, *args, **kwargs):
"""
消除代办
+ 财务查看时,只需要传type和id,不需要传state(默认为查看通过)
:param request:
:param args:
:param kwargs:
@@ -1057,14 +1058,21 @@ class approvalProcessing(APIView):
type = request.data.get('type')
id = request.data.get('id')
- if not all([state, type, id]):
- return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
+ if not all([type, id]):
+ return Response({'status': 'error', 'message': '缺少参数type或id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
approval = Approval.objects.get(id=id, is_deleted=False)
except Approval.DoesNotExist:
return Response({'status': 'error', 'message': '审批记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
+ # 财务查看逻辑:如果只传了type和id,没有传state,且当前是财务审核,则默认为查看通过
+ from User.utils import is_finance_personincharge
+ is_finance_view = False
+ if not state and is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
+ state = "已通过" # 财务查看默认通过
+ is_finance_view = True
+
if type == "入职财务登记":
try:
user = User.objects.get(id=approval.user_id, is_deleted=False)
@@ -1073,7 +1081,10 @@ class approvalProcessing(APIView):
# 检查当前是否已经是财务审核
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
- # 财务部审核逻辑:财务部只需要一个人审核完即可完成
+ # 财务部审核逻辑:如果只传了type和id,不传state,则默认为"已通过"
+ if not state:
+ state = "已通过"
+
if state == "已通过":
approval.state = "已通过"
user.state = "在职"
@@ -1085,6 +1096,10 @@ class approvalProcessing(APIView):
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 使用统一的审核流程处理函数
+ # 非财务查看时,state参数是必填的
+ if not state:
+ return Response({'status': 'error', 'message': '缺少参数state(审核状态:已通过/未通过)', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
+
from User.utils import process_approval_flow
import logging
logger = logging.getLogger(__name__)
@@ -1124,7 +1139,10 @@ class approvalProcessing(APIView):
# 检查当前是否已经是财务审核
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
- # 财务部审核逻辑:财务部只需要一个人审核完即可完成
+ # 财务部审核逻辑:如果只传了type和id,不传state,则默认为"已通过"
+ if not state:
+ state = "已通过"
+
if state == "已通过":
approval.state = "已通过"
invoice.state = "已通过"
@@ -1136,6 +1154,10 @@ class approvalProcessing(APIView):
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 使用统一的审核流程处理函数
+ # 非财务查看时,state参数是必填的
+ if not state:
+ return Response({'status': 'error', 'message': '缺少参数state(审核状态:已通过/未通过)', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
+
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
@@ -1169,7 +1191,11 @@ class approvalProcessing(APIView):
approval.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
else:
- # 财务查看通过,状态保持"已通过"(已经是"已通过")
+ # 财务查看通过(默认或不传state),状态保持"已通过"(已经是"已通过")
+ # 如果审批记录状态不是"已通过",更新为"已通过"(表示财务已查看)
+ if approval.state != "已通过":
+ approval.state = "已通过"
+ approval.save(update_fields=['state'])
return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK)
# 负责人填写收入分配
@@ -1229,6 +1255,10 @@ class approvalProcessing(APIView):
income.save(update_fields=['allocate'])
# 使用统一的审核流程处理函数(兼容旧流程)
+ # 非财务查看时,state参数是必填的
+ if not state:
+ return Response({'status': 'error', 'message': '缺少参数state(审核状态:已通过/未通过)', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
+
from User.utils import process_approval_flow
current_approver = approval.personincharge
is_completed, error = process_approval_flow(
@@ -2017,6 +2047,92 @@ class DeleteTeam(APIView):
return Response({'message': '删除成功', 'code': 0}, status=status.HTTP_200_OK)
+class ApprovalStatusCheck(APIView):
+ """查询待办是否已经审核完全通过"""
+
+ def post(self, request, *args, **kwargs):
+ """
+ 查询待办的审核状态,判断是否已经审核完全通过
+ :param request:
+ :param args:
+ :param kwargs:
+ :return:
+ """
+ type = request.data.get('type')
+ id = request.data.get('id')
+
+ if not all([type, id]):
+ return Response({'status': 'error', 'message': '缺少参数type或id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
+
+ try:
+ approval = Approval.objects.get(id=id, is_deleted=False)
+ except Approval.DoesNotExist:
+ return Response({'status': 'error', 'message': '审批记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
+
+ # 获取业务记录状态
+ business_state = None
+ is_approved = False
+
+ try:
+ if type == "收入确认":
+ from finance.models import Income
+ try:
+ income = Income.objects.get(id=approval.user_id, is_deleted=False)
+ business_state = income.state
+ is_approved = (income.state == "已通过")
+ except Income.DoesNotExist:
+ pass
+ elif type == "开票":
+ from finance.models import Invoice
+ try:
+ invoice = Invoice.objects.get(id=approval.user_id, is_deleted=False)
+ business_state = invoice.state
+ is_approved = (invoice.state == "已通过")
+ except Invoice.DoesNotExist:
+ pass
+ elif type == "付款申请":
+ from finance.models import Payment
+ try:
+ payment = Payment.objects.get(id=approval.user_id, is_deleted=False)
+ business_state = payment.state
+ is_approved = (payment.state == "已通过")
+ except Payment.DoesNotExist:
+ pass
+ elif type == "报销申请":
+ from finance.models import Reimbursement
+ try:
+ reimbursement = Reimbursement.objects.get(id=approval.user_id, is_deleted=False)
+ business_state = reimbursement.state
+ is_approved = (reimbursement.state == "已完成")
+ except Reimbursement.DoesNotExist:
+ pass
+ elif type == "案件管理":
+ from business.models import Case
+ try:
+ case = Case.objects.get(id=approval.user_id, is_deleted=False)
+ business_state = case.state
+ is_approved = (case.state == "已通过")
+ except Case.DoesNotExist:
+ pass
+ # 可以根据需要添加其他类型
+ except Exception as e:
+ import logging
+ logger = logging.getLogger(__name__)
+ logger.error(f"查询业务记录状态失败: {str(e)}")
+
+ return Response({
+ 'message': '查询成功',
+ 'code': 0,
+ 'data': {
+ 'approval_id': approval.id,
+ 'approval_state': approval.state,
+ 'business_state': business_state,
+ 'is_approved': is_approved, # 是否已经审核完全通过
+ 'type': type
+ }
+ }, status=status.HTTP_200_OK)
+
+
class ChangePasswordView(APIView):
"""修改密码接口"""
diff --git a/财务查看功能使用说明.md b/财务查看功能使用说明.md
new file mode 100644
index 0000000..f3a6db8
--- /dev/null
+++ b/财务查看功能使用说明.md
@@ -0,0 +1,513 @@
+# 财务查看功能使用说明
+
+## 概述
+
+收入确认在抄送财务时,状态已经是"已通过",财务只是查看,不需要再次审核。如果财务发现问题,可以标记为"未通过"。
+
+## 审批接口
+
+**接口地址:** `POST /user/approval_processing`
+
+**接口描述:** 处理审批待办,支持审核通过、审核未通过等操作。
+
+### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| token | String | 是 | 用户认证token(请求头) |
+| id | Integer | 是 | 审批记录ID(从待办列表获取) |
+| type | String | 是 | 审批类型,收入确认为 `"收入确认"` |
+| state | String | 否 | 审核状态:`"已通过"` 或 `"未通过"`
**财务查看时可不传**(默认为"已通过")
**非财务审核时必填** |
+| allocate | String | 否 | 收入分配(仅当状态为"待负责人填写分配"时必填) |
+
+### 请求示例
+
+#### 1. 财务查看收入确认(默认通过,推荐方式)
+
+```json
+{
+ "id": 10,
+ "type": "收入确认"
+}
+```
+
+**说明:**
+- 财务查看时,只需要传 `id` 和 `type`,不需要传 `state`
+- 系统会自动判断:如果是财务查看(`personincharge` 为财务且 `state` 为"已抄送财务"),默认 `state` 为 `"已通过"`
+- 接口会直接返回成功,状态保持"已通过"(因为已经是"已通过")
+
+#### 2. 财务标记为未通过(发现问题)
+
+```json
+{
+ "id": 10,
+ "type": "收入确认",
+ "state": "未通过"
+}
+```
+
+**说明:**
+- 如果财务发现问题,需要传 `state: "未通过"`
+- 接口会将收入确认状态更新为"未通过"
+- 审批记录状态也会更新为"未通过"
+
+#### 3. 负责人填写收入分配
+
+```json
+{
+ "id": 10,
+ "type": "收入确认",
+ "state": "已通过",
+ "allocate": "50%分配给A,50%分配给B"
+}
+```
+
+**说明:**
+- 当收入确认状态为"待负责人填写分配"时,负责人必须填写 `allocate` 字段
+- 填写后,状态自动变为"已通过",并抄送财务
+- 财务收到时状态已经是"已通过",财务只是查看
+
+### 返回数据
+
+**成功响应:**
+```json
+{
+ "message": "处理成功",
+ "code": 0
+}
+```
+
+**失败响应:**
+```json
+{
+ "status": "error",
+ "message": "收入确认记录不存在或已被删除",
+ "code": 1
+}
+```
+
+---
+
+## 查询待办审核状态
+
+**接口地址:** `POST /user/approval-status-check`
+
+**接口描述:** 查询待办是否已经审核完全通过,用于前端判断待办状态。
+
+### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| token | String | 是 | 用户认证token(请求头) |
+| id | Integer | 是 | 审批记录ID |
+| type | String | 是 | 审批类型,如 `"收入确认"`、`"开票"` 等 |
+
+### 请求示例
+
+```json
+{
+ "id": 10,
+ "type": "收入确认"
+}
+```
+
+### 返回数据
+
+**成功响应:**
+```json
+{
+ "message": "查询成功",
+ "code": 0,
+ "data": {
+ "approval_id": 10,
+ "approval_state": "已抄送财务",
+ "business_state": "已通过",
+ "is_approved": true,
+ "type": "收入确认"
+ }
+}
+```
+
+### 字段说明
+
+- **approval_id**:审批记录ID
+- **approval_state**:审批记录状态(审核中/已抄送财务/已通过/未通过)
+- **business_state**:业务记录状态(如收入确认的状态)
+- **is_approved**:是否已经审核完全通过
+ - `true`:业务记录状态为"已通过"(或"已完成")
+ - `false`:业务记录状态不是"已通过"
+- **type**:审批类型
+
+---
+
+## 获取待办列表
+
+**接口地址:** `POST /user/roxyexhibition`
+
+**接口描述:** 获取当前用户的待办列表,包括收入确认的待办事项。
+
+### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| token | String | 是 | 用户认证token(请求头) |
+| page | Integer | 是 | 页码(从1开始) |
+| per_page | Integer | 是 | 每页数量 |
+| type | String | 否 | 筛选类型,如 `"收入确认"` |
+
+### 请求示例
+
+```json
+{
+ "page": 1,
+ "per_page": 10,
+ "type": "收入确认"
+}
+```
+
+### 返回数据
+
+**成功响应:**
+```json
+{
+ "message": "展示成功",
+ "total": 5,
+ "code": 0,
+ "data": [
+ {
+ "id": 10,
+ "title": "张三提交收入确认",
+ "content": "张三在2024-01-15提交了收入确认,合同编号:HT2024001,客户名称:某某公司,收款金额:10000,收入分配:待负责人指定,已抄送财务部",
+ "times": "2024-01-15",
+ "personincharge": "财务",
+ "state": "已抄送财务",
+ "type": "收入确认",
+ "user_id": "1"
+ }
+ ]
+}
+```
+
+### 字段说明
+
+- **id**:审批记录ID,用于调用审批处理接口
+- **type**:审批类型,收入确认为 `"收入确认"`
+- **state**:审批状态
+ - `"审核中"`:正在审核中
+ - `"已抄送财务"`:已抄送财务,财务可以查看
+ - `"已通过"`:审核通过
+ - `"未通过"`:审核未通过
+- **personincharge**:当前负责人
+ - 如果是财务部门ID或"财务",表示已抄送财务
+- **user_id**:关联的业务记录ID(收入确认记录ID)
+
+---
+
+## 财务查看流程
+
+### 场景1:个人团队提交的收入确认
+
+1. **提交阶段**:
+ - 用户提交收入确认
+ - 状态直接设为 `"已通过"`
+ - 直接抄送财务,审批状态为 `"已抄送财务"`
+
+2. **财务查看**:
+ - 财务在待办列表中看到该收入确认
+ - 调用审批接口,`state` 传 `"已通过"`(表示查看通过)
+ - 接口直接返回成功,状态保持 `"已通过"`
+
+3. **财务发现问题**:
+ - 如果财务发现问题,调用审批接口,`state` 传 `"未通过"`
+ - 收入确认状态更新为 `"未通过"`
+ - 审批记录状态也更新为 `"未通过"`
+
+### 场景2:团队类型提交的收入确认
+
+1. **提交阶段**:
+ - 用户提交收入确认
+ - 状态为 `"审核中"`
+ - 创建待办事项,第一个审核人审核
+
+2. **审核阶段**:
+ - 审核人依次审核(按顺序)
+ - 所有审核人通过后,状态变为 `"待负责人填写分配"`
+
+3. **负责人填写分配**:
+ - 负责人调用审批接口,填写 `allocate` 字段
+ - 状态自动变为 `"已通过"`
+ - 抄送财务,审批状态为 `"已抄送财务"`
+
+4. **财务查看**:
+ - 财务在待办列表中看到该收入确认
+ - 调用审批接口,`state` 传 `"已通过"`(表示查看通过)
+ - 接口直接返回成功,状态保持 `"已通过"`
+
+---
+
+## 代码示例
+
+### 前端调用示例(JavaScript)
+
+```javascript
+// 1. 获取待办列表
+async function getTodoList(page = 1, perPage = 10) {
+ const response = await fetch('/user/roxyexhibition', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'token': localStorage.getItem('token')
+ },
+ body: JSON.stringify({
+ page: page,
+ per_page: perPage,
+ type: '收入确认' // 可选,筛选收入确认类型
+ })
+ });
+ const data = await response.json();
+ return data;
+}
+
+// 2. 查询待办审核状态(判断是否已审核通过)
+async function checkApprovalStatus(approvalId, type = '收入确认') {
+ const response = await fetch('/user/approval-status-check', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'token': localStorage.getItem('token')
+ },
+ body: JSON.stringify({
+ id: approvalId,
+ type: type
+ })
+ });
+ const data = await response.json();
+ return data;
+}
+
+// 3. 财务查看通过(默认操作,推荐方式)
+async function financeViewPass(approvalId, type = '收入确认') {
+ const response = await fetch('/user/approval_processing', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'token': localStorage.getItem('token')
+ },
+ body: JSON.stringify({
+ id: approvalId,
+ type: type
+ // 不传state,财务查看时默认为"已通过"
+ })
+ });
+ const data = await response.json();
+ return data;
+}
+
+// 4. 财务标记为未通过(发现问题)
+async function financeMarkReject(approvalId, type = '收入确认') {
+ const response = await fetch('/user/approval_processing', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'token': localStorage.getItem('token')
+ },
+ body: JSON.stringify({
+ id: approvalId,
+ type: type,
+ state: '未通过' // 财务发现问题,标记为未通过
+ })
+ });
+ const data = await response.json();
+ return data;
+}
+
+// 5. 负责人填写收入分配
+async function fillAllocate(approvalId, allocate) {
+ const response = await fetch('/user/approval_processing', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'token': localStorage.getItem('token')
+ },
+ body: JSON.stringify({
+ id: approvalId,
+ type: '收入确认',
+ state: '已通过',
+ allocate: allocate // 收入分配,如:"50%分配给A,50%分配给B"
+ })
+ });
+ const data = await response.json();
+ return data;
+}
+```
+
+---
+
+## 状态说明
+
+### 收入确认状态(Income.state)
+
+| 状态 | 说明 | 何时出现 |
+|------|------|----------|
+| `"审核中"` | 正在审核中 | 团队类型,审核人正在审核 |
+| `"待负责人填写分配"` | 待负责人填写收入分配 | 所有审核人通过后 |
+| `"已通过"` | 审核通过 | 抄送财务时就已经是"已通过",财务只是查看 |
+| `"未通过"` | 审核未通过 | 审核人未通过或财务发现问题 |
+
+### 审批记录状态(Approval.state)
+
+| 状态 | 说明 | 何时出现 |
+|------|------|----------|
+| `"审核中"` | 正在审核中 | 审核人正在审核 |
+| `"已抄送财务"` | 已抄送财务 | 抄送财务时(收入确认状态已经是"已通过") |
+| `"已通过"` | 审批通过 | 财务查看通过或审批完成 |
+| `"未通过"` | 审批未通过 | 审核人未通过或财务标记为未通过 |
+
+---
+
+## 注意事项
+
+1. **财务查看逻辑**:
+ - 抄送财务时,收入确认状态已经是 `"已通过"`
+ - 财务调用审批接口时,`state` 传 `"已通过"` 表示查看通过,不会改变状态
+ - 只有财务标记为 `"未通过"` 时,才会改变状态
+
+2. **权限判断**:
+ - 接口会自动判断当前用户是否是财务部门
+ - 只有财务部门的人员才能看到 `personincharge` 为财务的待办
+
+3. **状态流转**:
+ - 个人团队:提交 → `"已通过"` → 抄送财务
+ - 团队类型:提交 → `"审核中"` → `"待负责人填写分配"` → `"已通过"` → 抄送财务
+
+4. **金额统计**:
+ - 只有状态为 `"已通过"` 的收入确认才会计入金额统计
+ - 抄送财务时已经是 `"已通过"`,所以会立即计入统计
+
+---
+
+## 完整流程示例
+
+### 示例1:个人团队提交收入确认
+
+```javascript
+// 1. 用户提交收入确认
+const submitResponse = await fetch('/finance/confirm', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'token': userToken
+ },
+ body: JSON.stringify({
+ case_id: 123,
+ times: '2024-01-15',
+ amount: '10000'
+ })
+});
+
+// 返回:{ state: "已通过", approval_id: 10 }
+
+// 2. 财务获取待办列表
+const todoList = await getTodoList(1, 10);
+// 财务看到:{ id: 10, type: "收入确认", state: "已抄送财务", personincharge: "财务" }
+
+// 3. 财务查看通过
+const viewResponse = await financeViewPass(10);
+// 返回:{ message: "处理成功", code: 0 }
+// 收入确认状态保持 "已通过"
+```
+
+### 示例2:团队类型提交收入确认
+
+```javascript
+// 1. 用户提交收入确认
+const submitResponse = await fetch('/finance/confirm', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'token': userToken
+ },
+ body: JSON.stringify({
+ case_id: 123,
+ times: '2024-01-15',
+ amount: '10000',
+ approvers: [1, 2, 3] // 审核人列表
+ })
+});
+
+// 返回:{ state: "审核中", approval_id: 10 }
+
+// 2. 审核人1审核通过
+await fetch('/user/approval_processing', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'token': approver1Token
+ },
+ body: JSON.stringify({
+ id: 10,
+ type: '收入确认',
+ state: '已通过'
+ })
+});
+
+// 3. 审核人2审核通过
+// ...(同上)
+
+// 4. 审核人3审核通过后,状态变为 "待负责人填写分配"
+
+// 5. 负责人填写收入分配
+await fillAllocate(10, '50%分配给A,50%分配给B');
+// 返回:{ state: "已通过" },已抄送财务
+
+// 6. 查询待办审核状态
+const statusResponse = await checkApprovalStatus(10, '收入确认');
+// 返回:{ is_approved: true, business_state: "已通过" }
+
+// 7. 财务查看通过(只需要传id和type)
+await financeViewPass(10, '收入确认');
+// 返回:{ message: "处理成功", code: 0 }
+```
+
+---
+
+## 接口地址汇总
+
+| 接口 | 地址 | 说明 |
+|------|------|------|
+| 获取待办列表 | `POST /user/roxyexhibition` | 获取当前用户的待办事项 |
+| 查询待办审核状态 | `POST /user/approval-status-check` | 查询待办是否已经审核完全通过 |
+| 审批处理 | `POST /user/approval_processing` | 处理审批待办(审核通过/未通过)
**财务查看时只需传id和type** |
+| 新增收入确认 | `POST /finance/confirm` | 创建收入确认记录 |
+| 收入确认列表 | `POST /finance/confirmdisplay` | 查询收入确认列表 |
+
+---
+
+## 常见问题
+
+### Q1: 财务如何知道哪些收入确认需要查看?
+
+**A:** 财务在待办列表接口中,筛选 `type="收入确认"` 且 `state="已抄送财务"` 的记录。
+
+### Q2: 财务查看时,`state` 参数应该传什么?
+
+**A:**
+- **推荐方式**:不传 `state` 参数,只传 `id` 和 `type`,系统会自动判断为财务查看通过
+- 如果发现问题,传 `state: "未通过"`,状态会更新为 `"未通过"`
+
+### Q3: 收入确认什么时候状态变为"已通过"?
+
+**A:**
+- 个人团队:提交时就是 `"已通过"`
+- 团队类型:负责人填写收入分配后,状态变为 `"已通过"`,然后抄送财务
+
+### Q4: 财务可以修改收入确认的金额吗?
+
+**A:** 不可以。财务只能查看,如果发现问题,可以标记为 `"未通过"`。如果需要修改,应该联系提交人重新提交。
+
+### Q5: 如何判断当前用户是否是财务?
+
+**A:** 系统会自动判断:
+- 用户的部门包含"财务部"
+- 用户的角色包含"财务部"
+- 审批记录的 `personincharge` 是财务部门ID或"财务"字符串