153 lines
4.5 KiB
TypeScript
153 lines
4.5 KiB
TypeScript
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
import { Product, Sale, Customer } from '../types';
|
|
|
|
const KEYS = {
|
|
PRODUCTS: '@offline_products',
|
|
PENDING_SALES: '@pending_sales',
|
|
CUSTOMERS: '@offline_customers',
|
|
LAST_SYNC: '@last_sync',
|
|
OFFLINE_MODE: '@offline_mode',
|
|
};
|
|
|
|
interface PendingSale {
|
|
id: string;
|
|
items: Array<{ productId: string; quantity: number; unitPrice: number }>;
|
|
paymentMethod: string;
|
|
customerId?: string;
|
|
total: number;
|
|
createdAt: string;
|
|
synced: boolean;
|
|
}
|
|
|
|
class OfflineStorage {
|
|
// Products
|
|
async saveProducts(products: Product[]): Promise<void> {
|
|
await AsyncStorage.setItem(KEYS.PRODUCTS, JSON.stringify(products));
|
|
}
|
|
|
|
async getProducts(): Promise<Product[]> {
|
|
const data = await AsyncStorage.getItem(KEYS.PRODUCTS);
|
|
return data ? JSON.parse(data) : [];
|
|
}
|
|
|
|
async getProductByBarcode(barcode: string): Promise<Product | null> {
|
|
const products = await this.getProducts();
|
|
return products.find((p) => p.barcode === barcode) || null;
|
|
}
|
|
|
|
async updateProductStock(productId: string, quantitySold: number): Promise<void> {
|
|
const products = await this.getProducts();
|
|
const updatedProducts = products.map((p) =>
|
|
p.id === productId ? { ...p, stock: Math.max(0, p.stock - quantitySold) } : p
|
|
);
|
|
await this.saveProducts(updatedProducts);
|
|
}
|
|
|
|
// Pending Sales (offline sales to sync later)
|
|
async savePendingSale(sale: Omit<PendingSale, 'id' | 'createdAt' | 'synced'>): Promise<string> {
|
|
const pendingSales = await this.getPendingSales();
|
|
const newSale: PendingSale = {
|
|
...sale,
|
|
id: `offline_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
createdAt: new Date().toISOString(),
|
|
synced: false,
|
|
};
|
|
pendingSales.push(newSale);
|
|
await AsyncStorage.setItem(KEYS.PENDING_SALES, JSON.stringify(pendingSales));
|
|
|
|
// Update local stock
|
|
for (const item of sale.items) {
|
|
await this.updateProductStock(item.productId, item.quantity);
|
|
}
|
|
|
|
return newSale.id;
|
|
}
|
|
|
|
async getPendingSales(): Promise<PendingSale[]> {
|
|
const data = await AsyncStorage.getItem(KEYS.PENDING_SALES);
|
|
return data ? JSON.parse(data) : [];
|
|
}
|
|
|
|
async getUnsyncedSales(): Promise<PendingSale[]> {
|
|
const sales = await this.getPendingSales();
|
|
return sales.filter((s) => !s.synced);
|
|
}
|
|
|
|
async markSaleAsSynced(saleId: string): Promise<void> {
|
|
const sales = await this.getPendingSales();
|
|
const updatedSales = sales.map((s) =>
|
|
s.id === saleId ? { ...s, synced: true } : s
|
|
);
|
|
await AsyncStorage.setItem(KEYS.PENDING_SALES, JSON.stringify(updatedSales));
|
|
}
|
|
|
|
async removeSyncedSales(): Promise<void> {
|
|
const sales = await this.getPendingSales();
|
|
const unsyncedSales = sales.filter((s) => !s.synced);
|
|
await AsyncStorage.setItem(KEYS.PENDING_SALES, JSON.stringify(unsyncedSales));
|
|
}
|
|
|
|
// Customers
|
|
async saveCustomers(customers: Customer[]): Promise<void> {
|
|
await AsyncStorage.setItem(KEYS.CUSTOMERS, JSON.stringify(customers));
|
|
}
|
|
|
|
async getCustomers(): Promise<Customer[]> {
|
|
const data = await AsyncStorage.getItem(KEYS.CUSTOMERS);
|
|
return data ? JSON.parse(data) : [];
|
|
}
|
|
|
|
// Sync metadata
|
|
async setLastSync(timestamp: string): Promise<void> {
|
|
await AsyncStorage.setItem(KEYS.LAST_SYNC, timestamp);
|
|
}
|
|
|
|
async getLastSync(): Promise<string | null> {
|
|
return AsyncStorage.getItem(KEYS.LAST_SYNC);
|
|
}
|
|
|
|
// Offline mode
|
|
async setOfflineMode(enabled: boolean): Promise<void> {
|
|
await AsyncStorage.setItem(KEYS.OFFLINE_MODE, JSON.stringify(enabled));
|
|
}
|
|
|
|
async isOfflineMode(): Promise<boolean> {
|
|
const data = await AsyncStorage.getItem(KEYS.OFFLINE_MODE);
|
|
return data ? JSON.parse(data) : false;
|
|
}
|
|
|
|
// Clear all offline data
|
|
async clearAll(): Promise<void> {
|
|
await AsyncStorage.multiRemove([
|
|
KEYS.PRODUCTS,
|
|
KEYS.PENDING_SALES,
|
|
KEYS.CUSTOMERS,
|
|
KEYS.LAST_SYNC,
|
|
]);
|
|
}
|
|
|
|
// Get offline stats
|
|
async getOfflineStats(): Promise<{
|
|
productCount: number;
|
|
pendingSalesCount: number;
|
|
pendingSalesTotal: number;
|
|
lastSync: string | null;
|
|
}> {
|
|
const [products, pendingSales, lastSync] = await Promise.all([
|
|
this.getProducts(),
|
|
this.getUnsyncedSales(),
|
|
this.getLastSync(),
|
|
]);
|
|
|
|
return {
|
|
productCount: products.length,
|
|
pendingSalesCount: pendingSales.length,
|
|
pendingSalesTotal: pendingSales.reduce((sum, s) => sum + s.total, 0),
|
|
lastSync,
|
|
};
|
|
}
|
|
}
|
|
|
|
export const offlineStorage = new OfflineStorage();
|
|
export default offlineStorage;
|