优化:开票申请合同号选择案件表数据

This commit is contained in:
27942
2026-01-08 18:19:13 +08:00
parent b7a30b118a
commit 332505b452
5 changed files with 714 additions and 22 deletions

View File

@@ -922,17 +922,36 @@ class approvalProcessing(APIView):
if type == "收入确认":
try:
user = Income.objects.get(id=approval.user_id, is_deleted=False)
income = Income.objects.get(id=approval.user_id, is_deleted=False)
except Income.DoesNotExist:
return Response({'status': 'error', 'message': '收入确认记录不存在或已被删除', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 审批人可以指定收入分配方案
allocate = request.data.get('allocate') # 收入分配(可选,审批时指定)
if state == "已通过":
approval.state = "已通过"
user.state = "已通过"
income.state = "已通过"
# 如果审批人指定了分配方案,更新分配字段
if allocate:
income.allocate = allocate
# 更新审批内容,添加分配信息
if "收入分配:待审批人指定" in approval.content:
approval.content = approval.content.replace("收入分配:待审批人指定", f"收入分配:{allocate}")
else:
approval.content = approval.content + f",收入分配:{allocate}"
income.save(update_fields=['state', 'allocate'])
approval.save(update_fields=['state', 'content'])
else:
# 如果审批通过但未指定分配,保持"待审批人指定"
income.save(update_fields=['state'])
approval.save(update_fields=['state'])
else:
approval.state = "未通过"
user.state = "未通过"
approval.save(update_fields=['state'])
user.save(update_fields=['state'])
income.state = "未通过"
income.save(update_fields=['state'])
approval.save(update_fields=['state'])
if type == "调账申请":
try:
user = Accounts.objects.get(id=approval.user_id, is_deleted=False)

View File

@@ -1,4 +1,4 @@
from .views import UserRegister,UserDeparture,UserDepartureDetail,EditUserDeparture,DeleteUserDeparture,issueAnInvoice,issueAnInvoiceDetail,confirm,loan,PaymentRequest,reimbursement,confirmdisplay,loandisplay,PaymentDisplay,reimbursementdetail,Change,ChangeDetail,EditInvoice,DeleteInvoice,EditIncome,DeleteIncome,EditAccounts,DeleteAccounts,EditPayment,DeletePayment,EditReimbursement,DeleteReimbursement,EditBonusChange,DeleteBonusChange
from .views import UserRegister,UserDeparture,UserDepartureDetail,EditUserDeparture,DeleteUserDeparture,issueAnInvoice,issueAnInvoiceDetail,confirm,loan,PaymentRequest,reimbursement,confirmdisplay,loandisplay,PaymentDisplay,reimbursementdetail,Change,ChangeDetail,EditInvoice,DeleteInvoice,EditIncome,DeleteIncome,EditAccounts,DeleteAccounts,EditPayment,DeletePayment,EditReimbursement,DeleteReimbursement,EditBonusChange,DeleteBonusChange,GetCaseListForInvoice
from django.urls import path
urlpatterns = [
@@ -7,6 +7,7 @@ urlpatterns = [
path("user-departure-detail", UserDepartureDetail.as_view(), name="user-departure-detail/"),
path("editUserDeparture", EditUserDeparture.as_view(), name="editUserDeparture/"),
path("deleteUserDeparture", DeleteUserDeparture.as_view(), name="deleteUserDeparture/"),
path("case-list-for-invoice", GetCaseListForInvoice.as_view(), name="case-list-for-invoice/"),
path("issue-invoice", issueAnInvoice.as_view(), name="issue-invoice/"),
path('issue-Detail', issueAnInvoiceDetail.as_view(), name="issue-Detail/"),
path('editInvoice', EditInvoice.as_view(), name="editInvoice/"),

View File

@@ -197,18 +197,51 @@ class UserRegister(APIView):
}, status=status.HTTP_200_OK)
class GetCaseListForInvoice(APIView):
"""获取案件列表(用于开票申请选择)"""
def post(self, request, *args, **kwargs):
"""
获取案件列表,用于开票申请时选择合同号
返回案件ID、合同号、负责人等信息
"""
from business.models import ProjectRegistration
# 获取所有未删除的案件
cases = ProjectRegistration.objects.filter(is_deleted=False).order_by('-id')
data = []
for case in cases:
data.append({
'id': case.id,
'ContractNo': case.ContractNo, # 合同号
'responsiblefor': case.responsiblefor, # 负责人
'type': case.type, # 项目类型
'times': case.times, # 立项时间
})
return Response({
'message': '获取成功',
'data': data,
'code': 0
}, status=status.HTTP_200_OK)
class issueAnInvoice(APIView):
def post(self, request, *args, **kwargs):
"""
财务开票
优化后:合同号从案件表选择,负责人自动同步
:param request:
:param args:
:param kwargs:
:return:
"""
token = request.META.get('token')
ContractNo = request.data.get('ContractNo')
personincharge = request.data.get('personincharge')
case_id = request.data.get('case_id') # 案件ID从案件表选择
# 以下字段改为可选如果提供了case_id则从案件表同步
ContractNo = request.data.get('ContractNo') # 合同号可选如果提供了case_id则自动同步
personincharge = request.data.get('personincharge') # 负责人可选如果提供了case_id则自动同步
amount = request.data.get('amount')
type = request.data.get('type')
unit = request.data.get('unit')
@@ -217,11 +250,65 @@ class issueAnInvoice(APIView):
bank = request.data.get('bank')
username = request.data.get('username')
if not all([token, ContractNo, personincharge, amount, type, unit, number, address_telephone, bank,username]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
# 必填字段验证
if not all([token, amount, type, unit, number, address_telephone, bank, username]):
missing_params = []
if not token:
missing_params.append('token')
if not amount:
missing_params.append('amount(开票金额)')
if not type:
missing_params.append('type(开票类型)')
if not unit:
missing_params.append('unit(开票单位)')
if not number:
missing_params.append('number(纳税人识别号)')
if not address_telephone:
missing_params.append('address_telephone(地址/电话)')
if not bank:
missing_params.append('bank(银行卡)')
if not username:
missing_params.append('username(提交人)')
return Response({
'status': 'error',
'message': f'缺少必填参数: {", ".join(missing_params)}',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 处理合同号和负责人:优先从案件表同步
from business.models import ProjectRegistration
if case_id:
# 如果提供了案件ID从案件表获取合同号和负责人
try:
case = ProjectRegistration.objects.get(id=case_id, is_deleted=False)
ContractNo = case.ContractNo # 自动同步合同号
personincharge = case.responsiblefor # 自动同步负责人
except ProjectRegistration.DoesNotExist:
return Response({
'status': 'error',
'message': '案件不存在或已被删除',
'code': 1
}, status=status.HTTP_404_NOT_FOUND)
else:
# 如果没有提供案件ID则ContractNo和personincharge必须手动提供
if not ContractNo:
return Response({
'status': 'error',
'message': '请选择案件或手动填写合同号',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
if not personincharge:
return Response({
'status': 'error',
'message': '请选择案件或手动填写负责人',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
today = datetime.datetime.now()
formatted_date = today.strftime("%Y-%m-%d")
invoice = Invoice.objects.create(
invoice = Invoice.objects.create(
ContractNo=ContractNo,
personincharge=personincharge,
amount=amount,
@@ -240,9 +327,11 @@ class issueAnInvoice(APIView):
new_data = {
'id': invoice.id,
'contract_no': invoice.ContractNo,
'personincharge': invoice.personincharge,
'amount': invoice.amount,
'type': invoice.type,
'unit': invoice.unit
'unit': invoice.unit,
'case_id': case_id if case_id else None
}
log_operation(
request=request,
@@ -253,10 +342,18 @@ class issueAnInvoice(APIView):
target_id=invoice.id,
target_name=invoice.ContractNo,
new_data=new_data,
remark=f'新增开票申请:合同号 {invoice.ContractNo},金额 {invoice.amount}'
remark=f'新增开票申请:合同号 {invoice.ContractNo}负责人 {invoice.personincharge}金额 {invoice.amount}'
)
return Response({'message': '提交成功', 'code': 0}, status=status.HTTP_200_OK)
return Response({
'message': '提交成功',
'code': 0,
'data': {
'id': invoice.id,
'ContractNo': invoice.ContractNo,
'personincharge': invoice.personincharge,
}
}, status=status.HTTP_200_OK)
class issueAnInvoiceDetail(APIView):
@@ -451,6 +548,8 @@ class confirm(APIView):
def post(self, request, *args, **kwargs):
"""
收入确认
优化后:收入分配由审批人指定,提交时不填写分配
数据来源案件管理
:param request:
:param args:
:param kwargs:
@@ -460,41 +559,124 @@ class confirm(APIView):
ContractNo = request.data.get('ContractNo')
CustomerID = request.data.get('CustomerID')
amount = request.data.get('amount')
allocate = request.data.get('allocate')
allocate = request.data.get('allocate') # 改为可选,由审批人指定
token = request.META.get('token')
personincharge = request.data.get('personincharge')
case_id = request.data.get('case_id') # 案件ID可选用于关联案件
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)
if not all([times, ContractNo, CustomerID, amount, allocate]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
# 必填字段验证allocate改为可选
if not all([times, ContractNo, CustomerID, amount]):
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(收款金额)')
return Response({
'status': 'error',
'message': f'缺少必填参数: {", ".join(missing_params)}',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 如果提供了案件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)
from datetime import datetime
now = datetime.now()
# 格式化日期为字符串,格式为 YYYY-MM-DD
date_string = now.strftime("%Y-%m-%d")
# 创建收入确认记录allocate为空或"待审批人指定"
income = Income.objects.create(
times=times,
ContractNo=ContractNo,
CustomerID=CustomerID,
amount=amount,
allocate=allocate,
allocate=allocate if allocate else "待审批人指定", # 如果未提供,设为"待审批人指定"
submit=user.username,
submit_tiem=date_string,
state="审核中"
)
# 构建审批内容,包含案件信息(如果提供了)
content_parts = [
f"{user.username}{times}提交了收入确认",
f"合同编号:{ContractNo}",
f"客户名称:{CustomerID}",
f"收入金额:{amount}"
]
if case_info:
content_parts.append(f"案件类型:{case_info['type']}")
content_parts.append(f"案件负责人:{case_info['responsiblefor']}")
content_parts.append("收入分配:待审批人指定")
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='审核中',
type="收入确认",
user_id=income.id
)
return Response({'message': '插入成功' ,'code': 0}, status=status.HTTP_200_OK)
# 记录操作日志
new_data = {
'id': income.id,
'ContractNo': income.ContractNo,
'CustomerID': income.CustomerID,
'amount': income.amount,
'allocate': income.allocate,
'case_id': case_id if case_id else None
}
log_operation(
request=request,
operation_type='CREATE',
module='Finance',
action='新增收入确认',
target_type='Income',
target_id=income.id,
target_name=f'{income.ContractNo} - {income.CustomerID}',
new_data=new_data,
remark=f'新增收入确认:合同号 {income.ContractNo},金额 {income.amount},分配待审批人指定'
)
return Response({
'message': '提交成功',
'code': 0,
'data': {
'id': income.id,
'ContractNo': income.ContractNo,
'allocate': income.allocate
}
}, status=status.HTTP_200_OK)
class confirmdisplay(APIView):
def post(self, request, *args, **kwargs):

View File

@@ -0,0 +1,247 @@
# 开票申请接口优化说明
## 优化内容
根据需求12开票申请接口已优化**合同号从案件表选择,负责人自动同步**。
## 接口信息
### 1. 获取案件列表接口(新增)
- **URL**`/finance/case-list-for-invoice`
- **方法**POST
- **功能**:获取案件列表,用于开票申请时选择合同号
#### 请求参数
无需参数
#### 响应示例
```json
{
"message": "获取成功",
"data": [
{
"id": 1,
"ContractNo": "HT2024001",
"responsiblefor": "张三",
"type": "民事诉讼",
"times": "2024-01-15"
},
{
"id": 2,
"ContractNo": "HT2024002",
"responsiblefor": "李四",
"type": "刑事诉讼",
"times": "2024-01-20"
}
],
"code": 0
}
```
### 2. 开票申请接口(优化)
- **URL**`/finance/issue-invoice`
- **方法**POST
- **功能**:财务开票
## 字段变更
### 优化前
-`ContractNo` - 合同号(必填,手动输入)
-`personincharge` - 负责人(必填,手动输入)
- ✅ 其他字段(金额、类型、单位等)
### 优化后
-`case_id` - 案件ID**推荐**,从案件表选择)
-`ContractNo` - 合同号可选如果提供了case_id则自动同步
-`personincharge` - 负责人可选如果提供了case_id则自动同步
- ✅ 其他字段(金额、类型、单位等,必填)
## 数据同步逻辑
### 方式1从案件表选择推荐
1. **前端调用案件列表接口**:获取所有案件列表
2. **用户选择案件**:前端显示案件列表,用户选择
3. **自动同步数据**
- 合同号(`ContractNo`)自动从案件表的 `ContractNo` 字段同步
- 负责人(`personincharge`)自动从案件表的 `responsiblefor` 字段同步
### 方式2手动填写兼容
如果没有提供 `case_id`,可以手动填写 `ContractNo``personincharge`
## 请求示例
### 方式1从案件表选择推荐
```json
POST /finance/issue-invoice
{
"case_id": 1,
"amount": "10000",
"type": "增值税专用发票",
"unit": "XX律师事务所",
"number": "91110000MA01234567",
"address_telephone": "北京市XX区XX路XX号 010-12345678",
"bank": "6222021234567890123",
"username": "提交人姓名"
}
```
**说明**
- 提供了 `case_id`,系统自动从案件表获取合同号和负责人
- 不需要手动填写 `ContractNo``personincharge`
### 方式2手动填写兼容
```json
POST /finance/issue-invoice
{
"ContractNo": "HT2024001",
"personincharge": "张三",
"amount": "10000",
"type": "增值税专用发票",
"unit": "XX律师事务所",
"number": "91110000MA01234567",
"address_telephone": "北京市XX区XX路XX号 010-12345678",
"bank": "6222021234567890123",
"username": "提交人姓名"
}
```
**说明**
- 没有提供 `case_id`,需要手动填写 `ContractNo``personincharge`
## 响应示例
### 成功响应
```json
{
"message": "提交成功",
"code": 0,
"data": {
"id": 123,
"ContractNo": "HT2024001",
"personincharge": "张三"
}
}
```
### 错误响应
#### 缺少必填参数
```json
{
"status": "error",
"message": "缺少必填参数: amount(开票金额), type(开票类型)",
"code": 1
}
```
#### 案件不存在
```json
{
"status": "error",
"message": "案件不存在或已被删除",
"code": 1
}
```
#### 未选择案件且未填写合同号
```json
{
"status": "error",
"message": "请选择案件或手动填写合同号",
"code": 1
}
```
#### 未选择案件且未填写负责人
```json
{
"status": "error",
"message": "请选择案件或手动填写负责人",
"code": 1
}
```
## 工作流程
### 推荐流程(从案件表选择)
```
1. 前端调用案件列表接口
2. 显示案件列表供用户选择
3. 用户选择案件获取case_id
4. 前端自动填充合同号和负责人(可选,提升用户体验)
5. 用户填写其他信息(金额、类型、单位等)
6. 提交开票申请传入case_id
7. 后端自动从案件表同步合同号和负责人
8. 创建开票申请记录
```
### 兼容流程(手动填写)
```
1. 用户手动填写合同号和负责人
2. 用户填写其他信息
3. 提交开票申请不传case_id
4. 后端验证必填字段
5. 创建开票申请记录
```
## 优势
1. **数据一致性**
- 合同号和负责人直接从案件表同步,避免手动输入错误
- 确保开票申请与案件信息一致
2. **操作便捷**
- 用户只需选择案件,无需手动输入合同号和负责人
- 减少重复录入,提高效率
3. **向后兼容**
- 仍然支持手动填写合同号和负责人
- 不影响现有功能
4. **数据可追溯**
- 开票申请关联到具体案件通过case_id
- 便于后续查询和统计
## 注意事项
1. **案件选择**
- 建议优先使用案件选择方式
- 确保案件在案件管理中已正确创建
2. **数据同步**
- 如果案件信息发生变化,已创建的开票申请不会自动更新
- 需要在编辑开票申请时重新选择案件
3. **必填字段**
- 金额、类型、单位、纳税人识别号、地址/电话、银行卡、提交人仍为必填
- 合同号和负责人:要么选择案件,要么手动填写
## 更新日志
### v2.0.0 (2024-01-XX)
- ✅ 新增获取案件列表接口
- ✅ 开票申请支持从案件表选择合同号
- ✅ 负责人自动从案件表的负责人字段同步
- ✅ 保持向后兼容,支持手动填写

View File

@@ -0,0 +1,243 @@
# 收入确认接口优化说明
## 优化内容
根据需求13收入确认接口已优化**收入分配由审批人指定**,提交时不填写分配。
## 接口信息
- **URL**`/finance/confirm`
- **方法**POST
- **功能**:收入确认
## 字段变更
### 优化前
-`allocate` - 收入分配(必填,提交时填写)
### 优化后
-`allocate` - 收入分配(**可选**,提交时不填写,由审批人指定)
-`case_id` - 案件ID可选用于关联案件管理
## 工作流程
### 提交阶段
1. 用户提交收入确认
2. **不填写** `allocate` 字段(或填写为空)
3. 系统自动设置 `allocate = "待审批人指定"`
4. 创建审批记录,状态为"审核中"
### 审批阶段
1. 审批人查看收入确认申请
2. 审批人**指定收入分配方案**`allocate` 字段)
3. 审批通过时,系统更新 `allocate` 字段
4. 审批内容自动更新,包含分配信息
## 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| times | string | 是 | 收款日期 |
| ContractNo | string | 是 | 合同号 |
| CustomerID | string | 是 | 客户名称 |
| amount | string | 是 | 收款金额 |
| allocate | string | 否 | 收入分配(可选,由审批人指定) |
| personincharge | string | 是 | 审批人 |
| case_id | int | 否 | 案件ID可选用于关联案件 |
## 请求示例
### 提交收入确认(不填写分配)
```json
POST /finance/confirm
{
"times": "2024-01-15",
"ContractNo": "HT2024001",
"CustomerID": "XX公司",
"amount": "100000",
"personincharge": "审批人姓名"
}
```
### 提交收入确认(关联案件)
```json
POST /finance/confirm
{
"times": "2024-01-15",
"ContractNo": "HT2024001",
"CustomerID": "XX公司",
"amount": "100000",
"personincharge": "审批人姓名",
"case_id": 1
}
```
**说明**
- 如果提供了 `case_id`,系统会从案件表获取相关信息
- 如果未提供 `ContractNo`,会自动从案件表同步
## 审批处理
### 审批接口
- **URL**`/user/approval_processing`
- **方法**POST
### 审批请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | int | 是 | 审批记录ID |
| type | string | 是 | 审批类型("收入确认" |
| state | string | 是 | 审批状态("已通过"或"未通过" |
| allocate | string | 否 | 收入分配方案(审批通过时指定) |
### 审批请求示例
#### 审批通过并指定分配
```json
POST /user/approval_processing
{
"id": 123,
"type": "收入确认",
"state": "已通过",
"allocate": "张三50%李四30%王五20%"
}
```
#### 审批通过但不指定分配
```json
POST /user/approval_processing
{
"id": 123,
"type": "收入确认",
"state": "已通过"
}
```
**说明**:如果审批通过但未指定分配,`allocate` 保持为"待审批人指定"
#### 审批不通过
```json
POST /user/approval_processing
{
"id": 123,
"type": "收入确认",
"state": "未通过"
}
```
## 响应示例
### 提交成功
```json
{
"message": "提交成功",
"code": 0,
"data": {
"id": 123,
"ContractNo": "HT2024001",
"allocate": "待审批人指定"
}
}
```
### 错误响应
#### 缺少必填参数
```json
{
"status": "error",
"message": "缺少必填参数: times(收款日期), ContractNo(合同号)",
"code": 1
}
```
#### 案件不存在
```json
{
"status": "error",
"message": "案件不存在或已被删除",
"code": 1
}
```
## 数据来源案件管理
### 关联案件(可选)
如果提供了 `case_id`,系统会:
1. 从案件表(`ProjectRegistration`)获取案件信息
2. 如果合同号未提供,自动从案件表同步
3. 在审批内容中包含案件类型和负责人信息
### 案件信息包含
- 合同号(`ContractNo`
- 负责人(`responsiblefor`
- 案件类型(`type`
## 审批内容格式
### 提交时(未指定分配)
```
张三在2024-01-15提交了收入确认合同编号HT2024001客户名称XX公司收入金额100000收入分配待审批人指定
```
### 审批后(已指定分配)
```
张三在2024-01-15提交了收入确认合同编号HT2024001客户名称XX公司收入金额100000收入分配张三50%李四30%王五20%
```
## 优势
1. **职责分离**
- 提交人只需填写基本信息
- 审批人负责指定分配方案,更专业
2. **数据准确性**
- 分配方案由审批人指定,确保准确
- 避免提交人填写错误
3. **灵活性**
- 支持关联案件管理
- 可以从案件表同步相关信息
4. **可追溯**
- 分配方案记录在审批内容中
- 便于后续查询和审计
## 注意事项
1. **分配方案格式**
- 建议使用清晰的格式,如"张三50%李四30%王五20%"
- 也可以使用其他格式,由审批人自由填写
2. **审批时机**
- 分配方案在审批通过时指定
- 如果审批不通过,不需要指定分配
3. **案件关联**
- 案件关联是可选的
- 如果提供了 `case_id`,系统会自动同步相关信息
4. **数据一致性**
- 如果案件信息发生变化,已创建的收入确认不会自动更新
- 需要在编辑时重新关联案件
## 更新日志
### v2.0.0 (2024-01-XX)
- ✅ 收入分配改为由审批人指定
- ✅ 提交时不填写分配,系统自动设为"待审批人指定"
- ✅ 审批通过时,审批人可以指定分配方案
- ✅ 支持关联案件管理(可选)
- ✅ 审批内容自动更新,包含分配信息