Files
jyls_django/finance/views.py
2026-01-14 11:01:02 +08:00

2486 lines
95 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 format_personincharge, log_operation
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
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')
if personincharge and not approvers:
# 兼容旧接口:将单个 personincharge 转换为 approvers 数组
approvers = [personincharge] if personincharge else None
# 调试日志:记录传入的 approvers 格式
if approvers:
import logging
logger = logging.getLogger(__name__)
logger.debug(f"UserRegister - approvers 原始值: {approvers}, 类型: {type(approvers)}")
print(approvers)
print(type(approvers))
# 只验证必填字段:姓名、入职时间、工资
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:
# 检查团队是否配置了审核人
team_has_approvers = False
team_approvers_list = []
team_approvers_valid = []
if team and team.team_type == 'team':
team_approvers = team.approvers.filter(is_deleted=False)
team_has_approvers = team_approvers.exists()
if team_has_approvers:
team_approvers_list = [approver.username for approver in team_approvers]
# 验证团队配置的审核人是否有效
for approver_name in team_approvers_list:
if User.objects.filter(username=approver_name, is_deleted=False).exists():
team_approvers_valid.append(approver_name)
# 检查传入的审核人是否存在
approvers_list = []
if approvers:
from User.utils import parse_approvers
approvers_list = parse_approvers(approvers)
# 验证审核人是否存在
invalid_approvers = []
for approver_name in approvers_list:
if not User.objects.filter(username=approver_name, is_deleted=False).exists():
invalid_approvers.append(approver_name)
if invalid_approvers:
return Response({
'status': 'error',
'message': f'审核人不存在或已被删除: {", ".join(invalid_approvers)},请检查审核人是否有效',
'code': 1
}, status=status.HTTP_400_BAD_REQUEST)
# 构建错误信息
if team_has_approvers:
if approvers_list:
# 传入了审核人但可能有问题(这种情况不应该发生,因为已经验证过了)
error_message = '团队类型需要指定有效的审核人请检查传入的approvers参数中的审核人是否有效'
else:
# 团队已配置审核人,但可能审核人不存在或已被删除
if team_approvers_valid:
# 团队有有效的审核人,但 create_approval_with_team_logic 返回了 None
# 这可能是其他原因导致的,提供通用错误信息
error_message = f'创建审批流程失败,请检查团队"{team_name}"配置。团队当前配置的审核人:{", ".join(team_approvers_valid)}'
else:
# 团队配置的审核人都无效
error_message = f'团队"{team_name}"配置的审核人无效或已被删除。团队当前配置的审核人:{", ".join(team_approvers_list) if team_approvers_list else ""}。请在前端传递approvers参数推荐格式[用户ID数组],如[1,2,3])或为团队重新配置有效的审核人'
else:
# 团队未配置审核人
error_message = f'团队类型需要指定审核人。团队"{team_name}"未配置审核人请在前端传递approvers参数推荐格式[用户ID数组],如[1,2,3])或为团队配置审核人'
return Response({
'status': 'error',
'message': error_message,
'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)
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()
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': user.team,
'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):
"""
收入确认
优化后:收入分配由审批人指定,提交时不填写分配
数据来源案件管理
: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')
allocate = request.data.get('allocate') # 改为可选,由审批人指定
token = request.META.get('token')
approvers = request.data.get('approvers') # 审核人列表可选多人团队时需要推荐用户ID数组如[1,2,3],兼容:用户名数组)
# 兼容旧接口:如果传了 personincharge转换为 approvers
personincharge = request.data.get('personincharge')
if personincharge and not approvers:
approvers = [personincharge] if personincharge else None
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)
# 必填字段验证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()
date_string = now.strftime("%Y-%m-%d")
# 创建收入确认记录allocate为空或"待审批人指定"
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="审核中"
)
# 获取用户的团队信息
team_name = user.team
from User.utils import create_approval_with_team_logic
# 构建审批内容,包含案件信息(如果提供了)
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, 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=income.id,
business_record=income,
today=date_string
)
# 如果返回None且需要审核说明缺少审核人
if approval is None and needs_approval:
return Response({
'status': 'error',
'message': '团队类型需要指定审核人请提供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_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):
"""
收入确认展示
权限控制:律师只能看自己的数据,管委会和财务部能查看所有
"""
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)
# 直接抄送财务,不需要审批人
Approval.objects.create(
title=user.username + "提交调账申请",
content=content,
times=date_string,
personincharge="财务", # 直接抄送财务
state='已抄送财务', # 直接标记为已抄送财务
type="调账申请",
user_id=acc.id
)
# 记录操作日志
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')
if personincharge and not approvers:
approvers = [personincharge] if personincharge else None
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': '团队类型需要指定审核人请提供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],兼容:用户名数组)
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': '团队类型需要指定审核人请提供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')
if personincharge and not approvers:
approvers = [personincharge] if personincharge else None
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': '团队类型需要指定审核人请提供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')
if personincharge and not approvers:
approvers = [personincharge] if personincharge else None
# 只验证必填字段:姓名和离职时间
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)
# 检查用户是否有案件(作为承办人员)
cases = PreFiling.objects.filter(Undertaker=username)
case_count = cases.count()
if case_count > 0:
# 用户有案件,需要先转移案件才能离职
case_list = []
for case in cases:
case_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'该用户还有{case_count}个案件未转移,请先转移案件后再进行离职登记',
'code': 1,
'data': {
'has_cases': True,
'case_count': case_count,
'cases': case_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': '团队类型需要指定审核人请提供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': ''
}
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
}
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)