import 'dart:async'; import 'package:pocketbase/pocketbase.dart'; import '../../../core/api/pocketbase_client.dart'; import '../../../core/services/finance_service.dart'; import '../../../core/services/job_history_service.dart'; import '../../../models/job.dart'; const _listExpand = 'clinic_tenant_id,lab_tenant_id,patient_id'; const _detailExpand = 'clinic_tenant_id,lab_tenant_id,patient_id,prosthetic_id'; class ClinicJobsRepository { ClinicJobsRepository._(); static final instance = ClinicJobsRepository._(); PocketBase get _pb => PocketBaseClient.instance.pb; Future> listOutbound( String clinicTenantId, { List? statuses, String? location, String? filterExtra, int page = 1, int limit = 30, }) async { final filterParts = ['clinic_tenant_id = "$clinicTenantId"']; if (statuses != null && statuses.isNotEmpty) { final statusFilter = statuses.map((s) => 'status = "$s"').join(' || '); filterParts.add('($statusFilter)'); } if (location != null) { filterParts.add('location = "$location"'); } if (filterExtra != null) { filterParts.add('($filterExtra)'); } final result = await _pb.collection('jobs').getList( page: page, perPage: limit, filter: filterParts.join(' && '), expand: _listExpand, ); return (result.items.map((r) => Job.fromJson(r.toJson())).toList() ..sort((a, b) => b.dateCreated.compareTo(a.dateCreated))); } Future getJob(String jobId) async { final record = await _pb.collection('jobs').getOne(jobId, expand: _detailExpand); return Job.fromJson(record.toJson()); } Future createJob({ required String clinicTenantId, required String labTenantId, required String clinicName, required String labName, required String patientCode, String? prostheticId, required ProstheticType prostheticType, required List teeth, String? patientId, String? color, String? description, String? dueDate, double? price, String? currency, JobWorkflowType? workflowType, bool provaRequired = true, List workflowSteps = const [], }) async { final record = await _pb.collection('jobs').create(body: { 'clinic_tenant_id': clinicTenantId, 'lab_tenant_id': labTenantId, 'patient_code': patientCode, if (patientId != null) 'patient_id': patientId, if (prostheticId != null && prostheticId.isNotEmpty) 'prosthetic_id': prostheticId, 'prosthetic_type': prostheticType.value, 'member_count': teeth.length, 'teeth': teeth, if (color != null) 'color': color, if (description != null) 'description': description, if (dueDate != null) 'due_date': dueDate, if (price != null) 'price': price, if (currency != null && currency.isNotEmpty) 'currency': currency, if (workflowType != null) 'workflow_type': workflowType.value, if (workflowSteps.isNotEmpty) 'workflow_steps': workflowSteps, 'status': 'pending', 'location': 'at_clinic', 'prova_required': provaRequired, }); final job = Job.fromJson(record.toJson()); if (price != null && price > 0) { try { await FinanceService.instance.ensureEntriesForJob( jobId: job.id, clinicTenantId: clinicTenantId, labTenantId: labTenantId, clinicName: clinicName, labName: labName, amount: price, currency: currency ?? 'TRY', ); } catch (_) { await _pb.collection('jobs').delete(job.id); rethrow; } } return job; } Future approveAtClinic(String jobId, Job job, {String? note}) async { final nextStep = job.nextStep; if (nextStep == null) throw Exception('Bu aşamadan ileri gidilemez.'); final record = await _pb.collection('jobs').update(jobId, body: { 'current_step': nextStep.value, 'location': 'at_lab', }); final updated = Job.fromJson(record.toJson()); unawaited(JobHistoryService.instance.append( jobId: jobId, clinicTenantId: job.clinicTenantId, labTenantId: job.labTenantId, action: JobHistoryAction.approved, step: job.currentStep, note: note, )); return updated; } Future requestRevision(String jobId, Job job, {required String note}) async { final record = await _pb.collection('jobs').update(jobId, body: { 'location': 'at_lab', }); final updated = Job.fromJson(record.toJson()); unawaited(JobHistoryService.instance.append( jobId: jobId, clinicTenantId: job.clinicTenantId, labTenantId: job.labTenantId, action: JobHistoryAction.revisionRequested, step: job.currentStep, note: note, )); return updated; } Future markDelivered(String jobId, Job job, {String? note}) async { final record = await _pb.collection('jobs').update(jobId, body: { 'status': 'delivered', }); unawaited(JobHistoryService.instance.append( jobId: jobId, clinicTenantId: job.clinicTenantId, labTenantId: job.labTenantId, action: JobHistoryAction.delivered, note: note, )); return Job.fromJson(record.toJson()); } Future cancelJob(String jobId, Job job) async { final record = await _pb.collection('jobs').update(jobId, body: { 'status': 'cancelled', }); await FinanceService.instance.deletePendingEntriesForJob(jobId); unawaited(JobHistoryService.instance.append( jobId: jobId, clinicTenantId: job.clinicTenantId, labTenantId: job.labTenantId, action: JobHistoryAction.cancelled, )); return Job.fromJson(record.toJson()); } Future>> listApprovedLabs( String clinicTenantId) async { final result = await _pb.collection('connections').getList( filter: 'clinic_tenant_id = "$clinicTenantId" && status = "approved"', expand: 'lab_tenant_id', perPage: 100, ); return result.items.map((r) { final expand = r.toJson()['expand'] as Map?; return expand?['lab_tenant_id'] as Map? ?? {'id': r.data['lab_tenant_id']}; }).toList(); } Future> listJobsByPatient(String patientId, {int limit = 50}) async { final result = await _pb.collection('jobs').getList( filter: 'patient_id = "$patientId"', perPage: limit, expand: _listExpand, ); return (result.items.map((r) => Job.fromJson(r.toJson())).toList() ..sort((a, b) => b.dateCreated.compareTo(a.dateCreated))); } Future countDelivered(String clinicTenantId, {DateTime? from, DateTime? to}) async { final parts = [ 'clinic_tenant_id = "$clinicTenantId"', 'status = "delivered"' ]; if (from != null) parts.add('updated >= "${_date(from)}"'); if (to != null) parts.add('updated < "${_date(to)}"'); final r = await _pb .collection('jobs') .getList(perPage: 1, filter: parts.join(' && ')); return r.totalItems; } static String _date(DateTime d) => d.toIso8601String().split('T').first; }