From b752f9e8c5cd90200f755774328d76954d86c1d7 Mon Sep 17 00:00:00 2001 From: Rodrigo Date: Sun, 15 Jun 2025 00:19:30 -0300 Subject: [PATCH] Subir saldos como csv --- app/Http/Controllers/ComprasController.php | 36 +++++++++++ resources/js/components/compras/Body.vue | 62 ++++++++++++++++++- .../js/components/comunes/InputFileButton.vue | 56 +++++++++++++++++ routes/web.php | 1 + 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 resources/js/components/comunes/InputFileButton.vue diff --git a/app/Http/Controllers/ComprasController.php b/app/Http/Controllers/ComprasController.php index 93ef20a..40daea7 100644 --- a/app/Http/Controllers/ComprasController.php +++ b/app/Http/Controllers/ComprasController.php @@ -8,10 +8,14 @@ use App\Producto; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\BinaryFileResponse; +use League\Csv\Reader; +use DatabaseSeeder; class ComprasController { const CANASTAS_PATH = 'csv/canastas/'; + const BARRIO = "Barrio"; + const SALDO = "Saldo"; public function indexPedidos() { return view('compras_pedidos'); @@ -59,4 +63,36 @@ class ComprasController $file = resource_path('csv/productos.csv'); return response()->download($file); } + + public function cargarSaldos(Request $request): JsonResponse + { + $request->validate([ + 'data' => 'required|file|mimes:csv,txt|max:2048', + ]); + + $file = $request->file('data')->getPathname(); + $csv = Reader::createFromPath($file, 'r'); + try { + $csv->setDelimiter("|"); + $csv->setEnclosure("'"); + $csv->setHeaderOffset(0); + $records = $csv->getRecords(); + } catch (InvalidArgument|Exception $e) { + Log::error($e->getMessage()); + return response()->json([ + 'message' => 'No se pudo leer el csv', + ]); + } + + foreach ($records as $record) { + $barrio = $record[self::BARRIO]; + $saldo = $record[self::SALDO]; + GrupoDeCompra::where('nombre', $barrio) + ->update(['saldo' => $saldo]); + } + + return response()->json([ + 'message' => 'Saldos cargados exitosamente', + ]); + } } diff --git a/resources/js/components/compras/Body.vue b/resources/js/components/compras/Body.vue index 1045710..dc0a7c6 100644 --- a/resources/js/components/compras/Body.vue +++ b/resources/js/components/compras/Body.vue @@ -47,7 +47,38 @@ :class="seccionActiva === 'saldos-compras-seccion' ? 'is-active' : 'is-hidden'" >
- +
+
+

+ + Cargar saldos +

+
+
+
+ La planilla de saldos tiene que tener el siguiente formato para que la aplicación la lea correctamente: +
    +
  • Los valores deben usar punto y no coma decimal
  • +
  • El nombre de las columnas deben ser "Barrio" y "Saldo"
  • +
  • Las celdas deben separarse con '|'
  • +
  • No puede haber "enters" en ninguna celda
  • +
  • El nombre de los barrios debe estar exactamente igual que como están configurados en esta aplicacaión
  • +
+
+
+
+ Cargar un archivo de saldos sólo reemplazará los saldos de los barrios presentes en la tabla. +
+
+
+
+ +
+
+ +
@@ -84,6 +115,7 @@ import TabsSecciones from "../comunes/TabsSecciones.vue"; import DropdownDescargar from "./DropdownDescargar.vue"; import CanastaInput from "./CanastaInput.vue"; +import InputFileButton from "../comunes/InputFileButton.vue"; import { mapActions, mapState } from "vuex"; export default { @@ -104,6 +136,7 @@ export default { seccionActiva: "pedidos-compras-seccion", archivo: undefined, saldo_modificado: {}, + show_saldos_file_dialog: false, } }, methods: { @@ -112,6 +145,7 @@ export default { this.seccionActiva = tabId + "-seccion"; }, ...mapActions('comisiones', ['getGruposDeCompra', 'setSaldo']), + ...mapActions('ui',["toast"]), async confirmar_saldo(gdc_id) { var saldo = this.getSaldo(gdc_id); await this.setSaldo({ @@ -135,6 +169,32 @@ export default { } return 0; }, + async saldosSubido(event) { + var archivo = event.archivo; + if (archivo.type === "text/csv") { + const formData = new FormData(); + formData.append("data", archivo); + try { + const response = await axios.post("/compras/saldos", formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + this.getGruposDeCompra(); + this.toast({ mensaje: (response.data.message || "Canasta cargada exitosamente") }); + } catch (error) { + console.log(error); + this.toast({ mensaje: (error.response?.data?.message || "Hubo errores.") }); + } + event.component.cargando = false; + } else { + this.toast("El archivo debe ser .CSV"); + event.component.cargando = false; + } + }, + toggleSaldosFileDialog() { + this.show_saldos_file_dialog = !this.show_saldos_file_dialog; + }, }, computed: { ...mapState('comisiones', [ diff --git a/resources/js/components/comunes/InputFileButton.vue b/resources/js/components/comunes/InputFileButton.vue new file mode 100644 index 0000000..a9f70a4 --- /dev/null +++ b/resources/js/components/comunes/InputFileButton.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/routes/web.php b/routes/web.php index c45aebd..b963c10 100644 --- a/routes/web.php +++ b/routes/web.php @@ -54,4 +54,5 @@ Route::middleware(['auth', 'role:comision'])->group( function() { Route::get('/compras/pedidos/pdf', 'ComprasController@pdf')->name('compras.pedidos.pdf'); Route::get('/compras/canasta/ejemplo', 'ComprasController@descargarCanastaEjemplo')->name('compras.canasta.ejemplo'); Route::post('/compras/canasta', 'ComprasController@cargarCanasta')->name('compras.canasta'); + Route::post('/compras/saldos', 'ComprasController@cargarSaldos')->name('compras.canasta'); });
Barrio