/// 加班申请主表,对应 OA_OVERTIME 表。 class OvertimeApplyModel { final String jbNo; // JB_NO 加班单号(主键) final DateTime? jbDd; // JB_DD 申请日期 final String salNo; // SAL_NO 申请人 final String salName; // SAL_NAME 申请人姓名(瞬态) final String dep; // DEP 所属部门 final String depName; // DEP_NAME 部门名称(瞬态) final String reason; // REASON 加班原因 final String rem; // REM 备注 final String usr; // USR 录入人 final DateTime? recordDd; // RECORD_DD 录入日期 final String chkMan; // CHK_MAN 审核人 final DateTime? clsDate; // CLS_DATE 终审日期 final bool isAuditApproved; // 瞬态:审批状态 final double totalJbHours; // 明细加班时长合计(列表API返回) // ── 子表 ── final List details; const OvertimeApplyModel({ required this.jbNo, this.jbDd, this.salNo = '', this.salName = '', this.dep = '', this.depName = '', this.reason = '', this.rem = '', this.usr = '', this.recordDd, this.chkMan = '', this.clsDate, this.isAuditApproved = false, this.totalJbHours = 0, this.details = const [], }); factory OvertimeApplyModel.fromJson(Map json) { return OvertimeApplyModel( jbNo: json['JB_NO'] as String? ?? '', jbDd: json['JB_DD'] != null ? DateTime.tryParse(json['JB_DD'] as String) : null, salNo: json['SAL_NO'] as String? ?? '', salName: json['SAL_NAME'] as String? ?? json['NAME'] as String? ?? '', dep: json['DEP'] as String? ?? '', depName: json['DEP_NAME'] as String? ?? '', reason: json['REASON'] as String? ?? '', rem: json['REM'] as String? ?? '', usr: json['USR'] as String? ?? '', recordDd: json['RECORD_DD'] != null ? DateTime.tryParse(json['RECORD_DD'] as String) : null, chkMan: json['CHK_MAN'] as String? ?? '', clsDate: json['CLS_DATE'] != null ? DateTime.tryParse(json['CLS_DATE'] as String) : null, isAuditApproved: (json['isAuditApproved'] as int?) == 1, totalJbHours: (json['TotalJbHours'] as num?)?.toDouble() ?? (json['totalJbHours'] as num?)?.toDouble() ?? 0, details: _parseDetails(json['Details'] ?? json['details']), ); } Map toJson() => { 'JB_NO': jbNo, 'JB_DD': jbDd?.toIso8601String(), 'SAL_NO': salNo, 'SAL_NAME': salName, 'DEP': dep, 'DEP_NAME': depName, 'REASON': reason, 'REM': rem, 'USR': usr, 'RECORD_DD': recordDd?.toIso8601String(), 'CHK_MAN': chkMan, 'CLS_DATE': clsDate?.toIso8601String(), 'isAuditApproved': isAuditApproved ? 1 : 0, 'details': details.map((d) => d.toJson()).toList(), }; static List _parseDetails(dynamic data) { if (data == null) return []; if (data is List) { return data .map((e) => OvertimeApplyDetailModel.fromJson(e as Map)) .toList(); } return []; } OvertimeApplyModel copyWith({ String? jbNo, DateTime? jbDd, String? salNo, String? salName, String? dep, String? depName, String? reason, String? rem, String? usr, DateTime? recordDd, String? chkMan, DateTime? clsDate, bool? isAuditApproved, List? details, }) { return OvertimeApplyModel( jbNo: jbNo ?? this.jbNo, jbDd: jbDd ?? this.jbDd, salNo: salNo ?? this.salNo, salName: salName ?? this.salName, dep: dep ?? this.dep, depName: depName ?? this.depName, reason: reason ?? this.reason, rem: rem ?? this.rem, usr: usr ?? this.usr, recordDd: recordDd ?? this.recordDd, chkMan: chkMan ?? this.chkMan, clsDate: clsDate ?? this.clsDate, isAuditApproved: isAuditApproved ?? this.isAuditApproved, details: details ?? this.details, ); } } /// 加班明细,对应 OA_OVERTIME_DETAIL 表。 class OvertimeApplyDetailModel { final int itm; // ITM 项次 final String? jbNo; // JB_NO 加班单号 final String salNo; // SAL_NO 员工代号 final String salName; // 员工姓名(瞬态) final String dep; // DEP 部门 final String jbType; // JB_TYPE: WORKING_DAY/REST_DAY/PUBLIC_HOLIDAY/SPECIAL_HOLIDAY/OTHER final DateTime? jbDate; // JB_DATE 加班日期 final DateTime? startTime; // START_TIME 开始时间 final DateTime? endTime; // END_TIME 结束时间 final double jbHours; // JB_HOURS 加班时长 final double jbDays; // JB_DAYS 加班天数 final String attPeriod; // ATT_PERIOD 考勤周期 yyyy-MM final String reason; // REASON 事由 final String compensationType; // COMPENSATION_TYPE: OVERTIME_PAY/COMPENSATORY_LEAVE/NO_COMPENSATION/OTHER final double compensationCount; // COMPENSATION_COUNT 折算补偿次数 final String adr; // ADR 地点 final String rem; // REM 备注 final int dayOfWeek; // DAY_OF_WEEK 星期几标记位(0=无, 1=日~7=六) const OvertimeApplyDetailModel({ this.itm = 0, this.jbNo = '', this.salNo = '', this.salName = '', this.dep = '', this.jbType = 'WORKING_DAY', this.jbDate, this.startTime, this.endTime, this.jbHours = 0.0, this.jbDays = 0.0, this.attPeriod = '', this.reason = '', this.compensationType = 'OVERTIME_PAY', this.compensationCount = 0.0, this.adr = '', this.rem = '', this.dayOfWeek = 0, }); factory OvertimeApplyDetailModel.fromJson(Map json) { return OvertimeApplyDetailModel( jbNo: json['JB_NO'] as String?, itm: json['ITM'] as int? ?? 0, salNo: json['SAL_NO'] as String? ?? '', salName: json['SAL_NAME'] as String? ?? '', dep: json['DEP'] as String? ?? '', jbType: json['JB_TYPE'] as String? ?? 'WORKING_DAY', jbDate: json['JB_DATE'] != null ? DateTime.tryParse(json['JB_DATE'] as String) : null, startTime: json['START_TIME'] != null ? DateTime.tryParse(json['START_TIME'] as String) : null, endTime: json['END_TIME'] != null ? DateTime.tryParse(json['END_TIME'] as String) : null, jbHours: (json['JB_HOURS'] as num?)?.toDouble() ?? 0.0, jbDays: (json['JB_DAYS'] as num?)?.toDouble() ?? 0.0, attPeriod: json['ATT_PERIOD'] as String? ?? '', reason: json['REASON'] as String? ?? '', compensationType: json['COMPENSATION_TYPE'] as String? ?? 'OVERTIME_PAY', compensationCount: (json['COMPENSATION_COUNT'] as num?)?.toDouble() ?? 0.0, adr: json['ADR'] as String? ?? '', rem: json['REM'] as String? ?? '', dayOfWeek: json['DAY_OF_WEEK'] is int ? json['DAY_OF_WEEK'] as int : int.tryParse( (json['DAY_OF_WEEK'] ?? json['dayOfWeek'])?.toString() ?? '') ?? 0, ); } Map toJson() => { 'JB_NO': jbNo, 'ITM': itm, 'SAL_NO': salNo, 'SAL_NAME': salName, 'DEP': dep, 'JB_TYPE': jbType, 'JB_DATE': jbDate?.toIso8601String(), 'START_TIME': startTime?.toIso8601String(), 'END_TIME': endTime?.toIso8601String(), 'JB_HOURS': jbHours, 'JB_DAYS': jbDays, 'ATT_PERIOD': attPeriod, 'REASON': reason, 'COMPENSATION_TYPE': compensationType, 'COMPENSATION_COUNT': compensationCount, 'ADR': adr, 'REM': rem, 'DAY_OF_WEEK': dayOfWeek.toString(), }; OvertimeApplyDetailModel copyWith({ int? itm, String? jbNo, String? salNo, String? salName, String? dep, String? jbType, DateTime? jbDate, DateTime? startTime, DateTime? endTime, double? jbHours, double? jbDays, String? attPeriod, String? reason, String? compensationType, double? compensationCount, String? adr, String? rem, int? dayOfWeek, }) { return OvertimeApplyDetailModel( itm: itm ?? this.itm, jbNo: jbNo ?? this.jbNo, salNo: salNo ?? this.salNo, salName: salName ?? this.salName, dep: dep ?? this.dep, jbType: jbType ?? this.jbType, jbDate: jbDate ?? this.jbDate, startTime: startTime ?? this.startTime, endTime: endTime ?? this.endTime, jbHours: jbHours ?? this.jbHours, jbDays: jbDays ?? this.jbDays, attPeriod: attPeriod ?? this.attPeriod, reason: reason ?? this.reason, compensationType: compensationType ?? this.compensationType, compensationCount: compensationCount ?? this.compensationCount, adr: adr ?? this.adr, rem: rem ?? this.rem, dayOfWeek: dayOfWeek ?? this.dayOfWeek, ); } }