2025-07-30 22:38:44 +07:00
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 < 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 " ) ;
2025-07-31 19:25:45 +07:00
// await deleteDatabase(path);
2025-07-30 22:38:44 +07:00
}
} 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 ] ) ;
} ) ;
}
//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 . productId ] ,
) ;
}
//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 . productId } | name: ${ product . name } | price: ${ product . price } | Printer Type ${ product . printerType } ' ) ;
}
}
//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 ) ;
}
}