repo_service.dart 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  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 = 8;
  9. static String dbName = 'joshipos026.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. _onCreate(Database db, int version) async {
  38. contexto.forEach((String nombre, dynamic modelo) async {
  39. Map<String, dynamic> model = json.decode(json.encode(modelo.toJson()));
  40. String sql =
  41. "CREATE TABLE ${modelo.runtimeType.toString()} (id INTEGER PRIMARY KEY AUTOINCREMENT";
  42. model.forEach((String key, dynamic value) {
  43. if (key != "id") {
  44. String tipo = value.runtimeType.toString();
  45. String sqlType = "";
  46. if (equals(tipo, 'int')) {
  47. sqlType = "INTEGER";
  48. } else if (equals(tipo, 'double')) {
  49. sqlType = "REAL";
  50. } else if (equals(tipo, 'bool')) {
  51. sqlType = "BOOLEAN";
  52. } else {
  53. sqlType = "TEXT";
  54. }
  55. sql += ", $key $sqlType";
  56. }
  57. });
  58. sql += ")";
  59. await db.execute(sql);
  60. await db.execute('''
  61. CREATE TABLE PedidoProductoTopping (
  62. id INTEGER PRIMARY KEY AUTOINCREMENT,
  63. idPedidoProducto INTEGER,
  64. idTopping INTEGER,
  65. FOREIGN KEY (idPedidoProducto) REFERENCES PedidoProducto(id),
  66. FOREIGN KEY (idTopping) REFERENCES Producto(id)
  67. )
  68. ''');
  69. if (modelo.runtimeType.toString() == 'Pedido') {
  70. await db.execute(
  71. 'ALTER TABLE Pedido ADD COLUMN descuento INTEGER DEFAULT 0');
  72. }
  73. await db.execute('''
  74. CREATE TABLE Descuento (
  75. id INTEGER PRIMARY KEY AUTOINCREMENT,
  76. porcentaje INTEGER
  77. )
  78. ''');
  79. await db.insert('Descuento', {'porcentaje': 0});
  80. await db.insert('Descuento', {'porcentaje': 5});
  81. await db.insert('Descuento', {'porcentaje': 10});
  82. await db.insert('Descuento', {'porcentaje': 15});
  83. await db.insert('Descuento', {'porcentaje': 20});
  84. await db.insert('Descuento', {'porcentaje': 25});
  85. await db.insert('Descuento', {'porcentaje': 30});
  86. });
  87. await db.execute('''
  88. CREATE TABLE ProductoTopping (
  89. id INTEGER PRIMARY KEY AUTOINCREMENT,
  90. idProducto INTEGER,
  91. idTopping INTEGER,
  92. FOREIGN KEY (idProducto) REFERENCES Producto(id),
  93. FOREIGN KEY (idTopping) REFERENCES Producto(id)
  94. )
  95. ''');
  96. await db.execute('''
  97. CREATE TABLE Variable (
  98. id INTEGER PRIMARY KEY AUTOINCREMENT,
  99. nombre TEXT,
  100. clave TEXT,
  101. descripcion TEXT,
  102. activo BOOLEAN,
  103. idLocal INTEGER,
  104. eliminado TEXT
  105. )
  106. ''');
  107. }
  108. void _onUpgrade(Database db, int oldVersion, int newVersion) async {
  109. while (oldVersion < newVersion) {
  110. switch (oldVersion) {
  111. case 1:
  112. await db.execute(
  113. "ALTER TABLE CategoriaProducto ADD COLUMN esToping INTEGER DEFAULT 0");
  114. await db.execute(
  115. "ALTER TABLE CategoriaProducto ADD COLUMN descripcion TEXT DEFAULT ''");
  116. await db.execute(
  117. "ALTER TABLE CategoriaProducto ADD COLUMN maximo INTEGER");
  118. await db.insert('CategoriaProducto', {
  119. 'nombre': 'BASE PRODUCTO',
  120. 'descripcion': 'Base del producto',
  121. 'esToping': 1,
  122. 'maximo': 1,
  123. });
  124. await db.insert('CategoriaProducto', {
  125. 'nombre': 'SALSAS PRODUCTO',
  126. 'descripcion': 'Elige tus salsas (Máx. 2)',
  127. 'esToping': 1,
  128. 'maximo': 2,
  129. });
  130. await db.insert('CategoriaProducto', {
  131. 'nombre': 'ADEREZO PRODUCTO',
  132. 'descripcion': 'Elige tu aderezo (Máx. 2)',
  133. 'esToping': 1,
  134. 'maximo': 2,
  135. });
  136. await db.insert('CategoriaProducto', {
  137. 'nombre': 'TOPPING PRODUCTO',
  138. 'descripcion': 'Elige tus toppings (Máx. 2)',
  139. 'esToping': 1,
  140. 'maximo': 2,
  141. });
  142. await db.insert('Producto', {
  143. 'nombre': 'Papa Gajo',
  144. 'precio': 0,
  145. });
  146. await db.insert('Producto', {
  147. 'nombre': 'Papa Regilla',
  148. 'precio': 0,
  149. });
  150. await db.insert('Producto', {
  151. 'nombre': 'Papa Curly',
  152. 'precio': 0,
  153. });
  154. await db.insert('Producto', {
  155. 'nombre': 'Papa Smile',
  156. 'precio': 0,
  157. });
  158. await db.insert('Producto', {
  159. 'nombre': 'Papa Francesa',
  160. 'precio': 0,
  161. });
  162. await db.insert('Producto', {
  163. 'nombre': 'BBQ',
  164. 'precio': 0,
  165. });
  166. await db.insert('Producto', {
  167. 'nombre': 'HOTBBQ',
  168. 'precio': 0,
  169. });
  170. await db.insert('Producto', {
  171. 'nombre': 'BUFFALO',
  172. 'precio': 0,
  173. });
  174. await db.insert('Producto', {
  175. 'nombre': 'TERIYAKI',
  176. 'precio': 0,
  177. });
  178. await db.insert('Producto', {
  179. 'nombre': 'PARMESAN GARLIC',
  180. 'precio': 0,
  181. });
  182. await db.insert('Producto', {
  183. 'nombre': 'QUESO AMARILLO',
  184. 'precio': 0,
  185. });
  186. await db.insert('Producto', {
  187. 'nombre': 'RANCH',
  188. 'precio': 0,
  189. });
  190. await db.insert('Producto', {
  191. 'nombre': 'CHIPOTLE',
  192. 'precio': 0,
  193. });
  194. await db.insert('Producto', {
  195. 'nombre': 'ADEREZO JALAPEÑO',
  196. 'precio': 0,
  197. });
  198. await db.insert('Producto', {
  199. 'nombre': 'KETCHUP',
  200. 'precio': 0,
  201. });
  202. await db.insert('Producto', {
  203. 'nombre': 'JALAPEÑO',
  204. 'precio': 0,
  205. });
  206. await db.insert('Producto', {
  207. 'nombre': 'QUESO BLANCO',
  208. 'precio': 0,
  209. });
  210. await db.insert('Producto', {
  211. 'nombre': 'TAKIS',
  212. 'precio': 0,
  213. });
  214. await db.insert('Producto', {
  215. 'nombre': 'RUFFLES',
  216. 'precio': 0,
  217. });
  218. await db.insert('Producto', {
  219. 'nombre': 'QUESO PARMESANO',
  220. 'precio': 0,
  221. });
  222. await db.insert('Producto', {
  223. 'nombre': 'ELOTE',
  224. 'precio': 0,
  225. });
  226. break;
  227. case 2:
  228. await db.execute('''
  229. CREATE TABLE ProductoTopping (
  230. id INTEGER PRIMARY KEY AUTOINCREMENT,
  231. idProducto INTEGER,
  232. idTopping INTEGER,
  233. FOREIGN KEY (idProducto) REFERENCES Producto(id),
  234. FOREIGN KEY (idTopping) REFERENCES Producto(id)
  235. )
  236. ''');
  237. break;
  238. case 3:
  239. await db.execute('''
  240. CREATE TABLE PedidoProductoTopping (
  241. id INTEGER PRIMARY KEY AUTOINCREMENT,
  242. idPedidoProducto INTEGER,
  243. idTopping INTEGER,
  244. FOREIGN KEY (idPedidoProducto) REFERENCES PedidoProducto(id),
  245. FOREIGN KEY (idTopping) REFERENCES Producto(id)
  246. )
  247. ''');
  248. break;
  249. case 4:
  250. await db.execute('''
  251. ALTER TABLE Pedido ADD COLUMN descuento INTEGER DEFAULT 0
  252. ''');
  253. break;
  254. case 5:
  255. await db.execute('''
  256. CREATE TABLE IF NOT EXISTS Descuento (
  257. id INTEGER PRIMARY KEY AUTOINCREMENT,
  258. porcentaje INTEGER
  259. )
  260. ''');
  261. await db.insert('Descuento', {'porcentaje': 0});
  262. await db.insert('Descuento', {'porcentaje': 5});
  263. await db.insert('Descuento', {'porcentaje': 10});
  264. await db.insert('Descuento', {'porcentaje': 15});
  265. await db.insert('Descuento', {'porcentaje': 20});
  266. await db.insert('Descuento', {'porcentaje': 25});
  267. await db.insert('Descuento', {'porcentaje': 30});
  268. break;
  269. case 6:
  270. await db.execute('''
  271. ALTER TABLE Pedido ADD COLUMN tipoPago TEXT DEFAULT '';
  272. ''');
  273. await db.execute('''
  274. ALTER TABLE Pedido ADD COLUMN cantEfectivo REAL DEFAULT 0;
  275. ''');
  276. await db.execute('''
  277. ALTER TABLE Pedido ADD COLUMN cantTarjeta REAL DEFAULT 0;
  278. ''');
  279. await db.execute('''
  280. ALTER TABLE Pedido ADD COLUMN cantTransferencia REAL DEFAULT 0;
  281. ''');
  282. break;
  283. case 7:
  284. await db.execute('''
  285. CREATE TABLE Variable (
  286. id INTEGER PRIMARY KEY AUTOINCREMENT,
  287. nombre TEXT,
  288. clave TEXT,
  289. descripcion TEXT,
  290. activo BOOLEAN,
  291. idLocal INTEGER,
  292. eliminado TEXT
  293. )
  294. ''');
  295. break;
  296. }
  297. oldVersion++;
  298. }
  299. }
  300. Future<int> guardar(T model) async {
  301. try {
  302. var dbClient = await db;
  303. String nombreTabla = model.runtimeType.toString();
  304. String modelo = json.encode(model, toEncodable: toEncodable);
  305. print("Guardando en tabla: $nombreTabla");
  306. print("Modelo serializado: $modelo");
  307. Map<String, dynamic> modelMap = json.decode(modelo);
  308. int id = 0;
  309. if (modelMap['id'] != null && modelMap['id'] != 0) {
  310. print("Actualizando el registro con ID: ${modelMap['id']}");
  311. await dbClient!.update(
  312. nombreTabla,
  313. modelMap,
  314. where: 'id = ?',
  315. whereArgs: [modelMap['id']],
  316. );
  317. id = modelMap['id'];
  318. } else {
  319. print("Insertando nuevo registro");
  320. modelMap.remove('id');
  321. id = await dbClient!.insert(nombreTabla, modelMap);
  322. }
  323. return id;
  324. } catch (e) {
  325. print('Error al guardar en dynamic: $e');
  326. return 0;
  327. }
  328. }
  329. Future<void> _guardarToppings(
  330. Database db, int idProducto, List<Producto>? topings) async {
  331. await db.delete('ProductoTopping',
  332. where: 'idProducto = ?', whereArgs: [idProducto]);
  333. if (topings != null) {
  334. for (var topping in topings) {
  335. await db.insert('ProductoTopping', {
  336. 'idProducto': idProducto,
  337. 'idTopping': topping.id,
  338. });
  339. }
  340. }
  341. }
  342. Future<int> guardarLocal(T model) async {
  343. var dbClient = await db;
  344. String nombreTabla = model.runtimeType.toString();
  345. String modelo = json.encode(model, toEncodable: toEncodable);
  346. Map<String, dynamic> modelMap = json.decode(modelo);
  347. if (nombreTabla == "PedidoProductoTopping") {
  348. modelMap.remove('idLocal');
  349. modelMap.remove('eliminado');
  350. }
  351. if (modelMap['id'] == null || modelMap['id'] == 0) {
  352. modelMap.remove('id');
  353. }
  354. int resultado;
  355. int? identificadorLocal = modelMap[Basico.identificadorLocal];
  356. if (identificadorLocal != null && identificadorLocal > 0) {
  357. resultado = await dbClient!.update(
  358. nombreTabla,
  359. modelMap,
  360. where: "$idLocal = ?",
  361. whereArgs: [identificadorLocal],
  362. );
  363. } else {
  364. resultado = await dbClient!.insert(nombreTabla, modelMap);
  365. var rawQuery =
  366. await dbClient.rawQuery("SELECT last_insert_rowid() as id");
  367. if (rawQuery.isNotEmpty) {
  368. resultado = int.parse(rawQuery.first["id"].toString());
  369. modelMap[Basico.identificadorLocal] = resultado;
  370. }
  371. }
  372. if (model is Pedido) {
  373. model.id = resultado;
  374. }
  375. if (model is Producto) {
  376. await _guardarToppings(dbClient!, model.id!, model.topings);
  377. }
  378. return resultado;
  379. }
  380. dynamic toEncodable(dynamic item) {
  381. print("Serializando objeto: $item");
  382. if (item is Pedido) {
  383. return item.toJson();
  384. } else if (item is PedidoProducto) {
  385. return item.toJson();
  386. } else if (item is PedidoProductoTopping) {
  387. return item.toJson();
  388. } else if (item is Producto) {
  389. return item.toJson();
  390. } else if (item is CategoriaProducto) {
  391. return item.toJson();
  392. } else if (item is Variable) {
  393. return item.toJson();
  394. }
  395. throw UnsupportedError(
  396. 'Type not supported for serialization: ${item.runtimeType}');
  397. }
  398. Future<List<T>> obtenerTodos({String orderBy = 'id DESC'}) async {
  399. var db = await this.db;
  400. String tableName = T.toString();
  401. var result = await db!.query(tableName, orderBy: orderBy);
  402. return result.map((map) => fromMap<T>(map)).toList();
  403. }
  404. T fromMap<T>(Map<String, dynamic> map) {
  405. switch (T) {
  406. case Pedido:
  407. return Pedido.fromJson(map) as T;
  408. case Producto:
  409. return Producto.fromJson(map) as T;
  410. default:
  411. throw Exception('Tipo no soportado');
  412. }
  413. }
  414. Future<Pedido?> obtenerPorId(int id) async {
  415. Database? dbClient = await db;
  416. List<Map> maps =
  417. await dbClient!.query('Pedido', where: 'id = ?', whereArgs: [id]);
  418. if (maps.isNotEmpty) {
  419. return Pedido.fromJson(Map<String, dynamic>.from(maps.first));
  420. }
  421. return null;
  422. }
  423. Future<List<PedidoProducto>> obtenerPorIdPedido(int idPedido) async {
  424. Database? dbClient = await db;
  425. List<Map> maps = await dbClient!
  426. .query('PedidoProducto', where: 'idPedido = ?', whereArgs: [idPedido]);
  427. return maps
  428. .map((map) => PedidoProducto.fromJson(Map<String, dynamic>.from(map)))
  429. .toList();
  430. }
  431. Future<int> contarPedidos() async {
  432. Database? dbClient = await db;
  433. var result = await dbClient!.rawQuery('SELECT COUNT(*) FROM Pedido');
  434. return Sqflite.firstIntValue(result) ?? 0;
  435. }
  436. Future<List<Pedido>> obtenerPedidosPaginados(int limit, int offset) async {
  437. Database? dbClient = await db;
  438. List<Map<String, dynamic>> result = await dbClient!
  439. .query('Pedido', limit: limit, offset: offset, orderBy: 'id DESC');
  440. return result.map((map) => Pedido.fromJson(map)).toList();
  441. }
  442. Future<Producto?> obtenerProductoPorId(int idProducto) async {
  443. Database? dbClient = await db;
  444. List<Map> maps = await dbClient!
  445. .query('Producto', where: 'id = ?', whereArgs: [idProducto]);
  446. if (maps.isNotEmpty) {
  447. return Producto.fromJson(Map<String, dynamic>.from(maps.first));
  448. }
  449. return null;
  450. }
  451. Future<List<int>> obtenerToppingsPorProducto(int idProducto) async {
  452. var dbClient = await db;
  453. var result = await dbClient!.query(
  454. 'ProductoTopping',
  455. where: 'idProducto = ?',
  456. whereArgs: [idProducto],
  457. );
  458. return result.map((map) => map['idTopping'] as int).toList();
  459. }
  460. Future<List<PedidoProductoTopping>> obtenerToppingsPorPedidoProducto(
  461. int idPedidoProducto) async {
  462. var dbClient = await db;
  463. List<Map> maps = await dbClient!.query('PedidoProductoTopping',
  464. where: 'idPedidoProducto = ?', whereArgs: [idPedidoProducto]);
  465. if (maps.isNotEmpty) {
  466. return maps
  467. .map((map) =>
  468. PedidoProductoTopping.fromJson(Map<String, dynamic>.from(map)))
  469. .toList();
  470. }
  471. return [];
  472. }
  473. Future<int> obtenerProximoFolio() async {
  474. var dbClient = await db;
  475. var result = await dbClient!.rawQuery(
  476. 'SELECT MAX(CAST(folio AS INTEGER)) as last_folio FROM Pedido');
  477. if (result.isNotEmpty && result.first["last_folio"] != null) {
  478. return int.tryParse(result.first["last_folio"].toString())! + 1;
  479. }
  480. return 1;
  481. }
  482. Future<List<T>> buscarPorFolio(String folio) async {
  483. var dbClient = await db;
  484. List<Map<String, dynamic>> maps = await dbClient!.query(
  485. 'Pedido',
  486. where: 'folio LIKE ?',
  487. whereArgs: ['%$folio%'],
  488. );
  489. return maps.map((map) => fromMap<T>(map)).toList();
  490. }
  491. Future<List<Pedido>> buscarPorFecha(
  492. DateTime startDate, DateTime endDate) async {
  493. var dbClient = await db;
  494. String startDateString =
  495. DateFormat('yyyy-MM-dd 00:00:00').format(startDate);
  496. String endDateString = DateFormat('yyyy-MM-dd 23:59:59').format(endDate);
  497. List<Map<String, dynamic>> maps = await dbClient!.rawQuery('''
  498. SELECT * FROM Pedido
  499. WHERE
  500. (datetime(substr(peticion, 7, 4) || '-' || substr(peticion, 4, 2) || '-' || substr(peticion, 1, 2) || ' ' || substr(peticion, 12)) BETWEEN ? AND ?)
  501. OR
  502. (datetime(substr(peticion, 7, 4) || '-' || substr(peticion, 1, 2) || '-' || substr(peticion, 4, 2) || ' ' || substr(peticion, 12)) BETWEEN ? AND ?)
  503. ''', [startDateString, endDateString, startDateString, endDateString]);
  504. return maps.map((map) => Pedido.fromJson(map)).toList();
  505. }
  506. Future<List<CorteCaja>> buscarPorFechaCorte(
  507. DateTime startDate, DateTime endDate) async {
  508. var dbClient = await db;
  509. String startDateString = DateFormat('dd-MM-yyyy').format(startDate);
  510. String endDateString = DateFormat('dd-MM-yyyy 23:59:59').format(endDate);
  511. List<Map<String, dynamic>> maps = await dbClient!.query(
  512. 'CorteCaja',
  513. where: 'fecha BETWEEN ? AND ?',
  514. whereArgs: [startDateString, endDateString],
  515. );
  516. return maps.map((map) => CorteCaja.fromJson(map)).toList();
  517. }
  518. Future<void> eliminar<T>(int id) async {
  519. var dbClient = await db;
  520. String tableName = T.toString();
  521. await dbClient!.delete(tableName, where: 'id = ?', whereArgs: [id]);
  522. }
  523. Future<List<Descuento>> obtenerTodosDescuentos() async {
  524. var dbClient = await db;
  525. var result = await dbClient!.query('Descuento', orderBy: 'porcentaje ASC');
  526. return result.map((map) => Descuento.fromJson(map)).toList();
  527. }
  528. Future<int> guardarDescuento(Descuento descuento) async {
  529. var dbClient = await db;
  530. if (descuento.id != null && descuento.id! > 0) {
  531. return await dbClient!.update(
  532. 'Descuento',
  533. descuento.toJson(),
  534. where: 'id = ?',
  535. whereArgs: [descuento.id],
  536. );
  537. } else {
  538. return await dbClient!.insert('Descuento', descuento.toJson());
  539. }
  540. }
  541. Future<int> eliminarDescuento(int id) async {
  542. var dbClient = await db;
  543. return await dbClient!
  544. .delete('Descuento', where: 'id = ?', whereArgs: [id]);
  545. }
  546. }