From f7b3f4bb48d4fb685b8e32315753b3823fa228db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=AD=E6=A3=AE?= Date: Mon, 17 Jun 2024 10:23:24 +0800 Subject: [PATCH] =?UTF-8?q?commit=20=E9=A1=B9=E7=9B=AE=E5=8F=B0=E8=B4=A6?= =?UTF-8?q?=E6=95=88=E6=9E=9C=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/fac_mgnt/urls.py | 16 +- application/fac_mgnt/views.py | 974 ++-------------------------------- application/hrm_mgnt/urls.py | 6 - application/hrm_mgnt/views.py | 298 +---------- application/org_mgnt/urls.py | 3 - application/org_mgnt/views.py | 125 +---- application/pjt_mgnt/views.py | 2 +- common/urls.py | 10 +- common/views.py | 137 +++-- 9 files changed, 163 insertions(+), 1408 deletions(-) diff --git a/application/fac_mgnt/urls.py b/application/fac_mgnt/urls.py index 06db79e..d6bf8b5 100644 --- a/application/fac_mgnt/urls.py +++ b/application/fac_mgnt/urls.py @@ -83,18 +83,6 @@ urlpatterns = [ path('edit_employee_commission_detail//', edit_employee_commission_detail, name='edit_employee_commission_detail'), - path('common_excel_parse_exps/', common_excel_parse_exp, name='common_excel_parse_exp'), - path('save_excel_table_data_exp/', save_excel_table_data_exp, name='save_excel_table_data_exp'), - - path('common_excel_parse_gab/', common_excel_parse_gab, name='common_excel_parse_gab'), - path('save_excel_table_data_gab/', save_excel_table_data_gab, name='save_excel_table_data_gab'), - - path('common_excel_parse_pjt/', common_excel_parse_pjt, name='common_excel_parse_pjt'), - path('save_excel_table_data_pjt/', save_excel_table_data_pjt, name='save_excel_table_data_pjt'), - - path('common_excel_parse_rr/', common_excel_parse_rr, name='common_excel_parse_rr'), - path('save_excel_table_data_rr/', save_excel_table_data_rr, name='save_excel_table_data_rr'), - - path('common_excel_parse_pc/', common_excel_parse_pc, name='common_excel_parse_pc'), - path('save_excel_table_data_pc/', save_excel_table_data_pc, name='save_excel_table_data_pc'), + path('get_invoice_records/', get_invoice_records, name='get_invoice_records'), + path('get_repayment_records/', get_repayment_records, name='get_repayment_records'), ] diff --git a/application/fac_mgnt/views.py b/application/fac_mgnt/views.py index 4d50051..a0c09d1 100644 --- a/application/fac_mgnt/views.py +++ b/application/fac_mgnt/views.py @@ -12,7 +12,7 @@ from django.views.decorators.http import require_POST, require_http_methods from application import pjt_mgnt from application.fac_mgnt.forms import * from application.fac_mgnt.models import * -from application.hrm_mgnt.models import PerformanceEvaluation +from application.hrm_mgnt.models import PerformanceEvaluation, EmployeeInformation from common.auth import custom_permission_required from common.utils.page_helper import paginate_query_and_assign_numbers @@ -1129,8 +1129,8 @@ def inv_rec_list_view(request): ], "excel_upload_config": { "template_url": reverse("download_template", kwargs={'template_name': template_name}), - "parse_url": reverse("common_excel_parse_pjt"), - "save_url": reverse("save_excel_table_data_pjt"), + "parse_url": reverse("common_excel_parse"), + "save_url": reverse("save_excel_table_data_fac_inv"), "fields_preview_config": { "project_name": {"type": "text", "width": "180px"}, "primary_department": {"type": "text", "width": "180px"}, @@ -1243,8 +1243,8 @@ def rep_rec_list_view(request): ], "excel_upload_config": { "template_url": reverse("download_template", kwargs={'template_name': template_name}), - "parse_url": reverse("common_excel_parse_rr"), - "save_url": reverse("save_excel_table_data_rr"), + "parse_url": reverse("common_excel_parse"), + "save_url": reverse("save_excel_table_data_fac_rep"), "fields_preview_config": { "project_name": {"type": "text", "width": "180px"}, "primary_department": {"type": "text", "width": "180px"}, @@ -1530,8 +1530,8 @@ def project_commission_list_view(request): ], "excel_upload_config": { "template_url": reverse("download_template", kwargs={'template_name': template_name}), - "parse_url": reverse("common_excel_parse_pc"), - "save_url": reverse("save_excel_table_data_pc"), + "parse_url": reverse("common_excel_parse"), + "save_url": reverse("save_excel_table_data"), "fields_preview_config": { "project_name": {"type": "text", "width": "180px"}, "year_month": {"type": "text", "width": "220px"}, @@ -1648,7 +1648,7 @@ def get_employees(request): def get_employee_info(request, employee_id): employee = get_object_or_404(EmployeeInformation, pk=employee_id) data = { - "primary_department": employee.primary_department, + "primary_department": employee.primary_department.department_name } return JsonResponse(data) @@ -1738,920 +1738,58 @@ def edit_employee_commission_detail(request, commission_id): return JsonResponse({"message": "无效的请求方法"}, status=405) -@csrf_protect @login_required -def common_excel_parse_exp(request): - """ - 该函数用于解析上传的Excel文件并返回数据。 +def get_invoice_records(request): + project_name = request.GET.get('project_name', None) - Args: - request: HTTP请求对象,包含上传的Excel文件。 + if project_name: + invoices = InvoiceRecord.objects.filter(project_name__icontains=project_name) - Returns: - JsonResponse: 包含解析后的Excel数据的JSON响应,或包含错误消息的JSON响应。 + data = [ + { + "record_id": invoice.record_id, + "project_name": invoice.project_name, + "primary_department": invoice.primary_department, + "project_manager": invoice.project_manager, + "nature": invoice.nature, + "billing_entity": invoice.billing_entity, + "invoice_number": invoice.invoice_number, + "invoice_date": invoice.invoice_date, + "total_amount": invoice.total_amount, + "tax_rate": invoice.tax_rate, + "amount_excluding_tax": invoice.amount_excluding_tax, + "tax_amount": invoice.tax_amount, + "invoice_type": invoice.invoice_type, + } + for invoice in invoices + ] + else: + data = [] - Raises: - N/A - """ - # 如果是POST请求并且有上传的文件 - if request.method == 'POST' and request.FILES: - # 获取上传的Excel文件 - excel_file = request.FILES.get('excel_file') - - # 如果没有提供文件,返回错误响应 - if not excel_file: - return JsonResponse({'error': '请先选择文件。'}, status=400) - - # 获取模型名称 - model_config = request.POST.get('model_config', '') - - # 动态获取模型 - try: - app_label, model_name = model_config.split('.') - model = apps.get_model(app_label, model_name) - except (LookupError, KeyError, ValueError): - return JsonResponse({'error': '模型配置错误。'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(model, 'excluded_fields', []) - - # 保存文件到服务器 - file_name = default_storage.save(excel_file.name, excel_file) - file_path = os.path.join(settings.MEDIA_ROOT, file_name) - - def create_dynamic_serializer(mod, include): - class DynamicSerializer(ModelSerializer): - class Meta: - model = mod - fields = include - - def to_representation(self, instance): - representation = super().to_representation(instance) - if 'type_id' in representation and instance.type_id: - representation['type_id'] = instance.type_id.expense_type - return representation - - return DynamicSerializer - - try: - # 打开并解析Excel文件 - workbook = load_workbook(file_path, data_only=True) - sheet = workbook.active - data = [] - - # 读取第一行作为表头 - header_row = [cell.value for cell in sheet[1]] - - # 获取字段名和 verbose_name,排除索引字段 - model_fields = [field.name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] - model_verbose_name = [field.verbose_name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] - - if not all(item in model_verbose_name for item in header_row): - return JsonResponse({'error': '表头不匹配,请使用正确的Excel上传模板。'}, status=400) - - # 组成 fields_map - fields_map = dict(zip(model_fields, header_row)) - fields_map_nf = dict(zip(header_row, model_fields)) - - # 创建一个映射,将Excel表头映射到模型字段名 - header_to_field_map = {header: fields_map_nf[header] for header in header_row} - header_fields = [header_to_field_map[header] for header in header_row] - - for row in sheet.iter_rows(min_row=2, values_only=True): - if not all(value is None for value in row): - instance_data = dict(zip(model_fields, row)) - - # 处理type_id字段,假设模型中有type_id字段并且Excel文件中的表头包含费用类型名称 - expense_type_name = instance_data.get('type_id') - if expense_type_name: - try: - expense_type_instance = ExpenseType.objects.get(expense_type=expense_type_name) - instance_data['type_id'] = expense_type_instance - except ExpenseType.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {expense_type_name} 的费用类型信息。'}, - status=400) - - instance = model(**instance_data) - try: - instance.full_clean() - data.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - - # 动态获取序列化器 - serializer_class = create_dynamic_serializer(model, include=header_fields) - - serializer = serializer_class(data, many=True) - - # 清理,删除上传的文件 - os.remove(file_path) - - return JsonResponse({"table_data": serializer.data, "fields_map": fields_map}, safe=False) - except Exception as e: - # 清理,删除上传的文件 - os.remove(file_path) - return JsonResponse({'error': f'解析文件时出错: {str(e)}'}, status=500) - - return JsonResponse({'error': '请求错误'}, status=400) + return JsonResponse({"invoices": data}) -@csrf_protect @login_required -def save_excel_table_data_exp(request): - if request.method == 'POST': - try: - data = json.loads(request.body) - model_config = data.get('model_config') - table_data = data.get('table_data') - - if not model_config or not table_data: - return JsonResponse({'error': '缺少必要的参数'}, status=400) - - # 分割 model_config 以获取 app_label 和 model_name - try: - app_label, model_name = model_config.split('.') - Model = apps.get_model(app_label, model_name) - except (ValueError, LookupError): - return JsonResponse({'error': '无效的 model_config'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(Model, 'excluded_fields', []) - - # 创建模型实例列表 - instances = [] - for row_data in table_data: - # 处理type_id字段,假设模型中有type_id字段并且table_data中包含费用类型名称 - expense_type_name = row_data.get('type_id') - if expense_type_name: - try: - expense_type_instance = ExpenseType.objects.get(expense_type=expense_type_name) - row_data['type_id'] = expense_type_instance - except ExpenseType.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {expense_type_name} 的费用类型信息。'}, status=400) - - try: - # 排除不需要的字段 - instance_data = {key: value for key, value in row_data.items() if key not in exclude_fields} - instance = Model(**instance_data) - instance.full_clean() # 验证数据 - instances.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - except Exception as e: - return JsonResponse({'error': f'创建实例时出错: {str(e)}'}, status=500) - - # 批量创建模型实例 - try: - Model.objects.bulk_create(instances) - except Exception as e: - return JsonResponse({'error': f'批量保存数据时出错: {str(e)}'}, status=500) - - return JsonResponse({'message': '表格数据保存成功'}, status=200) - except json.JSONDecodeError: - return JsonResponse({'error': '无效的JSON格式'}, status=400) - except Exception as e: - return JsonResponse({'error': f'服务器内部错误: {str(e)}'}, status=500) - return JsonResponse({'error': '无效的请求方法'}, status=400) - - -@csrf_protect -@login_required -def common_excel_parse_gab(request): - """ - 该函数用于解析上传的Excel文件并返回数据。 - - Args: - request: HTTP请求对象,包含上传的Excel文件。 - - Returns: - JsonResponse: 包含解析后的Excel数据的JSON响应,或包含错误消息的JSON响应。 - - Raises: - N/A - """ - # 如果是POST请求并且有上传的文件 - if request.method == 'POST' and request.FILES: - # 获取上传的Excel文件 - excel_file = request.FILES.get('excel_file') - - # 如果没有提供文件,返回错误响应 - if not excel_file: - return JsonResponse({'error': '请先选择文件。'}, status=400) - - # 获取模型名称 - model_config = request.POST.get('model_config', '') - - # 动态获取模型 - try: - app_label, model_name = model_config.split('.') - model = apps.get_model(app_label, model_name) - except (LookupError, KeyError, ValueError): - return JsonResponse({'error': '模型配置错误。'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(model, 'excluded_fields', []) - - # 保存文件到服务器 - file_name = default_storage.save(excel_file.name, excel_file) - file_path = os.path.join(settings.MEDIA_ROOT, file_name) - - def create_dynamic_serializer(mod, include): - class DynamicSerializer(ModelSerializer): - class Meta: - model = mod - fields = include - - def to_representation(self, instance): - representation = super().to_representation(instance) - if 'type_id' in representation and instance.type_id: - representation['type_id'] = instance.type_id.expense_type - if 'expense_detail' in representation and instance.expense_detail: - representation['expense_detail'] = instance.expense_detail.expense_detail - return representation - - return DynamicSerializer - - try: - # 打开并解析Excel文件 - workbook = load_workbook(file_path, data_only=True) - sheet = workbook.active - data = [] - - # 读取第一行作为表头 - header_row = [cell.value for cell in sheet[1]] - - # 获取字段名和 verbose_name,排除索引字段 - model_fields = [field.name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] - model_verbose_name = [field.verbose_name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] - - if not all(item in model_verbose_name for item in header_row): - return JsonResponse({'error': '表头不匹配,请使用正确的Excel上传模板。'}, status=400) - - # 组成 fields_map - fields_map = dict(zip(model_fields, header_row)) - fields_map_nf = dict(zip(header_row, model_fields)) - - # 创建一个映射,将Excel表头映射到模型字段名 - header_to_field_map = {header: fields_map_nf[header] for header in header_row} - header_fields = [header_to_field_map[header] for header in header_row] - - for row in sheet.iter_rows(min_row=2, values_only=True): - if not all(value is None for value in row): - instance_data = dict(zip(model_fields, row)) - - # 处理type_id字段,假设模型中有type_id字段并且Excel文件中的表头包含费用类型名称 - expense_type_name = instance_data.get('expense_type') - if expense_type_name: - try: - expense_type_instance = ExpenseType.objects.get(expense_type=expense_type_name) - instance_data['expense_type'] = expense_type_instance - except ExpenseType.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {expense_type_name} 的费用类型信息。'}, - status=400) - - # 处理expense_detail字段,假设模型中有expense_detail字段并且Excel文件中的表头包含费用明细名称 - expense_detail_name = instance_data.get('expense_detail') - if expense_detail_name: - try: - expense_detail_instance = ExpenseDetail.objects.get(expense_detail=expense_detail_name) - instance_data['expense_detail'] = expense_detail_instance - except ExpenseDetail.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {expense_detail_name} 的费用明细信息。'}, - status=400) - - instance = model(**instance_data) - try: - instance.full_clean() - data.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - - # 动态获取序列化器 - serializer_class = create_dynamic_serializer(model, include=header_fields) - - serializer = serializer_class(data, many=True) - - # 清理,删除上传的文件 - os.remove(file_path) - - return JsonResponse({"table_data": serializer.data, "fields_map": fields_map}, safe=False) - except Exception as e: - # 清理,删除上传的文件 - os.remove(file_path) - return JsonResponse({'error': f'解析文件时出错: {str(e)}'}, status=500) - - return JsonResponse({'error': '请求错误'}, status=400) - - -@csrf_protect -@login_required -def save_excel_table_data_gab(request): - if request.method == 'POST': - try: - data = json.loads(request.body) - model_config = data.get('model_config') - table_data = data.get('table_data') - - if not model_config or not table_data: - return JsonResponse({'error': '缺少必要的参数'}, status=400) - - # 分割 model_config 以获取 app_label 和 model_name - try: - app_label, model_name = model_config.split('.') - Model = apps.get_model(app_label, model_name) - except (ValueError, LookupError): - return JsonResponse({'error': '无效的 model_config'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(Model, 'excluded_fields', []) - - # 创建模型实例列表 - instances = [] - for row_data in table_data: - # 处理type_id字段,假设模型中有type_id字段并且table_data中包含费用类型名称 - expense_type_name = row_data.get('expense_type') - if expense_type_name: - try: - expense_type_instance = ExpenseType.objects.get(type_id=expense_type_name) - row_data['expense_type'] = expense_type_instance - except ExpenseType.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {expense_type_name} 的费用类型信息。'}, status=400) - - # 处理expense_detail字段,假设模型中有expense_detail字段并且table_data中包含费用明细名称 - expense_detail_name = row_data.get('expense_detail') - if expense_detail_name: - try: - expense_detail_instance = ExpenseDetail.objects.get(expense_detail=expense_detail_name) - row_data['expense_detail'] = expense_detail_instance - except ExpenseDetail.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {expense_detail_name} 的费用明细信息。'}, - status=400) - - try: - # 排除不需要的字段 - instance_data = {key: value for key, value in row_data.items() if key not in exclude_fields} - instance = Model(**instance_data) - instance.full_clean() # 验证数据 - instances.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - except Exception as e: - return JsonResponse({'error': f'创建实例时出错: {str(e)}'}, status=500) - - # 批量创建模型实例 - try: - Model.objects.bulk_create(instances) - except Exception as e: - return JsonResponse({'error': f'批量保存数据时出错: {str(e)}'}, status=500) - - return JsonResponse({'message': '表格数据保存成功'}, status=200) - except json.JSONDecodeError: - return JsonResponse({'error': '无效的JSON格式'}, status=400) - except Exception as e: - return JsonResponse({'error': f'服务器内部错误: {str(e)}'}, status=500) - return JsonResponse({'error': '无效的请求方法'}, status=400) - - -@csrf_protect -@login_required -def common_excel_parse_pjt(request): - """ - 该函数用于解析上传的Excel文件并返回数据。 - - Args: - request: HTTP请求对象,包含上传的Excel文件。 - - Returns: - JsonResponse: 包含解析后的Excel数据的JSON响应,或包含错误消息的JSON响应。 - - Raises: - N/A - """ - # 如果是POST请求并且有上传的文件 - if request.method == 'POST' and request.FILES: - # 获取上传的Excel文件 - excel_file = request.FILES.get('excel_file') - - # 如果没有提供文件,返回错误响应 - if not excel_file: - return JsonResponse({'error': '请先选择文件。'}, status=400) - - # 获取模型名称 - model_config = request.POST.get('model_config', '') - - # 动态获取模型 - try: - app_label, model_name = model_config.split('.') - model = apps.get_model(app_label, model_name) - except (LookupError, KeyError, ValueError): - return JsonResponse({'error': '模型配置错误。'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(model, 'excluded_fields', ['项目编号']) - - # 保存文件到服务器 - file_name = default_storage.save(excel_file.name, excel_file) - file_path = os.path.join(settings.MEDIA_ROOT, file_name) - - def create_dynamic_serializer(mod, include): - class DynamicSerializer(ModelSerializer): - class Meta: - model = mod - fields = include - - def to_representation(self, instance): - representation = super().to_representation(instance) - if 'project_id' in representation and instance.project_id: - representation['project_id'] = instance.project_id.project_name - return representation - - return DynamicSerializer - - try: - # 打开并解析Excel文件 - workbook = load_workbook(file_path, data_only=True) - sheet = workbook.active - data = [] - - # 读取第一行作为表头 - header_row = [cell.value for cell in sheet[1]] - - # 获取字段名和 verbose_name,排除索引字段 - model_fields = [field.name for field in model._meta.fields if - not field.primary_key and field.name not in ['project_id']] - model_verbose_name = [field.verbose_name for field in model._meta.fields if - not field.primary_key and field.name not in ['project_id']] - - if not all(item in model_verbose_name for item in header_row): - return JsonResponse({'error': '表头不匹配,请使用正确的Excel上传模板。'}, status=400) - - # 组成 fields_map - fields_map = dict(zip(model_fields, header_row)) - fields_map_nf = dict(zip(header_row, model_fields)) - - # 创建一个映射,将Excel表头映射到模型字段名 - header_to_field_map = {header: fields_map_nf[header] for header in header_row} - header_fields = [header_to_field_map[header] for header in header_row] - - for row in sheet.iter_rows(min_row=2, values_only=True): - if not all(value is None for value in row): - instance_data = dict(zip(model_fields, row)) - - # 处理project_id字段,假设模型中有project_id字段并且Excel文件中的表头包含项目名称 - project_name = instance_data.get('project_name') - if project_name: - try: - project_instance = pjt_mgnt.models.ProjectLedger.objects.get(project_name=project_name) - instance_data['project'] = project_instance - except ProjectLedger.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {project_name} 的项目台账记录。'}, status=400) - - instance = model(**instance_data) - try: - instance.full_clean() - data.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - - # 动态获取序列化器 - serializer_class = create_dynamic_serializer(model, include=header_fields) - - serializer = serializer_class(data, many=True) - - # 清理,删除上传的文件 - os.remove(file_path) - - return JsonResponse({"table_data": serializer.data, "fields_map": fields_map}, safe=False) - except Exception as e: - # 清理,删除上传的文件 - os.remove(file_path) - return JsonResponse({'error': f'解析文件时出错: {str(e)}'}, status=500) - - return JsonResponse({'error': '请求错误'}, status=400) - - -@csrf_protect -@login_required -def save_excel_table_data_pjt(request): - if request.method == 'POST': - try: - data = json.loads(request.body) - model_config = data.get('model_config') - table_data = data.get('table_data') - - if not model_config or not table_data: - return JsonResponse({'error': '缺少必要的参数'}, status=400) - - # 分割 model_config 以获取 app_label 和 model_name - try: - app_label, model_name = model_config.split('.') - Model = apps.get_model(app_label, model_name) - except (ValueError, LookupError): - return JsonResponse({'error': '无效的 model_config'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(Model, 'excluded_fields', []) - - # 创建模型实例列表 - instances = [] - for row_data in table_data: - # 处理project_id字段,假设模型中有project_id字段并且table_data中包含项目名称 - project_name = row_data.get('project_name') - if project_name: - try: - project_instance = ProjectLedger.objects.get(project_name=project_name) - row_data['project_id'] = project_instance - row_data['project_name'] = project_instance.project_name - except ProjectLedger.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {project_name} 的项目台账记录。'}, status=400) - - try: - # 排除不需要的字段 - instance_data = {key: value for key, value in row_data.items() if key not in exclude_fields} - instance = Model(**instance_data) - instance.full_clean() # 验证数据 - instances.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - except Exception as e: - return JsonResponse({'error': f'创建实例时出错: {str(e)}'}, status=500) - - # 批量创建模型实例 - try: - Model.objects.bulk_create(instances) - except Exception as e: - return JsonResponse({'error': f'批量保存数据时出错: {str(e)}'}, status=500) - - return JsonResponse({'message': '表格数据保存成功'}, status=200) - except json.JSONDecodeError: - return JsonResponse({'error': '无效的JSON格式'}, status=400) - except Exception as e: - return JsonResponse({'error': f'服务器内部错误: {str(e)}'}, status=500) - return JsonResponse({'error': '无效的请求方法'}, status=400) - - -@csrf_protect -@login_required -def common_excel_parse_rr(request): - """ - 该函数用于解析上传的Excel文件并返回数据。 - - Args: - request: HTTP请求对象,包含上传的Excel文件。 - - Returns: - JsonResponse: 包含解析后的Excel数据的JSON响应,或包含错误消息的JSON响应。 - - Raises: - N/A - """ - # 如果是POST请求并且有上传的文件 - if request.method == 'POST' and request.FILES: - # 获取上传的Excel文件 - excel_file = request.FILES.get('excel_file') - - # 如果没有提供文件,返回错误响应 - if not excel_file: - return JsonResponse({'error': '请先选择文件。'}, status=400) - - # 获取模型名称 - model_config = request.POST.get('model_config', '') - - # 动态获取模型 - try: - app_label, model_name = model_config.split('.') - model = apps.get_model(app_label, model_name) - except (LookupError, KeyError, ValueError): - return JsonResponse({'error': '模型配置错误。'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(model, 'excluded_fields', ['项目编号']) - - # 保存文件到服务器 - file_name = default_storage.save(excel_file.name, excel_file) - file_path = os.path.join(settings.MEDIA_ROOT, file_name) - - def create_dynamic_serializer(mod, include): - class DynamicSerializer(ModelSerializer): - class Meta: - model = mod - fields = include - - def to_representation(self, instance): - representation = super().to_representation(instance) - if 'project_id' in representation and instance.project_id: - representation['project_id'] = instance.project_id.project_name - return representation - - return DynamicSerializer - - try: - # 打开并解析Excel文件 - workbook = load_workbook(file_path, data_only=True) - sheet = workbook.active - data = [] - - # 读取第一行作为表头 - header_row = [cell.value for cell in sheet[1]] - - # 获取字段名和 verbose_name,排除索引字段 - model_fields = [field.name for field in model._meta.fields if - not field.primary_key and field.name not in ['project_id', 'invoice']] - model_verbose_name = [field.verbose_name for field in model._meta.fields if - not field.primary_key and field.name not in ['project_id', 'invoice']] - - if not all(item in model_verbose_name for item in header_row): - return JsonResponse({'error': '表头不匹配,请使用正确的Excel上传模板。'}, status=400) - - # 组成 fields_map - fields_map = dict(zip(model_fields, header_row)) - fields_map_nf = dict(zip(header_row, model_fields)) - - # 创建一个映射,将Excel表头映射到模型字段名 - header_to_field_map = {header: fields_map_nf[header] for header in header_row} - header_fields = [header_to_field_map[header] for header in header_row] - - for row in sheet.iter_rows(min_row=2, values_only=True): - if not all(value is None for value in row): - instance_data = dict(zip(model_fields, row)) - - # 处理project_id字段,假设模型中有project_id字段并且Excel文件中的表头包含项目名称 - project_name = instance_data.get('project_name') - if project_name: - try: - project_instance = ProjectLedger.objects.get(project_name=project_name) - invoice_instance = InvoiceRecord.objects.get(project_name=project_name) - instance_data['invoice'] = invoice_instance - instance_data['project_id'] = project_instance - instance_data['project_name'] = project_instance.project_name - except ProjectLedger.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {project_name} 的项目台账记录。'}, status=400) - - instance = model(**instance_data) - try: - instance.full_clean() - data.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - - # 动态获取序列化器 - serializer_class = create_dynamic_serializer(model, include=header_fields) - - serializer = serializer_class(data, many=True) - - # 清理,删除上传的文件 - os.remove(file_path) - - return JsonResponse({"table_data": serializer.data, "fields_map": fields_map}, safe=False) - except Exception as e: - # 清理,删除上传的文件 - os.remove(file_path) - return JsonResponse({'error': f'解析文件时出错: {str(e)}'}, status=500) - - return JsonResponse({'error': '请求错误'}, status=400) - - -@csrf_protect -@login_required -def save_excel_table_data_rr(request): - if request.method == 'POST': - try: - data = json.loads(request.body) - model_config = data.get('model_config') - table_data = data.get('table_data') - - if not model_config or not table_data: - return JsonResponse({'error': '缺少必要的参数'}, status=400) - - # 分割 model_config 以获取 app_label 和 model_name - try: - app_label, model_name = model_config.split('.') - Model = apps.get_model(app_label, model_name) - except (ValueError, LookupError): - return JsonResponse({'error': '无效的 model_config'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(Model, 'excluded_fields', []) - - # 创建模型实例列表 - instances = [] - for row_data in table_data: - # 处理project_id字段,假设模型中有project_id字段并且table_data中包含项目名称 - project_name = row_data.get('project_name') - if project_name: - try: - project_instance = ProjectLedger.objects.get(project_name=project_name) - invoice_instance = InvoiceRecord.objects.get(project_name=project_name) - - row_data['invoice'] = invoice_instance - row_data['project_id'] = project_instance - row_data['project_name'] = project_instance.project_name - except ProjectLedger.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {project_name} 的项目台账记录。'}, status=400) - - try: - # 排除不需要的字段 - instance_data = {key: value for key, value in row_data.items() if key not in exclude_fields} - instance = Model(**instance_data) - instance.full_clean() # 验证数据 - instances.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - except Exception as e: - return JsonResponse({'error': f'创建实例时出错: {str(e)}'}, status=500) - - # 批量创建模型实例 - try: - Model.objects.bulk_create(instances) - except Exception as e: - return JsonResponse({'error': f'批量保存数据时出错: {str(e)}'}, status=500) - - return JsonResponse({'message': '表格数据保存成功'}, status=200) - except json.JSONDecodeError: - return JsonResponse({'error': '无效的JSON格式'}, status=400) - except Exception as e: - return JsonResponse({'error': f'服务器内部错误: {str(e)}'}, status=500) - return JsonResponse({'error': '无效的请求方法'}, status=400) - - -@csrf_protect -@login_required -def common_excel_parse_pc(request): - """ - 该函数用于解析上传的Excel文件并返回数据。 - - Args: - request: HTTP请求对象,包含上传的Excel文件。 - - Returns: - JsonResponse: 包含解析后的Excel数据的JSON响应,或包含错误消息的JSON响应。 - - Raises: - N/A - """ - # 如果是POST请求并且有上传的文件 - if request.method == 'POST' and request.FILES: - # 获取上传的Excel文件 - excel_file = request.FILES.get('excel_file') - - # 如果没有提供文件,返回错误响应 - if not excel_file: - return JsonResponse({'error': '请先选择文件。'}, status=400) - - # 获取模型名称 - model_config = request.POST.get('model_config', '') - - # 动态获取模型 - try: - app_label, model_name = model_config.split('.') - model = apps.get_model(app_label, model_name) - except (LookupError, KeyError, ValueError): - return JsonResponse({'error': '模型配置错误。'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(model, 'excluded_fields', ['记录编号']) - - # 保存文件到服务器 - file_name = default_storage.save(excel_file.name, excel_file) - file_path = os.path.join(settings.MEDIA_ROOT, file_name) - - def create_dynamic_serializer(mod, include): - class DynamicSerializer(ModelSerializer): - class Meta: - model = mod - fields = include - - def to_representation(self, instance): - representation = super().to_representation(instance) - if 'project_id' in representation and instance.project_id: - representation['project_id'] = instance.project_id.project_name - if 'project_name' in representation and instance.project_name: - representation['project_name'] = instance.project_name.project_name - return representation - - return DynamicSerializer - - try: - # 打开并解析Excel文件 - workbook = load_workbook(file_path, data_only=True) - sheet = workbook.active - data = [] - - # 读取第一行作为表头 - header_row = [cell.value for cell in sheet[1]] - - # 获取字段名和 verbose_name,排除索引字段 - model_fields = [field.name for field in model._meta.fields if - not field.primary_key and field.name not in ['record_id']] - model_verbose_name = [field.verbose_name for field in model._meta.fields if - not field.primary_key and field.name not in ['record_id']] - - if not all(item in model_verbose_name for item in header_row): - return JsonResponse({'error': '表头不匹配,请使用正确的Excel上传模板。'}, status=400) - - # 组成 fields_map - fields_map = dict(zip(model_fields, header_row)) - fields_map_nf = dict(zip(header_row, model_fields)) - - # 创建一个映射,将Excel表头映射到模型字段名 - header_to_field_map = {header: fields_map_nf[header] for header in header_row} - header_fields = [header_to_field_map[header] for header in header_row] - - for row in sheet.iter_rows(min_row=2, values_only=True): - if not all(value is None for value in row): - instance_data = dict(zip(model_fields, row)) - - # 处理project_id字段,假设模型中有project_id字段并且Excel文件中的表头包含项目名称 - project_name = instance_data.get('project_name') - if project_name: - try: - project_instance = ProjectLedger.objects.get(project_name=project_name) - instance_data['project_name'] = project_instance - except ProjectLedger.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {project_name} 的项目台账记录。'}, status=400) - - instance = model(**instance_data) - try: - instance.full_clean() - data.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - - # 动态获取序列化器 - serializer_class = create_dynamic_serializer(model, include=header_fields) - - serializer = serializer_class(data, many=True) - - # 清理,删除上传的文件 - os.remove(file_path) - - return JsonResponse({"table_data": serializer.data, "fields_map": fields_map}, safe=False) - except Exception as e: - # 清理,删除上传的文件 - os.remove(file_path) - return JsonResponse({'error': f'解析文件时出错: {str(e)}'}, status=500) - - return JsonResponse({'error': '请求错误'}, status=400) - - -@csrf_protect -@login_required -def save_excel_table_data_pc(request): - if request.method == 'POST': - try: - data = json.loads(request.body) - model_config = data.get('model_config') - table_data = data.get('table_data') - - if not model_config or not table_data: - return JsonResponse({'error': '缺少必要的参数'}, status=400) - - # 分割 model_config 以获取 app_label 和 model_name - try: - app_label, model_name = model_config.split('.') - Model = apps.get_model(app_label, model_name) - except (ValueError, LookupError): - return JsonResponse({'error': '无效的 model_config'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(Model, 'excluded_fields', []) - - # 创建模型实例列表 - instances = [] - for row_data in table_data: - # 处理project_id字段,假设模型中有project_id字段并且table_data中包含项目名称 - project_name = row_data.get('project_name') - if project_name: - try: - project_instance = ProjectLedger.objects.get(project_name=project_name) - row_data['project_name'] = project_instance - except ProjectLedger.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {project_name} 的项目台账记录。'}, status=400) - - try: - # 排除不需要的字段 - instance_data = {key: value for key, value in row_data.items() if key not in exclude_fields} - instance = Model(**instance_data) - instance.full_clean() # 验证数据 - instances.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - except Exception as e: - return JsonResponse({'error': f'创建实例时出错: {str(e)}'}, status=500) - - # 批量创建模型实例 - try: - Model.objects.bulk_create(instances) - except Exception as e: - return JsonResponse({'error': f'批量保存数据时出错: {str(e)}'}, status=500) - - return JsonResponse({'message': '表格数据保存成功'}, status=200) - except json.JSONDecodeError: - return JsonResponse({'error': '无效的JSON格式'}, status=400) - except Exception as e: - return JsonResponse({'error': f'服务器内部错误: {str(e)}'}, status=500) - return JsonResponse({'error': '无效的请求方法'}, status=400) +def get_repayment_records(request): + project_name = request.GET.get('project_name', None) + + if project_name: + repayments = RepaymentRecord.objects.filter(project_name=project_name) + + data = [ + { + "record_id": repayment.record_id, + "project_name": repayment.project_name, + "primary_department": repayment.primary_department, + "project_manager": repayment.project_manager, + "invoice_number": repayment.invoice.invoice_number, + "invoice_date": repayment.invoice.invoice_date, + "total_amount": float(repayment.invoice.total_amount), + "repayment_amount": float(repayment.repayment_amount), + } + for repayment in repayments + ] + else: + data = [] + + return JsonResponse({"repayments": data}) diff --git a/application/hrm_mgnt/urls.py b/application/hrm_mgnt/urls.py index 217ac04..4842183 100644 --- a/application/hrm_mgnt/urls.py +++ b/application/hrm_mgnt/urls.py @@ -39,10 +39,4 @@ urlpatterns = [ path('performance_add/', performance_add, name='performance_add'), path('performance_modify/', performance_modify, name='performance_modify'), path('performance_delete/', performance_delete, name='performance_delete'), - - path('common_excel_parse/', common_excel_parse, name='common_excel_parse_attd'), - path('common_excel_parse/', common_excel_parse_pe, name='common_excel_parse_pe'), - - path('save_excel_table_data/', save_excel_table_data, name='save_excel_table_data_attd'), - path('save_excel_table_data/', save_excel_table_data_pe, name='save_excel_table_data_pe'), ] diff --git a/application/hrm_mgnt/views.py b/application/hrm_mgnt/views.py index de834f2..00ff675 100644 --- a/application/hrm_mgnt/views.py +++ b/application/hrm_mgnt/views.py @@ -286,8 +286,8 @@ def attd_rec_list_view(request): ], "excel_upload_config": { "template_url": reverse("download_template", kwargs={'template_name': template_name}), - "parse_url": reverse("common_excel_parse_attd"), - "save_url": reverse("save_excel_table_data_attd"), + "parse_url": reverse("common_excel_parse"), + "save_url": reverse("save_excel_table_data"), "fields_preview_config": { "employee": {"type": "text", "width": "180px"}, "year_month": {"type": "date", "width": "110px"}, @@ -858,8 +858,8 @@ def performance_list_view(request): "table_exclude_field_name": ['performance_id'], "excel_upload_config": { "template_url": reverse("download_template", kwargs={'template_name': template_name}), - "parse_url": reverse("common_excel_parse_pe"), - "save_url": reverse("save_excel_table_data_pe"), + "parse_url": reverse("common_excel_parse"), + "save_url": reverse("save_excel_table_data"), "fields_preview_config": { "employee": {"type": "text", "width": "180px"}, "year": {"type": "text", "width": "180px"}, @@ -937,123 +937,6 @@ def performance_delete(request): return JsonResponse({"message": "无效的请求方法"}, status=405) -@csrf_protect -@login_required -def common_excel_parse(request): - """ - 该函数用于解析上传的Excel文件并返回数据。 - - Args: - request: HTTP请求对象,包含上传的Excel文件。 - - Returns: - JsonResponse: 包含解析后的Excel数据的JSON响应,或包含错误消息的JSON响应。 - - Raises: - N/A - """ - - # 如果是POST请求并且有上传的文件 - if request.method == 'POST' and request.FILES: - # 获取上传的Excel文件 - excel_file = request.FILES.get('excel_file') - - # 如果没有提供文件,返回错误响应 - if not excel_file: - return JsonResponse({'error': '请先选择文件。'}, status=400) - - # 获取模型名称 - model_config = request.POST.get('model_config', '') - - # 动态获取模型 - try: - app_label, model_name = model_config.split('.') - model = apps.get_model(app_label, model_name) - except (LookupError, KeyError, ValueError): - return JsonResponse({'error': '模型配置错误。'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(model, 'excluded_fields', []) - - # 保存文件到服务器 - file_name = default_storage.save(excel_file.name, excel_file) - file_path = os.path.join(settings.MEDIA_ROOT, file_name) - - def create_dynamic_serializer(mod, include): - class DynamicSerializer(ModelSerializer): - class Meta: - model = mod - fields = include - - def to_representation(self, instance): - representation = super().to_representation(instance) - if 'employee' in representation and instance.employee: - representation['employee'] = instance.employee.name - return representation - - return DynamicSerializer - - try: - # 打开并解析Excel文件 - workbook = load_workbook(file_path, data_only=True) - sheet = workbook.active - data = [] - - # 读取第一行作为表头 - header_row = [cell.value for cell in sheet[1]] - - # 获取字段名和 verbose_name,排除索引字段 - model_fields = [field.name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] - model_verbose_name = [field.verbose_name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] - - if not all(item in model_verbose_name for item in header_row): - return JsonResponse({'error': '表头不匹配,请使用正确的Excel上传模板。'}, status=400) - - # 组成 fields_map - fields_map = dict(zip(model_fields, header_row)) - fields_map_nf = dict(zip(header_row, model_fields)) - - # 创建一个映射,将Excel表头映射到模型字段名 - header_to_field_map = {header: fields_map_nf[header] for header in header_row} - header_fields = [header_to_field_map[header] for header in header_row] - - for row in sheet.iter_rows(min_row=2, values_only=True): - if not all(value is None for value in row): - instance_data = dict(zip(model_fields, row)) - - # 处理employee字段,假设模型中有employee字段并且Excel文件中的表头包含员工姓名 - employee_name = instance_data.get('employee') - if employee_name: - try: - employee_instance = EmployeeInformation.objects.get(name=employee_name) - instance_data['employee'] = employee_instance - except EmployeeInformation.DoesNotExist: - return JsonResponse({'error': f'找不到姓名为 {employee_name} 的员工信息。'}, status=400) - - instance = model(**instance_data) - try: - instance.full_clean() - data.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - - # 动态获取序列化器 - serializer_class = create_dynamic_serializer(model, include=header_fields) - - serializer = serializer_class(data, many=True) - - # 清理,删除上传的文件 - os.remove(file_path) - - return JsonResponse({"table_data": serializer.data, "fields_map": fields_map}, safe=False) - except Exception as e: - # 清理,删除上传的文件 - os.remove(file_path) - return JsonResponse({'error': f'解析文件时出错: {str(e)}'}, status=500) - - return JsonResponse({'error': '请求错误'}, status=400) @csrf_protect @@ -1115,180 +998,7 @@ def save_excel_table_data(request): return JsonResponse({'error': '无效的请求方法'}, status=400) -@csrf_protect -@login_required -def common_excel_parse_pe(request): - """ - 该函数用于解析上传的Excel文件并返回数据。 - - Args: - request: HTTP请求对象,包含上传的Excel文件。 - - Returns: - JsonResponse: 包含解析后的Excel数据的JSON响应,或包含错误消息的JSON响应。 - - Raises: - N/A - """ - - # 如果是POST请求并且有上传的文件 - if request.method == 'POST' and request.FILES: - # 获取上传的Excel文件 - excel_file = request.FILES.get('excel_file') - - # 如果没有提供文件,返回错误响应 - if not excel_file: - return JsonResponse({'error': '请先选择文件。'}, status=400) - - # 获取模型名称 - model_config = request.POST.get('model_config', '') - - # 动态获取模型 - try: - app_label, model_name = model_config.split('.') - model = apps.get_model(app_label, model_name) - except (LookupError, KeyError, ValueError): - return JsonResponse({'error': '模型配置错误。'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(model, 'excluded_fields', []) - - # 保存文件到服务器 - file_name = default_storage.save(excel_file.name, excel_file) - file_path = os.path.join(settings.MEDIA_ROOT, file_name) - - def create_dynamic_serializer(mod, include): - class DynamicSerializer(ModelSerializer): - class Meta: - model = mod - fields = include - - def to_representation(self, instance): - representation = super().to_representation(instance) - if 'employee' in representation and instance.employee: - representation['employee'] = instance.employee.name - return representation - - return DynamicSerializer - - try: - # 打开并解析Excel文件 - workbook = load_workbook(file_path, data_only=True) - sheet = workbook.active - data = [] - - # 读取第一行作为表头 - header_row = [cell.value for cell in sheet[1]] - - # 获取字段名和 verbose_name,排除索引字段 - model_fields = [field.name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] - model_verbose_name = [field.verbose_name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] - - if not all(item in model_verbose_name for item in header_row): - return JsonResponse({'error': '表头不匹配,请使用正确的Excel上传模板。'}, status=400) - - # 组成 fields_map - fields_map = dict(zip(model_fields, header_row)) - fields_map_nf = dict(zip(header_row, model_fields)) - - # 创建一个映射,将Excel表头映射到模型字段名 - header_to_field_map = {header: fields_map_nf[header] for header in header_row} - header_fields = [header_to_field_map[header] for header in header_row] - - for row in sheet.iter_rows(min_row=2, values_only=True): - if not all(value is None for value in row): - instance_data = dict(zip(model_fields, row)) - - # 处理employee字段,假设模型中有employee字段并且Excel文件中的表头包含员工姓名 - employee_name = instance_data.get('employee') - if employee_name: - try: - employee_instance = EmployeeInformation.objects.get(name=employee_name) - instance_data['employee'] = employee_instance - except EmployeeInformation.DoesNotExist: - return JsonResponse({'error': f'找不到姓名为 {employee_name} 的员工信息。'}, status=400) - - instance = model(**instance_data) - try: - instance.full_clean() - data.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - - # 动态获取序列化器 - serializer_class = create_dynamic_serializer(model, include=header_fields) - - serializer = serializer_class(data, many=True) - - # 清理,删除上传的文件 - os.remove(file_path) - - return JsonResponse({"table_data": serializer.data, "fields_map": fields_map}, safe=False) - except Exception as e: - # 清理,删除上传的文件 - os.remove(file_path) - return JsonResponse({'error': f'解析文件时出错: {str(e)}'}, status=500) - - return JsonResponse({'error': '请求错误'}, status=400) -@csrf_protect -@login_required -def save_excel_table_data_pe(request): - if request.method == 'POST': - try: - data = json.loads(request.body) - model_config = data.get('model_config') - table_data = data.get('table_data') - if not model_config or not table_data: - return JsonResponse({'error': '缺少必要的参数'}, status=400) - - # 分割 model_config 以获取 app_label 和 model_name - try: - app_label, model_name = model_config.split('.') - Model = apps.get_model(app_label, model_name) - except (ValueError, LookupError): - return JsonResponse({'error': '无效的 model_config'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(Model, 'excluded_fields', []) - - # 创建模型实例列表 - instances = [] - for row_data in table_data: - # 处理employee字段,假设模型中有employee字段并且table_data中包含员工姓名 - employee_name = row_data.get('employee') - if employee_name: - try: - employee_instance = EmployeeInformation.objects.get(name=employee_name) - row_data['employee'] = employee_instance - except EmployeeInformation.DoesNotExist: - return JsonResponse({'error': f'找不到姓名为 {employee_name} 的员工信息。'}, status=400) - - try: - # 排除不需要的字段 - instance_data = {key: value for key, value in row_data.items() if key not in exclude_fields} - instance = Model(**instance_data) - instance.full_clean() # 验证数据 - instances.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - except Exception as e: - return JsonResponse({'error': f'创建实例时出错: {str(e)}'}, status=500) - - # 批量创建模型实例 - try: - Model.objects.bulk_create(instances) - except Exception as e: - return JsonResponse({'error': f'批量保存数据时出错: {str(e)}'}, status=500) - - return JsonResponse({'message': '表格数据保存成功'}, status=200) - except json.JSONDecodeError: - return JsonResponse({'error': '无效的JSON格式'}, status=400) - except Exception as e: - return JsonResponse({'error': f'服务器内部错误: {str(e)}'}, status=500) - return JsonResponse({'error': '无效的请求方法'}, status=400) diff --git a/application/org_mgnt/urls.py b/application/org_mgnt/urls.py index 2516249..4ba1b0f 100644 --- a/application/org_mgnt/urls.py +++ b/application/org_mgnt/urls.py @@ -24,7 +24,4 @@ urlpatterns = [ path('sd_list/add/', sd_list_add, name='sd_list_add'), path('sd_list/modify/', sd_list_modify, name='sd_list_modify'), path('sd_list/delete/', sd_list_delete, name='sd_list_delete'), - - path('common_excel_parse/', common_excel_parse, name='common_excel_parse_sd'), - path('save_excel_table_data/', save_excel_table_data, name='save_excel_table_data_sd'), ] diff --git a/application/org_mgnt/views.py b/application/org_mgnt/views.py index 8a978f3..847577d 100644 --- a/application/org_mgnt/views.py +++ b/application/org_mgnt/views.py @@ -419,8 +419,8 @@ def sd_list_view(request): "table_exclude_field_name": ['secondary_department_id'], "excel_upload_config": { "template_url": reverse("download_template", kwargs={'template_name': template_name}), - "parse_url": reverse("common_excel_parse_sd"), - "save_url": reverse("save_excel_table_data_sd"), + "parse_url": reverse("common_excel_parse"), + "save_url": reverse("save_excel_table_data"), "fields_preview_config": { "primary_department": {"type": "text", "width": "180px"}, "secondary_department_name": {"type": "text", "width": "180px"}, @@ -496,127 +496,6 @@ def sd_list_delete(request): return JsonResponse({"message": "无效的请求方法"}, status=405) -@csrf_protect -@login_required -def common_excel_parse(request): - """ - 该函数用于解析上传的Excel文件并返回数据。 - - Args: - request: HTTP请求对象,包含上传的Excel文件。 - - Returns: - JsonResponse: 包含解析后的Excel数据的JSON响应,或包含错误消息的JSON响应。 - - Raises: - N/A - """ - - # 如果是POST请求并且有上传的文件 - if request.method == 'POST' and request.FILES: - # 获取上传的Excel文件 - excel_file = request.FILES.get('excel_file') - - # 如果没有提供文件,返回错误响应 - if not excel_file: - return JsonResponse({'error': '请先选择文件。'}, status=400) - - # 获取模型名称 - model_config = request.POST.get('model_config', '') - - # 动态获取模型 - try: - app_label, model_name = model_config.split('.') - model = apps.get_model(app_label, model_name) - except (LookupError, KeyError, ValueError): - return JsonResponse({'error': '模型配置错误。'}, status=400) - - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(model, 'excluded_fields', []) - - # 保存文件到服务器 - file_name = default_storage.save(excel_file.name, excel_file) - file_path = os.path.join(settings.MEDIA_ROOT, file_name) - - def create_dynamic_serializer(mod, include): - class DynamicSerializer(ModelSerializer): - class Meta: - model = mod - fields = include - - def to_representation(self, instance): - representation = super().to_representation(instance) - if 'primary_department' in representation and instance.primary_department: - representation['primary_department'] = instance.primary_department.department_name - return representation - - return DynamicSerializer - - try: - # 打开并解析Excel文件 - workbook = load_workbook(file_path, data_only=True) - sheet = workbook.active - data = [] - - # 读取第一行作为表头 - header_row = [cell.value for cell in sheet[1]] - - # 获取字段名和 verbose_name,排除索引字段 - model_fields = [field.name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] - model_verbose_name = [field.verbose_name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] - - if not all(item in model_verbose_name for item in header_row): - return JsonResponse({'error': '表头不匹配,请使用正确的Excel上传模板。'}, status=400) - - # 组成 fields_map - fields_map = dict(zip(model_fields, header_row)) - fields_map_nf = dict(zip(header_row, model_fields)) - - # 创建一个映射,将Excel表头映射到模型字段名 - header_to_field_map = {header: fields_map_nf[header] for header in header_row} - header_fields = [header_to_field_map[header] for header in header_row] - - for row in sheet.iter_rows(min_row=2, values_only=True): - if not all(value is None for value in row): - instance_data = dict(zip(model_fields, row)) - - # 处理primary_department字段,假设模型中有primary_department字段并且Excel文件中的表头包含一级部门名称 - primary_department_name = instance_data.get('primary_department') - if primary_department_name: - try: - primary_department_instance = PrimaryDepartment.objects.get( - department_name=primary_department_name) - instance_data['primary_department'] = primary_department_instance - except PrimaryDepartment.DoesNotExist: - return JsonResponse({'error': f'找不到名称为 {primary_department_name} 的一级部门信息。'}, - status=400) - - instance = model(**instance_data) - try: - instance.full_clean() - data.append(instance) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) - - # 动态获取序列化器 - serializer_class = create_dynamic_serializer(model, include=header_fields) - - serializer = serializer_class(data, many=True) - - # 清理,删除上传的文件 - os.remove(file_path) - - return JsonResponse({"table_data": serializer.data, "fields_map": fields_map}, safe=False) - except Exception as e: - # 清理,删除上传的文件 - os.remove(file_path) - return JsonResponse({'error': f'解析文件时出错: {str(e)}'}, status=500) - - return JsonResponse({'error': '请求错误'}, status=400) - - @csrf_protect @login_required def save_excel_table_data(request): diff --git a/application/pjt_mgnt/views.py b/application/pjt_mgnt/views.py index f3c76ff..3ff8720 100644 --- a/application/pjt_mgnt/views.py +++ b/application/pjt_mgnt/views.py @@ -99,7 +99,7 @@ def proj_ledger_list_view(request): "import_excel_button": True } - return render(request, 'items_list.html', context) + return render(request, 'project_list.html', context) @login_required diff --git a/common/urls.py b/common/urls.py index fbc5718..322b8be 100644 --- a/common/urls.py +++ b/common/urls.py @@ -1,14 +1,16 @@ from django.urls import path from common.views import * - urlpatterns = [ path('error_page/', error_page, name='error_page'), path('download_excel_template//', download_template, name='download_template'), - path('download_excel_template///', download_excel_template, name='download_excel_template'), + path('download_excel_template///', download_excel_template, + name='download_excel_template'), path('common_excel_parse/', common_excel_parse, name='common_excel_parse'), path('save_excel_data/', save_excel_table_data, name='save_excel_table_data'), path('load_secondary_departments/', load_secondary_departments, name='load_secondary_departments'), + + path('save_excel_table_data_fac_inv/', save_excel_table_data_fac_inv, name='save_excel_table_data_fac_inv'), + path('save_excel_table_data_fac_rep/', save_excel_table_data_fac_rep, name='save_excel_table_data_fac_rep'), + ] - - diff --git a/common/views.py b/common/views.py index ccde708..ca9e8a1 100644 --- a/common/views.py +++ b/common/views.py @@ -16,7 +16,10 @@ from django.http import FileResponse, HttpResponseNotFound, JsonResponse from django.views.decorators.csrf import csrf_protect from rest_framework.serializers import ModelSerializer +from application import fac_mgnt +from application.fac_mgnt.models import InvoiceRecord from application.org_mgnt.models import SecondaryDepartment +from application.pjt_mgnt.models import ProjectLedger def error_page(request): @@ -128,35 +131,23 @@ def common_excel_parse(request): except (LookupError, KeyError, ValueError): return JsonResponse({'error': '模型配置错误。'}, status=400) - # 获取模型中配置的不需要的字段 - exclude_fields = getattr(model, 'excluded_fields', []) - # 保存文件到服务器 file_name = default_storage.save(excel_file.name, excel_file) file_path = os.path.join(settings.MEDIA_ROOT, file_name) - def create_dynamic_serializer(mod, include): - class DynamicSerializer(ModelSerializer): - class Meta: - model = mod - fields = include - - return DynamicSerializer - try: # 打开并解析Excel文件 workbook = load_workbook(file_path, data_only=True) sheet = workbook.active - data = [] # 读取第一行作为表头 header_row = [cell.value for cell in sheet[1]] # 获取字段名和 verbose_name,排除索引字段 model_fields = [field.name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] + not field.primary_key and field.verbose_name in header_row] model_verbose_name = [field.verbose_name for field in model._meta.fields if - not field.primary_key and field.name not in exclude_fields] + not field.primary_key and field.verbose_name in header_row] if not all(item in model_verbose_name for item in header_row): return JsonResponse({'error': '表头不匹配,请使用正确的Excel上传模板。'}, status=400) @@ -167,43 +158,15 @@ def common_excel_parse(request): # 创建一个映射,将Excel表头映射到模型字段名 header_to_field_map = {header: fields_map_nf[header] for header in header_row} - header_fields = [header_to_field_map[header] for header in header_row] - - # 动态处理外键关系 - def get_related_instance(model, field_name, value): - field = model._meta.get_field(field_name) - if field.is_relation: - related_model = field.related_model - related_field_name_list = related_model._meta.fields # 获取关联模型的第一个字段名 - for related_field_name in related_field_name_list: - try: - related_instance = related_model.objects.get(**{related_field_name.name: value}) - value = related_instance - return value - except Exception: - continue - return None - else: - return value - + instance_list = [] for row in sheet.iter_rows(min_row=2, values_only=True): if not all(value is None for value in row): - instance_data = {header_to_field_map[header]: value for header, value in zip(header_row, row)} - preview_data = instance_data.copy() - for field_name, value in instance_data.items(): - instance_data[field_name] = get_related_instance(model, field_name, value) - - instance = model(**instance_data) - try: - instance.full_clean() - data.append(preview_data) - except ValidationError as e: - return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) + instance_list.append({header_to_field_map[header]: value for header, value in zip(header_row, row)}) # 清理,删除上传的文件 os.remove(file_path) - return JsonResponse({"table_data": data, "fields_map": fields_map}, safe=False) + return JsonResponse({"table_data": instance_list, "fields_map": fields_map}, safe=False) except Exception as e: # 清理,删除上传的文件 os.remove(file_path) @@ -275,3 +238,87 @@ def load_secondary_departments(request): 'secondary_department_name') return JsonResponse(list(secondary_departments.values('secondary_department_id', 'secondary_department_name')), safe=False) + + +@csrf_protect +@login_required +def save_excel_table_data_fac_inv(request): + if request.method == 'POST': + try: + data = json.loads(request.body) + model_config = data.get('model_config') + table_data = data.get('table_data') + + if not model_config or not table_data: + return JsonResponse({'error': '缺少必要的参数'}, status=400) + + # 分割 model_config 以获取 app_label 和 model_name + try: + app_label, model_name = model_config.split('.') + Model = apps.get_model(app_label, model_name) + except (ValueError, LookupError): + return JsonResponse({'error': '无效的 model_config'}, status=400) + + instances = [] + for row_data in table_data: + row_data['project_id'] = ProjectLedger.objects.get(project_name=row_data['project_name']) + instance = Model(**row_data) + try: + instance.full_clean() + instances.append(instance) + except ValidationError as e: + return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) + + # 批量保存数据 + Model.objects.bulk_create(instances) + + return JsonResponse({'success': '数据保存成功'}, status=201) + except json.JSONDecodeError: + return JsonResponse({'error': '无效的JSON数据'}, status=400) + except Exception as e: + return JsonResponse({'error': f'保存数据时出错: {str(e)}'}, status=500) + + return JsonResponse({'error': '请求错误'}, status=400) + + +@csrf_protect +@login_required +def save_excel_table_data_fac_rep(request): + if request.method == 'POST': + try: + data = json.loads(request.body) + model_config = data.get('model_config') + table_data = data.get('table_data') + + if not model_config or not table_data: + return JsonResponse({'error': '缺少必要的参数'}, status=400) + + # 分割 model_config 以获取 app_label 和 model_name + try: + app_label, model_name = model_config.split('.') + Model = apps.get_model(app_label, model_name) + except (ValueError, LookupError): + return JsonResponse({'error': '无效的 model_config'}, status=400) + + instances = [] + for row_data in table_data: + row_data['project_id'] = ProjectLedger.objects.get(project_name=row_data['project_name']) + row_data['invoice'] = InvoiceRecord.objects.get(project_name=row_data['project_name']) + instance = Model(**row_data) + try: + instance.full_clean() + instances.append(instance) + except ValidationError as e: + return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400) + + # 批量保存数据 + Model.objects.bulk_create(instances) + + return JsonResponse({'success': '数据保存成功'}, status=201) + except json.JSONDecodeError: + return JsonResponse({'error': '无效的JSON数据'}, status=400) + except Exception as e: + return JsonResponse({'error': f'保存数据时出错: {str(e)}'}, status=500) + + return JsonResponse({'error': '请求错误'}, status=400) +