388 lines
14 KiB
Dart
388 lines
14 KiB
Dart
class PenugasanModel {
|
|
final int idPenugasan;
|
|
final int idTeknisi;
|
|
final String? fotoSurat; // ✅ Ganti lokasi → foto surat
|
|
final String? fotoSuratUrl; // ✅ URL lengkap foto surat
|
|
final String tanggalDiberikan;
|
|
final String? catatanAdmin;
|
|
final String? alamatLokasi;
|
|
final String? namaPelanggan;
|
|
final String? noSambungan;
|
|
|
|
// Detail diisi teknisi via mobile
|
|
final String? jenisPekerjaan;
|
|
final String? labelJenisPekerjaan;
|
|
final String? dimensiPipa;
|
|
final double? jarakMeter;
|
|
final int? jumlahUnit;
|
|
final int? jumlahTitik;
|
|
final bool? pakaiPipaBesi;
|
|
final String? jenisPengangkatan;
|
|
final String? detailPekerjaan;
|
|
|
|
// Ongkos
|
|
final int? idTarif;
|
|
final double? totalNilaiPekerjaan;
|
|
|
|
// Status
|
|
final String statusPekerjaan;
|
|
final String? tanggalMulai;
|
|
final String? tanggalDiselesaikan;
|
|
|
|
// Garansi
|
|
final String? tanggalGaransiMulai;
|
|
final String? tanggalGaransiSelesai;
|
|
final String? catatanGaransi;
|
|
final bool? isGaransiAktif;
|
|
final int? sisaHariGaransi;
|
|
|
|
// Foto bukti pekerjaan
|
|
final String? fotoSebelum;
|
|
final String? fotoSesudah;
|
|
final String? fotoSebelumUrl;
|
|
final String? fotoSesudahUrl;
|
|
|
|
// Relasi
|
|
final TeknisiModel? teknisi;
|
|
final TarifModel? tarif;
|
|
final List<TimTeknisiModel>? timTeknisi;
|
|
|
|
PenugasanModel({
|
|
required this.idPenugasan,
|
|
required this.idTeknisi,
|
|
this.fotoSurat,
|
|
this.fotoSuratUrl,
|
|
required this.tanggalDiberikan,
|
|
this.catatanAdmin,
|
|
this.alamatLokasi,
|
|
this.namaPelanggan,
|
|
this.noSambungan,
|
|
this.jenisPekerjaan,
|
|
this.labelJenisPekerjaan,
|
|
this.dimensiPipa,
|
|
this.jarakMeter,
|
|
this.jumlahUnit,
|
|
this.jumlahTitik,
|
|
this.pakaiPipaBesi,
|
|
this.jenisPengangkatan,
|
|
this.detailPekerjaan,
|
|
this.idTarif,
|
|
this.totalNilaiPekerjaan,
|
|
required this.statusPekerjaan,
|
|
this.tanggalMulai,
|
|
this.tanggalDiselesaikan,
|
|
this.tanggalGaransiMulai,
|
|
this.tanggalGaransiSelesai,
|
|
this.catatanGaransi,
|
|
this.isGaransiAktif,
|
|
this.sisaHariGaransi,
|
|
this.fotoSebelum,
|
|
this.fotoSesudah,
|
|
this.fotoSebelumUrl,
|
|
this.fotoSesudahUrl,
|
|
this.teknisi,
|
|
this.tarif,
|
|
this.timTeknisi,
|
|
});
|
|
|
|
factory PenugasanModel.fromJson(Map<String, dynamic> json) {
|
|
return PenugasanModel(
|
|
idPenugasan: int.tryParse(json['id_penugasan']?.toString() ?? '') ?? 0,
|
|
idTeknisi: int.tryParse(json['id_teknisi']?.toString() ?? '') ?? 0,
|
|
fotoSurat: json['foto_surat'],
|
|
fotoSuratUrl: json['foto_surat_url'],
|
|
tanggalDiberikan: json['tanggal_diberikan'] ?? '',
|
|
catatanAdmin: json['catatan_admin'],
|
|
alamatLokasi: json['alamat_lokasi'],
|
|
namaPelanggan: json['nama_pelanggan'],
|
|
noSambungan: json['no_sambungan'],
|
|
jenisPekerjaan: json['jenis_pekerjaan'],
|
|
labelJenisPekerjaan: json['label_jenis_pekerjaan'],
|
|
dimensiPipa: json['dimensi_pipa'],
|
|
jarakMeter: json['jarak_meter'] != null
|
|
? double.tryParse(json['jarak_meter'].toString())
|
|
: null,
|
|
jumlahUnit: json['jumlah_unit'] != null
|
|
? int.tryParse(json['jumlah_unit'].toString())
|
|
: null,
|
|
jumlahTitik: json['jumlah_titik'] != null
|
|
? int.tryParse(json['jumlah_titik'].toString())
|
|
: null,
|
|
pakaiPipaBesi: json['pakai_pipa_besi'] != null
|
|
? json['pakai_pipa_besi'] == 1 ||
|
|
json['pakai_pipa_besi'] == true ||
|
|
json['pakai_pipa_besi'].toString() == '1' ||
|
|
json['pakai_pipa_besi'].toString() == 'true'
|
|
: null,
|
|
jenisPengangkatan: json['jenis_pengangkatan'],
|
|
detailPekerjaan: json['detail_pekerjaan'],
|
|
idTarif: json['id_tarif'] != null
|
|
? int.tryParse(json['id_tarif'].toString())
|
|
: null,
|
|
totalNilaiPekerjaan: json['total_nilai_pekerjaan'] != null
|
|
? double.tryParse(json['total_nilai_pekerjaan'].toString())
|
|
: null,
|
|
statusPekerjaan: json['status_pekerjaan'] ?? 'belum_mulai',
|
|
tanggalMulai: json['tanggal_mulai'],
|
|
tanggalDiselesaikan: json['tanggal_diselesaikan'],
|
|
tanggalGaransiMulai: json['tanggal_garansi_mulai'],
|
|
tanggalGaransiSelesai:json['tanggal_garansi_selesai'],
|
|
catatanGaransi: json['catatan_garansi'],
|
|
isGaransiAktif: json['is_garansi_aktif'] != null
|
|
? json['is_garansi_aktif'] == 1 ||
|
|
json['is_garansi_aktif'] == true ||
|
|
json['is_garansi_aktif'].toString() == '1' ||
|
|
json['is_garansi_aktif'].toString() == 'true'
|
|
: null,
|
|
sisaHariGaransi: json['sisa_hari_garansi'] != null
|
|
? int.tryParse(json['sisa_hari_garansi'].toString())
|
|
: null,
|
|
fotoSebelum: json['foto_sebelum'],
|
|
fotoSesudah: json['foto_sesudah'],
|
|
fotoSebelumUrl: json['foto_sebelum_url'],
|
|
fotoSesudahUrl: json['foto_sesudah_url'],
|
|
teknisi: json['teknisi'] != null
|
|
? TeknisiModel.fromJson(json['teknisi'])
|
|
: null,
|
|
tarif: json['tarif'] != null
|
|
? TarifModel.fromJson(json['tarif'])
|
|
: null,
|
|
timTeknisi: json['tim_teknisi'] != null
|
|
? (json['tim_teknisi'] as List)
|
|
.map((e) => TimTeknisiModel.fromJson(e))
|
|
.toList()
|
|
: null,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'id_penugasan': idPenugasan,
|
|
'id_teknisi': idTeknisi,
|
|
'foto_surat': fotoSurat,
|
|
'tanggal_diberikan': tanggalDiberikan,
|
|
'catatan_admin': catatanAdmin,
|
|
'alamat_lokasi': alamatLokasi,
|
|
'nama_pelanggan': namaPelanggan,
|
|
'no_sambungan': noSambungan,
|
|
'jenis_pekerjaan': jenisPekerjaan,
|
|
'dimensi_pipa': dimensiPipa,
|
|
'jarak_meter': jarakMeter,
|
|
'jumlah_unit': jumlahUnit,
|
|
'jumlah_titik': jumlahTitik,
|
|
'pakai_pipa_besi': pakaiPipaBesi,
|
|
'jenis_pengangkatan': jenisPengangkatan,
|
|
'detail_pekerjaan': detailPekerjaan,
|
|
'id_tarif': idTarif,
|
|
'total_nilai_pekerjaan': totalNilaiPekerjaan,
|
|
'status_pekerjaan': statusPekerjaan,
|
|
};
|
|
}
|
|
|
|
// ===================================
|
|
// HELPER
|
|
// ===================================
|
|
|
|
bool get isDetailLengkap => jenisPekerjaan != null;
|
|
|
|
String get statusLabel {
|
|
switch (statusPekerjaan) {
|
|
case 'belum_mulai': return 'Belum Mulai';
|
|
case 'dalam_proses': return 'Dalam Proses';
|
|
case 'selesai': return 'Selesai';
|
|
case 'dibatalkan': return 'Dibatalkan';
|
|
default: return statusPekerjaan;
|
|
}
|
|
}
|
|
|
|
String get statusColor {
|
|
switch (statusPekerjaan) {
|
|
case 'belum_mulai': return '#9E9E9E';
|
|
case 'dalam_proses': return '#FFA500';
|
|
case 'selesai': return '#4CAF50';
|
|
case 'dibatalkan': return '#F44336';
|
|
default: return '#9E9E9E';
|
|
}
|
|
}
|
|
|
|
String get labelJenisPekerjaanFallback {
|
|
if (labelJenisPekerjaan != null) return labelJenisPekerjaan!;
|
|
switch (jenisPekerjaan) {
|
|
case 'sr': return 'SR (Sambungan Rumah)';
|
|
case 'pengembangan_jaringan_pipa': return 'Pengembangan Jaringan Pipa';
|
|
case 'pengangkatan': return 'Pengangkatan';
|
|
case 'pemasangan_gate_valve': return 'Pemasangan Gate Valve';
|
|
case 'gali_urug': return 'Gali Urug';
|
|
case 'perbaikan_jaringan_pipa': return 'Perbaikan Jaringan Pipa';
|
|
case 'pengecatan_pipa_besi': return 'Pengecatan Pipa Besi';
|
|
case 'penyempurnaan_jaringan_pipa': return 'Penyempurnaan Jaringan Pipa';
|
|
default: return jenisPekerjaan ?? '-';
|
|
}
|
|
}
|
|
|
|
String get totalNilaiFormatted {
|
|
if (totalNilaiPekerjaan == null) return '-';
|
|
final formatted = totalNilaiPekerjaan!.toStringAsFixed(0)
|
|
.replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (m) => '${m[1]}.');
|
|
return 'Rp $formatted';
|
|
}
|
|
|
|
// ✅ Getter untuk mengambil semua nama tim
|
|
String get namaTim {
|
|
if (timTeknisi == null || timTeknisi!.isEmpty) return teknisi?.nama ?? '-';
|
|
// Gabungkan nama unik agar tidak double
|
|
final names = timTeknisi!
|
|
.map((e) => e.teknisi?.nama ?? '')
|
|
.where((n) => n.isNotEmpty)
|
|
.toSet()
|
|
.toList();
|
|
return names.isNotEmpty ? names.join(', ') : (teknisi?.nama ?? '-');
|
|
}
|
|
}
|
|
|
|
// ===================================
|
|
// TEKNISI MODEL
|
|
// ===================================
|
|
class TeknisiModel {
|
|
final int idTeknisi;
|
|
final String nama;
|
|
final String? noTelepon;
|
|
final String? spesialisasi;
|
|
|
|
TeknisiModel({
|
|
required this.idTeknisi,
|
|
required this.nama,
|
|
this.noTelepon,
|
|
this.spesialisasi,
|
|
});
|
|
|
|
factory TeknisiModel.fromJson(Map<String, dynamic> json) {
|
|
return TeknisiModel(
|
|
idTeknisi: int.tryParse(json['id_teknisi']?.toString() ?? '') ?? 0,
|
|
nama: json['nama'] ?? '',
|
|
noTelepon: json['no_telepon'],
|
|
spesialisasi: json['spesialisasi'],
|
|
);
|
|
}
|
|
}
|
|
|
|
// ===================================
|
|
// TARIF MODEL
|
|
// ===================================
|
|
class TarifModel {
|
|
final int idTarif;
|
|
final String jenisPekerjaan;
|
|
final String namaItem;
|
|
final String kodeItem;
|
|
final String? dimensiPipa;
|
|
final double? tarifPerUnit;
|
|
final double? tarifPerMeter;
|
|
final bool? pakaiPipaBesi;
|
|
final bool adaGaransi;
|
|
final int? durasiGaransiBulan;
|
|
|
|
TarifModel({
|
|
required this.idTarif,
|
|
required this.jenisPekerjaan,
|
|
required this.namaItem,
|
|
required this.kodeItem,
|
|
this.dimensiPipa,
|
|
this.tarifPerUnit,
|
|
this.tarifPerMeter,
|
|
this.pakaiPipaBesi,
|
|
required this.adaGaransi,
|
|
this.durasiGaransiBulan,
|
|
});
|
|
|
|
factory TarifModel.fromJson(Map<String, dynamic> json) {
|
|
return TarifModel(
|
|
idTarif: int.tryParse(json['id_tarif']?.toString() ?? '') ?? 0,
|
|
jenisPekerjaan: json['jenis_pekerjaan'] ?? '',
|
|
namaItem: json['nama_item'] ?? '',
|
|
kodeItem: json['kode_item'] ?? '',
|
|
dimensiPipa: json['dimensi_pipa'],
|
|
tarifPerUnit: json['tarif_per_unit'] != null
|
|
? double.tryParse(json['tarif_per_unit'].toString())
|
|
: null,
|
|
tarifPerMeter: json['tarif_per_meter'] != null
|
|
? double.tryParse(json['tarif_per_meter'].toString())
|
|
: null,
|
|
pakaiPipaBesi: json['pakai_pipa_besi'] != null
|
|
? json['pakai_pipa_besi'] == 1 ||
|
|
json['pakai_pipa_besi'] == true ||
|
|
json['pakai_pipa_besi'].toString() == '1' ||
|
|
json['pakai_pipa_besi'].toString() == 'true'
|
|
: null,
|
|
adaGaransi: json['ada_garansi'] == 1 ||
|
|
json['ada_garansi'] == true ||
|
|
json['ada_garansi'].toString() == '1' ||
|
|
json['ada_garansi'].toString() == 'true',
|
|
durasiGaransiBulan: json['durasi_garansi_bulan'] != null
|
|
? int.tryParse(json['durasi_garansi_bulan'].toString())
|
|
: null,
|
|
);
|
|
}
|
|
}
|
|
|
|
// ===================================
|
|
// TIM TEKNISI MODEL
|
|
// ===================================
|
|
class TimTeknisiModel {
|
|
final int idTimTeknisi;
|
|
final int idPenugasan;
|
|
final int idTeknisi;
|
|
final String statusKehadiran;
|
|
final TeknisiModel? teknisi;
|
|
|
|
TimTeknisiModel({
|
|
required this.idTimTeknisi,
|
|
required this.idPenugasan,
|
|
required this.idTeknisi,
|
|
required this.statusKehadiran,
|
|
this.teknisi,
|
|
});
|
|
|
|
factory TimTeknisiModel.fromJson(Map<String, dynamic> json) {
|
|
return TimTeknisiModel(
|
|
idTimTeknisi: int.tryParse(json['id_tim_penugasan']?.toString() ?? json['id_tim_teknisi']?.toString() ?? '') ?? 0,
|
|
idPenugasan: int.tryParse(json['id_penugasan']?.toString() ?? '') ?? 0,
|
|
idTeknisi: int.tryParse(json['id_teknisi']?.toString() ?? '') ?? 0,
|
|
statusKehadiran: json['status_kehadiran'] ?? 'hadir',
|
|
teknisi: json['teknisi'] != null
|
|
? TeknisiModel.fromJson(json['teknisi'])
|
|
: null,
|
|
);
|
|
}
|
|
}
|
|
|
|
// ===================================
|
|
// STATISTIK MODEL
|
|
// ===================================
|
|
class StatistikModel {
|
|
final int totalPenugasan;
|
|
final int belumMulai;
|
|
final int dalamProses;
|
|
final int selesai;
|
|
final int menungguDetail;
|
|
final int detailLengkap;
|
|
|
|
StatistikModel({
|
|
required this.totalPenugasan,
|
|
required this.belumMulai,
|
|
required this.dalamProses,
|
|
required this.selesai,
|
|
required this.menungguDetail,
|
|
required this.detailLengkap,
|
|
});
|
|
|
|
factory StatistikModel.fromJson(Map<String, dynamic> json) {
|
|
return StatistikModel(
|
|
totalPenugasan: int.tryParse(json['total_penugasan']?.toString() ?? '') ?? 0,
|
|
belumMulai: int.tryParse(json['belum_mulai']?.toString() ?? '') ?? 0,
|
|
dalamProses: int.tryParse(json['dalam_proses']?.toString() ?? '') ?? 0,
|
|
selesai: int.tryParse(json['selesai']?.toString() ?? '') ?? 0,
|
|
menungguDetail: int.tryParse(json['menunggu_detail']?.toString() ?? '') ?? 0,
|
|
detailLengkap: int.tryParse(json['detail_lengkap']?.toString() ?? '') ?? 0,
|
|
);
|
|
}
|
|
} |