Compare commits

..

No commits in common. "57b8d6bcceb4206bf10ec7af1fd310a0b956a696" and "cad8dffbcf1a3ffdbf2d872ecff52e5b81bb40b2" have entirely different histories.

10 changed files with 128 additions and 284 deletions

1
.gitignore vendored
View File

@ -12,7 +12,6 @@ npm-debug.log
yarn-error.log yarn-error.log
.idea .idea
/resources/csv/exports/*.csv /resources/csv/exports/*.csv
/resources/csv/canastas/*.csv
/public/css/ /public/css/
/public/js/ /public/js/
/public/mix-manifest.json /public/mix-manifest.json

View File

@ -1,97 +0,0 @@
<?php
namespace App\Helpers;
use App\Proveedor;
use DatabaseSeeder;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use League\Csv\Reader;
class CanastaHelper
{
const FILA_HEADER = "Tipo";
const ULTIMA_FILA = "TOTAL";
public static function cargarCanasta($archivo) {
$csv = Reader::createFromPath(resource_path($archivo), 'r');
$csv->setDelimiter("|");
$iHeader = self::obtenerIndiceDeHeader($csv);
$csv->setHeaderOffset($iHeader);
$registros = $csv->getRecords();
$toInsert = [];
$categoria = '';
foreach($registros as $i => $registro){
//filas que están arriba del header
if ($i <= $iHeader){
continue;
}
//finalizar
if ($registro[self::FILA_HEADER] == self::ULTIMA_FILA) {
break;
}
//filas que no tienen tipo
if (!Arr::has($registro,self::FILA_HEADER)|| trim($registro[self::FILA_HEADER]) == ''){
var_dump("no hay tipo en la fila " . $i);
continue;
}
//saltear bono de transporte
if ($registro[self::FILA_HEADER] == "T"){
continue;
}
//obtener categoria
if ($registro['Producto'] == '') {
//es la pregunta de la copa?
if (Str::contains($registro[self::FILA_HEADER],"¿")) { continue; }
$categoria = $registro[self::FILA_HEADER];
continue;
}
//completar producto
$toInsert[] = [
'fila' => $i,
'categoria' => $categoria,
'nombre' => trim(str_replace('*', ' ',$registro['Producto'])),
'precio' => $registro['Precio'],
'proveedor_id' => self::obtenerProveedor($registro['Producto']),
'bono' => $registro[self::FILA_HEADER] == "B",
'requiere_notas'=> $registro[self::FILA_HEADER] =="PTC",
];
}
foreach (array_chunk($toInsert,DatabaseSeeder::CHUNK_SIZE) as $chunk)
{
DB::table('productos')->insert($chunk);
}
}
private static function obtenerIndiceDeHeader($csv){
$registros = $csv->getRecords();
$iheader = 0;
foreach ($registros as $i => $registro){
if (strtolower($registro[0]) == strtolower(self::FILA_HEADER)) {
$iheader = $i;
break;
}
}
return $iheader;
}
private static function obtenerProveedor($nombre) {
$result = null;
if (Str::contains($nombre,"*")){
$result = Proveedor::firstOrCreate([
'nombre' => 'Proveedor de economía solidaria',
'economia_solidaria' => 1,
'nacional' => 1
])->id;
}
return $result;
}
}

View File

@ -3,14 +3,10 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\GrupoDeCompra; use App\GrupoDeCompra;
use App\Helpers\CanastaHelper;
use App\Producto; use App\Producto;
use Illuminate\Http\Request;
class ComprasController class ComprasController
{ {
const CANASTAS_PATH = 'csv/canastas/';
public function indexPedidos() { public function indexPedidos() {
return view('compras_pedidos'); return view('compras_pedidos');
} }
@ -37,21 +33,4 @@ class ComprasController
{ {
return view('auth/compras_login'); return view('auth/compras_login');
} }
public function cargarCanasta(Request $request)
{
$request->validate([
'data' => 'required|file|mimes:csv,txt|max:2048',
]);
$data = $request->file('data');
$nombre = $data->getClientOriginalName();
$data->move(resource_path(self::CANASTAS_PATH), $nombre);
CanastaHelper::cargarCanasta(self::CANASTAS_PATH . $nombre);
return response()->json([
'message' => 'Canasta cargada exitosamente',
], 200);
}
} }

View File

@ -1,13 +1,13 @@
<?php <?php
use App\Helpers\CanastaHelper;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
use League\Csv\Reader; use League\Csv\Reader;
use App\Proveedor; use App\Proveedor;
class CanastaSeeder extends Seeder class CanastaSeeder extends Seeder
{ {
const ARCHIVO_DEFAULT = 'csv/productos.csv'; const FILA_HEADER = "Tipo";
const ULTIMA_FILA = "TOTAL";
/** /**
* Run the database seeds. * Run the database seeds.
@ -16,6 +16,83 @@ class CanastaSeeder extends Seeder
*/ */
public function run() public function run()
{ {
CanastaHelper::cargarCanasta(self::ARCHIVO_DEFAULT); $csv = Reader::createFromPath(resource_path('csv/productos.csv'), 'r');
$csv->setDelimiter("|");
$iHeader = $this->obtenerIndiceDeHeader($csv);
$csv->setHeaderOffset($iHeader);
$registros = $csv->getRecords();
$toInsert = [];
$categoria = '';
foreach($registros as $i => $registro){
//filas que están arriba del header
if ($i <= $iHeader){
continue;
}
//finalizar
if ($registro[$this::FILA_HEADER] == $this::ULTIMA_FILA){
break;
}
//filas que no tienen tipo
if (!Arr::has($registro,$this::FILA_HEADER)|| trim($registro[$this::FILA_HEADER]) == ''){
var_dump("no hay tipo en la fila " . $i);
continue;
}
//saltear bono de transporte
if ($registro[$this::FILA_HEADER] == "T"){
continue;
}
//obtener categoria
if ($registro['Producto'] == '') {
//es la pregunta de la copa?
if (Str::contains($registro[$this::FILA_HEADER],"¿")) { continue; }
$categoria = $registro[$this::FILA_HEADER];
continue;
}
//completar producto
$toInsert[] = [
'fila' => $i,
'categoria' => $categoria,
'nombre' => trim(str_replace('*', ' ',$registro['Producto'])),
'precio' => $registro['Precio'],
'proveedor_id' => $this->obtenerProveedor($registro['Producto']),
'bono' => $registro[$this::FILA_HEADER] == "B",
'requiere_notas'=> $registro[$this::FILA_HEADER] =="PTC",
];
}
foreach (array_chunk($toInsert,DatabaseSeeder::CHUNK_SIZE) as $chunk)
{
DB::table('productos')->insert($chunk);
}
}
private function obtenerIndiceDeHeader($csv){
$registros = $csv->getRecords();
$iheader = 0;
foreach ($registros as $i => $registro){
if (strtolower($registro[0]) == strtolower($this::FILA_HEADER)) {
$iheader = $i;
break;
}
}
return $iheader;
}
private function obtenerProveedor($nombre) {
$result = null;
if (Str::contains($nombre,"*")){
$result = Proveedor::firstOrCreate([
'nombre' => 'Proveedor de economía solidaria',
'economia_solidaria' => 1,
'nacional' => 1
])->id;
}
return $result;
} }
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="block ml-3 mr-3 is-max-widescreen is-max-desktop"> <div class="block ml-3 mr-3 is-max-widescreen is-max-desktop">
<comunes-tabs-secciones :tabs="tabs" :tabInicial="tabActiva"></comunes-tabs-secciones> <admin-tabs-secciones></admin-tabs-secciones>
<div class="block" id="pedidos-seccion" <div class="block" id="pedidos-seccion"
:class="seccionActiva === 'pedidos-seccion' ? 'is-active' : 'is-hidden'"> :class="seccionActiva === 'pedidos-seccion' ? 'is-active' : 'is-hidden'">
<div class="block pb-6" id="pedidos-tabla-y-dropdown" v-show="hayPedidos"> <div class="block pb-6" id="pedidos-tabla-y-dropdown" v-show="hayPedidos">
@ -34,7 +34,7 @@
<script> <script>
import CaracteristicasOpcionales from "./CaracteristicasOpcionales.vue"; import CaracteristicasOpcionales from "./CaracteristicasOpcionales.vue";
import TabsSecciones from "../comunes/TabsSecciones.vue"; import TabsSecciones from "./TabsSecciones.vue";
import DropdownDescargar from "./DropdownDescargar.vue"; import DropdownDescargar from "./DropdownDescargar.vue";
import TablaPedidos from "./TablaPedidos.vue"; import TablaPedidos from "./TablaPedidos.vue";
import TablaBonos from "./TablaBonos.vue"; import TablaBonos from "./TablaBonos.vue";
@ -52,9 +52,6 @@ export default {
pedidos: [], pedidos: [],
bonosDeTransporte: 0, bonosDeTransporte: 0,
totalBonosBarriales: 0, totalBonosBarriales: 0,
tabs: [{ id: "pedidos", nombre: "Pedidos" },
{ id: "bonos", nombre: "Bonos" },
{ id: "caracteristicas", nombre: "Caracteristicas opcionales" }],
tabActiva: "pedidos", tabActiva: "pedidos",
seccionActiva: "pedidos-seccion", seccionActiva: "pedidos-seccion",
} }

View File

@ -19,13 +19,23 @@
<script> <script>
export default { export default {
props: {
tabs: Array,
tabInicial: String,
},
data() { data() {
return { return {
tabActiva: this.tabInicial, tabActiva: "pedidos",
tabs: [
{
id: "pedidos",
nombre: "Pedidos"
},
{
id: "bonos",
nombre: "Bonos"
},
{
id: "caracteristicas",
nombre: "Caracteristicas opcionales"
}
]
} }
}, },
methods: { methods: {

View File

@ -1,49 +1,42 @@
<template> <template>
<div class="block ml-3 mr-3 is-max-widescreen is-max-desktop"> <div class="container is-fluid has-text-centered">
<comunes-tabs-secciones :tabs="tabs" :tabInicial="tabActiva"></comunes-tabs-secciones> <div class="block">
<div class="block" id="pedidos-compras-seccion" <div class="field">
:class="seccionActiva === 'pedidos-compras-seccion' ? 'is-active' : 'is-hidden'"> <p class="control">
<div class="block pb-6" id="pedidos-compras-tabla-y-dropdown"> <a href="/compras/pedidos/descargar" class="button">
<compras-dropdown-descargar> <span class="icon is-small">
</compras-dropdown-descargar> <i class="fas fa-download"></i>
</span>
<span>Descargar planilla de totales</span>
</a>
</p>
</div> </div>
<div class="field">
<p class="control">
<a href="/compras/pedidos/notas" class="button">
<span class="icon is-small">
<i class="fas fa-sticky-note"></i>
</span>
<span>Descargar planilla de notas</span>
</a>
</p>
</div> </div>
<div class="block pb-6" id="canasta-compras-seccion" <div class="field">
:class="seccionActiva === 'canasta-compras-seccion' ? 'is-active' : 'is-hidden'"> <p class="control">
<div class="block" id="canasta-compras-seccion"> <a href="/compras/pedidos/transporte" class="button">
<div class="buttons is-right"> <span class="icon is-small">
<compras-canasta-input></compras-canasta-input> <i class="fa fa-truck"></i>
</div> </span>
<span>Descargar planilla de transporte</span>
</a>
</p>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import TabsSecciones from "../comunes/TabsSecciones.vue";
import DropdownDescargar from "./DropdownDescargar.vue";
import CanastaInput from "./CanastaInput.vue";
export default { export default {
components: {
TabsSecciones,
DropdownDescargar,
CanastaInput,
},
data() {
return {
tabs: [{ id: "pedidos-compras", nombre: "Pedidos" },
{ id: "canasta-compras", nombre: "Canasta" }],
tabActiva: "pedidos-compras",
seccionActiva: "pedidos-compras-seccion",
archivo: undefined,
}
},
methods: {
setSeccionActiva(tabId) {
this.tabActiva = tabId;
this.seccionActiva = tabId + "-seccion";
},
}
} }
</script> </script>

View File

@ -1,69 +0,0 @@
<template>
<div class="block">
<div class="file has-name is-right">
<label class="file-label">
<input
class="file-input"
type="file"
name="canasta"
@change="archivoSubido"
/>
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-cloud-upload-alt"></i>
</span>
<span class="file-label">Subir canasta</span>
</span>
<span class="file-name" v-if="archivo">
{{ archivo.nombre }}
</span>
</label>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "CanastaInput",
data() {
return {
archivo: null,
cargando: false,
};
},
methods: {
async archivoSubido(event) {
const archivo = event.target.files[0];
if (archivo && archivo.type === "text/csv") {
this.archivo = {data: archivo, nombre: archivo.name};
const formData = new FormData();
formData.append("data", this.archivo.data);
try {
this.cargando = true;
const response = await axios.post("/compras/canasta", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
this.$root.$toast(response.data.message || "Canasta cargada exitosamente");
} catch (error) {
this.$root.$toast(error.response?.data?.message || "Hubo errores.");
} finally {
this.cargando = false;
this.archivo = null;
}
} else {
this.$root.$toast("La canasta debe ser .CSV")
this.archivo = null;
}
},
},
};
</script>
<style scoped>
</style>

View File

@ -1,44 +0,0 @@
<template>
<div class="buttons is-right">
<div class="dropdown" :class="{'is-active': dropdownActivo}" @mouseleave="dropdownActivo = false">
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu" @click="dropdownActivo = !dropdownActivo">
<span class="icon is-small">
<i class="fas fa-download"></i>
</span>
<span>Descargar planillas</span>
<span class="icon is-small">
<i class="fas fa-angle-down" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
<a href="/compras/pedidos/descargar" class="dropdown-item">
Pedidos por barrio
</a>
<a href="/compras/pedidos/notas" class="dropdown-item">
Notas por barrio
</a>
<a href="/compras/pedidos/transporte" class="dropdown-item">
Transporte por barrio
</a>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
dropdownActivo: false
}
},
}
</script>
<style>
</style>

View File

@ -83,5 +83,4 @@ Route::middleware(['compras'])->group( function() {
Route::get('/compras/pedidos/descargar', 'ComprasController@descargarPedidos')->name('compras.pedidos.descargar'); Route::get('/compras/pedidos/descargar', 'ComprasController@descargarPedidos')->name('compras.pedidos.descargar');
Route::get('/compras/pedidos/notas', 'ComprasController@descargarNotas')->name('compras.pedidos.descargar'); Route::get('/compras/pedidos/notas', 'ComprasController@descargarNotas')->name('compras.pedidos.descargar');
Route::get('/compras/pedidos/transporte', 'ComprasController@descargarTransporte')->name('compras.pedidos.descargar'); Route::get('/compras/pedidos/transporte', 'ComprasController@descargarTransporte')->name('compras.pedidos.descargar');
Route::post('/compras/canasta', 'ComprasController@cargarCanasta')->name('compras.canasta');
}); });