added product import and billing integration

This commit is contained in:
2026-08-05 18:15:21 +05:30
parent 33b4337933
commit 6c0266c9c7
32 changed files with 889 additions and 457 deletions

View File

@@ -11,10 +11,13 @@ import 'catalogue_wire.dart';
/// Pulls the catalogue from the back office over HTTP.
///
/// ```
/// GET {base}/catalogue?since={revision}&page={n}
/// GET {base}/catalogue?since={revision}&page={n}&page_size={pageSize}&store_id={storeId}
/// Authorization: Bearer {apiKey}
/// ```
///
/// Pages are 0-indexed — the first page requested is `page=0` — matching the
/// back office's own convention rather than the more common 1-indexed one.
///
/// ```json
/// {
/// "revision": "rev-8821",
@@ -53,6 +56,10 @@ class HttpCatalogueSource implements CatalogueSource {
/// connection.
static const int maxPages = 200;
/// Rows requested per page. Sent as `page_size` on every request so the
/// back office doesn't fall back to its own (smaller) default.
static const int pageSize = 500;
static const Duration _timeout = Duration(seconds: 30);
@override
@@ -77,7 +84,7 @@ class HttpCatalogueSource implements CatalogueSource {
var revision = since ?? '';
var isDelta = false;
var page = 1;
var page = 0;
onProgress?.call(0.05, 'Contacting the back office…');
@@ -137,6 +144,7 @@ class HttpCatalogueSource implements CatalogueSource {
queryParameters: {
if (since != null && since.isNotEmpty) 'since': since,
'page': '$page',
'page_size': '$pageSize',
'store_id': config.storeId,
'terminal_id': config.terminalId,
},

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:uuid/uuid.dart';
import '../../core/config/sync_config.dart';
import 'order_transport.dart';
@@ -13,6 +14,12 @@ import 'order_transport.dart';
/// having as the route to bring up first, and as the fallback when a broker is
/// unreachable but the internet is not.
///
/// ```
/// POST {base}/orders
/// { "schema": 1, "batch_id": "…", "store_id": "…", "terminal_id": "…",
/// "orders": [ … ] }
/// ```
///
/// The endpoint must answer with the ids it committed:
///
/// ```json
@@ -29,6 +36,8 @@ class HttpOrderTransport implements OrderTransport {
final SyncConfig config;
final http.Client _client;
static const _uuid = Uuid();
final _connection = StreamController<bool>.broadcast();
bool _reachable = true;
@@ -73,6 +82,16 @@ class HttpOrderTransport implements OrderTransport {
final uri = Uri.parse('${config.httpBaseUrl}/$path');
// Deterministic from the set of ids in this batch — not a fresh random
// id per attempt — so a retry after a timeout (the same rows, because
// nothing was marked sent) carries the exact same batch_id as the
// attempt that may already have landed. That is what lets the back
// office collapse a retried batch server-side instead of re-billing it.
final batchId = _uuid.v5(
Uuid.NAMESPACE_URL,
items.map((o) => o['id']).join('|'),
);
http.Response response;
try {
response = await _client
@@ -82,15 +101,13 @@ class HttpOrderTransport implements OrderTransport {
'content-type': 'application/json',
if (config.apiKey != null)
'authorization': 'Bearer ${config.apiKey}',
// Lets the endpoint collapse a retried batch server-side rather
// than relying on every order id being checked individually.
'idempotency-key': _batchKey(items),
'idempotency-key': batchId,
},
body: jsonEncode({
'schema': 1,
'batch_id': batchId,
'store_id': config.storeId,
'terminal_id': config.terminalId,
'sent_at': DateTime.now().toIso8601String(),
key: items,
}),
)
@@ -143,11 +160,6 @@ class HttpOrderTransport implements OrderTransport {
return PushReceipt(accepted: accepted, rejected: rejected);
}
/// Stable for a given set of records, so a retry after a timeout carries the
/// same key as the attempt that may already have landed.
String _batchKey(List<Map<String, Object?>> items) =>
items.map((o) => o['id']).join('|').hashCode.toRadixString(16);
void _setReachable(bool value) {
if (_reachable == value) return;
_reachable = value;