h-1.flutter.0/react_native_app/src/services/database.ts

200 lines
5 KiB
TypeScript

// Version: 2026-02-15
/**
* SQLite Database Service
* Migrated from Flutter lib/services/database_helper.dart
*/
import * as SQLite from 'expo-sqlite';
const DATABASE_NAME = 'gemi_invoice.db';
const DATABASE_VERSION = 1;
let db: SQLite.SQLiteDatabase | null = null;
/**
* Initialize database connection
*/
export const initDatabase = async (): Promise<SQLite.SQLiteDatabase> => {
if (db) return db;
db = await SQLite.openDatabaseAsync(DATABASE_NAME);
// Enable foreign keys
await db.execAsync('PRAGMA foreign_keys = ON;');
await createTables();
return db;
};
/**
* Get database instance
*/
export const getDatabase = async (): Promise<SQLite.SQLiteDatabase> => {
if (!db) {
return await initDatabase();
}
return db;
};
/**
* Create all database tables
*/
const createTables = async () => {
if (!db) throw new Error('Database not initialized');
// Company info table
await db.execAsync(`
CREATE TABLE IF NOT EXISTS company_info (
id INTEGER PRIMARY KEY CHECK (id = 1),
name TEXT NOT NULL,
postal_code TEXT,
address TEXT,
tel TEXT,
fax TEXT,
email TEXT,
representative_name TEXT,
tax_rate REAL NOT NULL DEFAULT 0.1,
tax_display_mode TEXT NOT NULL DEFAULT 'normal'
);
`);
// Customers table
await db.execAsync(`
CREATE TABLE IF NOT EXISTS customers (
id TEXT PRIMARY KEY,
display_name TEXT NOT NULL,
formal_name TEXT NOT NULL,
title TEXT NOT NULL DEFAULT '様',
department TEXT,
address TEXT,
tel TEXT,
odoo_id INTEGER,
is_synced INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
`);
// Products table
await db.execAsync(`
CREATE TABLE IF NOT EXISTS products (
id TEXT PRIMARY KEY,
code TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
unit_price REAL NOT NULL,
description TEXT,
barcode TEXT,
category TEXT,
is_active INTEGER NOT NULL DEFAULT 1,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
`);
// Invoices table
await db.execAsync(`
CREATE TABLE IF NOT EXISTS invoices (
id TEXT PRIMARY KEY,
invoice_number TEXT NOT NULL UNIQUE,
date INTEGER NOT NULL,
customer_id TEXT NOT NULL,
document_type TEXT NOT NULL,
subtotal REAL NOT NULL,
tax REAL NOT NULL,
total_amount REAL NOT NULL,
tax_rate REAL NOT NULL,
is_draft INTEGER NOT NULL DEFAULT 1,
notes TEXT,
subject TEXT,
pdf_path TEXT,
signature_data TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
`);
// Invoice items table
await db.execAsync(`
CREATE TABLE IF NOT EXISTS invoice_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
invoice_id TEXT NOT NULL,
description TEXT NOT NULL,
quantity REAL NOT NULL,
unit_price REAL NOT NULL,
subtotal REAL NOT NULL,
item_order INTEGER NOT NULL,
FOREIGN KEY (invoice_id) REFERENCES invoices(id) ON DELETE CASCADE
);
`);
// GPS history table
await db.execAsync(`
CREATE TABLE IF NOT EXISTS gps_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_id TEXT NOT NULL,
latitude REAL NOT NULL,
longitude REAL NOT NULL,
recorded_at INTEGER NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
`);
// Activity log table
await db.execAsync(`
CREATE TABLE IF NOT EXISTS activity_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
entity_type TEXT NOT NULL,
entity_id TEXT NOT NULL,
action TEXT NOT NULL,
description TEXT,
timestamp INTEGER NOT NULL,
metadata TEXT
);
`);
// Create indexes
await db.execAsync(`
CREATE INDEX IF NOT EXISTS idx_invoices_date ON invoices(date DESC);
CREATE INDEX IF NOT EXISTS idx_invoices_customer ON invoices(customer_id);
CREATE INDEX IF NOT EXISTS idx_invoice_items_invoice ON invoice_items(invoice_id);
CREATE INDEX IF NOT EXISTS idx_products_code ON products(code);
CREATE INDEX IF NOT EXISTS idx_gps_history_customer ON gps_history(customer_id);
CREATE INDEX IF NOT EXISTS idx_activity_logs_entity ON activity_logs(entity_type, entity_id);
`);
};
/**
* Close database connection
*/
export const closeDatabase = async () => {
if (db) {
await db.closeAsync();
db = null;
}
};
/**
* Execute a raw SQL query
*/
export const executeQuery = async (
sql: string,
params: any[] = []
): Promise<any[]> => {
const database = await getDatabase();
const result = await database.getAllAsync(sql, params);
return result;
};
/**
* Execute a raw SQL command (INSERT, UPDATE, DELETE)
*/
export const executeCommand = async (
sql: string,
params: any[] = []
): Promise<SQLite.SQLiteRunResult> => {
const database = await getDatabase();
const result = await database.runAsync(sql, params);
return result;
};