diff --git a/.ddev/config.yaml b/.ddev/config.yaml
index 85bd9b6..84a2edb 100644
--- a/.ddev/config.yaml
+++ b/.ddev/config.yaml
@@ -16,6 +16,7 @@ composer_version: "2"
web_environment: []
nodejs_version: "16"
disable_upload_dirs_warning: true
+timezone: Europe/Berlin
# Key features of ddev's config.yaml:
diff --git a/composer.json b/composer.json
index cc9c08f..5878d0d 100644
--- a/composer.json
+++ b/composer.json
@@ -37,6 +37,7 @@
"symfony/string": "6.3.*",
"symfony/translation": "6.3.*",
"symfony/twig-bundle": "6.3.*",
+ "symfony/uid": "6.3.*",
"symfony/validator": "6.3.*",
"symfony/web-link": "6.3.*",
"symfony/yaml": "6.3.*",
diff --git a/composer.lock b/composer.lock
index fa403d1..f3beef0 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": "321e741fbc0979c63e04752cd30aba64",
+ "content-hash": "ef1d5f48771d4c4589b11cab4cc343fa",
"packages": [
{
"name": "doctrine/cache",
@@ -5208,6 +5208,88 @@
],
"time": "2022-11-03T14:55:06+00:00"
},
+ {
+ "name": "symfony/polyfill-uuid",
+ "version": "v1.28.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-uuid.git",
+ "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/9c44518a5aff8da565c8a55dbe85d2769e6f630e",
+ "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-uuid": "*"
+ },
+ "suggest": {
+ "ext-uuid": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Uuid\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Grégoire Pineau",
+ "email": "lyrixx@lyrixx.info"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for uuid functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "uuid"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-uuid/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-26T09:26:14+00:00"
+ },
{
"name": "symfony/process",
"version": "v6.3.0",
@@ -6626,6 +6708,80 @@
],
"time": "2023-05-06T09:53:41+00:00"
},
+ {
+ "name": "symfony/uid",
+ "version": "v6.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/uid.git",
+ "reference": "01b0f20b1351d997711c56f1638f7a8c3061e384"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/uid/zipball/01b0f20b1351d997711c56f1638f7a8c3061e384",
+ "reference": "01b0f20b1351d997711c56f1638f7a8c3061e384",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-uuid": "^1.15"
+ },
+ "require-dev": {
+ "symfony/console": "^5.4|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Uid\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Grégoire Pineau",
+ "email": "lyrixx@lyrixx.info"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to generate and represent UIDs",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "UID",
+ "ulid",
+ "uuid"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/uid/tree/v6.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-04-08T07:25:02+00:00"
+ },
{
"name": "symfony/validator",
"version": "v6.3.1",
@@ -9503,7 +9659,8 @@
"platform": {
"php": ">=8.1",
"ext-ctype": "*",
- "ext-iconv": "*"
+ "ext-iconv": "*",
+ "ext-simplexml": "*"
},
"platform-dev": [],
"plugin-api-version": "2.3.0"
diff --git a/config/packages/uid.yaml b/config/packages/uid.yaml
new file mode 100644
index 0000000..0152094
--- /dev/null
+++ b/config/packages/uid.yaml
@@ -0,0 +1,4 @@
+framework:
+ uid:
+ default_uuid_version: 7
+ time_based_uuid_version: 7
diff --git a/config/services.yaml b/config/services.yaml
index c13812c..16c9545 100644
--- a/config/services.yaml
+++ b/config/services.yaml
@@ -19,6 +19,10 @@ services:
- '../src/Entity/'
- '../src/Kernel.php'
+ App\EventListener\TimestampableEntitySubscriber:
+ tags:
+ - { name: 'doctrine.event_subscriber', connection: 'default' }
+
App\BusProNet\ApiClient:
arguments:
$options: { 'bpn_username': '%bpn_username%', 'bpn_password': '%bpn_password%', 'bpn_url': '%bpn_url%' }
diff --git a/migrations/Version20230707115553.php b/migrations/Version20230707115553.php
deleted file mode 100644
index 92a7712..0000000
--- a/migrations/Version20230707115553.php
+++ /dev/null
@@ -1,33 +0,0 @@
-addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, bus_pro_address_id INT NOT NULL, email VARCHAR(255) NOT NULL, first_name VARCHAR(255) NOT NULL, last_name VARCHAR(255) NOT NULL, role VARCHAR(255) NOT NULL, last_login_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
- $this->addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', available_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', delivered_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
- }
-
- public function down(Schema $schema): void
- {
- // this down() migration is auto-generated, please modify it to your needs
- $this->addSql('DROP TABLE user');
- $this->addSql('DROP TABLE messenger_messages');
- }
-}
diff --git a/migrations/Version20230707135208.php b/migrations/Version20230707135208.php
deleted file mode 100644
index b02614e..0000000
--- a/migrations/Version20230707135208.php
+++ /dev/null
@@ -1,31 +0,0 @@
-addSql('ALTER TABLE user ADD bus_pro_person_id INT NOT NULL');
- }
-
- public function down(Schema $schema): void
- {
- // this down() migration is auto-generated, please modify it to your needs
- $this->addSql('ALTER TABLE user DROP bus_pro_person_id');
- }
-}
diff --git a/migrations/Version20230902154310.php b/migrations/Version20230902154310.php
deleted file mode 100644
index 5470e70..0000000
--- a/migrations/Version20230902154310.php
+++ /dev/null
@@ -1,31 +0,0 @@
-addSql('ALTER TABLE user ADD roles LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', DROP role');
- }
-
- public function down(Schema $schema): void
- {
- // this down() migration is auto-generated, please modify it to your needs
- $this->addSql('ALTER TABLE user ADD role VARCHAR(255) NOT NULL, DROP roles');
- }
-}
diff --git a/migrations/Version20230902155910.php b/migrations/Version20230902155910.php
deleted file mode 100644
index 7a68998..0000000
--- a/migrations/Version20230902155910.php
+++ /dev/null
@@ -1,31 +0,0 @@
-addSql('ALTER TABLE user ADD address_street VARCHAR(255) DEFAULT NULL, ADD address_post_code VARCHAR(255) DEFAULT NULL, ADD address_city VARCHAR(255) DEFAULT NULL, ADD address_country VARCHAR(255) DEFAULT NULL');
- }
-
- public function down(Schema $schema): void
- {
- // this down() migration is auto-generated, please modify it to your needs
- $this->addSql('ALTER TABLE user DROP address_street, DROP address_post_code, DROP address_city, DROP address_country');
- }
-}
diff --git a/migrations/Version20230902163321.php b/migrations/Version20230902163321.php
deleted file mode 100644
index 814218c..0000000
--- a/migrations/Version20230902163321.php
+++ /dev/null
@@ -1,31 +0,0 @@
-addSql('ALTER TABLE user ADD profile_bus_pro_address_id INT NOT NULL, ADD profile_bus_pro_person_id INT NOT NULL, ADD profile_title VARCHAR(255) NOT NULL, ADD profile_salutation VARCHAR(255) NOT NULL, ADD profile_first_name VARCHAR(255) NOT NULL, ADD profile_last_name VARCHAR(255) NOT NULL, ADD profile_gender VARCHAR(1) DEFAULT NULL, ADD profile_date_of_birth DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', ADD profile_street VARCHAR(255) DEFAULT NULL, ADD profile_post_code VARCHAR(255) DEFAULT NULL, ADD profile_city VARCHAR(255) DEFAULT NULL, ADD profile_country VARCHAR(255) DEFAULT NULL, ADD profile_phone VARCHAR(255) DEFAULT NULL, ADD profile_mobile VARCHAR(255) DEFAULT NULL, DROP bus_pro_address_id, DROP first_name, DROP last_name, DROP bus_pro_person_id, DROP address_street, DROP address_post_code, DROP address_city, DROP address_country');
- }
-
- public function down(Schema $schema): void
- {
- // this down() migration is auto-generated, please modify it to your needs
- $this->addSql('ALTER TABLE user ADD bus_pro_address_id INT NOT NULL, ADD first_name VARCHAR(255) NOT NULL, ADD last_name VARCHAR(255) NOT NULL, ADD bus_pro_person_id INT NOT NULL, ADD address_street VARCHAR(255) DEFAULT NULL, ADD address_post_code VARCHAR(255) DEFAULT NULL, ADD address_city VARCHAR(255) DEFAULT NULL, ADD address_country VARCHAR(255) DEFAULT NULL, DROP profile_bus_pro_address_id, DROP profile_bus_pro_person_id, DROP profile_title, DROP profile_salutation, DROP profile_first_name, DROP profile_last_name, DROP profile_gender, DROP profile_date_of_birth, DROP profile_street, DROP profile_post_code, DROP profile_city, DROP profile_country, DROP profile_phone, DROP profile_mobile');
- }
-}
diff --git a/migrations/Version20230902164055.php b/migrations/Version20230902164055.php
deleted file mode 100644
index 902d966..0000000
--- a/migrations/Version20230902164055.php
+++ /dev/null
@@ -1,31 +0,0 @@
-addSql('ALTER TABLE user ADD address_street VARCHAR(255) DEFAULT NULL, ADD address_post_code VARCHAR(255) DEFAULT NULL, ADD address_city VARCHAR(255) DEFAULT NULL, ADD address_country VARCHAR(255) DEFAULT NULL, ADD communication_phone VARCHAR(255) DEFAULT NULL, ADD communication_mobile VARCHAR(255) DEFAULT NULL, ADD communication_email VARCHAR(255) DEFAULT NULL, DROP profile_phone, DROP profile_mobile');
- }
-
- public function down(Schema $schema): void
- {
- // this down() migration is auto-generated, please modify it to your needs
- $this->addSql('ALTER TABLE user ADD profile_phone VARCHAR(255) DEFAULT NULL, ADD profile_mobile VARCHAR(255) DEFAULT NULL, DROP address_street, DROP address_post_code, DROP address_city, DROP address_country, DROP communication_phone, DROP communication_mobile, DROP communication_email');
- }
-}
diff --git a/migrations/Version20230912134406.php b/migrations/Version20230912134406.php
new file mode 100644
index 0000000..0c93459
--- /dev/null
+++ b/migrations/Version20230912134406.php
@@ -0,0 +1,37 @@
+addSql('CREATE TABLE teamer (id INT AUTO_INCREMENT NOT NULL, first_name VARCHAR(255) NOT NULL, last_name VARCHAR(255) NOT NULL, gender VARCHAR(1) NOT NULL, date_of_birth DATE NOT NULL COMMENT \'(DC2Type:date_immutable)\', academic_title VARCHAR(255) DEFAULT NULL, salutation VARCHAR(255) DEFAULT NULL, nationality VARCHAR(255) DEFAULT NULL, tax_id VARCHAR(255) DEFAULT NULL, health_insurance_company VARCHAR(255) DEFAULT NULL, status VARCHAR(32) NOT NULL, remarks LONGTEXT DEFAULT NULL, address_street VARCHAR(255) DEFAULT NULL, address_post_code VARCHAR(255) DEFAULT NULL, address_city VARCHAR(255) DEFAULT NULL, address_country VARCHAR(255) DEFAULT NULL, communication_phone VARCHAR(255) DEFAULT NULL, communication_mobile VARCHAR(255) DEFAULT NULL, communication_email VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+ $this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, teamer_id INT DEFAULT NULL, bus_pro_address_id INT NOT NULL, bus_pro_person_id INT NOT NULL, email VARCHAR(255) NOT NULL, roles LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', last_login_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', UNIQUE INDEX UNIQ_8D93D6494302FF75 (teamer_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+ $this->addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', available_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', delivered_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+ $this->addSql('ALTER TABLE user ADD CONSTRAINT FK_8D93D6494302FF75 FOREIGN KEY (teamer_id) REFERENCES teamer (id)');
+ }
+
+ public function down(Schema $schema): void
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->addSql('ALTER TABLE user DROP FOREIGN KEY FK_8D93D6494302FF75');
+ $this->addSql('DROP TABLE teamer');
+ $this->addSql('DROP TABLE user');
+ $this->addSql('DROP TABLE messenger_messages');
+ }
+}
diff --git a/migrations/Version20230912134737.php b/migrations/Version20230912134737.php
new file mode 100644
index 0000000..62dc629
--- /dev/null
+++ b/migrations/Version20230912134737.php
@@ -0,0 +1,37 @@
+addSql('ALTER TABLE teamer ADD uuid VARCHAR(36) NOT NULL');
+ $this->addSql('CREATE UNIQUE INDEX UNIQ_15B751AED17F50A6 ON teamer (uuid)');
+ $this->addSql('ALTER TABLE user ADD uuid VARCHAR(36) NOT NULL');
+ $this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D649D17F50A6 ON user (uuid)');
+ }
+
+ public function down(Schema $schema): void
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->addSql('DROP INDEX UNIQ_8D93D649D17F50A6 ON user');
+ $this->addSql('ALTER TABLE user DROP uuid');
+ $this->addSql('DROP INDEX UNIQ_15B751AED17F50A6 ON teamer');
+ $this->addSql('ALTER TABLE teamer DROP uuid');
+ }
+}
diff --git a/migrations/Version20230902164150.php b/migrations/Version20230912140412.php
similarity index 56%
rename from migrations/Version20230902164150.php
rename to migrations/Version20230912140412.php
index 9e86426..79f7bc0 100644
--- a/migrations/Version20230902164150.php
+++ b/migrations/Version20230912140412.php
@@ -10,7 +10,7 @@ use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
-final class Version20230902164150 extends AbstractMigration
+final class Version20230912140412 extends AbstractMigration
{
public function getDescription(): string
{
@@ -20,12 +20,12 @@ final class Version20230902164150 extends AbstractMigration
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
- $this->addSql('ALTER TABLE user DROP profile_street, DROP profile_post_code, DROP profile_city, DROP profile_country');
+ $this->addSql('ALTER TABLE teamer ADD bank_account_iban VARCHAR(255) DEFAULT NULL, ADD bank_account_bic VARCHAR(255) DEFAULT NULL, ADD bank_account_bank VARCHAR(255) DEFAULT NULL, ADD bank_account_holder VARCHAR(255) DEFAULT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
- $this->addSql('ALTER TABLE user ADD profile_street VARCHAR(255) DEFAULT NULL, ADD profile_post_code VARCHAR(255) DEFAULT NULL, ADD profile_city VARCHAR(255) DEFAULT NULL, ADD profile_country VARCHAR(255) DEFAULT NULL');
+ $this->addSql('ALTER TABLE teamer DROP bank_account_iban, DROP bank_account_bic, DROP bank_account_bank, DROP bank_account_holder');
}
}
diff --git a/migrations/Version20230912142633.php b/migrations/Version20230912142633.php
new file mode 100644
index 0000000..fea2ac9
--- /dev/null
+++ b/migrations/Version20230912142633.php
@@ -0,0 +1,33 @@
+addSql('ALTER TABLE teamer ADD created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', ADD updated_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\'');
+ $this->addSql('ALTER TABLE user ADD created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', ADD updated_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\'');
+ }
+
+ public function down(Schema $schema): void
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->addSql('ALTER TABLE user DROP created_at, DROP updated_at');
+ $this->addSql('ALTER TABLE teamer DROP created_at, DROP updated_at');
+ }
+}
diff --git a/src/BusProNet/UserDataHandler.php b/src/BusProNet/UserDataHandler.php
new file mode 100644
index 0000000..809900a
--- /dev/null
+++ b/src/BusProNet/UserDataHandler.php
@@ -0,0 +1,99 @@
+isAdmin()) {
+ $roles[] = 'ROLE_ADMIN';
+ }
+ if ($crmAttributes->isManager()) {
+ $roles[] = 'ROLE_MANAGER';
+ }
+ if ($crmAttributes->isTeamer()) {
+ $roles[] = 'ROLE_TEAMER';
+ }
+
+ return $roles;
+ }
+
+ public function findLocalUser(ProfileResponse $profileResponse): ?User
+ {
+ // Check if user is already present in local database
+ $repository = $this->entityManager->getRepository(User::class);
+
+ return $repository->findOneBy([
+ 'busProAddressId' => $profileResponse->getAddressId(),
+ 'busProPersonId' => $profileResponse->getPersonId(),
+ ]);
+ }
+
+ public function createLocalUser(ProfileResponse $profileResponse, array $roles, bool $isTeamer = false): User
+ {
+ $user = new User();
+ $user
+ ->setEmail($profileResponse->getCommunication()->getEmail())
+ ->setBusProPersonId($profileResponse->getPersonId())
+ ->setBusProAddressId($profileResponse->getAddressId())
+ ->setRoles($roles)
+ ;
+
+ if (true === $isTeamer) {
+ $teamer = Teamer::fromApiResponse($profileResponse);
+ $user->setTeamer($teamer);
+ $this->logger->info('Create teamer', [
+ 'uuid' => $teamer->getUuid(),
+ ]);
+ }
+
+ $this->entityManager->persist($user);
+ $this->entityManager->flush();
+
+ $this->logger->info('Create user', [
+ 'uuid' => $user->getUuid(),
+ ]);
+
+ return $user;
+ }
+
+ public function updateLocalUser(
+ User $user,
+ ProfileResponse $profileResponse,
+ array $roles,
+ bool $isTeamer = false
+ ): void {
+ $user->setRoles($roles);
+
+ if (true === $isTeamer) {
+ $address = Address::fromApiResponse($profileResponse);
+ $communication = Communication::fromApiResponse($profileResponse);
+ $user
+ ->getTeamer()
+ ->setAddress($address)
+ ->setCommunication($communication)
+ ;
+ }
+
+ $this->entityManager->flush();
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Embeddable/BankAccount.php b/src/Entity/Embeddable/BankAccount.php
new file mode 100644
index 0000000..c126e05
--- /dev/null
+++ b/src/Entity/Embeddable/BankAccount.php
@@ -0,0 +1,77 @@
+iban;
+ }
+
+ if (null === $iban = $this->getIban()) {
+ return '';
+ }
+
+ return sprintf('%s*****%s', substr($iban, 0, 6), substr($iban, -4));
+ }
+
+ public function setIban(?string $iban): static
+ {
+ $this->iban = $iban;
+
+ return $this;
+ }
+
+ public function getBic(): ?string
+ {
+ return $this->bic;
+ }
+
+ public function setBic(?string $bic): static
+ {
+ $this->bic = $bic;
+
+ return $this;
+ }
+
+ public function getBank(): ?string
+ {
+ return $this->bank;
+ }
+
+ public function setBank(?string $bank): static
+ {
+ $this->bank = $bank;
+
+ return $this;
+ }
+
+ public function getHolder(): ?string
+ {
+ return $this->holder;
+ }
+
+ public function setHolder(?string $holder): static
+ {
+ $this->holder = $holder;
+
+ return $this;
+ }
+}
diff --git a/src/Entity/Embeddable/Profile.php b/src/Entity/Embeddable/Profile.php
deleted file mode 100644
index d7c86c6..0000000
--- a/src/Entity/Embeddable/Profile.php
+++ /dev/null
@@ -1,149 +0,0 @@
-setBusProAddressId($profileResponse->getAddressId())
- ->setBusProPersonId($profileResponse->getPersonId())
- ->setTitle($profileResponse->getTitle())
- ->setSalutation($profileResponse->getSalutation())
- ->setFirstName($profileResponse->getFirstName())
- ->setLastName($profileResponse->getName())
- ->setGender($profileResponse->getGender())
- ->setDateOfBirth($profileResponse->getDateOfBirth())
- ;
-
- return $instance;
-
- }
-
- public function getBusProAddressId(): ?int
- {
- return $this->busProAddressId;
- }
-
- public function setBusProAddressId(int $busProAddressId): static
- {
- $this->busProAddressId = $busProAddressId;
-
- return $this;
- }
-
- public function getBusProPersonId(): ?int
- {
- return $this->busProPersonId;
- }
-
- public function setBusProPersonId(int $busProPersonId): static
- {
- $this->busProPersonId = $busProPersonId;
-
- return $this;
- }
-
- public function getSalutation(): ?string
- {
- return $this->salutation;
- }
-
- public function setSalutation(?string $salutation): static
- {
- $this->salutation = $salutation;
-
- return $this;
- }
-
- public function getTitle(): ?string
- {
- return $this->title;
- }
-
- public function setTitle(?string $title): static
- {
- $this->title = $title;
-
- return $this;
- }
-
- public function getFirstName(): ?string
- {
- return $this->firstName;
- }
-
- public function setFirstName(string $firstName): static
- {
- $this->firstName = $firstName;
-
- return $this;
- }
-
- public function getLastName(): ?string
- {
- return $this->lastName;
- }
-
- public function setLastName(string $lastName): static
- {
- $this->lastName = $lastName;
-
- return $this;
- }
-
- public function getGender(): ?string
- {
- return $this->gender;
- }
-
- public function setGender(?string $gender): static
- {
- $this->gender = $gender;
-
- return $this;
- }
-
- public function getDateOfBirth(): ?\DateTimeImmutable
- {
- return $this->dateOfBirth;
- }
-
- public function setDateOfBirth(?\DateTimeImmutable $dateOfBirth): static
- {
- $this->dateOfBirth = $dateOfBirth;
-
- return $this;
- }
-}
\ No newline at end of file
diff --git a/src/Entity/Teamer.php b/src/Entity/Teamer.php
new file mode 100644
index 0000000..abfb680
--- /dev/null
+++ b/src/Entity/Teamer.php
@@ -0,0 +1,284 @@
+uuid = Uuid::v4();
+ $this->status = static::STATUS_NEW;
+ }
+
+ public static function fromApiResponse(ProfileResponse $profileResponse): static
+ {
+ $instance = new static();
+
+ $instance
+ ->setAcademicTitle($profileResponse->getTitle())
+ ->setSalutation($profileResponse->getSalutation())
+ ->setFirstName($profileResponse->getFirstName())
+ ->setLastName($profileResponse->getName())
+ ->setGender($profileResponse->getGender())
+ ->setDateOfBirth($profileResponse->getDateOfBirth())
+ ;
+
+ $address = Address::fromApiResponse($profileResponse);
+ $communication = Communication::fromApiResponse($profileResponse);
+
+ $instance
+ ->setAddress($address)
+ ->setCommunication($communication)
+ ;
+
+ return $instance;
+ }
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getUuid(): string
+ {
+ return $this->uuid;
+ }
+
+ public function getFirstName(): ?string
+ {
+ return $this->firstName;
+ }
+
+ public function setFirstName(string $firstName): static
+ {
+ $this->firstName = $firstName;
+
+ return $this;
+ }
+
+ public function getLastName(): ?string
+ {
+ return $this->lastName;
+ }
+
+ public function setLastName(string $lastName): static
+ {
+ $this->lastName = $lastName;
+
+ return $this;
+ }
+
+ public function getGender(): ?string
+ {
+ return $this->gender;
+ }
+
+ public function setGender(string $gender): static
+ {
+ $this->gender = $gender;
+
+ return $this;
+ }
+
+ public function getDateOfBirth(): ?\DateTimeImmutable
+ {
+ return $this->dateOfBirth;
+ }
+
+ public function setDateOfBirth(\DateTimeImmutable $dateOfBirth): static
+ {
+ $this->dateOfBirth = $dateOfBirth;
+
+ return $this;
+ }
+
+ public function getAcademicTitle(): ?string
+ {
+ return $this->academicTitle;
+ }
+
+ public function setAcademicTitle(?string $academicTitle): static
+ {
+ $this->academicTitle = $academicTitle;
+
+ return $this;
+ }
+
+ public function getSalutation(): ?string
+ {
+ return $this->salutation;
+ }
+
+ public function setSalutation(?string $salutation): static
+ {
+ $this->salutation = $salutation;
+
+ return $this;
+ }
+
+ public function getNationality(): ?string
+ {
+ return $this->nationality;
+ }
+
+ public function setNationality(string $nationality): static
+ {
+ $this->nationality = $nationality;
+
+ return $this;
+ }
+
+ public function getAddress(): ?Address
+ {
+ return $this->address;
+ }
+
+ public function setAddress(?Address $address): static
+ {
+ $this->address = $address;
+
+ return $this;
+ }
+
+ public function getCommunication(): ?Communication
+ {
+ return $this->communication;
+ }
+
+ public function setCommunication(?Communication $communication): static
+ {
+ $this->communication = $communication;
+
+ return $this;
+ }
+
+ public function getBankAccount(): ?BankAccount
+ {
+ return $this->bankAccount;
+ }
+
+ public function setBankAccount(?BankAccount $bankAccount): static
+ {
+ $this->bankAccount = $bankAccount;
+
+ return $this;
+ }
+
+ public function getTaxId(): ?string
+ {
+ return $this->taxId;
+ }
+
+ public function setTaxId(?string $taxId): static
+ {
+ $this->taxId = $taxId;
+
+ return $this;
+ }
+
+ public function getHealthInsuranceCompany(): ?string
+ {
+ return $this->healthInsuranceCompany;
+ }
+
+ public function setHealthInsuranceCompany(?string $healthInsuranceCompany): static
+ {
+ $this->healthInsuranceCompany = $healthInsuranceCompany;
+
+ return $this;
+ }
+
+ public function getStatus(): ?string
+ {
+ return $this->status;
+ }
+
+ public function setStatus(string $status): static
+ {
+ $this->status = $status;
+
+ return $this;
+ }
+
+ public function getRemarks(): ?string
+ {
+ return $this->remarks;
+ }
+
+ public function setRemarks(?string $remarks): static
+ {
+ $this->remarks = $remarks;
+
+ return $this;
+ }
+}
diff --git a/src/Entity/TimestampableEntityInterface.php b/src/Entity/TimestampableEntityInterface.php
new file mode 100644
index 0000000..0a3cb1e
--- /dev/null
+++ b/src/Entity/TimestampableEntityInterface.php
@@ -0,0 +1,14 @@
+createdAt;
+ }
+
+ public function setCreatedAt(\DateTimeImmutable $createdAt): static
+ {
+ $this->createdAt = $createdAt;
+
+ return $this;
+ }
+
+ public function getUpdatedAt(): ?\DateTimeImmutable
+ {
+ return $this->updatedAt;
+ }
+
+ public function setUpdatedAt(\DateTimeImmutable $updatedAt): static
+ {
+ $this->updatedAt = $updatedAt;
+
+ return $this;
+ }
+}
diff --git a/src/Entity/User.php b/src/Entity/User.php
index 5afaed7..1ac1b4a 100644
--- a/src/Entity/User.php
+++ b/src/Entity/User.php
@@ -2,48 +2,82 @@
namespace App\Entity;
-use App\Entity\Embeddable\Address;
-use App\Entity\Embeddable\Communication;
-use App\Entity\Embeddable\Profile;
+use App\Entity\Traits\TimestampableEntity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
-use Symfony\Component\Validator\Constraints as Assert;
+use Symfony\Component\Uid\Uuid;
#[ORM\Entity(repositoryClass: UserRepository::class)]
-class User implements UserInterface
+class User implements UserInterface, TimestampableEntityInterface
{
+ use TimestampableEntity;
+
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
+ #[ORM\Column(type: 'string', length: 36, unique: true)]
+ private string $uuid;
+
+ #[ORM\Column]
+ private ?int $busProAddressId = null;
+
+ #[ORM\Column]
+ private ?int $busProPersonId = null;
+
#[ORM\Column(length: 255)]
private ?string $email = null;
- #[ORM\Embedded(class: Profile::class)]
- #[Assert\Valid()]
- private ?Profile $profile = null;
-
- #[ORM\Embedded(class: Address::class)]
- #[Assert\Valid()]
- private ?Address $address = null;
-
- #[ORM\Embedded(class: Communication::class)]
- #[Assert\Valid()]
- private ?Communication $communication = null;
-
#[ORM\Column(type: 'json')]
private array $roles = [];
#[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $lastLoginAt = null;
+ #[ORM\OneToOne(cascade: ['persist', 'remove'])]
+ private ?Teamer $teamer = null;
+
+ public function __construct()
+ {
+ $this->uuid = Uuid::v4();
+ }
+
public function getId(): ?int
{
return $this->id;
}
+ public function getUuid(): string
+ {
+ return $this->uuid;
+ }
+
+ public function getBusProAddressId(): ?int
+ {
+ return $this->busProAddressId;
+ }
+
+ public function setBusProAddressId(int $busProAddressId): static
+ {
+ $this->busProAddressId = $busProAddressId;
+
+ return $this;
+ }
+
+ public function getBusProPersonId(): ?int
+ {
+ return $this->busProPersonId;
+ }
+
+ public function setBusProPersonId(int $busProPersonId): static
+ {
+ $this->busProPersonId = $busProPersonId;
+
+ return $this;
+ }
+
public function getEmail(): ?string
{
return $this->email;
@@ -56,42 +90,6 @@ class User implements UserInterface
return $this;
}
- public function getProfile(): ?Profile
- {
- return $this->profile;
- }
-
- public function setProfile(?Profile $profile): static
- {
- $this->profile = $profile;
-
- return $this;
- }
-
- public function getAddress(): ?Address
- {
- return $this->address;
- }
-
- public function setAddress(?Address $address): static
- {
- $this->address = $address;
-
- return $this;
- }
-
- public function getCommunication(): ?Communication
- {
- return $this->communication;
- }
-
- public function setCommunication(?Communication $communication): static
- {
- $this->communication = $communication;
-
- return $this;
- }
-
public function getRoles(): array
{
$roles = ['ROLE_USER', ...$this->roles];
@@ -142,4 +140,16 @@ class User implements UserInterface
{
return $this->email;
}
+
+ public function getTeamer(): ?Teamer
+ {
+ return $this->teamer;
+ }
+
+ public function setTeamer(?Teamer $teamer): static
+ {
+ $this->teamer = $teamer;
+
+ return $this;
+ }
}
diff --git a/src/EventListener/TimestampableEntitySubscriber.php b/src/EventListener/TimestampableEntitySubscriber.php
new file mode 100644
index 0000000..08b14f8
--- /dev/null
+++ b/src/EventListener/TimestampableEntitySubscriber.php
@@ -0,0 +1,41 @@
+getObject();
+
+ if ($entity instanceof TimestampableEntityInterface) {
+ $now = new \DateTimeImmutable('now');
+ $entity->setCreatedAt($now);
+ $entity->setUpdatedAt($now);
+ }
+ }
+
+ public function preUpdate(PreUpdateEventArgs $args): void
+ {
+ $entity = $args->getObject();
+
+ if ($entity instanceof TimestampableEntityInterface) {
+ $now = new \DateTimeImmutable('now');
+ $entity->setUpdatedAt($now);
+ }
+ }
+}
diff --git a/src/Repository/TeamerRepository.php b/src/Repository/TeamerRepository.php
new file mode 100644
index 0000000..23338e6
--- /dev/null
+++ b/src/Repository/TeamerRepository.php
@@ -0,0 +1,66 @@
+
+ *
+ * @method Teamer|null find($id, $lockMode = null, $lockVersion = null)
+ * @method Teamer|null findOneBy(array $criteria, array $orderBy = null)
+ * @method Teamer[] findAll()
+ * @method Teamer[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
+ */
+class TeamerRepository extends ServiceEntityRepository
+{
+ public function __construct(ManagerRegistry $registry)
+ {
+ parent::__construct($registry, Teamer::class);
+ }
+
+ public function save(Teamer $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(Teamer $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+// /**
+// * @return Teamer[] Returns an array of Teamer objects
+// */
+// public function findByExampleField($value): array
+// {
+// return $this->createQueryBuilder('t')
+// ->andWhere('t.exampleField = :val')
+// ->setParameter('val', $value)
+// ->orderBy('t.id', 'ASC')
+// ->setMaxResults(10)
+// ->getQuery()
+// ->getResult()
+// ;
+// }
+
+// public function findOneBySomeField($value): ?Teamer
+// {
+// return $this->createQueryBuilder('t')
+// ->andWhere('t.exampleField = :val')
+// ->setParameter('val', $value)
+// ->getQuery()
+// ->getOneOrNullResult()
+// ;
+// }
+}
diff --git a/src/Security/BpnAuthenticator.php b/src/Security/BpnAuthenticator.php
index 9db48ee..e49cc84 100644
--- a/src/Security/BpnAuthenticator.php
+++ b/src/Security/BpnAuthenticator.php
@@ -6,9 +6,7 @@ use App\BusProNet\ApiClient;
use App\BusProNet\ApiClientException;
use App\BusProNet\Model\CrmAttributesResponse;
use App\BusProNet\Model\ProfileResponse;
-use App\Entity\Embeddable\Address;
-use App\Entity\Embeddable\Communication;
-use App\Entity\Embeddable\Profile;
+use App\BusProNet\UserDataHandler;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
@@ -33,7 +31,8 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent
private readonly UrlGeneratorInterface $urlGenerator,
private readonly EntityManagerInterface $entityManager,
private readonly ApiClient $apiClient,
- private readonly LoggerInterface $logger
+ private readonly LoggerInterface $logger,
+ private readonly UserDataHandler $userDataHandler
) {
}
@@ -72,6 +71,9 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent
$user = $token->getUser();
$user->setLastLoginAt(new \DateTimeImmutable());
+ $this->logger->info('Login', [
+ 'user' => $user->getUserIdentifier(),
+ ]);
$this->entityManager->flush();
@@ -95,56 +97,33 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent
}
// Collect user's roles from CRM attributes
- $roles = [];
+ $roles = $this
+ ->userDataHandler
+ ->collectRoles($crmAttributes)
+ ;
- if ($crmAttributes->isAdmin()) {
- $roles[] = 'ROLE_ADMIN';
- }
- if ($crmAttributes->isManager()) {
- $roles[] = 'ROLE_MANAGER';
- }
- if ($crmAttributes->isTeamer()) {
- $roles[] = 'ROLE_TEAMER';
- }
-
- // Get profile data from API response
- $profile = Profile::fromApiResponse($profileResponse);
- $address = Address::fromApiResponse($profileResponse);
- $communication = Communication::fromApiResponse($profileResponse);
+ // Determine teamer status from CRM attributes
+ $isTeamer = $crmAttributes->isTeamer();
// Check if user is already present in local database
- $repository = $this->entityManager->getRepository(User::class);
+ $user = $this
+ ->userDataHandler
+ ->findLocalUser($profileResponse)
+ ;
- $user = $repository->findOneBy([
- 'profile.busProAddressId' => $profileResponse->getAddressId(),
- 'profile.busProPersonId' => $profileResponse->getPersonId(),
- ]);
-
- // Update existing user's roles and address and return it
+ // Update existing user's roles and teamer data and return it
if (null !== $user) {
- $user
- ->setProfile($profile)
- ->setAddress($address)
- ->setCommunication($communication)
- ->setRoles($roles)
+ $this
+ ->userDataHandler
+ ->updateLocalUser($user, $profileResponse, $roles, $isTeamer)
;
return $user;
}
- // Create new user entity to persist locally otherwise
- $user = new User();
- $user
- ->setEmail($profileResponse->getCommunication()->getEmail())
- ->setProfile($profile)
- ->setAddress($address)
- ->setCommunication($communication)
- ->setRoles($roles)
+ return $this
+ ->userDataHandler
+ ->createLocalUser($profileResponse, $roles, $isTeamer)
;
-
- $this->entityManager->persist($user);
- $this->entityManager->flush();
-
- return $user;
}
}
diff --git a/symfony.lock b/symfony.lock
index d020c1b..bc30400 100644
--- a/symfony.lock
+++ b/symfony.lock
@@ -230,6 +230,18 @@
"templates/base.html.twig"
]
},
+ "symfony/uid": {
+ "version": "6.3",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "6.2",
+ "ref": "d294ad4add3e15d7eb1bae0221588ca89b38e558"
+ },
+ "files": [
+ "config/packages/uid.yaml"
+ ]
+ },
"symfony/validator": {
"version": "6.3",
"recipe": {
diff --git a/tests/BusProNet/ResponseParserTest.php b/tests/BusProNet/ResponseParserTest.php
index 08166a0..aecab1f 100644
--- a/tests/BusProNet/ResponseParserTest.php
+++ b/tests/BusProNet/ResponseParserTest.php
@@ -13,7 +13,7 @@ class ResponseParserTest extends TestCase
{
$content = '853ID, EMail oder Passwort falsch';
- $parser = new ResponseParser();
+ $parser = $this->getParserInstance();
$response = $parser->parseXmlString($content);
$this->assertEquals(853, $response->getCode());
@@ -25,7 +25,7 @@ class ResponseParserTest extends TestCase
{
$content = 'Adressdaten141747224526FrommeBjörnHerrM16.04.1972D119447Emilienstraße 5742853RemscheidDmail@fromme.orgFalse02191-4615837';
- $parser = new ResponseParser();
+ $parser = $this->getParserInstance();
$response = $parser->parseXmlString($content);
$this->assertInstanceOf(ProfileResponse::class, $response);
@@ -38,7 +38,7 @@ class ResponseParserTest extends TestCase
{
$content = 'SelektionCRM141747224526';
- $parser = new ResponseParser();
+ $parser = $this->getParserInstance();
$response = $parser->parseXmlString($content);
$this->assertInstanceOf(CrmAttributesResponse::class, $response);
@@ -48,4 +48,13 @@ class ResponseParserTest extends TestCase
$this->assertCount(3, $response->getAttributeGroups());
$this->assertTrue($response->isTeamer());
}
+
+ private function getParserInstance(): ResponseParser
+ {
+ return new ResponseParser([
+ 'bpn_crm_id_admin' => 1292,
+ 'bpn_crm_id_manager' => 1293,
+ 'bpn_crm_id_teamer' => 1070,
+ ]);
+ }
}
\ No newline at end of file