Compare commits

..

1 Commits

Author SHA1 Message Date
nat 95ba0733ab WIP 2022-02-22 10:47:06 -03:00
34 changed files with 118 additions and 3815 deletions

View File

@ -47,5 +47,4 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
WEB_CLIENT_EMAIL=informaticamps@buzon.uy
WEB_CLIENT_NAME=web
WEB_CLIENT_PASS=pass
NGINX_PORT=8000
WEB_CLIENT_PASS=pass

8
.idea/.gitignore vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="framework" type="frameworkType"/>
<xs:complexType name="commandType">
<xs:all>
<xs:element type="xs:string" name="name" minOccurs="1" maxOccurs="1"/>
<xs:element type="xs:string" name="params" minOccurs="0" maxOccurs="1"/>
<xs:element type="xs:string" name="help" minOccurs="0" maxOccurs="1"/>
<xs:element type="optionsBeforeType" name="optionsBefore" minOccurs="0" maxOccurs="1"/>
</xs:all>
</xs:complexType>
<xs:complexType name="frameworkType">
<xs:sequence>
<xs:element type="xs:string" name="extraData" minOccurs="0" maxOccurs="1"/>
<xs:element type="commandType" name="command" maxOccurs="unbounded" minOccurs="0"/>
<xs:element type="xs:string" name="help" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute type="xs:string" name="name" use="required"/>
<xs:attribute type="xs:string" name="invoke" use="required"/>
<xs:attribute type="xs:string" name="alias" use="required"/>
<xs:attribute type="xs:boolean" name="enabled" use="required"/>
<xs:attribute type="xs:integer" name="version" use="required"/>
<xs:attribute type="xs:string" name="frameworkId" use="optional"/>
</xs:complexType>
<xs:complexType name="optionsBeforeType">
<xs:sequence>
<xs:element type="optionType" name="option" maxOccurs="unbounded" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="optionType">
<xs:sequence>
<xs:element type="xs:string" name="help" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute type="xs:string" name="name" use="required"/>
<xs:attribute type="xs:string" name="shortcut" use="optional"/>
<xs:attribute name="pattern" use="optional">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="space"/>
<xs:enumeration value="equals"/>
<xs:enumeration value="unknown"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:schema>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/pedi2.iml" filepath="$PROJECT_DIR$/.idea/pedi2.iml" />
</modules>
</component>
</project>

View File

@ -1,85 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/app" isTestSource="false" packagePrefix="App\" />
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="Tests\" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/event-dispatcher" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php81" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/routing" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/simple-cache" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/css-selector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-grapheme" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
<excludeFolder url="file://$MODULE_DIR$/vendor/egulias/email-validator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/process" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-normalizer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php73" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-kernel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/finder" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/asm89/stack-cors" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/brick/math" />
<excludeFolder url="file://$MODULE_DIR$/vendor/swiftmailer/swiftmailer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/flysystem" />
<excludeFolder url="file://$MODULE_DIR$/vendor/opis/closure" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/csv" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/mime-type-detection" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/collection" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/uuid" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/inflector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/lexer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/voku/portable-ascii" />
<excludeFolder url="file://$MODULE_DIR$/vendor/facade/ignition-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/facade/flare-client-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/facade/ignition" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fideloper/proxy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/tinker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/framework" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/ui" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/sanctum" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fruitcake/laravel-cors" />
<excludeFolder url="file://$MODULE_DIR$/vendor/vlucas/phpdotenv" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpoption/phpoption" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psy/psysh" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nunomaduro/collision" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nesbot/carbon" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nikic/php-parser" />
<excludeFolder url="file://$MODULE_DIR$/vendor/tijsverkoyen/css-to-inline-styles" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/commonmark" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/var-dumper" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/error-handler" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/deprecation-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/monolog/monolog" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/string" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fakerphp/faker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mime" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-client" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-iconv" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-factory" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
<excludeFolder url="file://$MODULE_DIR$/vendor/dragonmantank/cron-expression" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php80" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/psr/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php81" />
<path value="$PROJECT_DIR$/vendor/psr/log" />
<path value="$PROJECT_DIR$/vendor/symfony/routing" />
<path value="$PROJECT_DIR$/vendor/psr/simple-cache" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/symfony/css-selector" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-grapheme" />
<path value="$PROJECT_DIR$/vendor/symfony/console" />
<path value="$PROJECT_DIR$/vendor/egulias/email-validator" />
<path value="$PROJECT_DIR$/vendor/symfony/process" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php72" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-normalizer" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php73" />
<path value="$PROJECT_DIR$/vendor/symfony/http-kernel" />
<path value="$PROJECT_DIR$/vendor/symfony/finder" />
<path value="$PROJECT_DIR$/vendor/symfony/translation" />
<path value="$PROJECT_DIR$/vendor/asm89/stack-cors" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
<path value="$PROJECT_DIR$/vendor/symfony/translation-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
<path value="$PROJECT_DIR$/vendor/brick/math" />
<path value="$PROJECT_DIR$/vendor/swiftmailer/swiftmailer" />
<path value="$PROJECT_DIR$/vendor/league/flysystem" />
<path value="$PROJECT_DIR$/vendor/opis/closure" />
<path value="$PROJECT_DIR$/vendor/league/csv" />
<path value="$PROJECT_DIR$/vendor/league/mime-type-detection" />
<path value="$PROJECT_DIR$/vendor/ramsey/collection" />
<path value="$PROJECT_DIR$/vendor/ramsey/uuid" />
<path value="$PROJECT_DIR$/vendor/doctrine/inflector" />
<path value="$PROJECT_DIR$/vendor/doctrine/lexer" />
<path value="$PROJECT_DIR$/vendor/composer" />
<path value="$PROJECT_DIR$/vendor/voku/portable-ascii" />
<path value="$PROJECT_DIR$/vendor/facade/ignition-contracts" />
<path value="$PROJECT_DIR$/vendor/facade/flare-client-php" />
<path value="$PROJECT_DIR$/vendor/facade/ignition" />
<path value="$PROJECT_DIR$/vendor/fideloper/proxy" />
<path value="$PROJECT_DIR$/vendor/laravel/tinker" />
<path value="$PROJECT_DIR$/vendor/laravel/framework" />
<path value="$PROJECT_DIR$/vendor/laravel/ui" />
<path value="$PROJECT_DIR$/vendor/laravel/sanctum" />
<path value="$PROJECT_DIR$/vendor/fruitcake/laravel-cors" />
<path value="$PROJECT_DIR$/vendor/vlucas/phpdotenv" />
<path value="$PROJECT_DIR$/vendor/phpoption/phpoption" />
<path value="$PROJECT_DIR$/vendor/psy/psysh" />
<path value="$PROJECT_DIR$/vendor/nunomaduro/collision" />
<path value="$PROJECT_DIR$/vendor/nesbot/carbon" />
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
<path value="$PROJECT_DIR$/vendor/nikic/php-parser" />
<path value="$PROJECT_DIR$/vendor/tijsverkoyen/css-to-inline-styles" />
<path value="$PROJECT_DIR$/vendor/league/commonmark" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
<path value="$PROJECT_DIR$/vendor/filp/whoops" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
<path value="$PROJECT_DIR$/vendor/symfony/var-dumper" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
<path value="$PROJECT_DIR$/vendor/symfony/error-handler" />
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
<path value="$PROJECT_DIR$/vendor/monolog/monolog" />
<path value="$PROJECT_DIR$/vendor/symfony/string" />
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
<path value="$PROJECT_DIR$/vendor/fakerphp/faker" />
<path value="$PROJECT_DIR$/vendor/psr/container" />
<path value="$PROJECT_DIR$/vendor/symfony/mime" />
<path value="$PROJECT_DIR$/vendor/psr/http-client" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-iconv" />
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-idn" />
<path value="$PROJECT_DIR$/vendor/dragonmantank/cron-expression" />
<path value="$PROJECT_DIR$/vendor/psr/http-message" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.2">
<option name="suggestChangeDefaultLanguageLevel" value="false" />
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,22 +0,0 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
class PedidoController extends Controller
{
public function generarTablas()
{
//GENERAR TABLA GENERAL
DB::unprepared("DROP VIEW if exists productos_por_grupo_de_compra;
SET @barrios = NULL;
SELECT
GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(`grupo_de_compra_nombre` = \"', `grupo_de_compra_nombre`,'\", `cantidad_pedida`,NULL)) AS \"',`grupo_de_compra_nombre`,'\"')) INTO @barrios
FROM pedidos;
SET @sql = CONCAT('CREATE VIEW productos_por_grupo_de_compra AS SELECT producto_nombre, ', @barrios, ' FROM pedidos GROUP BY producto_nombre');
PREPARE stmt FROM @sql;
EXECUTE stmt;");
return "Tabla productos_por_grupo_de_compra generada. " . date('l jS \of F Y h:i:s A');
}
}

View File

@ -3,12 +3,11 @@
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Producto;
use Illuminate\Http\Request;
use App\Filtros\FiltroDeSubpedido;
use App\Subpedido;
use App\GrupoDeCompra;
use App\Http\Resources\SubpedidoResource;
use App\Producto;
use Illuminate\Validation\Rule;
use Symfony\Component\HttpKernel\Exception\HttpException;
@ -28,7 +27,7 @@ class SubpedidoController extends Controller
}
/**
* Store a newly created resource in storage.
* Guardar un nuevo registro en el almacenamiento.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
@ -46,6 +45,28 @@ class SubpedidoController extends Controller
return $s;
}
/**
* Agregar un producto a un subpedido.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function updateProducto(Subpedido $subpedido, Producto $producto, Request $request)
{
$validado = $this->validateActualizacionDeProducto();
$cantidad = $validado['cantidad'];
if ($cantidad){
//si la cantidad es 1 o más se agrega el producto o actualiza la cantidad
$subpedido->productos()->syncWithoutDetaching([$producto->id => ['cantidad' => $cantidad]]);
} else {
//si la cantidad es 0, se elimina el producto del subpedido
$subpedido->productos()->detach($producto->id);
}
return response('Producto ' . $producto->nombre . ' actualizado en subpedido de ' . $subpedido->nombre . ' (cantidad ' . $cantidad . ')', 200);
}
protected function validateSubpedido(){
return request()->validate([
'nombre' => 'required|max:255',
@ -56,31 +77,9 @@ class SubpedidoController extends Controller
]);
}
/**
* Display the specified resource.
*
* @param \App\Subpedido $subpedido
* @return \Illuminate\Http\Response
*/
public function show(Subpedido $subpedido)
{
return new SubpedidoResource($subpedido);
}
// recibe request, saca producto y cantidad, valida, y pasa a syncProducto en Subpedido
public function syncProductos(Subpedido $subpedido) {
$valid = request()->validate([
'cantidad' => 'required|min:0',
'producto_id' => [
'required',
Rule::in(Producto::all()->pluck('id')),
]
protected function validateActualizacionDeProducto(){
return request()->validate([
'cantidad' => 'required|min:0'
]);
$producto = Producto::find($valid['producto_id']);
$subpedido->syncProducto($producto, $valid['cantidad']);
return new SubpedidoResource($subpedido);
}
}

View File

@ -1,24 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ChismosaController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware(['auth','subpedido']);
}
public function show()
{
return view('chismosa');
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class SubpedidoResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'nombre' => $this->nombre,
'subtotal_productos' => number_format($this->getSubtotalProductos(),0),
'subtotal_bonos' => number_format($this->getSubtotalBonos(),0),
'bonos_de_transporte' => $this->cantidadBDT(),
'subtotal_bonos_de_transporte' => number_format($this->getSubtotalBDT(),0),
'total' => number_format($this->getTotal(),0),
'grupo_de_compra' => $this->grupoDeCompra,
'productos' => $this->productos
];
}
}

View File

@ -15,76 +15,18 @@ class Subpedido extends Model
public function productos()
{
return $this->belongsToMany('App\Producto')->withPivot(["cantidad","total"]);
return $this->belongsToMany('App\Producto')->withPivot(["cantidad"]);
}
//Bonos del MPS, Sororo, etc. NO devuelve bonos de transporte
private function bonos()
{
return $this->productos()->where('bono',1);
}
private function productosSinBonos()
{
return $this->productos()->where('bono',false);
}
public function grupoDeCompra()
{
return $this->belongsTo('App\GrupoDeCompra');
}
//Permite que se apliquen los filtros al hacer una request (por ejemplo, de búsqueda)
//Este método permite que se apliquen los filtros al hacer una request (por ejemplo, de búsqueda)
public function scopeFiltrar($query, FiltroDeSubpedido $filtros)
{
return $filtros->aplicar($query);
}
//Subtotal de dinero de productos del pedido, sin bonos ni transporte
public function getSubtotalProductos()
{
return $this->productosSinBonos()->sum('total');
}
//Cantidad de bonos de transporte
public function cantidadBDT()
{
return ceil($this->getSubtotalProductos() / 500);
}
//Subtotal de dinero de bonos de transporte
public function getSubtotalBDT()
{
return $this->cantidadBDT() * 15;
}
//Subtotal de dinero de bonos (MPS, Sororo, etc)
public function getSubtotalBonos()
{
return $this->bonos()->sum('total');
}
public function getTotal()
{
return $this->getSubtotalProductos() + $this->getSubtotalBDT() + $this->getSubtotalBonos();
}
//Actualiza el pedido, agregando o quitando del subpedido según sea necesario. Debe ser llamado desde el controlador de subpedidos, luego de validar que los parámetros $producto y $cantidad son correctos. También calcula el subtotal por producto.
public function syncProducto(Producto $producto, Int $cantidad) {
if ($cantidad){
//si la cantidad es 1 o más se agrega el producto o actualiza la cantidad
$this->productos()->syncWithoutDetaching([
$producto->id => [
'cantidad' => $cantidad,
'total' => $cantidad * $producto->precio
]
]);
} else {
//si la cantidad es 0, se elimina el producto del subpedido
$this->productos()->detach($producto->id);
}
}
}

View File

@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AgregarColumnaBonoATablaDeProductos extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('productos', function (Blueprint $table) {
$table->boolean('bono')->after('apto_celiacxs');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('productos', function (Blueprint $table) {
$table->dropColumn('bono');
});
}
}

View File

@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AgregarColumnaTotalATablaDeProductoSubpedido extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('producto_subpedido', function (Blueprint $table) {
$table->double('total',10,2)->after('cantidad')->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('producto_subpedido', function (Blueprint $table) {
$table->dropColumn('total');
});
}
}

View File

@ -1,43 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePedidosView extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
DB::statement("CREATE VIEW pedidos AS
SELECT
g.id as grupo_de_compra_id,
g.nombre as grupo_de_compra_nombre,
g.region as grupo_de_compra_region,
pr.id AS producto_id,
pr.nombre as producto_nombre,
pr.precio as producto_precio,
SUM(ps.cantidad) as cantidad_pedida,
pr.precio*SUM(ps.cantidad) as total_por_producto
FROM grupos_de_compra g
JOIN subpedidos s ON (s.grupo_de_compra_id = g.id)
JOIN producto_subpedido ps ON (ps.subpedido_id = s.id)
JOIN productos pr ON (pr.id = ps.producto_id)
GROUP BY g.id, g.nombre, pr.id, pr.nombre;"
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
DB::statement("DROP VIEW pedidos");
}
}

View File

@ -27,8 +27,7 @@ class ProductoSeeder extends Seeder
'precio' => $registro['precio'],
'proveedor_id' => isset($registro['proveedor']) ? Proveedor::firstOrCreate([
'nombre' => $registro['proveedor']
])->id : null,
'bono' => $registro['categoria'] == 'BONOS Y FINANCIAMIENTO SORORO'
])->id : null
];
}

View File

@ -1,4 +1,4 @@
version: "3.3"
version: "3.7"
services:
app:
build:
@ -8,7 +8,7 @@ services:
context: ./
dockerfile: Dockerfile
image: laravel-image
container_name: pedi2-app
container_name: laravel-app
restart: unless-stopped
working_dir: /var/www/
volumes:
@ -19,8 +19,10 @@ services:
db:
image: mysql:5.7
container_name: pedi2-db
container_name: laravel-db
restart: unless-stopped
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
@ -37,10 +39,10 @@ services:
nginx:
image: nginx:alpine
container_name: pedi2-nginx
container_name: laravel-nginx
restart: unless-stopped
ports:
- ${NGINX_PORT}:80
- 8000:80
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

File diff suppressed because one or more lines are too long

45
public/css/app.css vendored
View File

@ -6,48 +6,3 @@ p.navbar-item:empty {
color: #cc0f35;
}
.help {
font-size: 1rem;
}
table.table td {
vertical-align: middle;
}
nav.breadcrumb.is-fixed-top {
left: 0;
position: fixed;
right: 0;
z-index: 30;
top: 3.25rem;
height: 3.25rem;
}
main.has-top-padding {
padding-top: 4.5rem !important;
}
.has-text-centered {
text-align: center;
margin: 0 1em;
}
.is-fixed-top {
position: fixed;
z-index: 30;
}
.chismosa-container {
top: 6.5rem;
max-height: 21rem;
overflow-y: scroll;
overflow-x: hidden;
width: 100%;
position: fixed;
z-index: 15;
}
main.chisma-abierta {
padding-top: 25.5rem !important;
}

View File

@ -1,10 +1,4 @@
figure.image.icono {
float: right;
margin: 4px;
}
.is-thin-centered {
width: 50%;
margin-left: auto;
margin-right: auto;
}
}

52
public/js/app.js vendored
View File

@ -2,16 +2,12 @@ window.Event = new Vue();
Vue.component('nav-bar', {
template: `
<nav class="navbar is-danger is-fixed-top" role="navigation" aria-label="main navigation">
<nav class="navbar is-danger" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="https://mps.org.uy">
<img src="/assets/logoMPS.png" height="28">
</a>
<p style="margin:0 auto" class="navbar-item"><slot name="subpedido"></slot></p>
<a class="navbar-item" href="#chismosa" @click.capture="toggleChismosa">
<img style="padding:0 0.3em;" src="/assets/chismosa.png" height="28">
<p style="margin:0 auto; color:white">$ <span v-text="subpedido == null ? 0 : subpedido.total"></span></p>
</a>
<a role="button" class="navbar-burger" :class="{'is-active':isActive}" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample" @click="toggleState">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
@ -34,46 +30,14 @@ Vue.component('nav-bar', {
</nav>`,
data() {
return {
isActive: false,
subpedido: null
isActive: false
}
},
methods: {
toggleState() {
this.isActive = !this.isActive;
},
actualizarSubpedido(){
axios.get("/api/subpedidos/" + this.subpedido.id)
.then(response => {
this.subpedido = response.data.data;
});
},
toggleChismosa(){
Event.$emit("toggle-chismosa");
}
}, mounted() {
axios.get("/subpedidos/obtener_sesion").then(response => {
this.subpedido = response.data.subpedido;
this.actualizarSubpedido()
});
//Emitir un evento subpedido-actualizado al agregar o eliminar un producto del subpedido para que el total de la chismosa se muestre correctamente
Event.$on('sync-subpedido', (cantidad, id) => {
axios.post("/api/subpedidos/"+this.subpedido.id+"/sync", {
cantidad: cantidad,
producto_id: id
}).then((response) => {
this.subpedido = response.data.data;
Event.$emit('sync-chismosa',this.subpedido);
bulmaToast.toast({
message: 'Pedido actualizado exitosamente',
duration: 1000,
type: 'is-danger',
position: 'bottom-center',
animate: { in: 'fadeIn', out: 'fadeOut' }
});
});
});
}
methods: {
toggleState() {
this.isActive = !this.isActive;
}
}
});
Vue.component('nav-migas', {
@ -106,4 +70,4 @@ Vue.component('nav-migas', {
new Vue({
el: '#app'
});
});

File diff suppressed because one or more lines are too long

109
public/js/chismosa.js vendored
View File

@ -1,109 +0,0 @@
Vue.component('chismosa', {
template: `
<div class="container table-container chismosa-container is-max-widescreen is-max-desktop animate__animated" :class="animation" v-show="!init">
<table v-show="this.subpedido.productos.length != 0" class="table is-fullwidth is-striped is-bordered">
<thead>
<tr>
<th>Producto</th>
<th><abbr title="Cantidad">C</abbr></th>
<th><abbr title="Precio Total">$</abbr></th>
<th></th>
<th><abbr title="Eliminar"></abbr></th>
</tr>
</thead>
<tfoot>
<tr>
<th><abbr title="Bonos de Transporte">B. Transporte</abbr></th>
<th>{{ this.subpedido.bonos_de_transporte }}</th>
<th>{{ this.subpedido.subtotal_bonos_de_transporte }}</th>
<th></th>
<th></th>
</tr>
<tr>
<th>Total total</th>
<th></th>
<th>{{ this.subpedido.total }}</th>
<th></th>
<th></th>
</tr>
</tfoot>
<tbody>
<producto-row v-for="producto in this.subpedido.productos" :producto="producto" :key="producto.id"></producto-row>
</tbody>
</table>
<p class="has-text-centered" v-show="this.subpedido.productos.length == 0">Compa, todavía no agregaste nada a la chismosa.</p>
</div>
`,
data() {
return {
subpedido: {
productos:[]
},
init: true,
visible: false
}
},
computed: {
animation: function() {
return this.visible ? "animate__slideInDown" : "animate__slideOutUp";
}
},
beforeCreate() {
axios.get("/subpedidos/obtener_sesion").then(response => {
this.subpedido = response.data.subpedido;
this.fetchSubpedido();
});
},
methods: {
fetchSubpedido() {
axios.get("/api/subpedidos/" + this.subpedido.id)
.then(response => {
this.subpedido = response.data.data;
});
}
},
mounted() {
Event.$on('sync-chismosa', (subpedido) => {
this.subpedido = subpedido;
});
Event.$on('toggle-chismosa', () => {
this.init = false;
this.visible = !this.visible;
var main = document.getElementById("main");
if (this.visible) main.classList.add("chisma-abierta");
else main.classList.remove("chisma-abierta");
});
}
});
Vue.component('producto-row', {
template: `
<tr>
<td>{{ this.producto.nombre }}</td>
<td>{{ this.producto.pivot.cantidad }}</td>
<td>{{ this.producto.pivot.total }}</td>
<td><button @click.capture="seleccionarProducto(producto)" class="button is-warning">
<span class="icon">
<i class="fas fa-edit"></i>
</span>
</button></td>
<td><button @click.capture="eliminarProducto(producto)" class="button is-danger">
<span class="icon">
<i class="fas fa-trash-alt"></i>
</span>
</button></td>
</tr>
`,
props: {
producto: Object
},
methods: {
seleccionarProducto(producto) {
Event.$emit("producto-seleccionado",producto);
},
eliminarProducto(producto) {
Event.$emit("sync-subpedido", 0, this.producto.id);
Event.$emit("sync-subpedido");
}
}
})

View File

@ -110,66 +110,41 @@ Vue.component('productos-container', {
Vue.component('producto-container', {
template: `
<div v-bind:class="visible ? 'is-active modal' : 'modal'">
<div v-bind:class="visible ? 'is-active modal' : 'modal'">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title" v-text="producto.nombre"></p>
<button class="delete" aria-label="close" @click.capture="cerrar"></button>
</header>
<section class="modal-card-body">
<div class="card-image">
<figure v-show="producto.nacional" class="image icono is-32x32" style="z-index:10">
<img src="/assets/uruguay.png">
</figure>
<figure v-show="producto.economia_solidaria" class="image icono is-32x32" style="z-index:10">
<img src="/assets/solidaria.png">
</figure>
<figure class="image is-4by3">
<img
v-bind:src="producto.imagen ? producto.imagen : 'https://bulma.io/images/placeholders/1280x960.png'">
</figure>
</div>
<div class="media-content">
<p class="title is-4" v-text="producto.proveedor"></p>
<p class="subtitle is-4">$<span v-text="producto.precio"></span></p>
<p class="subtitle is-6"><span v-show="producto.apto_veganxs">Apto para veganxs. </span><span v-show="producto.apto_celiacxs">Apto para celíacxs.</span></p>
<p class="subtitle is-5"><span v-text="producto.descripcion"></span></p>
<div class="field has-addons is-centered is-thin-centered">
<p class="control">
<button class="button" @click="cant !== 0 ? cant-- : cant">
<span class="icon is-small">
<!-- Habría que ver de poner un ícono de - -->
</span>
<span>-</span>
</button>
</p>
<p class="control">
<input id="cantidad" class="input" type="number" v-model.number="cant" style="text-align: center">
</p>
<p class="control">
<button class="button" @click="cant++">
<span class="icon is-small">
<!-- Habría que ver de poner un ícono de + -->
</span>
<span>+</span>
</button>
</p>
</div>
</div>
</section>
<footer class="modal-card-foot">
<!-- Habría que ver si cambiar el botón cuando al cantidad es 0 -->
<button class="button is-success" :disabled="cant <= 0" @click="agregarProducto">Aceptar</button>
<button class="button" @click.capture="cerrar">Cancelar</button>
</footer>
<header class="modal-card-head">
<p class="modal-card-title" v-text="producto.nombre"></p>
<button class="delete" aria-label="close" @click.capture="cerrar"></button>
</header>
<section class="modal-card-body">
<div class="card-image">
<figure class="image is-4by3">
<img v-bind:src="producto.imagen ? producto.imagen : 'https://bulma.io/images/placeholders/1280x960.png'">
</figure>
<figure v-show="producto.nacional" class="image icono is-32x32">
<img src="/assets/uruguay.png">
</figure>
<figure v-show="producto.economia_solidaria" class="image icono is-32x32">
<img src="/assets/solidaria.png">
</figure>
</div>
<div class="media-content">
<p class="title is-4" v-text="producto.proveedor"></p>
<p class="subtitle is-4">$<span v-text="producto.precio"></span></p>
<p class="subtitle is-5"><span v-text="producto.descripcion"></span></p>
</div>
</section>
<footer class="modal-card-foot">
<button class="button is-success">Agregar a la chismosa</button>
<button class="button" @click.capture="cerrar">Cancelar</button>
</footer>
</div>
</div>`,
</div>`,
data() {
return {
producto: null,
visible: false,
cant: 0
visible: false
}
},
computed: {
@ -182,17 +157,8 @@ Vue.component('producto-container', {
},
methods: {
cerrar() {
this.cant = 0;
this.visible = false;
Event.$emit("migas-pop");
},
agregarProducto() {
if (this.cant < 0) alert("No se puede agregar cantidades negativas")
else if (!Number.isInteger(this.cant)) alert("Las cantidades deben ser números enteros")
else {
Event.$emit("sync-subpedido", this.cant, this.producto.id);
this.cerrar();
}
this.visible = false;
Event.$emit("migas-pop");
}
},
mounted() {
@ -202,4 +168,4 @@ Vue.component('producto-container', {
Event.$emit("migas-agregar",this.miga);
});
}
});
});

View File

@ -5,30 +5,13 @@ Vue.component('subpedido-select', {
subpedidosExistentes: []
}
},
computed: {
nombresDeSubpedidos: function() {
return this.subpedidosExistentes.map(a => a.nombre.toLowerCase())
},
botonCrearDesabilitado : function() {
return !this.subpedido || this.nombresDeSubpedidos.includes(this.subpedido.toLowerCase())
}
},
props: ["gdcid"],
mounted() {
console.log("ready");
},
},
methods: {
onType() {
if (!this.subpedido){
this.subpedidosExistentes = [];
return;
}
axios.get("/api/subpedidos", {
params: {
nombre: this.subpedido,
grupo_de_compra: this.gdcid
}
}).then(response => {
axios.get("/api/subpedidos?nombre=" + this.subpedido).then(response => {
this.subpedidosExistentes = response.data
});
},
@ -37,20 +20,14 @@ Vue.component('subpedido-select', {
nombre: this.subpedido,
grupo_de_compra_id: this.gdcid
}).then(response => {
//se creo el subpedido
this.elegirSubpedido(response.data);
//se creo el subpedido, guardamos el subpedido en sesion
axios.post("/subpedidos/guardar_sesion", {
subpedido: response.data
}).then(response => {
window.location.href = 'productos';
});
});
},
elegirSubpedido(subpedido){
//lo guardamos en sesion
this.guardarSubpedidoEnSesion(subpedido);
},
guardarSubpedidoEnSesion(subpedido) {
axios.post("/subpedidos/guardar_sesion", {
subpedido: subpedido
}).then(response => {
window.location.href = 'productos';
});
}
}
}
});

View File

@ -5,7 +5,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ config('app.name', 'Compras del MPS') }}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
</head>
<body>
<section class="section">

View File

@ -8,20 +8,17 @@
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ session("subpedido_nombre") ? "Pedido de " . session("subpedido_nombre") . " - " . config('app.name', 'Compras del MPS') : config('app.name', 'Compras del MPS')}}</title>
<link rel="icon" type="image/x-icon" href="/assets/favicon.png">
<!-- Fonts -->
<script src="https://kit.fontawesome.com/9235d1c676.js" crossorigin="anonymous"></script>
<script src="{{ asset('js/bulma-toast.min.js') }}"></script>
<!-- Styles -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
<link rel="stylesheet" href="{{ asset('css/animate.min.css') }}">
@yield('stylesheets')
</head>
<body class="has-navbar-fixed-top">
<body>
<div id="app">
<nav-bar>
<template slot="subpedido">{{ session('subpedido_nombre') ? 'Pedido de '. session('subpedido_nombre') : Auth::user()->name }}</template>
@ -33,14 +30,14 @@
</template>
</nav-bar>
<nav-migas inline-template>
<nav class="breadcrumb is-centered has-background-danger-light is-fixed-top" aria-label="breadcrumbs" v-show="visible">
<nav class="breadcrumb is-centered has-background-danger-light" aria-label="breadcrumbs" v-show="visible">
<ul>
<li v-for="(miga, i) in migas" v-bind:class="i==migas.length-1 ? 'is-active' : ''"><a :href="miga.href" v-text="miga.nombre"></a></li>
</ul>
</nav>
</nav-migas>
<main id="main" class="py-4 has-top-padding">
<main class="py-4">
@yield('content')
</main>
</div>

View File

@ -5,7 +5,6 @@
@endsection
@section('content')
<chismosa></chismosa>
<categorias-container></categorias-container>
<productos-container></productos-container>
<producto-container></producto-container>
@ -13,5 +12,4 @@
@section('scripts')
<script src="{{ asset('js/productos.js') }}"></script>
<script src="{{ asset('js/chismosa.js') }}"></script>
@endsection

View File

@ -10,31 +10,25 @@
Bienvenidx a la aplicación de compras del <strong>Mercado Popular de Subsistencia</strong>
</p>
<subpedido-select inline-template gdcid="{{Auth::user()->grupoDeCompra->id}}">
<div>
<label class="label">Escribí el nombre de tu familia o grupo de convivencia</label>
<div class="columns">
<div class="column is-two-thirds">
<div class="field">
<div class="control">
<input class="input" @input="onType" v-model="subpedido"></input>
</div>
<p class="help">Debe ser claro para que tus compas del barrio te identifiquen.</p>
</div>
</div>
<div class="column is-one-third buttons">
<button class="button is-danger" v-show="!botonCrearDesabilitado" @click="submit">Crear nuevo pedido</button>
<div class="block">
<div class="field">
<label class="label">Escribí el nombre de tu familia o grupo de convivencia</label>
<div class="control">
<input class="input" @input="onType" v-model="subpedido"></input>
</div>
<p class="help">Intentá que sea claro como para que tus compas del barrio te identifiquen.</p>
</div>
<div v-if="subpedidosExistentes.length" class="block">
<label class="label">Si ya comenzaste a hacer tu pedido este mes, elegilo en esta lista:</label>
<p class="help">Podés seguir escribiendo en el campo de arriba para refinar la búsqueda.</p>
<div class="columns is-mobile" v-for="(subpedidoExistente, index) in subpedidosExistentes" :class="{'has-background-grey-lighter': index % 2}">
<div class="column is-half-mobile is-two-thirds-desktop is-two-thirds-tablet"><p style="padding-top: calc(.5em - 1px); margin-bottom: .5rem" v-text="subpedidoExistente.nombre"></p></div>
<div class="buttons column is-half-mobile is-one-third-desktop is-one-third-tablet">
<button class="button is-danger" @click="elegirSubpedido(subpedidoExistente)">Continuar pedido</button>
</div>
</div>
</div>
<div class="buttons">
<button class="button is-primary" @click="submit">Crear nuevo pedido</button>
</div>
<div class="table-container">
<table class="table is-hoverable">
<tbody>
<tr v-for="subpedidoExistente in subpedidosExistentes" >
<td><a v-text="subpedidoExistente.nombre" :href="subpedidoExistente.nombre"></a></td>
</tr>
</tbody>
</table>
</div>
</div>
</subpedido-select>

View File

@ -28,15 +28,13 @@ Route::middleware('api')->group(function () {
});
});
Route::prefix('pedidos')->group(function () {
Route::get('/generar_tablas','Api\PedidoController@generarTablas');
});
Route::prefix('subpedidos')->group(function () {
Route::get('/','Api\SubpedidoController@index');
Route::get('{subpedido}','Api\SubpedidoController@show');
Route::post('/','Api\SubpedidoController@store');
Route::post('/{subpedido}/sync', 'Api\SubpedidoController@syncProductos');
//@TO DO -> esta ruta debe estar en middleware de auth y/o subpedido
Route::put('/{subpedido}/producto/{producto}','Api\SubpedidoController@updateProducto');
});
//@TO DO -> esta ruta debe estar en middleware de auth y/o subpedido
@ -47,6 +45,8 @@ Route::middleware('api')->group(function () {
//@TO DO -> esta ruta debe estar en middleware de auth y/o subpedido
Route::prefix('productos')->group(function () {
Route::get('/','Api\ProductoController@index');
Route::get('{producto}','Api\ProductoController@show');
Route::get('{producto}','Api\ProductoController@show');
});
//@TO DO -> esta ruta debe estar en middleware de auth y/o subpedido
});

View File

@ -13,25 +13,19 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
|
*/
if (App::environment('production')) {
URL::forceScheme('https');
}
Route::get('/', 'ProductoController@index')->name('productos');
Auth::routes(['register' => false]);
Route::get('/productos', 'ProductoController@index')->name('productos.index');
Route::get('/chismosa', 'ChismosaController@show')->name('chismosa.show');
Route::middleware('auth')->group( function() {
Route::name('subpedidos.')->prefix("subpedidos")->group( function() {
Route::get('/', function() {
return view('subpedidos_create');
})->name('create');
Route::post('guardar_sesion', function() {
$r = request();
if (!isset($r["subpedido"])) {
@ -41,15 +35,5 @@ Route::middleware('auth')->group( function() {
session(["subpedido_id" => $r["subpedido"]["id"]]);
return "Subpedido guardado en sesión";
})->name('guardarSesion');
Route::get('obtener_sesion', function() {
$sesion = [
'subpedido' => [
'nombre' => session("subpedido_nombre"),
'id' => session("subpedido_id")
]
];
return $sesion;
})->name('obtenerSesion');
});
});