import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/network/api_client.dart'; import '../../app.dart'; import '../../shared/models/pagination_model.dart'; import '../../shared/models/bill_attachment.dart'; import 'expense_apply_model.dart'; final expenseApplyApiProvider = Provider( (ref) => ExpenseApplyApi(ref.read(apiClientProvider)), ); // ═══ 参考数据模型(API 返回) ═══ class CostTypeItem { final String typeNo; final String typeName; final String accNo; final String accName; const CostTypeItem({required this.typeNo, required this.typeName, required this.accNo, required this.accName}); factory CostTypeItem.fromJson(Map json) => CostTypeItem( typeNo: json['typeNo'] as String? ?? '', typeName: json['typeName'] as String? ?? '', accNo: json['accNo'] as String? ?? '', accName: json['accName'] as String? ?? '', ); } class ProjectCodeItem { final String objNo; final String name; const ProjectCodeItem({required this.objNo, required this.name}); factory ProjectCodeItem.fromJson(Map json) => ProjectCodeItem( objNo: json['objNo'] as String? ?? '', name: json['name'] as String? ?? '', ); } class DepartmentItem { final String dep; final String name; const DepartmentItem({required this.dep, required this.name}); factory DepartmentItem.fromJson(Map json) => DepartmentItem( dep: json['dep'] as String? ?? '', name: json['name'] as String? ?? '', ); } class ExpenseApplyApi { final ApiClient _client; ExpenseApplyApi(this._client); /// 费用申请列表(分页) Future> fetchList({ String status = '', String keyword = '', String startDate = '', String endDate = '', String usr = '', int page = 1, int size = 20, }) async { final response = await _client.get>( '/OA/GetExpenseApplies', queryParameters: { 'status': status, 'keyword': keyword, 'startDate': startDate, 'endDate': endDate, 'usr': usr, 'page': page, 'size': size, }, ); return PaginatedData.fromJson(response.data!, ExpenseApplyModel.fromJson); } /// 费用申请详情(主表+明细) Future fetchDetail(String billNo) async { final response = await _client.get>( '/OA/GetExpenseApplyDetail', queryParameters: {'billNo': billNo}, ); return ExpenseApplyModel.fromJson(response.data!); } /// 费用类别字典 Future> getCostTypes({String keyword = '', String accNo = ''}) async { final response = await _client.get>( '/OA/GetCostTypes', queryParameters: {'keyword': keyword, 'accNo': accNo, 'page': 1, 'size': 100}, ); final list = (response.data?['list'] as List?) ?? []; return list.map((e) => CostTypeItem.fromJson(e as Map)).toList(); } /// 项目代号 Future> getProjectCodes({String keyword = '', String billDate = ''}) async { final response = await _client.get>( '/OA/GetProjectCodes', queryParameters: {'keyword': keyword, 'billDate': billDate, 'page': 1, 'size': 100}, ); final list = (response.data?['list'] as List?) ?? []; return list.map((e) => ProjectCodeItem.fromJson(e as Map)).toList(); } /// 部门 Future> getDepartments({String keyword = '', bool onlyActive = true}) async { final response = await _client.get>( '/OA/GetDepartments', queryParameters: {'keyword': keyword, 'onlyActive': onlyActive, 'page': 1, 'size': 100}, ); final list = (response.data?['list'] as List?) ?? []; return list.map((e) => DepartmentItem.fromJson(e as Map)).toList(); } /// 提交审批,返回申请单号 Future submit(Map data) async { final response = await _client.post>('/OA/BillSave', data: { 'erpCategory': 'MasterService', 'billId': 'AE', 'procId': '', 'data': data, }); final resData = response.data; // 尝试从 resultData 或 HeadData 中提取 AE_NO if (resData != null) { final resultData = resData['resultData'] as Map?; if (resultData != null) { final aeNo = resultData['AE_NO'] as String?; if (aeNo != null && aeNo.isNotEmpty) return aeNo; final headData = resultData['HeadData'] as Map?; if (headData != null) { final hdAeNo = headData['AE_NO'] as String?; if (hdAeNo != null && hdAeNo.isNotEmpty) return hdAeNo; } } } throw Exception('BillSave succeeded but could not extract bill number'); } /// 审批进度 Future> fetchApprovalTimeline(String bilId, String bilNo, {int bilItm = 0}) async { final response = await _client.get>( '/OA/GetApprovalTimeline', queryParameters: {'bilId': bilId, 'bilNo': bilNo, 'bilItm': bilItm}, ); return response.data!; } /// 获取单据附件列表 Future> getAttachments(String bilId, String bilNo, {int? srcItm}) async { final params = { 'bilId': bilId, 'bilNo': bilNo, }; if (srcItm != null) params['srcItm'] = srcItm; final response = await _client.get>( '/OA/GetAttachments', queryParameters: params, ); final data = response.data; final list = (data?['Result']?['list'] as List?) ?? (data?['list'] as List?) ?? []; return list.map((e) => BillAttachment.fromJson(e as Map)).toList(); } /// 上传附件 Future> uploadAttachment( String filePath, Map metadata, ) async { final fileName = (metadata['FILENAME'] as String?) ?? filePath.split('/').last; final response = await _client.uploadMultipart>( '/OA/UploadAttachment', files: [await MultipartFile.fromFile(filePath, filename: fileName)], extraFields: {'metadata': json.encode(metadata)}, ); return response.data ?? {}; } /// 审核执行(通过/驳回/反审核) Future> executeApproval({ required String bilId, required String bilNo, int bilItm = 0, required String action, String rem = '', String effDd = '', String reason = '', bool isPreToStart = false, int nodeIndex = -1, String dataBx = '', }) async { final response = await _client.post>( '/OA/ExecuteApproval', data: { 'bilId': bilId, 'bilNo': bilNo, 'bilItm': bilItm, 'action': action, 'rem': rem, 'effDd': effDd, 'reason': reason, 'isPreToStart': isPreToStart, 'nodeIndex': nodeIndex, 'dataBx': dataBx, }, ); return response.data!; } }