640 lines
19 KiB
Dart
640 lines
19 KiB
Dart
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: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<void> _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<Database> _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<void> _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<Database> get database async {
|
|
if (_database != null) return _database!;
|
|
_database = await _initDB('dbresto36.db');
|
|
return _database!;
|
|
}
|
|
|
|
//save order
|
|
Future<int> 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<List<OrderModel>> getOrderByIsNotSync() async {
|
|
final db = await instance.database;
|
|
final List<Map<String, dynamic>> maps =
|
|
await db.query(tableOrder, where: 'is_sync = ?', whereArgs: [0]);
|
|
return List.generate(maps.length, (i) {
|
|
return OrderModel.fromMap(maps[i]);
|
|
});
|
|
}
|
|
|
|
Future<List<OrderModel>> 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<Map<String, dynamic>> 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]);
|
|
});
|
|
}
|
|
|
|
Future<List<OrderModel>> getAllOrderByRange(
|
|
DateTime start, DateTime end) async {
|
|
final db = await instance.database;
|
|
|
|
// Format ke ISO 8601 untuk range, hasil: yyyy-MM-ddTHH:mm:ss
|
|
final startIso = start.toIso8601String();
|
|
final endIso = end.toIso8601String();
|
|
|
|
final startDateYYYYMMDD = startIso.substring(0, 10);
|
|
final endDateYYYYMMDD = endIso.substring(0, 10);
|
|
|
|
final List<Map<String, dynamic>> maps = await db.query(
|
|
tableOrder,
|
|
where: 'substr(transaction_time, 1, 10) BETWEEN ? AND ?',
|
|
whereArgs: [startDateYYYYMMDD, endDateYYYYMMDD],
|
|
orderBy: 'transaction_time DESC',
|
|
);
|
|
log("Get All Order By Range: $startDateYYYYMMDD $endDateYYYYMMDD");
|
|
|
|
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<List<ProductQuantity>> getOrderItemByOrderId(int orderId) async {
|
|
final db = await instance.database;
|
|
final List<Map<String, dynamic>> 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<void> 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<void> updateOrderIsSync(int orderId) async {
|
|
final db = await instance.database;
|
|
await db.update(tableOrder, {'is_sync': 1},
|
|
where: 'id = ?', whereArgs: [orderId]);
|
|
}
|
|
|
|
//insert data product
|
|
|
|
Future<void> 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<void> 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.id],
|
|
);
|
|
}
|
|
|
|
//insert list of product
|
|
Future<void> insertProducts(List<Product> 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.id} | name: ${product.name} | price: ${product.price} ');
|
|
}
|
|
}
|
|
|
|
//get all products
|
|
Future<List<Product>> getProducts() async {
|
|
final db = await instance.database;
|
|
final List<Map<String, dynamic>> maps = await db.query(tableProduct);
|
|
return List.generate(maps.length, (i) {
|
|
return Product.fromLocalMap(maps[i]);
|
|
});
|
|
}
|
|
|
|
Future<Product?> 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<TableModel?> getLastTableManagement() async {
|
|
final db = await instance.database;
|
|
final List<Map<String, dynamic>> 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<void> 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<void> 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<void> updateTable(TableModel table) async {
|
|
final db = await instance.database;
|
|
await db.update(
|
|
tableManagement,
|
|
table.toMap(),
|
|
where: 'id = ?',
|
|
whereArgs: [table.id],
|
|
);
|
|
}
|
|
|
|
// get all table
|
|
Future<List<TableModel>> getAllTable() async {
|
|
final db = await instance.database;
|
|
final List<Map<String, dynamic>> 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<OrderModel?> getLastOrderTable(int tableNumber) async {
|
|
final db = await instance.database;
|
|
final List<Map<String, dynamic>> 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<List<TableModel>> getTableByStatus(String status) async {
|
|
final db = await instance.database;
|
|
List<Map<String, dynamic>> 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<void> _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<void> 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<void> 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<void> deleteAllProducts() async {
|
|
final db = await instance.database;
|
|
await db.delete(tableProduct);
|
|
}
|
|
|
|
Future<int> 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<List<DraftOrderModel>> getAllDraftOrder() async {
|
|
final db = await instance.database;
|
|
final result = await db.query('draft_orders', orderBy: 'id ASC');
|
|
|
|
List<DraftOrderModel> 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<DraftOrderModel?> 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<List<DraftOrderItem>> getDraftOrderItemByOrderId(int idOrder) async {
|
|
final db = await instance.database;
|
|
final result =
|
|
await db.query('draft_order_items', where: 'id_draft_order = $idOrder');
|
|
|
|
List<DraftOrderItem> 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<void> 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<void> 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<void> createPrinter(PrintModel print) async {
|
|
final db = await instance.database;
|
|
await db.insert(tablePrint, print.toMap());
|
|
}
|
|
|
|
Future<void> 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<void> deletePrinter(int id) async {
|
|
final db = await instance.database;
|
|
await db.delete(tablePrint, where: 'id = ?', whereArgs: [id]);
|
|
}
|
|
|
|
// get printer by code
|
|
Future<PrintModel?> 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);
|
|
}
|
|
}
|