funcion/refactor-general #45

Open
atasistro wants to merge 111 commits from funcion/refactor-general into master
80 changed files with 1957 additions and 1295 deletions

View file

@ -14,10 +14,8 @@ use Illuminate\Support\Facades\Log;
class GrupoDeCompra extends Model
{
public $timestamps = false;
protected $fillable = ["nombre", "region", "telefono", "correo", "referente_finanzas", "cantidad_de_nucleos", "fila", "devoluciones_habilitadas"];
protected $fillable = ["nombre", "region", "devoluciones_habilitadas"];
protected $table = 'grupos_de_compra';
protected $hidden = ['password'];
public function subpedidos(): HasMany
{

View file

@ -3,14 +3,12 @@
namespace App\Helpers;
use App\Producto;
use App\Proveedor;
use App\CanastaLog;
use DatabaseSeeder;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class CanastaHelper
{
const TIPO = "Tipo";
@ -39,58 +37,38 @@ class CanastaHelper
$categoria = '';
foreach($registros as $i => $registro) {
// saltear filas que no tienen tipo
if (self::noTieneTipo($registro)) {
var_dump("no hay tipo en la fila " . $i);
// saltear bono de transporte y filas que no tienen tipo
if (self::noTieneTipo($registro) || $registro[self::TIPO] == "T")
continue;
}
// saltear bono de transporte
if ($registro[self::TIPO] == "T"){
continue;
}
// obtener categoria si no hay producto
if ($registro[self::PRODUCTO] == '') {
// no es la pregunta de la copa?
if (!Str::contains($registro[self::TIPO],"¿"))
$categoria = $registro[self::TIPO];
continue;
continue; // saltear si es la pregunta de la copa
}
// completar producto
$toInsert[] = [
$toInsert[] = DatabaseSeeder::addTimestamps([
'fila' => $i,
'categoria' => $categoria,
'nombre' => trim(str_replace('*', '',$registro[self::PRODUCTO])),
'precio' => $registro[self::PRECIO],
'proveedor_id' => self::obtenerProveedor($registro[self::PRODUCTO]),
'es_solidario' => Str::contains($registro[self::PRODUCTO],"*"),
'bono' => preg_match(self::REGEX_BONO, $registro[self::TIPO]),
'requiere_notas'=> $registro[self::TIPO] == self::PRODUCTO_TALLE_COLOR,
];
]);
}
foreach (array_chunk($toInsert,DatabaseSeeder::CHUNK_SIZE) as $chunk) {
DB::table('productos')->insert($chunk);
}
foreach (array_chunk($toInsert,DatabaseSeeder::CHUNK_SIZE) as $chunk)
Producto::insert($chunk);
self::agregarBonoBarrial();
self::log($archivo, self::CANASTA_CARGADA);
}
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;
}
/**
* @param $path
* @param $descripcion
@ -122,13 +100,13 @@ class CanastaHelper
return Str::contains($c, 'BONO');
});
DB::table('productos')->insert([
Producto::create([
'fila' => 420,
'nombre' => "Bono barrial",
'precio' => 20,
'categoria' => $categoria,
'bono' => 1,
'proveedor_id' => null,
'es_solidario' => 0,
'requiere_notas'=> false,
]);
}

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
use App\GrupoDeCompra;
use App\Http\Controllers\Controller;
use App\Http\Resources\GrupoDeCompraReducido;
use App\Http\Resources\GrupoDeCompraResource;
class GrupoDeCompraController extends Controller
@ -16,4 +17,8 @@ class GrupoDeCompraController extends Controller
{
return new GrupoDeCompraResource($grupoDeCompra);
}
public function reducido(GrupoDeCompra $grupoDeCompra)
{
return new GrupoDeCompraReducido($grupoDeCompra);
}
}

View file

@ -17,7 +17,7 @@ class SubpedidoController extends Controller
{
public function index(FiltroDeSubpedido $filtros, Request $request)
{
return Subpedido::filtrar($filtros)->get();
return Subpedido::filtrar($filtros)->select('id','nombre')->get();
}
public function indexResources(FiltroDeSubpedido $filtros, Request $request)
@ -35,7 +35,7 @@ class SubpedidoController extends Controller
$s->nombre = $validado["nombre"];
$s->grupo_de_compra_id = $validado["grupo_de_compra_id"];
$s->save();
return $s;
return $this->show($s);
}
protected function validateSubpedido(): array
@ -57,7 +57,7 @@ class SubpedidoController extends Controller
// recibe request, saca producto y cantidad, valida, y pasa a syncProducto en Subpedido
public function syncProductos(Subpedido $subpedido) {
if ($subpedido->aprobado)
return new SubpedidoResource($subpedido);
abort(400, "No se puede modificar un pedido aprobado.");
$valid = request()->validate([
'cantidad' => ['integer','required','min:0'],
@ -80,11 +80,12 @@ class SubpedidoController extends Controller
'aprobacion' => 'required | boolean'
]);
$subpedido->toggleAprobacion($valid['aprobacion']);
return new SubpedidoResource($subpedido);
return response()->noContent();
}
public function syncDevoluciones(Subpedido $subpedido) {
if ($subpedido->aprobado) return new SubpedidoResource($subpedido);
if ($subpedido->aprobado)
abort(400, "No se puede modificar un pedido aprobado.");
$valid = request()->validate([
'total' => 'required|min:0',

View file

@ -6,7 +6,6 @@ use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
@ -31,14 +30,7 @@ class LoginController extends Controller
protected function authenticated(Request $request, $user)
{
if ($user->is_compras) {
return redirect('compras/pedidos');
} else if ($user->is_admin) {
session(['admin_gdc' => $user->grupo_de_compra_id]);
return redirect('admin/pedidos');
} else {
return redirect('/');
}
return redirect('/');
}
/**

View file

@ -0,0 +1,34 @@
<?php
namespace App\Http\Controllers;
use App\UserRole;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RouteController extends Controller
{
function home(Request $request) {
if (!Auth::check())
return redirect('/login');
$barrio = UserRole::where('nombre', 'barrio')->first();
$admin = UserRole::where('nombre', 'admin_barrio')->first();
$comision = UserRole::where('nombre', 'comision')->first();
switch ($request->user()->role_id) {
case $barrio->id:
return redirect('/pedido');
case $admin->id:
return redirect('/admin');
case $comision->id:
return redirect('/compras');
default:
abort(400, 'Rol de usuario invalido');
}
}
function main(Request $request) {
return view('main');
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Http\Controllers;
use App\Subpedido;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;
class SessionController extends Controller
{
public function store(Request $request) {
$grupo_de_compra_id = Auth::user()->grupo_de_compra_id;
$validated = $request->validate([
'id' => 'required',
Rule::in(Subpedido::where('grupo_de_compra_id', $grupo_de_compra_id)->pluck('id')),
]);
session()->put('pedido_id', $validated["id"]);
return response()->noContent();
}
public function fetch() {
return session('pedido_id');
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace App\Http\Controllers;
use App\GrupoDeCompra;
use App\Http\Resources\GrupoDeCompraReducido;
use App\Http\Resources\GrupoDeCompraResource;
use App\UserRole;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class UserController extends Controller
{
public function rol(Request $request) {
return ["rol" => UserRole::find($request->user()->role_id)->nombre];
}
public function grupoDeCompra(Request $request)
{
$user = Auth::user();
$result = [ 'grupo_de_compra' => null, ];
$grupo_de_compra = GrupoDeCompra::find($user->grupo_de_compra_id);
switch (UserRole::findOrFail($user->role_id)->nombre) {
case 'barrio':
$result['grupo_de_compra'] = new GrupoDeCompraReducido($grupo_de_compra);
break;
case 'admin_barrio':
$result['grupo_de_compra'] = new GrupoDeCompraResource($grupo_de_compra);
break;
case 'comision':
break;
default:
abort(400, 'Rol invalido.');
}
return $result;
}
}

View file

@ -2,6 +2,7 @@
namespace App\Http;
use App\Http\Middleware\CheckRole;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
@ -58,6 +59,7 @@ class Kernel extends HttpKernel
'auth' => \App\Http\Middleware\Authenticate::class,
'admin' => \App\Http\Middleware\Admin::class,
'compras' => \App\Http\Middleware\Compras::class,
'role' => \App\Http\Middleware\CheckRole::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,

View file

@ -1,20 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
class Admin
{
public function handle(Request $request, Closure $next)
{
$user = Auth::user();
if ($user->is_admin) {
return $next($request);
} else {
return response('Necesitás ser admin para hacer esto', 403);
}
}
}

View file

@ -14,7 +14,12 @@ class Authenticate extends Middleware
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
if (!$request->expectsJson()) {
$path = $request->path();
if (preg_match('~^admin.*~i', $path))
return route('admin.login');
if (preg_match('~^compras.*~i', $path))
return route('compras.login');
return route('login');
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Http\Middleware;
use App\UserRole;
use Closure;
use Illuminate\Http\Request;
class CheckRole
{
/**
* Handle the incoming request.
*
* @param Request $request
* @param Closure $next
* @param string $role
* @return mixed
*/
public function handle($request, Closure $next, $role)
{
$role_id = UserRole::where('nombre', $role)->first()->id;
return $request->user()->role_id == $role_id ? $next($request)
: response('No tenés permiso para esto.', 403);
}
}

View file

@ -1,29 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class Compras
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if (!Auth::check())
return redirect()->route('compras_login.show');
if (Auth::user()->is_compras) {
return $next($request);
} else {
return response('Necesitás ser de comisión compras para hacer esto', 403);
}
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class GrupoDeCompraReducido extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request): array {
return [
'id' => $this->id,
'nombre' => $this->nombre,
'devoluciones_habilitadas' => $this->devoluciones_habilitadas,
];
}
}

View file

@ -20,13 +20,8 @@ class ProductoResource extends JsonResource
'nombre' => $this->nombre,
'precio' => $this->precio,
'categoria' => $this->categoria,
'proveedor' => optional($this->proveedor)->nombre,
'economia_solidaria' => optional($this->proveedor)->economia_solidaria,
'nacional' => optional($this->proveedor)->nacional,
'imagen' => optional($this->poster)->url(),
'descripcion' => $this->descripcion,
'apto_veganxs' => $this->apto_veganxs,
'apto_celiacxs' => $this->apto_celiacxs,
'economia_solidaria' => $this->es_solidario,
'nacional' => $this->es_solidario,
'requiere_notas' => $this->requiere_notas,
];
}

View file

@ -15,11 +15,14 @@ class SubpedidoResource extends JsonResource
*/
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,
'grupo_de_compra' => $this->grupoDeCompra,
'productos' => $this->productos,
'productos' => $productos,
'aprobado' => (bool) $this->aprobado,
'total' => number_format($this->total(),2),
'total_transporte' => number_format($this->totalTransporte()),

View file

@ -7,7 +7,6 @@ use App\Helpers\CsvHelper;
use App\Helpers\TransporteHelper;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
@ -15,21 +14,14 @@ use Illuminate\Support\Facades\DB;
class Producto extends Model
{
public $timestamps = false;
protected $fillable = ["nombre", "precio", "presentacion", "stock", "categoria"];
static int $paginarPorDefecto = 10;
protected $fillable = ["nombre", "precio", "categoria"];
public function subpedidos(): BelongsToMany
{
return $this->belongsToMany('App\Subpedido', 'productos_subpedidos')->withPivot(["cantidad", "notas"]);
return $this->belongsToMany(Subpedido::class, 'productos_subpedidos')->withPivot(["cantidad", "notas"]);
}
public function proveedor(): BelongsTo
{
return $this->belongsTo('App\Proveedor');
}
//Este método 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, FiltroDeProducto $filtros): Builder
{
return $filtros->aplicar($query);
@ -37,7 +29,7 @@ class Producto extends Model
public static function getPaginar(Request $request): int
{
return $request->has('paginar') && intval($request->input('paginar')) ? intval($request->input('paginar')) : self::$paginarPorDefecto;
return $request->has('paginar') && intval($request->input('paginar')) ? intval($request->input('paginar')) : self::all()->count();
}
public static function productosFilaID()

View file

@ -1,18 +0,0 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Proveedor extends Model
{
public $timestamps = false;
protected $fillable = [ "nombre","direccion","telefono","correo","comentario" ];
protected $table = 'proveedores';
public function productos(): HasMany
{
return $this->hasMany('App\Producto');
}
}

View file

@ -12,17 +12,16 @@ use App\Filtros\FiltroDeSubpedido;
class Subpedido extends Model
{
public $timestamps = false;
protected $fillable = ['grupo_de_compra_id', 'aprobado', 'nombre', 'devoluciones_total', 'devoluciones_notas'];
public function productos(): BelongsToMany
{
return $this->belongsToMany('App\Producto')->withPivot(["cantidad", "total", "notas"]);
return $this->belongsToMany(Producto::class)->withPivot(["cantidad", "notas"]);
}
public function grupoDeCompra(): BelongsTo
{
return $this->belongsTo('App\GrupoDeCompra');
return $this->belongsTo(GrupoDeCompra::class);
}
// Permite que se apliquen los filtros al hacer una request (por ejemplo, de búsqueda)
@ -92,7 +91,7 @@ class Subpedido extends Model
return TransporteHelper::cantidadTransporte($this->totalCentralesQuePaganTransporte());
}
//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.
// 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, string $notas)
{
if ($cantidad) {
@ -100,7 +99,6 @@ class Subpedido extends Model
$this->productos()->syncWithoutDetaching([
$producto->id => [
'cantidad' => $cantidad,
'total' => $cantidad * $producto->precio,
'notas' => $notas,
]
]);
@ -108,11 +106,15 @@ class Subpedido extends Model
//si la cantidad es 0, se elimina el producto del subpedido
$this->productos()->detach($producto->id);
}
$this->updated_at = now();
$this->save();
}
public function toggleAprobacion(bool $aprobacion)
{
$this->aprobado = $aprobacion;
$this->update(['aprobado' => $aprobacion]);
$this->save();
}

View file

@ -16,7 +16,7 @@ class User extends Authenticatable
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
'name', 'email', 'password', 'role_id',
];
/**
@ -40,6 +40,6 @@ class User extends Authenticatable
public function grupoDeCompra(): BelongsTo
{
return $this->belongsTo('App\GrupoDeCompra');
return $this->belongsTo(GrupoDeCompra::class);
}
}

View file

@ -4,7 +4,7 @@ namespace App;
use Illuminate\Database\Eloquent\Model;
class Admin extends Model
class UserRole extends Model
{
//
protected $fillable = ["nombre"];
}

View file

@ -0,0 +1,41 @@
<?php
use App\UserRole;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class CreateUserRolesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_roles', function (Blueprint $table) {
$table->id();
$table->string('nombre');
$table->timestamps();
});
$tipos = ["barrio", "admin_barrio", "comision"];
foreach ($tipos as $tipo) {
UserRole::create([
"nombre" => $tipo,
]);
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_roles');
}
}

View file

@ -0,0 +1,43 @@
<?php
use App\User;
use App\UserRole;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AgregarRolAUser extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->foreignId('role_id');
});
$barrio = UserRole::where('nombre', 'barrio')->first();
$admin_barrio = UserRole::where('nombre', 'admin_barrio')->first();
$comision = UserRole::where('nombre', 'comision')->first();
User::all()->each(function($user) use ($barrio, $comision, $admin_barrio) {
$user->role_id = $user->is_admin ? $admin_barrio->id :
($user->is_compras ? $comision->id : $barrio->id);
$user->save();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('user', function (Blueprint $table) {
$table->dropForeign('role_id');
});
}
}

View file

@ -0,0 +1,43 @@
<?php
use App\User;
use App\UserRole;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class SimplificarUsers extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['is_admin', 'is_compras']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('is_admin')->default(false);
$table->boolean('is_compras')->default(false);
});
$admin_barrio = UserRole::where('nombre', 'admin_barrio')->first();
$comision = UserRole::where('nombre', 'comision')->first();
foreach (User::all() as $user) {
$user->is_admin = $user->role_id == $admin_barrio->id;
$user->is_compras = $user->role_id == $comision->id;
$user->save();
}
}
}

View file

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class SimplificarBarrios extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('grupos_de_compra', function (Blueprint $table) {
$table->dropColumn([
'cantidad_de_nucleos',
'telefono',
'correo',
'referente_finanzas',
]);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('grupos_de_compra', function (Blueprint $table) {
$table->double('cantidad_de_nucleos');
$table->string('telefono');
$table->string('correo');
$table->string('referente_finanzas');
});
}
}

View file

@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class SimplificarProductos extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('productos', function (Blueprint $table) {
$table->dropColumn([
'presentacion',
'stock',
'imagen_id',
'descripcion',
'apto_veganxs',
'apto_celiacxs',
]);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('productos', function (Blueprint $table) {
$table->integer('presentacion')->nullable();
$table->integer('stock')->nullable();
$table->foreignId('imagen_id')->nullable();
$table->string('descripcion')->nullable();
$table->boolean('apto_veganxs')->nullable();
$table->boolean('apto_celiacxs')->nullable();
});
}
}

View file

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

View file

@ -0,0 +1,38 @@
<?php
use App\Producto;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AgregarEsSolidario extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('productos', function (Blueprint $table) {
$table->boolean('es_solidario')->default(false);
});
foreach (Producto::all() as $producto) {
$producto->es_solidario = $producto->proveedor_id != null;
$producto->save();
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('productos', function (Blueprint $table) {
$table->dropColumn('es_solidario');
});
}
}

View file

@ -0,0 +1,59 @@
<?php
use App\Producto;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class EliminarProveedor extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('productos', function (Blueprint $table) {
$table->dropColumn('proveedor_id');
});
Schema::dropIfExists('proveedores');
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::create('proveedores', function (Blueprint $table) {
$table->id();
$table->string('nombre');
$table->string('direccion')->nullable();
$table->string('telefono')->nullable();
$table->string('correo')->nullable();
$table->boolean('economia_solidaria')->nullable();
$table->boolean('nacional')->nullable();
$table->text('detalles_de_pago')->nullable();
$table->text('comentario')->nullable();
$table->timestamps();
});
Schema::table('productos', function (Blueprint $table) {
$table->foreignId('proveedor_id')->nullable();
});
$proveedor_id = DB::table('proveedores')->insertGetId([
['nombre' => 'Proveedor de economía solidaria',
'economia_solidaria' => 1,
'nacional' => 1]
]);
foreach (Producto::all() as $producto) {
$producto->proveedor_id = $producto->es_solidario ? $proveedor_id : null;
$producto->save();
}
}
}

View file

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class EliminarAdmin extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::dropIfExists('admins');
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::create('admins', function (Blueprint $table) {
$table->id();
$table->string('nombre');
$table->foreignId('grupo_de_compra_id');
$table->string('email');
$table->string('contrasena');
$table->timestamps();
});
}
}

View file

@ -1,10 +1,19 @@
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class DatabaseSeeder extends Seeder
{
const CHUNK_SIZE = 100;
static function addTimestamps($object) {
$now = DB::raw('CURRENT_TIMESTAMP');
$object['created_at'] = $now;
$object['updated_at'] = $now;
return $object;
}
/**
* Seed the application's database.
*

View file

@ -1,8 +1,10 @@
<?php
use App\Helpers\CsvHelper as CsvHelperAlias;
use App\Helpers\CsvHelper;
use App\GrupoDeCompra;
use App\User;
use App\UserRole;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
class GrupoDeCompraSeeder extends Seeder
@ -14,42 +16,32 @@ class GrupoDeCompraSeeder extends Seeder
*/
public function run()
{
$registros = CsvHelperAlias::getRecords('csv/barrios.csv');
$registros = CsvHelper::getRecords('csv/barrios.csv');
$gdcToInsert = [];
$usersToInsert = [];
$roles = UserRole::where('nombre', 'barrio')->orWhere('nombre', 'admin_barrio')->get();
foreach($registros as $key => $registro){
$gdcToInsert[] = [
$gdcToInsert[] = DatabaseSeeder::addTimestamps([
'nombre' => $registro['barrio'],
'region' => $registro['region'],
'telefono' => $registro['telefono'],
'correo' => $registro['correo'],
'referente_finanzas' => $registro['referente']
];
]);
$usersToInsert[] = [
'name' => $registro['barrio'],
'password' => Hash::make("123"),
"is_admin" => 0,
'grupo_de_compra_id' => $key
];
$usersToInsert[] = [
'name' => $registro['barrio'] . "_admin",
'password' => Hash::make("123"),
"is_admin" => 1,
'grupo_de_compra_id' => $key
];
foreach($roles as $role) {
$nombre = $registro['barrio'] . ($role->nombre == 'barrio' ? '' : '_admin');
$usersToInsert[] = DatabaseSeeder::addTimestamps([
'name' => $nombre,
'password' => Hash::make("123"),
'role_id' => $role->id,
'grupo_de_compra_id' => $key,
]);
}
}
foreach (array_chunk($gdcToInsert,DatabaseSeeder::CHUNK_SIZE) as $chunk)
{
DB::table('grupos_de_compra')->insert($chunk);
}
GrupoDeCompra::insert($chunk);
foreach (array_chunk($usersToInsert,DatabaseSeeder::CHUNK_SIZE) as $chunk)
{
DB::table('users')->insert($chunk);
}
User::insert($chunk);
}
}

View file

@ -1,7 +1,8 @@
<?php
use App\User;
use App\UserRole;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
class UserSeeder extends Seeder
@ -15,17 +16,14 @@ class UserSeeder extends Seeder
{
$usersToInsert = [];
$usersToInsert[] = [
'name' => 'compras',
$usersToInsert[] = DatabaseSeeder::addTimestamps([
'name' => 'comi',
'password' => Hash::make("123"),
'is_admin' => 0,
'is_compras' => 1
];
'role_id' => UserRole::where('nombre', 'comision')->first()->id,
]);
foreach (array_chunk($usersToInsert,DatabaseSeeder::CHUNK_SIZE) as $chunk)
{
DB::table('users')->insert($chunk);
}
User::insert($chunk);
}
}

61
package-lock.json generated
View file

@ -8,7 +8,8 @@
"animate.css": "^4.1.1",
"bulma": "^0.9.4",
"bulma-switch": "^2.0.4",
"bulma-toast": "^2.4.1"
"bulma-toast": "^2.4.1",
"vuex": "^3.6.2"
},
"devDependencies": {
"axios": "^0.19.2",
@ -350,7 +351,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@ -359,7 +359,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@ -404,7 +403,6 @@
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz",
"integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==",
"dev": true,
"dependencies": {
"@babel/types": "^7.27.1"
},
@ -1568,7 +1566,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
"integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
@ -2060,7 +2057,6 @@
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
"integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.23.5",
"postcss": "^8.4.14",
@ -2075,7 +2071,6 @@
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true,
"funding": [
{
"type": "opencollective",
@ -4609,8 +4604,7 @@
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/cyclist": {
"version": "1.0.2",
@ -8717,7 +8711,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
"type": "github",
@ -9555,8 +9548,7 @@
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/picomatch": {
"version": "2.3.1",
@ -10557,7 +10549,6 @@
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"dev": true,
"optional": true,
"bin": {
"prettier": "bin-prettier.js"
@ -12085,7 +12076,6 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -12094,7 +12084,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -13345,7 +13334,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz",
"integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
"deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
"dev": true,
"dependencies": {
"@vue/compiler-sfc": "2.7.16",
"csstype": "^3.1.0"
@ -13463,6 +13451,14 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"node_modules/vuex": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
"integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==",
"peerDependencies": {
"vue": "^2.0.0"
}
},
"node_modules/watchpack": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
@ -15238,14 +15234,12 @@
"@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"dev": true
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="
},
"@babel/helper-validator-identifier": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"dev": true
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="
},
"@babel/helper-validator-option": {
"version": "7.27.1",
@ -15278,7 +15272,6 @@
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz",
"integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==",
"dev": true,
"requires": {
"@babel/types": "^7.27.1"
}
@ -16040,7 +16033,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
"integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
"dev": true,
"requires": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
@ -16309,7 +16301,6 @@
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
"integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
"dev": true,
"requires": {
"@babel/parser": "^7.23.5",
"postcss": "^8.4.14",
@ -16321,7 +16312,6 @@
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true,
"requires": {
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
@ -18344,8 +18334,7 @@
"csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"cyclist": {
"version": "1.0.2",
@ -21555,8 +21544,7 @@
"nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="
},
"nanomatch": {
"version": "1.2.13",
@ -22207,8 +22195,7 @@
"picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"picomatch": {
"version": "2.3.1",
@ -23060,7 +23047,6 @@
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"dev": true,
"optional": true
},
"private": {
@ -24290,14 +24276,12 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
},
"source-map-resolve": {
"version": "0.5.3",
@ -25287,7 +25271,6 @@
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz",
"integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
"dev": true,
"requires": {
"@vue/compiler-sfc": "2.7.16",
"csstype": "^3.1.0"
@ -25382,6 +25365,12 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"vuex": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
"integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==",
"requires": {}
},
"watchpack": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",

View file

@ -27,6 +27,7 @@
"animate.css": "^4.1.1",
"bulma": "^0.9.4",
"bulma-switch": "^2.0.4",
"bulma-toast": "^2.4.1"
"bulma-toast": "^2.4.1",
"vuex": "^3.6.2"
}
}

126
resources/js/app.js vendored
View file

@ -5,6 +5,7 @@
*/
import axios from 'axios';
import Vue from 'vue';
window.Vue = require('vue');
window.Event = new Vue();
window.axios = axios;
@ -18,33 +19,18 @@ window.bulmaToast = require('bulma-toast');
* Eg. ./components/ExampleComponent.vue -> <example-component></example-component>
*/
import './components';
import store from "./store";
/**
* Constants
*/
Vue.prototype.$rootMiga = {
nombre: "Categorías",
href: "/productos"
}
/**
* Global methods
*/
Vue.prototype.$settearProducto = function(cantidad, id) {
Event.$emit("sync-subpedido", this.cant, this.producto.id)
}
Vue.prototype.$toast = function(mensaje, duration = 2000) {
return window.bulmaToast.toast({
message: mensaje,
duration: duration,
type: 'is-danger',
position: 'bottom-center',
});
}
Vue.prototype.$limpiarFloat = function(unFloat) {
return parseFloat(unFloat.replace(/,/g, ''))
}
Vue.prototype.$limpiarInt = function(unInt) {
return parseInt(unInt.replace(/,/g, ''))
Vue.prototype.$toast = function (mensaje, duration = 2000) {
return window.bulmaToast.toast({
message: mensaje,
duration: duration,
type: 'is-danger',
position: 'bottom-center',
});
}
/**
@ -52,98 +38,8 @@ Vue.prototype.$limpiarInt = function(unInt) {
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
const app = new Vue({
new Vue({
el: '#root',
data() {
return {
gdc: null,
pedido: null,
devoluciones: null,
}
},
computed: {
productos: function() {
return this.pedido.productos
}
},
methods: {
cantidad(producto) {
let pedido = this.productos.some(p => p.id == producto.id)
return pedido ? this.productos.find(p => p.id == producto.id).pivot.cantidad : 0
},
notas(producto) {
let pedido = this.productos.some(p => p.id == producto.id);
return pedido ? this.productos.find(p => p.id == producto.id).pivot.notas : "";
},
settearDevoluciones() {
axios.get(`/api/grupos-de-compra/${this.gdc}/devoluciones`)
.then(response => {
this.devoluciones = response.data.devoluciones;
});
}
},
mounted() {
Event.$on('obtener-sesion', () => {
axios.get('/subpedidos/obtener_sesion')
.then(response => {
if (response.data.subpedido.id) {
this.gdc = response.data.gdc;
this.settearDevoluciones();
this.pedido = response.data.subpedido.id;
axios.get('/api/subpedidos/' + this.pedido)
.then(response => {
this.pedido = response.data.data;
Event.$emit("pedido-actualizado");
});
} else {
axios.get('/admin/obtener_sesion')
.then(response => {
this.gdc = response.data.gdc
});
}
})
})
Event.$on('sync-subpedido', (cantidad, id, notas) => {
if (this.pedido.aprobado) {
this.$toast('No se puede modificar un pedido ya aprobado', 2000);
return;
}
axios.post("/api/subpedidos/" + this.pedido.id + "/sync", {
cantidad: cantidad,
producto_id: id,
notas: notas,
}).then((response) => {
this.pedido = response.data.data
this.$toast('Pedido actualizado exitosamente')
Event.$emit("pedido-actualizado");
});
});
// Actualizar monto y notas de devoluciones
Event.$on('sync-devoluciones', (total, notas) => {
if (this.pedido.aprobado) {
this.$toast('No se puede modificar un pedido ya aprobado', 2000);
return;
}
axios.post("api/subpedidos/" + this.pedido.id + "/sync_devoluciones", {
total: total,
notas: notas,
}).then((response) => {
this.pedido = response.data.data;
this.$toast('Pedido actualizado');
Event.$emit("pedido-actualizado");
});
});
Event.$on('aprobacion-subpedido', (subpedidoId, aprobado) => {
axios.post("/api/admin/subpedidos/" + subpedidoId + "/aprobacion", {
aprobacion: aprobado
}).then((response) => {
Event.$emit('sync-aprobacion', response.data.data);
this.$toast('Pedido ' + (aprobado ? 'aprobado' : 'desaprobado') + ' exitosamente')
})
})
Event.$emit('obtener-sesion')
},
store,
});

View file

@ -0,0 +1,30 @@
<script>
import NavBar from "./comunes/NavBar.vue";
import { mapActions, mapState } from "vuex";
export default {
name: 'Main',
components: { NavBar },
computed: {
...mapState('login',["rol"]),
},
methods: {
...mapActions('login',["getRol"]),
},
async mounted() {
await this.getRol();
},
}
</script>
<template>
<div id="app-main">
<nav-bar></nav-bar>
<pedidos-body v-if="rol === 'barrio'"></pedidos-body>
<admin-body v-if="rol === 'admin_barrio'"></admin-body>
<compras-body v-if="rol === 'comision'"></compras-body>
</div>
</template>
<style scoped>
</style>

View file

@ -1,15 +1,11 @@
<template>
<div class="block ml-3 mr-3 is-max-widescreen is-max-desktop">
<comunes-tabs-secciones :tabs="tabs" :tabInicial="tabActiva"></comunes-tabs-secciones>
<tabs-secciones :tabs="tabs" :tabInicial="tabActiva"></tabs-secciones>
<div class="block" id="pedidos-seccion"
:class="seccionActiva === 'pedidos-seccion' ? 'is-active' : 'is-hidden'">
<div class="block pb-6" id="pedidos-tabla-y-dropdown" v-if="hayPedidos">
<admin-dropdown-descargar
:gdc_id="gdc.id">
</admin-dropdown-descargar>
<admin-tabla-pedidos
:gdc="this.gdc"
></admin-tabla-pedidos>
<dropdown-descargar></dropdown-descargar>
<tabla-pedidos></tabla-pedidos>
</div>
<p class="has-text-centered" v-else>
Todavía no hay ningún pedido para administrar.
@ -17,8 +13,7 @@
</div>
<div class="block pb-6" id="caracteristicas-seccion"
:class="seccionActiva === 'caracteristicas-seccion' ? 'is-active' : 'is-hidden'">
<admin-caracteristicas-opcionales>
</admin-caracteristicas-opcionales>
<caracteristicas-opcionales></caracteristicas-opcionales>
</div>
</div>
</template>
@ -29,7 +24,7 @@ import TabsSecciones from "../comunes/TabsSecciones.vue";
import DropdownDescargar from "./DropdownDescargar.vue";
import TablaPedidos from "./TablaPedidos.vue";
import TablaBonos from "./TablaBonos.vue";
import axios from "axios";
import { mapActions, mapGetters } from "vuex";
export default {
components: {
CaracteristicasOpcionales,
@ -40,7 +35,6 @@ export default {
},
data() {
return {
gdc: undefined,
tabs: [{ id: "pedidos", nombre: "Pedidos" },
{ id: "caracteristicas", nombre: "Caracteristicas opcionales" }],
tabActiva: "pedidos",
@ -48,32 +42,17 @@ export default {
}
},
computed: {
hayPedidos: function() {
return this.gdc && this.gdc.pedidos.length !== 0
},
hayAprobados: function() {
return this.gdc && this.gdc.pedidos.filter(p => p.aprobado).length > 0
}
...mapGetters('admin', ['hayPedidos']),
},
methods: {
...mapActions('admin', ['getGrupoDeCompra']),
setSeccionActiva(tabId) {
this.tabActiva = tabId;
this.seccionActiva = tabId + "-seccion";
},
actualizar() {
axios.get('/api/grupos-de-compra/' + this.$root.gdc)
.then(response => {
this.gdc = response.data.data;
console.log(this.gdc);
})
}
},
async mounted() {
Event.$on('sync-aprobacion', (_) => {
this.actualizar();
});
await new Promise(r => setTimeout(r, 1000));
this.actualizar();
await this.getGrupoDeCompra();
},
}
</script>

View file

@ -8,8 +8,7 @@ export default {
caracteristicas: [
{
id: "devoluciones",
nombre: "Devoluciones",
habilitada: false
nombre: "Devoluciones"
},
]
}
@ -27,16 +26,15 @@ export default {
</tr>
</thead>
<tbody>
<admin-fila-caracteristica
<fila-caracteristica
v-for="(c,i) in caracteristicas"
:key="i"
:caracteristica="c">
</admin-fila-caracteristica>
</fila-caracteristica>
</tbody>
</table>
</div>
</template>
<style scoped>
</style>

View file

@ -14,13 +14,13 @@
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
<a :href="'/admin/exportar-pedido-a-csv/' + gdc_id" class="dropdown-item has-background-primary">
<a :href="'/admin/exportar-pedido-a-csv/' + grupo_de_compra_id" class="dropdown-item has-background-primary">
Planilla para central (CSV)
</a>
<a :href="'/admin/exportar-planillas-a-pdf/' + gdc_id" class="dropdown-item">
<a :href="'/admin/exportar-planillas-a-pdf/' + grupo_de_compra_id" class="dropdown-item">
Planillas para armado (PDF)
</a>
<a :href="'/admin/exportar-pedido-con-nucleos-a-csv/' + gdc_id" class="dropdown-item">
<a :href="'/admin/exportar-pedido-con-nucleos-a-csv/' + grupo_de_compra_id" class="dropdown-item">
Planilla completa de la canasta (CSV)
</a>
</div>
@ -30,27 +30,19 @@
</template>
<script>
import { mapGetters, mapState } from "vuex";
export default {
props: {
gdc_id: {
type: Number,
required: true
},
},
data() {
return {
dropdownActivo: false
}
};
},
computed: {
hayAprobados: function() {
return this.$parent.hayAprobados;
}
...mapState('admin',["grupo_de_compra_id"]),
...mapGetters('admin',["hayAprobados"]),
},
}
</script>
<style>
</style>
<style></style>

View file

@ -1,48 +1,20 @@
<script>
import axios from "axios";
import {mapActions, mapGetters, mapState} from "vuex";
export default {
props: {
caracteristica: Object
},
data() {
return {
gdc: undefined
computed: {
...mapState('admin',["grupo_de_compra_id"]),
...mapGetters('admin',["getCaracteristica"]),
habilitada() {
return this.getCaracteristica(this.caracteristica.id);
}
},
watch: {
'$root.gdc' : {
handler(newValue) {
if (newValue) {
this.gdc = newValue;
this.obtenerValor();
}
}
},
},
methods: {
toggleActivacion() {
const id = this.caracteristica.id;
axios.post(`/api/grupos-de-compra/${this.gdc}/${id}`)
.then(response => {
this.caracteristica.habilitada = response.data[id];
this.$root[id] = response.data[id];
});
},
obtenerValor() {
const id = this.caracteristica.id;
axios.get(`/api/grupos-de-compra/${this.gdc}/${id}`)
.then(response => {
this.caracteristica.habilitada = response.data[id];
this.$root[id] = response.data[id];
});
},
},
mounted() {
if (this.$root.gdc) {
this.gdc = this.$root.gdc;
this.obtenerValor();
}
...mapActions('admin',["toggleCaracteristica"]),
}
}
</script>
@ -53,17 +25,17 @@ export default {
<td>
<div class="field">
<input type="checkbox" class="switch is-rounded is-success"
:id="'switch-'+caracteristica.id"
:checked="caracteristica.habilitada"
@change="toggleActivacion(caracteristica)">
<label :for="'switch-'+caracteristica.id">
<span class="is-hidden-mobile">{{ caracteristica.habilitada ? 'Habilitada' : 'Deshabilitada' }}</span>
:id="'switch-' + caracteristica.id"
:checked="habilitada"
@change="toggleCaracteristica({ caracteristica_id: caracteristica.id })">
<label :for="'switch-' + caracteristica.id">
<span class="is-hidden-mobile">
{{ habilitada ? 'Habilitada' : 'Deshabilitada' }}
</span>
</label>
</div>
</td>
</tr>
</template>
<style scoped>
</style>
<style scoped></style>

View file

@ -1,36 +1,39 @@
<template>
<tr>
<td>{{ pedido.nombre }}</td>
<td v-if="$root.devoluciones" class="has-text-right" >{{ pedido.total_sin_devoluciones }}</td>
<td v-if="$root.devoluciones" class="has-text-right" ><abbr :title="pedido.devoluciones_notas">-{{ pedido.devoluciones_total }}</abbr></td>
<td class="has-text-right" >{{ $root.devoluciones ? pedido.total : pedido.total_sin_devoluciones }}</td>
<td>
<admin-switch-aprobacion
:pedido="pedido">
</admin-switch-aprobacion>
<td v-if="devoluciones_habilitadas" class="has-text-right" >
{{ pedido.total_sin_devoluciones }}
</td>
<td v-if="devoluciones_habilitadas" class="has-text-right" >
<abbr :title="pedido.devoluciones_notas">
-{{ pedido.devoluciones_total }}
</abbr>
</td>
<td class="has-text-right" >
{{ devoluciones_habilitadas ? pedido.total : pedido.total_sin_devoluciones }}
</td>
<td><switch-aprobacion :pedido_id="pedido_id"></switch-aprobacion></td>
</tr>
</template>
<script>
import SwitchAprobacion from "./SwitchAprobacion.vue";
import { mapGetters, mapState } from "vuex";
export default {
components: {
SwitchAprobacion
},
props: {
pedido: Object
pedido_id: Number
},
computed: {
...mapState('admin',["devoluciones_habilitadas"]),
...mapGetters('admin',["getPedido"]),
pedido() {
return this.getPedido(this.pedido_id);
},
},
mounted() {
Event.$on('sync-aprobacion', (unPedido) => {
if (this.pedido.id === unPedido.id) {
this.pedido = unPedido
}
})
}
}
</script>
<style scoped>
</style>
<style scoped></style>

View file

@ -1,56 +0,0 @@
<template>
<div v-show="visible" class="block">
<div class="field">
<label class="label has-text-white">Contraseña de administración del barrio</label>
<div class="field has-addons">
<div class="control">
<input required class="input" :type="this.passwordType" name="password" placeholder="Contraseña de admin del barrio">
</div>
<div class="control">
<a class="button is-warning" @click="togglePassword">
{{ (passwordVisible ? 'Ocultar' : 'Mostrar') + ' contraseña'}}
</a>
</div>
</div>
<p class="help has-text-white">Si no la sabés, consultá a la comisión informática.</p>
</div>
<div class="field">
<div class="control">
<input type="submit" class="button is-warning" value="Ingresar"/>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
visible: false,
gdc: null,
passwordVisible: false,
passwordType: "password",
}
},
mounted() {
Event.$on('gdc-seleccionado', (gdc) => {
this.gdc = gdc;
this.visible = true;
});
},
methods: {
togglePassword() {
if (this.passwordVisible) this.passwordType = "password";
else this.passwordType = "text"
this.passwordVisible = !this.passwordVisible
}
}
}
</script>
<style>
.help {
font-size: 1rem;
}
</style>

View file

@ -1,46 +1,34 @@
<template>
<div class="field">
<input type="checkbox" name="switchRoundedSuccess" class="switch is-rounded is-success"
:id="'switch'+this.pedido.id"
:checked="pedido.aprobado"
@change="toggleAprobacion">
<label :for="'switch'+this.pedido.id">
:id="'switch' + pedido_id"
:checked="aprobado"
@change="setAprobacionPedido({ pedido_id: pedido_id, aprobacion: !aprobado })">
<label :for="'switch' + pedido_id">
<span class="is-hidden-mobile">{{ mensaje }}</span>
</label>
</div>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
export default {
props: {
pedido: Object
},
data() {
return {
aprobado: this.pedido.aprobado
}
pedido_id: Number
},
computed: {
mensaje: function () {
return this.aprobado ? "Pagado" : "No pagado"
...mapGetters('admin', ["getPedido"]),
aprobado() {
return this.getPedido(this.pedido_id).aprobado;
},
mensaje() {
return this.aprobado ? "Pagado" : "No pagado";
}
},
methods: {
toggleAprobacion() {
Event.$emit('aprobacion-subpedido', this.pedido.id, !this.aprobado);
this.aprobado = !this.aprobado
}
...mapActions('admin',["setAprobacionPedido"]),
},
mounted() {
Event.$on('sync-aprobacion', (unPedido) => {
if (this.pedido.id === unPedido.id) {
this.pedido = unPedido
}
})
}
}
</script>
<style scoped>
</style>
<style scoped></style>

View file

@ -4,17 +4,18 @@
<thead>
<tr>
<th>Núcleo</th>
<th v-if="$root.devoluciones"><abbr title="Total sin tomar en cuenta las devoluciones">Total parcial $</abbr></th>
<th v-if="$root.devoluciones"><abbr title="Devoluciones correspondientes al núcleo">Devoluciones $</abbr></th>
<th><abbr title="Total a Pagar por el núleo">{{ $root.devoluciones ? 'Total real' : 'Total' }} $</abbr></th>
<th v-if="devoluciones_habilitadas"><abbr title="Total sin tomar en cuenta las devoluciones">Total parcial $</abbr></th>
<th v-if="devoluciones_habilitadas"><abbr title="Devoluciones correspondientes al núcleo">Devoluciones $</abbr></th>
<th><abbr title="Total a Pagar por el núleo">{{ devoluciones_habilitadas ? 'Total real' : 'Total' }} $</abbr></th>
<th class="is-1"><abbr title="Pagado">Pagado</abbr></th>
</tr>
</thead>
<tbody>
<admin-fila-pedido
v-for="pedido in gdc.pedidos"
:pedido="pedido" :key="pedido.id">
</admin-fila-pedido>
<fila-pedido
v-for="pedido in pedidos"
:pedido_id="pedido.id"
:key="pedido.id">
</fila-pedido>
</tbody>
</table>
<table class="table is-striped is-bordered">
@ -23,27 +24,27 @@
</tr>
<tr>
<th>Total a recaudar:</th>
<td class="has-text-right">$ {{ gdc.total_a_recaudar }}</td>
<td class="has-text-right">$ {{ total_a_recaudar }}</td>
</tr>
<tr>
<th>Total bonos barriales:</th>
<td class="has-text-right">$ {{ gdc.total_barrial }}</td>
<td class="has-text-right">$ {{ total_barrial }}</td>
</tr>
<tr v-if="$root.devoluciones">
<tr v-if="devoluciones_habilitadas">
<th>Total devoluciones:</th>
<td class="has-text-right">- $ {{ gdc.total_devoluciones }}</td>
<td class="has-text-right">- $ {{ total_devoluciones }}</td>
</tr>
<tr>
<th>Cantidad bonos de transporte:</th>
<td class="has-text-right">{{ gdc.cantidad_transporte }}</td>
<td class="has-text-right">{{ cantidad_transporte }}</td>
</tr>
<tr>
<th>Total bonos de transporte:</th>
<td class="has-text-right">$ {{ gdc.total_transporte }}</td>
<td class="has-text-right">$ {{ total_transporte }}</td>
</tr>
<tr>
<th>Total a depositar:</th>
<td class="has-text-right">$ {{ gdc.total_a_transferir }}</td>
<td class="has-text-right">$ {{ total_a_transferir }}</td>
</tr>
</table>
</div>
@ -51,19 +52,25 @@
<script>
import FilaPedido from "./FilaPedido.vue";
import { mapGetters, mapState } from "vuex";
export default {
components: {
FilaPedido
},
props: {
gdc: {
type: Object,
required: true
}
}
computed: {
...mapState('admin', [
"devoluciones_habilitadas",
"pedidos",
"total_a_recaudar",
"total_barrial",
"total_devoluciones",
"cantidad_transporte",
"total_transporte",
"total_a_transferir",
]),
...mapGetters('admin', ['pedidosAprobados']),
},
}
</script>
<style>
</style>
<style></style>

View file

@ -1,13 +1,19 @@
<template>
<div v-show="visible" class="block">
<div v-if="region_elegida !== null" class="block">
<div class="field">
<label class="label" :class="isAdmin ? 'has-text-white' : ''">Seleccioná tu barrio o grupo de compra</label>
<label class="label" :class="adminUrl ? 'has-text-white' : ''">
Seleccioná tu barrio o grupo de compra
</label>
<div class="control">
<div class="select">
<select @change="onGDCSelected" v-model="gdc" name="name">
<option :disabled="isDefaultDisabled==1" value=null>Seleccionar</option>
<option v-for="(gdc, index) in gdcs" :key="index" v-text="gdc.nombre + (isAdmin ? '_admin' : '')"
:name="gdc.nombre + (isAdmin ? '_admin' : '')">
<select @change="selectGrupoDeCompra({ grupo_de_compra: barrio })" v-model="barrio">
<option :disabled="grupo_de_compra_elegido !== null" value=null>
Seleccionar
</option>
<option v-for="(gdc, index) in grupos_de_compra"
:key="index"
v-text="gdc.nombre"
:name="gdc.nombre">
</option>
</select>
</div>
@ -17,35 +23,24 @@
</template>
<script>
export default {
data() {
return {
visible: false,
region: null,
gdcs: [],
isDefaultDisabled: 0,
gdc: null,
isAdmin: this.admin == null ? false : this.admin
}
},
mounted() {
Event.$on('region-seleccionada', (region)=> {
this.region = region;
this.fillGDC(region);
this.visible = true;
});
},
methods : {
fillGDC(region) {
axios.get("/api/grupos-de-compra").then(response => {
this.gdcs = response.data[this.region];
});
},
onGDCSelected() {
this.isDefaultDisabled = 1;
Event.$emit("gdc-seleccionado",this.gdc);
}
},
props: {'admin': Boolean}
}
import { mapActions, mapGetters, mapMutations, mapState } from "vuex";
export default {
name: 'BarrioSelect',
async mounted() {
await this.getRegiones();
},
methods: {
...mapMutations('login',["selectGrupoDeCompra"]),
...mapActions('login',["getRegiones", "getGruposDeCompra"]),
},
computed: {
...mapState('login',["region_elegida","grupos_de_compra","grupo_de_compra_elegido"]),
...mapGetters('login',["adminUrl"]),
},
data() {
return {
barrio: null,
};
},
}
</script>

View file

@ -0,0 +1,57 @@
<template>
<div v-if="grupo_de_compra_elegido !== null" class="block">
<div class="field">
<label class="label"
:class="adminUrl ? 'has-text-white' : ''">{{ mensajes.mensaje }}</label>
<div class="field has-addons">
<div class="control">
<input required class="input" :type="this.passwordType" name="password" :placeholder="mensajes.mensaje">
</div>
<div class="control">
<a class="button is-warning" @click="togglePassword">
{{ (passwordVisible ? 'Ocultar' : 'Mostrar') + ' contraseña'}}
</a>
</div>
</div>
<p class="help"
:class="adminUrl ? 'has-text-white' : ''">{{ mensajes.ayuda }}</p>
</div>
<div class="field">
<div class="control">
<input type="submit" class="button is-warning" value="Log in"/>
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapState } from "vuex";
export default {
name: 'Login',
data() {
return {
passwordVisible: false,
passwordType: "password",
}
},
computed: {
...mapState('login',["grupo_de_compra_elegido"]),
...mapGetters('login',["adminUrl","mensajes"]),
},
methods: {
togglePassword() {
if (this.passwordVisible) this.passwordType = "password";
else this.passwordType = "text"
this.passwordVisible = !this.passwordVisible
}
}
}
</script>
<style>
.help {
font-size: 1rem;
}
</style>

View file

@ -0,0 +1,35 @@
<template>
<div class="block">
<barrio-select></barrio-select>
<login></login>
<input readonly v-model="nombre" type="hidden" name="name">
</div>
</template>
<script>
import {mapActions, mapGetters, mapState} from "vuex";
import BarrioSelect from "./BarrioSelect.vue";
import Login from "./Login.vue";
export default {
components: {
BarrioSelect,
Login,
},
async mounted() {
await this.getRegiones();
},
computed: {
...mapGetters('login',["adminUrl"]),
...mapState('login',["grupo_de_compra_elegido"]),
nombre() {
return `${this.grupo_de_compra_elegido}${this.adminUrl ? '_admin' : ''}`;
}
},
methods: {
...mapActions('login',["getRegiones"]),
},
}
</script>
<style scoped></style>

View file

@ -1,91 +1,110 @@
<template>
<nav id="nav-bar" class="navbar is-danger is-fixed-top" 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>
<!-- Styles nombre del barrio-->
<p class="navbar-item hide-below-1024">
<slot name="gdc"></slot>
</p>
<p class="navbar-item">
<slot name="subpedido"></slot>
</p>
<pedidos-chismosa-dropdown v-if="this.$root.pedido != null" class="hide-above-1023" id="mobile"></pedidos-chismosa-dropdown>
<a role="button" class="navbar-burger" :class="{'is-active':burgerActiva}" aria-label="menu" aria-expanded="false" data-target="nav-bar" @click="toggleBurger">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div class="navbar-menu" :class="{'is-active':burgerActiva}">
<div class="navbar-end">
<div v-if="this.$root.pedido != null" class="navbar-item field has-addons mt-2 mr-3">
<a class="button is-small has-text-dark-grey" @click.capture="buscar">
<div class="navbar-brand">
<a class="navbar-item" href="https://mps.org.uy">
<img src="/assets/logoMPS.png" height="28">
</a>
<div class="navbar-item" id="datos-pedido" v-if="pedidoDefinido">
<p class="hide-below-1024">
{{ `Núcleo: ${nombre} - Barrio: ${grupo_de_compra}` }}
</p>
</div>
<chismosa-dropdown
v-if="pedidoDefinido"
class="hide-above-1023"
ariaControls="mobile">
</chismosa-dropdown>
<a role="button" class="navbar-burger" :class="{'is-active':burgerActiva}" aria-label="menu"
aria-expanded="false" data-target="nav-bar" @click="toggleBurger">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div class="navbar-menu" :class="{'is-active':burgerActiva}">
<div class="navbar-end">
<div v-if="pedidoDefinido" class="navbar-item field has-addons mt-2 mr-3">
<a class="button is-small has-text-dark-grey" @click.capture="buscar">
<span class="icon">
<i class="fas fa-search"></i>
</span>
</a>
<input class="input is-small" type="text" placeholder="Harina" v-model="searchString" @keyup.enter="buscar" >
</div>
<pedidos-chismosa-dropdown v-if="this.$root.pedido != null" class="hide-below-1024" id="wide"></pedidos-chismosa-dropdown>
<div class="block navbar-item">
<a onclick="event.preventDefault(); document.getElementById('logout-form').submit();" class="text-a">
Cerrar sesión
</a>
<slot name="logout-form"></slot>
</a>
<input class="input is-small" type="text" placeholder="Harina" v-model="searchString"
@keyup.enter="buscar">
</div>
<chismosa-dropdown
v-if="pedidoDefinido"
class="hide-below-1024"
ariaControls="wide">
</chismosa-dropdown>
<div class="block navbar-item">
<a onclick="event.preventDefault(); document.getElementById('logout-form').submit();"
class="text-a">
Cerrar sesión
</a>
<slot name="logout-form"></slot>
</div>
</div>
</div>
</div>
</nav>
</template>
<script>
import ChismosaDropdown from '../pedidos/ChismosaDropdown.vue';
import { mapActions, mapGetters, mapMutations, mapState } from "vuex";
export default {
components: { ChismosaDropdown },
data() {
return {
burgerActiva: false,
searchString: "",
}
},
methods: {
toggleBurger() {
this.burgerActiva = !this.burgerActiva
components: { ChismosaDropdown },
data() {
return {
burgerActiva: false,
searchString: "",
}
},
computed: {
...mapGetters('pedido', ["pedidoDefinido"]),
...mapState('pedido',["nombre"]),
...mapState('barrio',["grupo_de_compra"]),
},
methods: {
...mapActions('productos', ["filtrarProductos"]),
...mapMutations('ui',["addMiga"]),
toggleBurger() {
this.burgerActiva = !this.burgerActiva
},
buscar() {
if (this.burgerActiva)
this.toggleBurger();
this.filtrarProductos({ filtro: "nombre", valor: this.searchString });
this.addMiga({ nombre: this.searchString });
}
},
buscar() {
if (this.burgerActiva) this.toggleBurger()
Event.$emit("migas-setear-como-inicio", this.$rootMiga)
Event.$emit("filtrar-productos",'nombre',this.searchString)
}
},
};
</script>
<style>
p.navbar-item:empty {
display: none;
display: none;
}
#nav-bar {
z-index: 10;
z-index: 10;
}
.text-a {
color: inherit;
color: inherit;
}
@media (max-width: 1023px) {
.hide-below-1024 {
display: none !important;
}
.hide-below-1024 {
display: none !important;
}
}
@media (min-width: 1024px) {
.hide-above-1023 {
display: none !important;
}
.hide-above-1023 {
display: none !important;
}
}
</style>

View file

@ -1,12 +1,20 @@
<template>
<div class="block">
<div class="field">
<label class="label" :class="whiteText ? 'has-text-white' : ''">Seleccioná tu región</label>
<label class="label" :class="adminUrl ? 'has-text-white' : ''">
Seleccioná tu región
</label>
<div class="control">
<div class="select">
<select @change="onRegionSelected" v-model="region">
<option :disabled="isDefaultDisabled===1" value=null>Seleccionar</option>
<option v-for="(region, index) in regiones" :key="index" v-text="region" :name="region"></option>
<select @change="selectRegion({ region })" v-model="region">
<option :disabled="region_elegida !== null" value=null>
Seleccionar
</option>
<option v-for="(region, index) in regiones"
:key="index"
v-text="region"
:name="region">
</option>
</select>
</div>
</div>
@ -15,24 +23,22 @@
</template>
<script>
export default {
data() {
return {
regiones: [],
isDefaultDisabled: 0,
region: null,
whiteText: this.admin == null ? false : this.admin
}
},
mounted() {
axios.get("/api/regiones").then(response => this.regiones = response.data);
},
methods: {
onRegionSelected() {
this.isDefaultDisabled = 1;
Event.$emit("region-seleccionada",this.region);
}
},
props: {'admin': Boolean}
}
import {mapActions, mapGetters, mapState} from "vuex";
export default {
async mounted() {
await this.getRegiones();
},
data() {
return {
region: null,
};
},
methods: {
...mapActions('login',["getRegiones","selectRegion"]),
},
computed: {
...mapState('login',["regiones","region_elegida"]),
...mapGetters('login',["adminUrl"]),
}
}
</script>

View file

@ -1,22 +1,29 @@
<template>
<div class="columns ml-3 mr-3">
<pedidos-categorias-container :class="chismosaActiva ? 'hide-below-1024' : ''"></pedidos-categorias-container>
<pedidos-productos-container :class="chismosaActiva ? 'hide-below-1024' : ''"></pedidos-productos-container>
<pedidos-chismosa v-show="chismosaActiva"></pedidos-chismosa>
<div id="pedidos-body">
<cartel-pedido-aprobado></cartel-pedido-aprobado>
<pedido-select-section v-if="!pedidoDefinido"></pedido-select-section>
<pedido v-else></pedido>
</div>
</template>
<script>
export default {
data() {
return {
chismosaActiva: false,
}
},
mounted() {
Event.$on('toggle-chismosa', (activa) => {
this.chismosaActiva = activa;
});
},
import { mapActions, mapGetters } from "vuex";
import PedidoSelectSection from "./PedidoSelectSection.vue";
import Pedido from "./Pedido.vue";
import CartelPedidoAprobado from "./CartelPedidoAprobado.vue";
export default {
components: { CartelPedidoAprobado, Pedido, Productos: Pedido, PedidoSelectSection },
computed: {
...mapGetters('pedido',["pedidoDefinido"]),
},
methods: {
...mapActions('productos',["init"]),
...mapActions('pedido',["getSesion"]),
},
async mounted() {
await this.init();
await this.getSesion();
}
}
</script>

View file

@ -1,32 +1,19 @@
<template>
<div v-show="aprobado" class="notification is-warning has-text-centered">
<div v-if="aprobado" class="notification is-warning has-text-centered">
Tu pedido fue <strong>aprobado</strong>, por lo que no puede ser modificado
</div>
</template>
<script>
export default {
data() {
return {
aprobado: false,
}
},
mounted() {
Event.$on('pedido-actualizado', this.actualizarEstado);
if (this.$root.pedido != null) {
this.actualizarEstado();
}
},
methods: {
pedidoAprobado: function() {
return this.$root.pedido.aprobado;
},
actualizarEstado: function() {
this.aprobado = this.pedidoAprobado();
},
},
import { mapState } from "vuex";
export default {
name: 'CartelPedidoAprobado',
computed: {
...mapState('pedido',["aprobado"]),
}
}
</script>
<style>
</style>
</style>

View file

@ -1,41 +1,39 @@
<template>
<div v-show="visible" class="column">
<div class="columns is-multiline is-mobile">
<div v-for="(categoria,i) in categorias" :key="i" class="block column is-one-quarter-desktop is-one-third-tablet is-half-mobile">
<div @click.capture="seleccionarCategoria(categoria)" class="card" style="height:100%" >
<div class="card-content">
<div class="media">
<div class="media-content" style="overflow:hidden">
<p class="title is-6" v-text="categoria"></p>
</div>
</div>
</div>
</div><!-- END CARD -->
</div><!-- END BLOCK COLUMN -->
</div><!-- END COLUMNS -->
</div><!-- END CONTAINER -->
<div class="columns is-multiline is-mobile">
<div v-for="(categoria,i) in categorias" :key="i"
class="block column is-one-quarter-desktop is-one-third-tablet is-half-mobile">
<div @click.capture="seleccionar(categoria)" class="card" style="height:100%">
<div class="card-content">
<div class="media">
<div class="media-content" style="overflow:hidden">
<p class="title is-6" v-text="categoria"></p>
</div>
</div>
</div>
</div><!-- END CARD -->
</div><!-- END BLOCK COLUMN -->
</div><!-- END COLUMNS -->
</div><!-- END CONTAINER -->
</template>
<script>
export default {
data() {
return {
categorias: null,
visible: true
}
},
mounted() {
axios.get("/api/categorias").then(response => {
this.categorias = response.data;
});
Event.$emit("migas-setear-como-inicio", this.$rootMiga);
Event.$on("filtrar-productos", (_) => this.visible = false)
},
methods: {
seleccionarCategoria(categoria) {
this.visible = false;
Event.$emit("filtrar-productos",'categoria',categoria);
}
}
}
import { mapActions, mapMutations, mapState } from "vuex";
export default {
name: 'CategoriasContainer',
computed: {
...mapState('productos',["categorias", "filtro"]),
visible() {
return this.filtro === null;
}
},
methods: {
...mapActions('productos',["seleccionarCategoria"]),
...mapMutations('ui',["addMiga"]),
seleccionar(categoria) {
this.seleccionarCategoria({ categoria: categoria });
this.addMiga({ nombre: categoria });
}
}
}
</script>

View file

@ -12,20 +12,20 @@
<tfoot>
<tr>
<th><abbr title="Bonos de Transporte">B. Transporte</abbr></th>
<th class="has-text-right">{{ cantidad_bonos_transporte }}</th>
<th class="has-text-right">{{ total_bonos_transporte }}</th>
<th class="has-text-right">{{ cantidad_transporte }}</th>
<th class="has-text-right">{{ total_transporte }}</th>
</tr>
<tr v-if="this.$root.devoluciones">
<tr v-if="devoluciones_habilitadas">
<th><p>Devoluciones</p></th>
<td>
<abbr :title="notas_devoluciones">{{ notas_devoluciones_abbr }}</abbr>
<button @click.capture="modificarDevoluciones()" class="button is-warning is-small">
<abbr :title="devoluciones_notas">{{ notas_abreviadas }}</abbr>
<button @click.capture="toggleDevoluciones()" class="button is-warning is-small">
<span class="icon">
<i class="fas fa-edit"></i>
</span>
</button>
</td>
<th class="has-text-right">-{{ devoluciones }}</th>
<th class="has-text-right">-{{ devoluciones_total }}</th>
</tr>
<tr>
<th>Total total</th>
@ -34,7 +34,7 @@
</tr>
</tfoot>
<tbody>
<pedidos-producto-row v-for="producto in productos" :producto="producto" :key="producto.id"></pedidos-producto-row>
<producto-row v-for="producto in productos" :producto="producto" :key="producto.id"></producto-row>
</tbody>
</table>
<p class="has-text-centered" v-show="!mostrar_tabla">
@ -45,58 +45,43 @@
</template>
<script>
export default {
data() {
return {
mostrar_tabla: false,
cantidad_bonos_transporte: 0,
total_bonos_transporte: 0,
devoluciones: 0,
notas_devoluciones: "",
notas_devoluciones_abbr: "",
total: 0,
productos: [],
}
import ProductoRow from "./ProductoRow.vue";
import { mapMutations, mapState } from "vuex";
export default {
components: { ProductoRow },
computed: {
...mapState('barrio',["devoluciones_habilitadas"]),
...mapState('pedido',[
"productos",
"total",
"total_transporte",
"cantidad_transporte",
"devoluciones_total",
"devoluciones_notas",
]),
notas_abreviadas() {
return this.devoluciones_notas.substring(0, 15) + (this.devoluciones_notas.length > 15 ? "..." : "");
},
mounted() {
Event.$on('pedido-actualizado', this.pedidoActualizado);
Event.$on('toggle-chismosa', this.pedidoActualizado);
mostrar_tabla() {
return this.productos?.length !== 0;
},
methods: {
pedidoActualizado: function() {
this.mostrar_tabla = this.$root.productos.length > 0;
this.cantidad_bonos_transporte = this.cantidadBonosDeTransporte();
this.total_bonos_transporte = this.totalBonosDeTransporte();
this.devoluciones = this.$root.pedido.devoluciones_total;
this.notas_devoluciones = this.$root.pedido.devoluciones_notas;
this.notas_devoluciones_abbr = this.notas_devoluciones.substring(0, 15);
if (this.notas_devoluciones.length > 15) {
this.notas_devoluciones_abbr += "...";
}
this.total = this.$root.pedido.total;
this.productos = this.$root.productos;
},
modificarDevoluciones: function() {
Event.$emit("modificar-devoluciones");
},
cantidadBonosDeTransporte: function() {
return this.$root.pedido.cantidad_transporte;
},
totalBonosDeTransporte: function() {
return this.$root.pedido.total_transporte
},
},
}
},
methods: {
...mapMutations('ui',["toggleDevoluciones"]),
},
}
</script>
<style>
.tabla-chismosa {
width: 100%;
}
.fixed-right {
position: fixed;
overflow-y: auto;
max-height: 81vh;
margin-right: 20px;
}
.tabla-chismosa {
width: 100%;
}
.fixed-right {
position: fixed;
overflow-y: auto;
max-height: 81vh;
margin-right: 20px;
}
</style>

View file

@ -1,7 +1,7 @@
<template>
<div class="dropdown is-right navbar-item" :class="{'is-active':activa}">
<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="id" @click.capture="toggle">
<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>
@ -13,33 +13,23 @@
<script>
import Chismosa from './Chismosa.vue'
import { mapMutations, mapState } from "vuex";
export default {
components: {
Chismosa
},
props: {
id: {
ariaControls: {
type: String,
required: true
}
},
data() {
return {
activa: false,
total: 0,
}
},
mounted() {
Event.$on('pedido-actualizado', this.actualizar);
computed: {
...mapState('pedido',["total"]),
...mapState('ui',["show_chismosa"]),
},
methods: {
toggle() {
this.activa = !this.activa;
Event.$emit("toggle-chismosa", this.activa);
},
actualizar() {
this.total = this.$root.pedido.total;
},
...mapMutations('ui',["toggleChismosa"]),
},
}
</script>

View file

@ -1,67 +1,71 @@
<template>
<div v-bind:class="visible ? 'is-active modal' : 'modal'">
<div :class="show_devoluciones ? 'is-active modal' : 'modal'">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Devoluciones</p>
<button class="delete" aria-label="close" @click.capture="cerrar"></button>
<button class="delete" aria-label="close" @click.capture="toggleDevoluciones()"></button>
</header>
<section class="modal-card-body">
<div class="field has-addons is-centered is-thin-centered">
<p class="control">
Total:
<input id="total" class="input" type="number" v-model="total" style="text-align: center">
<input id="totalControl" class="input" type="number" v-model="totalControl"
style="text-align: center">
</p>
</div>
<div class="field has-addons is-centered is-thin-centered">
<p class="control">
Notas:
<input id="notas" class="input" type="text" v-model.text="notas">
<input id="notasControl" class="input" type="text" v-model.text="notasControl">
</p>
</div>
</section>
<footer class="modal-card-foot">
<button class="button is-success" @click="modificar">Aceptar</button>
<button class="button" @click.capture="cerrar">Cancelar</button>
<button class="button" @click.capture="toggleDevoluciones()">Cancelar</button>
</footer>
</div>
</div>
</template>
<script>
export default {
data() {
return {
visible: false,
total: 0,
notas: "",
}
import { mapActions, mapMutations, mapState } from "vuex";
export default {
name: 'DevolucionesModal',
data() {
return {
totalControl: 0,
notasControl: "",
}
},
mounted() {
this.actualizar();
},
watch: {
cantidadEnChismosa() {
this.actualizar();
},
computed: {
miga: function() {
return {
nombre: "Devoluciones",
href: "#devoluciones",
}
},
notasEnChismosa() {
this.actualizar();
}
},
computed: {
...mapState('ui', ["show_devoluciones"]),
...mapState('pedido', ["devoluciones_total", "devoluciones_notas"])
},
methods: {
...mapMutations('ui', ["toggleDevoluciones"]),
...mapActions('pedido', ["modificarDevoluciones"]),
modificar() {
this.modificarDevoluciones({ monto: this.totalControl, notas: this.notasControl });
this.toggleDevoluciones();
},
methods: {
cerrar() {
this.visible = false;
Event.$emit("migas-pop");
},
modificar() {
Event.$emit('sync-devoluciones', this.total, this.notas);
this.cerrar();
}
actualizar() {
this.totalControl = this.devoluciones_total;
this.notasControl = this.devoluciones_notas;
},
mounted() {
Event.$on('modificar-devoluciones', () => {
this.visible = true;
this.total = this.$root.pedido.devoluciones_total;
this.notas = this.$root.pedido.devoluciones_notas;
Event.$emit("migas-agregar", this.miga);
});
},
}
</script>
},
}
</script>

View file

@ -1,55 +0,0 @@
<template>
<div v-show="visible" class="block">
<div class="field">
<label class="label">Contraseña del barrio</label>
<div class="field has-addons">
<div class="control">
<input required class="input" :type="this.passwordType" name="password" placeholder="Contraseña del barrio">
</div>
<div class="control">
<a class="button is-info" @click="togglePassword">
{{ (passwordVisible ? 'Ocultar' : 'Mostrar') + ' contraseña'}}
</a>
</div>
</div>
<p class="help">Si no la sabés, consultá a tus compañerxs.</p>
</div>
<div class="field">
<div class="control">
<input type="submit" class="button is-success" value="Ingresar"/>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
visible: false,
gdc: this.$root.gdc,
passwordVisible: false,
passwordType: "password",
}
},
mounted() {
Event.$on('gdc-seleccionado', (gdc) => {
this.$root.gdc = gdc;
this.visible = true;
});
},
methods: {
togglePassword() {
if (this.passwordVisible) this.passwordType = "password";
else this.passwordType = "text"
this.passwordVisible = !this.passwordVisible
}
}
}
</script>
<style>
.help {
font-size: 1rem;
}
</style>

View file

@ -1,54 +1,43 @@
<template>
<nav class="breadcrumb is-centered has-background-danger-light is-fixed-top" aria-label="breadcrumbs" v-show="visible">
<ul class="mt-4">
<li v-for="(miga, i) in migas" :key="i" :class="{'is-active': i == migaActiva}">
<a :href="miga.href" v-text="miga.nombre"
:class="{'has-text-danger': i != migaActiva}"></a>
</li>
</ul>
</nav>
<nav v-if="pedidoDefinido" class="breadcrumb is-centered has-background-danger-light is-fixed-top"
aria-label="breadcrumbs" v-show="visible">
<ul class="mt-4">
<li v-for="(miga, i) in migas" :key="i" :class="{'is-active': i === migaActiva}">
<a @click="clickMiga({ miga: miga })" v-text="miga.nombre"
:class="{'has-text-danger': i !== migaActiva}"></a>
</li>
</ul>
</nav>
</template>
<script>
export default {
data() {
return {
migas: []
}
},
computed: {
visible: function() {
return this.migas.length > 0
},
migaActiva: function() {
return this.migas.length-1
}
},
mounted() {
Event.$on('migas-setear-como-inicio', (miga) => {
this.migas = [];
this.migas.push(miga);
});
Event.$on('migas-agregar', (miga) => {
this.migas.push(miga);
});
Event.$on('migas-reset', () => {
this.migas = [];
});
Event.$on('migas-pop', () => {
this.migas.pop();
});
}
}
import { mapActions, mapGetters, mapState } from "vuex";
export default {
methods: {
...mapActions('productos',["getProductos"]),
...mapActions('ui',["clickMiga"]),
},
computed: {
...mapState('ui',["migas"]),
...mapGetters('pedido',["pedidoDefinido"]),
visible() {
return this.migas.length > 0
},
migaActiva() {
return this.migas.length - 1
}
},
}
</script>
<style>
nav.breadcrumb.is-fixed-top {
position: fixed;
left: 0;
right: 0;
top: 3.25rem;
height: 2.75rem;
z-index: 5;
position: fixed;
left: 0;
right: 0;
top: 3.25rem;
height: 2.75rem;
z-index: 5;
}
</style>
</style>

View file

@ -0,0 +1,30 @@
<script >
import { defineComponent } from "vue";
import { mapActions, mapState } from "vuex";
import SubpedidoSelect from "./SubpedidoSelect.vue";
import CategoriasContainer from "./CategoriasContainer.vue";
import ProductosContainer from "./ProductosContainer.vue";
import Chismosa from "./Chismosa.vue";
import DevolucionesModal from "./DevolucionesModal.vue";
import CartelPedidoAprobado from "./CartelPedidoAprobado.vue";
export default defineComponent({
components: { CartelPedidoAprobado, DevolucionesModal, SubpedidoSelect, CategoriasContainer, ProductosContainer, Chismosa },
computed: {
...mapState('ui',["show_chismosa","show_devoluciones"])
},
})
</script>
<template>
<div class="columns ml-3 mr-3" v-else>
<categorias-container :class="show_chismosa ? 'hide-below-1024' : ''"></categorias-container>
<productos-container :class="show_chismosa ? 'hide-below-1024' : ''"></productos-container>
<chismosa v-show="show_chismosa"></chismosa>
<devoluciones-modal v-show="show_devoluciones"></devoluciones-modal>
</div>
</template>
<style scoped>
</style>

View file

@ -0,0 +1,26 @@
<script>
import { defineComponent } from "vue";
import SubpedidoSelect from "./SubpedidoSelect.vue";
import { mapGetters } from "vuex";
export default defineComponent({
components: { SubpedidoSelect },
})
</script>
<template>
<section class="section">
<div id="root" class="container">
<h1 class="title">
Pedidos MPS
</h1>
<p class="subtitle">
Bienvenidx a la aplicación de pedidos del <strong>Mercado Popular de Subsistencia</strong>
</p>
<subpedido-select></subpedido-select>
</div>
</section>
</template>
<style scoped>
</style>

View file

@ -7,29 +7,31 @@
</button>
</div>
<div class="control">
<input id="cantidad" v-model="cantidad" class="input is-small" type="number" style="text-align: center">
<input id="cantidad" v-model="cantidadControl" class="input is-small" type="number"
style="text-align: center">
</div>
<div class="control" @click="incrementar();">
<button class="button is-small">
<i class="fa fa-solid fa-plus"></i>
</button>
</div>
<button :disabled="disableConfirm()" class="button is-small is-success ml-1" @click="confirmar()">
<button :disabled="!hayCambios" class="button is-small is-success ml-1" @click="confirmar()">
<span class="icon">
<i class="fas fa-check"></i>
</span>
</button>
<button :disabled="!puedeBorrar()" class="button is-small is-danger ml-1" @click="borrar()">
<button :disabled="!puedeBorrar" class="button is-small is-danger ml-1" @click="borrar()">
<span class="icon">
<i class="fas fa-trash-alt"></i>
</span>
</button>
</div>
<div v-if="producto.requiere_notas" v-bind:class="{'has-icons-right': notas_warning_visible}" class="control is-full-width has-icons-left">
<div v-if="requiere_notas" :class="{'has-icons-right': notas_warning_visible}"
class="control is-full-width has-icons-left">
<span class="icon is-small is-left">
<i class="fas fa-sticky-note"></i>
</span>
<input v-model="notas" v-bind:class="{'is-danger': notas_warning_visible}" id="notas" class="input" type="text" placeholder="Talle o color" />
<input v-model="notasControl" v-bind:class="{'is-danger': notas_warning_visible}" id="notas" class="input" type="text" placeholder="Talle o color"/>
<span v-if="notas_warning_visible" class="icon is-small is-right">
<i class="fas fa-exclamation-triangle"></i>
</span>
@ -43,98 +45,110 @@
</template>
<script>
export default {
props: {
producto: Object
import { mapActions, mapGetters } from "vuex";
export default {
props: {
producto_id: {
type: Number,
required: true,
},
data() {
return {
cantidad: this.cantidadEnChismosa(),
notas: this.notasEnChismosa(),
notas_warning_visible: false,
requiere_notas: {
type: Number,
required: true,
}
},
data() {
return {
cantidadControl: 0,
notasControl: '',
notas_warning_visible: false,
}
},
watch: {
cantidadEnChismosa() {
this.actualizar();
},
notasEnChismosa() {
this.actualizar();
}
},
mounted() {
this.actualizar();
},
computed: {
...mapGetters('pedido', ["enChismosa", "cantidad", "notas"]),
cantidadEnChismosa() {
return this.cantidad(this.producto_id);
},
notasEnChismosa() {
return this.notas(this.producto_id);
},
hayCambios() {
return this.cantidadControl !== this.cantidadEnChismosa || this.notasControl !== this.notasEnChismosa;
},
puedeBorrar() {
return this.enChismosa(this.producto_id);
},
faltaNotas() {
return this.requiere_notas && this.cantidadControl > 0 && !this.notasControl;
},
},
methods: {
...mapActions('pedido', ["modificarChismosa"]),
decrementar() {
this.cantidadControl -= 1;
},
incrementar() {
this.cantidadControl += 1;
},
borrar() {
this.cantidadControl = 0;
this.confirmar();
},
async confirmar() {
if (this.faltaNotas) {
this.notas_warning_visible = true;
return;
}
},
mounted() {
Event.$on('sync-subpedido', (cantidad, productoId, notas) => {
if (this.producto.id === productoId)
this.sincronizar(cantidad, notas);
await this.modificarChismosa({
producto_id: this.producto_id,
cantidad: this.cantidadControl,
notas: this.notasControl
});
},
methods: {
notasEnChismosa() {
return this.producto.pivot !== undefined ? this.producto.pivot.notas : "";
},
cantidadEnChismosa() {
return this.producto.pivot !== undefined ? this.producto.pivot.cantidad : 0;
},
decrementar() {
this.cantidad -= 1;
},
incrementar() {
this.cantidad += 1;
},
confirmar() {
if (this.warningNotas()) {
this.notas_warning_visible = true;
return;
}
console.log("Emit sync " + this.cantidad + " " + this.notas);
Event.$emit('sync-subpedido', this.cantidad, this.producto.id, this.notas);
},
borrar() {
this.cantidad = 0;
this.confirmar();
},
sincronizar(cantidad, notas) {
this.notas_warning_visible = false;
this.notas = notas;
this.cantidad = cantidad;
if (this.producto.pivot !== undefined) {
this.producto.pivot.cantidad = cantidad;
this.producto.pivot.notas = notas;
}
},
hayCambios() {
if (this.cantidad != this.cantidadEnChismosa()) return true;
return this.cantidad > 0 && this.notas != this.notasEnChismosa();
},
puedeBorrar() {
return this.cantidadEnChismosa() > 0;
},
warningNotas() {
return this.producto.requiere_notas && this.cantidad > 0 && !this.notas;
},
disableConfirm() {
return !this.hayCambios();
},
}
actualizar() {
this.cantidadControl = this.cantidadEnChismosa;
this.notasControl = this.notasEnChismosa;
},
}
}
</script>
<style>
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
appearance: textfield;
-moz-appearance: textfield;
}
.contador {
min-width: 178px;
}
/* Firefox */
input[type=number] {
appearance: textfield;
-moz-appearance: textfield;
}
.is-danger {
background-color: #fca697;
}
.is-danger::placeholder {
color: #fff;
opacity: 1; /* Firefox */
}
</style>
.contador {
min-width: 178px;
}
.is-danger {
background-color: #fca697;
}
.is-danger::placeholder {
color: #fff;
opacity: 1; /* Firefox */
}
</style>

View file

@ -1,50 +1,25 @@
<script>
import ProductoCantidad from "./ProductoCantidad.vue";
import { mapGetters } from "vuex";
export default {
name: "ProductoCard",
components: { ProductoCantidad },
props: {
producto: Object
producto: {
type: Object,
required: true
}
},
data() {
return {
cantidad: this.producto.cantidad,
enChismosa: this.producto.cantidad,
notas: this.producto.notas,
}
computed: {
...mapGetters('pedido',["enChismosa", "cantidad"]),
fuePedido() {
return this.enChismosa(this.producto.id);
},
cantidadEnChismosa() {
return this.cantidad(this.producto.id);
}
},
mounted() {
Event.$on('sync-subpedido', (cantidad, productoId, notas) => {
if (this.producto.id === productoId)
this.sincronizar(cantidad, notas);
});
},
methods: {
decrementar() {
this.cantidad -= 1;
},
incrementar() {
this.cantidad += 1;
},
confirmar() {
Event.$emit('sync-subpedido', this.cantidad, this.producto.id, this.notas);
},
borrar() {
this.cantidad = 0;
this.confirmar();
},
sincronizar(cantidad, notas) {
this.cantidad = cantidad;
this.producto.cantidad = cantidad;
this.enChismosa = cantidad;
this.notas = notas;
this.producto.notas = notas;
},
hayCambios() {
return this.cantidad !== this.enChismosa || this.notas !== this.producto.notas;
},
puedeBorrar() {
return this.enChismosa > 0;
},
}
}
</script>
@ -56,8 +31,7 @@ export default {
<p class="title is-6">
{{ producto.nombre }}
</p>
<p class="subtitle is-7" v-text="producto.proveedor"></p>
<span class="subtitle is-7 hidden-from-tablet" v-if="enChismosa !== 0">{{ enChismosa }} en chismosa</span>
<span class="subtitle is-7 hidden-from-tablet" v-if="fuePedido">{{ cantidadEnChismosa }}</span>
</div>
<div class="column is-one-quarter has-text-right">
<p class="has-text-weight-bold has-text-primary">
@ -71,13 +45,16 @@ export default {
</div>
<footer class="columns">
<div class="column is-three-quarters">
<pedidos-producto-cantidad :producto="producto"></pedidos-producto-cantidad>
<producto-cantidad
:producto_id="producto.id"
:requiere_notas="producto.requiere_notas">
</producto-cantidad>
</div>
<div class="column">
<p class="subtitle is-7 is-hidden-mobile" v-if="enChismosa > 0">{{ enChismosa }} en chismosa</p>
<p class="subtitle is-7 is-hidden-mobile" v-if="fuePedido">{{ cantidadEnChismosa }} en chismosa</p>
</div>
</footer>
</div><!-- END BOX -->
</div>
</div>
</template>

View file

@ -2,15 +2,25 @@
<tr>
<td>{{ this.producto.nombre }}</td>
<td class="has-text-right">
<pedidos-producto-cantidad :producto="producto"></pedidos-producto-cantidad>
<producto-cantidad
:producto_id="producto.id"
:requiere_notas="producto.requiere_notas">
</producto-cantidad>
</td>
<td class="has-text-right">{{ Math.ceil(this.producto.pivot.total) }}</td>
<td class="has-text-right">{{ cantidad(producto.id) }}</td>
</tr>
</template>
<script>
import ProductoCantidad from "./ProductoCantidad.vue";
import { mapGetters } from "vuex";
export default {
components: { ProductoCantidad },
props: {
producto: Object
},
computed: {
...mapGetters('pedido',["cantidad"]),
},
}
</script>

View file

@ -1,55 +1,32 @@
<template>
<div v-show="visible" class="column">
<div class="columns is-multiline is-mobile">
<pedidos-producto-card v-for="(producto,i) in productos" :key="i" :producto="producto">
</pedidos-producto-card><!-- END BLOCK COLUMN -->
</div><!-- END COLUMNS -->
</div><!-- END CONTAINER -->
<div v-show="visible" class="column">
<div class="columns is-multiline is-mobile">
<producto-card
v-for="(producto,i) in this.productos"
:key="i"
:producto="producto">
</producto-card>
</div>
</div>
</template>
<script>
import ProductoCard from "./ProductoCard.vue";
import { mapState } from "vuex";
export default {
data() {
return {
productos: [],
visible: false,
paginar: 150,
valor: null,
filtro: null
}
},
computed: {
miga: function(){
return {
nombre: this.valor,
href: "#" + this.valor
}
}
},
mounted() {
Event.$on('filtrar-productos', (filtro,valor) => {
this.filtro = filtro
this.valor = valor
axios.get("/api/productos", {
params: this.params(filtro,valor)
}).then(response => {
this.productos = response.data.data;
this.productos.forEach(p => {
p.pivot = {};
p.pivot.cantidad = this.$root.cantidad(p);
p.pivot.notas = this.$root.notas(p);
});
});
this.visible = true;
Event.$emit("migas-agregar",this.miga);
});
},
methods: {
params(filtro,valor) {
let params = { paginar: this.paginar }
params[filtro] = valor
return params
},
}
}
name: 'ProductosContainer',
components: { ProductoCard },
computed: {
...mapState('productos', ["productos", "filtro"]),
visible() {
return this.filtro !== null;
},
miga: function () {
return {
nombre: this.filtro.valor,
href: "#" + this.filtro.valor
}
}
},
}
</script>

View file

@ -1,28 +1,32 @@
<template>
<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"/>
</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>
</div>
<div v-if="subpedidosExistentes.length" class="block">
<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="searchString"/>
</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-if="!deshabilitado" @click="submit">Crear nuevo pedido
</button>
</div>
</div>
<div v-if="pedidos.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}" :key="index">
<div class="columns is-mobile" v-for="(subpedidoExistente, index) in pedidos"
:class="{'has-background-grey-lighter': index % 2}" :key="index">
<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>
<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>
<button class="button is-danger" @click="elegirPedido({ pedido: subpedidoExistente })">Continuar pedido
</button>
</div>
</div>
</div>
@ -30,62 +34,40 @@
</template>
<script>
export default {
data() {
return {
subpedido: null,
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 => {
this.subpedidosExistentes = response.data
});
},
submit() {
axios.post("/api/subpedidos", {
nombre: this.subpedido,
grupo_de_compra_id: this.gdcid
}).then(response => {
//se creo el subpedido
this.elegirSubpedido(response.data);
});
},
elegirSubpedido(subpedido){
//lo guardamos en sesion
this.guardarSubpedidoEnSesion(subpedido);
},
guardarSubpedidoEnSesion(subpedido) {
axios.post("/subpedidos/guardar_sesion", {
subpedido: subpedido,
grupo_de_compra_id: this.gdcid
}).then(_ => {
Event.$emit('obtener-sesion')
window.location.href = 'productos';
});
}
}
}
import { mapActions, mapMutations, mapState } from "vuex";
export default {
name: 'SubpedidoSelect',
async mounted() {
await this.getGrupoDeCompra();
},
data() {
return {
searchString: null
}
},
computed: {
...mapState('barrio',["grupo_de_compra_id","pedidos"]),
...mapState('pedido',["nombre","pedido_id"]),
deshabilitado: function () {
return !this.searchString?.trim()
|| this.pedidos.some(p => p.nombre.toLowerCase() === this.searchString.toLowerCase())
}
},
methods: {
...mapActions('barrio',["getGrupoDeCompra","getPedidos"]),
...mapActions('pedido',["crearPedido","elegirPedido"]),
...mapMutations('barrio',["setPedidos"]),
onType() {
if (!this.searchString) {
this.setPedidos([]);
return;
}
this.getPedidos(this.searchString);
},
async submit() {
await this.crearPedido({ nombre: this.searchString, grupo_de_compra_id: this.grupo_de_compra_id });
},
}
}
</script>

21
resources/js/store/index.js vendored Normal file
View file

@ -0,0 +1,21 @@
import Vue from 'vue';
import Vuex from 'vuex';
import admin from "./modules/admin";
import login from "./modules/login";
import pedido from "./modules/pedido";
import barrio from "./modules/barrio";
import productos from "./modules/productos";
import ui from "./modules/ui";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
admin,
login,
pedido,
barrio,
productos,
ui,
},
});

81
resources/js/store/modules/admin.js vendored Normal file
View file

@ -0,0 +1,81 @@
import axios from "axios";
const state = {
lastFetch: null,
grupo_de_compra_id: null,
nombre: null,
devoluciones_habilitadas: null,
pedidos: null,
total_a_recaudar: null,
total_barrial: null,
total_devoluciones: null,
total_a_transferir: null,
total_transporte: null,
cantidad_transporte: null,
};
const mutations = {
setState(state, { grupo_de_compra }) {
state.lastFetch = new Date();
state.grupo_de_compra_id = grupo_de_compra.id;
state.nombre = grupo_de_compra.nombre;
state.devoluciones_habilitadas = grupo_de_compra.devoluciones_habilitadas;
state.pedidos = grupo_de_compra.pedidos;
state.total_a_recaudar = grupo_de_compra.total_a_recaudar;
state.total_barrial = grupo_de_compra.total_barrial;
state.total_devoluciones = grupo_de_compra.total_devoluciones;
state.total_a_transferir = grupo_de_compra.total_a_transferir;
state.total_transporte = grupo_de_compra.total_transporte;
state.cantidad_transporte = grupo_de_compra.cantidad_transporte;
},
toggleCaracteristica(state, { caracteristica_id }) {
state[`${caracteristica_id}_habilitadas`] = !state[`${caracteristica_id}_habilitadas`];
}
};
const actions = {
async getGrupoDeCompra({ commit }) {
const response = await axios.get('/user/grupo_de_compra');
commit('setState', response.data);
},
async setAprobacionPedido({ commit, dispatch }, { pedido_id, aprobacion }){
await axios.post("/api/admin/subpedidos/" + pedido_id + "/aprobacion", { aprobacion: aprobacion });
await actions.getGrupoDeCompra({ commit });
dispatch("ui/toast",
{ mensaje: `Pedido ${aprobacion ? '' : 'des' }aprobado con éxito.` },
{ root: true });
},
async toggleCaracteristica({ commit }, { caracteristica_id }) {
await axios.post(`/api/grupos-de-compra/${state.grupo_de_compra_id}/${caracteristica_id}`);
commit('toggleCaracteristica', { caracteristica_id: caracteristica_id })
},
};
const getters = {
grupoDeCompraDefinido() {
return state.lastFetch !== null;
},
hayPedidos() {
return getters.grupoDeCompraDefinido() && state.pedidos.length > 0;
},
pedidosAprobados() {
return getters.grupoDeCompraDefinido() ? state.pedidos.filter(p => p.aprobado) : [];
},
hayAprobados() {
return getters.pedidosAprobados().length !== 0;
},
getPedido() {
return (pedido_id) => state.pedidos.find(p => p.id === pedido_id);
},
getCaracteristica() {
return (caracteristica) => state[`${caracteristica}_habilitadas`];
}
};
export default {
namespaced: true,
state,
mutations,
actions,
getters,
};

42
resources/js/store/modules/barrio.js vendored Normal file
View file

@ -0,0 +1,42 @@
import axios from "axios";
const state = {
grupo_de_compra_id: null,
grupo_de_compra: null,
devoluciones_habilitadas: null,
pedidos: [],
};
const mutations = {
setGrupoDeCompra(state, { grupo_de_compra }) {
state.grupo_de_compra_id = grupo_de_compra.id;
state.grupo_de_compra = grupo_de_compra.nombre;
state.devoluciones_habilitadas = grupo_de_compra.devoluciones_habilitadas;
},
setPedidos(state, pedidos) {
state.pedidos = pedidos;
},
};
const actions = {
async getGrupoDeCompra({ commit }) {
const response = await axios.get('/user/grupo_de_compra');
commit('setGrupoDeCompra', response.data);
},
async getPedidos({ commit }, nombre) {
const response = await axios.get('/api/subpedidos/',{
params: {
nombre: nombre,
grupo_de_compra: state.grupo_de_compra_id
}
});
commit('setPedidos', response.data);
}
};
export default {
namespaced: true,
state,
mutations,
actions,
};

60
resources/js/store/modules/login.js vendored Normal file
View file

@ -0,0 +1,60 @@
import axios from "axios";
const state = {
regiones: null,
grupos_de_compra: null,
region_elegida: null,
grupo_de_compra_elegido: null,
rol: null,
};
const mutations = {
setRegiones(state, { regiones }) {
state.regiones = regiones;
},
setRegionYBarrios(state, { region, grupos_de_compra }) {
state.region_elegida = region;
state.grupos_de_compra = grupos_de_compra;
},
selectGrupoDeCompra(state, { grupo_de_compra }) {
state.grupo_de_compra_elegido = grupo_de_compra;
},
setRol(state, { rol }) {
state.rol = rol;
},
};
const actions = {
async getRegiones({ commit }) {
const response = await axios.get("/api/regiones");
commit('setRegiones', { regiones: response.data });
},
async selectRegion({ commit }, { region }) {
const response = await axios.get("/api/grupos-de-compra");
commit('setRegionYBarrios', { region: region, grupos_de_compra: response.data[region] });
},
async getRol({ commit }) {
const response = await axios.get("/user/rol");
commit('setRol', { rol: response.data.rol });
}
};
const getters = {
adminUrl() {
return window.location.pathname.startsWith('/admin');
},
mensajes() {
return {
mensaje: `Contraseña de ${getters.adminUrl() ? 'administración ' : ''}del barrio`,
ayuda: `Si no la sabés, consultá a ${getters.adminUrl() ? 'la comisión informática ' : 'tus compañerxs'}.`
};
},
};
export default {
namespaced: true,
state,
mutations,
actions,
getters,
};

105
resources/js/store/modules/pedido.js vendored Normal file
View file

@ -0,0 +1,105 @@
import axios from "axios";
const state = {
lastFetch: 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,
};
const mutations = {
setState(state, pedido) {
state.lastFetch = new Date();
state.pedido_id = pedido.id;
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.devoluciones_notas = pedido.devoluciones_notas;
},
};
const actions = {
async guardarSesion(_, { pedido_id }) {
await axios.post("/subpedidos/sesion", { id: pedido_id });
},
async getSesion({ commit }) {
const sesion = await axios.get("/subpedidos/sesion");
if (sesion.data) {
const response = await axios.get(`/api/subpedidos/${sesion.data}`);
commit('setState', response.data.data);
}
},
async crearPedido({ commit, dispatch }, { nombre, grupo_de_compra_id }) {
const response = await axios.post("/api/subpedidos", {
nombre: nombre,
grupo_de_compra_id: grupo_de_compra_id
});
dispatch("guardarSesion", { pedido_id: response.data.data.id});
commit('setState', response.data.data);
},
async elegirPedido({ commit, dispatch }, { pedido }) {
const response = await axios.get(`/api/subpedidos/${pedido.id}`);
dispatch("guardarSesion", { pedido_id: response.data.data.id})
commit('setState', response.data.data);
},
async modificarChismosa({ commit, dispatch }, { producto_id, cantidad, notas }) {
try {
const response = await axios.post("/api/subpedidos/" + state.pedido_id + "/sync", {
cantidad: cantidad,
producto_id: producto_id,
notas: notas,
});
commit('setState', response.data.data);
dispatch("ui/toast", { mensaje: 'Pedido modificado con éxito' }, { root: true });
} catch (error) {
dispatch("ui/error", { error: error }, { root: true });
}
},
async modificarDevoluciones({ commit, dispatch }, { monto, notas }) {
try {
const response = await axios.post("api/subpedidos/" + state.pedido_id + "/sync_devoluciones", {
total: monto,
notas: notas,
});
commit('setState', response.data.data);
dispatch("ui/toast", { mensaje: 'Devoluciones modificadas con éxito' }, { root: true });
} catch (error) {
dispatch("ui/error", { error: error }, { root: true });
}
},
};
const getters = {
pedidoDefinido() {
return state.lastFetch !== null;
},
enChismosa() {
return ((producto_id) => state.productos.some(p => p.id === producto_id));
},
cantidad() {
return ((producto_id) => state.productos.find(p => p.id === producto_id)?.pivot.cantidad ?? 0);
},
notas() {
return ((producto_id) => state.productos.find(p => p.id === producto_id)?.pivot.notas ?? "");
}
}
export default {
namespaced: true,
state,
mutations,
actions,
getters,
};

56
resources/js/store/modules/productos.js vendored Normal file
View file

@ -0,0 +1,56 @@
import axios from "axios";
const state = {
lastFetch: null,
categorias: [],
productos: [],
filtro: null,
};
const mutations = {
setCategorias(state, categorias) {
state.lastFetch = new Date();
state.categorias = categorias;
},
setProductos(state, productos) {
state.lastFetch = new Date();
state.productos = productos;
},
setFiltro(state, filtro) {
state.lastFetch = new Date();
state.filtro = filtro;
},
};
const actions = {
async init({ dispatch }) {
dispatch('getCategorias');
dispatch('getProductos');
},
async getCategorias({ commit }) {
const response = await axios.get('api/categorias');
commit('setCategorias', response.data);
},
async getProductos({ commit }) {
const response = await axios.get("/api/productos");
commit('setFiltro', null);
commit('setProductos', response.data.data);
},
async seleccionarCategoria({ dispatch }, { categoria }) {
dispatch('filtrarProductos', { filtro: "categoria", valor: categoria });
},
async filtrarProductos({ commit }, { filtro, valor }) {
const response = await axios.get("/api/productos", {
params: { [filtro]: valor }
});
commit('setFiltro', { clave: filtro, valor: valor });
commit('setProductos', response.data.data);
}
};
export default {
namespaced: true,
state,
mutations,
actions,
};

48
resources/js/store/modules/ui.js vendored Normal file
View file

@ -0,0 +1,48 @@
import { dropWhile } from "lodash/array";
const state = {
show_chismosa: false,
show_devoluciones: false,
miga_inicial: { nombre: 'Categorias', action: 'productos/getProductos' },
migas: [{ nombre: 'Categorias', action: 'productos/getProductos' }],
};
const mutations = {
toggleChismosa(state) {
state.show_chismosa = !state.show_chismosa;
},
toggleDevoluciones(state) {
state.show_devoluciones = !state.show_devoluciones;
},
addMiga(state, miga) {
state.migas.push(miga);
},
};
const actions = {
clickMiga({ dispatch }, { miga }) {
dispatch(miga.action, null, { root: true });
state.migas = dropWhile(state.migas.reverse(),(m => m.nombre !== miga.nombre)).reverse();
},
toast(_, { mensaje }) {
return window.bulmaToast.toast({
message: mensaje,
duration: 2000,
type: 'is-danger',
position: 'bottom-center',
});
},
error({ dispatch }, { error }) {
const errorMsg = error.response && error.response.data && error.response.data.message
? error.response.data.message
: error.message;
dispatch("toast", { mensaje: errorMsg });
},
};
export default {
namespaced: true,
state,
mutations,
actions,
};

View file

@ -21,11 +21,10 @@
Contraseña incorrecta, intentalo nuevamente.
</div>
@enderror
<comunes-region-select v-bind:admin="true"></comunes-region-select>
<form method="post" action="login">
<comunes-region-select></comunes-region-select>
<form method="post" action="/login">
@csrf
<comunes-barrio-select v-bind:admin="true"></comunes-barrio-select>
<admin-login></admin-login>
<comunes-login-form></comunes-login-form>
</form>
</div>
</section>

View file

@ -22,7 +22,7 @@
Contraseña incorrecta, intentalo nuevamente.
</div>
@enderror
<form method="post" action="login">
<form method="post" action="/login">
@csrf
<compras-login></compras-login>
</form>

View file

@ -24,11 +24,10 @@
</div>
@enderror
<comunes-region-select></comunes-region-select>
<form method="post" action="login">
@csrf
<comunes-barrio-select></comunes-barrio-select>
<pedidos-login></pedidos-login>
</form>
<form method="post" action="/login">
@csrf
<comunes-login-form></comunes-login-form>
</form>
</div>
</section>
<script src="{{ mix('js/app.js') }}" defer></script>

View file

@ -7,31 +7,25 @@
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ session("subpedido_nombre") ? "Pedido de " . session("subpedido_nombre") . " - " . config('app.name', 'Pedidos del MPS') : config('app.name', 'Pedidos del MPS')}}</title>
<title>{{ config('app.name', 'Pedidos 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>
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
@yield('stylesheets')
</head>
<body class="has-navbar-fixed-top">
<div id="root">
<comunes-nav-bar>
<template slot="subpedido">{{ session('subpedido_nombre') ? 'Pedido de '. session('subpedido_nombre') : Auth::user()->name }}</template>
<template slot="gdc">{{ session('subpedido_nombre') ? Auth::user()->name : "" }}</template>
<template slot="logout-form">
<template #logout-form>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
</template>
</comunes-nav-bar>
<pedidos-nav-migas></pedidos-nav-migas>
<main id="main" class="py-4 has-top-padding">
<pedidos-cartel-pedido-aprobado></pedidos-cartel-pedido-aprobado>
@yield('content')
</main>
</div>

View file

@ -0,0 +1,8 @@
@extends('layouts.app')
@section('content')
<app-main></app-main>
@endsection
@section('scripts')
@endsection

View file

@ -1,17 +1,7 @@
@extends('layouts.app')
@section('content')
<section class="section">
<div id="root" class="container">
<h1 class="title">
Pedidos MPS
</h1>
<p class="subtitle">
Bienvenidx a la aplicación de pedidos del <strong>Mercado Popular de Subsistencia</strong>
</p>
<pedidos-subpedido-select gdcid="{{Auth::user()->grupoDeCompra->id}}"></pedidos-subpedido-select>
</div>
</section>
<app-main></app-main>
@endsection
@section('scripts')

View file

@ -34,8 +34,8 @@ Route::middleware('api')->group(function () {
});
Route::post('/{gdc}/devoluciones', function($gdc) {
$habilitadas = GrupoDeCompra::find($gdc)->toggleDevoluciones();
return ['devoluciones' => $habilitadas];
GrupoDeCompra::find($gdc)->toggleDevoluciones();
return response()->noContent();
});
});

View file

@ -20,22 +20,59 @@ 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('/', 'RouteController@home')->name('home');
Route::get('/admin', 'AdminController@show')->name('admin_login.show');
Route::middleware(['auth'])->group(function () {
Route::get('/user/rol', 'UserController@rol')->name('user.rol');
Route::get('/user/grupo_de_compra', 'UserController@grupoDeCompra');
});
Route::get('/admin/obtener_sesion', function() {
return [
'gdc' => session("admin_gdc")
];
})->name('admin_obtener_sesion');
Route::middleware(['auth', 'role:barrio'])->group(function() {
Route::get('/pedido', 'RouteController@main')->name('pedido');
Route::middleware(['auth', 'admin'])->group( function () {
Route::get('/admin/pedidos', 'AdminController@index')->name('admin_login.index');
Route::get('/productos', 'ProductoController@index')->name('productos.index');
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"])) {
throw new HttpException(400, "La request necesita un subpedido para guardar en sesión");
}
if (!isset($r["grupo_de_compra_id"])) {
throw new HttpException(400, "La request necesita un grupo de compra para guardar en sesión");
}
session(["subpedido_nombre" => $r["subpedido"]["nombre"]]);
session(["subpedido_id" => $r["subpedido"]["id"]]);
session(["gdc" => $r["grupo_de_compra_id"]]);
return "Subpedido guardado en sesión";
})->name('guardarSesion');
Route::get('obtener_sesion', function() {
return [
'subpedido' => [
'nombre' => session("subpedido_nombre"),
'id' => session("subpedido_id")
],
'gdc' => session("gdc")
];
})->name('obtenerSesion');
Route::post('sesion', 'SessionController@store');
Route::get('sesion', 'SessionController@fetch');
});
});
Route::get('/admin/login', 'AdminController@show')->name('admin.login');
Route::middleware(['auth', 'role:admin_barrio'])->group(function () {
Route::get('/admin', 'RouteController@main')->name('admin');
Route::get('/admin/exportar-planillas-a-pdf/{gdc}', 'AdminController@exportarPedidosAPdf');
@ -44,43 +81,10 @@ Route::middleware(['auth', 'admin'])->group( function () {
Route::get('/admin/exportar-pedido-con-nucleos-a-csv/{gdc}', 'AdminController@exportarPedidoConNucleosACSV');
});
Route::middleware('auth')->group( function() {
Route::get('/compras/login', 'ComprasController@show')->name('compras.login');
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"])) {
throw new HttpException(400, "La request necesita un subpedido para guardar en sesión");
}
if (!isset($r["grupo_de_compra_id"])) {
throw new HttpException(400, "La request necesita un grupo de compra para guardar en sesión");
}
session(["subpedido_nombre" => $r["subpedido"]["nombre"]]);
session(["subpedido_id" => $r["subpedido"]["id"]]);
session(["gdc" => $r["grupo_de_compra_id"]]);
return "Subpedido guardado en sesión";
})->name('guardarSesion');
Route::get('obtener_sesion', function() {
return [
'subpedido' => [
'nombre' => session("subpedido_nombre"),
'id' => session("subpedido_id")
],
'gdc' => session("gdc")
];
})->name('obtenerSesion');
});
});
Route::get('/compras', 'ComprasController@show')->name('compras_login.show');
Route::middleware(['compras'])->group( function() {
Route::get('/compras/pedidos', 'ComprasController@indexPedidos')->name('compras.pedidos');
Route::middleware(['auth', 'role:comision'])->group( function() {
Route::get('/compras', 'RouteController@main')->name('compras');
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/pdf', 'ComprasController@pdf')->name('compras.pedidos.pdf');