Add AI batch reconciliation for accessibility bills
This commit is contained in:
@@ -185,6 +185,27 @@ class PeriodStats {
|
||||
analysis = json['analysis'] as String?;
|
||||
}
|
||||
|
||||
class RecognitionBatchDraft {
|
||||
final String candidateId, clientRequestId, type, source;
|
||||
final int categoryId;
|
||||
final double amount;
|
||||
final DateTime occurredAt;
|
||||
final String? note, paymentMethod, sourceText;
|
||||
|
||||
const RecognitionBatchDraft({
|
||||
required this.candidateId,
|
||||
required this.clientRequestId,
|
||||
required this.categoryId,
|
||||
required this.type,
|
||||
required this.amount,
|
||||
required this.occurredAt,
|
||||
required this.source,
|
||||
this.note,
|
||||
this.paymentMethod,
|
||||
this.sourceText,
|
||||
});
|
||||
}
|
||||
|
||||
class TxApi {
|
||||
static final _dio = ApiClient.instance.dio;
|
||||
|
||||
@@ -361,6 +382,86 @@ class TxApi {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, TxItem>> createRecognitionBatch(
|
||||
String batchId,
|
||||
List<RecognitionBatchDraft> drafts,
|
||||
) async {
|
||||
if (drafts.isEmpty) return const {};
|
||||
final localPayloads = drafts
|
||||
.map(
|
||||
(draft) => <String, dynamic>{
|
||||
'ledgerId': _ledgerId,
|
||||
'categoryId': draft.categoryId,
|
||||
'type': draft.type,
|
||||
'amount': draft.amount,
|
||||
'note': draft.note,
|
||||
'paymentMethod': draft.paymentMethod,
|
||||
'source': draft.source,
|
||||
'sourceText': draft.sourceText,
|
||||
'occurredAt': ShanghaiTime.civilToUtc(
|
||||
draft.occurredAt,
|
||||
).toIso8601String(),
|
||||
'clientRequestId': draft.clientRequestId,
|
||||
},
|
||||
)
|
||||
.toList(growable: false);
|
||||
|
||||
Map<String, TxItem> createLocal() {
|
||||
final values = LocalDatabase.instance.createTransactionsBatch(
|
||||
localPayloads,
|
||||
enqueueSyncChanges: _queueOfflineChanges,
|
||||
);
|
||||
return {
|
||||
for (var index = 0; index < drafts.length; index++)
|
||||
drafts[index].candidateId: TxItem.fromJson(values[index]),
|
||||
};
|
||||
}
|
||||
|
||||
final session = SessionStore.instance;
|
||||
if (session.shouldUseLocalOnly ||
|
||||
_ledgerId < 0 ||
|
||||
drafts.any((draft) => draft.categoryId < 0)) {
|
||||
return createLocal();
|
||||
}
|
||||
try {
|
||||
final response = await _dio.post(
|
||||
'/api/transactions/recognition-batch',
|
||||
data: {
|
||||
'batchId': batchId,
|
||||
'ledgerId': _ledgerId,
|
||||
'items': [
|
||||
for (var index = 0; index < drafts.length; index++)
|
||||
{
|
||||
'candidateId': drafts[index].candidateId,
|
||||
'clientRequestId': drafts[index].clientRequestId,
|
||||
'categoryId': drafts[index].categoryId,
|
||||
'type': drafts[index].type,
|
||||
'amount': drafts[index].amount,
|
||||
'note': drafts[index].note,
|
||||
'paymentMethod': drafts[index].paymentMethod,
|
||||
'occurredAt': localPayloads[index]['occurredAt'],
|
||||
'source': drafts[index].source,
|
||||
'sourceText': drafts[index].sourceText,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
final result = <String, TxItem>{};
|
||||
for (final raw in response.data as List<dynamic>) {
|
||||
final item = Map<String, dynamic>.from(raw as Map);
|
||||
final transaction = Map<String, dynamic>.from(
|
||||
item['transaction'] as Map,
|
||||
);
|
||||
LocalDatabase.instance.cacheTransaction(transaction);
|
||||
result[item['candidateId'].toString()] = TxItem.fromJson(transaction);
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (!isConnectivityError(error) || !session.isAccount) rethrow;
|
||||
return createLocal();
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> delete(int id) async {
|
||||
final baseUpdatedAt = LocalDatabase.instance.transaction(
|
||||
id,
|
||||
|
||||
Reference in New Issue
Block a user