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

@@ -239,6 +239,24 @@ class CatalogueDao {
});
}
/// Drops every product and forgets when the catalogue was last imported.
///
/// Used at sign-out. Leaves customers, staff, orders and every other table
/// untouched — this is about the shelf, not the terminal's history — so the
/// next session starts with nothing to sell until it pulls a fresh copy from
/// the back office rather than carrying over whatever this session ended
/// with.
Future<void> clearCatalogue() async {
await _db.transaction((txn) async {
await txn.delete(Tables.products);
await txn.delete(
Tables.meta,
where: 'key IN (?, ?)',
whereArgs: [MetaKeys.lastImportAt, MetaKeys.catalogueRevision],
);
});
}
/// Applies stock movement after a sale, clamped at zero.
Future<void> decrementStock(Map<String, double> quantities) async {
if (quantities.isEmpty) return;

View File

@@ -85,6 +85,46 @@ class OrderDao {
});
}
/// Reverses [commitSale]: deletes the order and its lines, adds the stock
/// back, and restores an attached customer's row exactly as passed in
/// (the caller supplies the pre-sale row — reconstructing it from deltas
/// here would get `last_visit_at` wrong).
Future<void> voidSale({
required String orderId,
required Map<String, double> stockMovements,
Map<String, Object?>? customerRow,
}) async {
await _db.transaction((txn) async {
await txn
.delete(Tables.orderItems, where: 'order_id = ?', whereArgs: [orderId]);
await txn.delete(Tables.orders, where: 'id = ?', whereArgs: [orderId]);
final batch = txn.batch();
final now = DateTime.now().millisecondsSinceEpoch;
stockMovements.forEach((id, qty) {
batch.rawUpdate(
'UPDATE ${Tables.products} SET stock = stock + ?, updated_at = ? '
'WHERE id = ?',
[qty, now, id],
);
});
if (customerRow != null) {
batch.update(
Tables.customers,
{
'loyalty_points': customerRow['loyalty_points'],
'lifetime_spend': customerRow['lifetime_spend'],
'visit_count': customerRow['visit_count'],
'last_visit_at': customerRow['last_visit_at'],
},
where: 'id = ?',
whereArgs: [customerRow['id']],
);
}
await batch.commit(noResult: true);
});
}
Future<void> _insertOrder(DatabaseExecutor txn, SaleTransaction t) async {
final cart = t.cart;

View File

@@ -53,7 +53,13 @@ class TerminalIdentityStore {
///
/// The mint is idempotent: an existing device id is never replaced, so a
/// terminal cannot silently change identity and orphan its own history.
Future<TerminalIdentity> load({String defaultStoreId = 'store-01'}) async {
///
/// [defaultStoreId] matches the store this build's default HTTP endpoint
/// serves — see `syncConfigProvider` — so a fresh terminal's first import
/// pulls that store's real catalogue without anyone visiting Settings
/// first. Settings → Connectivity & sync → Configure changes it per
/// terminal from there.
Future<TerminalIdentity> load({String defaultStoreId = '1135'}) async {
var deviceId = await _catalogue.meta(MetaKeys.deviceId);
var code = await _catalogue.meta(MetaKeys.terminalCode);