Initial commit: DLS - Dental Lab System

- Flutter + PocketBase dental lab management system
- Clinic & lab dashboards, job tracking, patient management
- Product catalog, finance tracking, multi-language support
- AI assistant integration, realtime notifications
- Windows installer (Inno Setup) included
- Developed by kovakyazilim.com
This commit is contained in:
Emre Emir
2026-06-11 15:57:31 +03:00
commit 8bbc9dbff2
226 changed files with 31308 additions and 0 deletions
@@ -0,0 +1,131 @@
import 'dart:async';
import 'package:pocketbase/pocketbase.dart';
import '../../../core/api/pocketbase_client.dart';
import '../../../core/services/job_history_service.dart';
import '../../../models/job.dart';
const _listExpand = 'clinic_tenant_id,lab_tenant_id';
const _detailExpand = 'clinic_tenant_id,lab_tenant_id,patient_id';
class LabJobsRepository {
LabJobsRepository._();
static final instance = LabJobsRepository._();
PocketBase get _pb => PocketBaseClient.instance.pb;
Future<List<Job>> listInbound(
String labTenantId, {
String? status,
int page = 1,
int limit = 30,
}) async {
final filterParts = ['lab_tenant_id = "$labTenantId"'];
if (status != null) filterParts.add('status = "$status"');
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<List<Job>> listInProgress(String labTenantId, {int limit = 50, String? location}) async {
final filterParts = ['lab_tenant_id = "$labTenantId"', 'status = "in_progress"'];
if (location != null) filterParts.add('location = "$location"');
final result = await _pb.collection('jobs').getList(
perPage: limit,
filter: filterParts.join(' && '),
expand: _listExpand,
);
return (result.items.map((r) => Job.fromJson(r.toJson())).toList()
..sort((a, b) {
if (a.dueDate == null && b.dueDate == null) return b.dateCreated.compareTo(a.dateCreated);
if (a.dueDate == null) return 1;
if (b.dueDate == null) return -1;
final cmp = a.dueDate!.compareTo(b.dueDate!);
return cmp != 0 ? cmp : 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> acceptJob(Job pendingJob) async {
final firstStep = pendingJob.stepTemplate.first;
final record = await _pb.collection('jobs').update(pendingJob.id, body: {
'status': 'in_progress',
'current_step': firstStep.value,
'location': 'at_lab',
});
final job = Job.fromJson(record.toJson());
unawaited(JobHistoryService.instance.append(
jobId: pendingJob.id,
clinicTenantId: job.clinicTenantId,
labTenantId: job.labTenantId,
action: JobHistoryAction.accepted,
step: firstStep,
));
return job;
}
Future<Job> handToClinic(String jobId, Job job, {String? note}) async {
final isFinal = job.currentStep == JobStep.cilaBitim;
final patch = isFinal
? {'status': 'sent', 'location': 'at_clinic'}
: {'location': 'at_clinic'};
final record = await _pb.collection('jobs').update(jobId, body: patch);
final updated = Job.fromJson(record.toJson());
unawaited(JobHistoryService.instance.append(
jobId: jobId,
clinicTenantId: job.clinicTenantId,
labTenantId: job.labTenantId,
action: JobHistoryAction.handedToClinic,
step: job.currentStep,
note: note,
));
return updated;
}
Future<Job> cancelJob(String jobId, Job job) async {
final record = await _pb.collection('jobs').update(jobId, body: {
'status': 'cancelled',
});
unawaited(JobHistoryService.instance.append(
jobId: jobId,
clinicTenantId: job.clinicTenantId,
labTenantId: job.labTenantId,
action: JobHistoryAction.cancelled,
step: job.currentStep,
));
return Job.fromJson(record.toJson());
}
Future<void> bulkAcceptPending(String labTenantId) async {
final pending = await listInbound(labTenantId, status: 'pending', limit: 200);
await Future.wait(pending.map((j) => acceptJob(j)));
}
Future<int> countByStatus(String labTenantId, String? status) async {
final filter = status != null
? 'lab_tenant_id = "$labTenantId" && status = "$status"'
: 'lab_tenant_id = "$labTenantId"';
final r = await _pb.collection('jobs').getList(perPage: 1, filter: filter);
return r.totalItems;
}
Future<int> countDelivered(String labTenantId, {DateTime? from, DateTime? to}) async {
final parts = ['lab_tenant_id = "$labTenantId"', '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;
}