repo_service.dart 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. import 'dart:convert';
  2. import 'package:intl/intl.dart';
  3. import 'package:path/path.dart';
  4. import 'package:path_provider/path_provider.dart';
  5. import 'package:sqflite/sqflite.dart';
  6. import '../models/models.dart';
  7. class RepoService<T> {
  8. static int dbVersion = 11;
  9. static String dbName = 'conalepPos7.db';
  10. static const String id = Basico.identificadorWeb;
  11. static const String idLocal = Basico.identificadorLocal;
  12. static Database? _db;
  13. final Map<String, dynamic> contexto = {
  14. 'Categoria': CategoriaProducto(),
  15. 'Producto': Producto(),
  16. 'Pedido': Pedido(productos: []),
  17. 'PedidoProducto': PedidoProducto(),
  18. 'Gasto': Gasto(),
  19. 'Deposito': Deposito(),
  20. 'CorteCaja': CorteCaja(),
  21. };
  22. Future<Database?> get db async {
  23. if (_db != null) return _db;
  24. _db = await databaseInit();
  25. return _db;
  26. }
  27. Future<Database> databaseInit() async {
  28. String dir = (await getApplicationDocumentsDirectory()).path;
  29. String path = join(dir, dbName);
  30. return await openDatabase(
  31. path,
  32. version: dbVersion,
  33. onCreate: _onCreate,
  34. onUpgrade: _onUpgrade,
  35. );
  36. }
  37. /// Crea la base de datos
  38. _onCreate(Database db, int version) async {
  39. contexto.forEach((String nombre, dynamic modelo) async {
  40. Map<String, dynamic> model = json.decode(json.encode(modelo.toJson()));
  41. String sql =
  42. "CREATE TABLE ${modelo.runtimeType.toString()} (id INTEGER PRIMARY KEY AUTOINCREMENT";
  43. model.forEach((String key, dynamic value) {
  44. if (key != "id") {
  45. String tipo = value.runtimeType.toString();
  46. String sqlType = "";
  47. if (equals(tipo, 'int')) {
  48. sqlType = "INTEGER";
  49. } else if (equals(tipo, 'double')) {
  50. sqlType = "REAL";
  51. } else if (equals(tipo, 'bool')) {
  52. sqlType = "BOOLEAN";
  53. } else {
  54. sqlType = "TEXT";
  55. }
  56. sql += ", $key $sqlType";
  57. }
  58. });
  59. sql += ")";
  60. await db.execute(sql);
  61. await db.execute('''
  62. CREATE TABLE PedidoProductoTopping (
  63. id INTEGER PRIMARY KEY AUTOINCREMENT,
  64. idPedidoProducto INTEGER,
  65. idTopping INTEGER,
  66. FOREIGN KEY (idPedidoProducto) REFERENCES PedidoProducto(id),
  67. FOREIGN KEY (idTopping) REFERENCES Producto(id)
  68. )
  69. ''');
  70. if (modelo.runtimeType.toString() == 'Pedido') {
  71. await db.execute(
  72. 'ALTER TABLE Pedido ADD COLUMN descuento INTEGER DEFAULT 0');
  73. }
  74. await db.execute('''
  75. CREATE TABLE Descuento (
  76. id INTEGER PRIMARY KEY AUTOINCREMENT,
  77. porcentaje INTEGER
  78. )
  79. ''');
  80. await db.insert('Descuento', {'porcentaje': 0});
  81. await db.insert('Descuento', {'porcentaje': 5});
  82. await db.insert('Descuento', {'porcentaje': 10});
  83. await db.insert('Descuento', {'porcentaje': 15});
  84. await db.insert('Descuento', {'porcentaje': 20});
  85. await db.insert('Descuento', {'porcentaje': 25});
  86. await db.insert('Descuento', {'porcentaje': 30});
  87. });
  88. await db.execute('''
  89. CREATE TABLE ProductoTopping (
  90. id INTEGER PRIMARY KEY AUTOINCREMENT,
  91. idProducto INTEGER,
  92. idTopping INTEGER,
  93. FOREIGN KEY (idProducto) REFERENCES Producto(id),
  94. FOREIGN KEY (idTopping) REFERENCES Producto(id)
  95. )
  96. ''');
  97. await db.execute('''
  98. CREATE TABLE Variable (
  99. id INTEGER PRIMARY KEY AUTOINCREMENT,
  100. nombre TEXT,
  101. clave TEXT,
  102. descripcion TEXT,
  103. activo BOOLEAN,
  104. idLocal INTEGER,
  105. eliminado TEXT
  106. )
  107. ''');
  108. }
  109. /// Actualiza la version de la base de datos
  110. void _onUpgrade(Database db, int oldVersion, int newVersion) async {
  111. while (oldVersion < newVersion) {
  112. switch (oldVersion) {
  113. case 1:
  114. await db.execute(
  115. "ALTER TABLE CategoriaProducto ADD COLUMN esToping INTEGER DEFAULT 0");
  116. await db.execute(
  117. "ALTER TABLE CategoriaProducto ADD COLUMN descripcion TEXT DEFAULT ''");
  118. await db.execute(
  119. "ALTER TABLE CategoriaProducto ADD COLUMN maximo INTEGER");
  120. break;
  121. case 2:
  122. await db.execute('''
  123. CREATE TABLE ProductoTopping (
  124. id INTEGER PRIMARY KEY AUTOINCREMENT,
  125. idProducto INTEGER,
  126. idTopping INTEGER,
  127. FOREIGN KEY (idProducto) REFERENCES Producto(id),
  128. FOREIGN KEY (idTopping) REFERENCES Producto(id)
  129. )
  130. ''');
  131. break;
  132. case 3:
  133. await db.execute('''
  134. CREATE TABLE PedidoProductoTopping (
  135. id INTEGER PRIMARY KEY AUTOINCREMENT,
  136. idPedidoProducto INTEGER,
  137. idTopping INTEGER,
  138. FOREIGN KEY (idPedidoProducto) REFERENCES PedidoProducto(id),
  139. FOREIGN KEY (idTopping) REFERENCES Producto(id)
  140. )
  141. ''');
  142. break;
  143. case 4:
  144. await db.execute('''
  145. ALTER TABLE Pedido ADD COLUMN descuento INTEGER DEFAULT 0
  146. ''');
  147. break;
  148. case 5:
  149. await db.execute('''
  150. CREATE TABLE IF NOT EXISTS Descuento (
  151. id INTEGER PRIMARY KEY AUTOINCREMENT,
  152. porcentaje INTEGER
  153. )
  154. ''');
  155. await db.insert('Descuento', {'porcentaje': 0});
  156. await db.insert('Descuento', {'porcentaje': 5});
  157. await db.insert('Descuento', {'porcentaje': 10});
  158. await db.insert('Descuento', {'porcentaje': 15});
  159. await db.insert('Descuento', {'porcentaje': 20});
  160. await db.insert('Descuento', {'porcentaje': 25});
  161. await db.insert('Descuento', {'porcentaje': 30});
  162. break;
  163. case 6:
  164. await db.execute('''
  165. ALTER TABLE Pedido ADD COLUMN tipoPago TEXT DEFAULT '';
  166. ''');
  167. await db.execute('''
  168. ALTER TABLE Pedido ADD COLUMN cantEfectivo REAL DEFAULT 0;
  169. ''');
  170. await db.execute('''
  171. ALTER TABLE Pedido ADD COLUMN cantTarjeta REAL DEFAULT 0;
  172. ''');
  173. await db.execute('''
  174. ALTER TABLE Pedido ADD COLUMN cantTransferencia REAL DEFAULT 0;
  175. ''');
  176. break;
  177. case 7:
  178. await db.execute('''
  179. CREATE TABLE Variable (
  180. id INTEGER PRIMARY KEY AUTOINCREMENT,
  181. nombre TEXT,
  182. clave TEXT,
  183. descripcion TEXT,
  184. activo BOOLEAN,
  185. idLocal INTEGER,
  186. eliminado TEXT
  187. )
  188. ''');
  189. break;
  190. case 8:
  191. await db.execute('''
  192. ALTER TABLE Producto ADD COLUMN toping INTEGER DEFAULT 0;
  193. ''');
  194. break;
  195. case 9:
  196. await db.execute('''
  197. ALTER TABLE Pedido ADD COLUMN idWeb INTEGER;
  198. ''');
  199. await db.execute('''
  200. ALTER TABLE Pedido ADD COLUMN sincronizado TEXT;
  201. ''');
  202. await db.execute('''
  203. ALTER TABLE PedidoProducto ADD COLUMN idWeb INTEGER;
  204. ''');
  205. await db.execute('''
  206. ALTER TABLE PedidoProducto ADD COLUMN sincronizado TEXT;
  207. ''');
  208. break;
  209. case 10:
  210. await db.execute('''
  211. ALTER TABLE Pedido ADD COLUMN idWeb INTEGER;
  212. ''');
  213. await db.execute('''
  214. ALTER TABLE Pedido ADD COLUMN sincronizado TEXT;
  215. ''');
  216. await db.execute('''
  217. ALTER TABLE PedidoProducto ADD COLUMN idWeb INTEGER;
  218. ''');
  219. await db.execute('''
  220. ALTER TABLE PedidoProducto ADD COLUMN sincronizado TEXT;
  221. ''');
  222. break;
  223. }
  224. oldVersion++;
  225. }
  226. }
  227. Future<int> guardar(T model) async {
  228. try {
  229. var dbClient = await db;
  230. String nombreTabla = model.runtimeType.toString();
  231. String modelo = json.encode(model, toEncodable: toEncodable);
  232. Map<String, dynamic> modelMap = json.decode(modelo);
  233. int id = 0;
  234. if (modelMap['id'] != null && modelMap['id'] != 0) {
  235. await dbClient!.update(
  236. nombreTabla,
  237. modelMap,
  238. where: 'id = ?',
  239. whereArgs: [modelMap['id']],
  240. );
  241. id = modelMap['id'];
  242. } else {
  243. modelMap.remove('id');
  244. id = await dbClient!.insert(nombreTabla, modelMap);
  245. }
  246. if (model is Producto) {
  247. await _guardarToppings(dbClient, id, model.topings);
  248. }
  249. return id;
  250. } catch (e) {
  251. print('Error al guardar en $T: $e');
  252. return 0;
  253. }
  254. }
  255. Future<void> _guardarToppings(
  256. Database db, int idProducto, List<Producto>? topings) async {
  257. await db.delete('ProductoTopping',
  258. where: 'idProducto = ?', whereArgs: [idProducto]);
  259. if (topings != null) {
  260. for (var topping in topings) {
  261. await db.insert('ProductoTopping', {
  262. 'idProducto': idProducto,
  263. 'idTopping': topping.id,
  264. });
  265. }
  266. }
  267. }
  268. Future<int> guardarLocal(T model) async {
  269. var dbClient = await db;
  270. String nombreTabla = model.runtimeType.toString();
  271. String modelo = json.encode(model, toEncodable: toEncodable);
  272. Map<String, dynamic> modelMap = json.decode(modelo);
  273. if (nombreTabla == "PedidoProductoTopping") {
  274. modelMap.remove('idLocal');
  275. modelMap.remove('eliminado');
  276. }
  277. if (modelMap['id'] == null || modelMap['id'] == 0) {
  278. modelMap.remove('id');
  279. }
  280. int resultado;
  281. int? identificadorLocal = modelMap[Basico.identificadorLocal];
  282. if (identificadorLocal != null && identificadorLocal > 0) {
  283. resultado = await dbClient!.update(
  284. nombreTabla,
  285. modelMap,
  286. where: "$idLocal = ?",
  287. whereArgs: [identificadorLocal],
  288. );
  289. } else {
  290. resultado = await dbClient!.insert(nombreTabla, modelMap);
  291. var rawQuery =
  292. await dbClient.rawQuery("SELECT last_insert_rowid() as id");
  293. if (rawQuery.isNotEmpty) {
  294. resultado = int.parse(rawQuery.first["id"].toString());
  295. modelMap[Basico.identificadorLocal] = resultado;
  296. }
  297. }
  298. if (model is Pedido) {
  299. model.id = resultado;
  300. }
  301. if (model is Producto) {
  302. await _guardarToppings(dbClient!, model.id!, model.topings);
  303. }
  304. return resultado;
  305. }
  306. dynamic toEncodable(dynamic item) {
  307. if (item is Pedido) {
  308. return item.toJson();
  309. } else if (item is PedidoProducto) {
  310. return item.toJson();
  311. } else if (item is PedidoProductoTopping) {
  312. return item.toJson();
  313. } else if (item is Producto) {
  314. return item.toJson();
  315. } else if (item is CategoriaProducto) {
  316. return item.toJson();
  317. } else if (item is Variable) {
  318. return item.toJson();
  319. }
  320. throw UnsupportedError(
  321. 'Type not supported for serialization: ${item.runtimeType}');
  322. }
  323. Future<List<T>> obtenerTodos({String orderBy = 'id DESC'}) async {
  324. var db = await this.db;
  325. String tableName = T.toString();
  326. var result = await db!.query(tableName, orderBy: orderBy);
  327. return result.map((map) => fromMap<T>(map)).toList();
  328. }
  329. T fromMap<T>(Map<String, dynamic> map) {
  330. switch (T) {
  331. case Pedido:
  332. return Pedido.fromJson(map) as T;
  333. case Producto:
  334. return Producto.fromJson(map) as T;
  335. default:
  336. throw Exception('Tipo no soportado');
  337. }
  338. }
  339. Future<Pedido?> obtenerPorId(int id) async {
  340. Database? dbClient = await db;
  341. List<Map> maps =
  342. await dbClient!.query('Pedido', where: 'id = ?', whereArgs: [id]);
  343. if (maps.isNotEmpty) {
  344. return Pedido.fromJson(Map<String, dynamic>.from(maps.first));
  345. }
  346. return null;
  347. }
  348. Future<List<PedidoProducto>> obtenerPorIdPedido(int idPedido) async {
  349. Database? dbClient = await db;
  350. List<Map> maps = await dbClient!
  351. .query('PedidoProducto', where: 'idPedido = ?', whereArgs: [idPedido]);
  352. return maps
  353. .map((map) => PedidoProducto.fromJson(Map<String, dynamic>.from(map)))
  354. .toList();
  355. }
  356. Future<int> contarPedidos() async {
  357. Database? dbClient = await db;
  358. var result = await dbClient!.rawQuery('SELECT COUNT(*) FROM Pedido');
  359. return Sqflite.firstIntValue(result) ?? 0;
  360. }
  361. Future<List<Pedido>> obtenerPedidosPaginados(int limit, int offset) async {
  362. Database? dbClient = await db;
  363. List<Map<String, dynamic>> result = await dbClient!
  364. .query('Pedido', limit: limit, offset: offset, orderBy: 'id DESC');
  365. return result.map((map) => Pedido.fromJson(map)).toList();
  366. }
  367. Future<Producto?> obtenerProductoPorId(int idProducto) async {
  368. Database? dbClient = await db;
  369. List<Map> maps = await dbClient!
  370. .query('Producto', where: 'id = ?', whereArgs: [idProducto]);
  371. if (maps.isNotEmpty) {
  372. return Producto.fromJson(Map<String, dynamic>.from(maps.first));
  373. }
  374. return null;
  375. }
  376. Future<List<int>> obtenerToppingsPorProducto(int idProducto) async {
  377. var dbClient = await db;
  378. var result = await dbClient!.query(
  379. 'ProductoTopping',
  380. where: 'idProducto = ?',
  381. whereArgs: [idProducto],
  382. );
  383. return result.map((map) => map['idTopping'] as int).toList();
  384. }
  385. Future<List<PedidoProductoTopping>> obtenerToppingsPorPedidoProducto(
  386. int idPedidoProducto) async {
  387. var dbClient = await db;
  388. List<Map> maps = await dbClient!.query('PedidoProductoTopping',
  389. where: 'idPedidoProducto = ?', whereArgs: [idPedidoProducto]);
  390. if (maps.isNotEmpty) {
  391. return maps
  392. .map((map) =>
  393. PedidoProductoTopping.fromJson(Map<String, dynamic>.from(map)))
  394. .toList();
  395. }
  396. return [];
  397. }
  398. Future<int> obtenerProximoFolio() async {
  399. var dbClient = await db;
  400. var result = await dbClient!.rawQuery(
  401. 'SELECT MAX(CAST(folio AS INTEGER)) as last_folio FROM Pedido');
  402. if (result.isNotEmpty && result.first["last_folio"] != null) {
  403. return int.tryParse(result.first["last_folio"].toString())! + 1;
  404. }
  405. return 1;
  406. }
  407. Future<List<T>> buscarPorFolio(String folio) async {
  408. var dbClient = await db;
  409. List<Map<String, dynamic>> maps = await dbClient!.query(
  410. 'Pedido',
  411. where: 'folio LIKE ?',
  412. whereArgs: ['%$folio%'],
  413. );
  414. return maps.map((map) => fromMap<T>(map)).toList();
  415. }
  416. Future<List<Pedido>> buscarPorFecha(
  417. DateTime startDate, DateTime endDate) async {
  418. var dbClient = await db;
  419. String startDateString =
  420. DateFormat('yyyy-MM-dd 00:00:00').format(startDate);
  421. String endDateString = DateFormat('yyyy-MM-dd 23:59:59').format(endDate);
  422. List<Map<String, dynamic>> maps = await dbClient!.rawQuery('''
  423. SELECT * FROM Pedido
  424. WHERE
  425. (datetime(substr(peticion, 7, 4) || '-' || substr(peticion, 4, 2) || '-' || substr(peticion, 1, 2) || ' ' || substr(peticion, 12)) BETWEEN ? AND ?)
  426. OR
  427. (datetime(substr(peticion, 7, 4) || '-' || substr(peticion, 1, 2) || '-' || substr(peticion, 4, 2) || ' ' || substr(peticion, 12)) BETWEEN ? AND ?)
  428. ''', [startDateString, endDateString, startDateString, endDateString]);
  429. return maps.map((map) => Pedido.fromJson(map)).toList();
  430. }
  431. Future<List<CorteCaja>> buscarPorFechaCorte(
  432. DateTime startDate, DateTime endDate) async {
  433. var dbClient = await db;
  434. String startDateString = DateFormat('dd-MM-yyyy').format(startDate);
  435. String endDateString = DateFormat('dd-MM-yyyy 23:59:59').format(endDate);
  436. List<Map<String, dynamic>> maps = await dbClient!.query(
  437. 'CorteCaja',
  438. where: 'fecha BETWEEN ? AND ?',
  439. whereArgs: [startDateString, endDateString],
  440. );
  441. return maps.map((map) => CorteCaja.fromJson(map)).toList();
  442. }
  443. Future<void> eliminar<T>(int id) async {
  444. var dbClient = await db;
  445. String tableName = T.toString();
  446. await dbClient!.delete(tableName, where: 'id = ?', whereArgs: [id]);
  447. }
  448. Future<List<Descuento>> obtenerTodosDescuentos() async {
  449. var dbClient = await db;
  450. var result = await dbClient!.query('Descuento', orderBy: 'porcentaje ASC');
  451. return result.map((map) => Descuento.fromJson(map)).toList();
  452. }
  453. Future<int> guardarDescuento(Descuento descuento) async {
  454. var dbClient = await db;
  455. if (descuento.id != null && descuento.id! > 0) {
  456. return await dbClient!.update(
  457. 'Descuento',
  458. descuento.toJson(),
  459. where: 'id = ?',
  460. whereArgs: [descuento.id],
  461. );
  462. } else {
  463. return await dbClient!.insert('Descuento', descuento.toJson());
  464. }
  465. }
  466. Future<int> eliminarDescuento(int id) async {
  467. var dbClient = await db;
  468. return await dbClient!
  469. .delete('Descuento', where: 'id = ?', whereArgs: [id]);
  470. }
  471. Future<Variable?> obtenerPorNombre(String nombre) async {
  472. var dbClient = await db;
  473. List<Map<String, dynamic>> maps = await dbClient!.query(
  474. 'Variable',
  475. where: 'nombre = ?',
  476. whereArgs: [nombre],
  477. );
  478. if (maps.isNotEmpty) {
  479. return Variable.fromJson(Map<String, dynamic>.from(maps.first));
  480. }
  481. return null;
  482. }
  483. Future<List<Pedido>> obtenerPedidosOrdenadosPorFecha() async {
  484. var dbClient = await db;
  485. String orderBy =
  486. "datetime(substr(peticion, 7, 4) || '-' || substr(peticion, 4, 2) || '-' || substr(peticion, 1, 2) || ' ' || substr(peticion, 12)) ASC";
  487. List<Map<String, dynamic>> result = await dbClient!.query(
  488. 'Pedido',
  489. where: 'sincronizado IS NULL',
  490. orderBy: orderBy,
  491. );
  492. return result.map((map) => Pedido.fromJson(map)).toList();
  493. }
  494. }