sync product to local
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import 'dart:async';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
class DatabaseHelper {
|
||||
static DatabaseHelper? _instance;
|
||||
static Database? _database;
|
||||
|
||||
DatabaseHelper._internal();
|
||||
|
||||
static DatabaseHelper get instance {
|
||||
_instance ??= DatabaseHelper._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
Future<Database> get database async {
|
||||
_database ??= await _initDatabase();
|
||||
return _database!;
|
||||
}
|
||||
|
||||
Future<Database> _initDatabase() async {
|
||||
String path = join(await getDatabasesPath(), 'pos_database.db');
|
||||
|
||||
return await openDatabase(
|
||||
path,
|
||||
version: 1,
|
||||
onCreate: _onCreate,
|
||||
onUpgrade: _onUpgrade,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onCreate(Database db, int version) async {
|
||||
// Products table
|
||||
await db.execute('''
|
||||
CREATE TABLE products (
|
||||
id TEXT PRIMARY KEY,
|
||||
organization_id TEXT,
|
||||
category_id TEXT,
|
||||
sku TEXT,
|
||||
name TEXT,
|
||||
description TEXT,
|
||||
price INTEGER,
|
||||
cost INTEGER,
|
||||
business_type TEXT,
|
||||
image_url TEXT,
|
||||
printer_type TEXT,
|
||||
metadata TEXT,
|
||||
is_active INTEGER,
|
||||
created_at TEXT,
|
||||
updated_at TEXT
|
||||
)
|
||||
''');
|
||||
|
||||
// Product Variants table
|
||||
await db.execute('''
|
||||
CREATE TABLE product_variants (
|
||||
id TEXT PRIMARY KEY,
|
||||
product_id TEXT,
|
||||
name TEXT,
|
||||
price_modifier INTEGER,
|
||||
cost INTEGER,
|
||||
metadata TEXT,
|
||||
created_at TEXT,
|
||||
updated_at TEXT,
|
||||
FOREIGN KEY (product_id) REFERENCES products (id) ON DELETE CASCADE
|
||||
)
|
||||
''');
|
||||
|
||||
// Create indexes for better performance
|
||||
await db.execute(
|
||||
'CREATE INDEX idx_products_category_id ON products(category_id)');
|
||||
await db.execute('CREATE INDEX idx_products_name ON products(name)');
|
||||
await db.execute('CREATE INDEX idx_products_sku ON products(sku)');
|
||||
await db.execute(
|
||||
'CREATE INDEX idx_products_description ON products(description)');
|
||||
}
|
||||
|
||||
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
||||
// Handle database upgrades here
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
final db = await database;
|
||||
await db.close();
|
||||
_database = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
class DatabaseMigrationHandler {
|
||||
static Future<void> migrate(
|
||||
Database db, int oldVersion, int newVersion) async {
|
||||
if (oldVersion < 2) {
|
||||
// Add indexes for better performance
|
||||
await db.execute(
|
||||
'CREATE INDEX IF NOT EXISTS idx_products_name_search ON products(name)');
|
||||
await db.execute(
|
||||
'CREATE INDEX IF NOT EXISTS idx_products_sku_search ON products(sku)');
|
||||
}
|
||||
|
||||
if (oldVersion < 3) {
|
||||
// Add full text search support
|
||||
await db.execute(
|
||||
'CREATE VIRTUAL TABLE products_fts USING fts5(name, sku, description, content=products, content_rowid=rowid)');
|
||||
await db.execute(
|
||||
'INSERT INTO products_fts SELECT name, sku, description FROM products');
|
||||
}
|
||||
|
||||
if (oldVersion < 4) {
|
||||
// Add sync tracking
|
||||
await db.execute('ALTER TABLE products ADD COLUMN last_sync_at TEXT');
|
||||
await db.execute(
|
||||
'ALTER TABLE products ADD COLUMN sync_version INTEGER DEFAULT 1');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
class DatabaseErrorHandler {
|
||||
static Future<T> executeWithRetry<T>(
|
||||
Future<T> Function() operation, {
|
||||
int maxRetries = 3,
|
||||
Duration delay = const Duration(milliseconds: 500),
|
||||
}) async {
|
||||
int attempts = 0;
|
||||
|
||||
while (attempts < maxRetries) {
|
||||
try {
|
||||
return await operation();
|
||||
} catch (e) {
|
||||
attempts++;
|
||||
|
||||
if (attempts >= maxRetries) {
|
||||
rethrow;
|
||||
}
|
||||
|
||||
log('Database operation failed (attempt $attempts/$maxRetries): $e');
|
||||
await Future.delayed(delay * attempts);
|
||||
}
|
||||
}
|
||||
|
||||
throw Exception('Max retries exceeded');
|
||||
}
|
||||
|
||||
static bool isDatabaseCorrupted(dynamic error) {
|
||||
final errorString = error.toString().toLowerCase();
|
||||
return errorString.contains('corrupt') ||
|
||||
errorString.contains('malformed') ||
|
||||
errorString.contains('no such table');
|
||||
}
|
||||
|
||||
static Future<void> handleDatabaseCorruption() async {
|
||||
try {
|
||||
// Delete corrupted database
|
||||
final dbPath = await getDatabasesPath();
|
||||
final file = File('$dbPath/pos_database.db');
|
||||
if (await file.exists()) {
|
||||
await file.delete();
|
||||
}
|
||||
|
||||
log('Corrupted database deleted, will be recreated');
|
||||
} catch (e) {
|
||||
log('Error handling database corruption: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import 'dart:developer';
|
||||
|
||||
class DatabasePerformanceMonitor {
|
||||
static final Map<String, List<int>> _queryTimes = {};
|
||||
|
||||
static Future<T> monitorQuery<T>(
|
||||
String queryName,
|
||||
Future<T> Function() query,
|
||||
) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
try {
|
||||
final result = await query();
|
||||
stopwatch.stop();
|
||||
|
||||
_recordQueryTime(queryName, stopwatch.elapsedMilliseconds);
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
stopwatch.stop();
|
||||
log('Query "$queryName" failed after ${stopwatch.elapsedMilliseconds}ms: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
static void _recordQueryTime(String queryName, int milliseconds) {
|
||||
if (!_queryTimes.containsKey(queryName)) {
|
||||
_queryTimes[queryName] = [];
|
||||
}
|
||||
|
||||
_queryTimes[queryName]!.add(milliseconds);
|
||||
|
||||
// Keep only last 100 entries
|
||||
if (_queryTimes[queryName]!.length > 100) {
|
||||
_queryTimes[queryName]!.removeAt(0);
|
||||
}
|
||||
|
||||
// Log slow queries
|
||||
if (milliseconds > 1000) {
|
||||
log('Slow query detected: "$queryName" took ${milliseconds}ms');
|
||||
}
|
||||
}
|
||||
|
||||
static Map<String, dynamic> getPerformanceStats() {
|
||||
final stats = <String, dynamic>{};
|
||||
|
||||
_queryTimes.forEach((queryName, times) {
|
||||
if (times.isNotEmpty) {
|
||||
final avgTime = times.reduce((a, b) => a + b) / times.length;
|
||||
final maxTime = times.reduce((a, b) => a > b ? a : b);
|
||||
final minTime = times.reduce((a, b) => a < b ? a : b);
|
||||
|
||||
stats[queryName] = {
|
||||
'average_ms': avgTime.round(),
|
||||
'max_ms': maxTime,
|
||||
'min_ms': minTime,
|
||||
'total_queries': times.length,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user