Files
jyls_django/finance/views.py
2026-01-19 19:42:08 +08:00

3087 lines
120 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
import json
import ast
from User.models import User, Approval, Department
from User.utils import log_operation, normalize_approvers_param, build_missing_approvers_message
import datetime
from .models import Invoice, Income, Accounts, Payment, Reimbursement, BonusChange
from utility.utility import flies
from django.contrib.sessions.backends.db import SessionStore
from django.db.models import Count, Q
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from business.models import PreFiling, Case, ProjectRegistration
class UserRegister(APIView):
def post(self, request, *args, **kwargs):
"""
入职财务登记
优化后:只需填写姓名、入职时间、工资三个字段
其他数据(身份证、岗位等)从人事管理同步
:param request:
:param args:
:param kwargs:
:return:
"""
username = request.data.get('username')
Dateofjoining = request.data.get('Dateofjoining')
salary = request.data.get('salary')
# 以下字段改为可选,如果未提供则从人事管理同步
card = request.data.get('card') # 身份证(可选)
position = request.data.get('position') # 岗位(可选)
approvers = request.data.get('approvers') # 审核人列表可选多人团队时需要推荐用户ID数组如[1,2,3],兼容:用户名数组)
# 兼容旧接口:如果传了 personincharge转换为 approvers
personincharge = request.data.get('personincharge')
approvers = normalize_approvers_param(approvers, personincharge)
# 只验证必填字段:姓名、入职时间、工资
if not all([username, Dateofjoining, salary]):
missing_params = []
if not username:
missing_params.append('username(姓名)')
if not Dateofjoining:
missing_params.append('Dateofjoining(入职时间)')
if not salary:
missing_params.append('salary(工资)')
return Response({
'status': 'error',
'message': f'缺少必填参数: {", ".join(missing_params)}',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 验证日期格式
try:
Dateofjoinings = datetime.datetime.strptime(Dateofjoining, "%Y-%m-%d")
except ValueError:
return Response({
'status': 'error',
'message': '入职时间格式错误请使用YYYY-MM-DD格式',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 查找用户(必须已存在于人事管理中)
try:
user = User.objects.get(username=username, is_deleted=False)
except User.DoesNotExist:
return Response({
'status': 'error',
'message': '用户不存在,请先在人事管理中创建该用户',
'code': 1
}, status=status.HTTP_404_NOT_FOUND)
# 从人事管理同步其他数据(如果入职登记未提供)
# 如果提供了card或position则使用提供的值否则使用人事管理中的值
# 团队信息始终从人事管理中获取,不在财务登记中修改
update_fields = []
# 更新必填字段
user.Dateofjoining = Dateofjoinings
user.salary = salary
update_fields.extend(['Dateofjoining', 'salary'])
# 处理可选字段:如果提供了则更新,否则保持人事管理中的值
if card:
user.card = card
update_fields.append('card')
# 如果未提供card使用人事管理中的card已存在无需更新
if position:
user.position = position
update_fields.append('position')
# 如果未提供position使用人事管理中的position已存在无需更新
# 更新状态
user.state = "待确认"
update_fields.append('state')
# 保存用户信息
user.save(update_fields=update_fields)
# 从人事管理中获取团队信息(团队信息不在财务登记中修改)
team_name = user.team # 用户的团队名称(从人事管理中获取)
team = None
team_type_display = None
if team_name:
try:
from User.models import Team
team = Team.objects.get(name=team_name, is_deleted=False)
team_type_display = team.get_team_type_display()
except Team.DoesNotExist:
# 如果团队不存在,默认按团队类型处理(需要审批)
pass
# 构建审批内容(使用实际的值,包括从人事管理同步的)
actual_card = card if card else (user.card if user.card else '未填写')
actual_position = position if position else (user.position if user.position else '未填写')
actual_team = team_name if team_name else '未分配团队'
today = datetime.datetime.now()
formatted_date = today.strftime("%Y-%m-%d")
# 根据团队类型判断是否需要审批,使用统一的审核流程函数
from User.utils import create_approval_with_team_logic, parse_approvers
# 构建审批内容
content_parts = [
f"{username}{Dateofjoining}办理入职",
f"身份证:{actual_card}",
f"岗位:{actual_position}",
f"团队:{actual_team}"
]
if team_type_display:
content_parts.append(f"团队类型:{team_type_display}")
content_parts.append(f"薪资:{salary}")
content = "".join(content_parts)
# 使用统一的审核流程函数
try:
approval, approvers_order_json, needs_approval = create_approval_with_team_logic(
team_name=team_name,
approvers=approvers,
title=username + "入职财务登记",
content=content,
approval_type="入职财务登记",
user_id=str(user.id),
business_record=user, # User对象没有approvers_order字段但可以更新state
today=formatted_date
)
except Exception as e:
# 记录异常并返回错误信息
import logging
logger = logging.getLogger(__name__)
logger.error(f"创建入职财务登记审批失败: {str(e)}", exc_info=True)
return Response({
'status': 'error',
'message': f'创建审批流程失败: {str(e)},请检查团队配置和审核人信息',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 如果返回None且需要审核说明缺少审核人
if approval is None and needs_approval:
return Response({
'status': 'error',
'message': build_missing_approvers_message(team_name, approvers),
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 记录操作日志(包含从人事管理获取的团队信息)
new_data = {
'user_id': user.id,
'username': user.username,
'card': user.card,
'position': user.position,
'team': team_name, # 从人事管理中获取的团队名称
'team_type': team_type_display if team else None, # 团队类型显示名称
'salary': user.salary,
'Dateofjoining': Dateofjoining,
'state': user.state
}
log_operation(
request=request,
operation_type='CREATE',
module='Finance',
action='新增入职财务登记',
target_type='User',
target_id=user.id,
target_name=user.username,
new_data=new_data,
remark=f'新增入职财务登记:{user.username},入职时间 {Dateofjoining},薪资 {salary},团队 {team_name if team_name else "未分配"}'
)
return Response({
'message': '登记成功',
'code': 0,
'data': {
'username': user.username,
'Dateofjoining': Dateofjoining,
'salary': salary,
'card': user.card, # 返回实际使用的身份证(从人事管理同步)
'position': user.position, # 返回实际使用的岗位(从人事管理同步)
'team': team_name, # 返回团队名称(从人事管理中获取)
'team_type': team_type_display if team else None, # 返回团队类型(从人事管理中获取)
}
}, status=status.HTTP_200_OK)
class RegisteredUserList(APIView):
"""查询已入职登记的用户列表"""
def post(self, request, *args, **kwargs):
"""
查询已完成入职财务登记的用户
:param request:
:param args:
:param kwargs:
:return:
"""
page = request.data.get('page', 1)
per_page = request.data.get('per_page', 10)
username = request.data.get('username', '') # 用户名搜索
department = request.data.get('department', '') # 部门搜索
if not all([page, per_page]):
return Response({
'status': 'error',
'message': '缺少参数:页码和每页数量不能为空',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 获取所有已完成入职登记的用户ID状态为"已通过"的入职财务登记)
completed_registration_user_ids = Approval.objects.filter(
type="入职财务登记",
state="已通过",
is_deleted=False
).values_list('user_id', flat=True)
# 将user_id转换为整数列表因为user_id是CharField存储的字符串
# 同时验证用户是否被软删除只保留未删除的用户ID
completed_user_ids = []
for user_id_str in completed_registration_user_ids:
try:
user_id = int(user_id_str)
# 验证用户是否存在且未被软删除
if User.objects.filter(id=user_id, is_deleted=False).exists():
completed_user_ids.append(user_id)
except (ValueError, TypeError):
continue
# 查询条件:已完成入职登记的用户
# 只查询未软删除的用户
Q_obj = Q(is_deleted=False)
# 只查询已完成入职登记的用户
if completed_user_ids:
Q_obj &= Q(id__in=completed_user_ids)
else:
# 如果没有已完成入职登记的用户,返回空列表
return Response({
'message': '查询成功',
'total': 0,
'data': [],
'code': 0
}, status=status.HTTP_200_OK)
# 排除admin用户超级管理员
Q_obj &= ~Q(username='admin')
Q_obj &= ~Q(account='admin')
# 用户名搜索
if username:
Q_obj &= Q(username__icontains=username)
# 部门搜索
if department:
Q_obj &= Q(department__username__icontains=department)
# 查询用户
users = User.objects.prefetch_related('department', 'role').filter(Q_obj).order_by('-id')
total = users.count()
# 分页
paginator = Paginator(users, per_page)
try:
users_page = paginator.page(page)
except PageNotAnInteger:
users_page = paginator.page(1)
except EmptyPage:
users_page = paginator.page(paginator.num_pages)
# 批量获取所有用户的团队信息,优化查询性能
from User.models import Team
team_names = [user.team for user in users_page.object_list if user.team]
teams_dict = {}
if team_names:
teams = Team.objects.prefetch_related('approvers').filter(
name__in=team_names,
is_deleted=False
)
for team in teams:
teams_dict[team.name] = team
data = []
for user in users_page.object_list:
# 获取入职登记的审批记录信息
approval = Approval.objects.filter(
type="入职财务登记",
user_id=str(user.id),
state="已通过",
is_deleted=False
).order_by('-id').first()
# 获取完整的团队信息
team_info = None
if user.team:
team = teams_dict.get(user.team)
if team:
team_info = {
'id': team.id,
'name': team.name,
'team_type': team.team_type,
'team_type_display': team.get_team_type_display(), # 个人团队/团队
'description': team.description,
'approvers': list(team.approvers.filter(is_deleted=False).values('id', 'username'))
}
else:
# 如果团队不存在,只返回团队名称(兼容旧数据)
team_info = {
'id': None,
'name': user.team,
'team_type': None,
'team_type_display': None,
'description': None,
'approvers': []
}
data.append({
'id': user.id,
'username': user.username,
'account': user.account,
'card': user.card,
'position': user.position,
'mobilePhone': user.mobilePhone,
'department': list(user.department.values('id', 'username')),
'role': list(user.role.values('id', 'RoleName', 'permissionId')),
'team': team_info, # 返回完整的团队信息
'Dateofjoining': user.Dateofjoining.strftime("%Y-%m-%d") if user.Dateofjoining else None,
'salary': user.salary,
'state': user.state,
'registration_date': approval.times.strftime("%Y-%m-%d") if approval and approval.times else None,
# 入职登记日期
'approval_id': approval.id if approval else None
})
return Response({
'message': '查询成功',
'total': total,
'data': data,
'code': 0
}, status=status.HTTP_200_OK)
class UnregisteredUserList(APIView):
"""查询未入职登记的用户列表"""
def post(self, request, *args, **kwargs):
"""
查询在人事管理中已创建,但还没有完成入职财务登记的用户
:param request:
:param args:
:param kwargs:
:return:
"""
page = request.data.get('page', 1)
per_page = request.data.get('per_page', 10)
username = request.data.get('username', '') # 用户名搜索
department = request.data.get('department', '') # 部门搜索
if not all([page, per_page]):
return Response({
'status': 'error',
'message': '缺少参数:页码和每页数量不能为空',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 获取所有已完成入职登记的用户ID状态为"已通过"的入职财务登记)
completed_registration_user_ids = Approval.objects.filter(
type="入职财务登记",
state="已通过",
is_deleted=False
).values_list('user_id', flat=True)
# 将user_id转换为整数列表因为user_id是CharField存储的字符串
# 同时验证用户是否被软删除只保留未删除的用户ID
completed_user_ids = []
for user_id_str in completed_registration_user_ids:
try:
user_id = int(user_id_str)
# 验证用户是否存在且未被软删除
if User.objects.filter(id=user_id, is_deleted=False).exists():
completed_user_ids.append(user_id)
except (ValueError, TypeError):
continue
# 查询条件:在人事管理中,但不在已完成入职登记的用户列表中
# 只查询未软删除的用户
Q_obj = Q(is_deleted=False)
# 排除已完成入职登记的用户
if completed_user_ids:
Q_obj &= ~Q(id__in=completed_user_ids)
# 排除admin用户超级管理员
Q_obj &= ~Q(username='admin')
Q_obj &= ~Q(account='admin')
# 用户名搜索
if username:
Q_obj &= Q(username__icontains=username)
# 部门搜索
if department:
Q_obj &= Q(department__username__icontains=department)
# 查询用户
users = User.objects.prefetch_related('department', 'role').filter(Q_obj).order_by('-id')
total = users.count()
# 分页
paginator = Paginator(users, per_page)
try:
users_page = paginator.page(page)
except PageNotAnInteger:
users_page = paginator.page(1)
except EmptyPage:
users_page = paginator.page(paginator.num_pages)
# 批量获取所有用户的团队信息,优化查询性能
from User.models import Team
team_names = [user.team for user in users_page.object_list if user.team]
teams_dict = {}
if team_names:
teams = Team.objects.prefetch_related('approvers').filter(
name__in=team_names,
is_deleted=False
)
for team in teams:
teams_dict[team.name] = team
# 批量获取所有用户的待审核入职登记,优化查询性能
user_ids = [user.id for user in users_page.object_list]
pending_approvals_dict = {}
if user_ids:
pending_approvals = Approval.objects.filter(
type="入职财务登记",
user_id__in=[str(uid) for uid in user_ids],
state="审核中",
is_deleted=False
)
for approval in pending_approvals:
try:
user_id = int(approval.user_id)
if user_id not in pending_approvals_dict:
pending_approvals_dict[user_id] = approval
except (ValueError, TypeError):
continue
data = []
for user in users_page.object_list:
# 检查是否有待审核的入职登记(状态为"审核中"
pending_approval = pending_approvals_dict.get(user.id)
# 获取完整的团队信息
team_info = None
if user.team:
team = teams_dict.get(user.team)
if team:
team_info = {
'id': team.id,
'name': team.name,
'team_type': team.team_type,
'team_type_display': team.get_team_type_display(), # 个人团队/团队
'description': team.description,
'approvers': list(team.approvers.filter(is_deleted=False).values('id', 'username'))
}
else:
# 如果团队不存在,只返回团队名称
team_info = {
'name': user.team,
'team_type': None,
'team_type_display': None,
'description': None,
'approvers': []
}
data.append({
'id': user.id,
'username': user.username,
'account': user.account,
'card': user.card,
'position': user.position,
'mobilePhone': user.mobilePhone,
'department': list(user.department.values('id', 'username')),
'role': list(user.role.values('id', 'RoleName', 'permissionId')),
'team': team_info, # 返回完整的团队信息对象
'Dateofjoining': user.Dateofjoining.strftime("%Y-%m-%d") if user.Dateofjoining else None,
'state': user.state,
'has_pending_approval': pending_approval is not None, # 是否有待审核的入职登记
'pending_approval_id': pending_approval.id if pending_approval else None
})
return Response({
'message': '查询成功',
'total': total,
'data': data,
'code': 0
}, 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')
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')
number = request.data.get('number')
address_telephone = request.data.get('address_telephone')
bank = request.data.get('bank')
username = request.data.get('username')
# 必填字段验证
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(
ContractNo=ContractNo,
personincharge=personincharge,
amount=amount,
type=type,
unit=unit,
number=number,
address_telephone=address_telephone,
bank=bank,
state="审核中",
username=username,
times=formatted_date,
)
# 已删除:不再创建开票待办事项
# 记录操作日志
new_data = {
'id': invoice.id,
'contract_no': invoice.ContractNo,
'personincharge': invoice.personincharge,
'amount': invoice.amount,
'type': invoice.type,
'unit': invoice.unit,
'case_id': case_id if case_id else None
}
log_operation(
request=request,
operation_type='CREATE',
module='Finance',
action='新增开票申请',
target_type='Invoice',
target_id=invoice.id,
target_name=invoice.ContractNo,
new_data=new_data,
remark=f'新增开票申请:合同号 {invoice.ContractNo},负责人 {invoice.personincharge},金额 {invoice.amount}'
)
return Response({
'message': '提交成功',
'code': 0,
'data': {
'id': invoice.id,
'ContractNo': invoice.ContractNo,
'personincharge': invoice.personincharge,
}
}, status=status.HTTP_200_OK)
class issueAnInvoiceDetail(APIView):
def post(self, request, *args, **kwargs):
"""
开票记录详情
"""
page = request.data.get('page')
per_page = request.data.get('per_page')
times = request.data.get('times')
end_time = request.data.get('end_time')
unit = request.data.get('unit')
if not all([page, per_page]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
Q_obj = Q()
if times and end_time:
Q_obj &= Q(times__gte=times) & Q(times__lte=end_time)
if unit:
Q_obj &= Q(unit__icontains=unit)
invos = Invoice.objects.filter(Q_obj, is_deleted=False).order_by('-id')
total = len(invos)
paginator = Paginator(invos, per_page)
try:
user_agents_page = paginator.page(page)
except PageNotAnInteger:
user_agents_page = paginator.page(1)
except EmptyPage:
user_agents_page = paginator.page(paginator.num_pages)
data = []
for info in user_agents_page.object_list:
itme = {
'id': info.id,
"ContractNo": info.ContractNo,
"personincharge": info.personincharge,
"amount": info.amount,
"type": info.type,
"unit": info.unit,
"number": info.number,
"address_telephone": info.address_telephone,
"bank": info.bank,
"state": info.state,
"username": info.username,
"times": info.times,
}
data.append(itme)
return Response({'message': '展示成功', "total": total, 'data': data, 'code': 0}, status=status.HTTP_200_OK)
class EditInvoice(APIView):
def post(self, request, *args, **kwargs):
"""
编辑开票申请
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
ContractNo = request.data.get('ContractNo')
personincharge = request.data.get('personincharge')
amount = request.data.get('amount')
type = request.data.get('type')
unit = request.data.get('unit')
number = request.data.get('number')
address_telephone = request.data.get('address_telephone')
bank = request.data.get('bank')
if not id:
return Response({'status': 'error', 'message': '缺少参数id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
invoice = Invoice.objects.get(id=id, is_deleted=False)
except Invoice.DoesNotExist:
return Response({'status': 'error', 'message': '开票申请不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
update_fields_list = []
if ContractNo:
invoice.ContractNo = ContractNo
update_fields_list.append('ContractNo')
if personincharge:
invoice.personincharge = personincharge
update_fields_list.append('personincharge')
if amount:
invoice.amount = amount
update_fields_list.append('amount')
if type:
invoice.type = type
update_fields_list.append('type')
if unit:
invoice.unit = unit
update_fields_list.append('unit')
if number:
invoice.number = number
update_fields_list.append('number')
if address_telephone:
invoice.address_telephone = address_telephone
update_fields_list.append('address_telephone')
if bank:
invoice.bank = bank
update_fields_list.append('bank')
# 记录操作前的数据
old_data = {
'id': invoice.id,
'contract_no': invoice.ContractNo,
'amount': invoice.amount,
'type': invoice.type,
'unit': invoice.unit
}
if update_fields_list:
invoice.save(update_fields=update_fields_list)
# 记录操作后的数据
invoice.refresh_from_db()
new_data = {
'id': invoice.id,
'contract_no': invoice.ContractNo,
'amount': invoice.amount,
'type': invoice.type,
'unit': invoice.unit
}
# 记录操作日志
log_operation(
request=request,
operation_type='UPDATE',
module='Finance',
action='编辑开票申请',
target_type='Invoice',
target_id=invoice.id,
target_name=invoice.ContractNo,
old_data=old_data,
new_data=new_data,
remark=f'编辑开票申请:合同号 {invoice.ContractNo}'
)
return Response({'message': '编辑成功', 'code': 0}, status=status.HTTP_200_OK)
class DeleteInvoice(APIView):
def post(self, request, *args, **kwargs):
"""
删除开票申请
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
if not id:
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
invoice = Invoice.objects.get(id=id, is_deleted=False)
except Invoice.DoesNotExist:
return Response({'status': 'error', 'message': '开票申请不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 记录操作前的数据
old_data = {
'id': invoice.id,
'contract_no': invoice.ContractNo,
'amount': invoice.amount,
'unit': invoice.unit
}
# 软删除:更新 is_deleted 字段
invoice.is_deleted = True
invoice.save()
# 记录操作日志
log_operation(
request=request,
operation_type='DELETE',
module='Finance',
action='删除开票申请',
target_type='Invoice',
target_id=invoice.id,
target_name=invoice.ContractNo,
old_data=old_data,
remark=f'删除开票申请:合同号 {invoice.ContractNo},金额 {invoice.amount}'
)
return Response({'message': '删除成功', 'code': 0}, status=status.HTTP_200_OK)
class confirm(APIView):
def post(self, request, *args, **kwargs):
"""
收入确认
优化后:
1. 通过合同号关联案件ContractNo必填
2. 从案件管理中同步:客户名称、负责人
3. 手动填写:收款日期、收款金额
4. 按照团队审核逻辑处理审核流程(与其他审核待办一致)
:param request:
:param args:
:param kwargs:
:return:
"""
# 必填参数:合同号
ContractNo = request.data.get('ContractNo') # 合同号(必填,用于关联案件)
# 手动填写信息
times = request.data.get('times') # 收款日期
amount = request.data.get('amount') # 收款金额
# 可选字段(如果通过合同号找到了案件,将从案件管理中同步)
CustomerID = request.data.get('CustomerID') # 客户名称(可选,如果通过合同号找到案件则自动同步)
# 审核人列表可选多人团队时需要推荐用户ID数组如[1,2,3],兼容:用户名数组)
approvers = request.data.get('approvers')
# 兼容旧接口:如果传了 personincharge转换为 approvers
personincharge = request.data.get('personincharge')
approvers = normalize_approvers_param(approvers, personincharge)
allocate = request.data.get('allocate') # 收入分配(财务提交时不填写,由负责人填写)
token = request.META.get('token')
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)
# 必填字段验证
missing_params = []
if not times:
missing_params.append('times(收款日期)')
if not amount:
missing_params.append('amount(收款金额)')
if not ContractNo:
missing_params.append('ContractNo(合同号)')
if missing_params:
return Response({
'status': 'error',
'message': f'缺少必填参数: {", ".join(missing_params)}',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 获取提交人的团队信息(用于审核流程)
from User.models import Team
team_name = user.team
team = None
if team_name:
try:
team = Team.objects.get(name=team_name, is_deleted=False)
except Team.DoesNotExist:
pass
# 通过合同号从案件管理中提取信息
from business.models import Case, ProjectRegistration
import json
responsible_person = None # 负责人姓名(用于抄送)
responsible_person_username = None # 负责人用户名(用于抄送)
case_info = None
# 优先从Case案件管理中查找
try:
case = Case.objects.select_related('project').get(contract_no=ContractNo, is_deleted=False)
# 获取客户名称
CustomerID = CustomerID or case.client_name
# 获取负责人信息
responsiblefor = case.responsiblefor
# 如果案件信息不完整从关联的ProjectRegistration获取
if case.project:
project = case.project
CustomerID = CustomerID or project.client_info
responsiblefor = responsiblefor or project.responsiblefor
# 解析负责人信息
try:
if responsiblefor:
if isinstance(responsiblefor, str):
responsiblefor_dict = json.loads(responsiblefor)
else:
responsiblefor_dict = responsiblefor
responsible_person = responsiblefor_dict.get('responsible_person', '')
# 查找负责人用户名
if responsible_person:
try:
responsible_user = User.objects.get(username=responsible_person, is_deleted=False)
responsible_person_username = responsible_user.username
except User.DoesNotExist:
pass
except (json.JSONDecodeError, TypeError, AttributeError):
responsible_person = str(responsiblefor) if responsiblefor else ''
case_info = {
'id': case.id,
'type': 'case',
'contract_no': ContractNo,
'customer_name': CustomerID,
'responsible_person': responsible_person
}
except Case.DoesNotExist:
# 如果Case中找不到尝试从ProjectRegistration立项登记中查找
try:
project = ProjectRegistration.objects.get(ContractNo=ContractNo, is_deleted=False)
CustomerID = CustomerID or project.client_info
responsiblefor = project.responsiblefor
# 解析负责人信息
try:
if responsiblefor:
if isinstance(responsiblefor, str):
responsiblefor_dict = json.loads(responsiblefor)
else:
responsiblefor_dict = responsiblefor
responsible_person = responsiblefor_dict.get('responsible_person', '')
# 查找负责人用户名
if responsible_person:
try:
responsible_user = User.objects.get(username=responsible_person, is_deleted=False)
responsible_person_username = responsible_user.username
except User.DoesNotExist:
pass
except (json.JSONDecodeError, TypeError, AttributeError):
responsible_person = str(responsiblefor) if responsiblefor else ''
case_info = {
'id': project.id,
'type': 'project',
'contract_no': ContractNo,
'customer_name': CustomerID,
'responsible_person': responsible_person
}
except ProjectRegistration.DoesNotExist:
return Response({
'status': 'error',
'message': f'未找到合同号为"{ContractNo}"的案件或立项登记,请检查合同号是否正确',
'code': 1
}, status=status.HTTP_404_NOT_FOUND)
# 验证合同号、客户名称是否获取成功
if not ContractNo:
return Response({
'status': 'error',
'message': '无法从案件管理中获取合同号,请检查案件信息',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
if not CustomerID:
return Response({
'status': 'error',
'message': '无法从案件管理中获取客户名称,请检查案件信息',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
from datetime import datetime
now = datetime.now()
date_string = now.strftime("%Y-%m-%d")
# 根据团队类型决定初始状态
# 如果是团队类型且需要审核,状态为"审核中";否则为"待财务处理"
initial_state = "审核中" if (team and team.team_type == 'team') else "待财务处理"
# 创建收入确认记录
income = Income.objects.create(
times=times,
ContractNo=ContractNo,
CustomerID=CustomerID,
amount=amount,
allocate=allocate if allocate else "待负责人指定", # 如果未提供,设为"待负责人指定"
submit=user.username,
submit_tiem=date_string,
state=initial_state
)
# 使用统一的审核流程函数(与其他审核待办一致)
from User.utils import create_approval_with_team_logic
# 构建审批内容
content_parts = [
f"{user.username}{times}提交了收入确认",
f"合同编号:{ContractNo}",
f"客户名称:{CustomerID}",
f"收款金额:{amount}",
f"收入分配:待负责人指定"
]
if responsible_person:
content_parts.append(f"案件负责人:{responsible_person}")
content = "".join(content_parts)
# 使用统一的审核流程函数
try:
approval, approvers_order_json, needs_approval = create_approval_with_team_logic(
team_name=team_name,
approvers=approvers,
title=user.username + "提交收入确认",
content=content,
approval_type="收入确认",
user_id=str(income.id),
business_record=income,
today=date_string
)
except Exception as e:
# 记录异常并返回错误信息
import logging
logger = logging.getLogger(__name__)
logger.error(f"创建收入确认审批失败: {str(e)}", exc_info=True)
return Response({
'status': 'error',
'message': f'创建审批流程失败: {str(e)},请检查团队配置和审核人信息',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 如果返回None且需要审核说明缺少审核人
if approval is None and needs_approval:
return Response({
'status': 'error',
'message': build_missing_approvers_message(team_name, approvers),
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 记录操作日志
new_data = {
'id': income.id,
'ContractNo': income.ContractNo,
'CustomerID': income.CustomerID,
'amount': income.amount,
'allocate': income.allocate,
'case_info': case_info,
'responsible_person': responsible_person,
'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='Income',
target_id=income.id,
target_name=f'{income.ContractNo} - {income.CustomerID}',
new_data=new_data,
remark=f'新增收入确认:合同号 {income.ContractNo},金额 {income.amount},团队类型 {team.team_type if team else "未知"}'
)
return Response({
'message': '提交成功',
'code': 0,
'data': {
'id': income.id,
'ContractNo': income.ContractNo,
'CustomerID': income.CustomerID,
'responsible_person': responsible_person,
'state': income.state,
'approval_id': approval.id if approval else None,
'needs_approval': team is None or team.team_type != 'personal'
}
}, status=status.HTTP_200_OK)
class GetApproversListForIncomeConfirm(APIView):
"""获取收入确认的审核人列表接口"""
def post(self, request, *args, **kwargs):
"""
获取收入确认时可选的审核人列表
返回所有未删除的用户列表,格式简洁,适合下拉选择
:param request:
:param args:
:param kwargs:
:return:
"""
# 可选参数:用户名搜索
username = request.data.get('username', '') # 用户名搜索(可选)
# 查询条件:只查询未软删除的用户
Q_obj = Q(is_deleted=False)
# 排除admin用户超级管理员
Q_obj &= ~Q(username='admin')
Q_obj &= ~Q(account='admin')
# 用户名搜索(模糊匹配)
if username:
Q_obj &= Q(username__icontains=username)
# 查询用户,按用户名排序
users = User.objects.filter(Q_obj).order_by('username')
# 构建返回数据(格式简洁,适合下拉选择)
data = []
for user in users:
data.append({
'id': user.id,
'username': user.username, # 用户名(用于显示和选择)
'account': user.account, # 账号(辅助信息)
'position': user.position if user.position else '', # 岗位(辅助信息)
})
return Response({
'message': '获取审核人列表成功',
'code': 0,
'total': len(data),
'data': data
}, status=status.HTTP_200_OK)
class confirmdisplay(APIView):
def post(self, request, *args, **kwargs):
"""
收入确认展示
权限控制:律师只能看自己的数据,管委会和财务部能查看所有
"""
page = request.data.get('page')
per_page = request.data.get('per_page')
times = request.data.get('times')
end_time = request.data.get('end_time')
CustomerID = request.data.get('CustomerID')
token = request.META.get('token')
if not all([page, per_page]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
# 获取当前用户信息
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)
# 检查用户是否有权限查看所有数据
# 获取用户的角色名称列表
user_roles = user.role.values_list('RoleName', flat=True)
user_role_names = list(user_roles)
# 获取用户的部门名称列表
user_departments = user.department.values_list('username', flat=True)
user_department_names = list(user_departments)
# 判断是否有权限查看所有数据:管委会角色或财务部角色或财务部部门
has_all_permission = (
'管委会' in user_role_names or
'财务部' in user_role_names or
'财务部' in user_department_names
)
# 构建查询条件
Q_obj = Q()
if times and end_time:
Q_obj &= Q(times__gte=times) & Q(times__lte=end_time)
if CustomerID:
Q_obj &= Q(CustomerID__icontains=CustomerID)
# 如果没有查看所有数据的权限,只显示自己提交的数据
if not has_all_permission:
Q_obj &= Q(submit=user.username)
income = Income.objects.filter(Q_obj, is_deleted=False).order_by('-id')
total = len(income)
paginator = Paginator(income, per_page)
try:
user_agents_page = paginator.page(page)
except PageNotAnInteger:
user_agents_page = paginator.page(1)
except EmptyPage:
user_agents_page = paginator.page(paginator.num_pages)
data = []
for info in user_agents_page.object_list:
itme = {
'id': info.id,
"times": info.times,
"ContractNo": info.ContractNo,
"CustomerID": info.CustomerID,
"amount": info.amount,
"allocate": info.allocate,
"state": info.state,
"submit": info.submit, # 添加提交人信息
"submit_tiem": info.submit_tiem, # 添加提交时间
}
data.append(itme)
return Response({'message': '展示成功', "total": total, 'data': data, 'code': 0}, status=status.HTTP_200_OK)
class EditIncome(APIView):
def post(self, request, *args, **kwargs):
"""
编辑收入确认
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
times = request.data.get('times')
ContractNo = request.data.get('ContractNo')
CustomerID = request.data.get('CustomerID')
amount = request.data.get('amount')
allocate = request.data.get('allocate')
if not id:
return Response({'status': 'error', 'message': '缺少参数id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
income = Income.objects.get(id=id, is_deleted=False)
except Income.DoesNotExist:
return Response({'status': 'error', 'message': '收入确认不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
update_fields_list = []
if times:
income.times = times
update_fields_list.append('times')
if ContractNo:
income.ContractNo = ContractNo
update_fields_list.append('ContractNo')
if CustomerID:
income.CustomerID = CustomerID
update_fields_list.append('CustomerID')
if amount:
income.amount = amount
update_fields_list.append('amount')
if allocate:
income.allocate = allocate
update_fields_list.append('allocate')
if update_fields_list:
income.save(update_fields=update_fields_list)
return Response({'message': '编辑成功', 'code': 0}, status=status.HTTP_200_OK)
class DeleteIncome(APIView):
def post(self, request, *args, **kwargs):
"""
删除收入确认
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
if not id:
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
income = Income.objects.get(id=id, is_deleted=False)
except Income.DoesNotExist:
return Response({'status': 'error', 'message': '收入确认不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 软删除:更新 is_deleted 字段
income.is_deleted = True
income.save()
return Response({'message': '删除成功', 'code': 0}, status=status.HTTP_200_OK)
class loan(APIView):
def post(self, request, *args, **kwargs):
"""
调账申请
优化后:不需要指定审批人,直接抄送财务(数据来源案件管理)
:param request:
:param args:
:param kwargs:
:return:
"""
times = request.data.get('times')
ContractNo = request.data.get('ContractNo')
CustomerID = request.data.get('CustomerID')
amount = request.data.get('amount')
situation = request.data.get('situation')
case_id = request.data.get('case_id') # 案件ID可选用于关联案件
token = request.META.get('token')
# 移除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()
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,
CustomerID=CustomerID,
amount=amount,
situation=situation,
submit=user.username,
submit_tiem=date_string,
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)
# 直接抄送财务,不需要审批人
from User.utils import create_approval_with_team_logic
create_approval_with_team_logic(
team_name=None,
approvers=None,
title=user.username + "提交调账申请",
content=content,
approval_type="调账申请",
user_id=acc.id,
business_record=acc,
today=date_string
)
# 记录操作日志
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):
page = request.data.get('page')
per_page = request.data.get('per_page')
times = request.data.get('times')
end_time = request.data.get('end_time')
CustomerID = request.data.get('CustomerID')
if not all([page, per_page]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
Q_obj = Q()
if times and end_time:
Q_obj &= Q(times__gte=times) & Q(times__lte=end_time)
if CustomerID:
Q_obj &= Q(CustomerID__icontains=CustomerID)
acc = Accounts.objects.filter(Q_obj, is_deleted=False).order_by('-id')
total = len(acc)
paginator = Paginator(acc, per_page)
try:
user_agents_page = paginator.page(page)
except PageNotAnInteger:
user_agents_page = paginator.page(1)
except EmptyPage:
user_agents_page = paginator.page(paginator.num_pages)
data = []
for info in user_agents_page.object_list:
itme = {
'id': info.id,
"times": info.times,
"ContractNo": info.ContractNo,
"CustomerID": info.CustomerID,
"amount": info.amount,
"situation": info.situation,
"state": info.state,
}
data.append(itme)
return Response({'message': '展示成功', "total": total, 'data': data, 'code': 0}, status=status.HTTP_200_OK)
class EditAccounts(APIView):
def post(self, request, *args, **kwargs):
"""
编辑调账申请
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
times = request.data.get('times')
ContractNo = request.data.get('ContractNo')
CustomerID = request.data.get('CustomerID')
amount = request.data.get('amount')
situation = request.data.get('situation')
if not id:
return Response({'status': 'error', 'message': '缺少参数id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
account = Accounts.objects.get(id=id, is_deleted=False)
except Accounts.DoesNotExist:
return Response({'status': 'error', 'message': '调账申请不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
update_fields_list = []
if times:
account.times = times
update_fields_list.append('times')
if ContractNo:
account.ContractNo = ContractNo
update_fields_list.append('ContractNo')
if CustomerID:
account.CustomerID = CustomerID
update_fields_list.append('CustomerID')
if amount:
account.amount = amount
update_fields_list.append('amount')
if situation:
account.situation = situation
update_fields_list.append('situation')
if update_fields_list:
account.save(update_fields=update_fields_list)
return Response({'message': '编辑成功', 'code': 0}, status=status.HTTP_200_OK)
class DeleteAccounts(APIView):
def post(self, request, *args, **kwargs):
"""
删除调账申请
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
if not id:
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
account = Accounts.objects.get(id=id, is_deleted=False)
except Accounts.DoesNotExist:
return Response({'status': 'error', 'message': '调账申请不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 软删除:更新 is_deleted 字段
account.is_deleted = True
account.save()
return Response({'message': '删除成功', 'code': 0}, status=status.HTTP_200_OK)
class PaymentRequest(APIView):
def post(self, request, *args, **kwargs):
"""
付款申请
:param request:
:param args:
:param kwargs:
:return:
"""
reason = request.data.get('reason')
amount = request.data.get('amount')
times = request.data.get('times')
payee = request.data.get('payee')
bankcard = request.data.get('bankcard')
BankName = request.data.get('BankName')
applicant = request.data.get('applicant')
approvers = request.data.get('approvers') # 审核人列表可选多人团队时需要推荐用户ID数组如[1,2,3],兼容:用户名数组)
# 兼容旧接口:如果传了 personincharge转换为 approvers
personincharge = request.data.get('personincharge')
approvers = normalize_approvers_param(approvers, personincharge)
if not all([times, payee, bankcard, BankName, reason, amount]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
# 获取申请人的团队信息
team_name = None
try:
applicant_user = User.objects.get(username=applicant, is_deleted=False)
team_name = applicant_user.team
except User.DoesNotExist:
pass
from datetime import datetime
now = datetime.now()
# 格式化日期为字符串,格式为 YYYY-MM-DD
date_string = now.strftime("%Y-%m-%d")
pay = Payment.objects.create(
reason=reason,
amount=amount,
times=times,
payee=payee,
bankcard=bankcard,
BankName=BankName,
applicant=applicant,
submit_tiem=date_string,
state="审核中"
)
# 使用统一的审核流程函数
from User.utils import create_approval_with_team_logic
content = f"{applicant}{times}提交了付款申请,付款理由:{reason},付款金额:{amount},付款日期:{times},收款人:{payee},银行卡:{bankcard},开户行:{BankName}"
approval, approvers_order_json, needs_approval = create_approval_with_team_logic(
team_name=team_name,
approvers=approvers,
title=applicant + "提交付款申请",
content=content,
approval_type="付款申请",
user_id=pay.id,
business_record=pay,
today=date_string
)
# 如果返回None且需要审核说明缺少审核人
if approval is None and needs_approval:
return Response({
'status': 'error',
'message': build_missing_approvers_message(team_name, approvers),
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
return Response({'message': '插入成功', 'code': 0}, status=status.HTTP_200_OK)
class PaymentDisplay(APIView):
def post(self, request, *args, **kwargs):
page = request.data.get('page')
per_page = request.data.get('per_page')
times = request.data.get('times')
end_time = request.data.get('end_time')
payee = request.data.get('payee')
if not all([page, per_page]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
Q_obj = Q()
if times and end_time:
Q_obj &= Q(times__gte=times) & Q(times__lte=end_time)
if payee:
Q_obj &= Q(payee__icontains=payee)
pay = Payment.objects.filter(Q_obj, is_deleted=False).order_by('-id')
total = len(pay)
paginator = Paginator(pay, per_page)
try:
user_agents_page = paginator.page(page)
except PageNotAnInteger:
user_agents_page = paginator.page(1)
except EmptyPage:
user_agents_page = paginator.page(paginator.num_pages)
data = []
for info in user_agents_page.object_list:
itme = {
'id': info.id,
"times": info.times,
"payee": info.payee,
"reason": info.reason,
"amount": info.amount,
"bankcard": info.bankcard,
"BankName": info.BankName,
"applicant": info.applicant,
"state": info.state,
}
data.append(itme)
return Response({'message': '展示成功', "total": total, 'data': data, 'code': 0}, status=status.HTTP_200_OK)
class EditPayment(APIView):
def post(self, request, *args, **kwargs):
"""
编辑付款申请
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
reason = request.data.get('reason')
amount = request.data.get('amount')
times = request.data.get('times')
payee = request.data.get('payee')
bankcard = request.data.get('bankcard')
BankName = request.data.get('BankName')
applicant = request.data.get('applicant')
if not id:
return Response({'status': 'error', 'message': '缺少参数id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
payment = Payment.objects.get(id=id, is_deleted=False)
except Payment.DoesNotExist:
return Response({'status': 'error', 'message': '付款申请不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
update_fields_list = []
if reason:
payment.reason = reason
update_fields_list.append('reason')
if amount:
payment.amount = amount
update_fields_list.append('amount')
if times:
payment.times = times
update_fields_list.append('times')
if payee:
payment.payee = payee
update_fields_list.append('payee')
if bankcard:
payment.bankcard = bankcard
update_fields_list.append('bankcard')
if BankName:
payment.BankName = BankName
update_fields_list.append('BankName')
if applicant:
payment.applicant = applicant
update_fields_list.append('applicant')
if update_fields_list:
payment.save(update_fields=update_fields_list)
return Response({'message': '编辑成功', 'code': 0}, status=status.HTTP_200_OK)
class DeletePayment(APIView):
def post(self, request, *args, **kwargs):
"""
删除付款申请
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
if not id:
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
payment = Payment.objects.get(id=id, is_deleted=False)
except Payment.DoesNotExist:
return Response({'status': 'error', 'message': '付款申请不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 软删除:更新 is_deleted 字段
payment.is_deleted = True
payment.save()
return Response({'message': '删除成功', 'code': 0}, status=status.HTTP_200_OK)
class reimbursement(APIView):
def post(self, request, *args, **kwargs):
"""
报销申请
优化后:根据团队类型判断是否需要审批
- 个人团队personal/独立律师):不触发审批,直接抄送财务
- 团队team/团队律师):需要多级审批,按顺序依次审批,最后抄送财务
:param request:
:param args:
:param kwargs:
:return:
"""
person = request.data.get('person')
times = request.data.get('times')
reason = request.data.get('reason')
amount = request.data.get('amount')
FeeDescription = request.data.get('FeeDescription')
approvers = request.data.get('approvers') # 审批人列表数组仅团队类型需要推荐用户ID数组如[1,2,3],兼容:用户名数组)
# 兼容旧接口:如果传了 personincharge转换为 approvers
personincharge = request.data.get('personincharge')
approvers = normalize_approvers_param(approvers, personincharge)
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=date_string,
state="审核中" if (team and team.team_type == 'team') else "待财务处理"
)
# 使用统一的审核流程函数
from User.utils import create_approval_with_team_logic
content = f"{person}{times}提交了报销申请,报销理由:{reason},付款金额:{amount},付款日期:{times},费用说明:{FeeDescription}"
approval, approvers_order_json, needs_approval = create_approval_with_team_logic(
team_name=team_name,
approvers=approvers,
title=person + "报销申请",
content=content,
approval_type="报销申请",
user_id=reim.id,
business_record=reim,
today=date_string
)
# 如果返回None且需要审核说明缺少审核人
if approval is None and needs_approval:
return Response({
'status': 'error',
'message': build_missing_approvers_message(team_name, approvers),
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 记录操作日志
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,
'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):
page = request.data.get('page')
per_page = request.data.get('per_page')
times = request.data.get('times')
end_time = request.data.get('end_time')
person = request.data.get('person')
if not all([page, per_page]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
Q_obj = Q()
if times and end_time:
Q_obj &= Q(times__gte=times) & Q(times__lte=end_time)
if person:
Q_obj &= Q(person__icontains=person)
rei = Reimbursement.objects.filter(Q_obj, is_deleted=False).order_by('-id')
total = len(rei)
paginator = Paginator(rei, per_page)
try:
user_agents_page = paginator.page(page)
except PageNotAnInteger:
user_agents_page = paginator.page(1)
except EmptyPage:
user_agents_page = paginator.page(paginator.num_pages)
data = []
for info in user_agents_page.object_list:
itme = {
"id": info.id,
"times": info.times,
"person": info.person,
"reason": info.reason,
"amount": info.amount,
"FeeDescription": info.FeeDescription,
"state": info.state,
}
data.append(itme)
return Response({'message': '展示成功', "total": total, 'data': data, 'code': 0}, status=status.HTTP_200_OK)
class EditReimbursement(APIView):
def post(self, request, *args, **kwargs):
"""
编辑报销申请
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
person = request.data.get('person')
times = request.data.get('times')
reason = request.data.get('reason')
amount = request.data.get('amount')
FeeDescription = request.data.get('FeeDescription')
if not id:
return Response({'status': 'error', 'message': '缺少参数id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
reimbursement = Reimbursement.objects.get(id=id, is_deleted=False)
except Reimbursement.DoesNotExist:
return Response({'status': 'error', 'message': '报销申请不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
update_fields_list = []
if person:
reimbursement.person = person
update_fields_list.append('person')
if times:
reimbursement.times = times
update_fields_list.append('times')
if reason:
reimbursement.reason = reason
update_fields_list.append('reason')
if amount:
reimbursement.amount = amount
update_fields_list.append('amount')
if FeeDescription:
reimbursement.FeeDescription = FeeDescription
update_fields_list.append('FeeDescription')
if update_fields_list:
reimbursement.save(update_fields=update_fields_list)
return Response({'message': '编辑成功', 'code': 0}, status=status.HTTP_200_OK)
class DeleteReimbursement(APIView):
def post(self, request, *args, **kwargs):
"""
删除报销申请
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
if not id:
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
reimbursement = Reimbursement.objects.get(id=id, is_deleted=False)
except Reimbursement.DoesNotExist:
return Response({'status': 'error', 'message': '报销申请不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 软删除:更新 is_deleted 字段
reimbursement.is_deleted = True
reimbursement.save()
return Response({'message': '删除成功', 'code': 0}, status=status.HTTP_200_OK)
class Change(APIView):
def post(self, request, *args, **kwargs):
"""
工资/奖金变更
优化后:增加提交人字段,明确是谁提交的
:param request:
:param args:
:param kwargs:
:return:
"""
username = request.data.get('username')
type = request.data.get('type')
Instructions = request.data.get('Instructions')
approvers = request.data.get('approvers') # 审核人列表可选多人团队时需要推荐用户ID数组如[1,2,3],兼容:用户名数组)
# 兼容旧接口:如果传了 personincharge转换为 approvers
personincharge = request.data.get('personincharge')
approvers = normalize_approvers_param(approvers, 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(
username=username,
type=type,
Instructions=Instructions,
times=now.strftime("%Y-%m-%d"),
state="审核中",
submitter=submitter # 记录提交人
)
# 获取用户的团队信息
team_name = user.team if user else None
from User.utils import create_approval_with_team_logic
content = f"{submitter}{now.strftime('%Y-%m-%d')}提交了工资/奖金变更,类型:{type},调整说明:{Instructions}"
# 使用统一的审核流程函数
approval, approvers_order_json, needs_approval = create_approval_with_team_logic(
team_name=team_name,
approvers=approvers,
title=username + "工资/奖金变更",
content=content,
approval_type="工资/奖金变更",
user_id=bonus.id,
business_record=bonus,
today=now.strftime("%Y-%m-%d")
)
# 如果返回None且需要审核说明缺少审核人
if approval is None and needs_approval:
return Response({
'status': 'error',
'message': build_missing_approvers_message(team_name, approvers),
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 记录操作日志
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):
page = request.data.get('page')
per_page = request.data.get('per_page')
times = request.data.get('times')
end_time = request.data.get('end_time')
username = request.data.get('username')
if not all([page, per_page]):
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
Q_obj = Q()
if times and end_time:
Q_obj &= Q(times__gte=times) & Q(times__lte=end_time)
if username:
Q_obj &= Q(username__icontains=username)
bon = BonusChange.objects.filter(Q_obj, is_deleted=False).order_by('-id')
total = len(bon)
paginator = Paginator(bon, per_page)
try:
user_agents_page = paginator.page(page)
except PageNotAnInteger:
user_agents_page = paginator.page(1)
except EmptyPage:
user_agents_page = paginator.page(paginator.num_pages)
data = []
for info in user_agents_page.object_list:
itme = {
"id": info.id,
"times": info.times,
"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)
class EditBonusChange(APIView):
def post(self, request, *args, **kwargs):
"""
编辑工资/奖金变更
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
username = request.data.get('username')
type = request.data.get('type')
Instructions = request.data.get('Instructions')
if not id:
return Response({'status': 'error', 'message': '缺少参数id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
bonus = BonusChange.objects.get(id=id, is_deleted=False)
except BonusChange.DoesNotExist:
return Response({'status': 'error', 'message': '工资/奖金变更不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
update_fields_list = []
if username:
bonus.username = username
update_fields_list.append('username')
if type:
bonus.type = type
update_fields_list.append('type')
if Instructions:
bonus.Instructions = Instructions
update_fields_list.append('Instructions')
if update_fields_list:
bonus.save(update_fields=update_fields_list)
return Response({'message': '编辑成功', 'code': 0}, status=status.HTTP_200_OK)
class DeleteBonusChange(APIView):
def post(self, request, *args, **kwargs):
"""
删除工资/奖金变更
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id')
if not id:
return Response({'status': 'error', 'message': '缺少参数', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
bonus = BonusChange.objects.get(id=id, is_deleted=False)
except BonusChange.DoesNotExist:
return Response({'status': 'error', 'message': '工资/奖金变更不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 软删除:更新 is_deleted 字段
bonus.is_deleted = True
bonus.save()
return Response({'message': '删除成功', 'code': 0}, status=status.HTTP_200_OK)
class UserDeparture(APIView):
def post(self, request, *args, **kwargs):
"""
离职财务登记
优化后:只需填写姓名和离职时间,其余字段从人事管理中同步
离职工资由审批人填写
如果用户有案件(作为承办人员),需要先转移案件才能完成离职登记
:param request:
:param args:
:param kwargs:
:return:
"""
username = request.data.get('username')
Dateofdeparture = request.data.get('Dateofdeparture') # 离职时间
approvers = request.data.get('approvers') # 审核人列表可选多人团队时需要推荐用户ID数组如[1,2,3],兼容:用户名数组)
# 兼容旧接口:如果传了 personincharge转换为 approvers
personincharge = request.data.get('personincharge')
approvers = normalize_approvers_param(approvers, personincharge)
# 只验证必填字段:姓名和离职时间
if not all([username, Dateofdeparture]):
return Response({'status': 'error', 'message': '缺少参数:姓名和离职时间不能为空', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
try:
user = User.objects.get(username=username, is_deleted=False)
except User.DoesNotExist:
return Response({'status': 'error', 'message': '用户不存在或已被删除', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 检查用户是否有案件(预立案 + 正式案件)
prefiling_cases = PreFiling.objects.filter(Undertaker=username, is_deleted=False)
prefiling_count = prefiling_cases.count()
def _user_in_responsible(responsiblefor_data, target_username):
if isinstance(responsiblefor_data, dict):
return target_username in responsiblefor_data.values()
return False
formal_case_list = []
formal_project_list = []
# 正式案件Case 表(责任人信息来自立项同步)
case_qs = Case.objects.filter(is_deleted=False, responsiblefor__icontains=username)
for case in case_qs:
try:
responsible_data = json.loads(case.responsiblefor) if case.responsiblefor else {}
except (json.JSONDecodeError, TypeError):
responsible_data = case.responsiblefor if case.responsiblefor else {}
if _user_in_responsible(responsible_data, username):
formal_case_list.append({
'id': case.id,
'project_id': case.project_id,
'contract_no': case.contract_no,
'project_type': case.project_type,
'times': case.times
})
# 立项登记:尚未生成案件但已绑定负责人
project_qs = ProjectRegistration.objects.filter(is_deleted=False, responsiblefor__icontains=username)
for project in project_qs:
try:
responsible_data = json.loads(project.responsiblefor) if project.responsiblefor else {}
except (json.JSONDecodeError, TypeError):
responsible_data = project.responsiblefor if project.responsiblefor else {}
if _user_in_responsible(responsible_data, username):
formal_project_list.append({
'id': project.id,
'contract_no': project.ContractNo,
'type': project.type,
'times': project.times
})
total_case_count = prefiling_count + len(formal_case_list) + len(formal_project_list)
if total_case_count > 0:
# 用户有案件,需要先转移案件才能离职
prefiling_list = []
for case in prefiling_cases:
prefiling_list.append({
'id': case.id,
'times': case.times,
'client_username': case.client_username,
'party_username': case.party_username,
'description': case.description
})
return Response({
'status': 'error',
'message': f'该用户还有{total_case_count}个案件未转移,请先转移案件后再进行离职登记',
'code': 1,
'data': {
'has_cases': True,
'case_count': total_case_count,
'prefiling_cases': prefiling_list,
'formal_cases': formal_case_list,
'formal_projects': formal_project_list
}
}, status=status.HTTP_400_BAD_REQUEST)
# 用户没有案件,可以正常离职登记
try:
Dateofdepartures = datetime.datetime.strptime(Dateofdeparture, "%Y-%m-%d")
except ValueError:
return Response({'status': 'error', 'message': '离职时间格式错误请使用YYYY-MM-DD格式', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 更新用户离职时间和状态
user.Dateofdeparture = Dateofdepartures
user.state = "已离职"
user.save(update_fields=['Dateofdeparture', 'state'])
# 根据团队类型判断是否需要审批
# 规则:
# - 个人团队personal/独立律师):不触发审批
# - 团队team/团队律师):需要审批,审批人需要指定结算工资
from User.models import Team
# 获取用户的团队信息
team_name = user.team # 用户的团队名称CharField
team = None
if team_name:
try:
team = Team.objects.get(name=team_name, is_deleted=False)
except Team.DoesNotExist:
# 如果团队不存在,默认按团队类型处理(需要审批)
pass
# 使用统一的审核流程函数
from User.utils import create_approval_with_team_logic
today = datetime.datetime.now()
formatted_date = today.strftime("%Y-%m-%d")
# 从人事管理同步用户信息,构建审批内容
# 包含用户的基本信息:姓名、岗位、部门、团队等
content_parts = [f"{user.username}{Dateofdeparture}办理离职登记"]
# 同步岗位信息
if user.position:
content_parts.append(f"岗位:{user.position}")
# 同步部门信息
departments = user.department.filter(is_deleted=False)
if departments.exists():
dept_names = [dept.username for dept in departments]
content_parts.append(f"部门:{', '.join(dept_names)}")
# 同步团队信息
if user.team:
content_parts.append(f"团队:{user.team}")
# 离职工资由审批人填写,初始状态为"待审批人指定"
content_parts.append("结算工资:待审批人指定")
content = "".join(content_parts)
approval, approvers_order_json, needs_approval = create_approval_with_team_logic(
team_name=team_name,
approvers=approvers,
title=user.username + "离职财务登记",
content=content,
approval_type="离职财务登记",
user_id=str(user.id),
business_record=user, # User对象没有approvers_order字段但可以更新state
today=formatted_date
)
# 如果返回None且需要审核说明缺少审核人
if approval is None and needs_approval:
return Response({
'status': 'error',
'message': build_missing_approvers_message(team_name, approvers),
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 如果是个人团队,不创建审批记录,但记录日志
if team and team.team_type == 'personal' and approval is None:
new_data = {
'user_id': user.id,
'username': user.username,
'Dateofdeparture': Dateofdeparture,
'state': user.state,
'team_type': 'personal',
'note': '个人团队,无需审批'
}
log_operation(
request=request,
operation_type='CREATE',
module='Finance',
action='新增离职财务登记(个人团队)',
target_type='User',
target_id=user.id,
target_name=user.username,
new_data=new_data,
remark=f'新增离职财务登记:{user.username},离职时间 {Dateofdeparture}(个人团队,无需审批)'
)
else:
# 如果创建了审批记录,记录操作日志
if approval:
new_data = {
'user_id': user.id,
'username': user.username,
'Dateofdeparture': Dateofdeparture,
'state': user.state,
'position': user.position,
'team': user.team,
'settlement_salary': '待审批人指定'
}
log_operation(
request=request,
operation_type='CREATE',
module='Finance',
action='新增离职财务登记',
target_type='User',
target_id=user.id,
target_name=user.username,
new_data=new_data,
remark=f'新增离职财务登记:{user.username},离职时间 {Dateofdeparture}'
)
return Response({
'message': '离职登记成功',
'code': 0,
'data': {
'has_cases': False,
'case_count': 0,
'needs_approval': team is None or team.team_type != 'personal' # 是否需要审批
}
}, status=status.HTTP_200_OK)
class UserDepartureDetail(APIView):
def post(self, request, *args, **kwargs):
"""
离职登记列表查询
:param request:
:param args:
:param kwargs:
:return:
"""
page = request.data.get('page')
per_page = request.data.get('per_page')
username = request.data.get('username') # 用户名搜索
times = request.data.get('times') # 开始时间
end_time = request.data.get('end_time') # 结束时间
state = request.data.get('state') # 审批状态
if not all([page, per_page]):
return Response({'status': 'error', 'message': '缺少参数:页码和每页数量不能为空', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 查询离职财务登记的审批记录
Q_obj = Q(type="离职财务登记", is_deleted=False)
if username:
Q_obj &= Q(title__icontains=username) | Q(content__icontains=username)
if times and end_time:
Q_obj &= Q(times__gte=times) & Q(times__lte=end_time)
if state:
Q_obj &= Q(state=state)
approvals = Approval.objects.filter(Q_obj).order_by('-id')
total = len(approvals)
paginator = Paginator(approvals, per_page)
try:
user_agents_page = paginator.page(page)
except PageNotAnInteger:
user_agents_page = paginator.page(1)
except EmptyPage:
user_agents_page = paginator.page(paginator.num_pages)
data = []
for approval in user_agents_page.object_list:
# 获取用户信息
try:
user = User.objects.get(id=int(approval.user_id), is_deleted=False)
user_info = {
'id': user.id,
'username': user.username,
'account': user.account,
'position': user.position,
'Dateofdeparture': user.Dateofdeparture.strftime("%Y-%m-%d") if user.Dateofdeparture else None,
'state': user.state
}
except (User.DoesNotExist, ValueError):
user_info = {
'id': None,
'username': '用户不存在',
'account': '',
'position': '',
'Dateofdeparture': None,
'state': ''
}
# 从content中解析结算工资
settlement_salary = None
if approval.content and "结算工资:" in approval.content:
import re
# 匹配格式结算工资xxx元 或 结算工资:待审批人指定
match = re.search(r'结算工资:([^]*)', approval.content)
if match:
salary_str = match.group(1).strip()
# 如果包含"元"字,提取数字部分
if '' in salary_str:
salary_match = re.search(r'(\d+(?:\.\d+)?)', salary_str)
if salary_match:
settlement_salary = salary_match.group(1)
elif salary_str == "待审批人指定":
settlement_salary = None
else:
# 尝试直接提取数字(处理其他格式)
salary_match = re.search(r'(\d+(?:\.\d+)?)', salary_str)
if salary_match:
settlement_salary = salary_match.group(1)
item = {
'id': approval.id,
'title': approval.title,
'content': approval.content,
'times': approval.times.strftime("%Y-%m-%d") if approval.times else None,
'completeTiem': approval.completeTiem.strftime("%Y-%m-%d") if approval.completeTiem else None,
'personincharge': approval.personincharge,
'state': approval.state,
'type': approval.type,
'user_id': approval.user_id,
'user_info': user_info,
'settlement_salary': settlement_salary # 单独返回离职工资字段
}
data.append(item)
return Response({
'message': '查询成功',
'total': total,
'data': data,
'code': 0
}, status=status.HTTP_200_OK)
class EditUserDeparture(APIView):
def post(self, request, *args, **kwargs):
"""
编辑离职登记
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id') # 审批记录ID
Dateofdeparture = request.data.get('Dateofdeparture') # 离职时间
personincharge = request.data.get('personincharge') # 负责人
if not id:
return Response({'status': 'error', 'message': '缺少参数id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
approval = Approval.objects.get(id=id, type="离职财务登记", is_deleted=False)
except Approval.DoesNotExist:
return Response({'status': 'error', 'message': '离职登记记录不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 获取用户信息
try:
user = User.objects.get(id=int(approval.user_id), is_deleted=False)
except (User.DoesNotExist, ValueError):
return Response({'status': 'error', 'message': '用户不存在', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
update_fields_list = []
approval_update_fields = []
# 更新离职时间
if Dateofdeparture:
try:
Dateofdepartures = datetime.datetime.strptime(Dateofdeparture, "%Y-%m-%d")
user.Dateofdeparture = Dateofdepartures
update_fields_list.append('Dateofdeparture')
# 更新审批记录内容
approval.content = user.username + "" + Dateofdeparture + "办理离职登记"
approval_update_fields.append('content')
except ValueError:
return Response({'status': 'error', 'message': '离职时间格式错误请使用YYYY-MM-DD格式', 'code': 1},
status=status.HTTP_400_BAD_REQUEST)
# 更新负责人
if personincharge:
approval.personincharge = personincharge
approval_update_fields.append('personincharge')
# 保存更新
if update_fields_list:
user.save(update_fields=update_fields_list)
if approval_update_fields:
approval.save(update_fields=approval_update_fields)
return Response({'message': '编辑成功', 'code': 0}, status=status.HTTP_200_OK)
class DeleteUserDeparture(APIView):
def post(self, request, *args, **kwargs):
"""
删除离职登记
删除离职登记记录,并恢复用户状态为"在职",清空离职时间
:param request:
:param args:
:param kwargs:
:return:
"""
id = request.data.get('id') # 审批记录ID
if not id:
return Response({'status': 'error', 'message': '缺少参数id', 'code': 1}, status=status.HTTP_400_BAD_REQUEST)
try:
approval = Approval.objects.get(id=id, type="离职财务登记", is_deleted=False)
except Approval.DoesNotExist:
return Response({'status': 'error', 'message': '离职登记记录不存在', 'code': 1},
status=status.HTTP_404_NOT_FOUND)
# 获取用户信息
try:
user = User.objects.get(id=int(approval.user_id), is_deleted=False)
except (User.DoesNotExist, ValueError):
return Response({'status': 'error', 'message': '用户不存在', 'code': 1}, status=status.HTTP_404_NOT_FOUND)
# 记录操作前的数据
old_data = {
'user_id': user.id,
'username': user.username,
'state': user.state,
'dateofdeparture': str(user.Dateofdeparture) if user.Dateofdeparture else None,
'approval_id': approval.id
}
# 恢复用户状态
user.state = "在职"
user.Dateofdeparture = None
user.save(update_fields=['state', 'Dateofdeparture'])
# 软删除:更新 is_deleted 字段
approval.is_deleted = True
approval.save()
# 记录操作日志
log_operation(
request=request,
operation_type='DELETE',
module='Finance',
action='删除离职登记',
target_type='Approval',
target_id=approval.id,
target_name=f'离职登记-{user.username}',
old_data=old_data,
new_data={'user_id': user.id, 'username': user.username, 'state': '在职'},
remark=f'删除离职登记,恢复用户 {user.username} 状态为"在职"'
)
return Response({'message': '删除成功,用户状态已恢复为"在职"', 'code': 0}, status=status.HTTP_200_OK)
class SearchCaseByContractNo(APIView):
"""通过合同号搜索案件(用于增开票功能)"""
def post(self, request, *args, **kwargs):
"""
通过合同号搜索案件支持从Case和ProjectRegistration中搜索
返回案件信息,包括合同号、负责人、项目类型等
"""
contract_no = request.data.get('contract_no', '').strip()
if not contract_no:
return Response({
'status': 'error',
'message': '缺少参数contract_no合同号',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
from business.models import Case, ProjectRegistration
import json
results = []
# 1. 从Case案件管理中搜索
cases = Case.objects.filter(
contract_no__icontains=contract_no,
is_deleted=False
).select_related('project').order_by('-id')[:20] # 限制返回数量
for case in cases:
# 获取案件信息
case_contract_no = case.contract_no
case_responsiblefor = case.responsiblefor
case_project_type = case.project_type
case_client_name = case.client_name
case_party_name = case.party_name
case_project_description = case.project_description
case_charge = case.charge
# 如果案件信息不完整尝试从关联的ProjectRegistration获取
if case.project:
project = case.project
case_contract_no = case_contract_no or project.ContractNo
case_responsiblefor = case_responsiblefor or project.responsiblefor
case_project_type = case_project_type or project.type
case_client_name = case_client_name or project.client_info
case_party_name = case_party_name or project.party_info
case_project_description = case_project_description or project.description
case_charge = case_charge or project.charge
# 解析负责人信息
responsiblefor_dict = {}
responsible_person = ''
try:
if case_responsiblefor:
if isinstance(case_responsiblefor, str):
responsiblefor_dict = json.loads(case_responsiblefor)
else:
responsiblefor_dict = case_responsiblefor
responsible_person = responsiblefor_dict.get('responsible_person', '')
except (json.JSONDecodeError, TypeError, AttributeError):
responsible_person = str(case_responsiblefor) if case_responsiblefor else ''
results.append({
'id': case.id,
'type': 'case', # 标识这是案件管理中的案件
'contract_no': case_contract_no or '',
'responsiblefor': responsiblefor_dict if responsiblefor_dict else case_responsiblefor,
'responsible_person': responsible_person, # 负责人姓名(用于显示)
'project_type': case_project_type or '',
'client_name': case_client_name or '',
'party_name': case_party_name or '',
'project_description': case_project_description or '',
'charge': case_charge or '',
'times': case.times,
'project_id': case.project_id
})
# 2. 从ProjectRegistration立项登记中搜索如果还没有生成案件
projects = ProjectRegistration.objects.filter(
ContractNo__icontains=contract_no,
is_deleted=False
).order_by('-id')[:20]
# 过滤掉已经生成案件的立项登记
existing_project_ids = [case.project_id for case in cases if case.project_id]
projects = projects.exclude(id__in=existing_project_ids)
for project in projects:
# 解析负责人信息
responsiblefor_dict = {}
responsible_person = ''
try:
if project.responsiblefor:
if isinstance(project.responsiblefor, str):
responsiblefor_dict = json.loads(project.responsiblefor)
else:
responsiblefor_dict = project.responsiblefor
responsible_person = responsiblefor_dict.get('responsible_person', '')
except (json.JSONDecodeError, TypeError, AttributeError):
responsible_person = str(project.responsiblefor) if project.responsiblefor else ''
results.append({
'id': project.id,
'type': 'project', # 标识这是立项登记
'contract_no': project.ContractNo,
'responsiblefor': responsiblefor_dict if responsiblefor_dict else project.responsiblefor,
'responsible_person': responsible_person, # 负责人姓名(用于显示)
'project_type': project.type or '',
'client_name': project.client_info or '',
'party_name': project.party_info or '',
'project_description': project.description or '',
'charge': project.charge or '',
'times': project.times,
'project_id': None
})
return Response({
'message': '搜索成功',
'code': 0,
'data': results,
'total': len(results)
}, status=status.HTTP_200_OK)
class AddInvoice(APIView):
"""增开票功能:通过合同号搜索,提取案件管理板块内容,负责人直接同步,其余信息手填,填好后直接抄送财务"""
def post(self, request, *args, **kwargs):
"""
增开票申请
- 通过合同号搜索选择案件case_id或project_id
- 从案件管理中提取信息(合同号、负责人等)
- 负责人自动同步
- 其余信息手填
- 提交后直接抄送财务(不需要审批流程)
"""
# 案件选择(二选一)
case_id = request.data.get('case_id') # 案件管理ID
project_id = request.data.get('project_id') # 立项登记ID
contract_no = request.data.get('contract_no') # 合同号(用于搜索,可选)
# 手填信息
amount = request.data.get('amount') # 开票金额
type = request.data.get('type') # 开票类型
unit = request.data.get('unit') # 开票单位全称
number = request.data.get('number') # 纳税人识别号
address_telephone = request.data.get('address_telephone') # 地址/电话
bank = request.data.get('bank') # 银行卡
username = request.data.get('username') # 提交人
# 必填字段验证
missing_params = []
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(提交人)')
if missing_params:
return Response({
'status': 'error',
'message': f'缺少必填参数: {", ".join(missing_params)}',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 必须提供case_id或project_id之一
if not case_id and not project_id:
return Response({
'status': 'error',
'message': '请选择案件提供case_id案件管理ID或project_id立项登记ID',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
from business.models import Case, ProjectRegistration
import json
# 从案件管理中提取信息
ContractNo = None
personincharge = None
case_info = None
if case_id:
# 从Case案件管理中获取信息
try:
case = Case.objects.select_related('project').get(id=case_id, is_deleted=False)
# 获取合同号
ContractNo = case.contract_no
# 获取负责人信息
personincharge = case.responsiblefor
# 如果案件信息不完整从关联的ProjectRegistration获取
if case.project:
project = case.project
ContractNo = ContractNo or project.ContractNo
personincharge = personincharge or project.responsiblefor
# 解析负责人信息,提取负责人姓名用于显示
responsible_person = ''
try:
if personincharge:
if isinstance(personincharge, str):
responsiblefor_dict = json.loads(personincharge)
else:
responsiblefor_dict = personincharge
responsible_person = responsiblefor_dict.get('responsible_person', '')
except (json.JSONDecodeError, TypeError, AttributeError):
responsible_person = str(personincharge) if personincharge else ''
case_info = {
'id': case.id,
'type': 'case',
'contract_no': ContractNo,
'responsible_person': responsible_person
}
except Case.DoesNotExist:
return Response({
'status': 'error',
'message': '案件不存在或已被删除',
'code': 1
}, status=status.HTTP_404_NOT_FOUND)
elif project_id:
# 从ProjectRegistration立项登记中获取信息
try:
project = ProjectRegistration.objects.get(id=project_id, is_deleted=False)
ContractNo = project.ContractNo
personincharge = project.responsiblefor
# 解析负责人信息,提取负责人姓名用于显示
responsible_person = ''
try:
if personincharge:
if isinstance(personincharge, str):
responsiblefor_dict = json.loads(personincharge)
else:
responsiblefor_dict = personincharge
responsible_person = responsiblefor_dict.get('responsible_person', '')
except (json.JSONDecodeError, TypeError, AttributeError):
responsible_person = str(personincharge) if personincharge else ''
case_info = {
'id': project.id,
'type': 'project',
'contract_no': ContractNo,
'responsible_person': responsible_person
}
except ProjectRegistration.DoesNotExist:
return Response({
'status': 'error',
'message': '立项登记不存在或已被删除',
'code': 1
}, status=status.HTTP_404_NOT_FOUND)
# 验证合同号和负责人是否获取成功
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)
# 处理负责人信息提取负责人姓名确保不超过100字符限制
personincharge_display = ''
try:
if isinstance(personincharge, str):
# 尝试解析JSON
try:
responsiblefor_dict = json.loads(personincharge)
personincharge_display = responsiblefor_dict.get('responsible_person', '')
except (json.JSONDecodeError, TypeError):
# 如果不是JSON直接使用字符串
personincharge_display = personincharge
else:
# 如果是字典,直接提取
personincharge_display = personincharge.get('responsible_person', '') if isinstance(personincharge, dict) else str(personincharge)
except (AttributeError, TypeError):
personincharge_display = str(personincharge) if personincharge else ''
# 确保不超过100字符Invoice.personincharge字段限制
if len(personincharge_display) > 100:
personincharge_display = personincharge_display[:100]
# 如果提取失败使用原始值但需要确保是字符串且不超过100字符
if not personincharge_display:
personincharge_str = str(personincharge) if personincharge else ''
personincharge_display = personincharge_str[:100] if len(personincharge_str) > 100 else personincharge_str
# 创建开票记录
today = datetime.datetime.now()
formatted_date = today.strftime("%Y-%m-%d")
invoice = Invoice.objects.create(
ContractNo=ContractNo,
personincharge=personincharge_display, # 负责人姓名字符串不超过100字符
amount=amount,
type=type,
unit=unit,
number=number,
address_telephone=address_telephone,
bank=bank,
state="待财务处理", # 直接设为待财务处理,不需要审批
username=username,
times=formatted_date,
)
# 直接抄送财务,不需要审批流程
from User.utils import create_approval_with_team_logic
# 构建审批内容
responsible_person_display = personincharge_display # 使用处理后的负责人姓名
content_parts = [
f"{username}{formatted_date}提交了增开票申请",
f"合同编号:{ContractNo}",
f"负责人:{responsible_person_display}",
f"开票金额:{amount}",
f"开票类型:{type}",
f"开票单位:{unit}"
]
content = "".join(content_parts)
# 直接抄送财务team_name=None, approvers=None
approval, approvers_order_json, needs_approval = create_approval_with_team_logic(
team_name=None,
approvers=None,
title=username + "提交增开票申请",
content=content,
approval_type="增开票申请",
user_id=invoice.id,
business_record=invoice,
today=formatted_date
)
# 记录操作日志
new_data = {
'id': invoice.id,
'contract_no': invoice.ContractNo,
'personincharge': invoice.personincharge,
'amount': invoice.amount,
'type': invoice.type,
'unit': invoice.unit,
'case_id': case_id if case_id else None,
'project_id': project_id if project_id else None
}
log_operation(
request=request,
operation_type='CREATE',
module='Finance',
action='新增增开票申请',
target_type='Invoice',
target_id=invoice.id,
target_name=invoice.ContractNo,
new_data=new_data,
remark=f'新增增开票申请:合同号 {invoice.ContractNo},负责人 {responsible_person_display},金额 {invoice.amount},已直接抄送财务'
)
return Response({
'message': '提交成功,已直接抄送财务',
'code': 0,
'data': {
'id': invoice.id,
'ContractNo': invoice.ContractNo,
'personincharge': invoice.personincharge,
'responsible_person': responsible_person_display,
'state': invoice.state,
'approval_id': approval.id if approval else None
}
}, status=status.HTTP_200_OK)