Files
pfm-ocr/lib/features/editor/editor_logic.dart
T
fhanyuh caf8e98378 chore: normalize line endings (CRLF -> LF)
No content changes: git diff --ignore-all-space over these files is empty.
The churn came from editing on Windows against a repo checked out with LF.
2026-08-27 10:40:49 +07:00

239 lines
7.7 KiB
Dart

part of 'editor_screen.dart';
mixin EditorLogic on ConsumerState<EditorScreen> {
DocumentModel? _document;
// Controllers
final _tanggalCtrl = TextEditingController();
final _noPoCtrl = TextEditingController();
final _noSoCtrl = TextEditingController();
final _noDoCtrl = TextEditingController();
final _kepadaYthCtrl = TextEditingController();
final _orderUntukCtrl = TextEditingController();
final _alamatCtrl = TextEditingController();
final _platTrukCtrl = TextEditingController();
final _namaDriverCtrl = TextEditingController();
final _namaPenerimaCtrl = TextEditingController();
final _latitudeCtrl = TextEditingController();
final _longitudeCtrl = TextEditingController();
List<Map<String, TextEditingController>> _itemControllers = [];
bool _isAgreed = false;
final _formKey = GlobalKey<FormState>();
void _loadDocumentData() {
if (widget.pendingId == null) return;
final pendingDocs = ref.read(pendingDocumentsProvider);
final pendingDoc = pendingDocs.firstWhere(
(doc) => doc.id == widget.pendingId,
orElse: () => PendingDocument(id: '', imagePath: '')
);
if (pendingDoc.document != null) {
final doc = pendingDoc.document!;
setState(() {
_document = doc;
_tanggalCtrl.text = doc.tanggal;
_noPoCtrl.text = doc.noPo;
_noSoCtrl.text = doc.noSo;
_noDoCtrl.text = doc.noDo;
_kepadaYthCtrl.text = doc.kepadaYth;
_orderUntukCtrl.text = doc.orderUntuk;
_alamatCtrl.text = doc.alamat;
_platTrukCtrl.text = doc.platTruk;
_namaDriverCtrl.text = doc.namaDriver;
final lat = pendingDoc.latitude ?? doc.latitude;
final lng = pendingDoc.longitude ?? doc.longitude;
_latitudeCtrl.text = lat != null ? lat.toString() : '-';
_longitudeCtrl.text = lng != null ? lng.toString() : '-';
for (var item in doc.items) {
_addItem(
sku: item.nomorSku,
nama: item.namaBarang,
banyak: item.banyak,
jumlah: item.jumlah,
);
}
});
}
}
void _addItem({String sku = '', String nama = '', String banyak = '', String jumlah = ''}) {
final skuCtrl = TextEditingController(text: sku);
final namaCtrl = TextEditingController(text: nama);
final banyakCtrl = TextEditingController(text: banyak);
final jumlahCtrl = TextEditingController(text: jumlah);
setState(() {
_itemControllers.add({
'sku': skuCtrl,
'nama': namaCtrl,
'banyak': banyakCtrl,
'jumlah': jumlahCtrl,
});
});
}
void _removeItem(int index) {
setState(() {
final ctrls = _itemControllers[index];
ctrls['sku']?.dispose();
ctrls['nama']?.dispose();
ctrls['banyak']?.dispose();
ctrls['jumlah']?.dispose();
_itemControllers.removeAt(index);
});
}
void _showConfirmationDialog() {
if (!_formKey.currentState!.validate()) return;
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (context) {
return ConfirmationDialog(
nameController: _namaPenerimaCtrl,
initialAgreed: _isAgreed,
onConfirm: (agreed, name) {
setState(() {
_isAgreed = agreed;
});
Navigator.pop(context);
_submitDocument();
},
);
}
);
}
Future<void> _submitDocument() async {
final pendingDocs = ref.read(pendingDocumentsProvider);
final pendingDoc = pendingDocs.firstWhere(
(doc) => doc.id == widget.pendingId,
orElse: () => PendingDocument(id: '', imagePath: '')
);
final double? finalLat = pendingDoc.latitude ?? _document?.latitude;
final double? finalLng = pendingDoc.longitude ?? _document?.longitude;
final saveAction = resolveDocumentSaveAction(
existingDocument: _document,
localImagePath: pendingDoc.imagePath,
);
if (saveAction.kind == DocumentSaveActionKind.blocked) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(saveAction.blockedMessage!),
backgroundColor: AppConfig.errorColor,
duration: const Duration(seconds: 5),
),
);
return;
}
final apiClient = ref.read(apiClientProvider);
String serverId;
if (saveAction.kind == DocumentSaveActionKind.reuploadThenPut) {
try {
final formData = FormData.fromMap({
'image': await MultipartFile.fromFile(
pendingDoc.imagePath,
filename: pendingDoc.imagePath.split(RegExp(r'[/\\]')).last,
),
if (finalLat != null) 'latitude': finalLat.toString(),
if (finalLng != null) 'longitude': finalLng.toString(),
'scan_mode': pendingDoc.scanMode ?? 'DO',
});
final response = await apiClient.client.post(AppConfig.uploadDocumentEndpoint, data: formData);
serverId = DocumentModel.fromJson(response.data['data']).id;
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Gagal membuat dokumen di server: ${ApiException.from(e).displayMessage}'),
backgroundColor: AppConfig.errorColor,
duration: const Duration(seconds: 5),
),
);
return;
}
} else {
serverId = saveAction.serverId!;
}
final finalDoc = DocumentModel(
id: serverId,
tanggal: _tanggalCtrl.text,
noPo: _noPoCtrl.text,
noSo: _noSoCtrl.text,
noDo: _noDoCtrl.text,
kepadaYth: _kepadaYthCtrl.text,
orderUntuk: _orderUntukCtrl.text,
alamat: _alamatCtrl.text,
platTruk: _platTrukCtrl.text,
namaDriver: _namaDriverCtrl.text,
namaPenerima: _namaPenerimaCtrl.text,
latitude: finalLat,
longitude: finalLng,
items: _itemControllers.map((ctrls) => DocumentItem(
nomorSku: ctrls['sku']!.text,
namaBarang: ctrls['nama']!.text,
banyak: ctrls['banyak']!.text,
jumlah: ctrls['jumlah']!.text,
)).toList(),
createdAt: DateTime.now(),
);
final localStorage = ref.read(localStorageProvider);
await localStorage.saveDocument(finalDoc);
bool syncedToServer = false;
String? syncError;
try {
await apiClient.client.put(
'${AppConfig.fetchDocumentsEndpoint}/${finalDoc.id}',
data: finalDoc.toPutPayload(),
);
syncedToServer = true;
} catch (e) {
syncError = ApiException.from(e).displayMessage;
}
if (widget.pendingId != null) {
if (syncedToServer) {
ref.read(pendingDocumentsProvider.notifier).removeDocument(widget.pendingId!);
} else {
ref.read(pendingDocumentsProvider.notifier).markSyncFailed(
widget.pendingId!,
finalDoc,
'Belum tersinkron ke server: $syncError',
);
}
}
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
syncedToServer
? const SnackBar(content: Text('Dokumen Berhasil Disimpan!'), backgroundColor: AppConfig.successColor)
: const SnackBar(
content: Text('Tersimpan lokal, tapi GAGAL sinkron ke server. Cek koneksi lalu retry dari daftar dokumen.'),
backgroundColor: Colors.deepOrange,
duration: Duration(seconds: 5),
),
);
context.pop(true);
}
}
}