expense_model.dart 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. import '../../shared/models/approval_status.dart';
  2. /// 费用报销主表,对应 [Expense] 表。
  3. class ExpenseModel {
  4. final String id;
  5. final String expenseNo;
  6. final DateTime? expenseDate;
  7. final String applicantId;
  8. final String applicantName;
  9. final String deptId;
  10. final String deptName;
  11. final String currencyCode;
  12. final bool isGenerateVoucher;
  13. final String voucherNo;
  14. final double approvedAmount;
  15. final double totalAmount;
  16. final String purpose;
  17. final String remark;
  18. final String paymentMethod;
  19. final bool isInvoiceVerified;
  20. final bool isTaxIdMatched;
  21. final bool isCategoryCompliant;
  22. final String bankTransferNo;
  23. final String paymentStatus;
  24. final String status;
  25. final String approvalInstanceId;
  26. final String previousInstanceIds;
  27. final DateTime? effectiveDate;
  28. final String auditorId;
  29. final int version;
  30. final DateTime createTime;
  31. final DateTime updateTime;
  32. final bool isDeleted;
  33. // ── 瞬态字段(API 从 ERP 实时查询,非存储) ──
  34. final String currentApproverId;
  35. final List<String> approvalChain;
  36. final List<ApprovalRecord> approvalRecords;
  37. // ── 子表 ──
  38. final List<ExpenseDetailModel> details;
  39. final List<String> attachments;
  40. const ExpenseModel({
  41. required this.id,
  42. required this.expenseNo,
  43. this.expenseDate,
  44. this.applicantId = '',
  45. this.applicantName = '',
  46. this.deptId = '',
  47. this.deptName = '',
  48. this.currencyCode = 'CNY',
  49. this.isGenerateVoucher = false,
  50. this.voucherNo = '',
  51. this.approvedAmount = 0.0,
  52. this.totalAmount = 0.0,
  53. this.purpose = '',
  54. this.remark = '',
  55. this.paymentMethod = '',
  56. this.isInvoiceVerified = false,
  57. this.isTaxIdMatched = false,
  58. this.isCategoryCompliant = false,
  59. this.bankTransferNo = '',
  60. this.paymentStatus = 'unpaid',
  61. this.status = 'draft',
  62. this.approvalInstanceId = '',
  63. this.previousInstanceIds = '',
  64. this.effectiveDate,
  65. this.auditorId = '',
  66. this.version = 1,
  67. required this.createTime,
  68. required this.updateTime,
  69. this.isDeleted = false,
  70. this.currentApproverId = '',
  71. this.approvalChain = const [],
  72. this.approvalRecords = const [],
  73. this.details = const [],
  74. this.attachments = const [],
  75. });
  76. factory ExpenseModel.fromJson(Map<String, dynamic> json) {
  77. return ExpenseModel(
  78. id: json['id'] as String,
  79. expenseNo: json['expenseNo'] as String? ?? '',
  80. expenseDate: json['expenseDate'] != null
  81. ? DateTime.parse(json['expenseDate'] as String)
  82. : null,
  83. applicantId: json['applicantId'] as String? ?? '',
  84. applicantName: json['applicantName'] as String? ?? '',
  85. deptId: json['deptId'] as String? ?? '',
  86. deptName: json['deptName'] as String? ?? '',
  87. currencyCode: json['currencyCode'] as String? ?? 'CNY',
  88. isGenerateVoucher: json['isGenerateVoucher'] as bool? ?? false,
  89. voucherNo: json['voucherNo'] as String? ?? '',
  90. approvedAmount: (json['approvedAmount'] as num?)?.toDouble() ?? 0.0,
  91. totalAmount: (json['totalAmount'] as num?)?.toDouble() ?? 0.0,
  92. purpose: json['purpose'] as String? ?? '',
  93. remark: json['remark'] as String? ?? '',
  94. paymentMethod: json['paymentMethod'] as String? ?? '',
  95. isInvoiceVerified: json['isInvoiceVerified'] as bool? ?? false,
  96. isTaxIdMatched: json['isTaxIdMatched'] as bool? ?? false,
  97. isCategoryCompliant: json['isCategoryCompliant'] as bool? ?? false,
  98. bankTransferNo: json['bankTransferNo'] as String? ?? '',
  99. paymentStatus: json['paymentStatus'] as String? ?? 'unpaid',
  100. status: json['status'] as String? ?? 'draft',
  101. approvalInstanceId: json['approvalInstanceId'] as String? ?? '',
  102. previousInstanceIds: json['previousInstanceIds'] as String? ?? '',
  103. effectiveDate: json['effectiveDate'] != null
  104. ? DateTime.parse(json['effectiveDate'] as String)
  105. : null,
  106. auditorId: json['auditorId'] as String? ?? '',
  107. version: json['version'] as int? ?? 1,
  108. createTime: DateTime.parse(json['createTime'] as String),
  109. updateTime: DateTime.parse(json['updateTime'] as String),
  110. isDeleted: json['isDeleted'] as bool? ?? false,
  111. currentApproverId: json['currentApproverId'] as String? ?? '',
  112. approvalChain:
  113. (json['approvalChain'] as List<dynamic>?)
  114. ?.map((e) => e as String)
  115. .toList() ??
  116. [],
  117. approvalRecords:
  118. (json['approvalRecords'] as List<dynamic>?)
  119. ?.map((e) => ApprovalRecord.fromJson(e as Map<String, dynamic>))
  120. .toList() ??
  121. [],
  122. details:
  123. (json['details'] as List<dynamic>?)
  124. ?.map(
  125. (e) => ExpenseDetailModel.fromJson(e as Map<String, dynamic>),
  126. )
  127. .toList() ??
  128. [],
  129. attachments:
  130. (json['attachments'] as List<dynamic>?)
  131. ?.map((e) => e as String)
  132. .toList() ??
  133. [],
  134. );
  135. }
  136. Map<String, dynamic> toJson() => {
  137. 'id': id,
  138. 'expenseNo': expenseNo,
  139. 'expenseDate': expenseDate?.toIso8601String(),
  140. 'applicantId': applicantId,
  141. 'applicantName': applicantName,
  142. 'deptId': deptId,
  143. 'deptName': deptName,
  144. 'currencyCode': currencyCode,
  145. 'isGenerateVoucher': isGenerateVoucher,
  146. 'voucherNo': voucherNo,
  147. 'approvedAmount': approvedAmount,
  148. 'totalAmount': totalAmount,
  149. 'purpose': purpose,
  150. 'remark': remark,
  151. 'paymentMethod': paymentMethod,
  152. 'isInvoiceVerified': isInvoiceVerified,
  153. 'isTaxIdMatched': isTaxIdMatched,
  154. 'isCategoryCompliant': isCategoryCompliant,
  155. 'bankTransferNo': bankTransferNo,
  156. 'paymentStatus': paymentStatus,
  157. 'status': status,
  158. 'approvalInstanceId': approvalInstanceId,
  159. 'previousInstanceIds': previousInstanceIds,
  160. 'effectiveDate': effectiveDate?.toIso8601String(),
  161. 'auditorId': auditorId,
  162. 'version': version,
  163. 'createTime': createTime.toIso8601String(),
  164. 'updateTime': updateTime.toIso8601String(),
  165. 'isDeleted': isDeleted,
  166. 'currentApproverId': currentApproverId,
  167. 'approvalChain': approvalChain,
  168. 'approvalRecords': approvalRecords.map((r) => r.toJson()).toList(),
  169. 'details': details.map((d) => d.toJson()).toList(),
  170. 'attachments': attachments,
  171. };
  172. ExpenseModel copyWith({
  173. String? id,
  174. String? expenseNo,
  175. DateTime? expenseDate,
  176. String? applicantId,
  177. String? applicantName,
  178. String? deptId,
  179. String? deptName,
  180. String? currencyCode,
  181. bool? isGenerateVoucher,
  182. String? voucherNo,
  183. double? approvedAmount,
  184. double? totalAmount,
  185. String? purpose,
  186. String? remark,
  187. String? paymentMethod,
  188. bool? isInvoiceVerified,
  189. bool? isTaxIdMatched,
  190. bool? isCategoryCompliant,
  191. String? bankTransferNo,
  192. String? paymentStatus,
  193. String? status,
  194. String? approvalInstanceId,
  195. String? previousInstanceIds,
  196. DateTime? effectiveDate,
  197. String? auditorId,
  198. int? version,
  199. DateTime? createTime,
  200. DateTime? updateTime,
  201. bool? isDeleted,
  202. String? currentApproverId,
  203. List<String>? approvalChain,
  204. List<ApprovalRecord>? approvalRecords,
  205. List<ExpenseDetailModel>? details,
  206. List<String>? attachments,
  207. }) {
  208. return ExpenseModel(
  209. id: id ?? this.id,
  210. expenseNo: expenseNo ?? this.expenseNo,
  211. expenseDate: expenseDate ?? this.expenseDate,
  212. applicantId: applicantId ?? this.applicantId,
  213. applicantName: applicantName ?? this.applicantName,
  214. deptId: deptId ?? this.deptId,
  215. deptName: deptName ?? this.deptName,
  216. currencyCode: currencyCode ?? this.currencyCode,
  217. isGenerateVoucher: isGenerateVoucher ?? this.isGenerateVoucher,
  218. voucherNo: voucherNo ?? this.voucherNo,
  219. approvedAmount: approvedAmount ?? this.approvedAmount,
  220. totalAmount: totalAmount ?? this.totalAmount,
  221. purpose: purpose ?? this.purpose,
  222. paymentMethod: paymentMethod ?? this.paymentMethod,
  223. isInvoiceVerified: isInvoiceVerified ?? this.isInvoiceVerified,
  224. isTaxIdMatched: isTaxIdMatched ?? this.isTaxIdMatched,
  225. isCategoryCompliant: isCategoryCompliant ?? this.isCategoryCompliant,
  226. bankTransferNo: bankTransferNo ?? this.bankTransferNo,
  227. paymentStatus: paymentStatus ?? this.paymentStatus,
  228. status: status ?? this.status,
  229. approvalInstanceId: approvalInstanceId ?? this.approvalInstanceId,
  230. previousInstanceIds: previousInstanceIds ?? this.previousInstanceIds,
  231. effectiveDate: effectiveDate ?? this.effectiveDate,
  232. auditorId: auditorId ?? this.auditorId,
  233. version: version ?? this.version,
  234. createTime: createTime ?? this.createTime,
  235. updateTime: updateTime ?? this.updateTime,
  236. isDeleted: isDeleted ?? this.isDeleted,
  237. currentApproverId: currentApproverId ?? this.currentApproverId,
  238. approvalChain: approvalChain ?? this.approvalChain,
  239. approvalRecords: approvalRecords ?? this.approvalRecords,
  240. details: details ?? this.details,
  241. attachments: attachments ?? this.attachments,
  242. );
  243. }
  244. }
  245. /// 费用报销明细,对应 [ExpenseDetail] 表。
  246. class ExpenseDetailModel {
  247. final String id;
  248. final String expenseId;
  249. final String expenseApplyId;
  250. final String expenseApplyNo;
  251. final DateTime? expenseApplyDate;
  252. final String expenseCategory;
  253. final String purpose;
  254. final String projectId;
  255. final String projectName;
  256. final String costDeptId;
  257. final String costDeptName;
  258. final String acctSubjectId;
  259. final String acctSubjectName;
  260. final double amount;
  261. final double taxRate;
  262. final double taxAmount;
  263. final double totalAmount;
  264. final String currencyCode;
  265. final double exchangeRate;
  266. final double baseAmount;
  267. final double approvedAmount;
  268. final String customerVendorId;
  269. final String customerVendorName;
  270. final double offsetAmount;
  271. final String bankName;
  272. final String bankAccountName;
  273. final String bankAccount;
  274. final String remark;
  275. final int sortOrder;
  276. final List<String> attachments;
  277. final DateTime createTime;
  278. final DateTime updateTime;
  279. final bool isDeleted;
  280. const ExpenseDetailModel({
  281. required this.id,
  282. required this.expenseId,
  283. this.expenseApplyId = '',
  284. this.expenseApplyNo = '',
  285. this.expenseApplyDate,
  286. this.expenseCategory = '',
  287. this.purpose = '',
  288. this.projectId = '',
  289. this.projectName = '',
  290. this.costDeptId = '',
  291. this.costDeptName = '',
  292. this.acctSubjectId = '',
  293. this.acctSubjectName = '',
  294. required this.amount,
  295. this.taxRate = 0.0,
  296. this.taxAmount = 0.0,
  297. required this.totalAmount,
  298. this.currencyCode = 'CNY',
  299. this.exchangeRate = 1.0,
  300. this.baseAmount = 0.0,
  301. this.approvedAmount = 0.0,
  302. this.customerVendorId = '',
  303. this.customerVendorName = '',
  304. this.offsetAmount = 0.0,
  305. this.bankName = '',
  306. this.bankAccountName = '',
  307. this.bankAccount = '',
  308. this.remark = '',
  309. this.sortOrder = 1,
  310. this.attachments = const [],
  311. required this.createTime,
  312. required this.updateTime,
  313. this.isDeleted = false,
  314. });
  315. factory ExpenseDetailModel.fromJson(Map<String, dynamic> json) {
  316. return ExpenseDetailModel(
  317. id: json['id'] as String,
  318. expenseId: json['expenseId'] as String? ?? '',
  319. expenseApplyId: json['expenseApplyId'] as String? ?? '',
  320. expenseApplyNo: json['expenseApplyNo'] as String? ?? '',
  321. expenseApplyDate: json['expenseApplyDate'] != null
  322. ? DateTime.parse(json['expenseApplyDate'] as String)
  323. : null,
  324. expenseCategory: json['expenseCategory'] as String? ?? '',
  325. purpose: json['purpose'] as String? ?? '',
  326. projectId: json['projectId'] as String? ?? '',
  327. projectName: json['projectName'] as String? ?? '',
  328. costDeptId: json['costDeptId'] as String? ?? '',
  329. costDeptName: json['costDeptName'] as String? ?? '',
  330. acctSubjectId: json['acctSubjectId'] as String? ?? '',
  331. acctSubjectName: json['acctSubjectName'] as String? ?? '',
  332. amount: (json['amount'] as num?)?.toDouble() ?? 0.0,
  333. taxRate: (json['taxRate'] as num?)?.toDouble() ?? 0.0,
  334. taxAmount: (json['taxAmount'] as num?)?.toDouble() ?? 0.0,
  335. totalAmount: (json['totalAmount'] as num?)?.toDouble() ?? 0.0,
  336. currencyCode: json['currencyCode'] as String? ?? 'CNY',
  337. exchangeRate: (json['exchangeRate'] as num?)?.toDouble() ?? 1.0,
  338. baseAmount: (json['baseAmount'] as num?)?.toDouble() ?? 0.0,
  339. approvedAmount: (json['approvedAmount'] as num?)?.toDouble() ?? 0.0,
  340. customerVendorId: json['customerVendorId'] as String? ?? '',
  341. customerVendorName: json['customerVendorName'] as String? ?? '',
  342. offsetAmount: (json['offsetAmount'] as num?)?.toDouble() ?? 0.0,
  343. bankName: json['bankName'] as String? ?? '',
  344. bankAccountName: json['bankAccountName'] as String? ?? '',
  345. bankAccount: json['bankAccount'] as String? ?? '',
  346. remark: json['remark'] as String? ?? '',
  347. sortOrder: json['sortOrder'] as int? ?? 1,
  348. attachments:
  349. (json['attachments'] as List<dynamic>?)
  350. ?.map((e) => e as String)
  351. .toList() ??
  352. [],
  353. createTime: DateTime.parse(json['createTime'] as String),
  354. updateTime: DateTime.parse(json['updateTime'] as String),
  355. isDeleted: json['isDeleted'] as bool? ?? false,
  356. );
  357. }
  358. Map<String, dynamic> toJson() => {
  359. 'id': id,
  360. 'expenseId': expenseId,
  361. 'expenseApplyId': expenseApplyId,
  362. 'expenseApplyNo': expenseApplyNo,
  363. 'expenseApplyDate': expenseApplyDate?.toIso8601String(),
  364. 'expenseCategory': expenseCategory,
  365. 'purpose': purpose,
  366. 'projectId': projectId,
  367. 'projectName': projectName,
  368. 'costDeptId': costDeptId,
  369. 'costDeptName': costDeptName,
  370. 'acctSubjectId': acctSubjectId,
  371. 'acctSubjectName': acctSubjectName,
  372. 'amount': amount,
  373. 'taxRate': taxRate,
  374. 'taxAmount': taxAmount,
  375. 'totalAmount': totalAmount,
  376. 'currencyCode': currencyCode,
  377. 'exchangeRate': exchangeRate,
  378. 'baseAmount': baseAmount,
  379. 'approvedAmount': approvedAmount,
  380. 'customerVendorId': customerVendorId,
  381. 'customerVendorName': customerVendorName,
  382. 'offsetAmount': offsetAmount,
  383. 'bankName': bankName,
  384. 'bankAccountName': bankAccountName,
  385. 'bankAccount': bankAccount,
  386. 'remark': remark,
  387. 'sortOrder': sortOrder,
  388. 'attachments': attachments,
  389. 'createTime': createTime.toIso8601String(),
  390. 'updateTime': updateTime.toIso8601String(),
  391. 'isDeleted': isDeleted,
  392. };
  393. ExpenseDetailModel copyWith({
  394. String? id,
  395. String? expenseId,
  396. String? expenseApplyId,
  397. String? expenseApplyNo,
  398. DateTime? expenseApplyDate,
  399. String? expenseCategory,
  400. String? purpose,
  401. String? projectId,
  402. String? projectName,
  403. String? costDeptId,
  404. String? costDeptName,
  405. String? acctSubjectId,
  406. String? acctSubjectName,
  407. double? amount,
  408. double? taxRate,
  409. double? taxAmount,
  410. double? totalAmount,
  411. String? currencyCode,
  412. double? exchangeRate,
  413. double? baseAmount,
  414. double? approvedAmount,
  415. String? customerVendorId,
  416. String? customerVendorName,
  417. double? offsetAmount,
  418. String? bankName,
  419. String? bankAccountName,
  420. String? bankAccount,
  421. String? remark,
  422. int? sortOrder,
  423. List<String>? attachments,
  424. DateTime? createTime,
  425. DateTime? updateTime,
  426. bool? isDeleted,
  427. }) {
  428. return ExpenseDetailModel(
  429. id: id ?? this.id,
  430. expenseId: expenseId ?? this.expenseId,
  431. expenseApplyId: expenseApplyId ?? this.expenseApplyId,
  432. expenseApplyNo: expenseApplyNo ?? this.expenseApplyNo,
  433. expenseApplyDate: expenseApplyDate ?? this.expenseApplyDate,
  434. expenseCategory: expenseCategory ?? this.expenseCategory,
  435. purpose: purpose ?? this.purpose,
  436. projectId: projectId ?? this.projectId,
  437. projectName: projectName ?? this.projectName,
  438. costDeptId: costDeptId ?? this.costDeptId,
  439. costDeptName: costDeptName ?? this.costDeptName,
  440. acctSubjectId: acctSubjectId ?? this.acctSubjectId,
  441. acctSubjectName: acctSubjectName ?? this.acctSubjectName,
  442. amount: amount ?? this.amount,
  443. taxRate: taxRate ?? this.taxRate,
  444. taxAmount: taxAmount ?? this.taxAmount,
  445. totalAmount: totalAmount ?? this.totalAmount,
  446. currencyCode: currencyCode ?? this.currencyCode,
  447. exchangeRate: exchangeRate ?? this.exchangeRate,
  448. baseAmount: baseAmount ?? this.baseAmount,
  449. approvedAmount: approvedAmount ?? this.approvedAmount,
  450. customerVendorId: customerVendorId ?? this.customerVendorId,
  451. customerVendorName: customerVendorName ?? this.customerVendorName,
  452. offsetAmount: offsetAmount ?? this.offsetAmount,
  453. bankName: bankName ?? this.bankName,
  454. bankAccountName: bankAccountName ?? this.bankAccountName,
  455. bankAccount: bankAccount ?? this.bankAccount,
  456. remark: remark ?? this.remark,
  457. sortOrder: sortOrder ?? this.sortOrder,
  458. attachments: attachments ?? this.attachments,
  459. createTime: createTime ?? this.createTime,
  460. updateTime: updateTime ?? this.updateTime,
  461. isDeleted: isDeleted ?? this.isDeleted,
  462. );
  463. }
  464. }