h-1.flutter.0/lib/services/inventory_repository.dart
2026-03-04 14:55:40 +09:00

101 lines
3.5 KiB
Dart

import 'package:sqflite/sqflite.dart';
import 'package:uuid/uuid.dart';
import '../models/inventory_models.dart';
import 'database_helper.dart';
class InventoryRepository {
InventoryRepository();
final DatabaseHelper _dbHelper = DatabaseHelper();
final Uuid _uuid = const Uuid();
Future<List<InventorySummary>> fetchSummaries({bool includeHidden = false}) async {
final db = await _dbHelper.database;
final whereClauses = <String>[];
if (!includeHidden) {
whereClauses.add('COALESCE(mh.is_hidden, p.is_hidden, 0) = 0');
}
final whereSql = whereClauses.isEmpty ? '' : 'WHERE ${whereClauses.join(' AND ')}';
final rows = await db.rawQuery('''
SELECT p.id, p.name, p.category, p.default_unit_price, p.stock_quantity,
MAX(m.created_at) AS last_movement_at
FROM products p
LEFT JOIN master_hidden mh ON mh.master_type = 'product' AND mh.master_id = p.id
LEFT JOIN inventory_movements m ON m.product_id = p.id
$whereSql
GROUP BY p.id
ORDER BY p.name COLLATE NOCASE ASC
''');
return rows.map((row) {
return InventorySummary(
productId: row['id'] as String,
productName: row['name'] as String? ?? '-',
stockQuantity: row['stock_quantity'] as int? ?? 0,
category: row['category'] as String?,
defaultUnitPrice: row['default_unit_price'] as int?,
lastMovementAt: row['last_movement_at'] != null ? DateTime.parse(row['last_movement_at'] as String) : null,
);
}).toList();
}
Future<List<InventoryMovement>> fetchMovements(String productId, {int limit = 50}) async {
final db = await _dbHelper.database;
final rows = await db.query(
'inventory_movements',
where: 'product_id = ?',
whereArgs: [productId],
orderBy: 'created_at DESC',
limit: limit,
);
return rows.map(InventoryMovement.fromMap).toList();
}
Future<InventorySummary> recordMovement({
required String productId,
required InventoryMovementType type,
required int quantity,
required int quantityDelta,
String? reference,
String? notes,
}) async {
final db = await _dbHelper.database;
late InventorySummary summary;
await db.transaction((txn) async {
final productRows = await txn.query('products', where: 'id = ?', whereArgs: [productId], limit: 1);
if (productRows.isEmpty) {
throw StateError('product not found: $productId');
}
final product = productRows.first;
final currentStock = product['stock_quantity'] as int? ?? 0;
final nextStock = currentStock + quantityDelta;
final now = DateTime.now();
final movement = InventoryMovement(
id: _uuid.v4(),
productId: productId,
productNameSnapshot: product['name'] as String? ?? '-',
type: type,
quantity: quantity,
quantityDelta: quantityDelta,
reference: reference,
notes: notes,
createdAt: now,
);
await txn.insert('inventory_movements', movement.toMap(), conflictAlgorithm: ConflictAlgorithm.replace);
await txn.update('products', {'stock_quantity': nextStock}, where: 'id = ?', whereArgs: [productId]);
summary = InventorySummary(
productId: productId,
productName: product['name'] as String? ?? '-',
stockQuantity: nextStock,
category: product['category'] as String?,
defaultUnitPrice: product['default_unit_price'] as int?,
lastMovementAt: now,
);
});
return summary;
}
}