forked from nathalie/pedi2
Compare commits
399 commits
agregar-pr
...
master
Author | SHA1 | Date | |
---|---|---|---|
7eb7dd04d7 | |||
45757f0ae1 | |||
45e8a189cf | |||
fd2c1b2970 | |||
2587c759fb | |||
2a1394a5c4 | |||
56eb59ddeb | |||
354079519a | |||
2ca91addb0 | |||
c46073ed06 | |||
df0b66bf65 | |||
641eb6a4d3 | |||
e598e1496b | |||
085d72b4f8 | |||
c54a0b361f | |||
2a5c215f40 | |||
584cebb902 | |||
d4bffd1df8 | |||
94c2baff07 | |||
7b36aa0b0c | |||
5feb2ff22f | |||
d0ffcda226 | |||
1f2ab91bd3 | |||
184b53cfc8 | |||
345be9c688 | |||
30eb822201 | |||
76f2af2ef9 | |||
9f93a17053 | |||
83016d38b0 | |||
cc734866c5 | |||
c31b808f05 | |||
95a9a404d2 | |||
57b8d6bcce | |||
ac4d5895be | |||
131bf33a73 | |||
5a4b933f11 | |||
bea8de2c8c | |||
92782be7f6 | |||
22cbac14f1 | |||
bddaf223e4 | |||
4b2ed9f90e | |||
cad8dffbcf | |||
c4eb4563d5 | |||
cef38cf69c | |||
8a0f8fbe13 | |||
9abeb98239 | |||
fab7695e6c | |||
2486826dba | |||
1b82700db3 | |||
635cecedb7 | |||
5c97fc9e70 | |||
8e885c18d8 | |||
b5d23c6740 | |||
f778a4f24e | |||
acc21091f9 | |||
ec10bdca92 | |||
f84e1f2954 | |||
df05b31b86 | |||
1b16669512 | |||
d170f9e46e | |||
f5f9838fc3 | |||
c58f24d2d0 | |||
e75fcc562a | |||
6dae6afe95 | |||
1c5b8ecb29 | |||
00b41afa5a | |||
1324898483 | |||
ba22988026 | |||
9c9b1dc6cb | |||
73f02dfff0 | |||
bbb57e1d7f | |||
2996cf4100 | |||
97ec11331b | |||
8c9a0ee4c1 | |||
01d5abdd99 | |||
![]() |
f9e55b38a5 | ||
![]() |
3ad9500f23 | ||
![]() |
538cc84e10 | ||
4e7e46f92d | |||
61e756f9e5 | |||
![]() |
be945b0eee | ||
![]() |
b5f4443836 | ||
c53e342f54 | |||
ff213bd90d | |||
![]() |
1e443ea2ca | ||
![]() |
c1af6909c4 | ||
![]() |
e31c375867 | ||
![]() |
6d10fbc0bf | ||
96eea84997 | |||
10273e2159 | |||
0b55fa8109 | |||
![]() |
9fc47513c2 | ||
![]() |
ff332fadc1 | ||
e106faceda | |||
![]() |
5ef5e93543 | ||
![]() |
177ba193de | ||
88e3532418 | |||
07289b01e9 | |||
5c1fa931eb | |||
![]() |
82f5862063 | ||
![]() |
7eeeae6a1e | ||
744b867af3 | |||
![]() |
52bea8f9b6 | ||
![]() |
d2fde9df83 | ||
![]() |
80a28f4162 | ||
![]() |
1586a1190a | ||
![]() |
a998f76d44 | ||
![]() |
b845637064 | ||
![]() |
6fc7021317 | ||
![]() |
728c54b3f3 | ||
917e3e1df3 | |||
517b95f14f | |||
2337ad9a37 | |||
99f327445e | |||
b4ca119f9a | |||
9d9ebd75b5 | |||
b79548624f | |||
![]() |
fbae6770df | ||
5f565a3f5a | |||
![]() |
69cd306263 | ||
bf3f697299 | |||
53691f6a30 | |||
9259248caf | |||
9f6f36e2e6 | |||
1204a80b3c | |||
b9702a736e | |||
4283eae3be | |||
28f2b3b498 | |||
079b21af04 | |||
ffd4570d44 | |||
82ad4dd910 | |||
e334234c9f | |||
3340de941b | |||
3770d728c3 | |||
c86eb97cb9 | |||
d91d46c589 | |||
449579a768 | |||
bb29e34b1b | |||
![]() |
078e29a01f | ||
189342d785 | |||
003cb86ec0 | |||
12b77de018 | |||
5453b1158f | |||
18ede25c16 | |||
6d58524823 | |||
2a57fdc1e9 | |||
ddd8d57d2b | |||
781ef8a7a1 | |||
43f2a1e928 | |||
97139905cc | |||
438071eea3 | |||
937e7ec16c | |||
9d96010752 | |||
4e7c1232b0 | |||
9b06839798 | |||
4a59b8e146 | |||
![]() |
39e1eaddcf | ||
![]() |
5ca11324a0 | ||
![]() |
758c425f91 | ||
2d7a90f6b2 | |||
11d18ad4a5 | |||
2beda0bf5b | |||
48e1a04bae | |||
f0f05f8361 | |||
800fed2097 | |||
![]() |
203b70e2ef | ||
![]() |
bf703489fd | ||
c57fd0436d | |||
d04650facf | |||
4ee91ed5a0 | |||
32d84879c7 | |||
6f1b4581ce | |||
0659f67e84 | |||
42f0cc11d4 | |||
05d13008fb | |||
5921767654 | |||
475d2a6cd9 | |||
9781d63a60 | |||
057170118d | |||
0c79d3b002 | |||
105335a773 | |||
749940233d | |||
d9e8264cd0 | |||
9babf1975f | |||
716d1ca6fa | |||
388beba5cc | |||
75b7507bbe | |||
9e7eb89014 | |||
808980d77e | |||
66984043bc | |||
05f2777a49 | |||
725a599b6a | |||
1c5cabb253 | |||
31dadcc10b | |||
70755ba0a2 | |||
8fc1a3b256 | |||
814295e8ee | |||
![]() |
fefd052a8e | ||
![]() |
ab7e44fd85 | ||
b66c2fe19d | |||
9cf4a117df | |||
0b427e4796 | |||
85238695c7 | |||
e9321678e2 | |||
43b0b04859 | |||
5036c2baa9 | |||
b19556fd39 | |||
35b0533664 | |||
![]() |
16e5075825 | ||
0665770972 | |||
1ccfbe11a7 | |||
4376586a23 | |||
d16a8de5ac | |||
10f4fefb0c | |||
f3ee5ea7d9 | |||
5b2c489f2d | |||
e7100121d4 | |||
e0a64d3653 | |||
fe2d8608cb | |||
e7c90a4899 | |||
0a51ccba2a | |||
988bf266e8 | |||
![]() |
3941dc0fd2 | ||
![]() |
1a8448ca45 | ||
9954dbd66a | |||
![]() |
14361a858e | ||
8f8733cb7b | |||
aab05541b2 | |||
![]() |
5b6fd933e3 | ||
9634f51054 | |||
80fbbd510d | |||
c278e0a7a5 | |||
f55ac3a4ff | |||
![]() |
e1aa6e2f68 | ||
71b4c1c38d | |||
a7a1f69c5a | |||
3a894f2965 | |||
381909fb7e | |||
17cc127f2e | |||
9991337b87 | |||
b58261bbf0 | |||
![]() |
b09d284a1d | ||
5a4a1ff391 | |||
72b3d1e4af | |||
ca3af75b5a | |||
![]() |
f50d4b975d | ||
![]() |
bf3b888e71 | ||
cadd9de7a0 | |||
![]() |
ccdba6efd4 | ||
dc339fb025 | |||
![]() |
962d6ec58a | ||
![]() |
8d3f686fa1 | ||
![]() |
148f0e5244 | ||
129d52d6d6 | |||
22e4253ea9 | |||
121dfa4c49 | |||
![]() |
9bf68050a7 | ||
![]() |
615c9427b9 | ||
023cef2bf3 | |||
2426638414 | |||
d18de9e4c0 | |||
6f2721a0f2 | |||
70aaadd901 | |||
daa998c89e | |||
c2ce43d295 | |||
d19c83289d | |||
2eac2a9554 | |||
599d55b684 | |||
ea6596a5b4 | |||
![]() |
3203f48f76 | ||
4acc2b0605 | |||
![]() |
55a94cb05b | ||
ddb09d56e7 | |||
![]() |
835cb14042 | ||
474e6871e3 | |||
![]() |
13a445ef64 | ||
![]() |
ee7cdc8d10 | ||
e712d3265f | |||
11ec71cd7e | |||
7b94e05d0a | |||
bc8e6cad10 | |||
![]() |
9e55955970 | ||
![]() |
334da613fa | ||
![]() |
1593a84b34 | ||
![]() |
a340647df4 | ||
![]() |
c8054a5272 | ||
![]() |
b56d82fff1 | ||
![]() |
9e1ea6299d | ||
![]() |
78a5e8ad61 | ||
![]() |
e61ad02dc2 | ||
![]() |
bb122c5e39 | ||
![]() |
2b68c2ea8d | ||
![]() |
d52382c1dc | ||
![]() |
f9f72cf830 | ||
eea1f2c3a2 | |||
aa70fa1c8d | |||
![]() |
896baf18dd | ||
![]() |
1a961c0732 | ||
![]() |
6005cc01b7 | ||
![]() |
323b77f6dc | ||
![]() |
f572b5a6bc | ||
![]() |
733e13ff66 | ||
![]() |
f8484959d6 | ||
![]() |
cf82e1e21e | ||
![]() |
b76f114ab0 | ||
![]() |
1b519ca440 | ||
![]() |
f67232f55c | ||
![]() |
426052ba09 | ||
![]() |
85f0a5a06b | ||
cf77fc5e61 | |||
![]() |
3e426d088d | ||
![]() |
4da7ff2431 | ||
![]() |
4906d2bb4d | ||
![]() |
69b244d074 | ||
![]() |
155a86975c | ||
![]() |
5c134989d2 | ||
![]() |
3f34483fd5 | ||
![]() |
e0d5bc5739 | ||
![]() |
f4d269721a | ||
a88e7ffb87 | |||
![]() |
60e8725ac3 | ||
![]() |
c0dd9c1124 | ||
![]() |
8284b793d3 | ||
be4ea4906a | |||
![]() |
f0a1a68144 | ||
![]() |
098b23b864 | ||
![]() |
cdb8522f65 | ||
b1f1444a0c | |||
![]() |
4939dbbd83 | ||
![]() |
25d5830462 | ||
![]() |
ac13e0a3f2 | ||
![]() |
1aa112aac2 | ||
![]() |
7468ca9086 | ||
![]() |
11cd073a51 | ||
![]() |
50cdbe57e8 | ||
![]() |
a3a4ecdd33 | ||
![]() |
6a00c6db22 | ||
![]() |
401c0e888f | ||
![]() |
75401556aa | ||
![]() |
53a203b067 | ||
![]() |
33d59073e3 | ||
c06a3ed38c | |||
eda9d5ce6c | |||
![]() |
4160f72615 | ||
![]() |
498c550a15 | ||
![]() |
b733100269 | ||
![]() |
8502fb72d9 | ||
![]() |
0d5551406a | ||
![]() |
72bbaac9a6 | ||
![]() |
ee916afdfd | ||
![]() |
ece866a3ec | ||
![]() |
77bdccdf86 | ||
![]() |
c8768665e0 | ||
f2dee0359f | |||
![]() |
5b18461bd9 | ||
![]() |
b0f25de000 | ||
![]() |
509ceaeefe | ||
![]() |
bcb1e1b2c8 | ||
![]() |
aa416b7ee4 | ||
![]() |
9e554b0a65 | ||
![]() |
b2e84204ce | ||
![]() |
dd1b66bd06 | ||
![]() |
8d5e780f94 | ||
dce0620365 | |||
323ab09238 | |||
3a2ffde0ab | |||
![]() |
a9bb6df711 | ||
![]() |
0410e844b1 | ||
![]() |
2d302d0116 | ||
![]() |
e4a08f5aed | ||
![]() |
70235970ab | ||
![]() |
67aadf157f | ||
![]() |
54e515cb7a | ||
![]() |
c82fdeee96 | ||
![]() |
34e4fd6bb5 | ||
![]() |
9fb5275007 | ||
![]() |
667d7dbddb | ||
![]() |
f7f06cfa3a | ||
![]() |
1779f573ec | ||
![]() |
fba73636de | ||
![]() |
3814022411 | ||
![]() |
53b998f1e5 | ||
86d2c0e9f6 | |||
8b4e07d66e | |||
420387cdb7 | |||
763fd3f646 | |||
7975528317 | |||
![]() |
89c9a85204 | ||
2f397c88d4 | |||
![]() |
3bf776b6f7 | ||
![]() |
0ec99a8cf5 | ||
![]() |
6f532eaf05 | ||
bc7b7e2f22 | |||
![]() |
b700239e18 | ||
0200160aa8 | |||
![]() |
39cc35341f | ||
![]() |
d17bc70c45 | ||
![]() |
48871e3f88 | ||
![]() |
546b901a05 |
104 changed files with 30831 additions and 1737 deletions
9
.directory
Normal file
9
.directory
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[Dolphin]
|
||||||
|
HeaderColumnWidths=372,72,103
|
||||||
|
SortRole=modificationtime
|
||||||
|
Timestamp=2022,6,1,16,36,48
|
||||||
|
Version=4
|
||||||
|
ViewMode=1
|
||||||
|
|
||||||
|
[Settings]
|
||||||
|
HiddenFilesShown=true
|
BIN
.docker-compose.yml.swp
Normal file
BIN
.docker-compose.yml.swp
Normal file
Binary file not shown.
|
@ -6,9 +6,11 @@ APP_URL=http://localhost
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
LOG_CHANNEL=stack
|
||||||
|
|
||||||
|
USERID=
|
||||||
|
|
||||||
DB_CONNECTION=mysql
|
DB_CONNECTION=mysql
|
||||||
DB_HOST=db
|
DB_HOST=db
|
||||||
DB_PORT=3306
|
DB_PORT_EXPOSED=3306
|
||||||
DB_DATABASE=pedi2
|
DB_DATABASE=pedi2
|
||||||
DB_USERNAME=pedi2
|
DB_USERNAME=pedi2
|
||||||
DB_PASSWORD=pedi2
|
DB_PASSWORD=pedi2
|
||||||
|
@ -48,3 +50,4 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
WEB_CLIENT_EMAIL=informaticamps@buzon.uy
|
WEB_CLIENT_EMAIL=informaticamps@buzon.uy
|
||||||
WEB_CLIENT_NAME=web
|
WEB_CLIENT_NAME=web
|
||||||
WEB_CLIENT_PASS=pass
|
WEB_CLIENT_PASS=pass
|
||||||
|
NGINX_PORT=8000
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -10,3 +10,9 @@ Homestead.json
|
||||||
Homestead.yaml
|
Homestead.yaml
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
|
.idea
|
||||||
|
/resources/csv/exports/*.csv
|
||||||
|
/resources/csv/canastas/*.csv
|
||||||
|
/public/css/
|
||||||
|
/public/js/
|
||||||
|
/public/mix-manifest.json
|
||||||
|
|
|
@ -12,7 +12,8 @@ RUN apt-get update && apt-get install -y \
|
||||||
libonig-dev \
|
libonig-dev \
|
||||||
libxml2-dev \
|
libxml2-dev \
|
||||||
zip \
|
zip \
|
||||||
unzip
|
unzip \
|
||||||
|
npm
|
||||||
|
|
||||||
# Clear cache
|
# Clear cache
|
||||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
33
README.md
33
README.md
|
@ -1,6 +1,6 @@
|
||||||
# Pedi2
|
# Pedi2
|
||||||
|
|
||||||
Aplicación de compras del Mercado Popular de Subsistencia.
|
Aplicación de pedidos del Mercado Popular de Subsistencia.
|
||||||
|
|
||||||
Pedi2 está hecha en Laravel 7 y utiliza laravel7-docker de dyarleniber.
|
Pedi2 está hecha en Laravel 7 y utiliza laravel7-docker de dyarleniber.
|
||||||
|
|
||||||
|
@ -17,41 +17,39 @@ Se utilizan los siguientes servicios, separadamente:
|
||||||
## Instalación
|
## Instalación
|
||||||
1. Una vez descargado el proyecto, hacé una copia del archivo `.env.example` que se encuentra en la raíz del proyecto y nombrala `.env`. Seteá los valores correctos - específicamente, para las variables, `APP_URL`, `DB_USERNAME` y `DB_PASSWORD`. Prestá atención a que `DB_HOST` sea el nombre del servicio que corre MySQL (por defecto `DB_HOST=db`).
|
1. Una vez descargado el proyecto, hacé una copia del archivo `.env.example` que se encuentra en la raíz del proyecto y nombrala `.env`. Seteá los valores correctos - específicamente, para las variables, `APP_URL`, `DB_USERNAME` y `DB_PASSWORD`. Prestá atención a que `DB_HOST` sea el nombre del servicio que corre MySQL (por defecto `DB_HOST=db`).
|
||||||
|
|
||||||
2. Construí la imagen de la app
|
2. Levantá los contenedores, construyendo la imagen de la app primero
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose build app
|
docker-compose up -d --build
|
||||||
```
|
|
||||||
|
|
||||||
3. Cuando termine, levantá los contenedores:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
```
|
||||||
|
|
||||||
El ambiente ahora está andando, pero necesitamos ejecutar un par de comandos para terminar la instalación de Laravel. Podemos usar `docker-compose exec [nombre-del-servicio]` previo a un comando para ejecutarlo dentro del contenedor.
|
El ambiente ahora está andando, pero necesitamos ejecutar un par de comandos para terminar la instalación de Laravel. Podemos usar `docker-compose exec [nombre-del-servicio]` previo a un comando para ejecutarlo dentro del contenedor.
|
||||||
|
|
||||||
4. Terminá de instalar las dependencias de la app, según fueron definidas en `composer.json`.
|
3. Terminá de instalar las dependencias de la app, según fueron definidas en `composer.json`.
|
||||||
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose exec app composer update
|
docker-compose exec app composer install
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Generá una clave de aplicación. Esta clave se usa para encriptar datos sensibles.
|
4. Generá una clave de aplicación. Esta clave se usa para encriptar datos sensibles.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose exec app php artisan key:generate
|
docker-compose exec app php artisan key:generate
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Corré las migraciones y seeders de Laravel
|
5. Corré las migraciones y seeders de Laravel
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose exec app php artisan migrate --seed
|
docker-compose exec app php artisan migrate:fresh --seed
|
||||||
```
|
```
|
||||||
|
|
||||||
7. Copia el token que se imprime al correr los seeders. Lo necesitamos para autenticar las llamadas que hagamos desde nuestro cliente web
|
6. Copia el token que se imprime al correr los seeders. Lo necesitamos para autenticar las llamadas que hagamos desde nuestro cliente web
|
||||||
|
|
||||||
|
7. Instala las dependencias de npm
|
||||||
|
```bash
|
||||||
|
docker-compose exec app npm install
|
||||||
|
```
|
||||||
|
|
||||||
Ahora la aplicación está corriendo y la podés ver en el puerto 8000 de tu dominio o IP. En caso de que estés en tu máquina local, la vas a ver accediendo a `http://localhost:8000` desde tu navegador.
|
Ahora la aplicación está corriendo y la podés ver en el puerto 8000 de tu dominio o IP. En caso de que estés en tu máquina local, la vas a ver accediendo a `http://localhost:8000` desde tu navegador.
|
||||||
|
|
||||||
|
@ -61,6 +59,11 @@ Podés usar el comando `logs` para ver los logs generados por tus servicios:
|
||||||
docker-compose logs nginx
|
docker-compose logs nginx
|
||||||
```
|
```
|
||||||
|
|
||||||
|
8. Ejecuta npm para compilar el js y css
|
||||||
|
```bash
|
||||||
|
docker-compose exec app npm run prod
|
||||||
|
```
|
||||||
|
|
||||||
## Services description
|
## Services description
|
||||||
|
|
||||||
### Dockerfile
|
### Dockerfile
|
||||||
|
|
11
app/CanastaLog.php
Normal file
11
app/CanastaLog.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class CanastaLog extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = ["path", "descripcion"];
|
||||||
|
protected $table = "carga_de_canastas";
|
||||||
|
}
|
62
app/Console/Commands/CrearPedidosAprobadosViewCommand.php
Normal file
62
app/Console/Commands/CrearPedidosAprobadosViewCommand.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class CrearPedidosAprobadosViewCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'view:CrearPedidosAprobadosView';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Crea view "pedidos_aprobados"';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
DB::statement("
|
||||||
|
CREATE OR REPLACE VIEW pedidos_aprobados
|
||||||
|
AS
|
||||||
|
SELECT
|
||||||
|
g.id as grupo_de_compra_id,
|
||||||
|
g.nombre as grupo_de_compra_nombre,
|
||||||
|
g.region as grupo_de_compra_region,
|
||||||
|
pr.id AS producto_id,
|
||||||
|
pr.nombre as producto_nombre,
|
||||||
|
pr.precio as producto_precio,
|
||||||
|
SUM(ps.cantidad) as cantidad_pedida,
|
||||||
|
pr.precio*SUM(ps.cantidad) as total_por_producto
|
||||||
|
FROM grupos_de_compra g
|
||||||
|
JOIN subpedidos s ON (s.grupo_de_compra_id = g.id AND s.aprobado=1)
|
||||||
|
JOIN producto_subpedido ps ON (ps.subpedido_id = s.id)
|
||||||
|
JOIN productos pr ON (pr.id = ps.producto_id)
|
||||||
|
GROUP BY
|
||||||
|
g.id, g.nombre, pr.id, pr.nombre
|
||||||
|
;");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,17 +2,319 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use App\Helpers\TransporteHelper;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use League\Csv\CannotInsertRecord;
|
||||||
|
use League\Csv\Reader;
|
||||||
|
use League\Csv\Writer;
|
||||||
|
use Mpdf\Mpdf;
|
||||||
|
|
||||||
class GrupoDeCompra extends Model
|
class GrupoDeCompra extends Model
|
||||||
{
|
{
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
protected $fillable = [ "nombre","region","telefono","correo","referente_finanzas","cantidad_de_nucleos"];
|
protected $fillable = ["nombre", "region", "telefono", "correo", "referente_finanzas", "cantidad_de_nucleos", "fila", "devoluciones_habilitadas"];
|
||||||
protected $table = 'grupos_de_compra';
|
protected $table = 'grupos_de_compra';
|
||||||
protected $hidden = ['password'];
|
protected $hidden = ['password'];
|
||||||
|
|
||||||
public function subpedidos() {
|
public function subpedidos()
|
||||||
|
{
|
||||||
return $this->hasMany('App\Subpedido');
|
return $this->hasMany('App\Subpedido');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function toggleDevoluciones()
|
||||||
|
{
|
||||||
|
$this->devoluciones_habilitadas = !$this->devoluciones_habilitadas;
|
||||||
|
$this->save();
|
||||||
|
return $this->devoluciones_habilitadas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pedidosAprobados()
|
||||||
|
{
|
||||||
|
return $this->subpedidos->where('aprobado', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalARecaudar()
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
foreach ($this->pedidosAprobados() as $subpedido) {
|
||||||
|
$total = $total + $subpedido->total();
|
||||||
|
}
|
||||||
|
return $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalBarrial()
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
foreach ($this->pedidosAprobados() as $subpedido) {
|
||||||
|
$total = $total + $subpedido->totalBarrial();
|
||||||
|
}
|
||||||
|
return $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalDevoluciones()
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
foreach ($this->pedidosAprobados() as $subpedido) {
|
||||||
|
$total = $total + $subpedido->devoluciones_total;
|
||||||
|
}
|
||||||
|
return $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalATransferir()
|
||||||
|
{
|
||||||
|
return $this->totalCentralesQueNoPaganTransporte()
|
||||||
|
+ $this->totalCentralesQuePaganTransporte()
|
||||||
|
+ $this->totalTransporte();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalCentralesQueNoPaganTransporte()
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
foreach ($this->pedidosAprobados() as $subpedido) {
|
||||||
|
$total = $total + $subpedido->totalCentralesQueNoPaganTransporte();
|
||||||
|
}
|
||||||
|
return $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalCentralesQuePaganTransporte()
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
foreach ($this->pedidosAprobados() as $subpedido) {
|
||||||
|
$total = $total + $subpedido->totalCentralesQuePaganTransporte();
|
||||||
|
}
|
||||||
|
return $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalTransporte()
|
||||||
|
{
|
||||||
|
return TransporteHelper::totalTransporte($this->totalCentralesQuePaganTransporte());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cantidadTransporte()
|
||||||
|
{
|
||||||
|
return TransporteHelper::cantidadTransporte($this->totalCentralesQuePaganTransporte());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exportarPlanillasAPdf()
|
||||||
|
{
|
||||||
|
$subpedidos = $this->pedidosAprobados();
|
||||||
|
//generar pdf
|
||||||
|
$mpdf = new Mpdf();
|
||||||
|
foreach ($subpedidos as $subpedido) {
|
||||||
|
$tabla = $subpedido->generarHTML();
|
||||||
|
// agregar la tabla al pdf en una nueva página
|
||||||
|
$mpdf->WriteHTML($tabla);
|
||||||
|
$mpdf->AddPage();
|
||||||
|
}
|
||||||
|
$filename = $this->nombre . '.pdf';
|
||||||
|
// imprimir el pdf
|
||||||
|
$mpdf->Output($filename, "D");
|
||||||
|
}
|
||||||
|
|
||||||
|
static function filaVacia(string $product, int $columns): array
|
||||||
|
{
|
||||||
|
$fila = [$product];
|
||||||
|
for ($i = 1; $i <= $columns; $i++) {
|
||||||
|
$fila[$i] = "0";
|
||||||
|
}
|
||||||
|
return $fila;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Asume que los productos están gruadados en orden de fila
|
||||||
|
public static function obtenerTemplateDeFilasVacias(int $columns)
|
||||||
|
{
|
||||||
|
$productosFilaID = Producto::productosFilaID();
|
||||||
|
$productosIDNombre = Producto::productosIDNombre();
|
||||||
|
$num_fila = 1;
|
||||||
|
$template = [];
|
||||||
|
foreach ($productosFilaID as $fila => $id) {
|
||||||
|
for ($i = $num_fila; $i < $fila; $i++) {
|
||||||
|
$template[$i] = GrupoDeCompra::filaVacia("", $columns);
|
||||||
|
}
|
||||||
|
$template[$fila] = GrupoDeCompra::filaVacia($productosIDNombre[$id], $columns);
|
||||||
|
$num_fila = $fila + 1;
|
||||||
|
}
|
||||||
|
$template[GrupoDeCompra::obtenerFilaDeBonoTransporte()] = GrupoDeCompra::filaVacia("Bonos de transporte", $columns);
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function obtenerFilaDeBonoTransporte()
|
||||||
|
{
|
||||||
|
$csv = Reader::createFromPath(resource_path('csv/productos.csv'), 'r');
|
||||||
|
$csv->setDelimiter("|");
|
||||||
|
$csv->setEnclosure("'");
|
||||||
|
$registros = $csv->getRecords();
|
||||||
|
|
||||||
|
foreach ($registros as $key => $registro)
|
||||||
|
if ($registro[0] == 'T') return $key;
|
||||||
|
|
||||||
|
throw new Exception('No hay bono de transporte');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function totalPedidosSinBonos()
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
foreach ($this->pedidosAprobados() as $pedido) {
|
||||||
|
$total += ceil($pedido->totalSinBonos());
|
||||||
|
}
|
||||||
|
return $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calcularCantidadBDT()
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
foreach ($this->pedidosAprobados() as $pedido) {
|
||||||
|
$total += $pedido->totalParaTransporte();
|
||||||
|
}
|
||||||
|
return ceil($total / 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalBonosBarriales()
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
$bonoBarrial = Producto::where('nombre', 'LIKE', '%barrial%')->first();
|
||||||
|
if ($bonoBarrial) {
|
||||||
|
$pedidos = $this->pedidosAprobados();
|
||||||
|
foreach ($pedidos as $pedido) {
|
||||||
|
$bonoPedido = $pedido->productos()->find($bonoBarrial["id"]);
|
||||||
|
if ($bonoPedido) {
|
||||||
|
$total += $bonoPedido["pivot"]["total"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exportarPedidoEnCSV()
|
||||||
|
{
|
||||||
|
$records = $this->generarColumnaCantidades();
|
||||||
|
try {
|
||||||
|
$writer = Writer::createFromPath(resource_path('csv/exports/' . $this->nombre . '.csv'), 'w');
|
||||||
|
$writer->insertAll($records);
|
||||||
|
} catch (CannotInsertRecord $e) {
|
||||||
|
var_export($e->getRecords());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generarColumnaCantidades()
|
||||||
|
{
|
||||||
|
$productos_en_pedido = DB::table('pedidos_aprobados')->where('grupo_de_compra_id', $this->id)->get()->keyBy('producto_id');
|
||||||
|
|
||||||
|
//si no hay pedidos aprobados, salir
|
||||||
|
if ($productos_en_pedido->count() == 0) {
|
||||||
|
\Log::debug("El grupo de compra " . $this->nombre . " no tiene pedidos aprobados.");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$records = $this->obtenerTemplateDeFilasVacias(1);
|
||||||
|
$productos_id_fila = Producto::productosIdFila();
|
||||||
|
foreach ($productos_en_pedido as $id => $producto_pedido) {
|
||||||
|
$fila = $productos_id_fila[$id];
|
||||||
|
$records[$fila][1] = $producto_pedido->cantidad_pedida;
|
||||||
|
}
|
||||||
|
|
||||||
|
$records[$this->obtenerFilaDeBonoTransporte()][1] = $this->calcularCantidadBDT();
|
||||||
|
|
||||||
|
return $records;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exportarPedidoConNucleosEnCSV()
|
||||||
|
{
|
||||||
|
$productos_en_pedido = DB::table('pedidos_aprobados')->where('grupo_de_compra_id', $this->id)->get()->keyBy('producto_id');
|
||||||
|
|
||||||
|
// si no hay pedidos aprobados, salir
|
||||||
|
if ($productos_en_pedido->count() == 0) {
|
||||||
|
\Log::debug("El grupo de compra " . $this->nombre . " no tiene pedidos aprobados.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pedidos = $this->pedidosAprobados();
|
||||||
|
// Generar tabla vacía con una columna por núcleo
|
||||||
|
$records = $this->obtenerTemplateDeFilasVacias($pedidos->count());
|
||||||
|
$productos_id_fila = Producto::productosIdFila();
|
||||||
|
|
||||||
|
foreach ($productos_en_pedido as $id => $producto_pedido) {
|
||||||
|
$fila = $productos_id_fila[$id];
|
||||||
|
$i = 1;
|
||||||
|
// Poner cantidad de cada producto para cada núcleo
|
||||||
|
foreach ($pedidos as $pedido) {
|
||||||
|
list($records, $i, $_) = $this->agregarCantidad($pedido, $id, $records, $fila, $i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Insertar lista de núcleos en la primera fila
|
||||||
|
$nucleos = [""];
|
||||||
|
$i = 1;
|
||||||
|
foreach ($pedidos as $pedido) {
|
||||||
|
$nucleos[$i] = $pedido->nombre;
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
array_splice($records, 0, 0, array($nucleos));
|
||||||
|
|
||||||
|
// Guardar en un archivo .csv
|
||||||
|
try {
|
||||||
|
$writer = Writer::createFromPath(resource_path('csv/exports/' . $this->nombre . '-completo.csv'), 'w');
|
||||||
|
$writer->insertAll($records);
|
||||||
|
} catch (CannotInsertRecord $e) {
|
||||||
|
var_export($e->getRecords());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function agregarCantidad($pedido, $id, array $records, $fila, int $i): array
|
||||||
|
{
|
||||||
|
$producto = $pedido->productos()->find($id);
|
||||||
|
$cantidad = $producto == NULL ? 0 : $producto->pivot->cantidad;
|
||||||
|
$records[$fila][$i] = $cantidad;
|
||||||
|
$i++;
|
||||||
|
return array($records, $i, $cantidad);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function totalesParaTransportePorBarrio() {
|
||||||
|
return DB::table('grupos_de_compra')
|
||||||
|
->leftJoin('subpedidos', 'grupos_de_compra.id', '=', 'subpedidos.grupo_de_compra_id')
|
||||||
|
->leftJoin('producto_subpedido', 'subpedidos.id', '=', 'producto_subpedido.subpedido_id')
|
||||||
|
->leftJoin('productos', 'producto_subpedido.producto_id', '=', 'productos.id')
|
||||||
|
->where(function ($query) {
|
||||||
|
$query->whereNull('productos.categoria')
|
||||||
|
->orWhere('productos.categoria', 'not like', '%SUBSIDIADO%');
|
||||||
|
})
|
||||||
|
->where(function ($query) {
|
||||||
|
$query->whereNull('productos.bono')
|
||||||
|
->orWhere('productos.bono', 0);
|
||||||
|
})
|
||||||
|
->where(function ($query) {
|
||||||
|
$query->whereNull('subpedidos.aprobado')
|
||||||
|
->orWhere('subpedidos.aprobado', 1);
|
||||||
|
})
|
||||||
|
->select(
|
||||||
|
'grupos_de_compra.id as id',
|
||||||
|
'grupos_de_compra.nombre as barrio',
|
||||||
|
DB::raw('COALESCE(SUM(producto_subpedido.cantidad * productos.precio), 0) as total')
|
||||||
|
)
|
||||||
|
->groupBy('grupos_de_compra.id')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static public function planillaTransporte() {
|
||||||
|
$totalesPorBarrio = self::totalesParaTransportePorBarrio();
|
||||||
|
$barrios = [];
|
||||||
|
$bonosDeTransporte = [];
|
||||||
|
|
||||||
|
foreach ($totalesPorBarrio as $totalBarrio) {
|
||||||
|
$barrios[] = $totalBarrio->barrio;
|
||||||
|
$bonosDeTransporte[] = ceil($totalBarrio->total / 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
$planilla = [];
|
||||||
|
$planilla[] = array_merge(['Barrio'], $barrios);
|
||||||
|
$planilla[] = array_merge(['Cant. bonos de transporte'], $bonosDeTransporte);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$writer = Writer::createFromPath(resource_path('csv/exports/transporte-por-barrio.csv'), 'w');
|
||||||
|
$writer->insertAll($planilla);
|
||||||
|
} catch (CannotInsertRecord $e) {
|
||||||
|
var_export($e->getRecords());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
151
app/Helpers/CanastaHelper.php
Normal file
151
app/Helpers/CanastaHelper.php
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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;
|
||||||
|
use League\Csv\Reader;
|
||||||
|
|
||||||
|
class CanastaHelper
|
||||||
|
{
|
||||||
|
const FILA_HEADER = "Tipo";
|
||||||
|
const ULTIMA_FILA = "TOTAL";
|
||||||
|
const ARCHIVO_SUBIDO = 'Archivo subido';
|
||||||
|
const CANASTA_CARGADA = 'Canasta cargada';
|
||||||
|
|
||||||
|
public static function guardarCanasta($data, $path): string {
|
||||||
|
$nombre = $data->getClientOriginalName();
|
||||||
|
|
||||||
|
$data->move(resource_path($path), $nombre);
|
||||||
|
|
||||||
|
self::log($path . $nombre, self::ARCHIVO_SUBIDO);
|
||||||
|
|
||||||
|
return $nombre;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function cargarCanasta($archivo) {
|
||||||
|
self::limpiarTablas();
|
||||||
|
|
||||||
|
$csv = Reader::createFromPath(resource_path($archivo), 'r');
|
||||||
|
$csv->setDelimiter("|");
|
||||||
|
$iHeader = self::obtenerIndiceDeHeader($csv);
|
||||||
|
$csv->setHeaderOffset($iHeader);
|
||||||
|
$registros = $csv->getRecords();
|
||||||
|
|
||||||
|
$toInsert = [];
|
||||||
|
$categoria = '';
|
||||||
|
foreach($registros as $i => $registro){
|
||||||
|
//filas que están arriba del header
|
||||||
|
if ($i <= $iHeader){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//finalizar
|
||||||
|
if ($registro[self::FILA_HEADER] == self::ULTIMA_FILA) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//filas que no tienen tipo
|
||||||
|
if (!Arr::has($registro,self::FILA_HEADER)|| trim($registro[self::FILA_HEADER]) == ''){
|
||||||
|
var_dump("no hay tipo en la fila " . $i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//saltear bono de transporte
|
||||||
|
if ($registro[self::FILA_HEADER] == "T"){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//obtener categoria
|
||||||
|
if ($registro['Producto'] == '') {
|
||||||
|
//es la pregunta de la copa?
|
||||||
|
if (Str::contains($registro[self::FILA_HEADER],"¿")) { continue; }
|
||||||
|
$categoria = $registro[self::FILA_HEADER];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//completar producto
|
||||||
|
$toInsert[] = [
|
||||||
|
'fila' => $i,
|
||||||
|
'categoria' => $categoria,
|
||||||
|
'nombre' => trim(str_replace('*', ' ',$registro['Producto'])),
|
||||||
|
'precio' => $registro['Precio'],
|
||||||
|
'proveedor_id' => self::obtenerProveedor($registro['Producto']),
|
||||||
|
'bono' => $registro[self::FILA_HEADER] == "B",
|
||||||
|
'requiere_notas'=> $registro[self::FILA_HEADER] =="PTC",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array_chunk($toInsert,DatabaseSeeder::CHUNK_SIZE) as $chunk) {
|
||||||
|
DB::table('productos')->insert($chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
self::agregarBonoBarrial();
|
||||||
|
|
||||||
|
self::log($archivo, self::CANASTA_CARGADA);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function obtenerIndiceDeHeader($csv){
|
||||||
|
$registros = $csv->getRecords();
|
||||||
|
$iheader = 0;
|
||||||
|
foreach ($registros as $i => $registro){
|
||||||
|
if (strtolower($registro[0]) == strtolower(self::FILA_HEADER)) {
|
||||||
|
$iheader = $i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $iheader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function obtenerProveedor($nombre) {
|
||||||
|
$result = null;
|
||||||
|
if (Str::contains($nombre,"*")){
|
||||||
|
$result = Proveedor::firstOrCreate([
|
||||||
|
'nombre' => 'Proveedor de economía solidaria',
|
||||||
|
'economia_solidaria' => 1,
|
||||||
|
'nacional' => 1
|
||||||
|
])->id;
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $path
|
||||||
|
* @param $descripcion
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function log($path, $descripcion): void
|
||||||
|
{
|
||||||
|
$log = new CanastaLog([
|
||||||
|
'path' => $path,
|
||||||
|
'descripcion' => $descripcion,
|
||||||
|
]);
|
||||||
|
$log->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function limpiarTablas()
|
||||||
|
{
|
||||||
|
DB::delete('delete from producto_subpedido');
|
||||||
|
DB::delete('delete from productos');
|
||||||
|
DB::delete('delete from subpedidos');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function agregarBonoBarrial()
|
||||||
|
{
|
||||||
|
$categoria = Producto::all()->pluck('categoria')->unique()->flatten()->first(function ($c) { return Str::contains($c, 'BONO'); });
|
||||||
|
DB::table('productos')->insert([
|
||||||
|
'fila' => 420,
|
||||||
|
'nombre' => "Bono barrial",
|
||||||
|
'precio' => 20,
|
||||||
|
'categoria' => $categoria,
|
||||||
|
'bono' => 1,
|
||||||
|
'proveedor_id' => null,
|
||||||
|
'requiere_notas'=> false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
19
app/Helpers/TransporteHelper.php
Normal file
19
app/Helpers/TransporteHelper.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
class TransporteHelper
|
||||||
|
{
|
||||||
|
const COSTO_TRANSPORTE = 15;
|
||||||
|
const MONTO_TRANSPORTE = 500;
|
||||||
|
|
||||||
|
public static function cantidadTransporte($monto)
|
||||||
|
{
|
||||||
|
return ceil($monto / self::MONTO_TRANSPORTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function totalTransporte($monto)
|
||||||
|
{
|
||||||
|
return self::cantidadTransporte($monto) * self::COSTO_TRANSPORTE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,34 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\GrupoDeCompra;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Response;
|
||||||
|
|
||||||
class AdminController extends Controller
|
class AdminController extends Controller
|
||||||
{
|
{
|
||||||
//
|
public function show()
|
||||||
|
{
|
||||||
|
return view('auth/admin_login');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
return view('auth/admin_subpedidos');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exportarPlanillasAPdf(GrupoDeCompra $gdc) {
|
||||||
|
return $gdc->exportarPlanillasAPdf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exportarPedidoACSV(GrupoDeCompra $gdc) {
|
||||||
|
$gdc->exportarPedidoEnCSV();
|
||||||
|
$file = resource_path('csv/exports/'.$gdc->nombre.'.csv');
|
||||||
|
return response()->download($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exportarPedidoConNucleosACSV(GrupoDeCompra $gdc) {
|
||||||
|
$gdc->exportarPedidoConNucleosEnCSV();
|
||||||
|
$file = resource_path('csv/exports/'.$gdc->nombre.'-completo.csv');
|
||||||
|
return response()->download($file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
19
app/Http/Controllers/Api/GrupoDeCompraController.php
Normal file
19
app/Http/Controllers/Api/GrupoDeCompraController.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\GrupoDeCompra;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Resources\GrupoDeCompraResource;
|
||||||
|
|
||||||
|
class GrupoDeCompraController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return GrupoDeCompraResource::collection(GrupoDeCompra::all());
|
||||||
|
}
|
||||||
|
public function show(GrupoDeCompra $grupoDeCompra)
|
||||||
|
{
|
||||||
|
return new GrupoDeCompraResource($grupoDeCompra);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,11 +3,12 @@
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Producto;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Filtros\FiltroDeSubpedido;
|
use App\Filtros\FiltroDeSubpedido;
|
||||||
use App\Subpedido;
|
use App\Subpedido;
|
||||||
use App\GrupoDeCompra;
|
use App\GrupoDeCompra;
|
||||||
use App\Producto;
|
use App\Http\Resources\SubpedidoResource;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
|
|
||||||
|
@ -26,8 +27,13 @@ class SubpedidoController extends Controller
|
||||||
return Subpedido::filtrar($filtros)->get();
|
return Subpedido::filtrar($filtros)->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function indexResources(FiltroDeSubpedido $filtros, Request $request)
|
||||||
|
{
|
||||||
|
return SubpedidoResource::collection(Subpedido::filtrar($filtros)->get());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guardar un nuevo registro en el almacenamiento.
|
* Store a newly created resource in storage.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
|
@ -45,28 +51,6 @@ class SubpedidoController extends Controller
|
||||||
return $s;
|
return $s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Agregar un producto a un subpedido.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function updateProducto(Subpedido $subpedido, Producto $producto, Request $request)
|
|
||||||
{
|
|
||||||
$validado = $this->validateActualizacionDeProducto();
|
|
||||||
$cantidad = $validado['cantidad'];
|
|
||||||
|
|
||||||
if ($cantidad){
|
|
||||||
//si la cantidad es 1 o más se agrega el producto o actualiza la cantidad
|
|
||||||
$subpedido->productos()->syncWithoutDetaching([$producto->id => ['cantidad' => $cantidad]]);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//si la cantidad es 0, se elimina el producto del subpedido
|
|
||||||
$subpedido->productos()->detach($producto->id);
|
|
||||||
}
|
|
||||||
return response('Producto ' . $producto->nombre . ' actualizado en subpedido de ' . $subpedido->nombre . ' (cantidad ' . $cantidad . ')', 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function validateSubpedido(){
|
protected function validateSubpedido(){
|
||||||
return request()->validate([
|
return request()->validate([
|
||||||
'nombre' => 'required|max:255',
|
'nombre' => 'required|max:255',
|
||||||
|
@ -77,9 +61,55 @@ class SubpedidoController extends Controller
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function validateActualizacionDeProducto(){
|
/**
|
||||||
return request()->validate([
|
* Display the specified resource.
|
||||||
'cantidad' => 'required|min:0'
|
*
|
||||||
|
* @param \App\Subpedido $subpedido
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function show(Subpedido $subpedido)
|
||||||
|
{
|
||||||
|
return new SubpedidoResource($subpedido);
|
||||||
|
}
|
||||||
|
|
||||||
|
// recibe request, saca producto y cantidad, valida, y pasa a syncProducto en Subpedido
|
||||||
|
public function syncProductos(Subpedido $subpedido) {
|
||||||
|
if ($subpedido->aprobado)
|
||||||
|
return new SubpedidoResource($subpedido);
|
||||||
|
|
||||||
|
$valid = request()->validate([
|
||||||
|
'cantidad' => ['integer','required','min:0'],
|
||||||
|
'notas' => 'nullable',
|
||||||
|
'producto_id' => [
|
||||||
|
'required',
|
||||||
|
Rule::in(Producto::all()->pluck('id')),
|
||||||
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$producto = Producto::find($valid['producto_id']);
|
||||||
|
$notas = $valid['notas'];
|
||||||
|
$cantidad = $valid['cantidad'];
|
||||||
|
$subpedido->syncProducto($producto, $cantidad, $notas ?? "");
|
||||||
|
return new SubpedidoResource($subpedido);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggleAprobacion(Subpedido $subpedido) {
|
||||||
|
$valid = request()->validate([
|
||||||
|
'aprobacion' => 'required | boolean'
|
||||||
|
]);
|
||||||
|
$subpedido->toggleAprobacion($valid['aprobacion']);
|
||||||
|
return new SubpedidoResource($subpedido);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function syncDevoluciones(Subpedido $subpedido) {
|
||||||
|
if ($subpedido->aprobado) return new SubpedidoResource($subpedido);
|
||||||
|
|
||||||
|
$valid = request()->validate([
|
||||||
|
'total' => 'required|min:0',
|
||||||
|
'notas' => 'min:0'
|
||||||
|
]);
|
||||||
|
$subpedido->syncDevoluciones($valid['total'], $valid['notas'] ?? "");
|
||||||
|
|
||||||
|
return new SubpedidoResource($subpedido);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers\Auth;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Providers\RouteServiceProvider;
|
use App\Providers\RouteServiceProvider;
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class LoginController extends Controller
|
class LoginController extends Controller
|
||||||
|
@ -28,6 +29,18 @@ class LoginController extends Controller
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = RouteServiceProvider::HOME;
|
protected $redirectTo = RouteServiceProvider::HOME;
|
||||||
|
|
||||||
|
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('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new controller instance.
|
* Create a new controller instance.
|
||||||
*
|
*
|
||||||
|
|
59
app/Http/Controllers/ComprasController.php
Normal file
59
app/Http/Controllers/ComprasController.php
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\GrupoDeCompra;
|
||||||
|
use App\Helpers\CanastaHelper;
|
||||||
|
use App\Producto;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ComprasController
|
||||||
|
{
|
||||||
|
const CANASTAS_PATH = 'csv/canastas/';
|
||||||
|
|
||||||
|
public function indexPedidos() {
|
||||||
|
return view('compras_pedidos');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function descargarPedidos() {
|
||||||
|
Producto::planillaTotales();
|
||||||
|
$file = resource_path('csv/exports/pedidos-por-barrio.csv');
|
||||||
|
return response()->download($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function descargarNotas() {
|
||||||
|
Producto::planillaNotas();
|
||||||
|
$file = resource_path('csv/exports/notas-por-barrio.csv');
|
||||||
|
return response()->download($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function descargarTransporte() {
|
||||||
|
GrupoDeCompra::planillaTransporte();
|
||||||
|
$file = resource_path('csv/exports/transporte-por-barrio.csv');
|
||||||
|
return response()->download($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show()
|
||||||
|
{
|
||||||
|
return view('auth/compras_login');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cargarCanasta(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'data' => 'required|file|mimes:csv,txt|max:2048',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$nombre = CanastaHelper::guardarCanasta($request->file('data'), self::CANASTAS_PATH);
|
||||||
|
CanastaHelper::cargarCanasta(self::CANASTAS_PATH . $nombre);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Canasta cargada exitosamente',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function descargarCanastaEjemplo() {
|
||||||
|
$file = resource_path('csv/productos.csv');
|
||||||
|
return response()->download($file);
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,6 +56,8 @@ class Kernel extends HttpKernel
|
||||||
*/
|
*/
|
||||||
protected $routeMiddleware = [
|
protected $routeMiddleware = [
|
||||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||||
|
'admin' => \App\Http\Middleware\Admin::class,
|
||||||
|
'compras' => \App\Http\Middleware\Compras::class,
|
||||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
|
|
26
app/Http/Middleware/Admin.php
Normal file
26
app/Http/Middleware/Admin.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Auth;
|
||||||
|
|
||||||
|
class Admin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
if ($user->is_admin) {
|
||||||
|
return $next($request);
|
||||||
|
} else {
|
||||||
|
return response('Necesitás ser admin para hacer esto', 403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
app/Http/Middleware/Compras.php
Normal file
29
app/Http/Middleware/Compras.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
app/Http/Resources/GrupoDeCompraResource.php
Normal file
30
app/Http/Resources/GrupoDeCompraResource.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class GrupoDeCompraResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray($request)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'nombre' => $this->nombre,
|
||||||
|
'devoluciones_habilitadas' => $this->devoluciones_habilitadas,
|
||||||
|
'pedidos' => SubpedidoResource::collection($this->subpedidos),
|
||||||
|
'total_a_recaudar' => number_format($this->totalARecaudar(),2),
|
||||||
|
'total_barrial' => number_format($this->totalBarrial(),2),
|
||||||
|
'total_devoluciones' => number_format($this->totalDevoluciones(),2),
|
||||||
|
'total_a_transferir' => number_format($this->totalATransferir(),2),
|
||||||
|
'total_transporte' => number_format($this->totalTransporte(),0),
|
||||||
|
'cantidad_transporte' => number_format($this->cantidadTransporte(),0),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,8 @@ class ProductoResource extends JsonResource
|
||||||
'imagen' => optional($this->poster)->url(),
|
'imagen' => optional($this->poster)->url(),
|
||||||
'descripcion' => $this->descripcion,
|
'descripcion' => $this->descripcion,
|
||||||
'apto_veganxs' => $this->apto_veganxs,
|
'apto_veganxs' => $this->apto_veganxs,
|
||||||
'apto_celiacxs' => $this->apto_celiacxs
|
'apto_celiacxs' => $this->apto_celiacxs,
|
||||||
|
'requiere_notas' => $this->requiere_notas,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
31
app/Http/Resources/SubpedidoResource.php
Normal file
31
app/Http/Resources/SubpedidoResource.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class SubpedidoResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray($request)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'nombre' => $this->nombre,
|
||||||
|
'grupo_de_compra' => $this->grupoDeCompra,
|
||||||
|
'productos' => $this->productos,
|
||||||
|
'aprobado' => (bool) $this->aprobado,
|
||||||
|
'total' => number_format($this->total(),2),
|
||||||
|
'total_transporte' => number_format($this->totalTransporte(),0),
|
||||||
|
'cantidad_transporte' => number_format($this->cantidadTransporte(),0),
|
||||||
|
'total_sin_devoluciones' => number_format($this->totalSinDevoluciones(),2),
|
||||||
|
'devoluciones_total' => number_format($this->devoluciones_total,2),
|
||||||
|
'devoluciones_notas' => $this->devoluciones_notas
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
131
app/Producto.php
131
app/Producto.php
|
@ -2,19 +2,24 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use App\Filtros\FiltroDeProducto;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Filtros\FiltroDeProducto;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use League\Csv\CannotInsertRecord;
|
||||||
|
use League\Csv\Reader;
|
||||||
|
use League\Csv\Writer;
|
||||||
|
|
||||||
class Producto extends Model
|
class Producto extends Model
|
||||||
{
|
{
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
protected $fillable = [ "nombre", "precio", "presentacion", "stock", "categoria" ];
|
protected $fillable = ["nombre", "precio", "presentacion", "stock", "categoria"];
|
||||||
static $paginarPorDefecto = 10;
|
static $paginarPorDefecto = 10;
|
||||||
|
|
||||||
public function subpedidos()
|
public function subpedidos()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany('App\Subpedido','productos_subpedidos')->withPivot(["cantidad"]);
|
return $this->belongsToMany('App\Subpedido', 'productos_subpedidos')->withPivot(["cantidad", "notas"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function proveedor()
|
public function proveedor()
|
||||||
|
@ -22,6 +27,11 @@ class Producto extends Model
|
||||||
return $this->belongsTo('App\Proveedor');
|
return $this->belongsTo('App\Proveedor');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function pagaTransporte()
|
||||||
|
{
|
||||||
|
return !($this->bono || Str::contains($this->categoria, 'SUBSIDIADO'));
|
||||||
|
}
|
||||||
|
|
||||||
//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)
|
public function scopeFiltrar($query, FiltroDeProducto $filtros)
|
||||||
{
|
{
|
||||||
|
@ -33,4 +43,119 @@ class Producto extends Model
|
||||||
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::$paginarPorDefecto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function productosFilaID()
|
||||||
|
{
|
||||||
|
return Producto::pluck('id', 'fila',)->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function productosIDFila()
|
||||||
|
{
|
||||||
|
return Producto::pluck('fila', 'id',)->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function productosIDNombre()
|
||||||
|
{
|
||||||
|
return Producto::pluck('nombre', 'id',)->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function cantidadesPorBarrio()
|
||||||
|
{
|
||||||
|
$barrios = DB::table('grupos_de_compra')
|
||||||
|
->where('nombre', '<>', 'PRUEBA')
|
||||||
|
->pluck('id', 'nombre');
|
||||||
|
|
||||||
|
$columnasBarrios = $barrios->map(function ($id, $nombre) {
|
||||||
|
return DB::raw("SUM(CASE WHEN subpedidos.grupo_de_compra_id = $id AND subpedidos.aprobado = 1 THEN producto_subpedido.cantidad ELSE 0 END) as `$nombre`");
|
||||||
|
})->toArray();
|
||||||
|
|
||||||
|
return DB::table('productos')
|
||||||
|
->where('productos.nombre', 'not like', '%barrial%')
|
||||||
|
->leftJoin('producto_subpedido', 'productos.id', '=', 'producto_subpedido.producto_id')
|
||||||
|
->leftJoin('subpedidos', 'subpedidos.id', '=', 'producto_subpedido.subpedido_id')
|
||||||
|
->select(array_merge(
|
||||||
|
['productos.fila as fila'],
|
||||||
|
['productos.nombre as producto'],
|
||||||
|
$columnasBarrios
|
||||||
|
))
|
||||||
|
->groupBy('productos.fila', 'productos.id', 'productos.nombre')
|
||||||
|
->orderBy('productos.fila')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function planillaTotales() {
|
||||||
|
$headers = ['Producto'];
|
||||||
|
$barrios = DB::table('grupos_de_compra')
|
||||||
|
->where('nombre', '<>', 'PRUEBA')
|
||||||
|
->pluck('nombre')->toArray();
|
||||||
|
$headers = array_merge($headers, $barrios);
|
||||||
|
|
||||||
|
$cantidadesPorBarrio = self::cantidadesPorBarrio();
|
||||||
|
$planilla = [];
|
||||||
|
$ultimaFila = 1;
|
||||||
|
|
||||||
|
foreach ($cantidadesPorBarrio as $productoCantidades) {
|
||||||
|
$fila = $productoCantidades->fila;
|
||||||
|
while ($fila - $ultimaFila > 1) {
|
||||||
|
$ultimaFila++;
|
||||||
|
$planilla[$ultimaFila] = ['---'];
|
||||||
|
}
|
||||||
|
$planilla[$fila] = [$productoCantidades->producto];
|
||||||
|
foreach ($barrios as $barrio) {
|
||||||
|
$planilla[$fila][] = $productoCantidades->$barrio ?? 0;
|
||||||
|
}
|
||||||
|
$ultimaFila = $fila;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$writer = Writer::createFromPath(resource_path('csv/exports/pedidos-por-barrio.csv'), 'w');
|
||||||
|
$writer->insertOne($headers);
|
||||||
|
$writer->insertAll($planilla);
|
||||||
|
} catch (CannotInsertRecord $e) {
|
||||||
|
var_export($e->getRecords());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function notasPorBarrio(): \Illuminate\Support\Collection
|
||||||
|
{
|
||||||
|
return DB::table('productos')
|
||||||
|
->join('producto_subpedido', 'productos.id', '=', 'producto_subpedido.producto_id')
|
||||||
|
->join('subpedidos', 'producto_subpedido.subpedido_id', '=', 'subpedidos.id')
|
||||||
|
->join('grupos_de_compra', 'subpedidos.grupo_de_compra_id', '=', 'grupos_de_compra.id')
|
||||||
|
->where('productos.requiere_notas', 1)
|
||||||
|
->select(
|
||||||
|
'productos.nombre as producto',
|
||||||
|
'grupos_de_compra.nombre as barrio',
|
||||||
|
'producto_subpedido.notas'
|
||||||
|
)
|
||||||
|
->get()
|
||||||
|
->groupBy('producto');
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function planillaNotas() {
|
||||||
|
$headers = ['Producto'];
|
||||||
|
$barrios = DB::table('grupos_de_compra')
|
||||||
|
->where('nombre', '<>', 'PRUEBA')
|
||||||
|
->pluck('nombre')->toArray();
|
||||||
|
$headers = array_merge($headers, $barrios);
|
||||||
|
|
||||||
|
$notasPorBarrio = self::notasPorBarrio();
|
||||||
|
$planilla = [];
|
||||||
|
|
||||||
|
foreach ($notasPorBarrio as $producto => $notasGrupo) {
|
||||||
|
$fila = [$producto];
|
||||||
|
foreach ($barrios as $barrio) {
|
||||||
|
$notas = $notasGrupo->where('barrio', $barrio)->pluck('notas')->implode('; ');
|
||||||
|
$fila[] = $notas ?: '';
|
||||||
|
}
|
||||||
|
$planilla[] = $fila;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$writer = Writer::createFromPath(resource_path('csv/exports/notas-por-barrio.csv'), 'w');
|
||||||
|
$writer->insertOne($headers);
|
||||||
|
$writer->insertAll($planilla);
|
||||||
|
} catch (CannotInsertRecord $e) {
|
||||||
|
var_export($e->getRecords());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use League\Csv\Reader;
|
use App\Helpers\TransporteHelper;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Log;
|
use Log;
|
||||||
|
@ -10,12 +10,24 @@ use App\Filtros\FiltroDeSubpedido;
|
||||||
|
|
||||||
class Subpedido extends Model
|
class Subpedido extends Model
|
||||||
{
|
{
|
||||||
|
const COSTO_TRANSPORTE = 15;
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
protected $fillable = ['grupo_de_compra_id', 'aprobado', 'nombre'];
|
protected $fillable = ['grupo_de_compra_id', 'aprobado', 'nombre', 'devoluciones_total', 'devoluciones_notas'];
|
||||||
|
|
||||||
public function productos()
|
public function productos()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany('App\Producto')->withPivot(["cantidad"]);
|
return $this->belongsToMany('App\Producto')->withPivot(["cantidad","total", "notas"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Bonos del MPS, Sororo, etc. NO devuelve bonos de transporte
|
||||||
|
private function bonos()
|
||||||
|
{
|
||||||
|
return $this->productos()->where('bono',1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function productosSinBonos()
|
||||||
|
{
|
||||||
|
return $this->productos()->where('bono',false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function grupoDeCompra()
|
public function grupoDeCompra()
|
||||||
|
@ -23,10 +35,103 @@ class Subpedido extends Model
|
||||||
return $this->belongsTo('App\GrupoDeCompra');
|
return $this->belongsTo('App\GrupoDeCompra');
|
||||||
}
|
}
|
||||||
|
|
||||||
//Este método permite que se apliquen los filtros al hacer una request (por ejemplo, de búsqueda)
|
//Permite que se apliquen los filtros al hacer una request (por ejemplo, de búsqueda)
|
||||||
public function scopeFiltrar($query, FiltroDeSubpedido $filtros)
|
public function scopeFiltrar($query, FiltroDeSubpedido $filtros)
|
||||||
{
|
{
|
||||||
return $filtros->aplicar($query);
|
return $filtros->aplicar($query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function total()
|
||||||
|
{
|
||||||
|
return $this->totalSinDevoluciones() - $this->devoluciones_total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalSinDevoluciones()
|
||||||
|
{
|
||||||
|
return $this->totalBarrial() + $this->totalCentral();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalBarrial()
|
||||||
|
{
|
||||||
|
return DB::table('producto_subpedido')
|
||||||
|
->join('productos', 'producto_subpedido.producto_id', '=', 'productos.id')
|
||||||
|
->where('producto_subpedido.subpedido_id', $this->id)
|
||||||
|
->where('productos.nombre', 'like', '%barrial%')
|
||||||
|
->selectRaw('SUM(productos.precio * producto_subpedido.cantidad) as total')
|
||||||
|
->value('total');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalCentral()
|
||||||
|
{
|
||||||
|
return $this->totalCentralesQueNoPaganTransporte() + $this->totalCentralesQuePaganTransporte() + $this->totalTransporte();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalCentralesQueNoPaganTransporte()
|
||||||
|
{
|
||||||
|
return DB::table('producto_subpedido')
|
||||||
|
->join('productos', 'producto_subpedido.producto_id', '=', 'productos.id')
|
||||||
|
->where('producto_subpedido.subpedido_id', $this->id)
|
||||||
|
->where('productos.nombre', 'not like', '%barrial%')
|
||||||
|
->where(function ($query) {
|
||||||
|
$query->where('productos.categoria', 'like', '%SUBSIDIADO%')
|
||||||
|
->orWhere('productos.bono', true);
|
||||||
|
})
|
||||||
|
->selectRaw('SUM(productos.precio * producto_subpedido.cantidad) as total')
|
||||||
|
->value('total');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalCentralesQuePaganTransporte()
|
||||||
|
{
|
||||||
|
return DB::table('producto_subpedido')
|
||||||
|
->join('productos', 'producto_subpedido.producto_id', '=', 'productos.id')
|
||||||
|
->where('producto_subpedido.subpedido_id', $this->id)
|
||||||
|
->where('productos.nombre', 'not like', '%barrial%')
|
||||||
|
->where('productos.bono', false)
|
||||||
|
->where('productos.categoria', 'not like', '%SUBSIDIADO%')
|
||||||
|
->selectRaw('SUM(productos.precio * producto_subpedido.cantidad) as total')
|
||||||
|
->value('total');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totalTransporte()
|
||||||
|
{
|
||||||
|
return TransporteHelper::totalTransporte($this->totalCentralesQuePaganTransporte());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cantidadTransporte()
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
public function syncProducto(Producto $producto, Int $cantidad, string $notas) {
|
||||||
|
if ($cantidad){
|
||||||
|
//si la cantidad es 1 o más se agrega el producto o actualiza la cantidad
|
||||||
|
$this->productos()->syncWithoutDetaching([
|
||||||
|
$producto->id => [
|
||||||
|
'cantidad' => $cantidad,
|
||||||
|
'total' => $cantidad * $producto->precio,
|
||||||
|
'notas' => $notas,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
//si la cantidad es 0, se elimina el producto del subpedido
|
||||||
|
$this->productos()->detach($producto->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggleAprobacion(bool $aprobacion) {
|
||||||
|
$this->aprobado = $aprobacion;
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generarHTML() {
|
||||||
|
$view = view("pdfgen.subpedido_tabla", ["subpedido" => $this]);
|
||||||
|
return $view->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function syncDevoluciones(float $total, string $notas) {
|
||||||
|
$this->devoluciones_total = $total;
|
||||||
|
$this->devoluciones_notas = $notas;
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2.5|^8.0",
|
"php": "^7.4",
|
||||||
"fideloper/proxy": "^4.4",
|
"fideloper/proxy": "^4.4",
|
||||||
"fruitcake/laravel-cors": "^2.0",
|
"fruitcake/laravel-cors": "^2.0",
|
||||||
"guzzlehttp/guzzle": "^6.3.1|^7.0.1",
|
"guzzlehttp/guzzle": "^6.3.1|^7.0.1",
|
||||||
|
@ -16,7 +16,9 @@
|
||||||
"laravel/sanctum": "^2.13",
|
"laravel/sanctum": "^2.13",
|
||||||
"laravel/tinker": "^2.5",
|
"laravel/tinker": "^2.5",
|
||||||
"laravel/ui": "*",
|
"laravel/ui": "*",
|
||||||
"league/csv": "^9.8"
|
"league/csv": "^9.8",
|
||||||
|
"mpdf/mpdf": "^8.1",
|
||||||
|
"prexview/prexview": "^1.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"facade/ignition": "^2.0",
|
"facade/ignition": "^2.0",
|
||||||
|
@ -52,13 +54,13 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"post-autoload-dump": [
|
"post-autoload-dump": [
|
||||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||||
"@php artisan package:discover --ansi"
|
"@php7.4 artisan package:discover --ansi"
|
||||||
],
|
],
|
||||||
"post-root-package-install": [
|
"post-root-package-install": [
|
||||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
"@php7.4 -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||||
],
|
],
|
||||||
"post-create-project-cmd": [
|
"post-create-project-cmd": [
|
||||||
"@php artisan key:generate --ansi"
|
"@php7.4 artisan key:generate --ansi"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1860
composer.lock
generated
1860
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -19,6 +19,8 @@ class CreateUsersTable extends Migration
|
||||||
$table->string('email')->unique()->nullable();
|
$table->string('email')->unique()->nullable();
|
||||||
$table->timestamp('email_verified_at')->nullable();
|
$table->timestamp('email_verified_at')->nullable();
|
||||||
$table->foreignId('grupo_de_compra_id')->nullable();
|
$table->foreignId('grupo_de_compra_id')->nullable();
|
||||||
|
$table->boolean('is_admin');
|
||||||
|
$table->unique(['name', 'is_admin']);
|
||||||
$table->string('password');
|
$table->string('password');
|
||||||
$table->rememberToken();
|
$table->rememberToken();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
|
@ -17,7 +17,7 @@ class CreateSubpedidosTable extends Migration
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('nombre');
|
$table->string('nombre');
|
||||||
$table->foreignId('grupo_de_compra_id');
|
$table->foreignId('grupo_de_compra_id');
|
||||||
$table->boolean('aprobado')->nullable();
|
$table->boolean('aprobado')->default(false);
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AgregarColumnaBonoATablaDeProductos extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('productos', function (Blueprint $table) {
|
||||||
|
$table->boolean('bono')->after('apto_celiacxs');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('productos', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('bono');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AgregarColumnaTotalATablaDeProductoSubpedido extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('producto_subpedido', function (Blueprint $table) {
|
||||||
|
$table->double('total',10,2)->after('cantidad')->default(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('producto_subpedido', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('total');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AgregarColumnaFilaEnPlanillaATablaDeProductos extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('productos', function (Blueprint $table) {
|
||||||
|
$table->integer('fila')->after('id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('productos', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('fila');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CallCrearPedidosAprobados extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Artisan::call("view:CrearPedidosAprobadosView");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class DevolucionesPedido extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('subpedidos', function (Blueprint $table) {
|
||||||
|
$table->double('devoluciones_total', 10, 2)->default(0);
|
||||||
|
$table->string('devoluciones_notas')->default("");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('subpedidos', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('devoluciones_total');
|
||||||
|
$table->dropColumn('devoluciones_notas');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class HabilitarDevoluciones extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('grupos_de_compra', function (Blueprint $table) {
|
||||||
|
$table->boolean('devoluciones_habilitadas')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('grupos_de_compra', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('devoluciones_habilitadas');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AgregarIsComprasAUser extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_compras')->after('is_admin')->default(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_compras');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
32
database/migrations/2024_09_17_234635_notas_producto.php
Normal file
32
database/migrations/2024_09_17_234635_notas_producto.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class NotasProducto extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('producto_subpedido', function (Blueprint $table) {
|
||||||
|
$table->string('notas')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('producto_subpedido', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('notas');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class ProductoRequiereNotas extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('productos', function (Blueprint $table) {
|
||||||
|
$table->boolean('requiere_notas')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('productos', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('requiere_notas');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CrearCargaDeCanastas extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('carga_de_canastas', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('path');
|
||||||
|
$table->string('descripcion');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('carga_de_canastas');
|
||||||
|
}
|
||||||
|
}
|
19
database/seeds/CanastaSeeder.php
Normal file
19
database/seeds/CanastaSeeder.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\CanastaHelper;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class CanastaSeeder extends Seeder
|
||||||
|
{
|
||||||
|
const ARCHIVO_DEFAULT = 'csv/productos.csv';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
CanastaHelper::cargarCanasta(self::ARCHIVO_DEFAULT);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ class DatabaseSeeder extends Seeder
|
||||||
*/
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
|
$this->call(CanastaSeeder::class);
|
||||||
$this->call(GrupoDeCompraSeeder::class);
|
$this->call(GrupoDeCompraSeeder::class);
|
||||||
$this->call(ProductoSeeder::class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,15 @@ class GrupoDeCompraSeeder extends Seeder
|
||||||
|
|
||||||
$usersToInsert[] = [
|
$usersToInsert[] = [
|
||||||
'name' => $registro['barrio'],
|
'name' => $registro['barrio'],
|
||||||
'password' => Hash::make($registro['barrio']),
|
'password' => Hash::make("asd"),
|
||||||
|
"is_admin" => 0,
|
||||||
|
'grupo_de_compra_id' => $key
|
||||||
|
];
|
||||||
|
|
||||||
|
$usersToInsert[] = [
|
||||||
|
'name' => $registro['barrio'] . "_admin",
|
||||||
|
'password' => Hash::make("asd"),
|
||||||
|
"is_admin" => 1,
|
||||||
'grupo_de_compra_id' => $key
|
'grupo_de_compra_id' => $key
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Database\Seeder;
|
|
||||||
use League\Csv\Reader;
|
|
||||||
use App\Proveedor;
|
|
||||||
|
|
||||||
class ProductoSeeder extends Seeder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the database seeds.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function run()
|
|
||||||
{
|
|
||||||
$csv = Reader::createFromPath(resource_path('csv/productos.csv'), 'r');
|
|
||||||
$csv->setDelimiter("|");
|
|
||||||
$csv->setEnclosure("'");
|
|
||||||
$csv->setHeaderOffset(0);
|
|
||||||
$registros = $csv->getRecords();
|
|
||||||
$toInsert = [];
|
|
||||||
|
|
||||||
foreach($registros as $registro){
|
|
||||||
$toInsert[] = [
|
|
||||||
'categoria' => $registro['categoria'],
|
|
||||||
'nombre' => $registro['producto'],
|
|
||||||
'precio' => $registro['precio'],
|
|
||||||
'proveedor_id' => isset($registro['proveedor']) ? Proveedor::firstOrCreate([
|
|
||||||
'nombre' => $registro['proveedor']
|
|
||||||
])->id : null
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (array_chunk($toInsert,DatabaseSeeder::CHUNK_SIZE) as $chunk)
|
|
||||||
{
|
|
||||||
DB::table('productos')->insert($chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
2
dev-start
Executable file
2
dev-start
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/usr/bin/bash
|
||||||
|
docker-compose up -d && docker-compose exec app npm run watch
|
|
@ -1,14 +1,14 @@
|
||||||
version: "3.7"
|
version: "3.3"
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
build:
|
build:
|
||||||
args:
|
args:
|
||||||
user: www
|
user: www
|
||||||
uid: 1000
|
uid: ${USERID}
|
||||||
context: ./
|
context: ./
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
image: laravel-image
|
image: laravel-image
|
||||||
container_name: laravel-app
|
container_name: pedi2-app
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
working_dir: /var/www/
|
working_dir: /var/www/
|
||||||
volumes:
|
volumes:
|
||||||
|
@ -19,10 +19,8 @@ services:
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: mysql:5.7
|
image: mysql:5.7
|
||||||
container_name: laravel-db
|
container_name: pedi2-db
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
|
||||||
- "3306:3306"
|
|
||||||
environment:
|
environment:
|
||||||
MYSQL_DATABASE: ${DB_DATABASE}
|
MYSQL_DATABASE: ${DB_DATABASE}
|
||||||
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
|
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
|
||||||
|
@ -36,13 +34,15 @@ services:
|
||||||
- dbdata:/var/lib/mysql
|
- dbdata:/var/lib/mysql
|
||||||
networks:
|
networks:
|
||||||
- app-network
|
- app-network
|
||||||
|
ports:
|
||||||
|
- ${DB_PORT_EXPOSED}:3306
|
||||||
|
|
||||||
nginx:
|
nginx:
|
||||||
image: nginx:alpine
|
image: nginx:alpine
|
||||||
container_name: laravel-nginx
|
container_name: pedi2-nginx
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 8000:80
|
- ${NGINX_PORT}:80
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/var/www
|
- ./:/var/www
|
||||||
- ./nginx/conf.d/:/etc/nginx/conf.d/
|
- ./nginx/conf.d/:/etc/nginx/conf.d/
|
||||||
|
|
25435
package-lock.json
generated
Normal file
25435
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
@ -10,9 +10,9 @@
|
||||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --config=node_modules/laravel-mix/setup/webpack.config.js"
|
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios": "^0.19",
|
"axios": "^0.19.2",
|
||||||
"bootstrap": "^4.0.0",
|
"bootstrap": "^4.0.0",
|
||||||
"cross-env": "^7.0",
|
"cross-env": "^7.0.3",
|
||||||
"jquery": "^3.2",
|
"jquery": "^3.2",
|
||||||
"laravel-mix": "^5.0.1",
|
"laravel-mix": "^5.0.1",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
|
@ -22,5 +22,11 @@
|
||||||
"sass-loader": "^8.0.0",
|
"sass-loader": "^8.0.0",
|
||||||
"vue": "^2.5.17",
|
"vue": "^2.5.17",
|
||||||
"vue-template-compiler": "^2.6.10"
|
"vue-template-compiler": "^2.6.10"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"animate.css": "^4.1.1",
|
||||||
|
"bulma": "^0.9.4",
|
||||||
|
"bulma-switch": "^2.0.4",
|
||||||
|
"bulma-toast": "^2.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
public/assets/chismosa.png
Normal file
BIN
public/assets/chismosa.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
BIN
public/assets/favicon.png
Normal file
BIN
public/assets/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
8
public/css/app.css
vendored
8
public/css/app.css
vendored
|
@ -1,8 +0,0 @@
|
||||||
p.navbar-item:empty {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.breadcrumb a {
|
|
||||||
color: #cc0f35;
|
|
||||||
}
|
|
||||||
|
|
4
public/css/productos.css
vendored
4
public/css/productos.css
vendored
|
@ -1,4 +0,0 @@
|
||||||
figure.image.icono {
|
|
||||||
float: right;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
73
public/js/app.js
vendored
73
public/js/app.js
vendored
|
@ -1,73 +0,0 @@
|
||||||
window.Event = new Vue();
|
|
||||||
|
|
||||||
Vue.component('nav-bar', {
|
|
||||||
template: `
|
|
||||||
<nav class="navbar is-danger" role="navigation" aria-label="main navigation">
|
|
||||||
<div class="navbar-brand">
|
|
||||||
<a class="navbar-item" href="https://mps.org.uy">
|
|
||||||
<img src="/assets/logoMPS.png" height="28">
|
|
||||||
</a>
|
|
||||||
<p style="margin:0 auto" class="navbar-item"><slot name="subpedido"></slot></p>
|
|
||||||
<a role="button" class="navbar-burger" :class="{'is-active':isActive}" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample" @click="toggleState">
|
|
||||||
<span aria-hidden="true"></span>
|
|
||||||
<span aria-hidden="true"></span>
|
|
||||||
<span aria-hidden="true"></span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="navbarBasicExample" class="navbar-menu" :class="{'is-active':isActive}">
|
|
||||||
<div class="navbar-start has-text-right-mobile">
|
|
||||||
<!-- Styles nombre del barrio-->
|
|
||||||
<p class="navbar-item"><slot name="gdc"></slot></p>
|
|
||||||
<a class="navbar-item"
|
|
||||||
onclick="event.preventDefault();
|
|
||||||
document.getElementById('logout-form').submit();">
|
|
||||||
Cerrar sesión
|
|
||||||
</a>
|
|
||||||
<slot name="logout-form"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isActive: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleState() {
|
|
||||||
this.isActive = !this.isActive;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Vue.component('nav-migas', {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
migas: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
visible: function() {
|
|
||||||
return this.migas.length > 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#app'
|
|
||||||
});
|
|
114
public/js/login.js
vendored
114
public/js/login.js
vendored
|
@ -1,114 +0,0 @@
|
||||||
window.Event = new Vue();
|
|
||||||
|
|
||||||
Vue.component('region-select', {
|
|
||||||
template: `
|
|
||||||
<div class="block">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">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 in regiones" v-text="region" :name="region"></option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
regiones: [],
|
|
||||||
isDefaultDisabled: 0,
|
|
||||||
region: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
axios.get("/api/regiones").then(response => this.regiones = response.data);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onRegionSelected() {
|
|
||||||
this.isDefaultDisabled = 1;
|
|
||||||
Event.$emit("region-seleccionada",this.region);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Vue.component('barrio-select', {
|
|
||||||
template: `
|
|
||||||
<div v-show="visible" class="block">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">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 in gdcs" v-text="gdc.nombre" :name="gdc.nombre"></option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
visible: false,
|
|
||||||
region: null,
|
|
||||||
gdcs: [],
|
|
||||||
isDefaultDisabled: 0,
|
|
||||||
gdc: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Vue.component('login', {
|
|
||||||
template: `
|
|
||||||
<div v-show="visible" class="block">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Contraseña del barrio</label>
|
|
||||||
<p class="control">
|
|
||||||
<input required class="input" type="password" name="password" placeholder="Contraseña del barrio">
|
|
||||||
</p>
|
|
||||||
<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">
|
|
||||||
</input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
visible: false,
|
|
||||||
gdc: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
Event.$on('gdc-seleccionado', (gdc) => {
|
|
||||||
this.gdc = gdc;
|
|
||||||
this.visible = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#root'
|
|
||||||
});
|
|
171
public/js/productos.js
vendored
171
public/js/productos.js
vendored
|
@ -1,171 +0,0 @@
|
||||||
Vue.component('categorias-container', {
|
|
||||||
template: `
|
|
||||||
<div v-show="visible" class="container">
|
|
||||||
<div class="columns is-multiline is-mobile">
|
|
||||||
<div v-for="catego in categorias" class="block column is-one-quarter-desktop is-one-third-tablet is-half-mobile">
|
|
||||||
<div @click.capture="seleccionarCategoria(catego)" 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="catego"></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div><!-- END CARD -->
|
|
||||||
</div><!-- END BLOCK COLUMN -->
|
|
||||||
</div><!-- END COLUMNS -->
|
|
||||||
</div><!-- END CONTAINER -->`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
categorias: null,
|
|
||||||
visible: true,
|
|
||||||
miga: {
|
|
||||||
nombre: "Categorías",
|
|
||||||
href: "/productos"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
axios.get("/api/categorias").then(response => {
|
|
||||||
this.categorias = response.data;
|
|
||||||
});
|
|
||||||
Event.$emit("migas-setear-como-inicio",this.miga);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
seleccionarCategoria(categoria) {
|
|
||||||
this.visible = false;
|
|
||||||
Event.$emit("categoria-seleccionada",categoria);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Vue.component('productos-container', {
|
|
||||||
template: `
|
|
||||||
<div v-show="visible" class="container">
|
|
||||||
<div class="columns is-multiline is-mobile">
|
|
||||||
<div v-for="producto in productos" class="block column is-one-quarter-desktop is-one-third-tablet is-half-mobile">
|
|
||||||
<div @click.capture="seleccionarProducto(producto)" class="card" style="height:100%">
|
|
||||||
<div class="card-image">
|
|
||||||
<figure class="image is-4by3">
|
|
||||||
<img v-bind:src="producto.imagen ? producto.imagen : 'https://bulma.io/images/placeholders/1280x960.png'">
|
|
||||||
</figure>
|
|
||||||
<figure v-show="producto.nacional" class="image icono is-32x32">
|
|
||||||
<img src="/assets/uruguay.png">
|
|
||||||
</figure>
|
|
||||||
<figure v-show="producto.economia_solidaria" class="image icono is-32x32">
|
|
||||||
<img src="/assets/solidaria.png">
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
<div class="card-content">
|
|
||||||
<div class="media">
|
|
||||||
<div class="media-content">
|
|
||||||
<p class="title is-6" v-text="producto.nombre"></p>
|
|
||||||
<p class="subtitle is-7" v-text="producto.proveedor"></p>
|
|
||||||
<p class="subtitle is-7">$<span v-text="producto.precio"></span></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div><!-- END CARD -->
|
|
||||||
</div><!-- END BLOCK COLUMN -->
|
|
||||||
</div><!-- END COLUMNS -->
|
|
||||||
</div><!-- END CONTAINER -->`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
productos: [],
|
|
||||||
visible: false,
|
|
||||||
categoria: null,
|
|
||||||
paginar: 150
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
miga: function(){
|
|
||||||
return {
|
|
||||||
nombre: this.categoria,
|
|
||||||
href: "#" + this.categoria
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
Event.$on('categoria-seleccionada', (categoria) => {
|
|
||||||
this.categoria = categoria;
|
|
||||||
|
|
||||||
axios.get("/api/productos", {
|
|
||||||
params: {
|
|
||||||
categoria: this.categoria,
|
|
||||||
paginar: this.paginar
|
|
||||||
}
|
|
||||||
}).then(response => {
|
|
||||||
this.productos = response.data.data;
|
|
||||||
});
|
|
||||||
this.visible = true;
|
|
||||||
Event.$emit("migas-agregar",this.miga);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
seleccionarProducto(producto) {
|
|
||||||
Event.$emit("producto-seleccionado",producto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Vue.component('producto-container', {
|
|
||||||
template: `
|
|
||||||
<div v-bind:class="visible ? 'is-active modal' : 'modal'">
|
|
||||||
<div class="modal-background"></div>
|
|
||||||
<div class="modal-card">
|
|
||||||
<header class="modal-card-head">
|
|
||||||
<p class="modal-card-title" v-text="producto.nombre"></p>
|
|
||||||
<button class="delete" aria-label="close" @click.capture="cerrar"></button>
|
|
||||||
</header>
|
|
||||||
<section class="modal-card-body">
|
|
||||||
<div class="card-image">
|
|
||||||
<figure class="image is-4by3">
|
|
||||||
<img v-bind:src="producto.imagen ? producto.imagen : 'https://bulma.io/images/placeholders/1280x960.png'">
|
|
||||||
</figure>
|
|
||||||
<figure v-show="producto.nacional" class="image icono is-32x32">
|
|
||||||
<img src="/assets/uruguay.png">
|
|
||||||
</figure>
|
|
||||||
<figure v-show="producto.economia_solidaria" class="image icono is-32x32">
|
|
||||||
<img src="/assets/solidaria.png">
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
<div class="media-content">
|
|
||||||
<p class="title is-4" v-text="producto.proveedor"></p>
|
|
||||||
<p class="subtitle is-4">$<span v-text="producto.precio"></span></p>
|
|
||||||
<p class="subtitle is-5"><span v-text="producto.descripcion"></span></p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<footer class="modal-card-foot">
|
|
||||||
<button class="button is-success">Agregar a la chismosa</button>
|
|
||||||
<button class="button" @click.capture="cerrar">Cancelar</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</div>`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
producto: null,
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
miga: function(){
|
|
||||||
return {
|
|
||||||
nombre: this.producto.nombre,
|
|
||||||
href: "#" + this.producto.nombre
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
cerrar() {
|
|
||||||
this.visible = false;
|
|
||||||
Event.$emit("migas-pop");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
Event.$on('producto-seleccionado', (producto) => {
|
|
||||||
this.producto = producto;
|
|
||||||
this.visible = true;
|
|
||||||
Event.$emit("migas-agregar",this.miga);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
33
public/js/subpedidos-create.js
vendored
33
public/js/subpedidos-create.js
vendored
|
@ -1,33 +0,0 @@
|
||||||
Vue.component('subpedido-select', {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
subpedido: null,
|
|
||||||
subpedidosExistentes: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: ["gdcid"],
|
|
||||||
mounted() {
|
|
||||||
console.log("ready");
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onType() {
|
|
||||||
axios.get("/api/subpedidos?nombre=" + this.subpedido).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, guardamos el subpedido en sesion
|
|
||||||
axios.post("/subpedidos/guardar_sesion", {
|
|
||||||
subpedido: response.data
|
|
||||||
}).then(response => {
|
|
||||||
window.location.href = 'productos';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,45 +1,4 @@
|
||||||
barrio|region|referente|telefono|correo
|
barrio|region|referente|telefono|correo
|
||||||
BUCEO|ESTE|||
|
ENTREVERO|SUR|||
|
||||||
MALVIN NORTE|ESTE|||
|
|
||||||
PINAR|ESTE|||
|
|
||||||
UNION|ESTE|||
|
|
||||||
SANTO DOMINGO|NORTE|||
|
|
||||||
21 DE FEBRERO|NORTE|||
|
|
||||||
PIEDRAS BLANCAS|NORTE|||
|
|
||||||
BELLA ITALIA|NORTE|||
|
|
||||||
JARDINES|NORTE|||
|
|
||||||
LA SOCIALISTA|NORTE|||
|
|
||||||
VILLA GARCIA|NORTE|||
|
|
||||||
LAS ACACIAS|NORTE|||
|
|
||||||
MANGA|NORTE|||
|
|
||||||
CERRITO|OESTE|||
|
|
||||||
LEZICA|OESTE|||
|
|
||||||
PEÑAROL|OESTE|||
|
|
||||||
LAS PIEDRAS|OESTE|||
|
|
||||||
A.C.T.E.O|OESTE|||
|
|
||||||
NUEVO PARIS|OESTE|||
|
|
||||||
SANTA CATALINA|OESTE|||
|
|
||||||
BELVEDERE|OESTE|||
|
|
||||||
BATLLE BERRES|OESTE|||
|
|
||||||
CERRO PTI|OESTE|||
|
|
||||||
COLECTIVO UTU LAVALLEJA|OESTE|||
|
|
||||||
FOGONES|OESTE|||
|
|
||||||
LAVALLEJA|OESTE|||
|
|
||||||
REJUNTE|OESTE|||
|
|
||||||
CAPURRO|OESTE|||
|
|
||||||
PARQUE RODO|SUR|||
|
|
||||||
REDUCTO|SUR|||
|
|
||||||
AGUADA|SUR|||
|
|
||||||
CASA DEL VECINO|SUR|||
|
|
||||||
CIUDAD VIEJA|SUR|||
|
|
||||||
COOP EJIDO|SUR|||
|
|
||||||
COVIREUS|SUR|||
|
|
||||||
LA BLANQUEADA|SUR|||
|
|
||||||
NITEP|SUR|||
|
|
||||||
LA CURVA|SUR|||
|
|
||||||
PANADERIA VIDAL|SUR|||
|
|
||||||
SUA|SUR|||
|
|
||||||
TRES CRUCES|SUR|||
|
TRES CRUCES|SUR|||
|
||||||
VILLA ESPAÑOLA|SUR|||
|
PRUEBA|SIN REGION|||
|
||||||
AUDA|OTRA|||
|
|
||||||
SINTEP|OTRA|||
|
|
|
|
@ -1,359 +1,381 @@
|
||||||
categoria|producto|precio|proveedor
|
Tipo|Producto|Precio
|
||||||
ALIMENTOS NO PERECEDEROS|Yerba Compuesta La Herboristería 1kg |140.3|
|
ALIMENTOS NO PERECEDEROS||
|
||||||
ALIMENTOS NO PERECEDEROS|Yerba Yusa tradicional 1kg |121.8|
|
P|Yerba Compuesta La Herboristería 1kg|157.63
|
||||||
ALIMENTOS NO PERECEDEROS|Yerba Yusa tradicional 500grs|65|
|
P|Yerba Yusa tradicional 1kg|148.00
|
||||||
ALIMENTOS NO PERECEDEROS|Yerba Sara tradicional 1kg|129.7|
|
P|Yerba Yusa tradicional 500grs|81.00
|
||||||
ALIMENTOS NO PERECEDEROS|Yerba Sara suave 1kg|132.2|
|
P|Yerba Sara tradicional 1kg (Sin TACC)|139.40
|
||||||
ALIMENTOS NO PERECEDEROS|Yerba Kiero mate 500grs|72.5|
|
P|Yerba Sara suave 1kg (Sin TACC)|139.40
|
||||||
ALIMENTOS NO PERECEDEROS|*Harina Santa Unión 000 1kg|27.3|Santa Unión
|
P|*Harina Santa Unión 000 1kg|33.11
|
||||||
ALIMENTOS NO PERECEDEROS|*Harina Santa Unión 0000 1kg|34.8|Santa Unión
|
P|*Harina Santa Unión 0000 1kg|38.70
|
||||||
ALIMENTOS NO PERECEDEROS|*Harina de trigo integral Pasaná 1 kg|65|Pasaná
|
P|*Harina de arroz Pasaná 1 kg (Puede contener gluten)|90.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Harina de arroz Pasaná 1 kg |65|Pasaná
|
P|*Harina de Garbanzos Pasaná 1 kg (Puede contener gluten)|140.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Harina de Garbanzos Pasaná 1 kg |140|Pasaná
|
P|*Mezcla para faina Pasaná 1 kg (Puede contener gluten)|160.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Mezcla para faina Pasaná 1 kg |150|Pasaná
|
P|*Fécula de Mandioca Pasaná 1 kg (Puede contener gluten)|140.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Fécula de Mandioca Pasaná 1 kg |85|Pasaná
|
P|Fécula de Mandioca Hornex 800g (Sin TACC)|125.00
|
||||||
ALIMENTOS NO PERECEDEROS|Tres Harinas 500grs. |85|
|
P|Tres Harinas Hornex 800grs (Sin TAAC)|129.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Harina Santa Unión 000 Bolsa 25kg|560.6|Santa Unión
|
P|*Harina Santa Unión 000 Bolsa 25kg|602.86
|
||||||
ALIMENTOS NO PERECEDEROS|*Polenta Santa Unión 450gr|15.4|Santa Unión
|
P|*Polenta Santa Unión 450gr|16.34
|
||||||
ALIMENTOS NO PERECEDEROS|*Mezcla de Fainá Santa Unión 5kg|294.8|Santa Unión
|
P|*Mezcla de Fainá Santa Unión 5kg|431.29
|
||||||
ALIMENTOS NO PERECEDEROS|*Fideos Caorsi Tirabuzón 1kg|59|Caorsi
|
P|*Harina de trigo intergal orgánica "La linda sauceña"|97.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Fideos Caorsi Tallarín 1kg|69|Caorsi
|
P|*Fideos Caorsi Tirabuzón 1kg|73.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Fideos Caorsi Moñita 1kg|69|Caorsi
|
P|*Fideos Caorsi Tallarín 1kg|81.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Fideos Caorsi para sopa 1kg|59|Caorsi
|
P|*Fideos Caorsi Moñita 1kg|81.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Fideos Caorsi para sopa 5kg|268|Caorsi
|
P|*Fideos Caorsi Tirabuzón 5kg|353.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Fideos Caorsi Tirabuzón 5kg|268|Caorsi
|
P|Arroz Blue Patna 1kg|50.02
|
||||||
ALIMENTOS NO PERECEDEROS|Arroz Blue Patna 1kg|40.2|
|
P|Arroz Shiva 1kg|35.65
|
||||||
ALIMENTOS NO PERECEDEROS|Arroz Shiva 1kg|27|
|
P|*Arroz integral 1kg|72.00
|
||||||
ALIMENTOS NO PERECEDEROS|Arroz integral 1kg |55|
|
P|*Arroz integral 3kg|204.00
|
||||||
ALIMENTOS NO PERECEDEROS|Arroz integral 3kg|155|
|
P|Aceite Condesa de Soja 900 cc.|59.80
|
||||||
ALIMENTOS NO PERECEDEROS|Aceite Condesa de Soja 900 cc.|84|
|
P|Aceite Uruguay de Girasol 900 cc.|81.70
|
||||||
ALIMENTOS NO PERECEDEROS|Aceite Uruguay de Girasol 900 cc.|108|
|
P|Aceite Optimo canola 900 cc.|75.00
|
||||||
ALIMENTOS NO PERECEDEROS|Aceite Optimo canola 900 cc.|82.4|
|
P|Vinagre Uruguay 900ml|61.60
|
||||||
ALIMENTOS NO PERECEDEROS|*Aceite de oliva 500 ml. |195|
|
P|*Salsa de soja La Posta 250 ml.|120.00
|
||||||
ALIMENTOS NO PERECEDEROS|Vinagre Uruguay 900ml|77.9|
|
P|*Aceitunas verdes sin carozo en frasco La Posta 500 gr.|220.00
|
||||||
ALIMENTOS NO PERECEDEROS|Aceite de Oliva Cuatro Piedras 3 lt|1090|
|
P|*Aceitunas negras sin carozo en frasco La Posta 500 gr.|240.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Salsa de soja La Posta 250 ml. |95|
|
P|Lata de atún Golden Fish desmenuzado al aceite 170g|33.35
|
||||||
ALIMENTOS NO PERECEDEROS|*Aceitunas verdes sin carozo en frasco La Posta 500 gr. |180|La Posta
|
P|Lata de arvejas Campero 300g|18.28
|
||||||
ALIMENTOS NO PERECEDEROS|*Aceitunas negras sin carozo en frasco frasco La Posta 500 gr. |180|La Posta
|
P|Lata de choclo Cosecha 300g|32.29
|
||||||
ALIMENTOS NO PERECEDEROS|Lata atún Golden Fish desmenuzado al aceite 170g|31.1|
|
P|Lata de jardinera Cosecha|30.34
|
||||||
ALIMENTOS NO PERECEDEROS|Lata de arvejas Campero 300g|18.3|
|
P|Lata de porotos negros Cosecha|37.06
|
||||||
ALIMENTOS NO PERECEDEROS|Lata de choclo Cosecha 300g|22|
|
P|Lata de porotos de frutilla Cosecha|37.06
|
||||||
ALIMENTOS NO PERECEDEROS|Lata de jardinera Cosecha|25|
|
P|Lata de duraznos en almíbar Campero|73.07
|
||||||
ALIMENTOS NO PERECEDEROS|Lata de porotos negros Cosecha|31.1|
|
P|Mayonesa Uruguay 500g|81.40
|
||||||
ALIMENTOS NO PERECEDEROS|Lata de porotos de frutilla Cosecha|31.1|
|
P|Azúcar Bella Unión 1kg|44.90
|
||||||
ALIMENTOS NO PERECEDEROS|Lata de duraznos en almíbar Campero |66.6|
|
P|Azúcar Mascabo 500g|102.00
|
||||||
ALIMENTOS NO PERECEDEROS|Mayonesa Uruguay 500g|77.9|
|
P|Azúcar impalpable Hornex 200 gr|39.00
|
||||||
ALIMENTOS NO PERECEDEROS|Azúcar Azucarlito 25kg|1007.5|
|
P|Almidón de maíz Hornex 1Kg|89.00
|
||||||
ALIMENTOS NO PERECEDEROS|Azúcar Bella Unión 1kg|39.8|
|
P|Almidón de maíz Ilu wayra 1Kg|75.00
|
||||||
ALIMENTOS NO PERECEDEROS|Azúcar impalpable Hornex 200 gr|31|
|
P|Polvo de Hornear Hornex 100 g + 20 g|38.00
|
||||||
ALIMENTOS NO PERECEDEROS|Azúcar impalpable Hornex 1kg|104|
|
P|*Esencia de vainilla La Posta 100ml|100.00
|
||||||
ALIMENTOS NO PERECEDEROS|Almidón de maíz Hornex 1Kg|64.5|
|
P|*Bicarbonato de sodio La Posta 250 gr|60.00
|
||||||
ALIMENTOS NO PERECEDEROS|Polvo de Hornear Hornex 1 kg|122|
|
P|Grasa Uruguay 400grs|45.80
|
||||||
ALIMENTOS NO PERECEDEROS|Polvo de Hornear Hornex 100 g + 20 g|27.9|
|
P|Levadura seca Hornex 125g|120.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Esencia de vainilla La Posta 100ml|80|
|
P|Café Sorocabana glaseado p/máquina 500 grs|396.00
|
||||||
ALIMENTOS NO PERECEDEROS|Grasa Uruguay 400grs|50|
|
P|Café Sorocabana natural p/máquina 500 grs|557.00
|
||||||
ALIMENTOS NO PERECEDEROS|Levadura seca Hornex 125g|85.5|
|
P|Café soluble Saint bollón 170 gr|232.50
|
||||||
ALIMENTOS NO PERECEDEROS|Café Sorocabana glaseado p/máquina 500 grs|232.8|
|
P|Té Negro en hebras 90gr hornimans|41.12
|
||||||
ALIMENTOS NO PERECEDEROS|Café Sorocabana natural p/máquina 500 grs |315.8|
|
P|Galletas de arroz SIN SAL Natural Rice 120 gr (Sin TACC)|39.00
|
||||||
ALIMENTOS NO PERECEDEROS|Café Saint 170 gr Instantaneo|185.3|
|
P|Galletas de arroz comunes Natural Rice 120 gr (Sin TACC)|39.00
|
||||||
ALIMENTOS NO PERECEDEROS|Té Negro en hebras 90gr hornimans|30.7|
|
P|Leche en polvo entera 250 g|105.00
|
||||||
ALIMENTOS NO PERECEDEROS|Galletas de arroz comunes Natural Rice 120 gr|34|
|
P|Leche en polvo entera 1kg|315.00
|
||||||
ALIMENTOS NO PERECEDEROS|* Leche en polvo 250grs|65|
|
P|* Coco rallado 200gr|72.00
|
||||||
ALIMENTOS NO PERECEDEROS|* Leche en polvo 1kg|220|
|
P|* Coco rallado 1kg|246.00
|
||||||
ALIMENTOS NO PERECEDEROS|* Coco rallado 200gr|70|
|
P|Cocoa Hornex 200gr|55.00
|
||||||
ALIMENTOS NO PERECEDEROS|* Coco rallado 1kg|275|
|
P|Postre de chocolate Hornex 8 porciones|44.00
|
||||||
ALIMENTOS NO PERECEDEROS|Cocoa Hornex 200gr|43|
|
P|Postre LIGHT de vainilla Hornex 8 porciones (aprobado por ADU)|68.00
|
||||||
ALIMENTOS NO PERECEDEROS|Postre de chocolate Hornex 8 porciones|36.5|
|
P|Flan de vainilla Hornex 8 porciones|44.00
|
||||||
ALIMENTOS NO PERECEDEROS|Prostre LIGHT de vainilla Hornex 8 porciones (aprobado por ADU)|57|
|
P|Gelatina de frutilla Hornex 8 porciones|44.00
|
||||||
ALIMENTOS NO PERECEDEROS|Flan de vainilla Hornex 8 porciones|36.5|
|
P|Bizcochuelo de vainilla SIN GLUTEN 500gr Hornex|180.00
|
||||||
ALIMENTOS NO PERECEDEROS|Gelatina de frutilla Hornex 8 porciones|36.5|
|
P|Pizza SIN GLUTEN 320gr Hornex|170.00
|
||||||
ALIMENTOS NO PERECEDEROS|Bizcochuelo de vainilla SIN GLUTEN 500gr Hornex|142|
|
P|Pulpa de Tomate De Ley 1lt (S-G)|50.43
|
||||||
ALIMENTOS NO PERECEDEROS|Pizza SIN GLUTEN 320gr Hornex|137|
|
P|Pure de papa instantaneo De Ley 125g|24.73
|
||||||
ALIMENTOS NO PERECEDEROS|Pulpa de Tomate De Ley 1lt|43.5|
|
P|*Sal fina sin fluor Polenteados 500g|36.00
|
||||||
ALIMENTOS NO PERECEDEROS|Pure de papa instantaneo De Ley 125g|21.3|
|
P|*Sal gruesa sin fluor Polenteados 500g|36.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Sal fina sin fluor Polenteados 500g|27|
|
P|*Sal Rosa 250gr |80.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Sal gruesa sin fluor Polenteados 500g|27|
|
P|*Salsa de tomate casera (puro tomate) 1 lt - (S-G) - azucar agregada: 1g/L - sal agregada: 0,25g/L|85.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Salsa de tomate casera (puro tomate) 1 lt|65|
|
P|*Barras de cereales (maní, sésamo, lino, girasol, avena, copos de arroz, copos de máiz, azúcar rubia y miel) - Pack x2|80.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Barras de cereales (maní, sésamo, lino, girasol, avena, copos de arroz, copos de máiz, azúcar rubia y miel) - Pack x2 |65|
|
P|*Barras de cereales bañadas en chocolate (maní, sésamo, lino, girasol, avena, copos de arroz, copos de máiz, azúcar rubia y miel) - Pack x2|90.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Barras de cereales bañadas en chocolate (maní, sésamo, lino, girasol, avena, copos de arroz, copos de máiz, azúcar rubia y miel) - Pack x2|75|
|
P|*Granola artesanal 500 gr Ing: copos ,avena,miel, vainilla,coco rallado, chia,girasol,sésamo, lino,maní, almendras, castañas de caju, nueces, chips de chocolate.|250.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Granola artesanal 500 gr Ing: copos ,avena,miel, vainilla,coco rallado, chia,girasol,sésamo, lino,maní, almendras, castañas de caju, nueces, chips de chocolate.|210|
|
P|*Granola simple (avena+ girasol+ pasaUva) 1kg|171.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Granola simple (avena+ girasol+ pasaUva) 1kg|160|
|
P|*Copos de maíz azucarados 500g|112.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Copos de maíz azucarados 500g|89|
|
P|*Copos de maíz naturales 500g|112.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Copos de maíz naturales 500g|89|
|
P|*Crema untable de maní 330 gr, envase de vidrio |225.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Crema untable de maní 250gr|89|
|
P|*Tableta artesanal chocolate semiamargo con frutos secos 100g |130.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Galletas de coco, avena y maní 400gr|100|
|
P|*Tableta artesanal chocolate semiamargo con pasas 100g |130.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Budin Panitep con cobertura de chocolate con almendras, maní, nueces y pasas 250grs|160|
|
P|*Tableta artesanal chocolate blanco con frutos secos 100g |130.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Budin Panitep con cobertura de almíbar y coco con almendras, maní, nueces y pasas 250grs|140|
|
P|*Tableta artesanal chocolate blanco con pasas 100g |130.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Budin Panitep de naranja 250grs|140|
|
P|Alfajor de chocolate negro 80 g (S-G) |48.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Chia 1/4 kg |65|
|
P|Galletitas dulces de chispas de chocolate y avena 150 g (S-G)|116.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Girasol 1/2 kg |85|
|
P|Crackers saladas de sésamo girasol y chía 180 g (S-G)|116.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Lino 1/4 kg |30|
|
P|*Lino 1/4 kg|42.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Sésamo 1/4 kg |55|
|
P|*Chía 1/4 kg|80.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Quinoa 1 kg |200|
|
P|*Girasol 1/2 kg|140.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Dátiles con carozo 500 gr|180|
|
P|*Sésamo 1/4 kg|65.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Cacao en polvo 250 grs |100|
|
P|*Quinoa 1 kg|210.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Almendra pelada (sin tostar) Polenteados 100g|80|Polenteados
|
P|*Dátiles con carozo 500 gr|240.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Pasas de Uva Polenteados 250 gr|69|Polenteados
|
P|*Cacao en polvo 250 grs (S-A)|125.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Nueces Polenteados 100g|79|Polenteados
|
P|*Almendra pelada (sin tostar) Polenteados 100g|93.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Castañas tostadas SIN sal 100grs|76|Polenteados
|
P|*Pasas de Uva Polenteados 500 gr|149.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Avena laminada instantánea Polenteados 500g|49|Polenteados
|
P|*Nueces Polenteados 100g|89.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Pan de molde Gigor Lacteado 550g|55|Gigor
|
P|*Castañas tostadas SIN sal 100grs|81.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Pan de molde Gigor Integral 550g|55|Gigor
|
P|*Avena laminada instantánea Polenteados 500g|69.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Galleta Malteada La Socialista 350g|56|La Socialista
|
P|*Pan de molde Gory Lacteado 550g|64.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Galleta Malteada c/semillas (sésamo, chia, lino) La Socialista 380gr |74|La Socialista
|
P|*Pan de molde Integral Gory 550g|64.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Galleta Cara Sucia La Socialista 350g|63|La Socialista
|
P|*Galleta Malteada La Socialista 350g|84.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Grisines La Socialista 350g|67|La Socialista
|
P|*Galleta Malteada c/semillas (sésamo, chia, lino) La Socialista 380gr|103.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Mezcla para panqueques La Socialista 2 x 250g|56|La Socialista
|
P|*Galleta Cara Sucia La Socialista 350g|92.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Mezcla para salsa blanca La Socialista 2 x 50g|46|La Socialista
|
P|*Grisines La Socialista 350g|96.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Maní pelado frito y salado Polenteados 500g|89|Polenteados
|
P|*Maní pelado frito y salado Polenteados 500g|103.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Maní pelado sin sal Polenteados 500g|89|Polenteados
|
P|*Maní pelado sin sal Polenteados 500g|103.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Garbanzo Polenteados 1kg|79|Polenteados
|
P|*Garbanzo Polenteados 1kg|109.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Lentejas Polenteados 1kg|96|Polenteados
|
P|* Maiz para pop 500grs|54.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Porotos negros Polenteados 1kg|86|Polenteados
|
P|*Lentejas Polenteados 1kg|125.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Porotos de manteca Polenteados 1kg |105|Polenteados
|
P|*Porotos negros Polenteados 1kg|125.00
|
||||||
ALIMENTOS NO PERECEDEROS|*Proteína de SOJA texturizada 1kg |125|
|
P|*Porotos de manteca Polenteados 1kg|149.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Tallarines frescos de yema Pastas Colon 1kg|155|Pastas Colón
|
P|*Porotos mung 1kg |150.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Tallarines frescos de espinaca Pastas Colon 1kg|165|Pastas Colón
|
P|*Arvejas 1kg |120.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Tallarines frescos de morrón Pastas Colon 1kg|165|Pastas Colón
|
P|*Proteína de SOJA texturizada 1kg|139.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Salsa pomarola 300gr ex trabajadores de La Spezia|90|
|
P|*Semillas Zapallo 100g.|60.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Fetuccine integral de zanahoria, apto veganos 1kg|190|
|
CONDIMENTOS, PERECEDEROS Y BEBIDAS||
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Romanitos rellenos jamón y queso ex trabajadores de La Spezia 1kg|490|
|
P|*Tallarines frescos de yema Pastas Colon 1kg|175.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Romanitos vegetarianos ex trabajadores de La Spezia 1kg|490|
|
P|*Tallarines frescos de espinaca Pastas Colon 1kg|185.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Sorrentinos jamón y queso 1Kg ex trabajadores de La Spezia|450|
|
P|*Tallarines frescos de morrón Pastas Colon 1kg|190.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Sorrentinos Ricota y Nuez 1kg ex Trabajadores de La Spezia|450|
|
P|*Prepizza 28cm ex trabajadores de La Spezia|90.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Raviolón vegetariano 1Kg ex trabajadores de La Spezia|450|
|
P|*Milanesas de carne 1kg|395.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Raviolón Caprese 1kg ex Trabajadores de La Spezia|450|
|
P|*Milanesas de pollo 1kg|370.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Prepizza de 28cm ex trabajadores de La Spezia|85|
|
P|*Empanada de pollo x 6|265.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Milanesas de carne empanadas 1kg|365|
|
P|*Empanada de carne x 6|265.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Milanesas de pollo empanadas 1kg|365|
|
P|*Pan rallado 1kg|75.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Milanesas de seitan x6 |300|
|
P|*Pan rallado saborizado 1Kg|95.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Hamburguesas parrilleras de soja no transgénica, sal, harina de avena y adobo sin picante x6|300|
|
P|*Pimienta blanca 30g|20.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Jamón vegano (gluten de trigo, salsa de tomate, sabor ahumado, sal) horma 250 g|200|
|
P|*Orégano 25g|20.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Hummus 200cc |140|
|
P|*Pimentón 30g|20.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Pate de zanahoria 200cc |140|
|
P|*Adobo 30g|20.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Pan rallado 1kg|55|
|
P|*Ajo y Perejil 30g|20.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Pan rallado saborizado 1Kg|75|
|
P|*Clavo de olor 15g|20.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Pimienta blanca 30g|20|
|
P|*Tomillo 25g|20.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Orégano 25g|20|
|
P|*PACK "A" Curry / Nuez moscada / Ajo en polvo / Condimento verde / Comino|100.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Pimentón 30g|20|
|
P|*PACK "B" Pimienta Negra polvo / Sal de ajo / Aji molido / Canela en polvo / Condimento para arroz|100.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Adobo 30g|20|
|
P|*PACK "C" Cebolla en polvo / Pimienta blanca en grano / Pimienta negra en grano / Ajo en escamas / Especias surtidas|100.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Ajo y Perejil 30g|20|
|
P|*Pimentón 250grs|110.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*PACK "A" Curry, nuez moscada, ajo polvo, condimento verde, comino|80|
|
P|*Orégano 250g|115.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*PACK "B" Pimienta Negra polvo / sal de ajo, aji molido, canela polvo y chimichurry|80|
|
P|*Nuez moscada entera 2 unidades|28.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*PACK "C" Cebolla en escamas, tomillo, clavo de olor, pimienta blanca en grano, ajo en escamas|80|
|
P|*Canela en rama 10g|25.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Pimentón 250grs |75|
|
P|*Cúrcuma 20g|25.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Orégano 250g|85|
|
P|*Pack "Mix sabores"- Paprika, Albahaca, Mostaza en polvo.|65.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Nuez moscada entera 2 unidades |21|
|
P|*Pack "Pal mate"- Boldo, Cedron, Marcela.|120.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Canela en rama 10g|21|
|
P|*Pack "Relax"- Tilo, Malva, Té rojo.|120.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Cúrcuma 20g |21|
|
P|*Pack "Power" - Ginseng, Carqueja, Ginkgo biloba.|120.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Pack "Sabores exoticos" - Paprika, fenogreco y cardamomo|57|
|
P|Atado de perejil|50.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|'''''''*Pack "Pa''''''''l mate" - Manzanilla, cedrón y carqueja'''''''|105|
|
P|Mix de hierbas (ciboullete, tomillo, laurel y pasto lim[on)|50.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|Pack "Medicinas de monte" - Tilo, malva y marcela|105|
|
P|Atado de romero|50.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Pack "Té helado light" - Té rojo, stevia y lemon grass|105|
|
P|Fernet 760 ml|405.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|Vino Santero Marselán 1 lt.|198|
|
P|Vino Santero Marselán 1 lt.|235.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|Vino Tannat-Cabernet Paso del Roble 1 lt.|90|
|
P|Frizzante de Maracuyá|246.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|Vino Rosado dulce Paso del Roble 1 lt.|90|
|
P|Frizzante de Frutos del bosque|246.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|Frizzante de Maracuyá|205|
|
P|*Cerveza artesanal APA Press 1L|170.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|Frizzante de Frutos del bosque|205|
|
P|* Cerveza artesanal IPA Press 1L |180.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Cerveza artesanal Sudaka Blonde 500cc|100|
|
P|*Cerveza artesanal Negra Press 1L|180.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Cerveza artesanal Sudaka Scottish 500cc|100|
|
P|* Cerveza artesanal Rubia Lager 1L |160.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Cerveza artesanal Punto Rojo Red Ipa 500cc|100|
|
P|Jugo en polvo "Juguito" sabores surtidos|8.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Cerveza artesanal Punto Rojo Negra 500cc|100|
|
P|Jugo Big C 200ml sabores surtidos|16.84
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Cerveza artesanal APA Guillotina 1L|180|
|
P|*Refresco U Naranja 2lt|94.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|Fernet artesanal 780 ml|370|
|
P|*Refresco U Mandarina 2lt|94.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|Jugo en polvo "Juguito" sabores surtidos|8.06|
|
P|*Refresco U Pomelo 2lt|94.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|Jugo Big C 200ml sabores surtidos|13.14|
|
P|*Refresco U Limonada 2lt|94.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Refresco U Naranja 2lt|82|
|
P|*Agua 6 lts|98.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Refresco U Mandarina 2lt|82|
|
P|*Queso Muzzarella 1/2 kg Unidad Cooperaria|172.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Refresco U Pomelo 2lt|82|
|
P|*Queso Magro s/sal 1/2 kg Unidad Cooperaria|191.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Refresco U Limonada 2lt|82|
|
P|*Queso Magro c/sal 1/2 kg Unidad Cooperaria|191.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Agua 6 lts|84|
|
P|*Queso Danbo 1/2 kg Unidad Cooperaria|188.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso Muzzarella 1/2 kg Unidad Cooperaria|130|
|
P|*Queso Sbrinz 1/2 kg Unidad Cooperaria|287.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso Magro s/sal 1/2 kg Unidad Cooperaria|147.5|
|
P|*Queso Colonia 1/2 Kg Unidad Cooperaria|197.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso Magro c/sal 1/2 kg Uniddad Cooperaria|147.5|
|
P|*Queso parrillero 350g Unidad Cooperaria|177.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso Danbo 1/2 kg Unidad Cooperaria|137.5|
|
P|*Queso semiduro 500grs (envasado al vacío) Productores Ismael Cortinas|258.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso Sbrinz 1/2 kg Unidad Cooperaria|170|
|
P|*Queso cuartirolo horma 1kg envasado al vacío|375.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso Colonia 1/2 Kg Unidad Cooperaria|160|
|
P|*Queso rallado 200grs|155.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso parrillero 350g Unidad Cooperaria|165|
|
P|*Dulce de Leche 1 Kg Unidad Cooperaria|269.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso de mandioca en horma 400 gr|200|
|
P|*Morrones en vinagre 330 gr|230.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso saborizado con ciboulette 600grs (envasado al vacío)|230|
|
P|*Berenjenas en vinagre 330 gr|230.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso saborizado con albahaca 600grs (envasado al vacío)|230|
|
P|*Mermelada de morrones 250 gr|230.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso saborizado con orégano 500grs (envasado al vacío)|250|
|
P|*Mermelada de frutilla, 450 grs.|145.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso semiduro 500grs (envasado al vacío) Productores Ismael Cortinas|195|
|
P|*Mermelada de durazno, 450 grs.|133.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso cuartirolo horma 1kg envasado al vacío|270|
|
P|*Mermelada de ciruela, 450 grs.|133.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso rallado 100grs|63|
|
P|*Mermelada de higo, 450 grs.|133.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Queso rallado 200grs|120|
|
P|*Mermelada de zapallo, 450 grs.|133.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Dulce de Leche 1 Kg Unidad Cooperaria|165|
|
P|*Mermelada de naranja, 450 grs.|134.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Dulce de leche de coco 360 gr|300|
|
P|*Mermelada de arándanos, 450 grs.|159.50
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Morrones en vinagre 330 gr|140|
|
P|*Dulce de membrillo, 900grs|123.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Berenjenas en vinagre 330 gr|140|
|
P|*Dulce de batata con chocolate 1kg|154.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Mermelada de morrones 250 gr|140|
|
P|*Dulce de zapallo 1kg|139.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Mermelada de frutilla, 450 grs. |123|
|
P|*Dulce de higo 1kg|134.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Mermelada de durazno, 450 grs.|110|
|
P|*Miel artesanal 500g|130.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Mermelada de ciruela, 450 grs. |115|
|
P|*Miel artesanal 1 kg|240.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Mermelada de higo, 450 grs. |110|
|
P|*Canasta de frutas y verduras "34 Sur Productos Orgánicos"|630.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Mermelada de zapallo, 450 grs.|110|
|
ARTÍCULOS PERSONALES Y DE LIMPIEZA||
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Mermelada de naranja, 450 grs. |115|
|
P|Preservativos Prime ultrafinos x3|89.70
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Mermelada de arándanos, 450 grs. |145|
|
P|Preservativos Kamasutra x3|34.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Dulce de membrillo, 900grs |105|
|
P|Tabaco Cerrito|125.67
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Dulce de batata con chocolate 1kg|115|
|
P|Hojilla JOB x30|28.36
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Dulce de zapallo 1kg|115|
|
P|Shampoo Suave 930ml|123.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|'''''''*Dulce de higo 1kg'''|115|
|
P|Acondicionador Suave 930ml|123.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|Miel artesanal 1 kg|210|
|
P|Jabón de tocador IO, 80gs|16.60
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Canasta de frutas y verduras "34 Sur Productos Orgánicos"|630|
|
P|Cepillo dental kolynos máster. |28.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Romero 25grs, El Ombú PTIc|45|
|
P|Pasta Dental kolynos 180 grs. |49.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Apio 25grs, El Ombú PTIc|45|
|
P|Pastillas para mosquitos Fuyi x 12|98.30
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Menta 25grs, El Ombú PTIc|45|
|
P|*Pack x3 jabones glicerina vegetal Natura|380.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Melissa 25grs, El Ombú Ptic|45|
|
P|*Shampoo artesanal pelo seco 250ml Natura|250.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Albahaca limón, El Ombú Ptic|45|
|
P|*Desodorante ecológico apto veganos Natura|190.00
|
||||||
CONDIMENTOS, PERECEDEROS Y BEBIDAS|*Llanten 25grs, El Ombú PTIc|45|
|
P|*Shampoo artesanal pelo graso 250ml Natura|250.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Preservativos Prudence clasico x3|59.09|
|
P|*Barrita quita manchas 75 gr Natura|100.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Tabaco Cerrito|99|
|
P|*Aromatizador ambiental fragancia floral de varilla Natura, 100ml |345.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Hojilla JOB x30 |22|
|
P|*Aromatizador ambiental fragancia cítrica de varilla Natura, 100ml |345.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Shampoo Ainé Cuidado Total 500cc|230|
|
P|*Talco pédico Natura, 200gr|235.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Shampoo Ainé Brillo Extremo 500cc|230|
|
P|*Repelente Natura, 125ml|245.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Acondicionador Ainé Cuidado Total 500cc |230|
|
P|*Shampoo pediculosis, envase 250 ml|320.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Acondicionador Ainé Brillo Extremo 500cc |230|
|
P|*Jabón en polvo Bonsai 800g|95.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Acondicionador Ainé Dos Minutos 500cc |230|
|
P|*Jabón en polvo Bonsai 5kg|550.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Shampoo Suave 930ml |144.09|
|
P|*Suavizante Bonsai 1lt|80.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Acondicionador Suave 930ml |144.09|
|
P|*Jabon liquido para lavarropas 900 cc Bonsai|85.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Jabón de tocador IO, 80gs |12|
|
P|*Jabon liquido para lavarropas 3 lts Bonsai|270.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Cepillo dental Introdento (medio)|29.74|
|
P|*Jabon liquido de Manos 500 cc Bonsai|60.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Pasta Dental Introdento menta 102 grs. |36.72|
|
P|*Perfumador de telas 250 cc Bonsai|120.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Loción repelente de mosquitos, 200 ml|210|
|
P|*Limpiador desengarsante para cocinas y baños aroma cítrico 1 litro (no es para cañerías)|120.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Pack x3 jabones glicerina vegetal Natura|330|
|
P|*Limpiador cremoso 650 cc *|120.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Desodorante ecológico apto veganos Natura|160|
|
P|*Entrebichitos - MEN 2lts. (graseras, pozos, cañerías, plantas)|180.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Pasta Dental Libre de Flúor Natura|165|
|
P|*Entrebichitos - MEN Limpieza 1lt (suelos, mesadas, paredes, combate hongos, bacterias y virus)|100.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Shampoo artesanal pelo graso 250ml Natura|235|
|
P|*Entrebichitos - Pastilla grasera|80.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Shampoo artesanal pelo seco 250ml Natura|230|
|
P|*Hipoclorito El Resistente 1800cc|76.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Shampoo sólido cabello seco y normal 50gr |340|
|
P|*Limpiador perfumado El Resistente (perfumol) 1800cc|76.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Protector solar factor 65, 200 ml. NUEVO!!|550|
|
P|*Detergente El Resistente 500ml|51.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Shampoo sólido cabello graso 50gr |355|
|
P|*KIT El Resistente (Hip./Perf./Det.)|188.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Acondicionador sólido 50gr |350|
|
P|Jabon en barra Primor x1|29.36
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Cepillo dental de bambú 97% biodegradable (niños y adultos)|165|
|
P|Rejilla de cocina 40 x 27.5 Tacuabé (ex Paylana) cm|36.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Talco pédico 200gr |205|
|
P|Trapo de piso 53 x 53 Tacuabé (ex Paylana)|36.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Bálsamo labial Herencias de aquelarre (protege y repara) |180|
|
P|Esponja de cocina|26.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Detergente ecológico grupo Flores Silvestres 500 grs|90|
|
P|Esponja de acero inoxidable|29.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Jabón en polvo Bonsai 800g|70|Bonsai
|
P|Repasador de algodón 43 x 65 cm|53.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Jabón en polvo Bonsai 5kg|350|Bonsai
|
P|Franela 34 x 34|37.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Suavizante Bonsai 1lt|70|Bonsai
|
P|Escoba|116.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Jabon liquido para lavarropas 900 cc Bonsai|80|Bonsai
|
P|Pala con mango|99.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Jabon liquido para lavarropas 3 lts Bonsai|220|Bonsai
|
P|Balde 9 Lts|109.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Jabon liquido de Manos 500 cc Bonsai|50|Bonsai
|
P|Bolsa de residuos 50x55 30 unidades|63.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Perfumador de telas 250 cc Bonsai|100|Bonsai
|
P|Lampazo|119.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Limpiador desengarsante para cocinas y baños aroma cítrico 1 litro (no es para cañerías)|90|Bonsai
|
P|Rollitos de aluminio Griselda x12|38.30
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Entrebichitos - MEN 2lts. (graseras, pozos, cañerías, plantas)|180|Entrebichitos
|
P|rollitos de aluminio jaspe x6|30.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Entrebichitos - MEN Limpieza 1lt (suelos, mesadas, paredes, combate hongos, bacterias y virus)|100|Entrebichitos
|
P|*Vela de apagón|8.40
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Entrebichitos - Pastilla grasera|80|Entrebichitos
|
P|*Vela de citronela 1 mecha|132.80
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Hipoclorito El Resistente 1800cc|66|El Resistente
|
P|Toallita de bebé BabySec ultra 50un|83.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Limpiador perfumado El Resistente (perfumol) 1800cc|66|El Resistente
|
P|Papel Higienico: Higienol Texturado x4|42.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Detergente El Resistente 500ml|43|El Resistente
|
P|Papel de Cocina Sussex extra x 2 -120 paños-|72.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*KIT El Resistente (Hip./Perf./Det.)|161|El Resistente
|
P|Pañales Babysec ULTRA (Celeste) XXG 24 unidades |448.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Jabon bulldog packx2|66.24|
|
P|Pañales Babysec ULTRA (Celeste) XG 24 unidades|448.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Rejilla de cocina 40 x 27.5 Tacuabé (ex Paylana) cm|22|
|
P|Pañales Babysec ULTRA (Celeste) G 30 unidades|448.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Trapo de piso 55 x 55 Tacuabé (ex Paylana)|35|
|
P|Pañales Babysec ULTRA (Celeste) M 36 unidades|448.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Esponja de cocina|22|
|
P|Pañales Babysec ULTRA (Celeste) P 36 unidades|448.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Esponja de acero inoxidable|23|
|
P|Pañales Babysec PREMIUM (Violeta) XXG 48 unidades|790.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Repasador de algodón 43 x 65 cm|39|
|
P|Pañales Babysec PREMIUM (Violeta) XG 48 unidades|790.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Franela 34 x 34|30|
|
P|Pañales Babysec PREMIUM (Violeta) G 60 unidades|790.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Escoba|97|
|
P|Pañales Babysec PREMIUM (Violeta) M 68 unidades|790.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Pala con mango|89|
|
P|Pañales para Adultes INCOPROTECT TALLE M|579.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Balde 9 Lts |99|
|
P|Pañales para Adultes INCOPROTECT TALLE G |617.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Bolsa de residuos 50x55 30 unidades |49|
|
P|Pañales para Adultes INCOPROTECT TALLE EXTRA G |743.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Lampazo |99|
|
TEXTIL||
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Guantes de latex talle M|80.1|
|
PTC|*Calza licra de algodon talle S|900.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Rollitos de alumnio x6 |27.3|
|
PTC|*Calza licra de algodon talle M|900.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Vela de apagón|8.37|
|
PTC|*Calza licra de algodon talle L|900.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|*Vela de citronela 1 mecha|122.2|
|
PTC|*Calza licra de algodon talle XL|900.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Pastillas para mosquitos Sapolio 12 unidades |52.61|
|
PTC|*Biker licra de algodón - Talle S |650.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Pañales Babysec ULTRA XXG 24 unidades|312|
|
PTC|*Biker licra de algodón - Talle M |650.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Pañales Babysec ULTRA XG 24 unidades|312|
|
PTC|*Biker licra de algodón - Talle L |650.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Pañales Babysec ULTRA G 30 unidades|312|
|
PTC|*Biker licra de algodón - Talle XL |650.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Pañales Babysec ULTRA M 36 unidades|312|
|
PTC|*Biker licra de algodón - Talle 0 |350.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Pañales Babysec ULTRA P 36 unidades|312|
|
PTC|*Biker licra de algodón - Talle 2 |350.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Toallita de bebé BabySec ultra 50un|67|
|
PTC|*Biker licra de algodón - Talle 4 |350.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Papel Higienico: Higienol Texturado x4|47|
|
PTC|*Biker licra de algodón - Talle 6 |400.00
|
||||||
ARTÍCULOS PERSONALES Y DE LIMPIEZA|Papel de Cocina Sussex extra x 2 -120 paños-|57|
|
PTC|*Biker licra de algodón - Talle 8 |400.00
|
||||||
TEXTIL|*Calza licra de algodon talle S|750|
|
PTC|*Biker licra de algodón - Talle 10 |400.00
|
||||||
TEXTIL|*Calza licra de algodon talle M|750|
|
PTC|*Biker licra de algodón - Talle 12 |500.00
|
||||||
TEXTIL|*Calza licra de algodon talle L|750|
|
PTC|*Biker licra de algodón - Talle 14 |500.00
|
||||||
TEXTIL|*Calza licra de algodon talle XL|750|
|
PTC|*Biker licra de algodón - Talle 16 |500.00
|
||||||
TEXTIL|*Calza licra de algodon talle 0|300|
|
P|*Conjunto primera muda 100% algodón (pack de pelele, bata y gorrito en bolsa de lienzo) color a elección sujeto a disponibilidad de tela|600.00
|
||||||
TEXTIL|*Calza licra de algodon talle 2 |300|
|
PTC|*Calza licra de algodon talle 0|350.00
|
||||||
TEXTIL|*Calza licra de algodon talle 4|300|
|
PTC|*Calza licra de algodon talle 2|350.00
|
||||||
TEXTIL|*Calza licra de algodon talle 6 |300|
|
PTC|*Calza licra de algodon talle 4|350.00
|
||||||
TEXTIL|*Calza licra de algodon talle 8 |300|
|
PTC|*Calza licra de algodon talle 6|450.00
|
||||||
TEXTIL|*Calza licra de algodon talle 10|300|
|
PTC|*Calza licra de algodon talle 8|450.00
|
||||||
TEXTIL|*Calza licra de algodon talle 12|300|
|
PTC|*Calza licra de algodon talle 10|450.00
|
||||||
TEXTIL|*Calza licra de algodon talle 14 |350|
|
PTC|*Calza licra de algodon talle 12|550.00
|
||||||
TEXTIL|*Calza licra de algodon talle 16 |350|
|
PTC|*Calza licra de algodon talle 14|550.00
|
||||||
TEXTIL|*Campera deportiva - Talle 0|350|
|
PTC|*Calza licra de algodon talle 16|550.00
|
||||||
TEXTIL|*Campera deportiva - Talle 2|350|
|
PTC|*Campera deportiva - Talle 0|400.00
|
||||||
TEXTIL|*Campera deportiva - Talle 4|350|
|
PTC|*Campera deportiva - Talle 2|400.00
|
||||||
TEXTIL|*Campera deportiva - Talle 6|400|
|
PTC|*Campera deportiva - Talle 4|400.00
|
||||||
TEXTIL|*Campera deportiva - Talle 8|400|
|
PTC|*Campera deportiva - Talle 6|450.00
|
||||||
TEXTIL|*Campera deportiva - Talle 10|400|
|
PTC|*Campera deportiva - Talle 8|450.00
|
||||||
TEXTIL|*Campera deportiva - Talle 12|450|
|
PTC|*Campera deportiva - Talle 10|450.00
|
||||||
TEXTIL|*Campera deportiva - Talle 14|450|
|
PTC|*Campera deportiva - Talle 12|500.00
|
||||||
TEXTIL|*Campera deportiva - Talle 16|450|
|
PTC|*Campera deportiva - Talle 14|500.00
|
||||||
TEXTIL|*Pantalón deportivo - Talle 0|250|
|
PTC|*Campera deportiva - Talle 16|500.00
|
||||||
TEXTIL|*Pantalón deportivo - Talle 2|250|
|
PTC|*Pantalón deportivo - Talle 0|300.00
|
||||||
TEXTIL|*Pantalón deportivo - Talle 4|250|
|
PTC|*Pantalón deportivo - Talle 2|300.00
|
||||||
TEXTIL|*Pantalón deportivo - Talle 6|300|
|
PTC|*Pantalón deportivo - Talle 4|300.00
|
||||||
TEXTIL|*Pantalón deportivo - Talle 8|300|
|
PTC|*Pantalón deportivo - Talle 6|350.00
|
||||||
TEXTIL|*Pantalón deportivo - Talle 10|300|
|
PTC|*Pantalón deportivo - Talle 8|350.00
|
||||||
TEXTIL|*Pantalón deportivo - Talle 12|350|
|
PTC|*Pantalón deportivo - Talle 10|350.00
|
||||||
TEXTIL|*Pantalón deportivo - Talle 14|350|
|
PTC|*Pantalón deportivo - Talle 12|400.00
|
||||||
TEXTIL|*Pantalón deportivo - Talle 16|350|
|
PTC|*Pantalón deportivo - Talle 14|400.00
|
||||||
TEXTIL|*Babucha deportiva en algodón - Talle 4|500|
|
PTC|*Pantalón deportivo - Talle 16|400.00
|
||||||
TEXTIL|*Babucha deportiva en algodón - Talle 6|500|
|
PTC|*Buzo deportivo de verano para adultes - Talle S |1000.00
|
||||||
TEXTIL|*Babucha deportiva en algodón - Talle 8 |500|
|
PTC|*Buzo deportivo de verano para adultes - Talle M |1000.00
|
||||||
TEXTIL|*Babucha deportiva en algodón - Talle 10 |600|
|
PTC|*Buzo deportivo de verano para adultes - Talle L |1000.00
|
||||||
TEXTIL|*Babucha deportiva en algodón - Talle 12 |600|
|
PTC|*Buzo deportivo de verano para adultes - Talle XL |1000.00
|
||||||
TEXTIL|*Babucha deportiva en algodón - Talle 14 |600|
|
PTC|*Pantalón deportivo de verano para adultes - Talle S |900.00
|
||||||
TEXTIL|*Babucha deportiva en algodón - Talle 16 |600|
|
PTC|*Pantalón deportivo de verano para adultes - Talle M |900.00
|
||||||
TEXTIL|*Babucha deportiva en algodón - Talle 18 |600|
|
PTC|*Pantalón deportivo de verano para adultes - Talle L |900.00
|
||||||
TEXTIL|*Pantalón deportivo liso de algodón - Talle S|850|
|
PTC|*Pantalón deportivo de verano para adultes - Talle XL |900.00
|
||||||
TEXTIL|*Pantalón deportivo liso de algodón - Talle M|850|
|
P|*Juego de sábanas de algodón 1 plaza|1300.00
|
||||||
TEXTIL|*Pantalón deportivo liso de algodón - Talle L|850|
|
P|*Juego de sábanas de algodón 2 plazas (para sommier)|1500.00
|
||||||
TEXTIL|*Pantalón deportivo liso de algodón - Talle XL |850|
|
P|*Materas de Lona.|450.00
|
||||||
TEXTIL|*Canguro deportivo liso con capucha de algodón - TalleS |1000|
|
P|*Sábana sola con elástico, 2 plazas (para sommier)|950.00
|
||||||
TEXTIL|*Canguro deportivo liso con capucha de algodón - Talle M |1000|
|
P|*Juego de toallón y toalla de algodón|900.00
|
||||||
TEXTIL|*Canguro deportivo liso con capucha de algodón - Talle L |1000|
|
P|*Toallón|750.00
|
||||||
TEXTIL|*Canguro deportivo liso con capucha de algodón - Talle XL|1000|
|
P|*Toalla de mano|350.00
|
||||||
TEXTIL|*Túnica niñe con cinto en espalda y tajo detrás- talles 6 a 16|480|
|
P|*Turbante toalla|450.00
|
||||||
TEXTIL|*Túnica niñe con martingala, festón y pinzas talles 6 a 16|480|
|
P|*Juego de Sábanas de poliéster 1 plaza |850.00
|
||||||
TEXTIL|*Pintor verde - talles 2 a 8|300|
|
P|*Juego de sábanas de poliéster 2 plazas |1000.00
|
||||||
TEXTIL|*Pintor azul - talles 2 a 8|300|
|
MADRES Y FAMILIARES||
|
||||||
TEXTIL|*Pintor rojo - talles 2 a 8|300|
|
PTC|Camiseta EDICION ESPECIAL - talles S al XXL|450.00
|
||||||
TEXTIL|*Pintor amarillo - talles 2 a 8|300|
|
PTC|Canguros Madres y Familiares |950.00
|
||||||
TEXTIL|*Túnicas adulto - talles 1 a 5|1100|
|
P|Pack 1: 1 pin redondo + 1 lapicera + 1 Pañuelo + Pegotines y marcalibros|150.00
|
||||||
TEXTIL|*Moña escolar satinada|50|
|
P|Pack 2: 1 Pin redondo + 1 Lapicera + Pegotines y marcalibros|100.00
|
||||||
TEXTIL|*Juego de sábanas de algodón 1 plaza|1000|
|
P|Pack 3: 1 Llavero + 1 Lapicera|100.00
|
||||||
TEXTIL|*Juego de sábanas de algodón 2 plazas (para sommier) |1200|
|
P|Libro "Desaparecidos"|50.00
|
||||||
TEXTIL|*Sábana sola sin elástico, 2 plazas (para sommier) |700|
|
ARTÍCULOS DE LA COORDINADORA POR PALESTINA||
|
||||||
TEXTIL|*Sábana sola con elástico, 2 plazas (para sommier) |700|
|
B|Bono colaboración|20.00
|
||||||
TEXTIL|*Juego de toallón y toalla de algodón |650|
|
P|Remera talle S - Palestina|450.00
|
||||||
TEXTIL|*Toallón|500|
|
P|Remera talle M - Palestina|450.00
|
||||||
TEXTIL|*Toalla de mano|250|
|
P|Remera talle L - Palestina|450.00
|
||||||
TEXTIL|*Turbante toalla|350|
|
P|Remera talle XL - Palestina|450.00
|
||||||
TEXTIL|*Tapaboca de tela|50|
|
P|Bandera 60x90 cm - Coordinadora por Palestina|450.00
|
||||||
ARTÍCULOS DE MADRES Y FAMILIARES|Pañuelo Madres y Familiares de Detenidos Desaparecidos|50|Madres y familiares
|
P|Pin Coordinadora por Palestina|40.00
|
||||||
ARTÍCULOS DE MADRES Y FAMILIARES|Balconera Madres y Familiares de Detenidos Desaparecidos|100|Madres y familiares
|
P|Balconera "Paremos el genocidio"|150.00
|
||||||
ARTÍCULOS DE MADRES Y FAMILIARES|Pack 2 - 1 Pin redondo + Lapicera + Pegotines y Marcalibros|100|Madres y familiares
|
P|Banderita para el auto |100.00
|
||||||
PRODUCTOS DEL MPS|Pack de 5 pegotines del MPS (2 logos y 3 consignas, todo a color).|40|Madres y familiares
|
P|Kit 5 pegotines - Coordinadora por Palestina|80.00
|
||||||
BONOS Y FINANCIAMIENTO SORORO|Campaña solidaria MPS - apoyo a ollas y merenderos|20|MPS
|
P|Remera talle S - Palestina - Puño - único color Negro *NUEVO|500.00
|
||||||
BONOS Y FINANCIAMIENTO SORORO|Financiamiento sororo para copa menstrual|20|
|
P|Remera talle M - Palestina - Puño - único color Negro *NUEVO|500.00
|
||||||
BONOS Y FINANCIAMIENTO SORORO|Galpón de corrales|20|
|
P|Remera talle L - Palestina - Puño - único color Negro *NUEVO|500.00
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|Copa menstrual de silicona, ecológica |750|
|
P|Remera talle XL - Palestina - Puño - único color Negro *NUEVO|500.00
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|Copa menstrual de silicona, ecológica (financiamiento sororo)|0|
|
PRODUCTOS ESPECIALES Y DE FIN DE AÑO||
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|Vaso esterilizador para copa menstrual|290|
|
P|*Cuaderno artesanal 80 hojas rayado|100.00
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|*Toallita de tela Nocturna "Chúlin"|170|Chúlin
|
P|*Cuaderno artesanal 80 hojas liso|100.00
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|*Toallita de tela para Colaless "Chúlin"|170|Chúlin
|
P|*Cuaderno artesanal 200 hojas rayado|180.00
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|*Toallitas de tela para Bombacha "Chúlin"|200|Chúlin
|
P|*Cuaderno artesanal 200 hojas liso|180.00
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|*Protector Diario de tela "Chúlin"|160|Chúlin
|
P|*Agenda 2025 NUEVO!|420.00
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|*Pack 1: 2 protectores diarios + 2 toallitas para bombacha "Chúlin"|610|Chúlin
|
P|Gorro del MPS - Ciudad Vieja NUEVO!|150.00
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|*Pack 2: 3 protectores diarios "Chúlin" |410|Chúlin
|
P|*Papas con sal 230 gr NUEVO!|154.00
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|Ladysoft Clasicas 8un|20.3|
|
P|*Papas sin sal 250 gr NUEVO!|154.00
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|Tampones Medianos Ladysoft 10un|84.7|
|
P|Turrón Portezuelo Blando 70g NUEVO!|39.67
|
||||||
PRODUCTOS DE GESTIÓN MENSTRUAL|Tampones Grandes Ladysoft 10un|84.7|
|
P|Turrón Portezuelo Bañado 70g NUEVO!|39.67
|
||||||
|
P |*Budín chocolate 300g NUEVO!|200.00
|
||||||
|
P|*Budín vainilla/cítrico 300g NUEVO!|200.00
|
||||||
|
P|*Budín marmolado 300g NUEVO!|200.00
|
||||||
|
P|Pan dulce artesanal con frutas y pasas 500gms NUEVO!|210.00
|
||||||
|
P|Libro para infancias "Encendiendo Memorias" NUEVO!|350.00
|
||||||
|
TRANSPORTE, BONOS Y FINANCIAMIENTO SORORO||
|
||||||
|
T|Por cada $ 500 de consumo, abonar $ 15 para transporte y gastos operativos, ej:$520 son $30|15.00
|
||||||
|
B|Campaña solidaria MPS - apoyo a ollas y merenderos|30.00
|
||||||
|
B|Financiamiento sororo para copa menstrual|20.00
|
||||||
|
B|Bono especial - Apoyo a la familia del compañero Alberto Colman|20.00
|
||||||
|
B|Bono especial - Apoyo al fletero Ramón|20.00
|
||||||
|
PRODUCTOS SUBSIDIADOS POR FINANCIAMIENTO SORORO||
|
||||||
|
P|*Toallita de tela Nocturna "Chúlin"|210.00
|
||||||
|
P|*Toallita de tela para Colaless "Chúlin"|168.00
|
||||||
|
P|*Toallitas de tela para Bombacha "Chúlin"|175.00
|
||||||
|
P|*Protector Diario de tela "Chúlin"|126.00
|
||||||
|
P|*Pack 1: 2 protectores diarios + 2 toallitas para bombacha "Chúlin"|560.00
|
||||||
|
P|*Pack 2: 3 protectores diarios "Chúlin"|364.00
|
||||||
|
P|Ladysoft Clasicas 8un|27.00
|
||||||
|
P|Tampones algodón orgánico - Talle mini- caja 18 un. |208.00
|
||||||
|
P|Tampones algodón orgánico -Talle medio- caja 18un. |208.00
|
||||||
|
P|Tampones algodón orgánico -Talle super-caja 15 un. |208.00
|
Can't render this file because it contains an unexpected character in line 19 and column 39.
|
135
resources/js/app.js
vendored
135
resources/js/app.js
vendored
|
@ -3,10 +3,12 @@
|
||||||
* includes Vue and other libraries. It is a great starting point when
|
* includes Vue and other libraries. It is a great starting point when
|
||||||
* building robust, powerful web applications using Vue and Laravel.
|
* building robust, powerful web applications using Vue and Laravel.
|
||||||
*/
|
*/
|
||||||
|
import axios from 'axios';
|
||||||
require('./bootstrap');
|
import Vue from 'vue';
|
||||||
|
|
||||||
window.Vue = require('vue');
|
window.Vue = require('vue');
|
||||||
|
window.Event = new Vue();
|
||||||
|
window.axios = axios;
|
||||||
|
window.bulmaToast = require('bulma-toast');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The following block of code may be used to automatically register your
|
* The following block of code may be used to automatically register your
|
||||||
|
@ -15,18 +17,133 @@ window.Vue = require('vue');
|
||||||
*
|
*
|
||||||
* Eg. ./components/ExampleComponent.vue -> <example-component></example-component>
|
* Eg. ./components/ExampleComponent.vue -> <example-component></example-component>
|
||||||
*/
|
*/
|
||||||
|
import './components';
|
||||||
|
|
||||||
// const files = require.context('./', true, /\.vue$/i)
|
/**
|
||||||
// files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default))
|
* Constants
|
||||||
|
*/
|
||||||
Vue.component('example-component', require('./components/ExampleComponent.vue').default);
|
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, ''))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Next, we will create a fresh Vue application instance and attach it to
|
* Next, we will create a fresh Vue application instance and attach it to
|
||||||
* the page. Then, you may begin adding components to this application
|
* the page. Then, you may begin adding components to this application
|
||||||
* or customize the JavaScript scaffolding to fit your unique needs.
|
* or customize the JavaScript scaffolding to fit your unique needs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
el: '#app',
|
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')
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
25
resources/js/components.js
vendored
Normal file
25
resources/js/components.js
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
const requireComponent = require.context('./components', true, /\.vue$/);
|
||||||
|
|
||||||
|
// Registro automático de componentes:
|
||||||
|
// e.g. components/foo/bar/UnComponente.vue
|
||||||
|
// se registra como 'foo-bar-un-componente'
|
||||||
|
requireComponent.keys().forEach(fileName => {
|
||||||
|
// Get the component config
|
||||||
|
const componentConfig = requireComponent(fileName);
|
||||||
|
// Get the PascalCase name of the component
|
||||||
|
const componentName = fileName
|
||||||
|
.replace(/^\.\/(.*)\.\w+$/, '$1') // Remove "./" from the beginning and the file extension from the end
|
||||||
|
.replace(/\//g, '-') // Replace directories with hyphens
|
||||||
|
.replace(/([a-z])([A-Z])/g, '$1-$2') // Insert hyphen between camelCase words
|
||||||
|
.toLowerCase() // Convert to lowercase
|
||||||
|
// Globally register the component
|
||||||
|
Vue.component(
|
||||||
|
componentName,
|
||||||
|
// Look for the component options on `.default`, which will
|
||||||
|
// exist if the component was exported with `export default`,
|
||||||
|
// otherwise fall back to module's root.
|
||||||
|
componentConfig.default || componentConfig
|
||||||
|
);
|
||||||
|
});
|
|
@ -1,23 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Example Component</div>
|
|
||||||
|
|
||||||
<div class="card-body">
|
|
||||||
I'm an example component.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
mounted() {
|
|
||||||
console.log('Component mounted.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
83
resources/js/components/admin/Body.vue
Normal file
83
resources/js/components/admin/Body.vue
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<template>
|
||||||
|
<div class="block ml-3 mr-3 is-max-widescreen is-max-desktop">
|
||||||
|
<comunes-tabs-secciones :tabs="tabs" :tabInicial="tabActiva"></comunes-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>
|
||||||
|
</div>
|
||||||
|
<p class="has-text-centered" v-else>
|
||||||
|
Todavía no hay ningún pedido para administrar.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="block pb-6" id="caracteristicas-seccion"
|
||||||
|
:class="seccionActiva === 'caracteristicas-seccion' ? 'is-active' : 'is-hidden'">
|
||||||
|
<admin-caracteristicas-opcionales>
|
||||||
|
</admin-caracteristicas-opcionales>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CaracteristicasOpcionales from "./CaracteristicasOpcionales.vue";
|
||||||
|
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";
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CaracteristicasOpcionales,
|
||||||
|
TabsSecciones,
|
||||||
|
DropdownDescargar,
|
||||||
|
TablaPedidos,
|
||||||
|
TablaBonos,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
gdc: undefined,
|
||||||
|
tabs: [{ id: "pedidos", nombre: "Pedidos" },
|
||||||
|
{ id: "caracteristicas", nombre: "Caracteristicas opcionales" }],
|
||||||
|
tabActiva: "pedidos",
|
||||||
|
seccionActiva: "pedidos-seccion",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
12
resources/js/components/admin/BotonLogin.vue
Normal file
12
resources/js/components/admin/BotonLogin.vue
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<template>
|
||||||
|
<div class="buttons is-right">
|
||||||
|
<a class="button is-danger is-light is-small" href="/admin">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa fa-solid fa-user-check"></i>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Admin
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
42
resources/js/components/admin/CaracteristicasOpcionales.vue
Normal file
42
resources/js/components/admin/CaracteristicasOpcionales.vue
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<script>
|
||||||
|
import FilaCaracteristica from "./FilaCaracteristica.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { FilaCaracteristica },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
caracteristicas: [
|
||||||
|
{
|
||||||
|
id: "devoluciones",
|
||||||
|
nombre: "Devoluciones",
|
||||||
|
habilitada: false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<table class="table is-fullwidth is-striped is-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th> Característica </th>
|
||||||
|
<th> Habilitada </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<admin-fila-caracteristica
|
||||||
|
v-for="(c,i) in caracteristicas"
|
||||||
|
:key="i"
|
||||||
|
:caracteristica="c">
|
||||||
|
</admin-fila-caracteristica>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
56
resources/js/components/admin/DropdownDescargar.vue
Normal file
56
resources/js/components/admin/DropdownDescargar.vue
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<template>
|
||||||
|
<div class="buttons is-right">
|
||||||
|
<div class="dropdown" :class="{'is-active': dropdownActivo}" @mouseleave="dropdownActivo = false">
|
||||||
|
<div class="dropdown-trigger">
|
||||||
|
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu" :disabled="!hayAprobados" @click="dropdownActivo = !dropdownActivo">
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</span>
|
||||||
|
<span>Descargar pedido</span>
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<a :href="'/admin/exportar-pedido-a-csv/' + gdc_id" class="dropdown-item has-background-primary">
|
||||||
|
Planilla para central (CSV)
|
||||||
|
</a>
|
||||||
|
<a :href="'/admin/exportar-planillas-a-pdf/' + gdc_id" class="dropdown-item">
|
||||||
|
Planillas para armado (PDF)
|
||||||
|
</a>
|
||||||
|
<a :href="'/admin/exportar-pedido-con-nucleos-a-csv/' + gdc_id" class="dropdown-item">
|
||||||
|
Planilla completa de la canasta (CSV)
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
gdc_id: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dropdownActivo: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hayAprobados: function() {
|
||||||
|
return this.$parent.hayAprobados;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
69
resources/js/components/admin/FilaCaracteristica.vue
Normal file
69
resources/js/components/admin/FilaCaracteristica.vue
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<script>
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
caracteristica: Object
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
gdc: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<tr>
|
||||||
|
<td>{{ caracteristica.nombre }}</td>
|
||||||
|
<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>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
36
resources/js/components/admin/FilaPedido.vue
Normal file
36
resources/js/components/admin/FilaPedido.vue
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<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>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SwitchAprobacion from "./SwitchAprobacion.vue";
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SwitchAprobacion
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
pedido: Object
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
Event.$on('sync-aprobacion', (unPedido) => {
|
||||||
|
if (this.pedido.id === unPedido.id) {
|
||||||
|
this.pedido = unPedido
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
56
resources/js/components/admin/Login.vue
Normal file
56
resources/js/components/admin/Login.vue
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<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>
|
46
resources/js/components/admin/SwitchAprobacion.vue
Normal file
46
resources/js/components/admin/SwitchAprobacion.vue
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<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">
|
||||||
|
<span class="is-hidden-mobile">{{ mensaje }}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
pedido: Object
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
aprobado: this.pedido.aprobado
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
mensaje: function () {
|
||||||
|
return this.aprobado ? "Pagado" : "No pagado"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleAprobacion() {
|
||||||
|
Event.$emit('aprobacion-subpedido', this.pedido.id, !this.aprobado);
|
||||||
|
this.aprobado = !this.aprobado
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
Event.$on('sync-aprobacion', (unPedido) => {
|
||||||
|
if (this.pedido.id === unPedido.id) {
|
||||||
|
this.pedido = unPedido
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
97
resources/js/components/admin/TablaBonos.vue
Normal file
97
resources/js/components/admin/TablaBonos.vue
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<div class="block" v-show="!hayBonos">
|
||||||
|
<p class="has-text-centered">
|
||||||
|
Todavía no hay bonos pedidos.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="block" v-show="hayBonos">
|
||||||
|
<table class="table is-bordered is-striped is-hoverable is-fullwidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><abbr title="Núcleo">Núcleo</abbr></th>
|
||||||
|
<td v-for="(bono,i) in bonos" :key="i" class="is-1">
|
||||||
|
{{ bono.nombre }}
|
||||||
|
</td>
|
||||||
|
<th><abbr title="Total a Pagar">Total $</abbr></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(bp, i) in bonosPorPedido" :key="i">
|
||||||
|
<td> {{ bp.nucleo }} </td>
|
||||||
|
<td v-for="(bono,j) in bp.bonos" :key="j" class="has-text-right">
|
||||||
|
{{ bono.cantidad }}
|
||||||
|
</td>
|
||||||
|
<td class="has-text-right"> {{ bp.total }} </td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th :colspan="bonos.length">Total bonos</th>
|
||||||
|
<th class="has-text-right">$ {{ totalBonos }}</th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
pedidos: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
bonos: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
pedidosAprobados: function() {
|
||||||
|
return this.pedidos.filter(p => p.aprobado)
|
||||||
|
},
|
||||||
|
hayBonos: function() {
|
||||||
|
return this.pedidosAprobados.filter(p => p.subtotal_bonos != 0).length !== 0
|
||||||
|
},
|
||||||
|
bonosPorPedido: function() {
|
||||||
|
let bonosPorPedido = this.pedidosAprobados.map(p => p = {"nucleo":p.nombre, "bonos":p.productos.filter(x => x.bono), "total":p.subtotal_bonos});
|
||||||
|
bonosPorPedido.forEach(bp => {
|
||||||
|
bp.bonos = bp.bonos.map(b => b = {"bono":b.nombre, "cantidad":b.pivot.cantidad, "total":b.pivot.total, "id":b.id})
|
||||||
|
})
|
||||||
|
return bonosPorPedido.map(bp => this.completarBonos(bp));
|
||||||
|
},
|
||||||
|
totalBonos: function() {
|
||||||
|
let total = 0
|
||||||
|
this.bonosPorPedido.map(bp => total += parseInt(bp.total))
|
||||||
|
return total
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
completarBonos(bonosPedido) {
|
||||||
|
this.bonos.map(b => {
|
||||||
|
if (!bonosPedido.bonos.map(x => x.id).includes(b.id))
|
||||||
|
bonosPedido.bonos.push({"bono":b.nombre, "cantidad":0, "total":0, "id":b.id})
|
||||||
|
})
|
||||||
|
bonosPedido.bonos = bonosPedido.bonos.sort((b1,b2) => b1.id - b2.id)
|
||||||
|
return bonosPedido
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
axios.get("../api/productos", {
|
||||||
|
params: {
|
||||||
|
categoria:'TRANSPORTE, BONOS Y FINANCIAMIENTO SORORO',
|
||||||
|
}
|
||||||
|
}).then(response => {
|
||||||
|
this.bonos = response.data.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
69
resources/js/components/admin/TablaPedidos.vue
Normal file
69
resources/js/components/admin/TablaPedidos.vue
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<table class="table is-fullwidth is-striped is-bordered">
|
||||||
|
<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 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>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="table is-striped is-bordered">
|
||||||
|
<tr>
|
||||||
|
<th colspan="2" class="has-background-black has-text-white has-text-centered">TOTALES</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Total a recaudar:</th>
|
||||||
|
<td class="has-text-right">$ {{ gdc.total_a_recaudar }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Total bonos barriales:</th>
|
||||||
|
<td class="has-text-right">$ {{ gdc.total_barrial }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="$root.devoluciones">
|
||||||
|
<th>Total devoluciones:</th>
|
||||||
|
<td class="has-text-right">- $ {{ gdc.total_devoluciones }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Cantidad bonos de transporte:</th>
|
||||||
|
<td class="has-text-right">{{ gdc.cantidad_transporte }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Total bonos de transporte:</th>
|
||||||
|
<td class="has-text-right">$ {{ gdc.total_transporte }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Total a depositar:</th>
|
||||||
|
<td class="has-text-right">$ {{ gdc.total_a_transferir }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FilaPedido from "./FilaPedido.vue";
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
FilaPedido
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
gdc: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
75
resources/js/components/compras/Body.vue
Normal file
75
resources/js/components/compras/Body.vue
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<template>
|
||||||
|
<div class="block ml-3 mr-3 is-max-widescreen is-max-desktop">
|
||||||
|
<comunes-tabs-secciones :tabs="tabs" :tabInicial="tabActiva"></comunes-tabs-secciones>
|
||||||
|
<div class="block pb-6" id="pedidos-compras-seccion"
|
||||||
|
:class="seccionActiva === 'pedidos-compras-seccion' ? 'is-active' : 'is-hidden'">
|
||||||
|
<div class="block" id="pedidos-compras-tabla-y-dropdown">
|
||||||
|
<compras-dropdown-descargar>
|
||||||
|
</compras-dropdown-descargar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="block pb-6" id="canasta-compras-seccion"
|
||||||
|
:class="seccionActiva === 'canasta-compras-seccion' ? 'is-active' : 'is-hidden'">
|
||||||
|
<div class="block" id="canasta-compras-seccion">
|
||||||
|
<article class="message is-warning">
|
||||||
|
<div class="message-header">
|
||||||
|
<p>Formato de la canasta</p>
|
||||||
|
</div>
|
||||||
|
<div class="message-body">
|
||||||
|
<div class="content">
|
||||||
|
La planilla de la canasta tiene que tener el siguiente formato para que la aplicación la lea correctamente:
|
||||||
|
<ul>
|
||||||
|
<li> Los precios deben usar punto y no coma decimal </li>
|
||||||
|
<li> El nombre de la columna de precios debe ser "Precio" </li>
|
||||||
|
<li> Las celdas deben separarse con '|' </li>
|
||||||
|
<li> No puede haber "enters" en ninguna celda </li>
|
||||||
|
<li> Todos los bonos deben tener tipo 'B' para evitar que paguen transporte </li>
|
||||||
|
<li> El bono de transporte debe tener tipo 'T' </li>
|
||||||
|
</ul>
|
||||||
|
<a class="has-text-info" href="/compras/canasta/ejemplo">Planilla de ejemplo.</a>
|
||||||
|
<article class="message is-danger mt-2">
|
||||||
|
<div class="message-body">
|
||||||
|
<div class="content">
|
||||||
|
Cuidado! Cargar una nueva canasta elimina todos los pedidos de la aplicación.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<div class="buttons is-right">
|
||||||
|
<compras-canasta-input></compras-canasta-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TabsSecciones from "../comunes/TabsSecciones.vue";
|
||||||
|
import DropdownDescargar from "./DropdownDescargar.vue";
|
||||||
|
import CanastaInput from "./CanastaInput.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
TabsSecciones,
|
||||||
|
DropdownDescargar,
|
||||||
|
CanastaInput,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tabs: [{ id: "pedidos-compras", nombre: "Pedidos" },
|
||||||
|
{ id: "canasta-compras", nombre: "Canasta" }],
|
||||||
|
tabActiva: "pedidos-compras",
|
||||||
|
seccionActiva: "pedidos-compras-seccion",
|
||||||
|
archivo: undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setSeccionActiva(tabId) {
|
||||||
|
this.tabActiva = tabId;
|
||||||
|
this.seccionActiva = tabId + "-seccion";
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
69
resources/js/components/compras/CanastaInput.vue
Normal file
69
resources/js/components/compras/CanastaInput.vue
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<div class="file has-name is-right">
|
||||||
|
<label class="file-label">
|
||||||
|
<input
|
||||||
|
class="file-input"
|
||||||
|
type="file"
|
||||||
|
name="canasta"
|
||||||
|
@change="archivoSubido"
|
||||||
|
/>
|
||||||
|
<span class="file-cta">
|
||||||
|
<span class="file-icon">
|
||||||
|
<i class="fas fa-cloud-upload-alt"></i>
|
||||||
|
</span>
|
||||||
|
<span class="file-label">Subir canasta</span>
|
||||||
|
</span>
|
||||||
|
<span class="file-name" v-if="archivo">
|
||||||
|
{{ 'Cargando ' + archivo.nombre }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CanastaInput",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
archivo: null,
|
||||||
|
cargando: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async archivoSubido(event) {
|
||||||
|
const archivo = event.target.files[0];
|
||||||
|
if (archivo && archivo.type === "text/csv") {
|
||||||
|
this.archivo = {data: archivo, nombre: archivo.name};
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("data", this.archivo.data);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.cargando = true;
|
||||||
|
const response = await axios.post("/compras/canasta", formData, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.$root.$toast(response.data.message || "Canasta cargada exitosamente");
|
||||||
|
} catch (error) {
|
||||||
|
this.$root.$toast(error.response?.data?.message || "Hubo errores.");
|
||||||
|
} finally {
|
||||||
|
this.cargando = false;
|
||||||
|
this.archivo = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$root.$toast("La canasta debe ser .CSV")
|
||||||
|
this.archivo = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
44
resources/js/components/compras/DropdownDescargar.vue
Normal file
44
resources/js/components/compras/DropdownDescargar.vue
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<template>
|
||||||
|
<div class="buttons is-right">
|
||||||
|
<div class="dropdown" :class="{'is-active': dropdownActivo}" @mouseleave="dropdownActivo = false">
|
||||||
|
<div class="dropdown-trigger">
|
||||||
|
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu" @click="dropdownActivo = !dropdownActivo">
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</span>
|
||||||
|
<span>Descargar planillas</span>
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<a href="/compras/pedidos/descargar" class="dropdown-item">
|
||||||
|
Pedidos por barrio
|
||||||
|
</a>
|
||||||
|
<a href="/compras/pedidos/notas" class="dropdown-item">
|
||||||
|
Notas por barrio
|
||||||
|
</a>
|
||||||
|
<a href="/compras/pedidos/transporte" class="dropdown-item">
|
||||||
|
Transporte por barrio
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dropdownActivo: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
52
resources/js/components/compras/Login.vue
Normal file
52
resources/js/components/compras/Login.vue
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Usuario</label>
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<input required class="input" type="text" name="name" placeholder="Usuario">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Contraseña</label>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control">
|
||||||
|
<input required class="input" :type="this.passwordType" name="password" placeholder="Contraseña">
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<a class="button is-dark" @click="togglePassword">
|
||||||
|
{{ (passwordVisible ? 'Ocultar' : 'Mostrar') + ' contraseña'}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<input type="submit" class="button is-dark" value="Ingresar"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "LoginAdmin",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
passwordVisible: false,
|
||||||
|
passwordType: "password",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
togglePassword() {
|
||||||
|
if (this.passwordVisible) this.passwordType = "password";
|
||||||
|
else this.passwordType = "text"
|
||||||
|
this.passwordVisible = !this.passwordVisible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
51
resources/js/components/comunes/BarrioSelect.vue
Normal file
51
resources/js/components/comunes/BarrioSelect.vue
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<template>
|
||||||
|
<div v-show="visible" class="block">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" :class="isAdmin ? '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' : '')">
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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}
|
||||||
|
}
|
||||||
|
</script>
|
91
resources/js/components/comunes/NavBar.vue
Normal file
91
resources/js/components/comunes/NavBar.vue
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<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">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ChismosaDropdown from '../pedidos/ChismosaDropdown.vue';
|
||||||
|
export default {
|
||||||
|
components: { ChismosaDropdown },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
burgerActiva: false,
|
||||||
|
searchString: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleBurger() {
|
||||||
|
this.burgerActiva = !this.burgerActiva
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-bar {
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-a {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
.hide-below-1024 {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.hide-above-1023 {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
38
resources/js/components/comunes/RegionSelect.vue
Normal file
38
resources/js/components/comunes/RegionSelect.vue
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" :class="whiteText ? '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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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}
|
||||||
|
}
|
||||||
|
</script>
|
55
resources/js/components/comunes/TabsSecciones.vue
Normal file
55
resources/js/components/comunes/TabsSecciones.vue
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<div class="tabs is-boxed" id="tabs">
|
||||||
|
<ul class="has-bottom-line">
|
||||||
|
<li v-for="(tab, index) in tabs" class="is-size-6"
|
||||||
|
:key="index"
|
||||||
|
:id="tab.id + '-tab'"
|
||||||
|
:class="{'is-active': tab.id === tabActiva}">
|
||||||
|
<a @click="setTabActiva(tab.id)">
|
||||||
|
<span>
|
||||||
|
{{ tab.nombre }}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
tabs: Array,
|
||||||
|
tabInicial: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tabActiva: this.tabInicial,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setTabActiva(tabId) {
|
||||||
|
this.$parent.setSeccionActiva(tabId);
|
||||||
|
this.tabActiva = tabId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '../../../../node_modules/bulma';
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
.has-bottom-line {
|
||||||
|
border-bottom-color: #dbdbdb !important;
|
||||||
|
border-bottom-style: solid !important;
|
||||||
|
border-bottom-width: 1px !important;
|
||||||
|
}
|
||||||
|
.tabs li.is-active a {
|
||||||
|
border-bottom-color: #e3342f;
|
||||||
|
color: #e3342f;
|
||||||
|
}
|
||||||
|
</style>
|
22
resources/js/components/pedidos/Body.vue
Normal file
22
resources/js/components/pedidos/Body.vue
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
chismosaActiva: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
Event.$on('toggle-chismosa', (activa) => {
|
||||||
|
this.chismosaActiva = activa;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
32
resources/js/components/pedidos/CartelPedidoAprobado.vue
Normal file
32
resources/js/components/pedidos/CartelPedidoAprobado.vue
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<template>
|
||||||
|
<div v-show="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();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
41
resources/js/components/pedidos/CategoriasContainer.vue
Normal file
41
resources/js/components/pedidos/CategoriasContainer.vue
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<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 -->
|
||||||
|
</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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
102
resources/js/components/pedidos/Chismosa.vue
Normal file
102
resources/js/components/pedidos/Chismosa.vue
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<template>
|
||||||
|
<div class="column is-one-third">
|
||||||
|
<div class="fixed-right">
|
||||||
|
<table v-show="mostrar_tabla" class="table is-striped is-bordered tabla-chismosa is-narrow">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Producto</th>
|
||||||
|
<th>Cantidad</th>
|
||||||
|
<th><abbr title="Precio Total">$</abbr></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<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>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="this.$root.devoluciones">
|
||||||
|
<th><p>Devoluciones</p></th>
|
||||||
|
<td>
|
||||||
|
<abbr :title="notas_devoluciones">{{ notas_devoluciones_abbr }}</abbr>
|
||||||
|
<button @click.capture="modificarDevoluciones()" 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>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Total total</th>
|
||||||
|
<th></th>
|
||||||
|
<th class="has-text-right">{{ total }}</th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
<tbody>
|
||||||
|
<pedidos-producto-row v-for="producto in productos" :producto="producto" :key="producto.id"></pedidos-producto-row>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="has-text-centered" v-show="!mostrar_tabla">
|
||||||
|
Compa, todavía no agregaste nada a la chismosa.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
Event.$on('pedido-actualizado', this.pedidoActualizado);
|
||||||
|
Event.$on('toggle-chismosa', this.pedidoActualizado);
|
||||||
|
},
|
||||||
|
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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tabla-chismosa {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.fixed-right {
|
||||||
|
position: fixed;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 81vh;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
45
resources/js/components/pedidos/ChismosaDropdown.vue
Normal file
45
resources/js/components/pedidos/ChismosaDropdown.vue
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<template>
|
||||||
|
<div class="dropdown is-right navbar-item" :class="{'is-active':activa}">
|
||||||
|
<div class="dropdown-trigger">
|
||||||
|
<a class="text-a" aria-haspopup="true" :aria-controls="id" @click.capture="toggle">
|
||||||
|
<span class="icon is-small mr-1">
|
||||||
|
<img src="/assets/chismosa.png">
|
||||||
|
</span>
|
||||||
|
<span v-text="'$' + total"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Chismosa from './Chismosa.vue'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Chismosa
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activa: false,
|
||||||
|
total: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
Event.$on('pedido-actualizado', this.actualizar);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggle() {
|
||||||
|
this.activa = !this.activa;
|
||||||
|
Event.$emit("toggle-chismosa", this.activa);
|
||||||
|
},
|
||||||
|
actualizar() {
|
||||||
|
this.total = this.$root.pedido.total;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
67
resources/js/components/pedidos/DevolucionesModal.vue
Normal file
67
resources/js/components/pedidos/DevolucionesModal.vue
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<div v-bind:class="visible ? 'is-active modal' : 'modal'">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-card">
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Devoluciones</p>
|
||||||
|
<button class="delete" aria-label="close" @click.capture="cerrar"></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">
|
||||||
|
</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">
|
||||||
|
</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>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
total: 0,
|
||||||
|
notas: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
miga: function() {
|
||||||
|
return {
|
||||||
|
nombre: "Devoluciones",
|
||||||
|
href: "#devoluciones",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
cerrar() {
|
||||||
|
this.visible = false;
|
||||||
|
Event.$emit("migas-pop");
|
||||||
|
},
|
||||||
|
modificar() {
|
||||||
|
Event.$emit('sync-devoluciones', this.total, this.notas);
|
||||||
|
this.cerrar();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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>
|
55
resources/js/components/pedidos/Login.vue
Normal file
55
resources/js/components/pedidos/Login.vue
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<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>
|
54
resources/js/components/pedidos/NavMigas.vue
Normal file
54
resources/js/components/pedidos/NavMigas.vue
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<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>
|
||||||
|
</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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
nav.breadcrumb.is-fixed-top {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 3.25rem;
|
||||||
|
height: 2.75rem;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
</style>
|
140
resources/js/components/pedidos/ProductoCantidad.vue
Normal file
140
resources/js/components/pedidos/ProductoCantidad.vue
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="field has-addons contador">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-small" @click.capture="decrementar();">
|
||||||
|
<i class="fa fa-solid fa-minus"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<input id="cantidad" v-model="cantidad" 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()">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-check"></i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<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">
|
||||||
|
<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" />
|
||||||
|
<span v-if="notas_warning_visible" class="icon is-small is-right">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
</span>
|
||||||
|
<article v-if="notas_warning_visible" class="message is-danger is-small">
|
||||||
|
<div class="message-body">
|
||||||
|
No se puede dejar este campo vacío
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
producto: Object
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cantidad: this.cantidadEnChismosa(),
|
||||||
|
notas: this.notasEnChismosa(),
|
||||||
|
notas_warning_visible: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
Event.$on('sync-subpedido', (cantidad, productoId, notas) => {
|
||||||
|
if (this.producto.id === productoId)
|
||||||
|
this.sincronizar(cantidad, notas);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-danger {
|
||||||
|
background-color: #fca697;
|
||||||
|
}
|
||||||
|
.is-danger::placeholder {
|
||||||
|
color: #fff;
|
||||||
|
opacity: 1; /* Firefox */
|
||||||
|
}
|
||||||
|
</style>
|
113
resources/js/components/pedidos/ProductoCard.vue
Normal file
113
resources/js/components/pedidos/ProductoCard.vue
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ProductoCard",
|
||||||
|
props: {
|
||||||
|
producto: Object
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cantidad: this.producto.cantidad,
|
||||||
|
enChismosa: this.producto.cantidad,
|
||||||
|
notas: this.producto.notas,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="block column is-one-quarter-desktop is-full-mobile is-half-tablet min-width-from-desktop">
|
||||||
|
<div class="box" style="height:100%">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<div class="column is-one-quarter has-text-right">
|
||||||
|
<p class="has-text-weight-bold has-text-primary">
|
||||||
|
<span class="is-left-mobile">
|
||||||
|
<img v-show="producto.economia_solidaria" height="30px" width="30px" src="/assets/solidaria.png" alt="proveedor de economía solidaria">
|
||||||
|
<img v-show="producto.nacional" height="30px" width="30px" src="/assets/uruguay.png" alt="proveedor nacional"/>
|
||||||
|
</span>
|
||||||
|
$<span v-text="producto.precio"></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer class="columns">
|
||||||
|
<div class="column is-three-quarters">
|
||||||
|
<pedidos-producto-cantidad :producto="producto"></pedidos-producto-cantidad>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<p class="subtitle is-7 is-hidden-mobile" v-if="enChismosa > 0">{{ enChismosa }} en chismosa</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div><!-- END BOX -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use "../../../../node_modules/bulma/sass/utilities/mixins";
|
||||||
|
|
||||||
|
@include mixins.until(mixins.$desktop) {
|
||||||
|
.hidden-until-desktop {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mixins.from(mixins.$desktop) {
|
||||||
|
.min-width-from-desktop {
|
||||||
|
min-width: 25rem;
|
||||||
|
}
|
||||||
|
.hidden-from-desktop {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mixins.from(mixins.$tablet) {
|
||||||
|
.hidden-from-tablet {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mixins.mobile() {
|
||||||
|
.is-left-mobile {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
16
resources/js/components/pedidos/ProductoRow.vue
Normal file
16
resources/js/components/pedidos/ProductoRow.vue
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<template>
|
||||||
|
<tr>
|
||||||
|
<td>{{ this.producto.nombre }}</td>
|
||||||
|
<td class="has-text-right">
|
||||||
|
<pedidos-producto-cantidad :producto="producto"></pedidos-producto-cantidad>
|
||||||
|
</td>
|
||||||
|
<td class="has-text-right">{{ Math.ceil(this.producto.pivot.total) }}</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
producto: Object
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
55
resources/js/components/pedidos/ProductosContainer.vue
Normal file
55
resources/js/components/pedidos/ProductosContainer.vue
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<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 -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
91
resources/js/components/pedidos/SubpedidoSelect.vue
Normal file
91
resources/js/components/pedidos/SubpedidoSelect.vue
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<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">
|
||||||
|
<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="column is-half-mobile is-two-thirds-desktop is-two-thirds-tablet">
|
||||||
|
<p style="padding-top: calc(.5em - 1px); margin-bottom: .5rem" v-text="subpedidoExistente.nombre"></p>
|
||||||
|
</div>
|
||||||
|
<div class="buttons column is-half-mobile is-one-third-desktop is-one-third-tablet">
|
||||||
|
<button class="button is-danger" @click="elegirSubpedido(subpedidoExistente)">Continuar pedido</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
55
resources/sass/app.scss
vendored
55
resources/sass/app.scss
vendored
|
@ -4,5 +4,56 @@
|
||||||
// Variables
|
// Variables
|
||||||
@import 'variables';
|
@import 'variables';
|
||||||
|
|
||||||
// Bootstrap
|
@import 'bulma';
|
||||||
@import '~bootstrap/scss/bootstrap';
|
@import '~bulma-switch';
|
||||||
|
|
||||||
|
main.has-top-padding {
|
||||||
|
padding-top: 4.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.table td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-text-centered {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-fixed-top {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Author: Aseem Lalfakawma <alalfakawma.github.io>
|
||||||
|
This SCSS mixin will allow sizing of table columns in Bulma CSS Framework.
|
||||||
|
The Bulma framework does not support this yet, this small code snippet fixes this.
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
* Should be applied on TH element, it controls all the columns under it *
|
||||||
|
The class should be "is-#",
|
||||||
|
is-1 will give the first widthamount, is-2 will give the second.
|
||||||
|
Eg. The code below shows the widthAmounts as (1, 2.5, 3, 4 , 5, 6, 7)
|
||||||
|
When typing <th class="is-2">, the width will be 2.5em, as the second value in widthAmounts array is 2.5
|
||||||
|
*/
|
||||||
|
|
||||||
|
$widthAmounts: (15); // Just add the numbers here, you can use points too.
|
||||||
|
$widthUnit: "em"; // Add the unit here (rem|em|px|%)
|
||||||
|
|
||||||
|
@each $width in $widthAmounts {
|
||||||
|
$i: index($widthAmounts, $width);
|
||||||
|
@media only screen and (min-width: 768px) {
|
||||||
|
.table thead th.is-#{$i} {
|
||||||
|
width: #{$width}#{$widthUnit} !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
resources/views/auth/admin_login.blade.php
Normal file
34
resources/views/auth/admin_login.blade.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="has-background-danger" lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{ config('app.name', 'Pedidos del MPS') }}</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section class="section">
|
||||||
|
<div id="root" class="container">
|
||||||
|
<h1 class="title has-text-white">
|
||||||
|
Administración de Pedidos MPS
|
||||||
|
</h1>
|
||||||
|
<p class="subtitle has-text-white">
|
||||||
|
Bienvenidx a la administración de pedidos del <strong class="has-text-white">Mercado Popular de Subsistencia</strong>
|
||||||
|
</p>
|
||||||
|
@error('name')
|
||||||
|
<div class="notification is-warning">
|
||||||
|
Contraseña incorrecta, intentalo nuevamente.
|
||||||
|
</div>
|
||||||
|
@enderror
|
||||||
|
<comunes-region-select v-bind:admin="true"></comunes-region-select>
|
||||||
|
<form method="post" action="login">
|
||||||
|
@csrf
|
||||||
|
<comunes-barrio-select v-bind:admin="true"></comunes-barrio-select>
|
||||||
|
<admin-login></admin-login>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<script src="{{ mix('js/app.js') }}" defer></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
resources/views/auth/admin_subpedidos.blade.php
Normal file
5
resources/views/auth/admin_subpedidos.blade.php
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<admin-body></admin-body>
|
||||||
|
@endsection
|
33
resources/views/auth/compras_login.blade.php
Normal file
33
resources/views/auth/compras_login.blade.php
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="has-background-warning" lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{ config('app.name', 'Pedidos del MPS') }}</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
|
||||||
|
<script src="https://kit.fontawesome.com/9235d1c676.js" crossorigin="anonymous"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section class="section">
|
||||||
|
<div id="root" class="container">
|
||||||
|
<h1 class="title">
|
||||||
|
Pedidos MPS
|
||||||
|
</h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
Bienvenidx a la sección de compras de la aplicación del <strong>Mercado Popular de Subsistencia</strong>
|
||||||
|
</p>
|
||||||
|
@error('name')
|
||||||
|
<div class="notification is-danger">
|
||||||
|
Contraseña incorrecta, intentalo nuevamente.
|
||||||
|
</div>
|
||||||
|
@enderror
|
||||||
|
<form method="post" action="login">
|
||||||
|
@csrf
|
||||||
|
<compras-login></compras-login>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<script src="{{ mix('js/app.js') }}" defer></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -3,33 +3,34 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{{ config('app.name', 'Compras del MPS') }}</title>
|
<title>{{ config('app.name', 'Pedidos del MPS') }}</title>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
|
||||||
|
<script src="https://kit.fontawesome.com/9235d1c676.js" crossorigin="anonymous"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div id="root" class="container">
|
<div id="root" class="container">
|
||||||
|
<admin-boton-login></admin-boton-login>
|
||||||
<h1 class="title">
|
<h1 class="title">
|
||||||
Compras MPS
|
Pedidos MPS
|
||||||
</h1>
|
</h1>
|
||||||
<p class="subtitle">
|
<p class="subtitle">
|
||||||
Bienvenidx a la aplicación de compras del <strong>Mercado Popular de Subsistencia</strong>
|
Bienvenidx a la aplicación de pedidos del <strong>Mercado Popular de Subsistencia</strong>
|
||||||
</p>
|
</p>
|
||||||
@error('name')
|
@error('name')
|
||||||
<div class="notification is-danger">
|
<div class="notification is-danger">
|
||||||
Contraseña incorrecta, intentalo nuevamente.
|
Contraseña incorrecta, intentalo nuevamente.
|
||||||
</div>
|
</div>
|
||||||
@enderror
|
@enderror
|
||||||
<region-select></region-select>
|
<comunes-region-select></comunes-region-select>
|
||||||
<form method="post" action="login">
|
<form method="post" action="login">
|
||||||
@csrf
|
@csrf
|
||||||
<barrio-select></barrio-select>
|
<comunes-barrio-select></comunes-barrio-select>
|
||||||
<login></login>
|
<pedidos-login></pedidos-login>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
|
<script src="{{ mix('js/app.js') }}" defer></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
|
||||||
<script src="{{ asset('js/login.js') }}" defer></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
5
resources/views/compras_pedidos.blade.php
Normal file
5
resources/views/compras_pedidos.blade.php
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<compras-body></compras-body>
|
||||||
|
@endsection
|
|
@ -7,20 +7,19 @@
|
||||||
<!-- CSRF Token -->
|
<!-- CSRF Token -->
|
||||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
|
|
||||||
<title>{{ session("subpedido_nombre") ? "Pedido de " . session("subpedido_nombre") . " - " . config('app.name', 'Compras del MPS') : config('app.name', 'Compras del MPS')}}</title>
|
<title>{{ session("subpedido_nombre") ? "Pedido de " . session("subpedido_nombre") . " - " . config('app.name', 'Pedidos del MPS') : config('app.name', 'Pedidos del MPS')}}</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/favicon.png">
|
||||||
|
|
||||||
<!-- Fonts -->
|
<!-- Fonts -->
|
||||||
|
<script src="https://kit.fontawesome.com/9235d1c676.js" crossorigin="anonymous"></script>
|
||||||
<!-- Styles -->
|
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
|
|
||||||
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
|
|
||||||
@yield('stylesheets')
|
@yield('stylesheets')
|
||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="has-navbar-fixed-top">
|
||||||
<div id="app">
|
<div id="root">
|
||||||
<nav-bar>
|
<comunes-nav-bar>
|
||||||
<template slot="subpedido">{{ session('subpedido_nombre') ? 'Pedido de '. session('subpedido_nombre') : Auth::user()->name }}</template>
|
<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="gdc">{{ session('subpedido_nombre') ? Auth::user()->name : "" }}</template>
|
||||||
<template slot="logout-form">
|
<template slot="logout-form">
|
||||||
|
@ -28,23 +27,16 @@
|
||||||
@csrf
|
@csrf
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
</nav-bar>
|
</comunes-nav-bar>
|
||||||
<nav-migas inline-template>
|
<pedidos-nav-migas></pedidos-nav-migas>
|
||||||
<nav class="breadcrumb is-centered has-background-danger-light" aria-label="breadcrumbs" v-show="visible">
|
|
||||||
<ul>
|
|
||||||
<li v-for="(miga, i) in migas" v-bind:class="i==migas.length-1 ? 'is-active' : ''"><a :href="miga.href" v-text="miga.nombre"></a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</nav-migas>
|
|
||||||
|
|
||||||
<main class="py-4">
|
<main id="main" class="py-4 has-top-padding">
|
||||||
|
<pedidos-cartel-pedido-aprobado></pedidos-cartel-pedido-aprobado>
|
||||||
@yield('content')
|
@yield('content')
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
|
<script src="{{ mix('js/app.js') }}" defer></script>
|
||||||
<script src="{{ asset('js/app.js') }}" defer></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
|
||||||
@yield('scripts')
|
@yield('scripts')
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
44
resources/views/pdfgen/subpedido_tabla.blade.php
Normal file
44
resources/views/pdfgen/subpedido_tabla.blade.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<!doctype html>
|
||||||
|
<style>
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background: #CCC
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<h3>{{$subpedido->nombre}}</h3>
|
||||||
|
|
||||||
|
<table style="width: 100%">
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Producto
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Cantidad
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Control 1
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Control 2
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Control 3
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
@foreach($subpedido->productos as $producto)
|
||||||
|
@if(!$producto->bono)
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ $producto->nombre }}
|
||||||
|
@if($producto->pivot->notas)
|
||||||
|
<br /><b>Talle/Color:</b> {{ $producto->pivot->notas }}
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td style="text-align: center">
|
||||||
|
{{ $producto->pivot->cantidad }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
</table>
|
|
@ -1,15 +1,6 @@
|
||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('stylesheets')
|
|
||||||
<link rel="stylesheet" href="{{ asset('css/productos.css') }}">
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<categorias-container></categorias-container>
|
<pedidos-body></pedidos-body>
|
||||||
<productos-container></productos-container>
|
<pedidos-devoluciones-modal></pedidos-devoluciones-modal>
|
||||||
<producto-container></producto-container>
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
@section('scripts')
|
|
||||||
<script src="{{ asset('js/productos.js') }}"></script>
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
@ -4,38 +4,15 @@
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div id="root" class="container">
|
<div id="root" class="container">
|
||||||
<h1 class="title">
|
<h1 class="title">
|
||||||
Compras MPS
|
Pedidos MPS
|
||||||
</h1>
|
</h1>
|
||||||
<p class="subtitle">
|
<p class="subtitle">
|
||||||
Bienvenidx a la aplicación de compras del <strong>Mercado Popular de Subsistencia</strong>
|
Bienvenidx a la aplicación de pedidos del <strong>Mercado Popular de Subsistencia</strong>
|
||||||
</p>
|
</p>
|
||||||
<subpedido-select inline-template gdcid="{{Auth::user()->grupoDeCompra->id}}">
|
<pedidos-subpedido-select gdcid="{{Auth::user()->grupoDeCompra->id}}"></pedidos-subpedido-select>
|
||||||
<div class="block">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Escribí el nombre de tu familia o grupo de convivencia</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" @input="onType" v-model="subpedido"></input>
|
|
||||||
</div>
|
|
||||||
<p class="help">Intentá que sea claro como para que tus compas del barrio te identifiquen.</p>
|
|
||||||
</div>
|
|
||||||
<div class="buttons">
|
|
||||||
<button class="button is-primary" @click="submit">Crear nuevo pedido</button>
|
|
||||||
</div>
|
|
||||||
<div class="table-container">
|
|
||||||
<table class="table is-hoverable">
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="subpedidoExistente in subpedidosExistentes" >
|
|
||||||
<td><a v-text="subpedidoExistente.nombre" :href="subpedidoExistente.nombre"></a></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</subpedido-select>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('scripts')
|
@section('scripts')
|
||||||
<script src="{{ asset('js/subpedidos-create.js') }}"></script>
|
|
||||||
@endsection
|
@endsection
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue