diff --git a/.idea/ferme.iml b/.idea/ferme.iml index 6a0c96f..5065845 100644 --- a/.idea/ferme.iml +++ b/.idea/ferme.iml @@ -152,6 +152,8 @@ + + diff --git a/.idea/php.xml b/.idea/php.xml index d4b4d6a..fbd036e 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -158,6 +158,8 @@ + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 254ba9c..e8dd904 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,14 +4,20 @@ - + + + + + + + - - - - - + + + + + { @@ -277,7 +285,7 @@ - + - @@ -657,9 +689,6 @@ - - - @@ -682,7 +711,10 @@ - diff --git a/README.md b/README.md index 9835150..d4b0c61 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,18 @@ Le frontend ne lit jamais directement le token, le navigateur envoie automatique - Le cookie est automatiquement envoyé pour les futures requêtes. - La déconnexion utilise `POST /api/logout` et redirige vers `/login`. +### Fixtures +Pour lancer les fixtures (Attention sa purge la bdd complètement) +```bash +php bin/console doctrine:fixtures:load +``` + +Attention cette commande est dangereuse, à utiliser que pour les débuts de la prod ou en recette. +Dans un premier temps pour remplir les listes, vous pouvez lancer la commande symfony +```bash +php bin/console app:seed +``` +La commande va faire une update ou une création en fonction des data existante. ## Livraison en recette ### Préparatifs diff --git a/composer.json b/composer.json index b09f4a0..74cc8b9 100644 --- a/composer.json +++ b/composer.json @@ -88,6 +88,7 @@ } }, "require-dev": { + "doctrine/doctrine-fixtures-bundle": "^4.3", "friendsofphp/php-cs-fixer": "^3.92", "phpunit/phpunit": "^12.5", "symfony/browser-kit": "8.0.*", diff --git a/composer.lock b/composer.lock index d308752..4bcf255 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d66ef6b521a08a262b1c043bb32cafe5", + "content-hash": "9c4e168c0540baf5d7d5d54040834d79", "packages": [ { "name": "api-platform/doctrine-common", @@ -8615,6 +8615,175 @@ ], "time": "2024-05-06T16:37:16+00:00" }, + { + "name": "doctrine/data-fixtures", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/data-fixtures.git", + "reference": "7a615ba135e45d67674bb623d90f34f6c7b6bd97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/7a615ba135e45d67674bb623d90f34f6c7b6bd97", + "reference": "7a615ba135e45d67674bb623d90f34f6c7b6bd97", + "shasum": "" + }, + "require": { + "doctrine/persistence": "^3.1 || ^4.0", + "php": "^8.1", + "psr/log": "^1.1 || ^2 || ^3" + }, + "conflict": { + "doctrine/dbal": "<3.5 || >=5", + "doctrine/orm": "<2.14 || >=4", + "doctrine/phpcr-odm": "<1.3.0" + }, + "require-dev": { + "doctrine/coding-standard": "^14", + "doctrine/dbal": "^3.5 || ^4", + "doctrine/mongodb-odm": "^1.3.0 || ^2.0.0", + "doctrine/orm": "^2.14 || ^3", + "ext-sqlite3": "*", + "fig/log-test": "^1", + "phpstan/phpstan": "2.1.31", + "phpunit/phpunit": "10.5.45 || 12.4.0", + "symfony/cache": "^6.4 || ^7", + "symfony/var-exporter": "^6.4 || ^7" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)", + "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", + "doctrine/orm": "For loading ORM fixtures", + "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\DataFixtures\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Data Fixtures for all Doctrine Object Managers", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "database" + ], + "support": { + "issues": "https://github.com/doctrine/data-fixtures/issues", + "source": "https://github.com/doctrine/data-fixtures/tree/2.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdata-fixtures", + "type": "tidelift" + } + ], + "time": "2025-10-17T20:06:20+00:00" + }, + { + "name": "doctrine/doctrine-fixtures-bundle", + "version": "4.3.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineFixturesBundle.git", + "reference": "9e013ed10d49bf7746b07204d336384a7d9b5a4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineFixturesBundle/zipball/9e013ed10d49bf7746b07204d336384a7d9b5a4d", + "reference": "9e013ed10d49bf7746b07204d336384a7d9b5a4d", + "shasum": "" + }, + "require": { + "doctrine/data-fixtures": "^2.2", + "doctrine/doctrine-bundle": "^2.2 || ^3.0", + "doctrine/orm": "^2.14.0 || ^3.0", + "doctrine/persistence": "^2.4 || ^3.0 || ^4.0", + "php": "^8.1", + "psr/log": "^2 || ^3", + "symfony/config": "^6.4 || ^7.0 || ^8.0", + "symfony/console": "^6.4 || ^7.0 || ^8.0", + "symfony/dependency-injection": "^6.4 || ^7.0 || ^8.0", + "symfony/deprecation-contracts": "^2.1 || ^3", + "symfony/doctrine-bridge": "^6.4.16 || ^7.1.9 || ^8.0", + "symfony/http-kernel": "^6.4 || ^7.0 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "< 3" + }, + "require-dev": { + "doctrine/coding-standard": "14.0.0", + "phpstan/phpstan": "2.1.11", + "phpunit/phpunit": "^10.5.38 || 11.4.14" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\FixturesBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Doctrine Project", + "homepage": "https://www.doctrine-project.org" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DoctrineFixturesBundle", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "Fixture", + "persistence" + ], + "support": { + "issues": "https://github.com/doctrine/DoctrineFixturesBundle/issues", + "source": "https://github.com/doctrine/DoctrineFixturesBundle/tree/4.3.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdoctrine-fixtures-bundle", + "type": "tidelift" + } + ], + "time": "2025-12-03T16:05:42+00:00" + }, { "name": "evenement/evenement", "version": "v3.0.2", diff --git a/config/bundles.php b/config/bundles.php index 5531a12..b2da949 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -4,6 +4,7 @@ declare(strict_types=1); use ApiPlatform\Symfony\Bundle\ApiPlatformBundle; use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; +use Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle; use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle; use Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle; use Malio\EdnotifBundle\EdnotifBundle; @@ -26,4 +27,5 @@ return [ MonologBundle::class => ['all' => true], EdnotifBundle::class => ['all' => true], WebProfilerBundle::class => ['dev' => true], + DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], ]; diff --git a/frontend/services/dto/address-data.ts b/frontend/services/dto/address-data.ts index 92b0169..db93c92 100644 --- a/frontend/services/dto/address-data.ts +++ b/frontend/services/dto/address-data.ts @@ -2,6 +2,7 @@ export interface AddressData { id: number label: string street: string + street2?: string | null postalCode: string city: string countryCode: string diff --git a/frontend/services/dto/supplier-data.ts b/frontend/services/dto/supplier-data.ts index 3fb1dd9..80f9f80 100644 --- a/frontend/services/dto/supplier-data.ts +++ b/frontend/services/dto/supplier-data.ts @@ -3,5 +3,7 @@ import type { AddressData } from '~/services/dto/address-data' export interface SupplierData { id: number name: string + email?: string | null + phone?: string | null addresses?: AddressData[] | null } diff --git a/makefile b/makefile index c7d34dd..92c2dc5 100644 --- a/makefile +++ b/makefile @@ -76,6 +76,9 @@ build-without-cache: migration-migrate: $(SYMFONY_CONSOLE) doctrine:migrations:migrate --no-interaction +fixtures: + $(SYMFONY_CONSOLE) doctrine:fixtures:load + # Attention, supprime votre bdd local db-reset: $(DOCKER_COMPOSE) down -v @@ -83,6 +86,7 @@ db-reset: $(MAKE) wait $(SYMFONY_CONSOLE) doctrine:database:create --if-not-exists $(MAKE) migration-migrate + $(MAKE) fixtures # Restart la bdd db-restart: diff --git a/migrations/Version20260130000100.php b/migrations/Version20260130000100.php new file mode 100644 index 0000000..781d3f3 --- /dev/null +++ b/migrations/Version20260130000100.php @@ -0,0 +1,30 @@ +addSql('ALTER TABLE address ADD street2 VARCHAR(180) DEFAULT NULL'); + $this->addSql('ALTER TABLE supplier ADD email VARCHAR(180) DEFAULT NULL'); + $this->addSql('ALTER TABLE supplier ADD phone VARCHAR(40) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE address DROP street2'); + $this->addSql('ALTER TABLE supplier DROP email'); + $this->addSql('ALTER TABLE supplier DROP phone'); + } +} diff --git a/src/Command/SeedCommand.php b/src/Command/SeedCommand.php new file mode 100644 index 0000000..d08394b --- /dev/null +++ b/src/Command/SeedCommand.php @@ -0,0 +1,256 @@ +entityManager->getRepository(Truck::class); + $carrierRepo = $this->entityManager->getRepository(Carrier::class); + $driverRepo = $this->entityManager->getRepository(Driver::class); + $vehicleRepo = $this->entityManager->getRepository(Vehicle::class); + $merchandiseTypeRepo = $this->entityManager->getRepository(MerchandiseType::class); + $pelletTypeRepo = $this->entityManager->getRepository(PelletType::class); + $buildingRepo = $this->entityManager->getRepository(Building::class); + $receptionTypeRepo = $this->entityManager->getRepository(ReceptionType::class); + $addressRepo = $this->entityManager->getRepository(Address::class); + $supplierRepo = $this->entityManager->getRepository(Supplier::class); + + $upsertByCode = function (string $entityClass, string $code, callable $apply) use (&$created, &$updated) { + $repo = $this->entityManager->getRepository($entityClass); + $entity = $repo->findOneBy(['code' => $code]); + if (!$entity) { + $entity = new $entityClass(); + ++$created; + } else { + ++$updated; + } + $apply($entity); + $this->entityManager->persist($entity); + + return $entity; + }; + + $upsertByName = function (string $entityClass, string $name, callable $apply) use (&$created, &$updated) { + $repo = $this->entityManager->getRepository($entityClass); + $entity = $repo->findOneBy(['name' => $name]); + if (!$entity) { + $entity = new $entityClass(); + ++$created; + } else { + ++$updated; + } + $apply($entity); + $this->entityManager->persist($entity); + + return $entity; + }; + + $trucks = ['Citerne', 'Porteur']; + $citerne = null; + $porteur = null; + foreach ($trucks as $name) { + $truck = $upsertByName(Truck::class, $name, static fn (Truck $truck) => $truck->setName($name)); + if ('Citerne' === $name) { + $citerne = $truck; + } + if ('Porteur' === $name) { + $porteur = $truck; + } + } + + $carriers = [ + ['name' => 'LIOT', 'code' => 'LIOT'], + ['name' => 'LUI-MEME', 'code' => 'LUI-MEME'], + ]; + $liot = null; + foreach ($carriers as $carrierData) { + $carrier = $upsertByCode(Carrier::class, $carrierData['code'], static function (Carrier $carrier) use ($carrierData) { + $carrier + ->setName($carrierData['name']) + ->setCode($carrierData['code']) + ; + }); + if ('LIOT' === $carrierData['code']) { + $liot = $carrier; + } + } + + if ($liot && $citerne && $porteur) { + $drivers = ['Eddy', 'Jean-Christophe', 'Etienne', 'Hersand']; + foreach ($drivers as $name) { + $driver = $driverRepo->findOneBy(['name' => $name, 'carrier' => $liot]); + if (!$driver) { + $driver = new Driver(); + ++$created; + } else { + ++$updated; + } + $driver + ->setName($name) + ->setCarrier($liot) + ; + $this->entityManager->persist($driver); + } + + $vehicles = [ + ['plate' => 'GH-684-VZ', 'truck' => $citerne], + ['plate' => 'FW-363-EC', 'truck' => $porteur], + ['plate' => 'FW-370-EC', 'truck' => $porteur], + ['plate' => 'FW-375-EC', 'truck' => $porteur], + ['plate' => 'FY-952-HS', 'truck' => $porteur], + ]; + foreach ($vehicles as $vehicleData) { + $vehicle = $vehicleRepo->findOneBy(['plate' => $vehicleData['plate']]); + if (!$vehicle) { + $vehicle = new Vehicle(); + ++$created; + } else { + ++$updated; + } + $vehicle + ->setPlate($vehicleData['plate']) + ->setCarrier($liot) + ->setTruck($vehicleData['truck']) + ; + $this->entityManager->persist($vehicle); + } + } else { + $io->warning('Transport data not fully available; drivers/vehicles skipped.'); + } + + $merchandiseTypes = [ + ['label' => 'Foin', 'code' => 'FOIN'], + ['label' => 'Paille', 'code' => 'PAILLE'], + ['label' => 'Granule', 'code' => 'GRANULE'], + ]; + foreach ($merchandiseTypes as $type) { + $upsertByCode(MerchandiseType::class, $type['code'], static function (MerchandiseType $entity) use ($type) { + $entity + ->setLabel($type['label']) + ->setCode($type['code']) + ; + }); + } + + $pelletTypes = [ + ['label' => 'JB croissance', 'code' => 'K750'], + ['label' => 'Genisse herbe', 'code' => 'K500'], + ['label' => 'Bovistart melasse ferme', 'code' => 'K130'], + ['label' => 'Bovin mise en forme', 'code' => 'K400'], + ]; + foreach ($pelletTypes as $type) { + $upsertByCode(PelletType::class, $type['code'], static function (PelletType $entity) use ($type) { + $entity + ->setLabel($type['label']) + ->setCode($type['code']) + ; + }); + } + + $buildings = [ + ['label' => 'Bâtiment 1', 'code' => 'B1'], + ['label' => 'Bâtiment 2', 'code' => 'B2'], + ['label' => 'Bâtiment 3', 'code' => 'B3'], + ]; + foreach ($buildings as $buildingData) { + $upsertByCode(Building::class, $buildingData['code'], static function (Building $entity) use ($buildingData) { + $entity + ->setLabel($buildingData['label']) + ->setCode($buildingData['code']) + ; + }); + } + + $receptionTypes = [ + ['label' => 'Marchandises', 'code' => 'MARCHANDISES'], + ['label' => 'Bovins', 'code' => 'BOVINS'], + ]; + foreach ($receptionTypes as $type) { + $upsertByCode(ReceptionType::class, $type['code'], static function (ReceptionType $entity) use ($type) { + $entity + ->setLabel($type['label']) + ->setCode($type['code']) + ; + }); + } + + $address = $addressRepo->findOneBy([ + 'label' => 'LIOT CHATELLERAULT', + 'postalCode' => '86100', + ]); + if (!$address) { + $address = new Address(); + ++$created; + } else { + ++$updated; + } + $address + ->setLabel('LIOT CHATELLERAULT') + ->setStreet("14 Allée d'Argenson") + ->setStreet2('ZI Nord') + ->setPostalCode('86100') + ->setCity('CHATELLERAULT') + ->setCountryCode('FR') + ; + $this->entityManager->persist($address); + + $supplier = $supplierRepo->findOneBy(['name' => 'LIOT']); + if (!$supplier) { + $supplier = new Supplier(); + ++$created; + } else { + ++$updated; + } + $supplier + ->setName('LIOT') + ->setEmail('lpc.contacts@lpc-liot.fr') + ->setPhone('05.49.20.09.10') + ; + if (!$supplier->getAddresses()->contains($address)) { + $supplier->getAddresses()->add($address); + } + $this->entityManager->persist($supplier); + + $this->entityManager->flush(); + + $io->success(sprintf('Seed completed: %d created, %d updated.', $created, $updated)); + + return Command::SUCCESS; + } +} diff --git a/src/Controller/.gitignore b/src/Controller/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php new file mode 100644 index 0000000..260a894 --- /dev/null +++ b/src/DataFixtures/AppFixtures.php @@ -0,0 +1,27 @@ +flush(); + } + + public function getDependencies(): array + { + return [ + TransportFixtures::class, + ReferenceFixtures::class, + SupplierFixtures::class, + UserFixtures::class, + ]; + } +} diff --git a/src/DataFixtures/ReferenceFixtures.php b/src/DataFixtures/ReferenceFixtures.php new file mode 100644 index 0000000..a86a13c --- /dev/null +++ b/src/DataFixtures/ReferenceFixtures.php @@ -0,0 +1,72 @@ + 'Foin', 'code' => 'FOIN'], + ['label' => 'Paille', 'code' => 'PAILLE'], + ['label' => 'Granule', 'code' => 'GRANULE'], + ]; + foreach ($merchandiseTypes as $type) { + $merchandiseType = new MerchandiseType() + ->setLabel($type['label']) + ->setCode($type['code']) + ; + $manager->persist($merchandiseType); + } + + $pelletTypes = [ + ['label' => 'JB croissance', 'code' => 'K750'], + ['label' => 'Genisse herbe', 'code' => 'K500'], + ['label' => 'Bovistart melasse ferme', 'code' => 'K130'], + ['label' => 'Bovin mise en forme', 'code' => 'K400'], + ]; + foreach ($pelletTypes as $type) { + $pelletType = new PelletType() + ->setLabel($type['label']) + ->setCode($type['code']) + ; + $manager->persist($pelletType); + } + + $buildings = [ + ['label' => 'Bâtiment 1', 'code' => 'B1'], + ['label' => 'Bâtiment 2', 'code' => 'B2'], + ['label' => 'Bâtiment 3', 'code' => 'B3'], + ]; + foreach ($buildings as $buildingData) { + $building = new Building() + ->setLabel($buildingData['label']) + ->setCode($buildingData['code']) + ; + $manager->persist($building); + } + + $receptionTypes = [ + ['label' => 'Marchandises', 'code' => 'MARCHANDISES'], + ['label' => 'Bovins', 'code' => 'BOVINS'], + ]; + foreach ($receptionTypes as $type) { + $receptionType = new ReceptionType() + ->setLabel($type['label']) + ->setCode($type['code']) + ; + $manager->persist($receptionType); + } + + $manager->flush(); + } +} diff --git a/src/DataFixtures/SupplierFixtures.php b/src/DataFixtures/SupplierFixtures.php new file mode 100644 index 0000000..499a7a2 --- /dev/null +++ b/src/DataFixtures/SupplierFixtures.php @@ -0,0 +1,38 @@ +setLabel('LIOT CHATELLERAULT') + ->setStreet("14 Allée d'Argenson") + ->setStreet2('ZI Nord') + ->setPostalCode('86100') + ->setCity('CHATELLERAULT') + ->setCountryCode('FR') + ; + + $supplier = new Supplier() + ->setName('LIOT') + ->setEmail('lpc.contacts@lpc-liot.fr') + ->setPhone('05.49.20.09.10') + ; + + $supplier->getAddresses()->add($address); + + $manager->persist($address); + $manager->persist($supplier); + + $manager->flush(); + } +} diff --git a/src/DataFixtures/TransportFixtures.php b/src/DataFixtures/TransportFixtures.php new file mode 100644 index 0000000..f14b9f9 --- /dev/null +++ b/src/DataFixtures/TransportFixtures.php @@ -0,0 +1,64 @@ +setName('Citerne'); + $porteur = new Truck()->setName('Porteur'); + + $manager->persist($citerne); + $manager->persist($porteur); + + $liot = new Carrier() + ->setName('LIOT') + ->setCode('LIOT') + ; + $luiMeme = new Carrier() + ->setName('LUI-MEME') + ->setCode('LUI-MEME') + ; + + $manager->persist($liot); + $manager->persist($luiMeme); + + $drivers = ['Eddy', 'Jean-Christophe', 'Etienne', 'Hersand']; + foreach ($drivers as $name) { + $driver = new Driver() + ->setName($name) + ->setCarrier($liot) + ; + $manager->persist($driver); + } + + $citerneVehicle = new Vehicle() + ->setPlate('GH-684-VZ') + ->setCarrier($liot) + ->setTruck($citerne) + ; + $manager->persist($citerneVehicle); + + $porteurPlates = ['FW-363-EC', 'FW-370-EC', 'FW-375-EC', 'FY-952-HS']; + foreach ($porteurPlates as $plate) { + $vehicle = new Vehicle() + ->setPlate($plate) + ->setCarrier($liot) + ->setTruck($porteur) + ; + $manager->persist($vehicle); + } + + $manager->flush(); + } +} diff --git a/src/DataFixtures/UserFixtures.php b/src/DataFixtures/UserFixtures.php new file mode 100644 index 0000000..9fbf52c --- /dev/null +++ b/src/DataFixtures/UserFixtures.php @@ -0,0 +1,27 @@ +setUsername('admin') + ->setRoles(['ROLE_ADMIN']) + ; + + $admin->setPassword( + '$2y$13$ZuB4LRD1i5Arc34CEO54FeUyQaIf3jamLf6caFK9v8TBMA5RcmIke' + ); + + $manager->persist($admin); + $manager->flush(); + } +} diff --git a/symfony.lock b/symfony.lock index 38273ee..a6b456c 100644 --- a/symfony.lock +++ b/symfony.lock @@ -36,6 +36,18 @@ "src/Repository/.gitignore" ] }, + "doctrine/doctrine-fixtures-bundle": { + "version": "4.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.0", + "ref": "1f5514cfa15b947298df4d771e694e578d4c204d" + }, + "files": [ + "src/DataFixtures/AppFixtures.php" + ] + }, "doctrine/doctrine-migrations-bundle": { "version": "4.0", "recipe": {