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: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 ;
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
// 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 " );
}
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
return await openDatabase (
2025-08-02 10:50:48 +07:00
path ,
version: 2 ,
2025-07-30 22:38:44 +07:00
onCreate: _createDb ,
onUpgrade: _onUpgrade ,
);
}
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
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 {
2025-08-02 10:50:48 +07:00
await db . execute (
'ALTER TABLE $ tableOrder ADD COLUMN order_type TEXT DEFAULT "DINE IN"' );
2025-07-30 22:38:44 +07:00
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 ;
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
// Since we're forcing database recreation, order_type column should exist
final orderMap = order . toMap ( includeOrderType: true );
log ( "Final orderMap for insertion: $ orderMap " );
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
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 ]);
});
}
2025-08-02 10:50:48 +07:00
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 ]);
});
}
2025-07-30 22:38:44 +07:00
//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 = ?' ,
2025-08-03 00:35:00 +07:00
whereArgs: [ product . id ],
2025-07-30 22:38:44 +07:00
);
}
//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 );
2025-08-03 00:35:00 +07:00
log ( 'inserted success id: ${ product . id } | name: ${ product . name } | price: ${ product . price } ' );
2025-07-30 22:38:44 +07:00
}
}
//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 {
2025-08-03 23:53:06 +07:00
// 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(),
// );
2025-07-30 22:38:44 +07:00
}
// 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 ;
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
if ( status == 'all' ) {
// Get all tables
maps = await db . query ( tableManagement );
log ( "Getting all tables, found: ${ maps . length } " );
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
// 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 ]);
});
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
log ( "Returning ${ tables . length } tables" );
tables . forEach (( table ) {
log ( "Table: ${ table . tableName } (ID: ${ table . id } , Status: ${ table . status } )" );
});
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
return tables ;
}
// Create default tables if none exist
Future < void > _createDefaultTables () async {
final db = await instance . database ;
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
// 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 () } " );
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
// Verify the update
final updatedTable = await db . query (
tableManagement ,
where: 'id = ?' ,
whereArgs: [ table . id ],
);
if ( updatedTable . isNotEmpty ) {
log ( "Verified table update: ${ updatedTable . first } " );
}
}
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
// 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 ;
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
// Update the draft order
await db . update (
'draft_orders' ,
draftOrder . toMapForLocal (),
where: 'id = ?' ,
whereArgs: [ draftOrder . id ],
);
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
// Remove existing items and add new ones
await db . delete ( 'draft_order_items' ,
where: 'id_draft_order = ?' , whereArgs: [ draftOrder . id ]);
2025-08-02 10:50:48 +07:00
2025-07-30 22:38:44 +07:00
for ( var orderItem in draftOrder . orders ) {
2025-08-02 10:50:48 +07:00
await db . insert (
'draft_order_items' , orderItem . toMapForLocal ( draftOrder . id ! ));
2025-07-30 22:38:44 +07:00
}
}
/// 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 );
}
}