Files
lab-app/lib/features/clinic/jobs/clinic_jobs_repository.dart
T
2026-10-10 14:52:05 +03:00

225 lines
7.4 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<List<Job>> listOutbound(
String clinicTenantId, {
List<String>? 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,
sort: '-created',
);
return (result.items.map((r) => Job.fromJson(r.toJson())).toList()
..sort((a, b) => b.dateCreated.compareTo(a.dateCreated)));
}
Future<Job> getJob(String jobId) async {
final record =
await _pb.collection('jobs').getOne(jobId, expand: _detailExpand);
return Job.fromJson(record.toJson());
}
Future<Job> createJob({
required String clinicTenantId,
required String labTenantId,
required String clinicName,
required String labName,
required String patientCode,
String? prostheticId,
required ProstheticType prostheticType,
required List<String> teeth,
String? patientId,
String? color,
String? description,
String? dueDate,
double? price,
String? currency,
JobWorkflowType? workflowType,
bool provaRequired = true,
List<String> 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<Job> approveAtClinic(String jobId, Job job, {String? note}) async {
if (job.status != JobStatus.inProgress ||
job.location != JobLocation.atClinic) {
throw StateError('Yalnızca klinikteki üretim işleri onaylanabilir.');
}
final nextStep = job.provaRequired ? job.currentStep : 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<Job> 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());
await JobHistoryService.instance.append(
jobId: jobId,
clinicTenantId: job.clinicTenantId,
labTenantId: job.labTenantId,
action: JobHistoryAction.revisionRequested,
step: job.currentStep,
note: note,
);
return updated;
}
Future<Job> 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<Job> 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<List<Map<String, dynamic>>> 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<String, dynamic>?;
return expand?['lab_tenant_id'] as Map<String, dynamic>? ??
{'id': r.data['lab_tenant_id']};
}).toList();
}
Future<List<Job>> listJobsByPatient(String patientId,
{int limit = 50}) async {
final result = await _pb.collection('jobs').getList(
filter: 'patient_id = "$patientId"',
perPage: limit,
expand: _listExpand,
sort: '-created',
);
return (result.items.map((r) => Job.fromJson(r.toJson())).toList()
..sort((a, b) => b.dateCreated.compareTo(a.dateCreated)));
}
Future<int> 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;
}