diff --git a/User/views.py b/User/views.py index 2322541..7c84f8f 100644 --- a/User/views.py +++ b/User/views.py @@ -1148,20 +1148,62 @@ class approvalProcessing(APIView): approval.save(update_fields=['state']) return Response({'message': '处理成功', 'code': 0}, status=status.HTTP_200_OK) - # 审批人可以指定收入分配方案 - allocate = request.data.get('allocate') # 收入分配(可选,审批时指定) + # 负责人填写收入分配 + allocate = request.data.get('allocate') # 收入分配(负责人必须填写) - # 如果审批人指定了分配方案,更新分配字段 + # 如果当前状态是"待负责人填写分配",负责人必须填写收入分配 + if income.state == "待负责人填写分配": + if not allocate: + return Response({ + 'status': 'error', + 'message': '请填写收入分配', + 'code': 1 + }, status=status.HTTP_400_BAD_REQUEST) + + # 更新收入分配 + income.allocate = allocate + income.state = "待财务处理" # 更新状态为待财务处理 + + # 更新审批内容,添加分配信息 + if "收入分配:待负责人指定" in approval.content: + approval.content = approval.content.replace("收入分配:待负责人指定", f"收入分配:{allocate}") + else: + approval.content = approval.content + f",收入分配:{allocate}" + + income.save(update_fields=['allocate', 'state']) + + # 负责人填写收入分配后,抄送给财务负责人 + from User.utils import get_finance_personincharge_value + finance_personincharge = get_finance_personincharge_value() + + # 更新审批记录,抄送给财务 + approval.personincharge = finance_personincharge + approval.state = "已抄送财务" + approval.content = approval.content + ",已抄送财务部" + approval.save(update_fields=['personincharge', 'state', 'content']) + + return Response({ + 'message': '收入分配已提交,已抄送给财务', + 'code': 0, + 'data': { + 'allocate': allocate, + 'state': income.state + } + }, status=status.HTTP_200_OK) + + # 兼容旧流程:如果负责人未填写分配,但审批人指定了分配方案 if allocate: income.allocate = allocate # 更新审批内容,添加分配信息 if "收入分配:待审批人指定" in approval.content: approval.content = approval.content.replace("收入分配:待审批人指定", f"收入分配:{allocate}") + elif "收入分配:待负责人指定" in approval.content: + approval.content = approval.content.replace("收入分配:待负责人指定", f"收入分配:{allocate}") else: approval.content = approval.content + f",收入分配:{allocate}" income.save(update_fields=['allocate']) - # 使用统一的审核流程处理函数 + # 使用统一的审核流程处理函数(兼容旧流程) from User.utils import process_approval_flow current_approver = approval.personincharge is_completed, error = process_approval_flow( diff --git a/finance/urls.py b/finance/urls.py index 3962e1f..243cc1b 100644 --- a/finance/urls.py +++ b/finance/urls.py @@ -1,4 +1,4 @@ -from .views import UserRegister,UnregisteredUserList,RegisteredUserList,UserDeparture,UserDepartureDetail,EditUserDeparture,DeleteUserDeparture,issueAnInvoice,issueAnInvoiceDetail,confirm,loan,PaymentRequest,reimbursement,confirmdisplay,loandisplay,PaymentDisplay,reimbursementdetail,Change,ChangeDetail,EditInvoice,DeleteInvoice,EditIncome,DeleteIncome,EditAccounts,DeleteAccounts,EditPayment,DeletePayment,EditReimbursement,DeleteReimbursement,EditBonusChange,DeleteBonusChange,GetCaseListForInvoice +from .views import UserRegister,UnregisteredUserList,RegisteredUserList,UserDeparture,UserDepartureDetail,EditUserDeparture,DeleteUserDeparture,issueAnInvoice,issueAnInvoiceDetail,confirm,loan,PaymentRequest,reimbursement,confirmdisplay,loandisplay,PaymentDisplay,reimbursementdetail,Change,ChangeDetail,EditInvoice,DeleteInvoice,EditIncome,DeleteIncome,EditAccounts,DeleteAccounts,EditPayment,DeletePayment,EditReimbursement,DeleteReimbursement,EditBonusChange,DeleteBonusChange,GetCaseListForInvoice,SearchCaseByContractNo,AddInvoice from django.urls import path urlpatterns = [ @@ -11,6 +11,8 @@ urlpatterns = [ path("editUserDeparture", EditUserDeparture.as_view(), name="editUserDeparture/"), path("deleteUserDeparture", DeleteUserDeparture.as_view(), name="deleteUserDeparture/"), path("case-list-for-invoice", GetCaseListForInvoice.as_view(), name="case-list-for-invoice/"), + path("search-case-by-contract-no", SearchCaseByContractNo.as_view(), name="search-case-by-contract-no/"), + path("add-invoice", AddInvoice.as_view(), name="add-invoice/"), path("issue-invoice", issueAnInvoice.as_view(), name="issue-invoice/"), path('issue-Detail', issueAnInvoiceDetail.as_view(), name="issue-Detail/"), path('editInvoice', EditInvoice.as_view(), name="editInvoice/"), diff --git a/finance/views.py b/finance/views.py index 991412f..5734304 100644 --- a/finance/views.py +++ b/finance/views.py @@ -870,42 +870,47 @@ class confirm(APIView): def post(self, request, *args, **kwargs): """ 收入确认 - 优化后:收入分配由审批人指定,提交时不填写分配 - 数据来源案件管理 + 优化后: + 1. 通过合同号搜索选择案件(case_id或project_id) + 2. 从案件管理中同步:客户名称、负责人 + 3. 手动填写:收款日期、收款金额 + 4. 财务填写好后直接抄送给案件负责人 + 5. 负责人在待办中填写收入分配后,再抄送给财务负责人 :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') # 改为可选,由审批人指定 + # 案件选择(二选一) + case_id = request.data.get('case_id') # 案件管理ID + project_id = request.data.get('project_id') # 立项登记ID + + # 手动填写信息 + times = request.data.get('times') # 收款日期 + amount = request.data.get('amount') # 收款金额 + + # 可选字段(如果提供了case_id或project_id,将从案件管理中同步) + ContractNo = request.data.get('ContractNo') # 合同号(可选,如果提供了case_id或project_id则自动同步) + CustomerID = request.data.get('CustomerID') # 客户名称(可选,如果提供了case_id或project_id则自动同步) + + 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') - approvers = normalize_approvers_param(approvers, personincharge) - case_id = request.data.get('case_id') # 案件ID(可选,用于关联案件) - + try: user = User.objects.get(token=token, is_deleted=False) except User.DoesNotExist: return Response({'status': 'error', 'message': '用户不存在或已被删除', 'code': 1}, status=status.HTTP_401_UNAUTHORIZED) - # 必填字段验证(allocate改为可选) - # ContractNo 允许通过 case_id 同步 + # 必填字段验证 missing_params = [] if not times: missing_params.append('times(收款日期)') - if not CustomerID: - missing_params.append('CustomerID(客户名称)') if not amount: missing_params.append('amount(收款金额)') - if not ContractNo and not case_id: - missing_params.append('ContractNo(合同号)') + if not case_id and not project_id: + missing_params.append('case_id或project_id(请选择案件)') + if missing_params: return Response({ 'status': 'error', @@ -913,81 +918,173 @@ class confirm(APIView): 'code': 1 }, status=status.HTTP_400_BAD_REQUEST) - # 如果提供了案件ID,从案件表获取相关信息(可选) - from business.models import ProjectRegistration + # 从案件管理中提取信息 + from business.models import Case, ProjectRegistration + import json + + responsible_person = None # 负责人姓名(用于抄送) + responsible_person_username = None # 负责人用户名(用于抄送) case_info = None + if case_id: + # 从Case(案件管理)中获取信息 try: - case = ProjectRegistration.objects.get(id=case_id, is_deleted=False) + case = Case.objects.select_related('project').get(id=case_id, is_deleted=False) + + # 获取合同号 + ContractNo = ContractNo or case.contract_no + + # 获取客户名称 + CustomerID = CustomerID or case.client_name + + # 获取负责人信息 + responsiblefor = case.responsiblefor + + # 如果案件信息不完整,从关联的ProjectRegistration获取 + if case.project: + project = case.project + ContractNo = ContractNo or project.ContractNo + 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, - 'ContractNo': case.ContractNo, - 'responsiblefor': case.responsiblefor, - 'type': case.type + 'type': 'case', + 'contract_no': ContractNo, + 'customer_name': CustomerID, + 'responsible_person': responsible_person } - # 如果合同号未提供,从案件表同步 - if not ContractNo: - ContractNo = case.ContractNo - except ProjectRegistration.DoesNotExist: + + 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 = ContractNo or project.ContractNo + 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': '立项登记不存在或已被删除', + '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) + + if not responsible_person_username: + return Response({ + 'status': 'error', + 'message': f'无法找到负责人"{responsible_person}"的用户信息,请检查负责人是否正确', + 'code': 1 + }, status=status.HTTP_400_BAD_REQUEST) from datetime import datetime now = datetime.now() date_string = now.strftime("%Y-%m-%d") - # 创建收入确认记录,allocate为空或"待审批人指定" + # 创建收入确认记录,allocate为"待负责人指定" income = Income.objects.create( times=times, ContractNo=ContractNo, CustomerID=CustomerID, amount=amount, - allocate=allocate if allocate else "待审批人指定", # 如果未提供,设为"待审批人指定" + allocate=allocate if allocate else "待负责人指定", # 如果未提供,设为"待负责人指定" submit=user.username, submit_tiem=date_string, - state="审核中" + state="待负责人填写分配" # 新状态:等待负责人填写收入分配 ) - # 获取用户的团队信息 - team_name = user.team - from User.utils import create_approval_with_team_logic - - # 构建审批内容,包含案件信息(如果提供了) + # 直接抄送给案件负责人(不经过团队审批流程) + from User.models import Approval + from User.utils import get_finance_personincharge_value + + # 构建审批内容 content_parts = [ f"{user.username}在{times}提交了收入确认", f"合同编号:{ContractNo}", f"客户名称:{CustomerID}", - f"收入金额:{amount}" + f"收款金额:{amount}", + f"收入分配:待负责人指定" ] - 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, + # 创建审批记录,直接抄送给负责人 + approval = Approval.objects.create( title=user.username + "提交收入确认", content=content, - approval_type="收入确认", - user_id=income.id, - business_record=income, - today=date_string + times=date_string, + personincharge=responsible_person_username, # 直接抄送给案件负责人 + state="审核中", # 负责人需要填写收入分配 + type="收入确认", + user_id=str(income.id) ) - # 如果返回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, @@ -995,7 +1092,9 @@ class confirm(APIView): 'CustomerID': income.CustomerID, 'amount': income.amount, 'allocate': income.allocate, - 'case_id': case_id if case_id else None + 'case_id': case_id if case_id else None, + 'project_id': project_id if project_id else None, + 'responsible_person': responsible_person_username } log_operation( request=request, @@ -1006,16 +1105,19 @@ class confirm(APIView): target_id=income.id, target_name=f'{income.ContractNo} - {income.CustomerID}', new_data=new_data, - remark=f'新增收入确认:合同号 {income.ContractNo},金额 {income.amount},分配待审批人指定' + remark=f'新增收入确认:合同号 {income.ContractNo},金额 {income.amount},已抄送给负责人 {responsible_person_username}' ) return Response({ - 'message': '提交成功', + 'message': '提交成功,已抄送给负责人', 'code': 0, 'data': { 'id': income.id, 'ContractNo': income.ContractNo, - 'allocate': income.allocate + 'CustomerID': income.CustomerID, + 'responsible_person': responsible_person_username, + 'state': income.state, + 'approval_id': approval.id } }, status=status.HTTP_200_OK) @@ -2525,3 +2627,393 @@ class DeleteUserDeparture(APIView): ) 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)