feat: improve patient flow and pricing workflow

This commit is contained in:
egecankomur
2026-06-12 00:04:53 +03:00
parent e12587398b
commit b42f68214e
26 changed files with 1283 additions and 243 deletions
+23
View File
@@ -21,6 +21,7 @@ class FinanceEntry {
required this.amount,
required this.currency,
required this.status,
this.counterpartyTenantId,
this.paidAt,
this.counterpartyName,
this.patientCode,
@@ -34,6 +35,7 @@ class FinanceEntry {
final double amount;
final String currency;
final FinanceStatus status;
final String? counterpartyTenantId;
final String? paidAt;
final String? counterpartyName;
final String? patientCode;
@@ -53,6 +55,7 @@ class FinanceEntry {
currency: j['currency'] as String? ?? 'TRY',
status: FinanceStatus.values.firstWhere((e) => e.value == j['status'],
orElse: () => FinanceStatus.pending),
counterpartyTenantId: _str(j['counterparty_tenant_id']),
paidAt: _str(j['paid_at']),
counterpartyName: _str(j['counterparty_name']),
patientCode: jobExp?['patient_code'] as String?,
@@ -60,3 +63,23 @@ class FinanceEntry {
);
}
}
class CounterpartyFinanceSummary {
const CounterpartyFinanceSummary({
required this.counterpartyName,
required this.currency,
required this.pendingAmount,
required this.paidAmount,
required this.entryCount,
this.counterpartyTenantId,
});
final String counterpartyName;
final String currency;
final double pendingAmount;
final double paidAmount;
final int entryCount;
final String? counterpartyTenantId;
double get totalAmount => pendingAmount + paidAmount;
}
+52
View File
@@ -13,6 +13,8 @@ enum JobStep {
enum JobLocation { atClinic, atLab }
enum JobWorkflowType { arjinat, geleneksel, dijital }
enum ProstheticType {
metalPorselen,
zirkonyum,
@@ -81,6 +83,20 @@ extension JobStepExt on JobStep {
};
}
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 {
@@ -146,7 +162,9 @@ class Job {
required this.status,
required this.dateCreated,
this.patientId,
this.patientName,
this.prostheticId,
this.prostheticName,
this.teeth = const [],
this.color,
this.description,
@@ -154,6 +172,7 @@ class Job {
this.currency,
this.currentStep,
this.location = JobLocation.atClinic,
this.workflowType,
this.dueDate,
this.clinicName,
this.labName,
@@ -165,8 +184,10 @@ class Job {
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;
@@ -177,6 +198,7 @@ class Job {
final JobStatus status;
final JobStep? currentStep;
final JobLocation location;
final JobWorkflowType? workflowType;
final DateTime? dueDate;
final DateTime dateCreated;
final List<String> attachments;
@@ -192,6 +214,7 @@ class Job {
JobStatus? status,
JobStep? currentStep,
JobLocation? location,
JobWorkflowType? workflowType,
String? clinicName,
String? labName,
bool clearCurrentStep = false,
@@ -201,8 +224,10 @@ class Job {
clinicTenantId: clinicTenantId,
labTenantId: labTenantId,
patientId: patientId,
patientName: patientName,
patientCode: patientCode,
prostheticId: prostheticId,
prostheticName: prostheticName,
prostheticType: prostheticType,
memberCount: memberCount,
teeth: teeth,
@@ -213,6 +238,7 @@ class Job {
status: status ?? this.status,
currentStep: clearCurrentStep ? null : (currentStep ?? this.currentStep),
location: location ?? this.location,
workflowType: workflowType ?? this.workflowType,
dueDate: dueDate,
dateCreated: dateCreated,
attachments: attachments,
@@ -240,6 +266,8 @@ class Job {
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;
@@ -250,8 +278,10 @@ class Job {
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
@@ -267,6 +297,9 @@ class Job {
: 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,
@@ -299,6 +332,25 @@ class Job {
_ => JobStep.olcu,
};
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,