675 lines
23 KiB
Dart
675 lines
23 KiB
Dart
enum JobStatus { pending, inProgress, sent, delivered, cancelled }
|
||
|
||
enum JobStep {
|
||
olcu, // legacy fallback
|
||
olcuKontrol, // geleneksel/arjinat ölçü veya model kontrolü
|
||
dijitalTasarim, // dijital tasarım klinik onayı
|
||
modelHazirlik, // internal hazırlık/model döküm
|
||
altYapiProva, // sabit seramik/metal — alt yapı (coping)
|
||
ustYapiProva, // sabit seramik — bisküvi prova
|
||
mumProva, // hareketli protez — mum prova
|
||
dislerProva, // hareketli protez — dişler prova
|
||
dayanakProva, // implant — dayanak prova
|
||
kronProva, // implant — kron prova
|
||
fotografOnay, // foto/mockup ile klinik onayı
|
||
kaliteKontrol, // internal kalite kontrol
|
||
teslimOncesiKontrol, // internal final kontrol
|
||
cilaBitim, // son cila / bitim (her şablonda son adım)
|
||
}
|
||
|
||
enum JobLocation { atClinic, atLab }
|
||
|
||
enum JobWorkflowType { arjinat, geleneksel, dijital }
|
||
|
||
enum ProstheticFamily { sabit, implant, hareketli, gecici, ozel }
|
||
|
||
enum ProstheticType {
|
||
metalPorselen,
|
||
zirkonyum,
|
||
implantUstuZirkonyum,
|
||
gecici,
|
||
eMax,
|
||
tamProtez,
|
||
parsiyel,
|
||
diger,
|
||
}
|
||
|
||
// ── Status ────────────────────────────────────────────────────────────────────
|
||
|
||
extension JobStatusExt on JobStatus {
|
||
String get label => switch (this) {
|
||
JobStatus.pending => 'Bekliyor',
|
||
JobStatus.inProgress => 'İşlemde',
|
||
JobStatus.sent => 'Gönderildi',
|
||
JobStatus.delivered => 'Teslim Alındı',
|
||
JobStatus.cancelled => 'İptal',
|
||
};
|
||
String get value => switch (this) {
|
||
JobStatus.pending => 'pending',
|
||
JobStatus.inProgress => 'in_progress',
|
||
JobStatus.sent => 'sent',
|
||
JobStatus.delivered => 'delivered',
|
||
JobStatus.cancelled => 'cancelled',
|
||
};
|
||
}
|
||
|
||
// ── Step ──────────────────────────────────────────────────────────────────────
|
||
|
||
extension JobStepExt on JobStep {
|
||
String get label => switch (this) {
|
||
JobStep.olcu => 'Ölçü',
|
||
JobStep.olcuKontrol => 'Ölçü / Model Kontrol',
|
||
JobStep.dijitalTasarim => 'Dijital Tasarım Onayı',
|
||
JobStep.modelHazirlik => 'Model Hazırlık',
|
||
JobStep.altYapiProva => 'Alt Yapı Prova',
|
||
JobStep.ustYapiProva => 'Üst Yapı Prova',
|
||
JobStep.mumProva => 'Mum Prova',
|
||
JobStep.dislerProva => 'Dişler Prova',
|
||
JobStep.dayanakProva => 'Dayanak Prova',
|
||
JobStep.kronProva => 'Kron Prova',
|
||
JobStep.fotografOnay => 'Fotoğraf / Mockup Onayı',
|
||
JobStep.kaliteKontrol => 'Kalite Kontrol',
|
||
JobStep.teslimOncesiKontrol => 'Teslim Öncesi Kontrol',
|
||
JobStep.cilaBitim => 'Cila / Bitim',
|
||
};
|
||
|
||
/// One-liner shown under the step on the stepper
|
||
String get description => switch (this) {
|
||
JobStep.olcu => 'İlk ölçü alındı',
|
||
JobStep.olcuKontrol => 'Ölçü, model veya kapanış kaydı kontrolü',
|
||
JobStep.dijitalTasarim =>
|
||
'Dijital tasarım ekranı veya mockup klinik onayı',
|
||
JobStep.modelHazirlik =>
|
||
'Model hazırlık, döküm veya artikülatör aşaması',
|
||
JobStep.altYapiProva => 'Metal/zirkonyum coping klinik onayı',
|
||
JobStep.ustYapiProva => 'Bisküvi pişirimi sonrası klinik onayı',
|
||
JobStep.mumProva => 'Mum prova klinik onayı',
|
||
JobStep.dislerProva => 'Diş dizimi klinik onayı',
|
||
JobStep.dayanakProva => 'Dayanak klinik onayı',
|
||
JobStep.kronProva => 'Kron klinik onayı',
|
||
JobStep.fotografOnay => 'Fotoğraf veya mockup üzerinden klinik teyidi',
|
||
JobStep.kaliteKontrol => 'Laboratuvar iç kalite kontrol aşaması',
|
||
JobStep.teslimOncesiKontrol => 'Teslimat öncesi son iç kontrol',
|
||
JobStep.cilaBitim => 'Son cila ve teslim hazırlığı',
|
||
};
|
||
|
||
String get value => switch (this) {
|
||
JobStep.olcu => 'olcu',
|
||
JobStep.olcuKontrol => 'olcu_kontrol',
|
||
JobStep.dijitalTasarim => 'dijital_tasarim',
|
||
JobStep.modelHazirlik => 'model_hazirlik',
|
||
JobStep.altYapiProva => 'alt_yapi_prova',
|
||
JobStep.ustYapiProva => 'ust_yapi_prova',
|
||
JobStep.mumProva => 'mum_prova',
|
||
JobStep.dislerProva => 'disler_prova',
|
||
JobStep.dayanakProva => 'dayanak_prova',
|
||
JobStep.kronProva => 'kron_prova',
|
||
JobStep.fotografOnay => 'fotograf_onay',
|
||
JobStep.kaliteKontrol => 'kalite_kontrol',
|
||
JobStep.teslimOncesiKontrol => 'teslim_oncesi_kontrol',
|
||
JobStep.cilaBitim => 'cila_bitim',
|
||
};
|
||
|
||
bool get requiresClinicApproval => switch (this) {
|
||
JobStep.modelHazirlik ||
|
||
JobStep.kaliteKontrol ||
|
||
JobStep.teslimOncesiKontrol =>
|
||
false,
|
||
_ => true,
|
||
};
|
||
|
||
bool get isLabOptional => switch (this) {
|
||
JobStep.modelHazirlik ||
|
||
JobStep.fotografOnay ||
|
||
JobStep.kaliteKontrol ||
|
||
JobStep.teslimOncesiKontrol =>
|
||
true,
|
||
_ => false,
|
||
};
|
||
}
|
||
|
||
extension JobWorkflowTypeExt on JobWorkflowType {
|
||
String get label => switch (this) {
|
||
JobWorkflowType.arjinat => 'Arjinat',
|
||
JobWorkflowType.geleneksel => 'Geleneksel',
|
||
JobWorkflowType.dijital => 'Dijital',
|
||
};
|
||
|
||
String get value => switch (this) {
|
||
JobWorkflowType.arjinat => 'arjinat',
|
||
JobWorkflowType.geleneksel => 'geleneksel',
|
||
JobWorkflowType.dijital => 'dijital',
|
||
};
|
||
}
|
||
|
||
// ── Prosthetic type ───────────────────────────────────────────────────────────
|
||
|
||
extension ProstheticTypeExt on ProstheticType {
|
||
String get label => switch (this) {
|
||
ProstheticType.metalPorselen => 'Metal Porselen',
|
||
ProstheticType.zirkonyum => 'Zirkonyum',
|
||
ProstheticType.implantUstuZirkonyum => 'İmplant Üstü Zirkonyum',
|
||
ProstheticType.gecici => 'Geçici',
|
||
ProstheticType.eMax => 'E-Max',
|
||
ProstheticType.tamProtez => 'Tam Protez',
|
||
ProstheticType.parsiyel => 'Parsiyel Protez',
|
||
ProstheticType.diger => 'Diğer',
|
||
};
|
||
String get value => switch (this) {
|
||
ProstheticType.metalPorselen => 'metal_porselen',
|
||
ProstheticType.zirkonyum => 'zirkonyum',
|
||
ProstheticType.implantUstuZirkonyum => 'implant_ustu_zirkonyum',
|
||
ProstheticType.gecici => 'gecici',
|
||
ProstheticType.eMax => 'e_max',
|
||
ProstheticType.tamProtez => 'tam_protez',
|
||
ProstheticType.parsiyel => 'parsiyel',
|
||
ProstheticType.diger => 'diger',
|
||
};
|
||
|
||
ProstheticFamily get family => switch (this) {
|
||
ProstheticType.metalPorselen ||
|
||
ProstheticType.zirkonyum ||
|
||
ProstheticType.eMax =>
|
||
ProstheticFamily.sabit,
|
||
ProstheticType.implantUstuZirkonyum => ProstheticFamily.implant,
|
||
ProstheticType.tamProtez ||
|
||
ProstheticType.parsiyel =>
|
||
ProstheticFamily.hareketli,
|
||
ProstheticType.gecici => ProstheticFamily.gecici,
|
||
ProstheticType.diger => ProstheticFamily.ozel,
|
||
};
|
||
}
|
||
|
||
// ── Step template ─────────────────────────────────────────────────────────────
|
||
|
||
class JobWorkflowPreset {
|
||
const JobWorkflowPreset({
|
||
required this.title,
|
||
required this.summary,
|
||
required this.steps,
|
||
});
|
||
|
||
final String title;
|
||
final String summary;
|
||
final List<JobStep> steps;
|
||
}
|
||
|
||
const optionalLabStepCatalog = <JobStep>[
|
||
JobStep.modelHazirlik,
|
||
JobStep.fotografOnay,
|
||
JobStep.kaliteKontrol,
|
||
JobStep.teslimOncesiKontrol,
|
||
];
|
||
|
||
bool isOptionalStepApplicable(
|
||
JobStep step, {
|
||
required JobWorkflowType workflowType,
|
||
required ProstheticFamily family,
|
||
required bool provaRequired,
|
||
}) {
|
||
switch (step) {
|
||
case JobStep.modelHazirlik:
|
||
return workflowType != JobWorkflowType.dijital &&
|
||
family != ProstheticFamily.gecici;
|
||
case JobStep.fotografOnay:
|
||
return family != ProstheticFamily.hareketli || provaRequired;
|
||
case JobStep.kaliteKontrol:
|
||
case JobStep.teslimOncesiKontrol:
|
||
return true;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
List<JobStep> mergeOptionalLabSteps({
|
||
required List<JobStep> baseSteps,
|
||
required List<JobStep> optionalSteps,
|
||
required JobWorkflowType workflowType,
|
||
required ProstheticFamily family,
|
||
required bool provaRequired,
|
||
}) {
|
||
final merged = List<JobStep>.from(baseSteps);
|
||
for (final step in optionalSteps) {
|
||
if (!step.isLabOptional ||
|
||
merged.contains(step) ||
|
||
!isOptionalStepApplicable(
|
||
step,
|
||
workflowType: workflowType,
|
||
family: family,
|
||
provaRequired: provaRequired,
|
||
)) {
|
||
continue;
|
||
}
|
||
|
||
final finalIndex = merged.indexOf(JobStep.cilaBitim);
|
||
switch (step) {
|
||
case JobStep.modelHazirlik:
|
||
final afterControl = merged.contains(JobStep.olcuKontrol)
|
||
? merged.indexOf(JobStep.olcuKontrol) + 1
|
||
: 0;
|
||
merged.insert(afterControl, step);
|
||
case JobStep.fotografOnay:
|
||
case JobStep.kaliteKontrol:
|
||
case JobStep.teslimOncesiKontrol:
|
||
merged.insert(finalIndex.clamp(0, merged.length), step);
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
return merged;
|
||
}
|
||
|
||
JobWorkflowPreset buildJobWorkflowPreset({
|
||
required ProstheticType prostheticType,
|
||
JobWorkflowType? workflowType,
|
||
required bool provaRequired,
|
||
List<JobStep> optionalSteps = const [],
|
||
}) {
|
||
final normalizedWorkflow = workflowType ?? JobWorkflowType.geleneksel;
|
||
final family = prostheticType.family;
|
||
|
||
List<JobStep> steps;
|
||
switch (normalizedWorkflow) {
|
||
case JobWorkflowType.dijital:
|
||
steps = switch (family) {
|
||
ProstheticFamily.sabit => provaRequired
|
||
? const [
|
||
JobStep.dijitalTasarim,
|
||
JobStep.ustYapiProva,
|
||
JobStep.cilaBitim,
|
||
]
|
||
: const [
|
||
JobStep.dijitalTasarim,
|
||
JobStep.cilaBitim,
|
||
],
|
||
ProstheticFamily.implant => provaRequired
|
||
? const [
|
||
JobStep.dijitalTasarim,
|
||
JobStep.dayanakProva,
|
||
JobStep.kronProva,
|
||
JobStep.cilaBitim,
|
||
]
|
||
: const [
|
||
JobStep.dijitalTasarim,
|
||
JobStep.cilaBitim,
|
||
],
|
||
ProstheticFamily.hareketli => provaRequired
|
||
? const [
|
||
JobStep.dijitalTasarim,
|
||
JobStep.mumProva,
|
||
JobStep.dislerProva,
|
||
JobStep.cilaBitim,
|
||
]
|
||
: const [
|
||
JobStep.dijitalTasarim,
|
||
JobStep.cilaBitim,
|
||
],
|
||
ProstheticFamily.gecici => const [
|
||
JobStep.dijitalTasarim,
|
||
JobStep.cilaBitim,
|
||
],
|
||
ProstheticFamily.ozel => provaRequired
|
||
? const [
|
||
JobStep.dijitalTasarim,
|
||
JobStep.altYapiProva,
|
||
JobStep.cilaBitim,
|
||
]
|
||
: const [
|
||
JobStep.dijitalTasarim,
|
||
JobStep.cilaBitim,
|
||
],
|
||
};
|
||
case JobWorkflowType.arjinat:
|
||
case JobWorkflowType.geleneksel:
|
||
steps = switch (family) {
|
||
ProstheticFamily.sabit => provaRequired
|
||
? const [
|
||
JobStep.olcuKontrol,
|
||
JobStep.altYapiProva,
|
||
JobStep.ustYapiProva,
|
||
JobStep.cilaBitim,
|
||
]
|
||
: const [
|
||
JobStep.olcuKontrol,
|
||
JobStep.cilaBitim,
|
||
],
|
||
ProstheticFamily.implant => provaRequired
|
||
? const [
|
||
JobStep.olcuKontrol,
|
||
JobStep.dayanakProva,
|
||
JobStep.kronProva,
|
||
JobStep.cilaBitim,
|
||
]
|
||
: const [
|
||
JobStep.olcuKontrol,
|
||
JobStep.cilaBitim,
|
||
],
|
||
ProstheticFamily.hareketli => provaRequired
|
||
? const [
|
||
JobStep.olcuKontrol,
|
||
JobStep.mumProva,
|
||
JobStep.dislerProva,
|
||
JobStep.cilaBitim,
|
||
]
|
||
: const [
|
||
JobStep.olcuKontrol,
|
||
JobStep.cilaBitim,
|
||
],
|
||
ProstheticFamily.gecici => const [
|
||
JobStep.olcuKontrol,
|
||
JobStep.cilaBitim,
|
||
],
|
||
ProstheticFamily.ozel => provaRequired
|
||
? const [
|
||
JobStep.olcuKontrol,
|
||
JobStep.altYapiProva,
|
||
JobStep.cilaBitim,
|
||
]
|
||
: const [
|
||
JobStep.olcuKontrol,
|
||
JobStep.cilaBitim,
|
||
],
|
||
};
|
||
}
|
||
|
||
final title =
|
||
'${normalizedWorkflow.label} · ${provaRequired ? "Provalı" : "Provasız"}';
|
||
final summary = switch ((normalizedWorkflow, family, provaRequired)) {
|
||
(JobWorkflowType.dijital, ProstheticFamily.sabit, false) =>
|
||
'Dijital tasarım onayı sonrası üretim ve teslim odaklı akış.',
|
||
(JobWorkflowType.dijital, _, false) =>
|
||
'Fiziksel prova azaltılmış, dijital onay ve hızlı teslim akışı.',
|
||
(JobWorkflowType.dijital, _, true) =>
|
||
'Dijital hazırlık üzerine klinik prova kapıları eklenmiş hibrit akış.',
|
||
(JobWorkflowType.arjinat, _, false) =>
|
||
'Ölçü/model kontrolü sonrası doğrudan üretim ve teslim akışı.',
|
||
(JobWorkflowType.arjinat, _, true) =>
|
||
'Arjinat ölçüden gelen işlerde klinik prova kapılarıyla ilerleyen akış.',
|
||
(JobWorkflowType.geleneksel, _, false) =>
|
||
'Klasik ölçüden gelen, minimum temaslı ve hızlı teslim akışı.',
|
||
(JobWorkflowType.geleneksel, _, true) =>
|
||
'Klasik laboratuvar süreçlerine uygun, prova bazlı aşamalı akış.',
|
||
};
|
||
|
||
return JobWorkflowPreset(
|
||
title: title,
|
||
summary: summary,
|
||
steps: mergeOptionalLabSteps(
|
||
baseSteps: steps,
|
||
optionalSteps: optionalSteps,
|
||
workflowType: normalizedWorkflow,
|
||
family: family,
|
||
provaRequired: provaRequired,
|
||
),
|
||
);
|
||
}
|
||
|
||
/// Returns the ordered clinic-facing approval steps for a job.
|
||
List<JobStep> jobStepTemplate(
|
||
ProstheticType type,
|
||
bool provaRequired, {
|
||
JobWorkflowType? workflowType,
|
||
List<JobStep> optionalSteps = const [],
|
||
}) {
|
||
return buildJobWorkflowPreset(
|
||
prostheticType: type,
|
||
workflowType: workflowType,
|
||
provaRequired: provaRequired,
|
||
optionalSteps: optionalSteps,
|
||
).steps;
|
||
}
|
||
|
||
// ── Job ───────────────────────────────────────────────────────────────────────
|
||
|
||
class Job {
|
||
const Job({
|
||
required this.id,
|
||
required this.clinicTenantId,
|
||
required this.labTenantId,
|
||
required this.patientCode,
|
||
required this.prostheticType,
|
||
required this.memberCount,
|
||
required this.status,
|
||
required this.dateCreated,
|
||
this.patientId,
|
||
this.patientName,
|
||
this.prostheticId,
|
||
this.prostheticName,
|
||
this.teeth = const [],
|
||
this.color,
|
||
this.description,
|
||
this.price,
|
||
this.currency,
|
||
this.currentStep,
|
||
this.location = JobLocation.atClinic,
|
||
this.workflowType,
|
||
this.dueDate,
|
||
this.clinicName,
|
||
this.labName,
|
||
this.attachments = const [],
|
||
this.provaRequired = true,
|
||
this.workflowSteps = const [],
|
||
});
|
||
|
||
final String id;
|
||
final String clinicTenantId;
|
||
final String labTenantId;
|
||
final String? patientId;
|
||
final String? patientName;
|
||
final String patientCode;
|
||
final String? prostheticId;
|
||
final String? prostheticName;
|
||
final ProstheticType prostheticType;
|
||
final int memberCount;
|
||
final List<String> teeth;
|
||
final String? color;
|
||
final String? description;
|
||
final double? price;
|
||
final String? currency;
|
||
final JobStatus status;
|
||
final JobStep? currentStep;
|
||
final JobLocation location;
|
||
final JobWorkflowType? workflowType;
|
||
final DateTime? dueDate;
|
||
final DateTime dateCreated;
|
||
final List<String> attachments;
|
||
final bool provaRequired;
|
||
final List<JobStep> workflowSteps;
|
||
|
||
// Denormalized from relation joins — list views only
|
||
final String? clinicName;
|
||
final String? labName;
|
||
|
||
// ── copyWith ──────────────────────────────────────────────────────────────
|
||
|
||
Job copyWith({
|
||
JobStatus? status,
|
||
JobStep? currentStep,
|
||
JobLocation? location,
|
||
JobWorkflowType? workflowType,
|
||
String? clinicName,
|
||
String? labName,
|
||
bool clearCurrentStep = false,
|
||
}) =>
|
||
Job(
|
||
id: id,
|
||
clinicTenantId: clinicTenantId,
|
||
labTenantId: labTenantId,
|
||
patientId: patientId,
|
||
patientName: patientName,
|
||
patientCode: patientCode,
|
||
prostheticId: prostheticId,
|
||
prostheticName: prostheticName,
|
||
prostheticType: prostheticType,
|
||
memberCount: memberCount,
|
||
teeth: teeth,
|
||
color: color,
|
||
description: description,
|
||
price: price,
|
||
currency: currency,
|
||
status: status ?? this.status,
|
||
currentStep:
|
||
clearCurrentStep ? null : (currentStep ?? this.currentStep),
|
||
location: location ?? this.location,
|
||
workflowType: workflowType ?? this.workflowType,
|
||
dueDate: dueDate,
|
||
dateCreated: dateCreated,
|
||
attachments: attachments,
|
||
provaRequired: provaRequired,
|
||
workflowSteps: workflowSteps,
|
||
clinicName: clinicName ?? this.clinicName,
|
||
labName: labName ?? this.labName,
|
||
);
|
||
|
||
// ── Step helpers ──────────────────────────────────────────────────────────
|
||
|
||
List<JobStep> get stepTemplate => workflowSteps.isNotEmpty
|
||
? workflowSteps
|
||
: jobStepTemplate(
|
||
prostheticType,
|
||
provaRequired,
|
||
workflowType: workflowType,
|
||
optionalSteps:
|
||
workflowSteps.where((step) => step.isLabOptional).toList(),
|
||
);
|
||
|
||
JobWorkflowPreset get workflowPreset {
|
||
final preset = buildJobWorkflowPreset(
|
||
prostheticType: prostheticType,
|
||
workflowType: workflowType,
|
||
provaRequired: provaRequired,
|
||
optionalSteps: workflowSteps.where((step) => step.isLabOptional).toList(),
|
||
);
|
||
if (workflowSteps.isEmpty) return preset;
|
||
return JobWorkflowPreset(
|
||
title: preset.title,
|
||
summary: preset.summary,
|
||
steps: workflowSteps,
|
||
);
|
||
}
|
||
|
||
bool get isLastStep =>
|
||
currentStep != null && currentStep == stepTemplate.last;
|
||
|
||
/// The next step after currentStep in this job's template, or null if done.
|
||
JobStep? get nextStep {
|
||
if (currentStep == null) return stepTemplate.firstOrNull;
|
||
final idx = stepTemplate.indexOf(currentStep!);
|
||
if (idx < 0 || idx >= stepTemplate.length - 1) return null;
|
||
return stepTemplate[idx + 1];
|
||
}
|
||
|
||
factory Job.fromJson(Map<String, dynamic> j) {
|
||
final expand = j['expand'] as Map<String, dynamic>?;
|
||
final clinicExp = expand?['clinic_tenant_id'] as Map<String, dynamic>?;
|
||
final labExp = expand?['lab_tenant_id'] as Map<String, dynamic>?;
|
||
final patientExp = expand?['patient_id'] as Map<String, dynamic>?;
|
||
final prostheticExp = expand?['prosthetic_id'] as Map<String, dynamic>?;
|
||
String? str(dynamic v) {
|
||
final s = v as String?;
|
||
return (s == null || s.isEmpty) ? null : s;
|
||
}
|
||
|
||
return Job(
|
||
id: j['id'] as String,
|
||
clinicTenantId: j['clinic_tenant_id'] as String,
|
||
labTenantId: j['lab_tenant_id'] as String,
|
||
patientId: str(j['patient_id']),
|
||
patientName: _patientName(patientExp),
|
||
patientCode: j['patient_code'] as String,
|
||
prostheticId: str(j['prosthetic_id']),
|
||
prostheticName: prostheticExp?['name'] as String?,
|
||
prostheticType: _parseProstheticType(j['prosthetic_type'] as String),
|
||
memberCount: (j['member_count'] as num).toInt(),
|
||
teeth: j['teeth'] is List
|
||
? (j['teeth'] as List).map((e) => e.toString()).toList()
|
||
: [],
|
||
color: str(j['color']),
|
||
description: str(j['description']),
|
||
price: (j['price'] as num?)?.toDouble(),
|
||
currency: str(j['currency']),
|
||
status: _parseStatus(j['status'] as String),
|
||
currentStep: str(j['current_step']) != null
|
||
? _parseStep(j['current_step'] as String)
|
||
: null,
|
||
location:
|
||
j['location'] == 'at_lab' ? JobLocation.atLab : JobLocation.atClinic,
|
||
workflowType: str(j['workflow_type']) != null
|
||
? _parseWorkflowType(j['workflow_type'] as String)
|
||
: null,
|
||
dueDate: str(j['due_date']) != null
|
||
? DateTime.parse(j['due_date'] as String)
|
||
: null,
|
||
dateCreated: DateTime.parse(j['created'] as String),
|
||
clinicName: clinicExp?['company_name'] as String?,
|
||
labName: labExp?['company_name'] as String?,
|
||
attachments: j['attachments'] is List
|
||
? (j['attachments'] as List).map((e) => e.toString()).toList()
|
||
: [],
|
||
provaRequired: (j['prova_required'] as bool?) ?? true,
|
||
workflowSteps: j['workflow_steps'] is List
|
||
? (j['workflow_steps'] as List)
|
||
.map((e) => _parseStep(e.toString()))
|
||
.toList()
|
||
: const [],
|
||
);
|
||
}
|
||
|
||
static JobStatus _parseStatus(String s) => switch (s) {
|
||
'in_progress' => JobStatus.inProgress,
|
||
'sent' => JobStatus.sent,
|
||
'delivered' => JobStatus.delivered,
|
||
'cancelled' => JobStatus.cancelled,
|
||
_ => JobStatus.pending,
|
||
};
|
||
|
||
static JobStep _parseStep(String s) => switch (s) {
|
||
'olcu_kontrol' => JobStep.olcuKontrol,
|
||
'dijital_tasarim' => JobStep.dijitalTasarim,
|
||
'model_hazirlik' => JobStep.modelHazirlik,
|
||
'alt_yapi_prova' => JobStep.altYapiProva,
|
||
'ust_yapi_prova' => JobStep.ustYapiProva,
|
||
'mum_prova' => JobStep.mumProva,
|
||
'disler_prova' => JobStep.dislerProva,
|
||
'dayanak_prova' => JobStep.dayanakProva,
|
||
'kron_prova' => JobStep.kronProva,
|
||
'fotograf_onay' => JobStep.fotografOnay,
|
||
'kalite_kontrol' => JobStep.kaliteKontrol,
|
||
'teslim_oncesi_kontrol' => JobStep.teslimOncesiKontrol,
|
||
'cila_bitim' => JobStep.cilaBitim,
|
||
_ => JobStep.olcu,
|
||
};
|
||
|
||
static JobStep parseStepValue(String s) => _parseStep(s);
|
||
|
||
static JobWorkflowType _parseWorkflowType(String s) => switch (s) {
|
||
'arjinat' => JobWorkflowType.arjinat,
|
||
'dijital' => JobWorkflowType.dijital,
|
||
_ => JobWorkflowType.geleneksel,
|
||
};
|
||
|
||
static String? _patientName(Map<String, dynamic>? patientExp) {
|
||
if (patientExp == null) return null;
|
||
final first = (patientExp['first_name'] as String?)?.trim();
|
||
final last = (patientExp['last_name'] as String?)?.trim();
|
||
final parts = [first, last]
|
||
.where((part) => part != null && part.isNotEmpty)
|
||
.cast<String>()
|
||
.toList();
|
||
if (parts.isNotEmpty) return parts.join(' ');
|
||
final code = (patientExp['patient_code'] as String?)?.trim();
|
||
return (code == null || code.isEmpty) ? null : code;
|
||
}
|
||
|
||
static ProstheticType _parseProstheticType(String s) => switch (s) {
|
||
'zirkonyum' => ProstheticType.zirkonyum,
|
||
'implant_ustu_zirkonyum' => ProstheticType.implantUstuZirkonyum,
|
||
'gecici' => ProstheticType.gecici,
|
||
'e_max' => ProstheticType.eMax,
|
||
'tam_protez' => ProstheticType.tamProtez,
|
||
'parsiyel' => ProstheticType.parsiyel,
|
||
'diger' => ProstheticType.diger,
|
||
_ => ProstheticType.metalPorselen,
|
||
};
|
||
}
|