Compare commits

..

28 commits

Author SHA1 Message Date
ale
e49c043231 Estilo 2025-06-23 22:49:49 -03:00
ale
a0ee4131c8 Agregada lógica de cantidad de ollas 2025-06-23 22:49:28 -03:00
ale
5a1d95c636 Api de ollas movido a web 2025-06-23 22:49:02 -03:00
ale
d261506775 Arreglada columna 2025-06-23 22:48:51 -03:00
ale
e58e19bfe1 Adaptada logica para usar PedidoOllasResource 2025-06-23 21:16:00 -03:00
ale
d86d52a478 Creado PedidoOllasResource 2025-06-23 21:15:30 -03:00
ale
0e42cf2c14 Agregado drop 2025-06-23 20:49:57 -03:00
ale
200c38cb29 Agregada columna y campo cantidad_ollas 2025-06-23 20:45:00 -03:00
ale
60c327f74b Agregado aviso de monto superado 2025-06-23 20:44:40 -03:00
ale
309ce637c8 Tipado de totales 2025-06-23 20:13:45 -03:00
ale
5c059057b9 formato 2025-06-23 20:12:44 -03:00
ale
8fa3f78280 Agregado modulo de ollas y logica para calcular monto total 2025-06-23 19:45:23 -03:00
ale
b6cd844ffd Cambio null por [] en productos 2025-06-23 19:36:03 -03:00
ale
3c14055006 Manejo de errores 2025-06-23 19:12:29 -03:00
ale
e37aa25b3d Agregados seccion, fila, y logica para obtener y modificar parametros desde comisiones 2025-06-23 19:12:19 -03:00
ale
d87091846d Agregados metodos, ruta, y csv para parametros de la aplicacion 2025-06-23 19:11:49 -03:00
ale
f520578525 Solo barrios con pedidos aprobados 2025-06-23 16:55:20 -03:00
ale
aaeee70c9e eliminada carpeta no uasda 2025-06-23 16:37:44 -03:00
ale
05dc58710d estilos 2025-06-23 16:34:07 -03:00
ale
5020d406bf formato 2025-06-23 16:20:15 -03:00
ale
6d6d8d4387 Cambio de nombre y movida burger 2025-06-23 16:18:28 -03:00
ale
ee96d89e55 Limpieza 2025-06-20 17:42:41 -03:00
ale
5d7d533083 Agregado input para cantidad de ollas 2025-06-20 17:34:46 -03:00
ale
b1b764fd7c Agregado slot a PedidosMain en vez de cartel aprobado fijo 2025-06-20 17:33:29 -03:00
ale
cc6b0f5a6e Formato + omitiendo pedidos de ollas de metodos que trabajan con pedidos 2025-06-20 17:04:23 -03:00
ale
db87c38e3b Devolviendo solamente pedidos de hogares 2025-06-20 17:03:52 -03:00
ale
8fb810c73f Omitiendo pedido de ollas de métodos que trabajan con pedidos 2025-06-20 17:03:35 -03:00
ale
208cb31b53 Manejo de erroes 2025-06-20 17:03:00 -03:00
35 changed files with 661 additions and 140 deletions

View file

@ -12,6 +12,7 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use League\Csv\Exception;
use Mpdf\MpdfException;
class GrupoDeCompra extends Model
{
@ -37,7 +38,14 @@ class GrupoDeCompra extends Model
public function pedidosAprobados()
{
return $this->subpedidos->where('aprobado', 1);
return $this->pedidosHogares()
->where('aprobado', 1);
}
public function pedidosHogares()
{
return $this->subpedidos
->where('tipo_pedido_id', '=', 1);
}
public function totalARecaudar()
@ -120,11 +128,15 @@ class GrupoDeCompra extends Model
return TransporteHelper::cantidadTransporte($this->totalCentralesQuePaganTransporte());
}
/**
* @throws MpdfException
*/
public function exportarPedidosAPdf()
{
$subpedidos = $this->pedidosAprobados();
$fecha = now()->format('Y-m-d');
PdfHelper::exportarPedidos($this->nombre . '-' . $fecha . '.pdf', $subpedidos);
$filepath = $this->nombre . '-' . $fecha . '.pdf';
PdfHelper::exportarPedidos($filepath, $subpedidos);
}
function pedidoParaPdf(): array
@ -154,11 +166,15 @@ class GrupoDeCompra extends Model
return $view->render();
}
/**
* @throws MpdfException
*/
public static function exportarPedidosBarrialesAPdf()
{
$barrios = GrupoDeCompra::barriosMenosPrueba()->get();
$barrios = GrupoDeCompra::barriosMenosPruebaConPedidosAprobados()->get();
$fecha = now()->format('Y-m-d');
PdfHelper::exportarPedidos('pedidos_por_barrio-' . $fecha . '.pdf', $barrios);
$filepath = 'pedidos_por_barrio-' . $fecha . '.pdf';
PdfHelper::exportarPedidos($filepath, $barrios);
}
static function filaVacia(string $product, int $columns): array
@ -276,9 +292,12 @@ class GrupoDeCompra extends Model
return array($records, $i, $cantidad);
}
public static function barriosMenosPrueba(): Builder
public static function barriosMenosPruebaConPedidosAprobados(): Builder
{
return self::where('nombre', '<>', 'PRUEBA')
->whereHas('subpedidos', function ($query) {
$query->where('aprobado', true);
})
->orderBy('region')
->orderBy('nombre');
}
@ -286,7 +305,7 @@ class GrupoDeCompra extends Model
public static function transportePorBarrio(): array
{
$result = [];
$barrios = GrupoDeCompra::barriosMenosPrueba()->get();
$barrios = GrupoDeCompra::barriosMenosPruebaConPedidosAprobados()->get();
foreach ($barrios as $barrio) {
$result[] = $barrio->cantidadTransporte();

View file

@ -2,6 +2,7 @@
namespace App\Helpers;
use App\Http\Controllers\ComisionesController;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Iterator;
@ -13,15 +14,13 @@ use League\Csv\Writer;
class CsvHelper
{
/**
* @throws Exception
*/
public static function getRecords($filePath, $message): Iterator {
$csv = Reader::createFromPath($filePath);
try {
$csv->setDelimiter("|");
$csv->setEnclosure("'");
$csv->setHeaderOffset(0);
$csv = self::getReader($filePath);
return $csv->getRecords();
} catch (InvalidArgument|Exception $e) {
Log::error($e->getMessage());
@ -29,20 +28,74 @@ class CsvHelper
}
}
public static function generarCsv($filePath, $contenido, $headers = null): void
/**
* @throws Exception
*/
public static function cambiarParametro(string $id, string $valor): void
{
if (!File::exists(storage_path('csv/exports'))) {
File::makeDirectory(storage_path('csv/exports'), 0755, true);
}
try {
$writer = Writer::createFromPath(storage_path($filePath), 'w');
if ($headers) {
$writer->insertOne($headers);
$updated = false;
$filePath = resource_path(ComisionesController::PARAMETROS_PATH);
$csv = self::getReader($filePath);
$headers = $csv->getHeader();
$records = array_map(fn($r) => (array) $r, iterator_to_array($csv->getRecords()));
foreach ($records as &$record) {
if ($record['id'] === $id) {
$record['valor'] = $valor;
$updated = true;
break;
}
}
$writer->insertAll($contenido);
} catch (CannotInsertRecord $e) {
Log::error($e->getMessage(), $e->getTrace());
unset($record);
if (!$updated)
throw new Exception("Parametro '{$id}' no encontrado.");
self::generarCsv($filePath, $records, $headers, "|", "'", false);
} catch (CannotInsertRecord | InvalidArgument $e) {
Log::error("Error al actualizar csv: " . $e->getMessage());
throw new Exception("Error al actualizar csv", $e);
}
}
/**
* @throws InvalidArgument
* @throws CannotInsertRecord
*/
public static function generarCsv($filePath, $contenido, $headers = null, $delimiter = null, $enclosure = null, $export = true): void
{
$path = $filePath;
if ($export) {
if (!File::exists(storage_path('csv/exports')))
File::makeDirectory(storage_path('csv/exports'), 0755, true);
$path = storage_path($filePath);
}
$writer = Writer::createFromPath($path, 'w');
if ($delimiter)
$writer->setDelimiter($delimiter);
if ($enclosure)
$writer->setEnclosure($enclosure);
if ($headers)
$writer->insertOne($headers);
$writer->insertAll($contenido);
}
/**
* @param string $filePath
* @return Reader
* @throws InvalidArgument
* @throws Exception
*/
private static function getReader(string $filePath): Reader
{
$csv = Reader::createFromPath($filePath);
$csv->setDelimiter("|");
$csv->setEnclosure("'");
$csv->setHeaderOffset(0);
return $csv;
}
}

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\GrupoDeCompra;
use League\Csv\Exception;
use Mpdf\MpdfException;
class AdminController extends Controller
{
@ -17,7 +18,12 @@ class AdminController extends Controller
}
public function exportarPedidosAPdf(GrupoDeCompra $gdc) {
$gdc->exportarPedidosAPdf();
try {
$gdc->exportarPedidosAPdf();
return response();
} catch (MpdfException $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
}
public function exportarPedidoACSV(GrupoDeCompra $gdc)

View file

@ -1,21 +0,0 @@
<?php
namespace App\Http\Controllers\Api;
use App\GrupoDeCompra;
use App\Http\Controllers\Controller;
use App\Http\Resources\SubpedidoResource;
use App\TipoPedido;
class OllasController extends Controller
{
public function pedido(GrupoDeCompra $gdc)
{
$tipoOlla = TipoPedido::firstOrCreate(['nombre' => 'olla']);
$pedido = $gdc->subpedidos()->firstOrCreate([
'nombre' => 'Pedido de Ollas de ' . $gdc->nombre,
'tipo_pedido_id' => $tipoOlla->id,
]);
return response()->json(new SubpedidoResource($pedido));
}
}

View file

@ -10,10 +10,12 @@ use App\Producto;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use League\Csv\Exception;
use Mpdf\MpdfException;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class ComisionesController
{
const PARAMETROS_PATH = 'csv/parametros.csv';
const CANASTAS_PATH = 'csv/canastas/';
const BARRIO = "Barrio";
const SALDO = "Saldo";
@ -54,7 +56,12 @@ class ComisionesController
}
public function pdf() {
GrupoDeCompra::exportarPedidosBarrialesAPdf();
try {
GrupoDeCompra::exportarPedidosBarrialesAPdf();
return response();
} catch (MpdfException $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
}
public function cargarCanasta(Request $request): JsonResponse
@ -104,4 +111,42 @@ class ComisionesController
return response()->json(GrupoDeCompraResource::collection(GrupoDeCompra::all()));
}
public function obtenerParametros()
{
try {
$records = self::parametrosRecords();
$result = [];
foreach ($records as $record)
$result[] = $record;
return response()->json($result);
} catch (Exception $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
}
public function modificarParametros(string $parametro_id, Request $request) {
try {
if (collect(self::parametrosRecords())
->contains('id', $parametro_id)) {
$valid = $request->validate([
'valor' => ['required', 'numeric', 'gte:0'],
]);
CsvHelper::cambiarParametro($parametro_id, $valid['valor']);
return response()->noContent();
}
return response()->json(['message' => 'Parametro no encontrado.'], 404);
} catch (Exception $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
}
/**
* @throws Exception
*/
private static function parametrosRecords(): array
{
$records = CsvHelper::getRecords(resource_path(self::PARAMETROS_PATH), "No se pudo leer el archivo.");
return iterator_to_array($records);
}
}

View file

@ -2,10 +2,38 @@
namespace App\Http\Controllers;
use App\GrupoDeCompra;
use App\Http\Resources\PedidoOllasResource;
use App\TipoPedido;
use Illuminate\Http\Request;
class OllasController extends Controller
{
public function show()
{
return view('auth/login');
}
public function pedido(GrupoDeCompra $gdc)
{
$tipoOlla = TipoPedido::firstOrCreate(['nombre' => 'olla']);
$pedido = $gdc->subpedidos()->firstOrCreate([
'nombre' => 'Pedido de Ollas de ' . $gdc->nombre,
'tipo_pedido_id' => $tipoOlla->id,
]);
return response()->json(new PedidoOllasResource($pedido));
}
public function actualizarCantidadOllas(GrupoDeCompra $gdc, Request $request)
{
$valid = $request->validate([
'cantidad' => 'required|numeric|min:0',
]);
$pedido = $gdc->subpedidos()->where([
'nombre' => 'Pedido de Ollas de ' . $gdc->nombre
])->first();
$pedido->cantidad_ollas = $valid['cantidad'];
$pedido->save();
return response()->noContent();
}
}

View file

@ -19,7 +19,7 @@ class GrupoDeCompraResource extends JsonResource
'id' => $this->id,
'nombre' => $this->nombre,
'devoluciones_habilitadas' => $this->devoluciones_habilitadas,
'pedidos' => SubpedidoResource::collection($this->subpedidos),
'pedidos' => SubpedidoResource::collection($this->pedidosHogares()),
'total_a_recaudar' => number_format($this->totalARecaudar(),2),
'saldo' => number_format($this->saldo, 2, ".", ""),
'total_sin_devoluciones' => number_format($this->totalSinDevoluciones(),2),

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Resources;
use App\TipoPedido;
use Illuminate\Http\Resources\Json\JsonResource;
class PedidoOllasResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request): array
{
$productos = $this->productos;
foreach ($productos as $producto) {
$producto['pivot']['total'] = number_format($producto->pivot->cantidad * $producto->precio, 2);
}
return [
'id' => $this->id,
'nombre' => $this->nombre,
'productos' => $productos,
'total' => number_format($this->totalCentralesSinTransporte(),2),
'cantidad_de_ollas' => $this->cantidad_ollas,
];
}
}

View file

@ -11,7 +11,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use League\Csv\CannotInsertRecord;
use League\Csv\Exception;
use League\Csv\InvalidArgument;
class Producto extends Model
{
@ -19,15 +21,10 @@ class Producto extends Model
public function subpedidos(): BelongsToMany
{
return $this->belongsToMany(Subpedido::class, 'productos_subpedidos')->withPivot(["cantidad", "notas"]);
return $this->belongsToMany(Subpedido::class, 'productos_subpedidos')
->withPivot(["cantidad", "notas"]);
}
public static function noBarriales()
{
return self::where('nombre', 'not like', '%barrial%');
}
// Este método permite que se apliquen los filtros al hacer una request (por ejemplo, de búsqueda)
public function scopeFiltrar($query, FiltroDeProducto $filtros): Builder
{
return $filtros->aplicar($query);
@ -35,7 +32,9 @@ class Producto extends Model
public static function getPaginar(Request $request): int
{
return $request->has('paginar') && intval($request->input('paginar')) ? intval($request->input('paginar')) : self::all()->count();
return $request->has('paginar') && intval($request->input('paginar')) ?
intval($request->input('paginar')) :
self::all()->count();
}
public static function productosFilaID()
@ -53,19 +52,30 @@ class Producto extends Model
return self::noBarriales()->pluck('nombre', 'id')->all();
}
public static function noBarriales()
{
return self::where('nombre', 'not like', '%barrial%');
}
static public function cantidadesPorBarrio(): Collection
{
$barrios = GrupoDeCompra::barriosMenosPrueba()
->pluck('id', 'nombre');
$barrios = GrupoDeCompra::barriosMenosPruebaConPedidosAprobados()->pluck('id', 'nombre');
$columnasBarrios = $barrios->map(function ($id, $nombre) {
return DB::raw("SUM(CASE WHEN subpedidos.grupo_de_compra_id = $id AND subpedidos.aprobado = 1 THEN producto_subpedido.cantidad ELSE 0 END) as `$nombre`");
return DB::raw("
SUM(CASE WHEN subpedidos.grupo_de_compra_id = $id
AND subpedidos.aprobado = 1
AND subpedidos.tipo_pedido_id = 1
THEN producto_subpedido.cantidad
ELSE 0 END)
as `$nombre`");
})->toArray();
return DB::table('productos')
->where('productos.nombre', 'not like', '%barrial%')
->leftJoin('producto_subpedido', 'productos.id', '=', 'producto_subpedido.producto_id')
->leftJoin('subpedidos', 'subpedidos.id', '=', 'producto_subpedido.subpedido_id')
->where('subpedidos.tipo_pedido_id', '=', 1)
->select(array_merge(
['productos.fila as fila'],
['productos.nombre as producto'],
@ -82,8 +92,7 @@ class Producto extends Model
static public function planillaTotales()
{
$headers = ['Producto'];
$barrios = GrupoDeCompra::barriosMenosPrueba()
->pluck('nombre')->toArray();
$barrios = GrupoDeCompra::barriosMenosPruebaConPedidosAprobados()->pluck('nombre')->toArray();
$headers = array_merge($headers, $barrios);
$cantidadesPorBarrio = self::cantidadesPorBarrio();
@ -109,21 +118,24 @@ class Producto extends Model
$ultimaFila = $fila;
}
foreach ($transportePorBarrio as $key => $cantidad) {
foreach ($transportePorBarrio as $cantidad) {
$planilla[$filaTransporte][] = $cantidad;
}
$fecha = now()->format('Y-m-d');
CsvHelper::generarCsv('csv/exports/pedidos-por-barrio- ' . $fecha . '.csv', $planilla, $headers);
$filePath = 'csv/exports/pedidos-por-barrio- ' . $fecha . '.csv';
CsvHelper::generarCsv($filePath, $planilla, $headers);
}
public static function notasPorBarrio(): Collection
{
return DB::table('productos')
->where('productos.nombre', 'not like', '%barrial%')
->join('producto_subpedido', 'productos.id', '=', 'producto_subpedido.producto_id')
->join('subpedidos', 'producto_subpedido.subpedido_id', '=', 'subpedidos.id')
->join('grupos_de_compra', 'subpedidos.grupo_de_compra_id', '=', 'grupos_de_compra.id')
->where('productos.requiere_notas', 1)
->where('subpedidos.tipo_pedido_id', '=', 1)
->select(
'productos.nombre as producto',
'grupos_de_compra.nombre as barrio',
@ -133,9 +145,13 @@ class Producto extends Model
->groupBy('producto');
}
/**
* @throws InvalidArgument
* @throws CannotInsertRecord
*/
static public function planillaNotas() {
$headers = ['Producto'];
$barrios = GrupoDeCompra::barriosMenosPrueba()
$barrios = GrupoDeCompra::barriosMenosPruebaConPedidosAprobados()
->pluck('nombre')->toArray();
$headers = array_merge($headers, $barrios);
@ -145,13 +161,16 @@ class Producto extends Model
foreach ($notasPorBarrio as $producto => $notasGrupo) {
$fila = [$producto];
foreach ($barrios as $barrio) {
$notas = $notasGrupo->where('barrio', $barrio)->pluck('notas')->implode('; ');
$notas = $notasGrupo->where('barrio', $barrio)
->pluck('notas')
->implode('; ');
$fila[] = $notas ?: '';
}
$planilla[] = $fila;
}
$fecha = now()->format('Y-m-d');
CsvHelper::generarCsv('csv/exports/notas-por-barrio-' . $fecha . '.csv', $planilla, $headers);
$filePath = 'csv/exports/notas-por-barrio-' . $fecha . '.csv';
CsvHelper::generarCsv($filePath, $planilla, $headers);
}
}

View file

@ -12,7 +12,15 @@ use App\Filtros\FiltroDeSubpedido;
class Subpedido extends Model
{
protected $fillable = ['grupo_de_compra_id', 'aprobado', 'nombre', 'devoluciones_total', 'devoluciones_notas', 'tipo_pedido_id'];
protected $fillable = [
'grupo_de_compra_id',
'aprobado',
'nombre',
'devoluciones_total',
'devoluciones_notas',
'tipo_pedido_id',
'cantidad_ollas'
];
public function productos(): BelongsToMany
{
@ -57,7 +65,11 @@ class Subpedido extends Model
public function totalCentral()
{
return $this->totalCentralesQueNoPaganTransporte() + $this->totalCentralesQuePaganTransporte() + $this->totalTransporte();
return $this->totalCentralesSinTransporte() + $this->totalTransporte();
}
public function totalCentralesSinTransporte() {
return $this->totalCentralesQueNoPaganTransporte() + $this->totalCentralesQuePaganTransporte();
}
public function totalCentralesQueNoPaganTransporte()

View file

@ -0,0 +1,34 @@
<?php
use App\Subpedido;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CantidadOllasPedidos extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('subpedidos', function (Blueprint $table) {
$table->integer('cantidad_ollas')->nullable();
});
Subpedido::where('tipo_pedido_id', 1)->update(['cantidad_ollas' => 0]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('subpedidos', function (Blueprint $table) {
$table->dropColumn('cantidad_ollas');
});
}
}

View file

View file

@ -0,0 +1,3 @@
id|nombre|valor
bono-transporte|'Bono de transporte'|15
monto-olla|'Monto por olla'|1200
1 id nombre valor
2 bono-transporte 'Bono de transporte' 15
3 monto-olla 'Monto por olla' 1200

View file

@ -1,10 +1,8 @@
<script>
import { mapGetters, mapMutations, mapState } from "vuex";
import Burger from "../comunes/Burger.vue";
export default {
name: "AdminNavBar",
components: { Burger },
name: "AdminNavBarBrand",
computed: {
...mapState('ui', ["burger_activa"]),
...mapGetters('admin', ["barrio"])
@ -20,7 +18,6 @@ export default {
<p>
{{`Barrio: ${barrio}`}}
</p>
<burger/>
</div>
</template>

View file

@ -18,6 +18,11 @@
:class="seccionActiva === 'saldos-comisiones-seccion' ? 'is-active' : 'is-hidden'">
<saldos-seccion/>
</div>
<div class="block pb-6"
id="parametros-comisiones-seccion"
:class="seccionActiva === 'parametros-comisiones-seccion' ? 'is-active' : 'is-hidden'">
<parametros-seccion/>
</div>
</div>
</template>
@ -27,11 +32,12 @@ import DropdownDescargar from "./DropdownDescargar.vue";
import InputFileButton from "../comunes/InputFileButton.vue";
import CanastaSeccion from "./canasta/CanastaSeccion.vue";
import SaldosSeccion from "./saldos/SaldosSeccion.vue";
import { mapActions } from "vuex";
import ParametrosSeccion from "./parametros/ParametrosSeccion.vue";
export default {
name: "ComisionesBody",
components: {
ParametrosSeccion,
SaldosSeccion,
CanastaSeccion,
TabsSecciones,
@ -44,6 +50,7 @@ export default {
{ id: "pedidos-comisiones", nombre: "Pedidos" },
{ id: "canasta-comisiones", nombre: "Canasta" },
{ id: "saldos-comisiones", nombre: "Saldos" },
{ id: "parametros-comisiones", nombre: "Parámetros" },
],
tabActiva: "pedidos-comisiones",
seccionActiva: "pedidos-comisiones-seccion",

View file

@ -1,10 +1,8 @@
<script>
import { mapMutations, mapState } from "vuex";
import Burger from "../comunes/Burger.vue";
export default {
name: "ComisionesNavBar",
components: { Burger },
name: "ComisionesNavBarBrand",
computed: {
...mapState('ui', ["burger_activa"])
},
@ -26,7 +24,6 @@ export default {
<template>
<div class="navbar-item hide-below-1024">
<p>{{ nombre }}</p>
<burger/>
</div>
</template>

View file

@ -0,0 +1,71 @@
<script>
import {mapActions, mapGetters} from "vuex";
export default {
props: {
parametro: {
type: Object,
required: true,
}
},
data() {
return {
control: this.parametro.valor,
};
},
computed: {
hayCambios() {
return this.control !== this.parametro.valor;
}
},
methods: {
...mapActions("comisiones", ["cambiarParametro"]),
modificar() {
this.cambiarParametro({
parametro_id: this.parametro.id,
valor: this.control,
});
}
}
}
</script>
<template>
<tr>
<td>{{ parametro.nombre }}</td>
<td>
<div class="field">
<input :type="parametro.tipo"
:id="'input-' + parametro.id"
v-model="control"
class="has-text-right">
</div>
</td>
<td class="has-text-centered">
<div class="control">
<button class="button is-small is-success"
@click="modificar"
:disabled="!hayCambios">
<span class="icon">
<i class="fas fa-check"></i>
</span>
</button>
</div>
</td>
<td class="has-text-centered">
<div class="control">
<button class="button is-small is-danger"
@click="control = parametro.valor"
:disabled="!hayCambios">
<span class="icon">
<i class="fa fa-undo" aria-hidden="true"></i>
</span>
</button>
</div>
</td>
</tr>
</template>
<style scoped>
</style>

View file

@ -0,0 +1,41 @@
<script>
import FilaCaracteristica from "../../admin/FilaCaracteristica.vue";
import FilaParametro from "./FilaParametro.vue";
import { mapActions, mapState } from "vuex";
export default {
name:"ParametrosSeccion",
components: { FilaParametro, FilaCaracteristica },
methods: {
...mapActions("comisiones", ["getParametros"]),
},
computed: {
...mapState("comisiones", ["parametros"]),
},
async mounted() {
await this.getParametros();
},
}
</script>
<template>
<table class="table is-striped is-bordered">
<thead>
<tr>
<th> Parámetro </th>
<th> Valor $ </th>
<th> Cambiar </th>
<th> Deshacer </th>
</tr>
</thead>
<tbody>
<fila-parametro v-for="(p,i) in parametros"
:key="i"
:parametro="p"/>
</tbody>
</table>
</template>
<style scoped>
</style>

View file

@ -7,13 +7,20 @@
<a class="navbar-item" href="https://mps.org.uy">
<img src="/assets/logoMPS.png" height="28" alt="Logo del MPS">
</a>
<pedidos-nav-bar v-if="rol === 'barrio'"/>
<ollas-nav-bar v-else-if="rol === 'ollas'"/>
<admin-nav-bar v-else-if="rol === 'admin_barrio'"/>
<comisiones-nav-bar v-else/>
<pedidos-nav-bar-brand v-if="rol === 'barrio'"/>
<ollas-nav-bar-brand v-else-if="rol === 'ollas'"/>
<admin-nav-bar-brand v-else-if="rol === 'admin_barrio'"/>
<comisiones-nav-bar-brand v-else/>
<burger/>
</div>
<div class="navbar-menu"
:class="{'is-active': burger_activa}">
<div class="navbar-menu" :class="{'is-active': burger_activa}">
<div class="navbar-start is-flex is-justify-content-center is-flex-grow-1">
<div v-if="mostrarAviso" class="is-absolute-center is-flex is-align-items-center navbar-item">
<span class="tag is-warning is-size-6">
Monto superado
</span>
</div>
</div>
<div class="navbar-end">
<buscador v-if="pedidoDefinido"/>
<chismosa-dropdown v-if="pedidoDefinido"
@ -32,18 +39,23 @@
<script>
import ChismosaDropdown from '../pedidos/ChismosaDropdown.vue';
import { mapGetters, mapState } from "vuex";
import PedidosNavBar from "../pedidos/PedidosNavBar.vue";
import ComisionesNavBar from "../comisiones/ComisionesNavBar.vue";
import AdminNavBar from "../admin/AdminNavBar.vue";
import OllasNavBar from "../ollas/OllasNavBar.vue";
import PedidosNavBarBrand from "../pedidos/PedidosNavBarBrand.vue";
import ComisionesNavBarBrand from "../comisiones/ComisionesNavBarBrand.vue";
import AdminNavBarBrand from "../admin/AdminNavBarBrand.vue";
import OllasNavBarBrand from "../ollas/OllasNavBarBrand.vue";
import Buscador from "../pedidos/Buscador.vue";
import Burger from "./Burger.vue";
export default {
components: { Buscador, OllasNavBar, AdminNavBar, ComisionesNavBar, PedidosNavBar, ChismosaDropdown },
components: { Burger, Buscador, OllasNavBarBrand, AdminNavBarBrand, ComisionesNavBarBrand, PedidosNavBarBrand, ChismosaDropdown },
computed: {
...mapGetters('pedido', ["pedidoDefinido"]),
...mapGetters('ollas', ["montoSuperado"]),
...mapState('login', ["rol"]),
...mapState('ui', ["burger_activa"])
...mapState('ui', ["burger_activa"]),
mostrarAviso() {
return this.pedidoDefinido && this.rol === 'ollas' && this.montoSuperado;
}
},
methods: {
logOut() {
@ -55,4 +67,9 @@ export default {
</script>
<style>
.is-absolute-center {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
</style>

View file

@ -0,0 +1,71 @@
<script>
import { mapActions, mapMutations, mapState } from "vuex";
export default {
name: "CantidadOllas",
methods: {
...mapActions("ollas", ["actualizarCantidadOllas"]),
...mapMutations("pedido", ["setCantidadOllas"]),
incrementar() {
this.control += 1;
this.actualizarDebounced();
},
decrementar() {
this.control -= 1;
this.actualizarDebounced();
},
async actualizarDebounced() {
const params = { cantidad: this.control };
clearTimeout(this.debounceTimer);
this.debounceTimer = setTimeout(() => {
this.setCantidadOllas(params);
this.actualizarCantidadOllas(params);
}, 500);
}
},
computed: {
...mapState("pedido", ["cantidad_de_ollas"])
},
data() {
return {
debounceTimer: null,
control: 0,
};
},
watch: {
cantidad_de_ollas() {
this.control = this.cantidad_de_ollas;
}
},
}
</script>
<template>
<div class="notification has-text-centered">
<label class="label">Cantidad de ollas:</label>
<div class="field has-addons is-justify-content-center contador">
<div class="control">
<button class="button is-small"
:disabled="control < 1"
@click.capture="decrementar">
<i class="fa fa-solid fa-minus"></i>
</button>
</div>
<div class="control">
<input id="cantidad"
v-model="control"
class="input is-small has-text-centered"
type="number"
@input="actualizarDebounced">
</div>
<div class="control">
<button class="button is-small" @click="incrementar">
<i class="fa fa-solid fa-plus"></i>
</button>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View file

@ -1,10 +1,11 @@
<script>
import PedidosMain from "../pedidos/PedidosMain.vue";
import { mapActions, mapMutations } from "vuex";
import CantidadOllas from "./CantidadOllas.vue";
export default {
name: "OllasBody",
components: { PedidosMain },
components: { CantidadOllas, PedidosMain },
methods: {
...mapActions('pedido', ["getPedidoDeOllas", "getGrupoDeCompra"]),
...mapMutations('ui', ["migasOllas"])
@ -19,7 +20,11 @@ export default {
<template>
<div id="ollas-body" class="pb-6 mb-6">
<pedidos-main></pedidos-main>
<pedidos-main>
<template v-slot:cartel>
<cantidad-ollas/>
</template>
</pedidos-main>
</div>
</template>

View file

@ -1,30 +1,33 @@
<script>
import { mapGetters, mapMutations, mapState } from "vuex";
import Burger from "../comunes/Burger.vue";
import { mapActions, mapGetters, mapMutations, mapState } from "vuex";
import ChismosaDropdown from "../pedidos/ChismosaDropdown.vue";
export default {
name: "OllasNavBar",
components: { Burger, ChismosaDropdown },
name: "OllasNavBarBrand",
components: { ChismosaDropdown },
computed: {
...mapGetters('pedido', ["pedidoDefinido"]),
...mapGetters('ollas', ["montoTotal"]),
...mapState('ui', ["burger_activa"]),
...mapState('pedido', ["nombre"]),
...mapState('ui', ["burger_activa"])
},
methods: {
...mapActions('ollas', ["getMontoPorOlla"]),
...mapMutations('ui', ["toggleBurger"]),
},
async mounted() {
await this.getMontoPorOlla();
}
}
</script>
<template>
<div class="navbar-item hide-below-1024">
<p v-if="pedidoDefinido">{{ nombre }}</p>
<p v-if="pedidoDefinido">{{ `${nombre} - Monto total: ${montoTotal}` }}</p>
<chismosa-dropdown
v-if="pedidoDefinido"
class="hide-above-1023"
ariaControls="mobile"/>
<burger/>
</div>
</template>

View file

@ -1,7 +1,11 @@
<template>
<div id="pedidos-body" class="pb-6 mb-6">
<pedido-select v-if="!pedidoDefinido"/>
<pedidos-main v-else/>
<pedidos-main v-else>
<template v-slot:cartel>
<cartel-pedido-aprobado/>
</template>
</pedidos-main>
</div>
</template>
@ -9,10 +13,11 @@
import { mapGetters } from "vuex";
import PedidoSelect from "./PedidoSelect.vue";
import PedidosMain from "./PedidosMain.vue";
import CartelPedidoAprobado from "./CartelPedidoAprobado.vue";
export default {
name: "PedidosBody",
components: { PedidosMain, PedidoSelect },
components: { CartelPedidoAprobado, PedidosMain, PedidoSelect },
computed: {
...mapGetters('pedido', ["pedidoDefinido"]),
},

View file

@ -25,9 +25,9 @@ export default {
<template>
<div class="navbar-item field has-addons mt-1 mr-3 mb-1">
<a class="button is-small has-text-dark-grey" @click.capture="buscar">
<span class="icon">
<i class="fas fa-search"></i>
</span>
<span class="icon">
<i class="fas fa-search"></i>
</span>
</a>
<input class="input is-small"
type="text"

View file

@ -2,10 +2,10 @@
<div class="dropdown is-right navbar-item" :class="{'is-active': show_chismosa}">
<div class="dropdown-trigger">
<a class="text-a" aria-haspopup="true" :aria-controls="ariaControls" @click.capture="toggleChismosa">
<span class="icon is-small mr-1">
<img src="/assets/chismosa.png">
</span>
<span v-text="'$' + total"></span>
<span class="icon is-small mr-1">
<img src="/assets/chismosa.png">
</span>
<span v-text="'$' + total"/>
</a>
</div>
</div>

View file

@ -1,6 +1,6 @@
<template>
<nav class="breadcrumb is-centered has-background-danger-light is-fixed-top"
aria-label="breadcrumbs" v-show="visible">
aria-label="breadcrumbs">
<ul class="mt-4">
<li v-for="(miga, i) in migas" :key="i" :class="{'is-active': i === migaActiva}">
<a @click="clickMiga({ miga: miga })"
@ -17,15 +17,11 @@ import { mapActions, mapMutations, mapState } from "vuex";
export default {
methods: {
...mapActions('productos', ["getProductos"]),
...mapActions('ui', ["clickMiga"]),
...mapMutations('ui', ["addMiga"]),
},
computed: {
...mapState('ui', ["migas"]),
visible() {
return this.migas.length > 0;
},
migaActiva() {
return this.migas.length - 1;
},

View file

@ -2,13 +2,12 @@
import { defineComponent } from "vue";
import Canasta from "./Canasta.vue";
import Chismosa from "./Chismosa.vue";
import CartelPedidoAprobado from "./CartelPedidoAprobado.vue";
import NavMigas from "./NavMigas.vue";
import { mapActions, mapState } from "vuex";
export default defineComponent({
name: "PedidosMain",
components: { NavMigas, CartelPedidoAprobado, Chismosa, Canasta },
components: { NavMigas, Chismosa, Canasta },
computed: {
...mapState('ui', ["show_chismosa"]),
},
@ -26,7 +25,7 @@ export default defineComponent({
<nav-migas/>
<div class="columns">
<div class="column" :class="{ 'is-two-thirds-desktop is-hidden-touch': show_chismosa }">
<cartel-pedido-aprobado/>
<slot name="cartel"></slot>
<canasta/>
</div>
<div class="column is-full-touch" v-if="show_chismosa">

View file

@ -1,11 +1,10 @@
<script>
import { mapGetters, mapMutations, mapState } from "vuex";
import ChismosaDropdown from "./ChismosaDropdown.vue";
import Burger from "../comunes/Burger.vue";
export default {
name: "PedidosNavBar",
components: { Burger, ChismosaDropdown },
name: "PedidosNavBarBrand",
components: { ChismosaDropdown },
computed: {
...mapGetters('pedido', ["pedidoDefinido"]),
...mapState('pedido', ["nombre", "grupo_de_compra"]),
@ -26,7 +25,6 @@ export default {
v-if="pedidoDefinido"
class="hide-above-1023"
ariaControls="mobile"/>
<burger/>
</div>
</template>

View file

@ -6,6 +6,7 @@ import login from "./modules/login";
import pedido from "./modules/pedido";
import productos from "./modules/productos";
import ui from "./modules/ui";
import ollas from "./modules/ollas";
Vue.use(Vuex);
@ -17,5 +18,6 @@ export default new Vuex.Store({
pedido,
productos,
ui,
ollas,
},
});

View file

@ -3,6 +3,7 @@ import axios from "axios";
const state = {
lastFetch: undefined,
grupos_de_compra: [],
parametros: [],
};
const mutations = {
@ -10,6 +11,15 @@ const mutations = {
state.grupos_de_compra = data;
state.lastFetch = new Date();
},
setParametros(state, parametros) {
state.parametros = parametros;
state.parametros.forEach(
p => p.valor = Number.parseInt(p.valor)
);
},
actualizarParametro(state, { parametro_id, valor }) {
state.parametros.find(p => p.id === parametro_id).valor = valor;
},
setSaldo(state, { gdc_id, saldo }) {
const barrio = state.grupos_de_compra.find(gdc => gdc.id === gdc_id);
const i = state.grupos_de_compra.indexOf(barrio);
@ -22,6 +32,22 @@ const actions = {
const response = await axios.get('/api/grupos-de-compra/saldos');
commit('setGruposDeCompra', response.data);
},
async getParametros({ commit }) {
const response = await axios.get('/api/parametros');
commit('setParametros', response.data);
},
async cambiarParametro({ commit, dispatch }, { parametro_id, valor }) {
try {
await axios.put(
`/comisiones/parametros/${parametro_id}`,
{ valor: valor }
);
commit('actualizarParametro', { parametro_id, valor });
dispatch("ui/toast", { mensaje: 'Parámetro modificado con éxito' }, { root: true });
} catch (error) {
dispatch("ui/error", { error: error }, { root: true });
}
},
async setSaldo({ commit, dispatch }, { gdc_id, saldo }) {
try {
await axios.post(

View file

@ -103,7 +103,7 @@ const getters = {
};
case 'comisiones':
return {
fondo: "has-background-grey-light",
fondo: "",
texto: "",
botones: "is-dark"
};

40
resources/js/store/modules/ollas.js vendored Normal file
View file

@ -0,0 +1,40 @@
import axios from "axios";
const state = {
monto_por_olla: undefined,
};
const mutations = {
setMontoPorOlla(state, parametros) {
state.monto_por_olla = Number.parseInt(parametros.find(p => p.id === 'monto-olla').valor);
},
};
const actions = {
async getMontoPorOlla({ commit }) {
const response = await axios.get('/api/parametros');
commit('setMontoPorOlla', response.data);
},
async actualizarCantidadOllas({ rootState }, { cantidad: cantidad}) {
const barrio = rootState.pedido.grupo_de_compra.id;
const params = { cantidad : cantidad };
await axios.put(`/ollas/${barrio}/cantidad`, params);
}
};
const getters = {
montoTotal(state, _, rootState) {
return state.monto_por_olla * rootState.pedido.cantidad_de_ollas;
},
montoSuperado: (_, getters, rootState) => {
return rootState.pedido.total > getters.montoTotal;
}
};
export default {
namespaced: true,
state,
mutations,
actions,
getters,
};

View file

@ -3,16 +3,17 @@ import axios from "axios";
const state = {
lastFetch: null,
grupo_de_compra: null,
pedido_id: null,
nombre: null,
productos: null,
aprobado: null,
total: null,
total_transporte: null,
cantidad_transporte: null,
total_sin_devoluciones: null,
devoluciones_total: null,
devoluciones_notas: null,
pedido_id: 0,
nombre: "",
productos: [],
aprobado: false,
total: 0,
total_transporte: 0,
cantidad_transporte: 0,
total_sin_devoluciones: 0,
devoluciones_total: 0,
devoluciones_notas: "",
cantidad_de_ollas: 0,
};
const mutations = {
@ -25,13 +26,27 @@ const mutations = {
state.nombre = pedido.nombre;
state.productos = pedido.productos;
state.aprobado = pedido.aprobado;
state.total = pedido.total;
state.total_transporte = pedido.total_transporte;
state.cantidad_transporte = pedido.cantidad_transporte;
state.total_sin_devoluciones = pedido.total_sin_devoluciones;
state.devoluciones_total = pedido.devoluciones_total;
state.total = Number.parseFloat(pedido.total.replace(',',''));
state.total_transporte = Number.parseInt(pedido.total_transporte?.replace(',',''));
state.cantidad_transporte = Number.parseInt(pedido.cantidad_transporte?.replace(',',''));
state.total_sin_devoluciones = Number.parseFloat(pedido.total_sin_devoluciones?.replace(',',''));
state.devoluciones_total = Number.parseFloat(pedido.devoluciones_total?.replace(',',''));
state.devoluciones_notas = pedido.devoluciones_notas;
},
setPedidoDeOllas(state, pedido) {
state.lastFetch = new Date();
state.pedido_id = pedido.id;
state.nombre = pedido.nombre;
state.productos = pedido.productos;
state.total = Number.parseFloat(pedido.total.replace(',',''));
state.cantidad_de_ollas = Number.parseInt(pedido.cantidad_de_ollas);
delete state.aprobado;
delete state.total_transporte;
delete state.cantidad_transporte;
delete state.total_sin_devoluciones;
delete state.devoluciones_total;
delete state.devoluciones_notas;
},
reset(state) {
state.lastFetch = null;
state.pedido_id = null;
@ -44,6 +59,9 @@ const mutations = {
state.total_sin_devoluciones = null;
state.devoluciones_total = null;
state.devoluciones_notas = null;
},
setCantidadOllas(state, { cantidad }) {
state.cantidad_de_ollas = cantidad;
}
};
@ -95,9 +113,8 @@ const actions = {
commit('reset');
},
async getPedidoDeOllas({ commit }) {
const response = await axios.get(`/api/ollas/${state.grupo_de_compra.id}`);
console.log(response);
commit('setPedido', response.data);
const response = await axios.get(`/ollas/${state.grupo_de_compra.id}`);
commit('setPedidoDeOllas', response.data);
},
};

View file

@ -49,7 +49,5 @@ Route::middleware('api')->group(function() {
Route::get('/','Api\ProductoController@index');
});
Route::prefix('ollas')->group(function() {
Route::get('/{gdc}','Api\OllasController@pedido');
});
Route::get('/parametros', 'ComisionesController@obtenerParametros');
});

View file

@ -56,10 +56,13 @@ Route::middleware(['auth', 'role:comision'])->group( function() {
Route::get('/comisiones/canasta/ejemplo', 'ComisionesController@descargarCanastaEjemplo')->name('comisiones.canasta.ejemplo');
Route::post('/comisiones/canasta', 'ComisionesController@cargarCanasta')->name('comisiones.canasta');
Route::post('/comisiones/saldos', 'ComisionesController@cargarSaldos')->name('comisiones.saldos');
Route::put('/comisiones/parametros/{parametro_id}', 'ComisionesController@modificarParametros');
});
Route::get('/ollas/login', 'OllasController@show')->name('ollas.login');
Route::middleware(['auth', 'role:ollas'])->group( function() {
Route::get('/ollas', 'RouteController@main')->name('ollas');
Route::middleware(['auth', 'role:ollas'])->prefix('ollas')->group( function() {
Route::get('/', 'RouteController@main')->name('ollas');
Route::get('/{gdc}','OllasController@pedido');
Route::put('/{gdc}/cantidad','OllasController@actualizarCantidadOllas');
});