import 'dart:developer'; import 'dart:ui'; import 'package:enaklo_pos/data/models/response/print_model.dart'; import 'package:enaklo_pos/data/models/response/product_response_model.dart'; import 'package:enaklo_pos/data/models/response/table_model.dart'; import 'package:enaklo_pos/presentation/home/models/order_model.dart'; import 'package:enaklo_pos/presentation/table/models/draft_order_item.dart'; import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart'; import 'package:intl/intl.dart'; import 'package:sqflite/sqflite.dart'; import '../../presentation/home/models/product_quantity.dart'; class ProductLocalDatasource { ProductLocalDatasource._init(); static final ProductLocalDatasource instance = ProductLocalDatasource._init(); final String tableProduct = 'products'; final String tableOrder = 'orders'; final String tableOrderItem = 'order_items'; final String tableManagement = 'table_management'; final String tablePrint = 'prints'; static Database? _database; // "id": 1, // "category_id": 1, // "name": "Mie Ayam", // "description": "Ipsa dolorem impedit dolor. Libero nisi quidem expedita quod mollitia ad. Voluptas ut quia nemo nisi odit fuga. Fugit autem qui ratione laborum eum.", // "image": "https://via.placeholder.com/640x480.png/002200?text=nihil", // "price": "2000.44", // "stock": 94, // "status": 1, // "is_favorite": 1, // "created_at": "2024-02-08T14:30:22.000000Z", // "updated_at": "2024-02-08T15:14:22.000000Z" Future _createDb(Database db, int version) async { await db.execute(''' CREATE TABLE $tableProduct ( id INTEGER PRIMARY KEY, product_id INTEGER, name TEXT, printer_type TEXT, categoryId INTEGER, categoryName TEXT, description TEXT, image TEXT, price TEXT, stock INTEGER, status INTEGER, isFavorite INTEGER, createdAt TEXT, updatedAt TEXT ) '''); await db.execute(''' CREATE TABLE $tableOrder ( id INTEGER PRIMARY KEY AUTOINCREMENT, payment_amount INTEGER, sub_total INTEGER, tax INTEGER, discount INTEGER, discount_amount INTEGER, service_charge INTEGER, total INTEGER, payment_method TEXT, total_item INTEGER, id_kasir INTEGER, nama_kasir TEXT, transaction_time TEXT, table_number INTEGER, customer_name TEXT, status TEXT, payment_status TEXT, order_type TEXT DEFAULT 'DINE IN', is_sync INTEGER DEFAULT 0 ) '''); await db.execute(''' CREATE TABLE $tableOrderItem ( id INTEGER PRIMARY KEY AUTOINCREMENT, id_order INTEGER, id_product INTEGER, quantity INTEGER, price INTEGER, notes TEXT DEFAULT '' ) '''); await db.execute(''' CREATE TABLE $tableManagement ( id INTEGER PRIMARY KEY AUTOINCREMENT, table_name Text, start_time Text, order_id INTEGER, payment_amount INTEGER, x_position REAL NOT NULL, y_position REAL NOT NULL, status TEXT ) '''); await db.execute(''' CREATE TABLE draft_orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, total_item INTEGER, subtotal INTEGER, tax INTEGER, discount INTEGER, discount_amount INTEGER, service_charge INTEGER, total INTEGER, transaction_time TEXT, table_number INTEGER, draft_name TEXT ) '''); await db.execute(''' CREATE TABLE draft_order_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, id_draft_order INTEGER, id_product INTEGER, quantity INTEGER, price INTEGER ) '''); await db.execute(''' CREATE TABLE $tablePrint ( id INTEGER PRIMARY KEY AUTOINCREMENT, code TEXT, name TEXT, address TEXT, paper TEXT, type TEXT ) '''); } Future _initDB(String filePath) async { final dbPath = await getDatabasesPath(); final path = dbPath + filePath; // Force delete existing database to ensure new schema try { final dbExists = await databaseExists(path); if (dbExists) { log("Deleting existing database to ensure new schema with order_type column"); await deleteDatabase(path); } } catch (e) { log("Error deleting database: $e"); } return await openDatabase( path, version: 2, onCreate: _createDb, onUpgrade: _onUpgrade, ); } Future _onUpgrade(Database db, int oldVersion, int newVersion) async { if (oldVersion < 2) { // Add order_type column to orders table if it doesn't exist try { await db.execute('ALTER TABLE $tableOrder ADD COLUMN order_type TEXT DEFAULT "DINE IN"'); log("Added order_type column to orders table"); } catch (e) { log("order_type column might already exist: $e"); } } } Future get database async { if (_database != null) return _database!; _database = await _initDB('dbresto36.db'); return _database!; } //save order Future saveOrder(OrderModel order) async { final db = await instance.database; // Since we're forcing database recreation, order_type column should exist final orderMap = order.toMap(includeOrderType: true); log("Final orderMap for insertion: $orderMap"); int id = await db.insert(tableOrder, orderMap, conflictAlgorithm: ConflictAlgorithm.replace); for (var item in order.orderItems) { log("Item: ${item.toLocalMap(id)}"); await db.insert(tableOrderItem, item.toLocalMap(id), conflictAlgorithm: ConflictAlgorithm.replace); } log("Success Order: ${order.toMap()}"); return id; } //get data order Future> getOrderByIsNotSync() async { final db = await instance.database; final List> maps = await db.query(tableOrder, where: 'is_sync = ?', whereArgs: [0]); return List.generate(maps.length, (i) { return OrderModel.fromMap(maps[i]); }); } Future> getAllOrder( DateTime date, ) async { final db = await instance.database; //date to iso8601 final dateIso = date.toIso8601String(); //get yyyy-MM-dd final dateYYYYMMDD = dateIso.substring(0, 10); // final formattedDate = DateFormat('yyyy-MM-dd').format(date); final List> maps = await db.query( tableOrder, where: 'transaction_time like ?', whereArgs: ['$dateYYYYMMDD%'], // where: 'transaction_time BETWEEN ? AND ?', // whereArgs: [ // DateFormat.yMd().format(start), // DateFormat.yMd().format(end) // ], ); return List.generate(maps.length, (i) { log("Save save OrderModel: ${OrderModel.fromMap(maps[i])}"); return OrderModel.fromMap(maps[i]); }); } //get order item by order id Future> getOrderItemByOrderId(int orderId) async { final db = await instance.database; final List> maps = await db .query(tableOrderItem, where: 'id_order = ?', whereArgs: [orderId]); return List.generate(maps.length, (i) { log("ProductQuantity: ${ProductQuantity.fromLocalMap(maps[i])}"); return ProductQuantity.fromLocalMap(maps[i]); }); } //update payment status by order id Future updatePaymentStatus( int orderId, String paymentStatus, String status) async { final db = await instance.database; await db.update( tableOrder, {'payment_status': paymentStatus, 'status': status}, where: 'id = ?', whereArgs: [orderId]); log('update payment status success | order id: $orderId | payment status: $paymentStatus | status: $status'); } //update order is sync Future updateOrderIsSync(int orderId) async { final db = await instance.database; await db.update(tableOrder, {'is_sync': 1}, where: 'id = ?', whereArgs: [orderId]); } //insert data product Future insertProduct(Product product) async { log("Product: ${product.toMap()}"); final db = await instance.database; await db.insert(tableProduct, product.toMap(), conflictAlgorithm: ConflictAlgorithm.replace); } //update product Future updateProduct(Product product) async { log("Update Product: ${product.toMap()}"); final db = await instance.database; await db.update( tableProduct, product.toLocalMap(), where: 'product_id = ?', whereArgs: [product.productId], ); } //insert list of product Future insertProducts(List products) async { final db = await instance.database; log("Save Products to Local"); for (var product in products) { await db.insert(tableProduct, product.toLocalMap(), conflictAlgorithm: ConflictAlgorithm.replace); log('inserted success id: ${product.productId} | name: ${product.name} | price: ${product.price} | Printer Type ${product.printerType}'); } } //get all products Future> getProducts() async { final db = await instance.database; final List> maps = await db.query(tableProduct); return List.generate(maps.length, (i) { return Product.fromLocalMap(maps[i]); }); } Future getProductById(int id) async { final db = await instance.database; final result = await db.query(tableProduct, where: 'product_id = ?', whereArgs: [id]); if (result.isEmpty) { return null; } return Product.fromMap(result.first); } // get Last Table Management Future getLastTableManagement() async { final db = await instance.database; final List> maps = await db.query(tableManagement, orderBy: 'id DESC', limit: 1); if (maps.isEmpty) { return null; } return TableModel.fromMap(maps[0]); } // generate table managent with count Future createTableManagement(String tableName, Offset position) async { final db = await instance.database; TableModel newTable = TableModel( tableName: tableName, status: 'available', orderId: 0, paymentAmount: 0, startTime: DateTime.now().toIso8601String(), position: position, ); await db.insert( tableManagement, newTable.toMap(), ); } // change position table Future changePositionTable(int id, Offset position) async { final db = await instance.database; await db.update( tableManagement, {'x_position': position.dx, 'y_position': position.dy}, where: 'id = ?', whereArgs: [id], ); } // update table Future updateTable(TableModel table) async { final db = await instance.database; await db.update( tableManagement, table.toMap(), where: 'id = ?', whereArgs: [table.id], ); } // get all table Future> getAllTable() async { final db = await instance.database; final List> maps = await db.query(tableManagement); log("Table Management: $maps"); return List.generate(maps.length, (i) { return TableModel.fromMap(maps[i]); }); } // get last order where table number Future getLastOrderTable(int tableNumber) async { final db = await instance.database; final List> maps = await db.query( tableOrder, where: 'table_number = ?', whereArgs: [tableNumber], orderBy: 'id DESC', // Urutkan berdasarkan id dari yang terbesar (terbaru) limit: 1, // Ambil hanya satu data terakhir ); if (maps.isEmpty) { return null; } return OrderModel.fromMap(maps[0]); } // get table by status Future> getTableByStatus(String status) async { final db = await instance.database; List> maps; if (status == 'all') { // Get all tables maps = await db.query(tableManagement); log("Getting all tables, found: ${maps.length}"); // If no tables exist, create some default tables if (maps.isEmpty) { log("No tables found, creating default tables..."); await _createDefaultTables(); maps = await db.query(tableManagement); log("After creating default tables, found: ${maps.length}"); } } else { // Get tables by specific status maps = await db.query( tableManagement, where: 'status = ?', whereArgs: [status], ); log("Getting tables with status '$status', found: ${maps.length}"); } final tables = List.generate(maps.length, (i) { return TableModel.fromMap(maps[i]); }); log("Returning ${tables.length} tables"); tables.forEach((table) { log("Table: ${table.tableName} (ID: ${table.id}, Status: ${table.status})"); }); return tables; } // Create default tables if none exist Future _createDefaultTables() async { final db = await instance.database; // Create 5 default tables for (int i = 1; i <= 5; i++) { await db.insert(tableManagement, { 'table_name': 'Table $i', 'start_time': DateTime.now().toIso8601String(), 'order_id': 0, 'payment_amount': 0, 'x_position': 100.0 + (i * 50.0), 'y_position': 100.0 + (i * 50.0), 'status': 'available', }); log("Created default table: Table $i"); } } // update status tabel Future updateStatusTable(TableModel table) async { log("Updating table status: ${table.toMap()}"); final db = await instance.database; await db.update(tableManagement, table.toMap(), where: 'id = ?', whereArgs: [table.id]); log("Success Update Status Table: ${table.toMap()}"); // Verify the update final updatedTable = await db.query( tableManagement, where: 'id = ?', whereArgs: [table.id], ); if (updatedTable.isNotEmpty) { log("Verified table update: ${updatedTable.first}"); } } // Debug method to reset all tables to available status Future resetAllTablesToAvailable() async { log("Resetting all tables to available status..."); final db = await instance.database; await db.update( tableManagement, { 'status': 'available', 'order_id': 0, 'payment_amount': 0, 'start_time': DateTime.now().toIso8601String(), }, ); log("All tables reset to available status"); } //delete all products Future deleteAllProducts() async { final db = await instance.database; await db.delete(tableProduct); } Future saveDraftOrder(DraftOrderModel order) async { log("save draft order: ${order.toMapForLocal()}"); final db = await instance.database; int id = await db.insert('draft_orders', order.toMapForLocal()); log("draft order id: $id | ${order.discountAmount}"); for (var orderItem in order.orders) { await db.insert('draft_order_items', orderItem.toMapForLocal(id)); log("draft order item ${orderItem.toMapForLocal(id)}"); } return id; } //get all draft order Future> getAllDraftOrder() async { final db = await instance.database; final result = await db.query('draft_orders', orderBy: 'id ASC'); List results = await Future.wait(result.map((item) async { // Your asynchronous operation here final draftOrderItem = await getDraftOrderItemByOrderId(item['id'] as int); return DraftOrderModel.newFromLocalMap(item, draftOrderItem); })); return results; } // get Darft Order by id Future getDraftOrderById(int id) async { final db = await instance.database; final result = await db.query('draft_orders', where: 'id = ?', whereArgs: [id]); if (result.isEmpty) { return null; } final draftOrderItem = await getDraftOrderItemByOrderId(result.first['id'] as int); log("draft order item: $draftOrderItem | ${result.first.toString()}"); return DraftOrderModel.newFromLocalMap(result.first, draftOrderItem); } //get draft order item by id order Future> getDraftOrderItemByOrderId(int idOrder) async { final db = await instance.database; final result = await db.query('draft_order_items', where: 'id_draft_order = $idOrder'); List results = await Future.wait(result.map((item) async { // Your asynchronous operation here final product = await getProductById(item['id_product'] as int); return DraftOrderItem( product: product!, quantity: item['quantity'] as int); })); return results; } //remove draft order by id Future removeDraftOrderById(int id) async { final db = await instance.database; await db.delete('draft_orders', where: 'id = ?', whereArgs: [id]); await db.delete('draft_order_items', where: 'id_draft_order = ?', whereArgs: [id]); } //update draft order Future updateDraftOrder(DraftOrderModel draftOrder) async { final db = await instance.database; // Update the draft order await db.update( 'draft_orders', draftOrder.toMapForLocal(), where: 'id = ?', whereArgs: [draftOrder.id], ); // Remove existing items and add new ones await db.delete('draft_order_items', where: 'id_draft_order = ?', whereArgs: [draftOrder.id]); for (var orderItem in draftOrder.orders) { await db.insert('draft_order_items', orderItem.toMapForLocal(draftOrder.id!)); } } /// create printer Future createPrinter(PrintModel print) async { final db = await instance.database; await db.insert(tablePrint, print.toMap()); } Future updatePrinter(PrintModel print, int id) async { final db = await instance.database; log("Update Printer: ${print.toMap()} | id: $id"); await db .update(tablePrint, print.toMap(), where: 'id = ?', whereArgs: [id]); } Future deletePrinter(int id) async { final db = await instance.database; await db.delete(tablePrint, where: 'id = ?', whereArgs: [id]); } // get printer by code Future getPrinterByCode(String code) async { final db = await instance.database; final result = await db.query(tablePrint, where: 'code = ?', whereArgs: [code]); if (result.isEmpty) { return null; } return PrintModel.fromMap(result.first); } }