michangarrito-mobile-v2/src/services/offlineStorage.ts
rckrdmrd a71600df2f Migración desde michangarrito/apps/mobile - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 08:26:57 -06:00

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;