Compare commits

...
Sign in to create a new pull request.

145 commits

Author SHA1 Message Date
Vincent BRIDIER
107bb774f6
Merge branch 'trunk' 2025-09-23 12:38:15 +02:00
Vincent BRIDIER
24d473ddab
Merge branch 'develop' into trunk 2025-09-23 12:38:07 +02:00
Vincent BRIDIER
94435d9820
feat: Remove 'OGM' occurences - Close #9 2025-09-23 12:37:52 +02:00
Vincent BRIDIER
e5fdd3d345
Merge branch 'trunk' 2024-10-04 17:13:22 +02:00
Vincent BRIDIER
e5dc2b82b5
Merge branch 'develop' into trunk 2024-10-04 17:13:12 +02:00
Vincent BRIDIER
7efde8651f
feat: Update terre action link in modal 2024-10-04 17:12:58 +02:00
Vincent BRIDIER
3255a089c4
Merge branch 'trunk' 2024-08-08 14:09:42 +02:00
Vincent BRIDIER
3f82a92221
Merge branch 'develop' into trunk 2024-08-08 14:09:34 +02:00
Vincent BRIDIER
af8a18bb8f Merge branch 'issue-8-hide-modal' into 'develop'
feat: #8 - Added date to conditionnaly display/hide modal 'Terre d'action'

See merge request ldc-web/projets/tracabilite-loue!17
2024-08-08 12:08:41 +00:00
Noah Lancien
6aca5dd3d7 feat: #8 - Added date to conditionnaly display/hide modal 'Terre d'action' 2024-08-07 17:32:52 +02:00
Vincent BRIDIER
64c8583dfc
Merge branch 'trunk' 2024-05-21 10:37:11 +02:00
Vincent BRIDIER
1c292023ea
Merge branch 'develop' into trunk 2024-05-21 10:36:57 +02:00
Vincent BRIDIER
cfe7e1a778
Close #7 - Update Terre Action Link 2024-05-21 10:36:42 +02:00
Vincent BRIDIER
e07780af67
Merge branch 'trunk' 2023-06-26 15:07:26 +02:00
Vincent BRIDIER
729127fa28
Merge branch 'develop' into trunk 2023-06-26 15:06:13 +02:00
Vincent BRIDIER
ede3aa889c Merge branch 'ts-1496' into 'develop'
Change picture field name to fix

See merge request ldc-web/projets/tracabilite-loue!16
2023-06-22 13:10:30 +00:00
Sebastien Hattry
d640238082 Change picture field name to fix 2023-05-26 13:37:53 +00:00
Vincent BRIDIER
3a49f0543c
Merge branch 'trunk' 2023-04-03 11:49:43 +02:00
Vincent BRIDIER
43a89a03a0
Merge branch 'develop' into trunk 2023-04-03 11:47:52 +02:00
Vincent BRIDIER
9e3cde59dd Merge branch 'issue-3' into 'develop'
Fixes #3 add missing abort 404 on route fallback

Closes #3

See merge request ldc-web/projets/tracabilite-loue!15
2023-03-31 07:44:50 +00:00
Vincent BRIDIER
a549f5a8a5 update composer json 2023-03-31 07:44:27 +00:00
Jérémy Munsch
ddb9c3fd59
Update gitlab CI + nuw gitlab on CI 2023-03-23 16:53:17 +01:00
Jérémy Munsch
c2d70b841a
Fix BS compilation 2023-03-23 16:46:17 +01:00
Jérémy Munsch
f5e5bc2362
Fixes #3 add missing abort 404 on route fallback 2023-03-23 16:45:55 +01:00
Jérémy Munsch
c43f5220e1
npm update 2023-03-23 16:23:40 +01:00
Vincent BRIDIER
608d420062 add issue templates 2023-03-15 15:52:41 +00:00
Vincent BRIDIER
bebb0b5026
Merge branch 'trunk' 2023-01-02 09:20:09 +01:00
Vincent BRIDIER
c56aed023d
Merge branch 'develop' into trunk 2023-01-02 09:20:01 +01:00
Vincent BRIDIER
ccf2fde716 Merge branch 'slowDownCafelCalls' into 'develop'
Slow down cafel calls

See merge request service-web-lab/tracabilite-loue!14
2023-01-02 08:19:14 +00:00
Jérémy Munsch
55012e8985
Slow down cafel calls 2022-12-22 17:00:08 +01:00
Vincent BRIDIER
714df38f4b
Merge branch 'trunk' 2022-12-13 13:34:51 +01:00
Vincent BRIDIER
f58b55efbf
Merge branch 'develop' into trunk 2022-12-13 13:34:08 +01:00
Vincent BRIDIER
7fd2068e77
Remove Kwaadpepper Handler + Add Sentry Handler 2022-12-13 13:30:00 +01:00
Vincent BRIDIER
61acab1cb1 Merge branch 'postProdFixes' into 'develop'
Post prod fixes

See merge request service-web-lab/tracabilite-loue!13
2022-12-12 12:36:30 +00:00
Jérémy Munsch
39f95895ae
update gitlab CI 2022-12-12 12:57:07 +01:00
Jérémy Munsch
791862202f
Plus d'intervalles sur Cafel API call 2022-12-12 12:53:59 +01:00
Jérémy Munsch
ac26d7ff21
Fix tracea eggs when no matching visuel libelle EAN13 2022-12-12 12:51:08 +01:00
Jérémy Munsch
c21919e141
Fix cafel egg breeder model events 2022-12-12 11:15:38 +01:00
Jérémy Munsch
0e3f299228
phpcbf 2022-12-12 11:15:21 +01:00
Jérémy Munsch
a458e5fd75
Fix RemoveOldXmlEntries Job 2022-12-12 09:12:15 +01:00
Vincent BRIDIER
7d60149a45
Merge branch 'trunk' 2022-12-07 14:55:57 +01:00
Vincent BRIDIER
47d2f3e5a7
Merge branch 'develop' into trunk 2022-12-07 14:55:43 +01:00
Vincent BRIDIER
c7bc5713f5
Add egg domain to trusted hosts 2022-12-07 14:30:11 +01:00
Vincent BRIDIER
a3b97ff5be
Remove useless app config 2022-12-07 14:29:50 +01:00
Vincent BRIDIER
46f75d216d
Fix auth.json gitignore 2022-12-07 14:29:29 +01:00
Vincent BRIDIER
4affcef601
Merge branch 'trunk' 2022-12-06 12:59:06 +01:00
Vincent BRIDIER
bd8da4cbe7
Merge branch 'develop' into trunk 2022-12-06 12:58:47 +01:00
Vincent BRIDIER
5f6236e24e
Handle bo.gt redirect 2022-12-06 12:58:18 +01:00
Vincent BRIDIER
e7c5480fc8
Fix redirect egg 2022-12-06 12:57:49 +01:00
Vincent BRIDIER
db03f10b97
Merge branch 'trunk' 2022-12-06 11:52:18 +01:00
Vincent BRIDIER
e38c3b4e4c
Merge branch 'develop' into trunk 2022-12-06 11:52:04 +01:00
Vincent BRIDIER
82202efb3c Merge branch 'handleGuzzleConnectionErrorForCafel' into 'develop'
HandleGuzzleConnectionErrors on Cafel fucking calls

See merge request service-web-lab/tracabilite-loue!12
2022-12-06 10:50:34 +00:00
Jérémy Munsch
e6a3f429e9
HandleGuzzleConnectionErrors on Cafel fucking calls 2022-12-06 11:03:38 +01:00
Vincent BRIDIER
c79d0d5137
Merge branch 'develop' into trunk 2022-12-06 10:37:13 +01:00
Vincent BRIDIER
4bb31c7aa4 Merge branch 'fixCafelTempFileReleaseFileDescriptor' into 'develop'
Fix file descriptor overload on fetch Cafel Breeders pictures

See merge request service-web-lab/tracabilite-loue!11
2022-12-06 09:27:53 +00:00
Jérémy Munsch
b94ca32ebc
Fix file descriptor overload on fetch Cafel Breeders pictures 2022-12-06 10:24:00 +01:00
Vincent BRIDIER
9cba6891ef
Merge branch 'develop' into trunk 2022-12-05 18:25:29 +01:00
Vincent BRIDIER
f8a6bf967b
Ticket 1192 OK 2022-12-05 18:17:50 +01:00
Vincent BRIDIER
6616aeb0c1
Ticket 1187 2022-12-05 17:28:06 +01:00
Vincent BRIDIER
13f7e2ee02
TMP : Ticket 1192 2022-11-23 16:12:07 +01:00
Vincent BRIDIER
ab9cee6303
Fix typo 2022-11-23 14:35:48 +01:00
Vincent BRIDIER
869b45ca8a
ticket 1186 2022-11-23 14:31:20 +01:00
Vincent BRIDIER
ced27969bc
ticket 1190 2022-11-23 13:07:45 +01:00
Vincent BRIDIER
87e694895a
ticket 1189 2022-11-23 12:44:41 +01:00
Vincent BRIDIER
721e700ec2
ticket 1188 2022-11-23 12:41:53 +01:00
Vincent BRIDIER
2b131028bd
ticket 1185 2022-11-23 12:36:56 +01:00
Vincent BRIDIER
f903fc97d7
Update package-lock 2022-11-23 12:03:49 +01:00
Jérémy Munsch
20e4084361
Merge branch 'develop' into trunk 2022-10-28 15:17:09 +02:00
Jérémy Munsch
6061721fa5
Tracea poultry label fix 2022-10-28 15:16:53 +02:00
Jérémy Munsch
c3a1a38cfe
Improve xml reports 2022-10-28 15:16:09 +02:00
Jérémy Munsch
21aeaa34c0
Merge branch 'develop' into trunk 2022-10-28 10:03:58 +02:00
Jérémy Munsch
5c618533a2
Check breeder is available 2022-10-28 10:03:22 +02:00
Jérémy Munsch
cd0f1fdd76
Merge branch 'develop' into trunk 2022-10-27 18:16:27 +02:00
Jérémy Munsch
9ea02d5bda
remove report from cafel breeder jobs 2022-10-27 18:16:17 +02:00
Jérémy Munsch
ba4aa3e001
Merge branch 'develop' into trunk 2022-10-27 18:13:55 +02:00
Jérémy Munsch
baba0fc4aa
Fix Cafel jobs for handling model event validation errors 2022-10-27 18:13:45 +02:00
Jérémy Munsch
c2a06ec828
Fix tracea Egg search request 2022-10-27 17:56:59 +02:00
Jérémy Munsch
e0aefec165
Merge branch 'develop' into trunk 2022-10-27 17:36:55 +02:00
Jérémy Munsch
4ee7a0749e
Mises en Places inuav becomes arrays 2022-10-27 17:36:46 +02:00
Jérémy Munsch
9db75e5b22
Evolution make sure Cafel Calls validation error return incriminated Data. 2022-10-27 17:12:30 +02:00
Jérémy Munsch
35aab2df1f
For Egg And Poultry Breeders
Truncate breeders before importing from Cafel
Trigger Breeder destroy event to remove pictures
2022-10-27 16:15:11 +02:00
Jérémy Munsch
cb6778adb3
Validate Ede_Code as unique on Egg breeder Cafel
Add unique on Egg breeders code ede
2022-10-27 16:15:10 +02:00
Jérémy Munsch
f0a7869052
Configuration Cafel food_retain doit etre egale a mises_en_places_retain pour assuer la traca volaille sur cette periode 2022-10-27 15:23:04 +02:00
Jérémy Munsch
f0c046ad6b
Evolution output failed validation data 2022-10-27 14:42:18 +02:00
Jérémy Munsch
746cc4d8eb
Merge branch 'develop' into trunk 2022-10-27 14:10:46 +02:00
Jérémy Munsch
e2b6aa1d30
Get in message inuav code failling unique assertion 2022-10-27 14:10:13 +02:00
Jérémy Munsch
c9c7752d2f
Merge branch 'develop' into trunk 2022-10-27 12:25:42 +02:00
Jérémy Munsch
303b80fab2
Fix CafelClientModel validateData now static 2022-10-27 12:25:05 +02:00
Jérémy Munsch
c05c124a5d
Fixed Batch codel validation regex 2022-10-27 12:24:43 +02:00
Jérémy Munsch
1266665702
Merge branch 'develop' into trunk 2022-10-27 12:15:33 +02:00
Jérémy Munsch
a88f61686f
Change Cafel Api regex validation for Batch Code allow batch_code like 30639/A 2022-10-27 12:14:58 +02:00
Jérémy Munsch
60159fdfb9
Merge branch 'develop' into trunk 2022-10-21 11:27:19 +02:00
Jérémy Munsch
85d7015157
speedup egg search 2022-10-21 11:20:26 +02:00
Jérémy Munsch
df4f4d360e
Merge branch 'develop' into trunk 2022-10-21 10:24:28 +02:00
Jérémy Munsch
96b231f7d9 Merge branch 'fixFuckingProjectSign' into 'develop'
Poultries Ajax call to fetch distribution info

See merge request service-web-lab/tracabilite-loue!9
2022-10-21 08:20:48 +00:00
Jérémy Munsch
98767fdfa2 Merge branch 'miseEnPlace' into 'develop'
Fix poultry cafel mise en place

See merge request service-web-lab/tracabilite-loue!10
2022-10-21 08:20:12 +00:00
Jérémy Munsch
deec0ea4d7
Fix Distribution date Localisation 2022-10-21 10:19:22 +02:00
Jérémy Munsch
97d546effa
Fix poultry cafel mise en place 2022-10-21 10:10:38 +02:00
Jérémy Munsch
d31d2c26e2
Add cafel presta API description file document 2022-10-21 09:27:41 +02:00
Jérémy Munsch
4490eddfb5 Merge branch 'issue1' into 'develop'
Solves #1

See merge request service-web-lab/tracabilite-loue!8
2022-10-20 16:40:10 +00:00
Jérémy Munsch
8580e4c9d4
Poultries Ajax call to fetch distribution info 2022-10-20 18:37:12 +02:00
Jérémy Munsch
8ee2127b74
Solves #1
clean up web-status-checker-laravel implementation
add xml:clean_olds to kernel
2022-10-20 16:23:14 +02:00
Vincent BRIDIER
3994a8df68 Merge branch 'mepvolailles' into 'develop'
Ajout Mep poultry + gestion sur poultry si on ne dispose pas de food ou mise en place poultry

See merge request service-web-lab/tracabilite-loue!7
2022-10-11 15:09:07 +00:00
Vincent BRIDIER
13859604e1 Merge branch 'ticket1082' into 'develop'
Added https://www.googletagmanager.com to Secure Middleware

See merge request service-web-lab/tracabilite-loue!6
2022-10-04 09:17:18 +00:00
Jérémy Munsch
bba427242c
Ajout Mep poultry + gestion sur poultry si on ne dispose pas de food ou mise en place poultry 2022-09-30 17:59:29 +02:00
Jérémy Munsch
d538d98bd3
Added https://www.googletagmanager.com to Secure Middleware
Fixed CSP
2022-09-29 13:33:04 +02:00
Jérémy Munsch
0c96300587
Merge branch 'develop' into trunk 2022-09-16 11:07:50 +02:00
Jérémy Munsch
a43a840d4b
Add missing exp_date usage on poultry front 2022-09-16 11:05:48 +02:00
Jérémy Munsch
649c7b08c1
fix composer json de triphasé 2022-09-15 17:32:45 +02:00
Jérémy Munsch
8a56fa108e
Merge branch 'develop' into trunk 2022-09-15 15:00:53 +02:00
Jérémy Munsch
504bde8f2d
Add missing admin-secure to route middleware 2022-09-15 14:59:39 +02:00
Jérémy Munsch
f19111bcf6 Merge branch 'add_date_emballage' into 'develop'
Add date emballage

See merge request service-web-lab/tracabilite-loue!4
2022-09-15 12:39:21 +00:00
Jérémy Munsch
1f40699a7b Merge branch 'add_bea_images_fondation_logo' into 'develop'
add some missing images

See merge request service-web-lab/tracabilite-loue!5
2022-09-15 12:24:58 +00:00
Olivier Legat
ba2e8c76a8
add some missing images 2022-09-07 11:08:19 +02:00
Olivier Legat
a9c2b57bc0 Merge branch 'ticket987' into 'develop'
Ticket987

See merge request service-web-lab/tracabilite-loue!3
2022-09-07 08:07:44 +00:00
Jérémy Munsch
d2f43a8bdd
Readd packing_date (date_emballage) 2022-08-31 16:31:26 +02:00
Jérémy Munsch
7967841935
update vscode settings 2022-08-31 15:58:24 +02:00
Jérémy Munsch
c4031c62e5
settings JSON update 2022-08-29 18:50:56 +02:00
Jérémy Munsch
bb116a0327
Fix progressbar files count on xArcadiaFlux 2022-08-29 18:50:42 +02:00
Vincent BRIDIER
1e9dbc7c06
remove useless .env var 2022-08-11 09:28:20 +02:00
Vincent BRIDIER
3f08a381ff
Merge branch 'develop' into trunk 2022-08-11 09:24:16 +02:00
Vincent BRIDIER
74799c2b1d Merge branch 'todos' into 'develop'
Todos

See merge request service-web-lab/tracabilite-loue!2
2022-08-11 07:22:30 +00:00
Jérémy Munsch
0511f31a01 Todos 2022-08-11 07:22:30 +00:00
Vincent BRIDIER
2a1d095e71
Merge branch 'develop' into trunk 2022-08-09 16:28:16 +02:00
Vincent BRIDIER
3ee5da13eb
PHPCS + Todos README 2022-08-09 16:25:29 +02:00
Vincent BRIDIER
104b9e6bd1
Add CI + ldcweb packages + sitemap 2022-08-09 16:22:02 +02:00
Vincent BRIDIER
7e17d0949d Merge branch 'retours' into 'develop'
Retours

See merge request service-web-lab/tracabilite-loue!1
2022-08-09 14:05:38 +00:00
Jérémy Munsch
24173c615c Retours 2022-08-09 14:05:38 +00:00
Vincent BRIDIER
c13ef55eee
Merge branch 'develop' into trunk 2022-07-11 10:05:22 +02:00
Vincent BRIDIER
c6a4f35202 update from master 2022-07-08 12:45:58 +02:00
Vincent BRIDIER
febbc9ae81 Merge remote-tracking branch 'origin/master' into evoTracabilite 2022-07-08 12:04:56 +02:00
Vincent BRIDIER
45e2838dc4
Update traca poultry job 2022-07-05 10:27:07 +02:00
Vincent BRIDIER
ea7ca7e6e2
Update traca 2022-07-05 09:35:04 +02:00
Vincent BRIDIER
3fa93f9b42
Add popec code 2022-07-04 11:23:48 +02:00
Olivier Legat
fa1f25a9ec Merge branch 'trunk' 2022-04-05 12:10:15 +02:00
Olivier Legat
94ea211484 Merge branch 'develop' into trunk 2022-04-05 11:58:31 +02:00
Alexis Gousseau
7f653f537e fix img popin sweetalert 2022-04-05 11:56:47 +02:00
Olivier Legat
54d16caf0f Merge branch 'develop' into trunk 2022-04-05 11:47:21 +02:00
Olivier Legat
ecbbf786f8 Merge remote-tracking branch 'origin/fix_sweetalert' into develop 2022-04-05 11:47:02 +02:00
Alexis Gousseau
1df9b8190d fix sweetalert popin 2022-04-05 11:43:36 +02:00
Olivier Legat
8028ad3f93 Merge branch 'develop' into trunk 2022-04-04 15:52:53 +02:00
Olivier Legat
8451effec2 Merge remote-tracking branch 'origin/sweetalert' into develop 2022-04-04 15:52:20 +02:00
Alexis Gousseau
16aadf2e05 add sweetalert 2022-04-04 15:11:48 +02:00
Olivier Legat
1c1301b8db Merge branch 'develop' into trunk 2022-03-03 16:44:20 +01:00
Vincent BRIDIER
c6cf95d68a Merge branch 'develop' into trunk 2022-01-17 18:46:06 +01:00
190 changed files with 10184 additions and 4783 deletions

View file

@ -3,6 +3,7 @@ APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_DOMAIN=
LOG_CHANNEL=stack
@ -37,4 +38,16 @@ CAFEL_PASSWORD=
MAIL_INVALID_CAFEL_DATA=equipe.web@ldc.fr
MAIL_INVALID_CAFEL_DATA_IMAGE=equipe.web@ldc.fr
MAIL_INVALID_XML=equipe.web@ldc.fr
MAIL_LIST_SERVICEWEB=equipe.web@ldc.fr
## LDC WEB STATUS CHECKER
LDC_STATUS_CHECKER_API_KEY="MY_KEY"
LDC_STATUS_CHECKER_UUID="UUID_PROJECT"
## LDC WEB ADMIN SECURE
LDC_WEB_API_KEY="MY_KEY"
## GOOGLE
ANALYTICS_GOOGLE_ID=
# TERRE D'ACTION MODAL (YYYY-MM-DD)
TERRE_ACTION_START_DATE=
TERRE_ACTION_END_DATE=

1
.gitignore vendored
View file

@ -18,3 +18,4 @@ Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
auth.json

12
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,12 @@
include:
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'lint/lint7.3.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'security/security7.3.yml'
stages:
- lint
- security

View file

@ -0,0 +1,11 @@
## Comportement constaté
## Comportement attendu
## Solution envisagée
## Étapes pour la reproduction du bug
*

View file

@ -0,0 +1,7 @@
## Présentation du besoin
## Implémentation
## Maquettes, documentations...

View file

@ -16,7 +16,8 @@
"titleBar.activeBackground": "#02514e",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#02514e99",
"titleBar.inactiveForeground": "#e7e7e799"
"titleBar.inactiveForeground": "#e7e7e799",
"commandCenter.border": "#e7e7e799"
},
"peacock.remoteColor": "#02514e"
}

View file

@ -1,6 +1,7 @@
# BOGT / Tracabilité Loué
## Installation
```
composer install
cp .env.example .env
@ -9,54 +10,71 @@ php artisan storage:link
```
Éditez le fichier .env puis :
```
php artisan migrate
```
Front :
```
npm install && npm run prod
```
Pour créer un compte admin :
```
php artisan user:create
```
## Commande à mettre dans la crontab toutes les minutes en PROD
## Crons
Put every min in PROD :
```
php artisan schedule:run
```
## Données
Pour synchroniser les aliments :
```
php artisan cafel:food
```
Pour synchroniser les données BEA (volaille) :
```
php artisan cafel:bea_vol
```
Pour synchroniser les mises en place (oeuf) :
```
php artisan cafel:mep_egg
```
Pour synchroniser les éleveurs (oeuf) :
```
php artisan cafel:breeder_egg
```
Pour synchroniser les éleveurs (volaille) :
```
php artisan cafel:breeder_poultry
```
Pour importer les XML (oeuf) :
```
php artisan xml:egg
```
Pour importer les XML (volaille) :
```
php artisan xml:poultry
```
## TODO
- Mettre analytics dans cookie notice
- Cacher la colonne Code EDE dans le cas du filtrage sur volaille (/bo/eleveurs)

View file

@ -0,0 +1,38 @@
<?php
namespace App\Console\Commands;
use App\Jobs\CafelMepPoultryJob;
use Illuminate\Console\Command;
class CafelPoultryMepCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'cafel:mep_poultry';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Syncronisation manuelle Cafel (/api/ldc/mepvolailles)';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Running the Job, this could take some time.');
$job = new CafelMepPoultryJob();
$job->handle();
$this->info('Fin du traitement');
}
}

View file

@ -1,79 +0,0 @@
<?php
namespace App\Console\Commands;
/*
* Command Name: LDC Web Status Checker
* Description: Envoi des infos Serveur/Laravel vers LDC Web
* Version: 1.0
* Author: Vincent BRIDIER
* Author URI: vincent.bridier@ldc.fr
*/
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
class LDCWebStatusChecker extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ldcweb:statuschecker';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Envoi des infos Serveur/Laravel vers LDC Web';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$data = [];
$data['uuid'] = config('app.project_uuid');
// Sitemap.xml ?
if (file_exists(public_path() . '/sitemap.xml')) {
$data['sitemap'] = 'true';
} else {
$data['sitemap'] = 'false';
}
// Robots.txt ?
if (file_exists(public_path() . '/robots.txt')) {
$data['robots'] = 'true';
} else {
$data['robots'] = 'false';
}
// Check logs .
$data['logs'] = 0;
$patherror = storage_path('/logs');
$files = glob($patherror . '/laravel-*.log');
if (count($files) > 0) {
foreach ($files as $file) {
$data['logs'] = $data['logs'] + filesize($file);
}
}
$data['code']['version'] = app()->version();
try {
Http::post(
'https://ldc-web.s-web.test/api/status-checker',
['token' => 'CE8S52Qq7stf5k8frF4T', 'datas' => json_encode($data)]
);
} catch (\Exception $e) {
report($e);
}
return 0;
}
}

View file

@ -30,14 +30,15 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule)
{
// * Jobs cafel sur queue cafel
$schedule->job(CafelFoodJob::class, 'cafel')->onOneServer()->everyThreeHours();
$schedule->job(CafelBeaVolJob::class, 'cafel')->onOneServer()->everyThreeHours();
$schedule->job(CafelMepEggJob::class, 'cafel')->onOneServer()->everyThreeHours();
$schedule->job(CafelEggBreederJob::class, 'cafel')->onOneServer()->everyThreeHours();
$schedule->job(CafelPoultryBreederJob::class, 'cafel')->onOneServer()->everyThreeHours();
$schedule->job(CafelFoodJob::class, 'cafel')->onOneServer()->dailyAt('22h30');
$schedule->job(CafelBeaVolJob::class, 'cafel')->onOneServer()->dailyAt('22h45');
$schedule->job(CafelMepEggJob::class, 'cafel')->onOneServer()->dailyAt('23h00');
$schedule->job(CafelEggBreederJob::class, 'cafel')->onOneServer()->dailyAt('23h15');
$schedule->job(CafelPoultryBreederJob::class, 'cafel')->onOneServer()->dailyAt('23h30');
$schedule->job(XmlFluxEggJob::class, 'tracea')->onOneServer()->dailyAt('02:00');
$schedule->job(XmlFluxPoultryJob::class, 'tracea')->onOneServer()->dailyAt('22:00');
$schedule->command('xml:clean_olds')->onOneServer()->dailyAt('06:00');
}
/**

View file

@ -4,8 +4,8 @@ namespace App\Exceptions;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Throwable;
use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Sentry\Laravel\Integration;
class Handler extends ExceptionHandler
{
@ -27,18 +27,19 @@ class Handler extends ExceptionHandler
];
/**
* Report or log an exception.
* Register the exception handling callbacks for the application.
*
* @param \Throwable $exception
* @return void
*/
public function report(Throwable $exception)
public function register()
{
if ($exception instanceof CafelApiImageFetchException) {
$this->reportCafelApiImageErrorException($exception);
return;
}
parent::report($exception);
$this->reportable(function (\Throwable $e) {
if ($e instanceof CafelApiImageFetchException) {
$this->reportCafelApiImageErrorException($e);
return;
}
Integration::captureUnhandledException($e);
});
}
/**

View file

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Fo;
use App\Enums\BreederCivilty;
use App\Http\Controllers\Controller;
use App\Models\CafelEggBreeder;
use App\Models\CafelEggMiseEnPlace;
@ -13,6 +14,11 @@ use Carbon\Carbon;
class EggController extends Controller
{
/**
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse
*/
public function findMyBox(Request $request)
{
$code = $request->code;
@ -21,7 +27,7 @@ class EggController extends Controller
$month = $date[1];
$moba = TraceaEgg::where('ede_code', '=', $code)
->whereDate('dcr', Carbon::createFromDate(null, $month, $day)->toDate())
->where('dcr', Carbon::createFromDate(null, $month, $day)->format('Y-m-d'))
->orderBy('box_code', 'DESC')->first();
if (!$moba) {
@ -31,6 +37,11 @@ class EggController extends Controller
return redirect()->route('egg.FrontPage', ['id' => $moba->box_code]);
}
/**
*
* @param mixed $id
* @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function indexId($id)
{
$multiple = false;
@ -38,7 +49,7 @@ class EggController extends Controller
$mobas = TraceaEgg::where('box_code', $id)->orderBy('box_code', 'DESC')->first();
if (!$mobas) {
return redirect('/search');
return redirect()->route('egg.search');
}
$edes = $mobas->code_edes;
@ -54,18 +65,18 @@ class EggController extends Controller
}
$datePonte = $mobas->laying_date;
$date_emballage = $mobas->date_emballage;
$date_emballage = $mobas->packing_date;
// * Compare DCR et Today
$date_dcr = $mobas->dcr;
$date_today = Carbon::now();
$dcrGood = $date_today < $date_dcr;
$eleveurs = CafelEggBreeder::whereIn('ede_code', $edes)->get();
if (!$eleveurs->count()) {
return view('front.egg.notFound');
}
$uniqueEleveurs = $eleveurs->unique('code_ede');
if (count($uniqueEleveurs) > 1) {
$multiple = true;
@ -73,8 +84,11 @@ class EggController extends Controller
$inuavs = $eleveurs->pluck('inuavs')->flatten(1);
$mep = CafelEggMiseEnPlace::whereIn('inuav_code', $inuavs)
->where('transfert_date', '<', $datePonte)
$mep = CafelEggMiseEnPlace::query();
collect($inuavs)->map(function ($inuav) use ($mep) {
$mep->where('inuav_codes', 'LIKE', "%{$inuav}%");
});
$mep = $mep->where('transfert_date', '<', $datePonte)
->orderBy('transfert_date', 'DESC')->first();
if ($mep) {
@ -99,7 +113,13 @@ class EggController extends Controller
]);
}
public function EleveurPortrait($id, $eleveurId)
/**
*
* @param mixed $id
* @param mixed $eleveurId
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function eleveurPortrait($id, $eleveurId)
{
$dateToday = Carbon::now()->toArray();
$currentYear = $dateToday['year'];
@ -127,16 +147,16 @@ class EggController extends Controller
}
if ($eleveur->vocation != null && !empty($eleveur->vocation)) {
switch ($eleveur->civilite) {
case '0':
case BreederCivilty::elle():
array_push($questions, array('Tu as toujours été éleveuse ?', $eleveur->vocation));
break;
case '1':
case BreederCivilty::il():
array_push($questions, array('Tu as toujours été éleveur ?', $eleveur->vocation));
break;
case '2':
case BreederCivilty::elles():
array_push($questions, array('Vous avez toujours été éleveuses ?', $eleveur->vocation));
break;
case '3':
case BreederCivilty::ils():
array_push($questions, array('Vous avez toujours été éleveurs ?', $eleveur->vocation));
break;
default:

View file

@ -3,11 +3,14 @@
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\Ajax\GetDistributionRequest;
use App\Http\Requests\Fo\SearchPoultryTraceaRequest;
use App\Models\TraceaPoultry;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
class TraceaPoultryController extends Controller
{
@ -22,15 +25,21 @@ class TraceaPoultryController extends Controller
/** @var \App\Models\TraceaPoultry|null */
$traceaPoultry = TraceaPoultry::whereExternalCodeLot($request->codelot)
->where(function ($q) use ($request) {
$q->whereBarcodePrice($request->eancode)
->orWhere('barcode_weight', $request->eancode);
if ($request->eancode[0] === "0") {
$code = Str::substr($request->eancode, 1);
$q->where('barcode_price', 'LIKE', Str::limit($code, 6, '') . '%')
->orWhere('barcode_weight', 'LIKE', Str::limit($code, 6, '') . '%');
} else {
$q->where('barcode_price', 'LIKE', Str::limit($request->eancode, 7, '') . '%')
->orWhere('barcode_weight', 'LIKE', Str::limit($request->eancode, 7, '') . '%');
}
})->first();
if (!$traceaPoultry) {
return back()->with(
'warning',
// phpcs:ignore Generic.Files.LineLength.TooLong
trans('Aucun produit n\'a pu être trouvé avec ces informations. Vérifiez bien les numéros saisis avec les indication comme sur l\'image ci-dessous.')
trans('Aucun produit n\'a pu être trouvé avec ces informations. Vérifiez bien les numéros saisis avec les indications comme sur l\'image ci-dessous.')
);
}
@ -61,14 +70,34 @@ class TraceaPoultryController extends Controller
\redirect()->route('poultry.search')->throwResponse();
}
$distributionPicture = $this->getDistributionPicture($traceaPoultry->sign);
// ! Test that breeder, food and mep are available
if (!$traceaPoultry->mep or !$traceaPoultry->food or !$traceaPoultry->breeder) {
return \view('front.poultry.unavailable');
}
return view('front.poultry.show', compact(
'traceaPoultry',
'distributionPicture'
'traceaPoultry'
));
}
/**
* Get Distribution Info
*
* @param \App\Http\Requests\Fo\Ajax\GetDistributionRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function getDistribution(GetDistributionRequest $request): JsonResponse
{
/** @var \App\Models\TraceaPoultry */
$traceaPoultry = TraceaPoultry::where('order_num', $request->ordernum)
->where('external_code_lot', $request->codelot)->firstOrFail();
return \response()->json([
'exp_date' => $traceaPoultry->exp_date,
'sign' => $traceaPoultry->sign,
'picture' => $this->getDistributionPicture($traceaPoultry->sign)
]);
}
/**
* Get distribution sign picture name if exists.
*

View file

@ -33,7 +33,7 @@ class Kernel extends HttpKernel
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,

View file

@ -44,7 +44,7 @@ class SecureHeaders
// phpcs:ignore Generic.Files.LineLength.TooLong
"style-src 'self' $cssDev https://fonts.googleapis.com;",
// phpcs:ignore Generic.Files.LineLength.TooLong
"script-src 'self' $jsDev https://fonts.googleapis.com;"
"script-src 'self' $jsDev https://fonts.googleapis.com https://www.googletagmanager.com;"
];
$response->headers->set(
'Content-Security-Policy',

View file

@ -14,6 +14,7 @@ class TrustHosts extends Middleware
public function hosts()
{
return [
\config('app.egg_domain'),
$this->allSubdomainsOfApplicationUrl(),
];
}

View file

@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Fo\Ajax;
use Illuminate\Foundation\Http\FormRequest;
class GetDistributionRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'codelot' => 'required|numeric|digits:10',
'ordernum' => 'required|numeric|digits:6',
];
}
}

View file

@ -1,103 +0,0 @@
<?php
namespace App\Http\Requests;
use App\Enums\BreederCivilty;
use App\Models\CafelEggBreeder;
use Illuminate\Foundation\Http\FormRequest;
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
class StoreEleveurRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
$this->merge([
'code_inuav' => explode(',', $this->code_inuav)
]);
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
// TODO: add validation rules using CAFEL API standards.
return [
'code_cafel' => 'nullable|string|min:1|max:100|unique:eleveurs,code_cafel',
'code_ede' => 'required|string|min:1|max:100|unique:eleveurs,code_ede',
'civilite' => ['required', new EnumIsValidRule(BreederCivilty::class)],
'nom' => 'required|string|min:1|max:255',
'prenom' => 'required|string|min:1|max:255',
'naissance' => 'required|date_format:Y-m-d',
'cp' => 'required|numeric|digits:5',
'ville' => 'required|string|min:1|max:255',
'elv_date_debut' => 'nullable|date_format:Y-m-d',
'code_inuav' => [
'array', function ($attribute, $value, $fail) {
if (\strlen(\json_encode($value)) > 255) {
$fail("$attribute : il n'\est pas possible de renseigner autant de code INUAV");
}
}
],
'code_inuav.*' => [
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if (strpos(trim($value), ' ') !== false) {
$fail(trans('Les espaces ne sont pas autorisés dans les codes INUAV'));
}
if (count($this->code_inuav) > count(array_unique($this->code_inuav))) {
$fail(trans('Les codes INUAVS doinvent être uniques'));
}
$q = CafelEggBreeder::query();
$q->where(function ($q) {
foreach ($this->code_inuav as $inuav) {
$q->orWhere('code_inuav', 'LIKE', "%$inuav%");
}
});
// * Update condition
if ($this->eleveur) {
$q->where('id', '!=', $this->eleveur->id);
}
if ($q->count()) {
$fail(trans('Les codes INUAVS doinvent être uniques'));
}
}
],
'exploitation' => 'nullable|string',
'aboutyou' => 'nullable|string',
'vocation' => 'nullable|string',
'likeinjob' => 'nullable|string',
'loue_means' => 'nullable|string',
'photo1' => 'sometimes|mimetypes:image/jpeg|max:1000',
'photo2' => 'sometimes|mimetypes:image/jpeg|max:1000',
'marquage_oeuf' => 'required|string|max:12',
'marquage_b1' => 'required|string|max:14',
'marquage_b2' => 'required|string|max:14',
];
}
/**
* Get the error messages for the defined validation rules.
*
* @return array
*/
public function messages()
{
return [
'photo1.max' => 'La Photo 1 ne doit pas dépasser 500 Ko',
'photo1.uploaded' => 'La Photo 1 ne doit pas dépasser 500 Ko',
'photo1.mimetypes' => 'La Photo 1 doit être un jpg',
'photo2.max' => 'La Photo 2 ne doit pas dépasser 500 Ko',
'photo2.mimetypes' => 'La Photo 2 doit être un jpg',
'photo2.uploaded' => 'La Photo 2 ne doit pas dépasser 500 Ko',
];
}
}

View file

@ -1,19 +0,0 @@
<?php
namespace App\Http\Requests;
class UpdateEleveurRequest extends StoreEleveurRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return \array_merge(parent::rules(), [
'code_cafel' => 'nullable|string|min:1|max:100|unique:eleveurs,code_cafel,' . $this->eleveur->id,
'code_ede' => 'required|string|min:1|max:100|unique:eleveurs,code_ede,' . $this->eleveur->id
]);
}
}

View file

@ -23,7 +23,7 @@ class RemoveOldXmlEntries
}
try {
$q = TraceaEgg::query();
$q->where('dcr', '<=', now()->subMonths(config('tracea.keepEggMonths', 12)))->destroy();
$q->where('dcr', '<=', now()->subMonths(config('tracea.keepEggMonths', 12)));
$q->delete();
} catch (\Exception $e) {
report($e);

View file

@ -323,7 +323,7 @@ abstract class XArcadiaFlux implements ShouldQueue
{
if ($this->console) {
$this->progressBar = $this->console->getOutput()
->createProgressBar($total - 1);
->createProgressBar($total);
$this->progressBar->start();
}
}

View file

@ -29,6 +29,7 @@ class XmlFluxEggJob extends XmlJobs
'code_boite' => 'box_code',
'code_ede' => 'ede_code',
'date_de_ponte' => 'laying_date',
'date_emballage' => 'packing_date',
'code_produit' => 'product_code',
'lib_produit' => 'product_label',
'lot_externe' => 'external_lot',

View file

@ -49,7 +49,8 @@ class XmlFluxPoultryJob extends XmlJobs
* @var array
*/
protected $nullableFields = [
'sign'
'barcode_price',
'barcode_weight'
];
/**

View file

@ -11,6 +11,7 @@ use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
abstract class XmlJobs extends XArcadiaFlux
{
@ -174,7 +175,25 @@ abstract class XmlJobs extends XArcadiaFlux
),
LogType::critical()
);
$this->sendInvalidXmlEmail($filename, $e);
$this->sendInvalidXmlEmail(
$filename,
$e->getPrevious()->getMessage(),
$e->getPrevious()
);
$this->moveToInvalid($filename);
continue;
} catch (ValidationException $e) {
$message = $e->getMessage();
$this->log(
sprintf(
'File %s could not be parsed => %s on line %d',
$filename,
$message,
$e->getLine()
),
LogType::critical()
);
$this->sendInvalidXmlEmail($filename, $e->getMessage(), $e);
$this->moveToInvalid($filename);
continue;
} catch (AbortXmlImportException $e) {
@ -187,6 +206,8 @@ abstract class XmlJobs extends XArcadiaFlux
),
LogType::error()
);
$this->sendInvalidXmlEmail($filename, $e->getMessage(), $e);
$this->moveToInvalid($filename);
report($e);
continue;
// * ignore the file
@ -365,12 +386,12 @@ abstract class XmlJobs extends XArcadiaFlux
* Send invalid XML email
*
* @param string $filename
* @param string $message
* @param \Throwable $e
* @return void
*/
protected function sendInvalidXmlEmail(string $filename, \Throwable $e)
protected function sendInvalidXmlEmail(string $filename, string $message, \Throwable $e)
{
$message = $e->getPrevious()->getMessage();
Mail::send('mails.invalidxml', [
'strError' => sprintf(
'File %s could not be parsed => %s on line %d',
@ -379,7 +400,7 @@ abstract class XmlJobs extends XArcadiaFlux
$e->getLine()
),
'filename' => $filename,
'exception' => $e->getPrevious()
'exception' => $e
], function ($m) {
$m->to(
config('mail.contactsList.invalidXml'),

View file

@ -5,7 +5,9 @@ namespace App\Jobs;
use App\Exceptions\CafelApiException;
use App\Exceptions\CafelApiImageFetchException;
use App\Lib\CafelApi\Models\CafelEggBreeder as ApiCafelEggBreeder;
use App\Lib\ToolboxHelper;
use App\Models\CafelEggBreeder;
use Illuminate\Validation\ValidationException;
class CafelEggBreederJob extends CafelJobs
{
@ -16,6 +18,16 @@ class CafelEggBreederJob extends CafelJobs
*/
public function handle()
{
// ! We have to remove all breeders to reimport them all in order to make sure
// ! to delete breeders that dont exists anymore (Api fetch constrain)
// ! vu avec Sylvain 27/10/2022
// * Destroy breeder one by one to trigger destroy event
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\CafelEggBreeder> */
$eggBreeders = CafelEggBreeder::all();
$eggBreeders->each(function (CafelEggBreeder $breeder) {
$breeder->deleteOrFail();
});
$this->syncWithCafelApi(
ApiCafelEggBreeder::class,
'cafel.ldc.eleveursoeufs',
@ -41,8 +53,23 @@ class CafelEggBreederJob extends CafelJobs
) : null;
} catch (CafelApiImageFetchException | CafelApiException $e) {
report($e);
$row['path_picture_1'] = null;
}
$validationException = ValidationException::withMessages([
'path_picture_1' => $e->getMessage()
]);
ToolboxHelper::useReflectionToUpdateProperty(
$validationException->validator,
'data',
['path_picture_1' => $row['path_picture_1']]
);
ToolboxHelper::useReflectionToUpdateProperty(
$validationException->validator,
'failedRules',
['path_picture_1' => $e->getMessage()]
);
throw $validationException;
}//end try
try {
$row['path_picture_2'] = !empty($row['path_picture_2']) ?
ApiCafelEggBreeder::fetchPicture(
@ -51,8 +78,23 @@ class CafelEggBreederJob extends CafelJobs
) : null;
} catch (CafelApiImageFetchException | CafelApiException $e) {
report($e);
$row['path_picture_2'] = null;
}
$validationException = ValidationException::withMessages([
'path_picture_2' => $e->getMessage()
]);
ToolboxHelper::useReflectionToUpdateProperty(
$validationException->validator,
'data',
['path_picture_2' => $row['path_picture_2']]
);
ToolboxHelper::useReflectionToUpdateProperty(
$validationException->validator,
'failedRules',
['path_picture_2' => $e->getMessage()]
);
throw $validationException;
}//end try
foreach (['exploitation', 'aboutyou', 'vocation', 'likeinjob', 'loue_means'] as $fieldName) {
$row[$fieldName] = !empty($row[$fieldName]) ? $row[$fieldName] : null;

View file

@ -3,6 +3,8 @@
namespace App\Jobs;
use App\Lib\CafelApi\Models\CafelClientModel;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
@ -70,12 +72,26 @@ abstract class CafelJobs implements ShouldQueue
try {
$saveCallback($row);
} catch (ValidationException $e) {
/** @var \Illuminate\Validation\Validator $validator */
$validator = $e->validator;
/** @var array<string,integer|string> */
$failed = $validator->invalid();
$this->invalidRows[] = collect($row)->put('validation_message', collect($e->errors())
->mapWithKeys(function (array $messages, string $attribute) {
return [$attribute => collect($messages)->implode(', ')];
->mapWithKeys(function (array $messages, string $attribute) use ($failed) {
$verifiedValue = '';
if (array_key_exists($attribute, $failed)) {
$verifiedValue = ' Valeur vérifiée : ' . \json_encode($failed[$attribute]);
}
return [
$attribute => collect($messages)->implode(', ') . $verifiedValue
];
})->implode(' '))->all();
}
}
} catch (ConnectException | RequestException $e) {
// ! Guzzle SSL Error connection failure
\report($e);
$this->invalidRows[] = collect($row)->put('validation_message', $e->getMessage())->all();
}//end try
}//end foreach
} while (!empty($result));
}

View file

@ -23,6 +23,11 @@ class CafelMepEggJob extends CafelJobs
'dateFin' => now()->format('Y-m-d')
],
function (array $row) {
// * Explode inuav
if (\array_key_exists('inuav_codes', $row)) {
$row['inuav_codes'] = explode(',', $row['inuav_codes']);
}
// * Validate row data
CafelMepEgg::validateDataRow($row);

View file

@ -0,0 +1,54 @@
<?php
namespace App\Jobs;
use App\Lib\CafelApi\Models\CafelMepPoultry;
use App\Models\CafelPoultryMiseEnPlace;
class CafelMepPoultryJob extends CafelJobs
{
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$dateDebut = now()->subDays(config('cafel.mises_en_places_retain', 370));
$this->syncWithCafelApi(
CafelMepPoultry::class,
'cafel.ldc.mepvolailles',
[
'dateDebut' => $dateDebut->format('Y-m-d'),
'dateFin' => now()->format('Y-m-d')
],
function (array $row) {
// * Explode inuav
if (\array_key_exists('inuav_codes', $row)) {
$row['inuav_codes'] = explode(',', $row['inuav_codes']);
}
// * Validate row data
CafelMepPoultry::validateDataRow($row);
/** @var array Unique Key columns */
$uniqueColumnsKey = [];
foreach (CafelMepPoultry::UNIQUE_COLUMN_KEYS as $uField) {
$uniqueColumnsKey[$uField] = $row[$uField];
}
forward_static_call_array(
[CafelPoultryMiseEnPlace::class, 'updateOrCreate'],
[$uniqueColumnsKey, $row]
);
}
);
$this->mailInvalidRows(CafelMepPoultry::UNIQUE_COLUMN_KEYS, CafelMepPoultry::class);
// * DeleteOld MEPs
// * Garder une interstice de X jours entre la suppression et ce qu'on récupère de CAFEL
CafelPoultryMiseEnPlace::whereDate('transfert_date', '<', $dateDebut->subDays(
config('cafel.mises_en_places_retain', 370)
))->delete();
}
}

View file

@ -5,7 +5,9 @@ namespace App\Jobs;
use App\Exceptions\CafelApiException;
use App\Exceptions\CafelApiImageFetchException;
use App\Lib\CafelApi\Models\CafelPoultryBreeder as ApiCafelPoultryBreeder;
use App\Lib\ToolboxHelper;
use App\Models\CafelPoultryBreeder;
use Illuminate\Validation\ValidationException;
class CafelPoultryBreederJob extends CafelJobs
{
@ -16,6 +18,16 @@ class CafelPoultryBreederJob extends CafelJobs
*/
public function handle()
{
// ! We have to remove all breeders to reimport them all in order to make sure
// ! to delete breeders that dont exists anymore (Api fetch constrain)
// ! vu avec Sylvain 27/10/2022
// * Destroy breeder one by one to trigger destroy event
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\CafelEggBreeder> */
$poultryBreeders = CafelPoultryBreeder::all();
$poultryBreeders->each(function (CafelPoultryBreeder $breeder) {
$breeder->deleteOrFail();
});
$this->syncWithCafelApi(
ApiCafelPoultryBreeder::class,
'cafel.ldc.eleveursvolailles',
@ -41,8 +53,23 @@ class CafelPoultryBreederJob extends CafelJobs
) : null;
} catch (CafelApiImageFetchException | CafelApiException $e) {
report($e);
$row['path_picture_1'] = null;
}
$validationException = ValidationException::withMessages([
'path_picture_1' => $e->getMessage()
]);
ToolboxHelper::useReflectionToUpdateProperty(
$validationException->validator,
'data',
['path_picture_1' => $row['path_picture_1']]
);
ToolboxHelper::useReflectionToUpdateProperty(
$validationException->validator,
'failedRules',
['path_picture_1' => $e->getMessage()]
);
throw $validationException;
}//end try
try {
$row['path_picture_2'] = !empty($row['path_picture_2']) ?
ApiCafelPoultryBreeder::fetchPicture(
@ -51,8 +78,23 @@ class CafelPoultryBreederJob extends CafelJobs
) : null;
} catch (CafelApiImageFetchException | CafelApiException $e) {
report($e);
$row['path_picture_2'] = null;
}
$validationException = ValidationException::withMessages([
'path_picture_2' => $e->getMessage()
]);
ToolboxHelper::useReflectionToUpdateProperty(
$validationException->validator,
'data',
['path_picture_2' => $row['path_picture_2']]
);
ToolboxHelper::useReflectionToUpdateProperty(
$validationException->validator,
'failedRules',
['path_picture_2' => $e->getMessage()]
);
throw $validationException;
}//end try
foreach (['issue_1', 'issue_2'] as $fieldName) {
$row[$fieldName] = !empty($row[$fieldName]) ? $row[$fieldName] : null;

View file

@ -25,7 +25,11 @@ abstract class CafelBeaVol extends CafelClientModel
// Eg Score A .
'bea_code' => 'required|string|min:2|max:10',
// Eg 19377/0 .
'batch_code' => 'required|max:10|regex:/^[0-9A-Z]*\/\d$/',
'batch_code' => [
'required',
'max:10',
'regex:/^[0-9A-Z]+\/(?:\d|[A-Z])$/',
],
'last_audit_date' => 'required|date_format:Y-m-d'
];

View file

@ -125,7 +125,7 @@ abstract class CafelClientModel implements \JsonSerializable
* @return array
* @throws \Illuminate\Validation\ValidationException If data does not validate.
*/
protected function validateData(array $data, array $rules): array
protected static function validateData(array $data, array $rules): array
{
return Validator::validate($data, $rules);
}
@ -228,15 +228,25 @@ abstract class CafelClientModel implements \JsonSerializable
// * Filter Keys using intersection
\extract(\array_intersect_key($response->body, ['Ext' => '', 'Name' => '', 'Data' => '']));
// * Create temp file and get its absolute path
if (!($tempFile = tmpfile())) {
new \RuntimeException('Could not create temp file');
}
return ToolboxHelper::storeImage(
$filePath = ToolboxHelper::storeImage(
$folder,
FileHelper::fromBase64(
$tempFile,
Str::lower($Name),
Str::lower($Ext),
$Data
),
false
);
// * Closing the file will cause to remove it from temp dir!
fclose($tempFile);
return $filePath;
}
}

View file

@ -17,7 +17,7 @@ abstract class CafelEggBreeder extends CafelClientModel
* @var array
*/
public const UNIQUE_COLUMN_KEYS = [
'cafel_code'
'cafel_code', 'ede_code'
];
/**
@ -43,7 +43,7 @@ abstract class CafelEggBreeder extends CafelClientModel
* 2 chiffres qui représentent le numéro d’ordre de chaque atelier dans une même exploitation
* eg : 1FRZFC01
*/
'ede_code' => 'required|regex:/[0-3][A-Z]{5}\d\d/',
'ede_code' => 'required|regex:/^[0-3][A-Z]{5}\d\d$/',
// Valeurs possibles pour civility : 1 M, 2 MME, 3 EUX, 4 ELLES .
'civility' => ['required', [EnumIsValidRule::class, BreederCivilty::class]],
'lastname' => 'required|string|min:1|max:255',

View file

@ -28,7 +28,11 @@ abstract class CafelFood extends CafelClientModel
// 1 Pondeuse ou 2 Poulet .
'food_type' => ['required', [EnumIsValidRule::class, FoodType::class]],
// Eg 19377/0 .
'batch_code' => 'required|max:10|regex:/^[0-9A-Z]*\/\d$/',
'batch_code' => [
'required',
'max:10',
'regex:/^[0-9A-Z]+\/(?:\d|[A-Z])$/',
],
'delivery_date' => 'required|date_format:Y-m-d'
];

View file

@ -22,10 +22,16 @@ abstract class CafelMepEgg extends CafelClientModel
* @var array
*/
protected static $validationRules = [
// Eg V072FXT (un seul ou plusieurs separe par une virgule).
'inuav_codes' => 'required|array',
// Eg V072FXT (un seul par ligne).
'inuav_code' => 'required|string|regex:/^[A-Z]\d{3}[A-Z]{3}$/',
'inuav_codes.*' => 'required|string|regex:/^[A-Z]\d{3}[A-Z]{3}$/',
// Eg 19377/0 .
'batch_code' => 'required|max:10|regex:/^[0-9A-Z]*\/\d$/',
'batch_code' => [
'required',
'max:10',
'regex:/^[0-9A-Z]+\/(?:\d|[A-Z])$/',
],
'transfert_date' => 'required|date_format:Y-m-d'
];
@ -35,7 +41,7 @@ abstract class CafelMepEgg extends CafelClientModel
* @var array
*/
protected static $columnsTranslations = [
'Inuav_code' => 'inuav_code',
'Inuav_code' => 'inuav_codes',
'Batch_code' => 'batch_code',
'Transfert_date' => 'transfert_date'
];

View file

@ -0,0 +1,48 @@
<?php
namespace App\Lib\CafelApi\Models;
/**
* @author Jérémy Munsch <jeremy.munsch@ldc.fr>
*/
abstract class CafelMepPoultry extends CafelClientModel
{
/**
* Unique columns key
*
* @var array
*/
public const UNIQUE_COLUMN_KEYS = [
'batch_code'
];
/**
* Validation rules from cafel Api
*
* @var array
*/
protected static $validationRules = [
// Eg V072FXT (un seul ou plusieurs separe par une virgule).
'inuav_codes' => 'required|array',
// Eg V072FXT (un seul par ligne).
'inuav_codes.*' => 'required|string|regex:/^[A-Z]\d{3}[A-Z]{3}$/',
// Eg 19377/0 .
'batch_code' => [
'required',
'max:10',
'regex:/^[0-9A-Z]+\/(?:\d|[A-Z])$/',
],
'transfert_date' => 'required|date_format:Y-m-d'
];
/**
* Column translations
*
* @var array
*/
protected static $columnsTranslations = [
'Inuav_code' => 'inuav_codes',
'Batch_code' => 'batch_code',
'Transfert_date' => 'transfert_date'
];
}

View file

@ -105,7 +105,7 @@ class ApiInterface
*
* @param \App\Lib\CafelApi\Transport\Request $request
* @return \App\Lib\CafelApi\Transport\Response
* @throws \App\Exceptions\CafelApiException If reponse status is KO.
* @throws \App\Exceptions\CafelApiException If reponse status is KO or not Json.
*/
public function call(Request $request): Response
{
@ -122,8 +122,9 @@ class ApiInterface
/**
* Process request
*
* @param Request $request
* @return Response
* @param \App\Lib\CafelApi\Transport\Request $request
* @return \App\Lib\CafelApi\Transport\Response
* @throws \App\Exceptions\CafelApiException If HTTP response body in not JSON.
*/
private function processRequest(Request $request): Response
{
@ -136,13 +137,13 @@ class ApiInterface
$status = in_array($response->getStatusCode(), [200, 201]) ?
ApiStatus::OK() : ApiStatus::KO();
return new Response($status, $response);
return new Response($status, $response, $request);
}
/**
* Add spécific headers to the request
*
* @param Request $request
* @param \App\Lib\CafelApi\Transport\Request $request
* @return void
*/
private function setHeaders(Request &$request): void

View file

@ -56,9 +56,10 @@ class Response
*
* @param \App\Enums\ApiStatus $status
* @param \Psr\Http\Message\ResponseInterface $response
* @param \App\Lib\CafelApi\Transport\Request $request
* @throws \App\Exceptions\CafelApiException If HTTP response body in not JSON.
*/
public function __construct(ApiStatus $status, ResponseInterface $response)
public function __construct(ApiStatus $status, ResponseInterface $response, Request $request)
{
$this->response = $response;
$this->status = $status;
@ -74,9 +75,18 @@ class Response
// * Body should be json data.
if (($decodedJson = $this->tryDecodeJson($this->body->getContents())) === false) {
if (in_array(config('app.env'), ['testing', 'local'])) {
dump((string)$this->body);
dump(
'should have been json',
$this->httpCode,
$this->status,
$this->headers,
$request->uri,
(string)$this->body
);
}
throw new CafelApiException('The Api should have returned JSON data');
throw new CafelApiException(
"The Api should have returned JSON data '{$request->uri}'"
);
}
$this->body = $decodedJson;

View file

@ -0,0 +1,190 @@
<?php
namespace App\Lib;
class ExceptionsHelper
{
/**
* Handle sentry event
*
* @param \Sentry\Event $event
* @return null|\Sentry\Event
*/
public static function sentryEventHandler(\Sentry\Event $event): ?\Sentry\Event
{
// Remove unwanted reports.
$event->setModules([]);
$event->setOsContext(null);
/** @var \Sentry\ExceptionDataBag[] */
$exceptionBag = [];
foreach ($event->getExceptions() as $exceptionDataBag) {
$exceptionBag[] = self::getAnonymizedSentryException($exceptionDataBag);
}
$event->setExceptions($exceptionBag);
return $event;
}
/**
* Anonimize Sentry Exception bag
*
* @param \Sentry\ExceptionDataBag $exceptionDataBag
* @return \Sentry\ExceptionDataBag
*/
private static function getAnonymizedSentryException(
\Sentry\ExceptionDataBag $exceptionDataBag
): \Sentry\ExceptionDataBag {
$exceptionDataBag->setValue(self::getAnonymizedMessage($exceptionDataBag->getValue()));
if ($stackTrace = $exceptionDataBag->getStacktrace()) {
$exceptionDataBag->setStacktrace(self::getAnonymizedSentryStackTrace($stackTrace));
}
return $exceptionDataBag;
}
/**
* Anonymise sentry stacktrace
*
* @param \Sentry\Stacktrace $stackTrace
* @return \Sentry\Stacktrace
*/
private static function getAnonymizedSentryStackTrace(\Sentry\Stacktrace $stackTrace): \Sentry\Stacktrace
{
/** @var \Sentry\Frame[] */
$frames = \array_reverse($stackTrace->getFrames(), true);
$i = \count($frames) - 1;
// * Remove all but keep first (index.php).
while ($i-- > 0) {
$stackTrace->removeFrame(count($stackTrace->getFrames()) - 1);
}
foreach ($frames as $index => $frame) {
$frame->setPostContext([]);
$frame->setPreContext([]);
$frame->setContextLine('');
$frame->setVars(self::getAnonymizedSentryParams($frame->getVars()));
$stackTrace->addFrame($frame);
if ($index === 0) {
$stackTrace->removeFrame(count($stackTrace->getFrames()) - 1);
}
}
return $stackTrace;
}
/**
* Get anonymized sentry params
*
* @param mixed[] $params
* @return string[]
*/
private static function getAnonymizedSentryParams(array $params): array
{
foreach ($params as &$param) {
$type = \strtolower(\gettype($param));
if (\is_string($param)) {
$type = \explode(' ', \trim($param))[0] ?? null;
}
switch ($type) {
case 'Lambda':
case 'Object':
continue 2;
case 'array':
$param = sprintf('Array(%d)', \count($param));
continue 2;
default:
if (\is_numeric($param)) {
$param = \ucfirst($type);
continue 2;
}
if (\is_string($param)) {
$param = sprintf('String(%d)', \strlen($param));
continue 2;
}
} //end switch
} //end foreach
return $params;
}
/**
* Anonymize at least Sql queries
*
* @param string $message
* @return string
*/
public static function getAnonymizedMessage(string $message): string
{
$message = \preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message;
if ($sqlPos = \strpos($message, 'to use near')) {
$message = \mb_strcut($message, 0, $sqlPos);
}
return $message;
}
/**
* Anonymize stack trace
*
* @param \Throwable $e
* @return string
*/
public static function getAnonymizedStackTrace(\Throwable $e): string
{
$projectPath = \realpath(\sprintf('%s/../', \app_path()));
$traceStack = $e->getTrace();
$traceStr = '';
$maxStackStage = 0;
foreach ($traceStack as $stackStage => $stack) {
$maxStackStage = $stackStage;
$traceStr .= \sprintf(
'#%s %s(%d): ',
$stackStage,
str_replace($projectPath, '', $stack['file'] ?? ''),
$stack['line'] ?? ''
);
if (isset($stack['class'])) {
$traceStr .= "{$stack['class']}->{$stack['function']}(";
if (isset($stack['args'])) {
$traceStr .= self::handleArgs($stack);
}
$traceStr .= ")\n";
} elseif (isset($stack['function'])) {
$traceStr .= "{$stack['function']}(";
if (isset($stack['args'])) {
$traceStr .= self::handleArgs($stack);
}
$traceStr .= ")\n";
}
} //end foreach
$maxStackStage++;
$traceStr .= "#$maxStackStage {main}";
return $traceStr;
}
/**
* Handle Args
*
* @param array $stack
* @return string
*/
public static function handleArgs(array $stack): string
{
$traceArgs = '';
$argsCount = count($stack['args']);
for ($i = 0; $i < $argsCount; $i++) {
$type = \gettype($stack['args'][$i]);
switch ($type) {
case 'object':
$traceArgs .= sprintf('Object(%s)', \get_class($stack['args'][$i]));
break;
case 'array':
$traceArgs .= sprintf('Array(%d)', \count($stack['args'][$i]));
break;
case 'string':
$traceArgs .= sprintf('String(%d)', \strlen($stack['args'][$i]));
break;
default:
$traceArgs .= \gettype($stack['args'][$i]);
}
$traceArgs .= ($i !== ($argsCount - 1) ? ',' : '');
}
return $traceArgs;
}
}

View file

@ -51,21 +51,18 @@ class FileHelper
}
/**
* Generate from blob
* Generate from base64 blob
*
* @param string $fileName
* @param string $extension
* @param string $base64String
* @param resource $tempFile A temp file where to store data.
* @param string $fileName The target filename.
* @param string $extension The file extension to use.
* @param string $base64String The base64 encode picture.
* @return SplFileInfo
* @throws \ErrorException If writing to temp file failed.
* @throws \App\Exceptions\CafelApiImageFetchException If invalid image was given or ext could not be confirmed.
*/
public static function fromBase64(string $fileName, string $extension, string $base64String): SplFileInfo
public static function fromBase64($tempFile, string $fileName, string $extension, string $base64String): SplFileInfo
{
// * Create temp file and get its absolute path
if (!($tempFile = tmpfile())) {
new \ErrorException('Could not create temp file');
}
$tempFilePath = stream_get_meta_data($tempFile)['uri'];
if (!($blob = \base64_decode($base64String, true))) {
@ -93,12 +90,6 @@ class FileHelper
throw new \ErrorException('Could not write on temp file');
}
// Close this file after response is sent.
// Closing the file will cause to remove it from temp director!
app()->terminating(function () use ($tempFile) {
fclose($tempFile);
});
return new SplFileInfo(\storage_path("tmp/$filePath.$extension"));
}
}

View file

@ -12,6 +12,23 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
abstract class ToolboxHelper
{
/**
* Update any prop using reflexion
*
* @param object $object
* @param string $property
* @param mixed $value
* @return void
* @throws \ReflectionException If the class or property does not exist.
*/
public static function useReflectionToUpdateProperty(object &$object, string $property, $value)
{
$refProperty = new \ReflectionProperty(\get_class($object), $property);
$refProperty->setAccessible(true);
$refProperty->setValue($object, $value);
$refProperty->setAccessible(false);
}
/**
* Store an image of different types
*

View file

@ -5,6 +5,8 @@ namespace App\Models;
use App\Enums\BreederCivilty;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Kwaadpepper\Enum\Traits\CastsEnums;
@ -97,7 +99,8 @@ class CafelEggBreeder extends Model
{
// * EVENT CLOSURES.
static::creating(function (self $model) {
return self::areInuavCodesUniques($model);
self::areInuavCodesUniques($model);
self::isEdeCodeUnique($model);
});
static::updating(function (self $model) {
if ($model->getRawOriginal('path_picture_1') !== $model->path_picture_1) {
@ -106,7 +109,16 @@ class CafelEggBreeder extends Model
if ($model->getRawOriginal('path_picture_2') !== $model->path_picture_2) {
Storage::delete(\public_path($model->getRawOriginal('path_picture_2')));
}
return self::areInuavCodesUniques($model);
self::areInuavCodesUniques($model);
self::isEdeCodeUnique($model);
});
static::deleting(function (self $model) {
if ($model->getRawOriginal('path_picture_1')) {
Storage::delete(\public_path($model->getRawOriginal('path_picture_1')));
}
if ($model->getRawOriginal('path_picture_2')) {
Storage::delete(\public_path($model->getRawOriginal('path_picture_2')));
}
});
}
@ -137,26 +149,44 @@ class CafelEggBreeder extends Model
/**
* Check if inuav codes are unique
*
* @param self $eleveur
* @param self $breeder
* @return boolean
* @throws \Illuminate\Validation\ValidationException If inuav code is already used.
*/
protected static function areInuavCodesUniques(self $eleveur): bool
protected static function areInuavCodesUniques(self $breeder): bool
{
$q = self::query();
// * Update condition
$q->where(function ($q) use ($eleveur) {
foreach ($eleveur->inuavs as $inuav) {
$q->orWhere('inuavs', 'LIKE', "%$inuav%");
$q->where(function ($q) use ($breeder) {
foreach ($breeder->inuavs as $inuav) {
$q->orWhere('inuavs', '=', $inuav);
}
});
if ($eleveur->cafel_code) {
$q->where('cafel_code', '!=', $eleveur->cafel_code);
if ($breeder->cafel_code) {
$q->where('cafel_code', '!=', $breeder->cafel_code);
}
if ($q->count()) {
throw ValidationException::withMessages(['inuavs' => 'Les codes INUAVS doinvent être uniques'
throw ValidationException::withMessages([
'inuavs' => 'Les codes INUAVS doinvent être uniques : ' . collect($breeder->inuavs)->implode(',')
]);
}
return true;
}
/**
* Check if ede code is unique
*
* @param self $breeder
* @return boolean
* @throws \Illuminate\Validation\ValidationException If ede code is already used.
*/
protected static function isEdeCodeUnique(self $breeder): bool
{
Validator::make([
'ede_code' => $breeder->ede_code
], ['ede_code' => [
Rule::unique($breeder->getTable(), 'ede_code')->ignore($breeder->id)
]])->validate();
return true;
}
}

View file

@ -33,7 +33,8 @@ class CafelEggMiseEnPlace extends Model
* @var array
*/
protected $casts = [
'transfert_date' => 'date'
'transfert_date' => 'date',
'inuav_codes' => 'array'
];
/**
@ -43,7 +44,7 @@ class CafelEggMiseEnPlace extends Model
*/
protected $fillable = [
'batch_code',
'inuav_code',
'inuav_codes',
'transfert_date'
];
}

View file

@ -94,6 +94,14 @@ class CafelPoultryBreeder extends Model
}
return self::areInuavCodesUniques($model);
});
static::deleting(function (self $model) {
if ($model->getRawOriginal('path_picture_1')) {
Storage::delete(\public_path($model->getRawOriginal('path_picture_1')));
}
if ($model->getRawOriginal('path_picture_2')) {
Storage::delete(\public_path($model->getRawOriginal('path_picture_2')));
}
});
}
// * METHODS
@ -119,7 +127,7 @@ class CafelPoultryBreeder extends Model
}
if ($q->count()) {
throw ValidationException::withMessages([
'inuavs' => 'Les codes INUAVS doinvent être uniques'
'inuavs' => 'Les codes INUAVS doinvent être uniques : ' . collect($breeder->inuavs)->implode(',')
]);
}
return true;

View file

@ -0,0 +1,70 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class CafelPoultryMiseEnPlace extends Model
{
/**
* The primary key for the model.
*
* @var string
*/
protected $primaryKey = 'batch_code';
/**
* Indicates if the IDs are auto-incrementing.
*
* @var boolean
*/
public $incrementing = false;
/**
* Indicates if the model should be timestamped.
*
* @var boolean
*/
public $timestamps = false;
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'transfert_date' => 'date',
'inuav_codes' => 'array'
];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'batch_code',
'inuav_codes',
'transfert_date'
];
// * METHODS
/**
* Get Mep using cafel lot code
*
* @param integer $codeLotCafel
* @return self|null
*/
public static function whereCodeLotCafel(int $codeLotCafel): ?self
{
return Cache::remember("poultryMep$codeLotCafel", 5, function () use ($codeLotCafel) {
return self::where(
'batch_code',
'LIKE',
'%' . \filter_var($codeLotCafel, \FILTER_SANITIZE_STRING) . '/%'
)->first();
});
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class PoultryPackshot extends Model
{
use HasFactory;
}

View file

@ -37,6 +37,7 @@ class TraceaEgg extends Model
'box_code',
'ede_code',
'laying_date',
'packing_date',
'product_code',
'product_label',
'external_lot',
@ -50,6 +51,7 @@ class TraceaEgg extends Model
* @var array
*/
protected $casts = [
'packing_date' => 'date',
'laying_date' => 'date',
'dcr' => 'date'
];

View file

@ -8,6 +8,7 @@ use App\Traits\HasCompositePrimaryKey;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class TraceaPoultry extends Model
{
@ -70,6 +71,22 @@ class TraceaPoultry extends Model
'prep_date' => 'date'
];
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted()
{
// * EVENT CLOSURES.
static::creating(function (self $model) {
self::hasAtLeastOneBarCode($model);
});
static::updating(function (self $model) {
self::hasAtLeastOneBarCode($model);
});
}
// * METHODS
/**
@ -81,10 +98,10 @@ class TraceaPoultry extends Model
{
$family = Str::upper($this->getAttributeFromArray('family'));
switch (true) {
case \strpos($family, 'LABEL') !== false:
case \strpos($family, 'LOUE') !== false:
return PoultryLabelType::labelRouge();
case \strpos($family, 'BIO') !== false:
return PoultryLabelType::labelRouge();
return PoultryLabelType::bio();
default:
return PoultryLabelType::unkown();
}
@ -98,6 +115,7 @@ class TraceaPoultry extends Model
public function getBreederAttribute(): ?CafelPoultryBreeder
{
$matches = [];
preg_match('/(?P<inuav>V\d{3}[A-Z]{3})/', \strval($this->breeder_code), $matches);
if (!\array_key_exists('inuav', $matches)) {
return collect();
@ -153,4 +171,53 @@ class TraceaPoultry extends Model
// * On utilise code lot_opa vue que son explication de merde est inversée ... Uu
return CafelFood::whereCodeLotCafel(FoodType::chicken(), \intval($this->opa_lot));
}
/**
* * Get Bea
*
* L'explication du batch code est un non sens.... putin de merde
*
* Comment faisons-nous pour lier notre lot à une note BEA ?
* (code lot sous ce format : XXXXX/0 pour les données BEA) -> Code lot CAFEL / Code OPA
* Comment faisons-nous pour lier notre lot à son alimentation ?
* (code lot sous ce format : XXXXX/0 pour les données alimentation) -> Code lot CAFEL / Code OPA
* Pour ces questions, peux-tu nous dire avec les informations que nous recevons du flux Sablé
* les règles que nous devons utiliser stp ? * -> @Mathilde Hervier peux-tu répondre à cette question ?
* Le 6e champ reçu dans le fichier xml est le code lot OPA qui sert donc à faire le lien avec la CAFEL
*
* @return \App\Models\CafelPoultryMiseEnPlace|null
*/
public function getMepAttribute(): ?CafelPoultryMiseEnPlace
{
// * On utilise code lot_opa vue que son explication de merde est inversée ... Uu
return CafelPoultryMiseEnPlace::whereCodeLotCafel(\intval($this->opa_lot));
}
/**
* Assert has at least one barcode
*
* @param self $tracea
* @return boolean
* @throws \Illuminate\Validation\ValidationException If inuav code is already used.
*/
protected static function hasAtLeastOneBarCode(self $tracea): bool
{
if (empty($tracea->barcode_price) and empty($tracea->barcode_weight)) {
throw ValidationException::withMessages([
'barcode_price' => "Il faut au moins un code barre pour faire la recherche '{$tracea->barcode_price}'",
'barcode_weight' => "Il faut au moins un code barre pour faire la recherche '{$tracea->barcode_weight}'"
]);
}
return true;
}
/**
* Get Packshot
*
* @return \App\Models\PoultryPackshot|null
*/
public function getPackshotAttribute(): ?PoultryPackshot
{
return PoultryPackshot::whereItemCode($this->item_code)->first();
}
}

View file

@ -27,7 +27,7 @@ class AppServiceProvider extends ServiceProvider
public function boot()
{
URL::forceScheme('https');
// * HA PROXY FIX
App::singleton('clientIp', function () {
if (config('app.env') === 'local') {

View file

@ -11,9 +11,7 @@ class AuthServiceProvider extends ServiceProvider
*
* @var array
*/
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
];
protected $policies = [];
/**
* Register any authentication / authorization services.

View file

@ -18,14 +18,4 @@ class EventServiceProvider extends ServiceProvider
SendEmailVerificationNotification::class,
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
parent::boot();
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace App\Providers;
use Carbon\Carbon;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class ViewServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot(): void
{
if (!app()->runningInConsole()) {
View::share('isDisplayedTAModal', (function () {
$now = now();
return $now->greaterThanOrEqualTo(new Carbon(config('modal.terreActionStartDate'))) &&
$now->lessThanOrEqualTo(new Carbon(config('modal.terreActionEndDate')));
})());
}
}
}

View file

@ -17,21 +17,26 @@
"fideloper/proxy": "^4.0",
"guzzlehttp/guzzle": "^7.4",
"kwaadpepper/enum": "^2.1",
"kwaadpepper/exception-handler": "^0.0.6",
"kwaadpepper/refresh-sitemap": "^1.0",
"laravel/framework": "^8.0",
"laravel/tinker": "^2.0",
"laravel/ui": "^3.0",
"laravelcollective/html": "^6.0",
"ldc/web-admin-secure-laravel": "^1.0",
"ldc/web-status-checker-laravel": "^1.0",
"mews/purifier": "^3.3",
"nesbot/carbon": "^2.29",
"sentry/sentry-laravel": "^3.1",
"spatie/laravel-mix-preload": "^1.3"
},
"require-dev": {
"facade/ignition": "^2.3.6",
"fzaninotto/faker": "^1.4",
"laravel/ui": "^3.0",
"mockery/mockery": "^1.0",
"facade/ignition": "^2.5",
"fakerphp/faker": "^1.9.1",
"laravel/sail": "^1.0.1",
"mockery/mockery": "^1.4.2",
"nunomaduro/collision": "^5.0",
"phpunit/phpunit": "^9.0",
"phpunit/phpunit": "^9.3.3",
"slevomat/coding-standard": "^7.0"
},
"config": {
@ -40,7 +45,10 @@
"sort-packages": true,
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
},
"gitlab-domains": [
"lab.s-web.fr"
]
},
"extra": {
"laravel": {
@ -72,5 +80,11 @@
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
]
},
"repositories": {
"lab.s-web.fr": {
"type": "composer",
"url": "https://lab.s-web.fr/api/v4/group/23/-/packages/composer/packages.json"
}
}
}

3818
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -53,6 +53,8 @@ return [
*/
'url' => env('APP_URL', 'http://localhost'),
'domain' => env('APP_DOMAIN', 'tracabilite.loue.fr'),
'egg_domain' => env('APP_EGG_DOMAIN', 'bo.gt'),
'asset_url' => env('ASSET_URL', null),
@ -175,6 +177,7 @@ return [
App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\ViewServiceProvider::class,
],
@ -234,7 +237,4 @@ return [
'CafelPoultryBreeder' => \App\Models\CafelPoultryBreeder::class,
],
'project_uuid' => env('PROJECT_UUID'),
];

View file

@ -1,8 +1,9 @@
<?php
return [
// ! Food et MEp sont requis pour afficher un resultat traca volaille
// * How many days should we retain Aliment
'food_retain' => 30,
'food_retain' => 370,
// * How many days should we retain MEP (egg)
'mises_en_places_retain' => 370,
'client_version' => '1.0',
@ -51,6 +52,12 @@ return [
'methods' => [
'GET'
]
],
'cafel.ldc.mepvolailles' => [
'uri' => 'api/ldc/mepvolailles',
'methods' => [
'GET'
]
]
]
];

View file

@ -1,15 +0,0 @@
<?php
return [
/**
* Send mail when app debug is turn off
* so you can be warned if an unhandled exception occurs
*/
'enableMaiLog' => !config('app.debug'),
/**
* Put a developper secured mailbox in here
* so you can receive email if things go mad
*/
'contactsList' => explode(',', env('MAIL_LIST_SERVICEWEB', 'equipe.web@ldc.fr')),
];

5
config/google.php Normal file
View file

@ -0,0 +1,5 @@
<?php
return [
'analyticsKey' => env('ANALYTICS_GOOGLE_ID')
];

View file

@ -62,9 +62,12 @@ return [
// * Contacts List
'contactsList' => [
'invalidXml' => explode(',', env('MAIL_INVALID_XML', 'equipe.web@ldc.fr')),
'invalidCafel' => explode(',', env('MAIL_INVALID_CAFEL_DATA', 'equipe.web@ldc.fr')),
'invalidCafelImage' => explode(',', env('MAIL_INVALID_CAFEL_DATA_IMAGE', 'equipe.web@ldc.fr')),
'invalidXml' => preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', env('MAIL_INVALID_XML', 'equipe.web@ldc.fr')),
'invalidCafel' => preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', env('MAIL_INVALID_CAFEL_DATA', 'equipe.web@ldc.fr')),
'invalidCafelImage' => preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', env(
'MAIL_INVALID_CAFEL_DATA_IMAGE',
'equipe.web@ldc.fr'
)),
],
/*

6
config/modal.php Normal file
View file

@ -0,0 +1,6 @@
<?php
return [
'terreActionStartDate' => env('TERRE_ACTION_START_DATE'),
'terreActionEndDate' => env('TERRE_ACTION_END_DATE')
];

108
config/refresh-sitemap.php Normal file
View file

@ -0,0 +1,108 @@
<?php
use Spatie\Sitemap\Tags\Url;
return [
/**
* A list of url to ignore
* These are hard coded
* Each url does not have to be exhaustive
*
* For example, if you want to ignore all routes
* of the backoffice group
* just add '/backoffice' into this table
*/
'ignoreRoutes' => [
'/bo',
'/frame',
'/eleveurs',
'/oeuf',
'/tracabilite'
],
/**
* This table is needed to resolve models matching
* routes
*
* it has to be filled like following
*
* 'products.meat-type' => [
* 'slugMeatType' => [MeatTypes::class, 'slug']
* ],
* 'kitchen-side.health-nutrition.balanced-menu.menu' => [
* 'type' => [BalancedMenuType::class, 'type']
* ]
*
* The index is the complete route name, the array content is an array
* within index is the argument name with its model class.
* This will allow the generator to generate a route for each of the models
* in this table
*/
'routesBinder' => [],
/**
* You can put route conditions here
* These will be used to decide whether the route should appear
* in the sitemap or not
*
* These are called in Eloquent where
*
* example
*
* 'queryConditions' => [
* 'status' => true,
* 'ocbStatus' => EleveurOcbStatus::yes()->value,
* 'star_moment' => true
* ]
*
*/
'queryConditions' => [],
/**
* The default route frequency to use with sitemap generator
*/
'routeDefaultFrequency' => Url::CHANGE_FREQUENCY_DAILY,
/**
* The default route priority to use with sitemap generator
*
* priority should be a number between 0 and 1
* https://www.sitemaps.org/protocol.html
*/
'routeDefaultPriority' => 0.5,
/**
* Frequencies for routes
* use the full or partial route name as index
* and a frequency within \Spatie\Sitemap\Tags\Url constants
*/
'routeFrequencies' => [],
/**
* Priorities for routes
* use the full or partial route name as index
* and a priority number between 0 and 1
*/
'routePriorities' => [],
/**
* Enable or disable schedule for the job
* If set to true the sitemap generator will be run
* using the 'cron' instruction
*/
'schedule' => false,
/**
* If you want to set specific
* instruction for when to schedule job
* you can set this with a cron instruction
* You can generate one using https://crontab.guru/
* https://laravel.com/docs/8.x/scheduling#schedule-frequency-options
*
* schedule must be set to true
*
* Default is every day at 15:45
*/
'cron' => '45 15 * * *'
];

62
config/sentry.php Normal file
View file

@ -0,0 +1,62 @@
<?php
return [
'dsn' => env('SENTRY_LARAVEL_DSN', env('SENTRY_DSN')),
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')).
'release' => env('SENTRY_RELEASE'),
// When left empty or `null` the Laravel environment will be used.
'environment' => env('SENTRY_ENVIRONMENT'),
'breadcrumbs' => [
// Capture Laravel logs in breadcrumbs.
'logs' => true,
// Capture SQL queries in breadcrumbs.
'sql_queries' => true,
// Capture bindings on SQL queries logged in breadcrumbs.
'sql_bindings' => true,
// Capture queue job information in breadcrumbs.
'queue_info' => true,
// Capture command information in breadcrumbs.
'command_info' => true,
],
'tracing' => [
// Trace queue jobs as their own transactions.
'queue_job_transactions' => env('SENTRY_TRACE_QUEUE_ENABLED', false),
// Capture queue jobs as spans when executed on the sync driver.
'queue_jobs' => true,
// Capture SQL queries as spans.
'sql_queries' => true,
// Try to find out where the SQL query originated from and add it to the query spans.
'sql_origin' => true,
// Capture views as spans.
'views' => true,
// Indicates if the tracing integrations supplied by Sentry should be loaded.
'default_integrations' => true,
// Indicates that requests without a matching route should be traced.
'missing_routes' => false,
],
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#send-default-pii
'send_default_pii' => env('SENTRY_SEND_DEFAULT_PII', false),
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#traces-sample-rate
'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? null : (float)env('SENTRY_TRACES_SAMPLE_RATE'),
'before_send' => [\App\Lib\ExceptionsHelper::class, 'sentryEventHandler'],
];

View file

@ -37,6 +37,8 @@ class CreateTraceaEggsTable extends Migration
->comment('Pas de description xs:integer [0-9]{6}|[0-9]{12}');
$table->date('laying_date')
->comment('Le date de ponte xs:date');
$table->date('packing_date')
->comment('Le date d\'emballage xs:date');
$table->date('dcr')
->comment('Date dcr ? xs:date');
});
@ -60,9 +62,9 @@ class CreateTraceaEggsTable extends Migration
$table->renameColumn('code_boite', 'box_code');
$table->renameColumn('ean13', 'ean_13');
$table->renameColumn('date_ponte', 'laying_date');
$table->renameColumn('date_emballage', 'packing_date');
});
Schema::table('tracea_eggs', function (Blueprint $table) {
$table->dropColumn(['date_emballage']);
$table->string('ede_code', 10)
->comment('Le code EDE xs:string length 8')
->change();

View file

@ -32,8 +32,8 @@ class CreateTraceaPoultriesTable extends Migration
$table->unsignedMediumInteger('opa_lot')->nullable()
->comment('Lot OPA xs:string [*]{0}|[0-9][0-9]{5} (soit chaine vide, soit integer)');
$table->unsignedInteger('barcode_price')
->comment('Gencode prix xs:string [0-9]{7} (soit chaine vide, soit integer)');
$table->unsignedInteger('barcode_price')->nullable()
->comment('Gencode prix xs:string [*]{0}|[0-9]{7} (soit chaine vide, soit integer)');
$table->unsignedInteger('barcode_weight')->nullable()
->comment('Gencode poids xs:string [*]{0}|[0-9]{7} (soit chaine vide, soit integer)');

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCafelPoultryMiseEnPlacesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cafel_poultry_mise_en_places', function (Blueprint $table) {
$table->string('batch_code', 10)->unique();
$table->string('inuav_code', 100);
$table->date('transfert_date');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cafel_poultry_mise_en_places');
}
}

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIndexesOnTracaEgg extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('tracea_eggs', function (Blueprint $table) {
$table->index(['ede_code']);
$table->index(['dcr']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('tracea_eggs', function (Blueprint $table) {
$table->dropIndex(['ede_code']);
$table->dropIndex(['dcr']);
});
}
}

View file

@ -0,0 +1,42 @@
<?php
use App\Models\CafelEggBreeder;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
class AddUniqueOnEdeEggBreeders extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// * Destroy breeder one by one to trigger destroy event
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\CafelEggBreeder> */
$eggBreeders = CafelEggBreeder::all();
$eggBreeders->each(function (CafelEggBreeder $breeder) {
$breeder->deleteOrFail();
});
Schema::table('cafel_egg_breeders', function (Blueprint $table) {
$table->unique(['ede_code']);
});
Log::alert('Please reimport all egg breeders');
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('cafel_egg_breeders', function (Blueprint $table) {
$table->dropUnique(['ede_code']);
});
}
}

View file

@ -0,0 +1,81 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class MakeMisesEnPlaceHandleMultipleInuav extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('cafel_egg_mise_en_places', function (Blueprint $table) {
$table->renameColumn('inuav_code', 'inuav_codes');
});
Schema::table('cafel_poultry_mise_en_places', function (Blueprint $table) {
$table->renameColumn('inuav_code', 'inuav_codes');
});
Schema::table('cafel_egg_mise_en_places', function (Blueprint $table) {
$table->string('inuav_codes', 400)->change();
});
Schema::table('cafel_poultry_mise_en_places', function (Blueprint $table) {
$table->string('inuav_codes', 400)->change();
});
DB::table('cafel_egg_mise_en_places')->get()
->each(function (object $eggMep) {
DB::table('cafel_egg_mise_en_places')
->where('batch_code', $eggMep->batch_code)
->update(['inuav_codes' => collect(
explode(',', $eggMep->inuav_codes)
)->filter()->jsonSerialize()]);
});
DB::table('cafel_poultry_mise_en_places')->get()
->each(function (object $poultryMep) {
DB::table('cafel_poultry_mise_en_places')
->where('batch_code', $poultryMep->batch_code)
->update(['inuav_codes' => collect(
explode(',', $poultryMep->inuav_codes)
)->filter()->jsonSerialize()]);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
DB::table('cafel_egg_mise_en_places')->get()
->each(function (object $eggMep) {
DB::table('cafel_egg_mise_en_places')
->where('batch_code', $eggMep->batch_code)
->update(['inuav_codes' => collect(json_decode($eggMep->inuav_codes))->filter()
->first()]);
});
DB::table('cafel_poultry_mise_en_places')->get()
->each(function (object $poultryMep) {
DB::table('cafel_poultry_mise_en_places')
->where('batch_code', $poultryMep->batch_code)
->update(['inuav_codes' => collect(json_decode($poultryMep->inuav_codes))->filter()
->first()]);
});
Schema::table('cafel_egg_mise_en_places', function (Blueprint $table) {
$table->string('inuav_codes', 100)->change();
});
Schema::table('cafel_poultry_mise_en_places', function (Blueprint $table) {
$table->string('inuav_codes', 100)->change();
});
Schema::table('cafel_egg_mise_en_places', function (Blueprint $table) {
$table->renameColumn('inuav_codes', 'inuav_code');
});
Schema::table('cafel_poultry_mise_en_places', function (Blueprint $table) {
$table->renameColumn('inuav_codes', 'inuav_code');
});
}
}

View file

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePoultryPackshotsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('poultry_packshots', function (Blueprint $table) {
$table->id();
$table->string('item_code', 14)->comment("Item Code = tracea_poultries.item_code");
$table->string('ean', 13)->comment("Ean 13");
$table->string('packshot_name')->comment("Packshot picture name");
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('poultry_packshots');
}
}

View file

@ -13,6 +13,6 @@ class DatabaseSeeder extends Seeder
*/
public function run()
{
// $this->call(UsersTableSeeder::class);
$this->call(PoultryPackshotsTableSeeder::class);
}
}

File diff suppressed because it is too large Load diff

5306
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -25,6 +25,7 @@
"resolve-url-loader": "^5.0.0",
"sass": "1.32.13",
"sass-loader": "^8.0.2",
"sweetalert2": "^11.4.35",
"vue": "^2.6.14",
"vue-template-compiler": "^2.6.14"
}

View file

@ -5,10 +5,8 @@
<arg name="extensions" value="php" />
<file>app/</file>
<file>config/</file>
<file>database/factories</file>
<file>database/seeders</file>
<file>routes/</file>
<file>tests/</file>
<rule ref="PSR1" />
<rule ref="PSR12" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 608 KiB

After

Width:  |  Height:  |  Size: 580 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 608 KiB

After

Width:  |  Height:  |  Size: 596 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Some files were not shown because too many files have changed in this diff Show more