227 lines
8.6 KiB
Dart
227 lines
8.6 KiB
Dart
import 'package:pocketbase/pocketbase.dart';
|
|
import '../api/pocketbase_client.dart';
|
|
import '../../models/tenant.dart';
|
|
|
|
class AiContextBuilder {
|
|
AiContextBuilder._();
|
|
static final instance = AiContextBuilder._();
|
|
|
|
PocketBase get _pb => PocketBaseClient.instance.pb;
|
|
|
|
Future<String> build(TenantMembership membership) async {
|
|
final tenant = membership.tenant;
|
|
final tenantId = tenant.id;
|
|
final isLab = tenant.kind == TenantKind.lab;
|
|
|
|
final now = DateTime.now();
|
|
final dateStr = '${now.day}.${now.month}.${now.year}';
|
|
|
|
final results = await Future.wait([
|
|
_fetchActiveJobs(tenantId, isLab),
|
|
_fetchRecentDelivered(tenantId, isLab),
|
|
_fetchFinance(tenantId, isLab),
|
|
_fetchTeam(tenantId),
|
|
]);
|
|
|
|
final actions = _actionsPrompt(isLab);
|
|
|
|
return 'Sen DLS (Dental Lab System) uygulamasinin akilli asistanisin.\n'
|
|
'${tenant.companyName} adli ${isLab ? 'dental laboratuvarinin' : 'dis kliniginin'} verilerine erisebilirsin.\n'
|
|
'Kullanici rolu: ${isLab ? 'LABORATUVAR' : 'KLINIK'}\n'
|
|
'\n'
|
|
'Tarih: $dateStr\n'
|
|
'\n'
|
|
'${results[0]}\n'
|
|
'\n'
|
|
'${results[1]}\n'
|
|
'\n'
|
|
'${results[2]}\n'
|
|
'\n'
|
|
'${results[3]}\n'
|
|
'\n'
|
|
'$actions\n'
|
|
'\n'
|
|
'Yanit kurallari:\n'
|
|
'- Turkce, kisa ve net yaz\n'
|
|
'- Sadece yukaridaki verilerden hareketle yorum yap\n'
|
|
'- Listelerde madde isareti (- ) kullan\n'
|
|
'- Onemli bilgileri **kalin** yaz\n'
|
|
'- Aksiyon etiketlerini HERZAMAN metnin sonuna koy\n'
|
|
'- ${isLab ? 'Is kodlari icin [ID:...] formatini kullan' : 'Hasta kodlari ve is durumlarini net belirt'}\n';
|
|
}
|
|
|
|
static String _actionsPrompt(bool isLab) {
|
|
final buf = StringBuffer();
|
|
buf.writeln('## EYLEM YETKILERIN');
|
|
buf.writeln('Kullanici bir islem yapmak istediginde asagidaki XML etiketlerini yanita ekle:');
|
|
buf.writeln('');
|
|
buf.writeln('Is dosyalarini gostermek:');
|
|
buf.writeln('<dls-action type="job_files" job_id="JOB_ID" label="AB001 dosyalarini goster"/>');
|
|
buf.writeln('');
|
|
buf.writeln('Is iptal etmek:');
|
|
buf.writeln('<dls-action type="cancel_job" job_id="JOB_ID" label="AB001 isini iptal et"/>');
|
|
if (!isLab) {
|
|
buf.writeln('');
|
|
buf.writeln('Teslim edildi isaretlemek (sadece klinik):');
|
|
buf.writeln('<dls-action type="mark_delivered" job_id="JOB_ID" label="AB001 teslim edildi"/>');
|
|
}
|
|
buf.writeln('');
|
|
buf.writeln('Ekip uyesi eklemek (TUM bilgiler alindiktan sonra):');
|
|
buf.writeln('<dls-action type="add_member" email="..." first_name="..." last_name="..." role="technician|admin|doctor|delivery|finance|member" password="..." label="Ad Soyad ekle"/>');
|
|
buf.writeln('');
|
|
buf.writeln('KURALLAR:');
|
|
buf.writeln('- Etiketi SADECE kullanici acikca islem istediginde ekle');
|
|
buf.writeln('- Sifre sorulursa kullanicidan al, ASLA uydurma');
|
|
buf.writeln('- iptal gibi geri alinmaz islemleri acikca belirt');
|
|
buf.writeln('- Etiket icindeki job_id degerini yukaridaki is listesinden al');
|
|
buf.writeln('- <dls-action> etiketlerini KESINLİKLE kod blogu (```xml veya ```) icine ALMA, duz metin olarak yaz');
|
|
return buf.toString();
|
|
}
|
|
|
|
Future<String> _fetchActiveJobs(String tenantId, bool isLab) async {
|
|
try {
|
|
final tenantField = isLab ? 'lab_tenant_id' : 'clinic_tenant_id';
|
|
final counterpartField = isLab ? 'clinic_tenant_id' : 'lab_tenant_id';
|
|
final result = await _pb.collection('jobs').getList(
|
|
filter: '$tenantField = "$tenantId" && status != "delivered" && status != "cancelled"',
|
|
perPage: 60,
|
|
sort: '-created',
|
|
expand: counterpartField,
|
|
);
|
|
|
|
if (result.items.isEmpty) return '## Aktif Isler\nSu an aktif is yok.';
|
|
|
|
final counterpartLabel = isLab ? 'Klinik' : 'Lab';
|
|
final lines = result.items.map((r) {
|
|
final j = r.toJson();
|
|
final jobId = j['id'] as String? ?? '';
|
|
final expand = j['expand'] as Map<String, dynamic>?;
|
|
final counterpart =
|
|
(expand?[counterpartField] as Map?)?['company_name'] as String? ?? '-';
|
|
final status = _statusTr(j['status'] as String? ?? '');
|
|
final prosthetic = j['prosthetic_type'] as String? ?? '-';
|
|
final patient = j['patient_code'] as String? ?? '-';
|
|
final step = j['current_step'] as String?;
|
|
final stepPart = (step != null && step.isNotEmpty) ? ' | Adim: $step' : '';
|
|
final due = j['due_date'] as String? ?? '';
|
|
final duePart = due.isNotEmpty ? ' | Termin: ${due.substring(0, 10)}' : '';
|
|
return '- [ID:$jobId] Hasta: $patient | $prosthetic | $status$stepPart | $counterpartLabel: $counterpart$duePart';
|
|
}).join('\n');
|
|
|
|
return '## Aktif Isler (${result.items.length})\n$lines';
|
|
} catch (e) {
|
|
return '## Aktif Isler\n(Veri alinamadi: $e)';
|
|
}
|
|
}
|
|
|
|
Future<String> _fetchRecentDelivered(String tenantId, bool isLab) async {
|
|
try {
|
|
final tenantField = isLab ? 'lab_tenant_id' : 'clinic_tenant_id';
|
|
final counterpartField = isLab ? 'clinic_tenant_id' : 'lab_tenant_id';
|
|
final result = await _pb.collection('jobs').getList(
|
|
filter: '$tenantField = "$tenantId" && status = "delivered"',
|
|
perPage: 10,
|
|
sort: '-updated',
|
|
expand: counterpartField,
|
|
);
|
|
|
|
if (result.items.isEmpty) return '## Son Teslim Edilenler\nHenuz teslim edilen is yok.';
|
|
|
|
final counterpartLabel = isLab ? 'Klinik' : 'Lab';
|
|
final lines = result.items.map((r) {
|
|
final j = r.toJson();
|
|
final jobId = j['id'] as String? ?? '';
|
|
final expand = j['expand'] as Map<String, dynamic>?;
|
|
final counterpart =
|
|
(expand?[counterpartField] as Map?)?['company_name'] as String? ?? '-';
|
|
final prosthetic = j['prosthetic_type'] as String? ?? '-';
|
|
final patient = j['patient_code'] as String? ?? '-';
|
|
final updated = (j['updated'] as String? ?? '');
|
|
final datePart = updated.length >= 10 ? updated.substring(0, 10) : '';
|
|
return '- [ID:$jobId] Hasta: $patient | $prosthetic | $counterpartLabel: $counterpart${datePart.isNotEmpty ? ' | Tarih: $datePart' : ''}';
|
|
}).join('\n');
|
|
|
|
return '## Son Teslim Edilenler (son 10)\n$lines';
|
|
} catch (_) {
|
|
return '## Son Teslim Edilenler\n(Veri alinamadi)';
|
|
}
|
|
}
|
|
|
|
Future<String> _fetchFinance(String tenantId, bool isLab) async {
|
|
try {
|
|
final type = isLab ? 'receivable' : 'payable';
|
|
final result = await _pb.collection('finance_entries').getList(
|
|
filter: 'tenant_id = "$tenantId" && type = "$type"',
|
|
perPage: 200,
|
|
);
|
|
|
|
double pending = 0, paid = 0;
|
|
for (final r in result.items) {
|
|
final j = r.toJson();
|
|
final amount = (j['amount'] as num?)?.toDouble() ?? 0;
|
|
if (j['status'] == 'pending') {
|
|
pending += amount;
|
|
} else {
|
|
paid += amount;
|
|
}
|
|
}
|
|
|
|
final label = isLab ? 'alacak' : 'borc';
|
|
return '## Finans\n'
|
|
'- Bekleyen $label: ${pending.toStringAsFixed(0)} TL\n'
|
|
'- Tahsil edilen: ${paid.toStringAsFixed(0)} TL';
|
|
} catch (_) {
|
|
return '## Finans\n(Veri alinamadi)';
|
|
}
|
|
}
|
|
|
|
Future<String> _fetchTeam(String tenantId) async {
|
|
try {
|
|
final result = await _pb.collection('tenant_members').getList(
|
|
filter: 'tenant_id = "$tenantId"',
|
|
expand: 'user_id',
|
|
perPage: 50,
|
|
);
|
|
|
|
if (result.items.isEmpty) return '## Ekip\nUye yok.';
|
|
|
|
final lines = result.items.map((r) {
|
|
final j = r.toJson();
|
|
final expand = j['expand'] as Map<String, dynamic>?;
|
|
final user = expand?['user_id'] as Map<String, dynamic>?;
|
|
final first = (user?['first_name'] as String?) ?? '';
|
|
final last = (user?['last_name'] as String?) ?? '';
|
|
final email = (user?['email'] as String?) ?? '';
|
|
final name =
|
|
'$first $last'.trim().isNotEmpty ? '$first $last'.trim() : email;
|
|
final role = _roleTr(j['role'] as String? ?? '');
|
|
return '- $name ($role)';
|
|
}).join('\n');
|
|
|
|
return '## Ekip (${result.items.length} uye)\n$lines';
|
|
} catch (_) {
|
|
return '## Ekip\n(Veri alinamadi)';
|
|
}
|
|
}
|
|
|
|
static String _statusTr(String s) => switch (s) {
|
|
'pending' => 'Bekliyor',
|
|
'in_progress' => 'Devam ediyor',
|
|
'sent' => 'Gonderildi',
|
|
'revision' => 'Revizyon',
|
|
'delivered' => 'Teslim edildi',
|
|
'cancelled' => 'Iptal',
|
|
_ => s,
|
|
};
|
|
|
|
static String _roleTr(String s) => switch (s) {
|
|
'owner' => 'Sahibi',
|
|
'admin' => 'Yonetici',
|
|
'technician' => 'Teknisyen',
|
|
'delivery' => 'Teslimat',
|
|
'finance' => 'Finans',
|
|
'doctor' => 'Hekim',
|
|
_ => 'Uye',
|
|
};
|
|
}
|