From cca3a5899f1ac4e621cb76e14f1665a4e71b1825 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Thu, 22 Sep 2022 16:32:30 +0200
Subject: [PATCH 01/41] nsj branch with deployment to dev
---
.github/workflows/deploy-nsj-dev.yml | 73 ++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
create mode 100644 .github/workflows/deploy-nsj-dev.yml
diff --git a/.github/workflows/deploy-nsj-dev.yml b/.github/workflows/deploy-nsj-dev.yml
new file mode 100644
index 000000000..fcba325be
--- /dev/null
+++ b/.github/workflows/deploy-nsj-dev.yml
@@ -0,0 +1,73 @@
+name: deploy-nsj-dev
+
+on:
+ push:
+ branches: [nsj/master]
+
+concurrency:
+ group: environment-nsj-dev
+
+jobs:
+ deploy:
+ name: "Deploy to srs-dev.skauting.cz"
+ environment: srs-dev.skauting.cz
+ runs-on: ubuntu-20.04
+ container:
+ image: skaut/lebeda:8.0
+ env:
+ CONFIG_DATABASE_HOST: ${{ secrets.CONFIG_DATABASE_HOST }}
+ CONFIG_DATABASE_NAME: ${{ secrets.CONFIG_DATABASE_NAME }}
+ CONFIG_DATABASE_PASSWORD: ${{ secrets.CONFIG_DATABASE_PASSWORD }}
+ CONFIG_DATABASE_USER: ${{ secrets.CONFIG_DATABASE_USER }}
+ CONFIG_MAIL_HOST:
+ CONFIG_MAIL_PASSWORD:
+ CONFIG_MAIL_PORT: 0
+ CONFIG_MAIL_SECURE:
+ CONFIG_MAIL_SMTP: false
+ CONFIG_MAIL_USERNAME:
+ CONFIG_SKAUTIS_APPLICATION_ID: ${{ secrets.CONFIG_SKAUTIS_APPLICATION_ID }}
+ CONFIG_SKAUTIS_TEST_MODE: ${{ secrets.CONFIG_SKAUTIS_TEST_MODE }}
+ CONFIG_RECAPTCHA_SITE_KEY: ${{ secrets.CONFIG_RECAPTCHA_SITE_KEY }}
+ CONFIG_RECAPTCHA_SECRET_KEY: ${{ secrets.CONFIG_RECAPTCHA_SECRET_KEY }}
+ DEPLOY_DIRECTORY: ${{ secrets.DEPLOY_DIRECTORY }}
+ DEPLOY_SSH_HOST: ${{ secrets.DEPLOY_SSH_HOST }}
+ DEPLOY_SSH_IP: ${{ secrets.DEPLOY_SSH_IP }}
+ DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
+ DEPLOY_SSH_PORT: ${{ secrets.DEPLOY_SSH_PORT }}
+ DEPLOY_SSH_USERNAME: ${{ secrets.DEPLOY_SSH_USERNAME }}
+ steps:
+ - uses: actions/checkout@v2
+ # Copy & paste from https://github.com/actions/cache/blob/master/examples.md#php---composer
+ - name: Get composer cache
+ id: composer-cache
+ run: |
+ echo "::set-output name=dir::$(composer config cache-files-dir)"
+ - uses: actions/cache@v1
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-composer-
+ - name: Install yarn
+ run: |
+ apt-get update
+ apt-get install -y npm
+ npm install --global yarn
+ #Copy & paste from https://github.com/actions/cache/blob/master/examples.md#node---yarn
+ - name: Get yarn cache
+ id: yarn-cache-dir-path
+ run: echo "::set-output name=dir::$(yarn cache dir)"
+ - uses: actions/cache@v1
+ id: yarn-cache
+ with:
+ path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
+ key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-yarn-
+ - name: Setup SSH key and deploy
+ run: |
+ mkdir -p /root/.ssh
+ ssh-keyscan -H "${DEPLOY_SSH_HOST}","${DEPLOY_SSH_IP}" >> /root/.ssh/known_hosts
+ eval `ssh-agent -s`
+ echo "${DEPLOY_SSH_KEY}" | tr -d '\r' | ssh-add -
+ phing deploy
From 71a168660c2f4c4521c700f26fb56ea172602907 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Thu, 22 Sep 2022 19:07:26 +0200
Subject: [PATCH 02/41] php 8.1 image
---
.github/workflows/deploy-nsj-dev.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/deploy-nsj-dev.yml b/.github/workflows/deploy-nsj-dev.yml
index fcba325be..7fa0437a7 100644
--- a/.github/workflows/deploy-nsj-dev.yml
+++ b/.github/workflows/deploy-nsj-dev.yml
@@ -13,7 +13,7 @@ jobs:
environment: srs-dev.skauting.cz
runs-on: ubuntu-20.04
container:
- image: skaut/lebeda:8.0
+ image: skaut/lebeda:8.1
env:
CONFIG_DATABASE_HOST: ${{ secrets.CONFIG_DATABASE_HOST }}
CONFIG_DATABASE_NAME: ${{ secrets.CONFIG_DATABASE_NAME }}
From 48975fe5904608c13cd475670e7849f53cdd691b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Thu, 6 Oct 2022 23:25:48 +0200
Subject: [PATCH 03/41] execute test for nsj/master (#887)
---
.github/workflows/test.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 77a8ef8ed..41ec20390 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -2,9 +2,9 @@ name: test
on:
push:
- branches: [master]
+ branches: [master, nsj/master]
pull_request:
- branches: [master]
+ branches: [master, nsj/master]
concurrency:
group: test-${{ github.ref }}
From da8b12ba0ae27bcc5b08da647ba6d4431b06c521 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Sat, 15 Oct 2022 19:31:23 +0200
Subject: [PATCH 04/41] =?UTF-8?q?=C3=9Aprava=20datab=C3=A1zov=C3=A9ho=20mo?=
=?UTF-8?q?delu=20pro=20skupiny=20(#886)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* db model for groups
* role getters
* patrol, troop entities
* migration
* coding standard
* type default value
* db model for groups
* migration
* fixes
* fixes
* warnings nullable
---
app/Model/Acl/Role.php | 65 ++++++++
app/Model/Enums/RoleType.php | 30 ++++
app/Model/Payment/Payment.php | 18 +++
app/Model/User/Patrol.php | 80 ++++++++++
app/Model/User/Troop.php | 229 +++++++++++++++++++++++++++
app/Model/User/UserGroupRole.php | 90 +++++++++++
migrations/Version20221015133440.php | 38 +++++
7 files changed, 550 insertions(+)
create mode 100644 app/Model/Enums/RoleType.php
create mode 100644 app/Model/User/Patrol.php
create mode 100644 app/Model/User/Troop.php
create mode 100644 app/Model/User/UserGroupRole.php
create mode 100644 migrations/Version20221015133440.php
diff --git a/app/Model/Acl/Role.php b/app/Model/Acl/Role.php
index 099aca455..a691f06cf 100644
--- a/app/Model/Acl/Role.php
+++ b/app/Model/Acl/Role.php
@@ -6,6 +6,7 @@
use App\Model\Cms\Page;
use App\Model\Cms\Tag;
+use App\Model\Enums\RoleType;
use App\Model\Program\Category;
use App\Model\User\User;
use DateTimeImmutable;
@@ -125,6 +126,12 @@ class Role
#[ORM\Column(type: 'boolean')]
protected bool $systemRole = true;
+ /**
+ * Typ role - individuální/družinová/skupinová.
+ */
+ #[ORM\Column(type: 'string')]
+ protected string $type = RoleType::INDIVIDUAL;
+
/**
* Registrovatelná role. Lze vybrat v přihlášce.
*/
@@ -174,6 +181,24 @@ class Role
#[ORM\Column(type: 'integer')]
protected int $minimumAge = 0;
+ /**
+ * Varování při příliš nízkém věku.
+ */
+ #[ORM\Column(type: 'string', nullable: true)]
+ protected ?string $minimumAgeWarning;
+
+ /**
+ * Maximální věk.
+ */
+ #[ORM\Column(type: 'integer')]
+ protected int $maximumAge = 150;
+
+ /**
+ * Varování při příliš vysokém věku.
+ */
+ #[ORM\Column(type: 'string', nullable: true)]
+ protected ?string $maximumAgeWarning;
+
/**
* Synchronizovat účastníky v roli se skautIS.
*/
@@ -375,6 +400,16 @@ public function setSystemRole(bool $systemRole): void
$this->systemRole = $systemRole;
}
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ public function setType(string $type): void
+ {
+ $this->type = $type;
+ }
+
public function isRegisterable(): bool
{
return $this->registerable;
@@ -467,6 +502,36 @@ public function setMinimumAge(int $age): void
$this->minimumAge = $age;
}
+ public function getMinimumAgeWarning(): ?string
+ {
+ return $this->minimumAgeWarning;
+ }
+
+ public function setMinimumAgeWarning(?string $minimumAgeWarning): void
+ {
+ $this->minimumAgeWarning = $minimumAgeWarning;
+ }
+
+ public function getMaximumAge(): int
+ {
+ return $this->maximumAge;
+ }
+
+ public function setMaximumAge(int $maximumAge): void
+ {
+ $this->maximumAge = $maximumAge;
+ }
+
+ public function getMaximumAgeWarning(): ?string
+ {
+ return $this->maximumAgeWarning;
+ }
+
+ public function setMaximumAgeWarning(?string $maximumAgeWarning): void
+ {
+ $this->maximumAgeWarning = $maximumAgeWarning;
+ }
+
public function isSyncedWithSkautIS(): bool
{
return $this->syncedWithSkautIS;
diff --git a/app/Model/Enums/RoleType.php b/app/Model/Enums/RoleType.php
new file mode 100644
index 000000000..574a9769e
--- /dev/null
+++ b/app/Model/Enums/RoleType.php
@@ -0,0 +1,30 @@
+
+ */
+ #[ORM\OneToMany(targetEntity: Troop::class, mappedBy: 'payment', cascade: ['persist'])]
+ protected Collection $pairedTroops;
+
/**
* Stav platby.
*/
@@ -86,6 +95,7 @@ class Payment
public function __construct()
{
$this->pairedApplications = new ArrayCollection();
+ $this->pairedTroops = new ArrayCollection();
}
public function getId(): ?int
@@ -221,6 +231,14 @@ public function getPairedValidApplicationsText(): string
return implode(', ', $usersTexts);
}
+ /**
+ * @return Collection
+ */
+ public function getPairedTroops(): Collection
+ {
+ return $this->pairedTroops;
+ }
+
public function getState(): string
{
return $this->state;
diff --git a/app/Model/User/Patrol.php b/app/Model/User/Patrol.php
new file mode 100644
index 000000000..8172ede41
--- /dev/null
+++ b/app/Model/User/Patrol.php
@@ -0,0 +1,80 @@
+
+ */
+ #[ORM\OneToMany(mappedBy: 'patrol', targetEntity: UserGroupRole::class, cascade: ['persist'])]
+ protected Collection $usersRoles;
+
+ public function __construct()
+ {
+ $this->usersRoles = new ArrayCollection();
+ }
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function setName(string $name): void
+ {
+ $this->name = $name;
+ }
+
+ public function getTroop(): Troop
+ {
+ return $this->troop;
+ }
+
+ public function setTroop(Troop $troop): void
+ {
+ $this->troop = $troop;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getUsersRoles(): Collection
+ {
+ return $this->usersRoles;
+ }
+}
diff --git a/app/Model/User/Troop.php b/app/Model/User/Troop.php
new file mode 100644
index 000000000..deb6bc8c8
--- /dev/null
+++ b/app/Model/User/Troop.php
@@ -0,0 +1,229 @@
+
+ */
+ #[ORM\OneToMany(mappedBy: 'troop', targetEntity: Patrol::class, cascade: ['persist'])]
+ protected Collection $patrols;
+
+ /**
+ * Uživatelé.
+ *
+ * @var Collection
+ */
+ #[ORM\OneToMany(mappedBy: 'troop', targetEntity: UserGroupRole::class, cascade: ['persist'])]
+ protected Collection $usersRoles;
+
+ /**
+ * Poplatek za oddíl.
+ */
+ #[ORM\Column(type: 'integer')]
+ protected int $fee;
+
+ /**
+ * Variabilní symbol.
+ */
+ #[ORM\ManyToOne(targetEntity: VariableSymbol::class, cascade: ['persist'])]
+ protected VariableSymbol $variableSymbol;
+
+ /**
+ * Datum podání přihlášky.
+ */
+ #[ORM\Column(type: 'datetime_immutable')]
+ protected DateTimeImmutable $applicationDate;
+
+ /**
+ * Datum splatnosti.
+ */
+ #[ORM\Column(type: 'date_immutable', nullable: true)]
+ protected ?DateTimeImmutable $maturityDate = null;
+
+ /**
+ * Platební metoda.
+ */
+ #[ORM\Column(type: 'string', nullable: true)]
+ protected ?string $paymentMethod = null;
+
+ /**
+ * Datum zaplacení.
+ */
+ #[ORM\Column(type: 'date_immutable', nullable: true)]
+ protected ?DateTimeImmutable $paymentDate = null;
+
+ /**
+ * Spárovaná platba.
+ */
+ #[ORM\ManyToOne(targetEntity: Payment::class, inversedBy: 'pairedTroops', cascade: ['persist'])]
+ protected ?Payment $payment = null;
+
+ /**
+ * Příjmový doklad. Používá se pro generování id.
+ */
+ #[ORM\ManyToOne(targetEntity: IncomeProof::class, cascade: ['persist'])]
+ protected ?IncomeProof $incomeProof = null;
+
+ /**
+ * Stav přihlášky.
+ */
+ #[ORM\Column(type: 'string')]
+ protected ?string $state = null;
+
+ public function __construct()
+ {
+ $this->patrols = new ArrayCollection();
+ $this->usersRoles = new ArrayCollection();
+ }
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function setName(string $name): void
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getPatrols(): Collection
+ {
+ return $this->patrols;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getUsersRoles(): Collection
+ {
+ return $this->usersRoles;
+ }
+
+ public function getFee(): int
+ {
+ return $this->fee;
+ }
+
+ public function setFee(int $fee): void
+ {
+ $this->fee = $fee;
+ }
+
+ public function getVariableSymbol(): VariableSymbol
+ {
+ return $this->variableSymbol;
+ }
+
+ public function setVariableSymbol(VariableSymbol $variableSymbol): void
+ {
+ $this->variableSymbol = $variableSymbol;
+ }
+
+ public function getApplicationDate(): DateTimeImmutable
+ {
+ return $this->applicationDate;
+ }
+
+ public function setApplicationDate(DateTimeImmutable $applicationDate): void
+ {
+ $this->applicationDate = $applicationDate;
+ }
+
+ public function getMaturityDate(): ?DateTimeImmutable
+ {
+ return $this->maturityDate;
+ }
+
+ public function setMaturityDate(?DateTimeImmutable $maturityDate): void
+ {
+ $this->maturityDate = $maturityDate;
+ }
+
+ public function getPaymentMethod(): ?string
+ {
+ return $this->paymentMethod;
+ }
+
+ public function setPaymentMethod(?string $paymentMethod): void
+ {
+ $this->paymentMethod = $paymentMethod;
+ }
+
+ public function getPaymentDate(): ?DateTimeImmutable
+ {
+ return $this->paymentDate;
+ }
+
+ public function setPaymentDate(?DateTimeImmutable $paymentDate): void
+ {
+ $this->paymentDate = $paymentDate;
+ }
+
+ public function getPayment(): ?Payment
+ {
+ return $this->payment;
+ }
+
+ public function setPayment(?Payment $payment): void
+ {
+ $this->payment = $payment;
+ }
+
+ public function getIncomeProof(): ?IncomeProof
+ {
+ return $this->incomeProof;
+ }
+
+ public function setIncomeProof(?IncomeProof $incomeProof): void
+ {
+ $this->incomeProof = $incomeProof;
+ }
+
+ public function getState(): ?string
+ {
+ return $this->state;
+ }
+
+ public function setState(?string $state): void
+ {
+ $this->state = $state;
+ }
+}
diff --git a/app/Model/User/UserGroupRole.php b/app/Model/User/UserGroupRole.php
new file mode 100644
index 000000000..e138039c8
--- /dev/null
+++ b/app/Model/User/UserGroupRole.php
@@ -0,0 +1,90 @@
+id;
+ }
+
+ public function getUser(): User
+ {
+ return $this->user;
+ }
+
+ public function setUser(User $user): void
+ {
+ $this->user = $user;
+ }
+
+ public function getTroop(): ?Troop
+ {
+ return $this->troop;
+ }
+
+ public function setTroop(?Troop $troop): void
+ {
+ $this->troop = $troop;
+ }
+
+ public function getPatrol(): ?Patrol
+ {
+ return $this->patrol;
+ }
+
+ public function setPatrol(?Patrol $patrol): void
+ {
+ $this->patrol = $patrol;
+ }
+
+ public function getRole(): Role
+ {
+ return $this->role;
+ }
+
+ public function setRole(Role $role): void
+ {
+ $this->role = $role;
+ }
+}
diff --git a/migrations/Version20221015133440.php b/migrations/Version20221015133440.php
new file mode 100644
index 000000000..e95137183
--- /dev/null
+++ b/migrations/Version20221015133440.php
@@ -0,0 +1,38 @@
+abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
+
+ $this->addSql('CREATE TABLE patrol (id INT AUTO_INCREMENT NOT NULL, troop_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, INDEX IDX_BFB2371263060AC (troop_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+ $this->addSql('CREATE TABLE troop (id INT AUTO_INCREMENT NOT NULL, variable_symbol_id INT DEFAULT NULL, payment_id INT DEFAULT NULL, income_proof_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, fee INT NOT NULL, application_date DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', maturity_date DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', payment_method VARCHAR(255) DEFAULT NULL, payment_date DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', state VARCHAR(255) NOT NULL, INDEX IDX_FAAD534C25813A9D (variable_symbol_id), INDEX IDX_FAAD534C4C3A3BB (payment_id), INDEX IDX_FAAD534CFE69EDFB (income_proof_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+ $this->addSql('CREATE TABLE user_group_role (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, troop_id INT DEFAULT NULL, patrol_id INT DEFAULT NULL, role_id INT DEFAULT NULL, INDEX IDX_D95417F6A76ED395 (user_id), INDEX IDX_D95417F6263060AC (troop_id), INDEX IDX_D95417F6A7B49BA9 (patrol_id), INDEX IDX_D95417F6D60322AC (role_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+ $this->addSql('ALTER TABLE patrol ADD CONSTRAINT FK_BFB2371263060AC FOREIGN KEY (troop_id) REFERENCES troop (id)');
+ $this->addSql('ALTER TABLE troop ADD CONSTRAINT FK_FAAD534C25813A9D FOREIGN KEY (variable_symbol_id) REFERENCES variable_symbol (id)');
+ $this->addSql('ALTER TABLE troop ADD CONSTRAINT FK_FAAD534C4C3A3BB FOREIGN KEY (payment_id) REFERENCES payment (id)');
+ $this->addSql('ALTER TABLE troop ADD CONSTRAINT FK_FAAD534CFE69EDFB FOREIGN KEY (income_proof_id) REFERENCES income_proof (id)');
+ $this->addSql('ALTER TABLE user_group_role ADD CONSTRAINT FK_D95417F6A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
+ $this->addSql('ALTER TABLE user_group_role ADD CONSTRAINT FK_D95417F6263060AC FOREIGN KEY (troop_id) REFERENCES troop (id)');
+ $this->addSql('ALTER TABLE user_group_role ADD CONSTRAINT FK_D95417F6A7B49BA9 FOREIGN KEY (patrol_id) REFERENCES patrol (id)');
+ $this->addSql('ALTER TABLE user_group_role ADD CONSTRAINT FK_D95417F6D60322AC FOREIGN KEY (role_id) REFERENCES role (id)');
+ $this->addSql('ALTER TABLE role ADD type VARCHAR(255) NOT NULL, ADD minimum_age_warning VARCHAR(255) DEFAULT NULL, ADD maximum_age INT NOT NULL, ADD maximum_age_warning VARCHAR(255) DEFAULT NULL');
+ }
+
+ public function down(Schema $schema): void
+ {
+ }
+}
From a6f43a1141bdddbdf6ac10392fe4d0de46351797 Mon Sep 17 00:00:00 2001
From: bojovyletoun <662941+bojovyletoun@users.noreply.github.com>
Date: Wed, 19 Oct 2022 21:34:11 +0200
Subject: [PATCH 05/41] =?UTF-8?q?=C3=BApravy=20rol=C3=AD=20(#888)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* migration
* + Validátor max.věku
* Min/Max věk - lokalizace formulářů
Přidána lokalizace pro maximální věk a pro formulářové prvky vlastních chybových hlášek omezení věků
* + min/max věk + hlášky formuláři rolí -AdminModule
* rozšíření kontroly max.věku role v Web Modulu
* min/max věk -AdminModule - zahrnutí AddRole
rozšíření min/maxAgeWarning dědičnost rolí
* refactoring: validateRoles...() -> trait
* fix: chybějící anotace
Validators::validateRoleMaximumAge
* +validátor pro hlášek věkových limitů rolí
Tohle je poněkud divočejší úprava existujících funkcí které původně validují věkové limity role. Pořád vrací bool, ale navíc dokážou do 3.optional argumentu vložit ty hlášky. Prosím o zvýšenou kontrolu
* Zobrazování chybových hlášek věk.limitůrolí (WebModule)
* fix: coding standard, typehint
* migration removed
* class name fix
Co-authored-by: Jan Staněk
Co-authored-by: bojovyletoun <>
---
app/AdminModule/Forms/AddRoleFormFactory.php | 3 +
app/AdminModule/Forms/EditRoleFormFactory.php | 23 ++++
app/Utils/Validators.php | 70 +++++++----
.../Forms/ApplicationFormFactory.php | 68 +----------
app/WebModule/Forms/RolesFormFactory.php | 65 +----------
app/WebModule/Forms/RolesFormFunctions.php | 109 ++++++++++++++++++
app/lang/admin.cs_CZ.neon | 9 ++
app/lang/web.cs_CZ.neon | 4 +-
8 files changed, 205 insertions(+), 146 deletions(-)
create mode 100644 app/WebModule/Forms/RolesFormFunctions.php
diff --git a/app/AdminModule/Forms/AddRoleFormFactory.php b/app/AdminModule/Forms/AddRoleFormFactory.php
index fd41dd690..79573c1ea 100644
--- a/app/AdminModule/Forms/AddRoleFormFactory.php
+++ b/app/AdminModule/Forms/AddRoleFormFactory.php
@@ -91,6 +91,9 @@ public function processForm(Form $form, stdClass $values): void
$role->setFee($parent->getFee());
$role->setCapacity($parent->getCapacity());
$role->setMinimumAge($parent->getMinimumAge());
+ $role->setMaximumAge($parent->getMaximumAge());
+ $role->setMinimumAgeWarning($parent->getMinimumAgeWarning());
+ $role->setMaximumAgeWarning($parent->getMaximumAgeWarning());
$role->setApprovedAfterRegistration($parent->isApprovedAfterRegistration());
$role->setSyncedWithSkautIS($parent->isSyncedWithSkautIS());
$role->setRegisterable($parent->isRegisterable());
diff --git a/app/AdminModule/Forms/EditRoleFormFactory.php b/app/AdminModule/Forms/EditRoleFormFactory.php
index 2bac062fa..e14b327a6 100644
--- a/app/AdminModule/Forms/EditRoleFormFactory.php
+++ b/app/AdminModule/Forms/EditRoleFormFactory.php
@@ -109,6 +109,23 @@ public function create(int $id): Form
->addRule(Form::INTEGER, 'admin.acl.roles.minimum_age.error_format')
->addRule(Form::MIN, 'admin.acl.roles.minimum_age.error_low', 0);
+ $form->addText('minimumAgeMsg', 'admin.acl.roles.minimum_age.custom_msg_label')
+ ->setHtmlAttribute('data-toggle', 'tooltip')
+ ->setHtmlAttribute('data-placement', 'bottom')
+ ->setHtmlAttribute('title', $form->getTranslator()->translate('admin.acl.roles.minimum_age.custom_msg_note'));
+
+ $form->addText('maximumAge', 'admin.acl.roles.maximum_age.label')
+ ->setHtmlAttribute('data-toggle', 'tooltip')
+ ->setHtmlAttribute('data-placement', 'bottom')
+ ->setHtmlAttribute('title', $form->getTranslator()->translate('admin.acl.roles.maximum_age.note'))
+ ->addRule(Form::INTEGER, 'admin.acl.roles.maximum_age.error_format')
+ ->addRule(Form::MIN, 'admin.acl.roles.maximum_age.error_low', 0);
+
+ $form->addText('maximumAgeMsg', 'admin.acl.roles.maximum_age.custom_msg_label')
+ ->setHtmlAttribute('data-toggle', 'tooltip')
+ ->setHtmlAttribute('data-placement', 'bottom')
+ ->setHtmlAttribute('title', $form->getTranslator()->translate('admin.acl.roles.maximum_age.custom_msg_note'));
+
$form->addMultiSelect('permissions', 'admin.acl.roles_permissions', $this->preparePermissionsOptions());
$pagesOptions = $this->pageRepository->getPagesOptions();
@@ -164,6 +181,9 @@ public function create(int $id): Form
'feeFromSubevents' => $this->role->getFee() === null,
'fee' => $this->role->getFee() ?? 0,
'minimumAge' => $this->role->getMinimumAge(),
+ 'maximumAge' => $this->role->getMaximumAge(),
+ 'minimumAgeMsg' => $this->role->getMinimumAgeWarning(),
+ 'maximumAgeMsg' => $this->role->getMaximumAgeWarning(),
'permissions' => $this->permissionRepository->findPermissionsIds($this->role->getPermissions()),
'pages' => $this->pageRepository->findPagesSlugs($this->role->getPages()),
'redirectAfterLogin' => array_key_exists($redirectAfterLoginValue, $pagesOptions) ? $redirectAfterLoginValue : null,
@@ -197,6 +217,9 @@ public function processForm(Form $form, stdClass $values): void
$this->role->setCapacity($capacity);
$this->role->setApprovedAfterRegistration($values->approvedAfterRegistration);
$this->role->setMinimumAge($values->minimumAge);
+ $this->role->setMaximumAge($values->maximumAge);
+ $this->role->setMinimumAgeWarning($values->minimumAgeMsg);
+ $this->role->setMaximumAgeWarning($values->maximumAgeMsg);
$this->role->setPermissions($this->permissionRepository->findPermissionsByIds($values->permissions));
$this->role->setPages($this->pageRepository->findPagesBySlugs($values->pages));
$this->role->setRedirectAfterLogin($values->redirectAfterLogin);
diff --git a/app/Utils/Validators.php b/app/Utils/Validators.php
index be753f87b..420d235e0 100644
--- a/app/Utils/Validators.php
+++ b/app/Utils/Validators.php
@@ -20,6 +20,7 @@
use function array_map;
use function explode;
+use function sprintf;
use function trim;
/**
@@ -50,6 +51,54 @@ public function validateRolesNonregistered(Collection $selectedRoles, User $user
return true;
}
+ /**
+ * Ověří požadovaný minimální věk.
+ *
+ * @param Collection $selectedRoles
+ * @param string[] $warnings
+ *
+ * @throws SettingsItemNotFoundException
+ * @throws Throwable
+ */
+ public function validateRolesMinimumAge(Collection $selectedRoles, User $user, array &$warnings = []): bool
+ {
+ $age = $this->queryBus->handle(new SettingDateValueQuery(Settings::SEMINAR_FROM_DATE))->diff($user->getBirthdate())->y;
+ $canary = true;
+ foreach ($selectedRoles as $role) {
+ $min = $role->getMinimumAge();
+ if ($min > $age) {
+ $warnings[] = sprintf($role->getMinimumAgeWarning(), $min, $age);
+ $canary = false;
+ }
+ }
+
+ return $canary;
+ }
+
+ /**
+ * Ověří požadovaný maximální věk.
+ *
+ * @param Collection $selectedRoles
+ * @param string[] $warnings
+ *
+ * @throws SettingsItemNotFoundException
+ * @throws Throwable
+ */
+ public function validateRolesMaximumAge(Collection $selectedRoles, User $user, array &$warnings = []): bool
+ {
+ $age = $this->queryBus->handle(new SettingDateValueQuery(Settings::SEMINAR_TO_DATE))->diff($user->getBirthdate())->y;
+ $canary = true;
+ foreach ($selectedRoles as $role) {
+ $max = $role->getMaximumAge();
+ if ($max > 0 && $max < $age) { // Hodnota 0 je bez omezení
+ $warnings[] = sprintf($role->getMaximumAgeWarning(), $max, $age);
+ $canary = false;
+ }
+ }
+
+ return $canary;
+ }
+
/**
* Ověří kapacitu rolí.
*
@@ -122,27 +171,6 @@ public function validateRolesRegisterable(Collection $selectedRoles, User $user)
return true;
}
- /**
- * Ověří požadovaný minimální věk.
- *
- * @param Collection $selectedRoles
- *
- * @throws SettingsItemNotFoundException
- * @throws Throwable
- */
- public function validateRolesMinimumAge(Collection $selectedRoles, User $user): bool
- {
- $age = $this->queryBus->handle(new SettingDateValueQuery(Settings::SEMINAR_FROM_DATE))->diff($user->getBirthdate())->y;
-
- foreach ($selectedRoles as $role) {
- if ($role->getMinimumAge() > $age) {
- return false;
- }
- }
-
- return true;
- }
-
/**
* Ověří kapacitu podakcí.
*
diff --git a/app/WebModule/Forms/ApplicationFormFactory.php b/app/WebModule/Forms/ApplicationFormFactory.php
index 2e4a1f122..f90858c33 100644
--- a/app/WebModule/Forms/ApplicationFormFactory.php
+++ b/app/WebModule/Forms/ApplicationFormFactory.php
@@ -5,7 +5,6 @@
namespace App\WebModule\Forms;
use App\Model\Acl\Repositories\RoleRepository;
-use App\Model\Acl\Role;
use App\Model\CustomInput\CustomCheckbox;
use App\Model\CustomInput\CustomCheckboxValue;
use App\Model\CustomInput\CustomDate;
@@ -23,7 +22,6 @@
use App\Model\CustomInput\Repositories\CustomInputRepository;
use App\Model\CustomInput\Repositories\CustomInputValueRepository;
use App\Model\Enums\Sex;
-use App\Model\Settings\Exceptions\SettingsItemNotFoundException;
use App\Model\Settings\Queries\SettingStringValueQuery;
use App\Model\Settings\Settings;
use App\Model\Structure\Repositories\SubeventRepository;
@@ -70,6 +68,7 @@
class ApplicationFormFactory
{
use Nette\SmartObject;
+ use RolesFormFunctions;
/**
* Přihlášený uživatel.
@@ -177,7 +176,8 @@ public function create(int $id): Form
'state' => $this->user->getState(),
]);
- $form->onSuccess[] = [$this, 'processForm'];
+ $form->onSuccess[] = [$this, 'processForm'];
+ $form->onValidate[] = [$this, 'validateRolesAgeLimits'];
return $form;
}
@@ -435,7 +435,8 @@ private function addRolesSelect(Form $form): void
$rolesSelect->addRule(Form::FILLED, 'web.application_content.roles_empty')
->addRule([$this, 'validateRolesCapacities'], 'web.application_content.roles_capacity_occupied')
->addRule([$this, 'validateRolesRegisterable'], 'web.application_content.roles_not_registerable')
- ->addRule([$this, 'validateRolesMinimumAge'], 'web.application_content.roles_require_minimum_age');
+ ->addRule([$this, 'validateRolesMinimumAge'], 'web.application_content.roles_require_minimum_age')
+ ->addRule([$this, 'validateRolesMaximumAge'], 'web.application_content.roles_require_maximum_age');
// generovani chybovych hlasek pro vsechny kombinace roli
foreach ($this->roleRepository->findFilteredRoles(true, false, true, $this->user) as $role) {
@@ -481,16 +482,6 @@ public function validateSubeventsCapacities(MultiSelectBox $field): bool
return $this->validators->validateSubeventsCapacities($selectedSubevents, $this->user);
}
- /**
- * Ověří kapacity rolí.
- */
- public function validateRolesCapacities(MultiSelectBox $field): bool
- {
- $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
-
- return $this->validators->validateRolesCapacities($selectedRoles, $this->user);
- }
-
/**
* Ověří kompatibilitu podakcí.
*
@@ -517,55 +508,6 @@ public function validateSubeventsRequired(MultiSelectBox $field, array $args): b
return $this->validators->validateSubeventsRequired($selectedSubevents, $testSubevent);
}
- /**
- * Ověří kompatibilitu rolí.
- *
- * @param Role[] $args
- */
- public function validateRolesIncompatible(MultiSelectBox $field, array $args): bool
- {
- $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
- $testRole = $args[0];
-
- return $this->validators->validateRolesIncompatible($selectedRoles, $testRole);
- }
-
- /**
- * Ověří výběr požadovaných rolí.
- *
- * @param Role[] $args
- */
- public function validateRolesRequired(MultiSelectBox $field, array $args): bool
- {
- $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
- $testRole = $args[0];
-
- return $this->validators->validateRolesRequired($selectedRoles, $testRole);
- }
-
- /**
- * Ověří registrovatelnost rolí.
- */
- public function validateRolesRegisterable(MultiSelectBox $field): bool
- {
- $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
-
- return $this->validators->validateRolesRegisterable($selectedRoles, $this->user);
- }
-
- /**
- * Ověří požadovaný minimální věk.
- *
- * @throws SettingsItemNotFoundException
- * @throws Throwable
- */
- public function validateRolesMinimumAge(MultiSelectBox $field): bool
- {
- $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
-
- return $this->validators->validateRolesMinimumAge($selectedRoles, $this->user);
- }
-
/**
* Přepíná povinnost podakcí podle kombinace rolí.
diff --git a/app/WebModule/Forms/RolesFormFactory.php b/app/WebModule/Forms/RolesFormFactory.php
index 117a76f31..586ebbf9c 100644
--- a/app/WebModule/Forms/RolesFormFactory.php
+++ b/app/WebModule/Forms/RolesFormFactory.php
@@ -19,7 +19,6 @@
use DateTimeImmutable;
use Nette;
use Nette\Application\UI\Form;
-use Nette\Forms\Controls\MultiSelectBox;
use Nette\Localization\Translator;
use stdClass;
use Throwable;
@@ -30,6 +29,7 @@
class RolesFormFactory
{
use Nette\SmartObject;
+ use RolesFormFunctions;
/**
* Přihlášený uživatel.
@@ -69,6 +69,7 @@ public function create(int $id): Form
->addRule([$this, 'validateRolesCapacities'], 'web.profile.roles_capacity_occupied')
->addRule([$this, 'validateRolesRegisterable'], 'web.profile.roles_not_registerable')
->addRule([$this, 'validateRolesMinimumAge'], 'web.application_content.roles_require_minimum_age')
+ ->addRule([$this, 'validateRolesMaximumAge'], 'web.application_content.roles_require_maximum_age')
->setDisabled(! $this->applicationService->isAllowedEditRegistration($this->user));
foreach ($this->roleRepository->findFilteredRoles(true, false, true, $this->user) as $role) {
@@ -144,7 +145,8 @@ public function create(int $id): Form
'id' => $id,
'roles' => $this->roleRepository->findRolesIds($this->user->getRoles()),
]);
- $form->onSuccess[] = [$this, 'processForm'];
+ $form->onSuccess[] = [$this, 'processForm'];
+ $form->onValidate[] = [$this, 'validateRolesAgeLimits'];
return $form;
}
@@ -163,63 +165,4 @@ public function processForm(Form $form, stdClass $values): void
$this->applicationService->cancelRegistration($this->user, ApplicationState::CANCELED, $this->user);
}
}
-
- /**
- * Ověří kapacitu rolí.
- */
- public function validateRolesCapacities(MultiSelectBox $field): bool
- {
- $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
-
- return $this->validators->validateRolesCapacities($selectedRoles, $this->user);
- }
-
- /**
- * Ověří kompatibilitu rolí.
- *
- * @param Role[] $args
- */
- public function validateRolesIncompatible(MultiSelectBox $field, array $args): bool
- {
- $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
- $testRole = $args[0];
-
- return $this->validators->validateRolesIncompatible($selectedRoles, $testRole);
- }
-
- /**
- * Ověří výběr vyžadovaných rolí.
- *
- * @param Role[] $args
- */
- public function validateRolesRequired(MultiSelectBox $field, array $args): bool
- {
- $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
- $testRole = $args[0];
-
- return $this->validators->validateRolesRequired($selectedRoles, $testRole);
- }
-
- /**
- * Ověří registrovatelnost rolí.
- */
- public function validateRolesRegisterable(MultiSelectBox $field): bool
- {
- $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
-
- return $this->validators->validateRolesRegisterable($selectedRoles, $this->user);
- }
-
- /**
- * Ověří požadovaný minimální věk.
- *
- * @throws SettingsItemNotFoundException
- * @throws Throwable
- */
- public function validateRolesMinimumAge(MultiSelectBox $field): bool
- {
- $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
-
- return $this->validators->validateRolesMinimumAge($selectedRoles, $this->user);
- }
}
diff --git a/app/WebModule/Forms/RolesFormFunctions.php b/app/WebModule/Forms/RolesFormFunctions.php
new file mode 100644
index 000000000..3c0c873b1
--- /dev/null
+++ b/app/WebModule/Forms/RolesFormFunctions.php
@@ -0,0 +1,109 @@
+roleRepository->findRolesByIds($values->roles);
+ $minWarnings = [];
+ $this->validators->validateRolesMinimumAge($selectedRoles, $this->user, $minWarnings);
+ foreach ($minWarnings as $error) {
+ $form->addError($error);
+ }
+
+ // Max a Min se kontroluje zvlášť, protože rozdíl může být jednou vůči FROM_DATE a pak TO_DATE
+ $maxWarnings = [];
+ $this->validators->validateRolesMaximumAge($selectedRoles, $this->user, $maxWarnings);
+ foreach ($maxWarnings as $error) {
+ $form->addError($error);
+ }
+ }
+
+ /**
+ * Ověří požadovaný minimální věk.
+ *
+ * @throws SettingsItemNotFoundException
+ * @throws Throwable
+ */
+ public function validateRolesMinimumAge(MultiSelectBox $field): bool
+ {
+ $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
+
+ return $this->validators->validateRolesMinimumAge($selectedRoles, $this->user);
+ }
+
+ /**
+ * Ověří požadovaný maximální věk.
+ *
+ * @throws SettingsItemNotFoundException
+ * @throws Throwable
+ */
+ public function validateRolesMaximumAge(MultiSelectBox $field): bool
+ {
+ $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
+
+ return $this->validators->validateRolesMaximumAge($selectedRoles, $this->user);
+ }
+
+ /**
+ * Ověří kapacitu rolí.
+ */
+ public function validateRolesCapacities(MultiSelectBox $field): bool
+ {
+ $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
+
+ return $this->validators->validateRolesCapacities($selectedRoles, $this->user);
+ }
+
+ /**
+ * Ověří kompatibilitu rolí.
+ *
+ * @param Role[] $args
+ */
+ public function validateRolesIncompatible(MultiSelectBox $field, array $args): bool
+ {
+ $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
+ $testRole = $args[0];
+
+ return $this->validators->validateRolesIncompatible($selectedRoles, $testRole);
+ }
+
+ /**
+ * Ověří registrovatelnost rolí.
+ */
+ public function validateRolesRegisterable(MultiSelectBox $field): bool
+ {
+ $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
+
+ return $this->validators->validateRolesRegisterable($selectedRoles, $this->user);
+ }
+
+ /**
+ * Ověří výběr vyžadovaných rolí.
+ *
+ * @param Role[] $args
+ */
+ public function validateRolesRequired(MultiSelectBox $field, array $args): bool
+ {
+ $selectedRoles = $this->roleRepository->findRolesByIds($field->getValue());
+ $testRole = $args[0];
+
+ return $this->validators->validateRolesRequired($selectedRoles, $testRole);
+ }
+}
diff --git a/app/lang/admin.cs_CZ.neon b/app/lang/admin.cs_CZ.neon
index 96be25b97..5f451a5cc 100644
--- a/app/lang/admin.cs_CZ.neon
+++ b/app/lang/admin.cs_CZ.neon
@@ -580,6 +580,15 @@ acl:
note: "Účastník musí minimálního věku dosáhnout první den akce."
error_format: "Zadejte číslo."
error_low: "Minimální věk nesmí být menší než 0."
+ custom_msg_label: "Vlastní chybová hláška k [↑]"
+ custom_msg_note: "Vlastní text chyby, která se zobrazí při nedosažení věku pro tuto roli. Možné použít %d"
+ maximum_age:
+ label: "Maximální věk"
+ note: "Účastník nesmí maximální věk překročit první den akce."
+ error_format: "Zadejte číslo."
+ error_low: "Maximální věk nesmí být menší než 0."
+ custom_msg_label: "Vlastní chybová hláška k [↑]"
+ custom_msg_note: "Vlastní text chyby, která se zobrazí při překročení věku pro tuto roli. Možné použít %d"
mailing:
menu:
diff --git a/app/lang/web.cs_CZ.neon b/app/lang/web.cs_CZ.neon
index 6960bff20..9e5a77086 100644
--- a/app/lang/web.cs_CZ.neon
+++ b/app/lang/web.cs_CZ.neon
@@ -36,8 +36,9 @@ profile:
incompatible_roles_selected: "Není možné kombinovat roli %role% s rolemi: %incompatibleRoles%."
roles_empty: "Musí být vybrána alespoň jedna role."
roles_not_registerable: "Registrace do některé z rolí již není možná."
- roles_capacity_occupied: "Všechna místa v některé roli jsou obsazena."
roles_require_minimum_age: "Některá role vyžaduje vyšší věk."
+ roles_require_maximum_age: "Některá role vyžaduje nižší věk."
+ roles_capacity_occupied: "Všechna místa v některé roli jsou obsazena."
change_roles: "Změnit role"
change_roles_confirm: "Pokud nová role vyžaduje schválení organizátora, budete Vám nastavena role \"Neschválený\". Opravdu chcete pokračovat?"
change_roles_disabled: "Změna rolí již není možná nebo nejste na seminář registrováni."
@@ -166,6 +167,7 @@ application_content:
roles_not_registerable: "Registrace do některé z rolí již není možná."
roles_capacity_occupied: "Všechna místa v některé roli jsou obsazena."
roles_require_minimum_age: "Některá role vyžaduje vyšší věk."
+ roles_require_maximum_age: "Některá role vyžaduje nižší věk."
agreement_empty: "Musíte souhlasit s poskytnutím údajů."
register: "Registrovat"
register_synchronization_failed: "Synchronizace se skautIS se nepodařila. Zkuste se znovu přihlásit a upravit údaje v profilu."
From 21d5f763a84f7b484b0a5c8df06afb706c4fc6c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Thu, 27 Oct 2022 16:57:45 +0200
Subject: [PATCH 06/41] =?UTF-8?q?Registra=C4=8Dn=C3=AD=20formul=C3=A1?=
=?UTF-8?q?=C5=99=20(#889)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* troop application content
* role switch
* skautis personall
* application draft
* db model update, commands
* forms
* forms
* 2 more steps of form
* coding standard
* coding standard
* overview, group lists
* coding standard
* coding standard
* cleanup, fixes deploment
* deploy from PR
* deploy revert, cs fix
* fixes
* missing lang added
* write fee to db
* maturity date set
* validate age
* filter 18+ for troops
* patrol attendees count validation
* troop count validation
* coding standard
* leader validation
---
.../Handlers/RolesByTypeQueryHandler.php | 25 ++
app/Model/Acl/Queries/RolesByTypeQuery.php | 17 ++
app/Model/Acl/Repositories/RoleRepository.php | 10 +
app/Model/Acl/Role.php | 9 +
app/Model/Cms/Content.php | 7 +
app/Model/Cms/TroopApplicationContent.php | 17 ++
app/Model/Enums/TroopApplicationState.php | 33 +++
app/Model/User/Commands/ConfirmPatrol.php | 18 ++
app/Model/User/Commands/ConfirmTroop.php | 24 ++
.../Handlers/ConfirmPatrolHandler.php | 24 ++
.../Commands/Handlers/ConfirmTroopHandler.php | 71 +++++
.../Handlers/RegisterTroopHandler.php | 50 ++++
.../Handlers/UpdateGroupMembersHandler.php | 146 +++++++++++
app/Model/User/Commands/RegisterTroop.php | 19 ++
.../User/Commands/UpdateGroupMembers.php | 42 +++
app/Model/User/Patrol.php | 46 +++-
.../Handlers/PatrolByIdQueryHandler.php | 22 ++
...trolByTroopAndNotConfirmedQueryHandler.php | 22 ++
.../Handlers/TroopByIdQueryHandler.php | 22 ++
.../Handlers/TroopByLeaderQueryHandler.php | 22 ++
app/Model/User/Queries/PatrolByIdQuery.php | 17 ++
.../PatrolByTroopAndNotConfirmedQuery.php | 17 ++
app/Model/User/Queries/TroopByIdQuery.php | 17 ++
app/Model/User/Queries/TroopByLeaderQuery.php | 17 ++
.../User/Repositories/PatrolRepository.php | 36 +++
.../User/Repositories/TroopRepository.php | 41 +++
.../Repositories/UserGroupRoleRepository.php | 54 ++++
.../User/Repositories/UserRepository.php | 8 +
app/Model/User/Troop.php | 159 ++++++++++-
app/Model/User/User.php | 120 +++++++++
app/Model/User/UserGroupRole.php | 10 +-
app/Services/SkautIsService.php | 68 ++++-
...ITroopApplicationContentControlFactory.php | 13 +
.../TroopApplicationContentControl.php | 183 +++++++++++++
.../templates/troop_application_content.latte | 204 +++++++++++++++
.../Forms/GroupAdditionalInfoForm.php | 143 ++++++++++
app/WebModule/Forms/GroupConfirmForm.php | 120 +++++++++
app/WebModule/Forms/GroupMembersForm.php | 247 ++++++++++++++++++
.../Forms/IGroupAdditionalInfoFormFactory.php | 16 ++
.../Forms/IGroupConfirmFormFactory.php | 16 ++
.../Forms/IGroupMembersFormFactory.php | 16 ++
.../Forms/ITroopApplicationFormFactory.php | 16 ++
.../Forms/ITroopConfirmFormFactory.php | 16 ++
app/WebModule/Forms/TroopApplicationForm.php | 153 +++++++++++
app/WebModule/Forms/TroopConfirmForm.php | 134 ++++++++++
.../group_additional_info_form.latte | 73 ++++++
.../Forms/templates/group_confirm_form.latte | 47 ++++
.../Forms/templates/group_members_form.latte | 59 +++++
.../Forms/templates/troop_confirm_form.latte | 81 ++++++
app/WebModule/Presenters/PagePresenter.php | 10 +
app/assets/common/main.js | 1 +
app/lang/common.cs_CZ.neon | 2 +
migrations/Version20221015133440.php | 2 +-
migrations/Version20221017191553.php | 28 ++
migrations/Version20221021195625.php | 30 +++
migrations/Version20221022182325.php | 32 +++
56 files changed, 2825 insertions(+), 27 deletions(-)
create mode 100644 app/Model/Acl/Queries/Handlers/RolesByTypeQueryHandler.php
create mode 100644 app/Model/Acl/Queries/RolesByTypeQuery.php
create mode 100644 app/Model/Cms/TroopApplicationContent.php
create mode 100644 app/Model/Enums/TroopApplicationState.php
create mode 100644 app/Model/User/Commands/ConfirmPatrol.php
create mode 100644 app/Model/User/Commands/ConfirmTroop.php
create mode 100644 app/Model/User/Commands/Handlers/ConfirmPatrolHandler.php
create mode 100644 app/Model/User/Commands/Handlers/ConfirmTroopHandler.php
create mode 100644 app/Model/User/Commands/Handlers/RegisterTroopHandler.php
create mode 100644 app/Model/User/Commands/Handlers/UpdateGroupMembersHandler.php
create mode 100644 app/Model/User/Commands/RegisterTroop.php
create mode 100644 app/Model/User/Commands/UpdateGroupMembers.php
create mode 100644 app/Model/User/Queries/Handlers/PatrolByIdQueryHandler.php
create mode 100644 app/Model/User/Queries/Handlers/PatrolByTroopAndNotConfirmedQueryHandler.php
create mode 100644 app/Model/User/Queries/Handlers/TroopByIdQueryHandler.php
create mode 100644 app/Model/User/Queries/Handlers/TroopByLeaderQueryHandler.php
create mode 100644 app/Model/User/Queries/PatrolByIdQuery.php
create mode 100644 app/Model/User/Queries/PatrolByTroopAndNotConfirmedQuery.php
create mode 100644 app/Model/User/Queries/TroopByIdQuery.php
create mode 100644 app/Model/User/Queries/TroopByLeaderQuery.php
create mode 100644 app/Model/User/Repositories/PatrolRepository.php
create mode 100644 app/Model/User/Repositories/TroopRepository.php
create mode 100644 app/Model/User/Repositories/UserGroupRoleRepository.php
create mode 100644 app/WebModule/Components/ITroopApplicationContentControlFactory.php
create mode 100644 app/WebModule/Components/TroopApplicationContentControl.php
create mode 100644 app/WebModule/Components/templates/troop_application_content.latte
create mode 100644 app/WebModule/Forms/GroupAdditionalInfoForm.php
create mode 100644 app/WebModule/Forms/GroupConfirmForm.php
create mode 100644 app/WebModule/Forms/GroupMembersForm.php
create mode 100644 app/WebModule/Forms/IGroupAdditionalInfoFormFactory.php
create mode 100644 app/WebModule/Forms/IGroupConfirmFormFactory.php
create mode 100644 app/WebModule/Forms/IGroupMembersFormFactory.php
create mode 100644 app/WebModule/Forms/ITroopApplicationFormFactory.php
create mode 100644 app/WebModule/Forms/ITroopConfirmFormFactory.php
create mode 100644 app/WebModule/Forms/TroopApplicationForm.php
create mode 100644 app/WebModule/Forms/TroopConfirmForm.php
create mode 100644 app/WebModule/Forms/templates/group_additional_info_form.latte
create mode 100644 app/WebModule/Forms/templates/group_confirm_form.latte
create mode 100644 app/WebModule/Forms/templates/group_members_form.latte
create mode 100644 app/WebModule/Forms/templates/troop_confirm_form.latte
create mode 100644 migrations/Version20221017191553.php
create mode 100644 migrations/Version20221021195625.php
create mode 100644 migrations/Version20221022182325.php
diff --git a/app/Model/Acl/Queries/Handlers/RolesByTypeQueryHandler.php b/app/Model/Acl/Queries/Handlers/RolesByTypeQueryHandler.php
new file mode 100644
index 000000000..0baa5431d
--- /dev/null
+++ b/app/Model/Acl/Queries/Handlers/RolesByTypeQueryHandler.php
@@ -0,0 +1,25 @@
+roleRepository->findByType($query->getType());
+ }
+}
diff --git a/app/Model/Acl/Queries/RolesByTypeQuery.php b/app/Model/Acl/Queries/RolesByTypeQuery.php
new file mode 100644
index 000000000..ffed4e8c5
--- /dev/null
+++ b/app/Model/Acl/Queries/RolesByTypeQuery.php
@@ -0,0 +1,17 @@
+type;
+ }
+}
diff --git a/app/Model/Acl/Repositories/RoleRepository.php b/app/Model/Acl/Repositories/RoleRepository.php
index 5391ba225..99670a246 100644
--- a/app/Model/Acl/Repositories/RoleRepository.php
+++ b/app/Model/Acl/Repositories/RoleRepository.php
@@ -53,6 +53,16 @@ public function findBySystemName(string $name): Role
return $this->getRepository()->findOneBy(['systemName' => $name]);
}
+ /**
+ * Vrací role podle typu.
+ *
+ * @return Role[]
+ */
+ public function findByType(string $type): array
+ {
+ return $this->getRepository()->findBy(['type' => $type]);
+ }
+
/**
* Vrací id naposledy přidané role.
*
diff --git a/app/Model/Acl/Role.php b/app/Model/Acl/Role.php
index a691f06cf..d9b1a28a7 100644
--- a/app/Model/Acl/Role.php
+++ b/app/Model/Acl/Role.php
@@ -68,6 +68,12 @@ class Role
*/
public const TEST = 'test';
+ public const PATROL_LEADER = 'patrol_leader';
+
+ public const LEADER = 'leader';
+
+ public const ESCORT = 'escort';
+
/** @var string[] */
public static array $roles = [
self::GUEST,
@@ -78,6 +84,9 @@ class Role
self::LECTOR,
self::ORGANIZER,
self::ADMIN,
+ self::PATROL_LEADER,
+ self::LEADER,
+ self::ESCORT,
];
#[ORM\Id]
#[ORM\GeneratedValue]
diff --git a/app/Model/Cms/Content.php b/app/Model/Cms/Content.php
index 72e176624..e2b5b2d40 100644
--- a/app/Model/Cms/Content.php
+++ b/app/Model/Cms/Content.php
@@ -25,6 +25,7 @@
'text_content' => TextContent::class,
'document_content' => DocumentContent::class,
'application_content' => ApplicationContent::class,
+ 'troop_application_content' => TroopApplicationContent::class,
'html_content' => HtmlContent::class,
'faq_content' => FaqContent::class,
'news_content' => NewsContent::class,
@@ -61,6 +62,11 @@ abstract class Content implements IContent
*/
public const APPLICATION = 'application';
+ /**
+ * TroopApplicationContent.
+ */
+ public const TROOP_APPLICATION = 'troop_application';
+
/**
* HtmlContent.
*/
@@ -140,6 +146,7 @@ abstract class Content implements IContent
self::NEWS,
self::DOCUMENT,
self::APPLICATION,
+ self::TROOP_APPLICATION,
self::PROGRAMS,
self::CONTACT_FORM,
self::FAQ,
diff --git a/app/Model/Cms/TroopApplicationContent.php b/app/Model/Cms/TroopApplicationContent.php
new file mode 100644
index 000000000..554b5c091
--- /dev/null
+++ b/app/Model/Cms/TroopApplicationContent.php
@@ -0,0 +1,17 @@
+patrolId;
+ }
+}
diff --git a/app/Model/User/Commands/ConfirmTroop.php b/app/Model/User/Commands/ConfirmTroop.php
new file mode 100644
index 000000000..e1332a9d1
--- /dev/null
+++ b/app/Model/User/Commands/ConfirmTroop.php
@@ -0,0 +1,24 @@
+troop_id;
+ }
+
+ public function getPairedTroopCode(): ?string
+ {
+ return $this->pairedTroopCode;
+ }
+}
diff --git a/app/Model/User/Commands/Handlers/ConfirmPatrolHandler.php b/app/Model/User/Commands/Handlers/ConfirmPatrolHandler.php
new file mode 100644
index 000000000..54bf1564e
--- /dev/null
+++ b/app/Model/User/Commands/Handlers/ConfirmPatrolHandler.php
@@ -0,0 +1,24 @@
+patrolRepository->findById($command->getPatrolId());
+ $patrol->setConfirmed(true);
+ $this->patrolRepository->save($patrol);
+ }
+}
diff --git a/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php b/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php
new file mode 100644
index 000000000..5866c747b
--- /dev/null
+++ b/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php
@@ -0,0 +1,71 @@
+troopRepository->findById($command->getTroopId());
+ $troop->setState(TroopApplicationState::WAITING_FOR_PAYMENT);
+ $troop->setPairedTroopCode($command->getPairedTroopCode());
+ $troop->setFee($troop->countFee());
+ $troop->setApplicationDate(new DateTimeImmutable());
+ $troop->setMaturityDate($this->countMaturityDate());
+ $this->troopRepository->save($troop);
+ }
+
+ /**
+ * Vypočítá datum splatnosti podle zvolené metody.
+ */
+ private function countMaturityDate(): ?DateTimeImmutable
+ {
+ switch (
+ $this->queryBus->handle(
+ new SettingStringValueQuery(Settings::MATURITY_TYPE)
+ )
+ ) {
+ case MaturityType::DATE:
+ return $this->queryBus->handle(new SettingDateValueQuery(Settings::MATURITY_DATE));
+
+ case MaturityType::DAYS:
+ return (new DateTimeImmutable())->modify('+' . $this->queryBus->handle(new SettingIntValueQuery(Settings::MATURITY_DAYS)) . ' days');
+
+ case MaturityType::WORK_DAYS:
+ $workDays = $this->queryBus->handle(new SettingIntValueQuery(Settings::MATURITY_WORK_DAYS));
+ $date = new DateTimeImmutable();
+
+ for ($i = 0; $i < $workDays;) {
+ $date = $date->modify('+1 days');
+ $holidays = Yasumi::create('CzechRepublic', (int) $date->format('Y'));
+
+ if ($holidays->isWorkingDay($date)) {
+ $i++;
+ }
+ }
+
+ return $date;
+ }
+
+ return null;
+ }
+}
diff --git a/app/Model/User/Commands/Handlers/RegisterTroopHandler.php b/app/Model/User/Commands/Handlers/RegisterTroopHandler.php
new file mode 100644
index 000000000..9f9263397
--- /dev/null
+++ b/app/Model/User/Commands/Handlers/RegisterTroopHandler.php
@@ -0,0 +1,50 @@
+em->wrapInTransaction(function () use ($command): void {
+ $variableSymbolCode = $this->queryBus->handle(new SettingStringValueQuery(Settings::VARIABLE_SYMBOL_CODE));
+
+ $variableSymbol = new VariableSymbol();
+ $this->variableSymbolRepository->save($variableSymbol);
+
+ $variableSymbolText = $variableSymbolCode . str_pad(strval($variableSymbol->getId()), 6, '0', STR_PAD_LEFT);
+
+ $variableSymbol->setVariableSymbol($variableSymbolText);
+ $this->variableSymbolRepository->save($variableSymbol);
+
+ $troop = new Troop($command->getLeader(), $variableSymbol);
+ $this->troopRepository->save($troop);
+ });
+ }
+}
diff --git a/app/Model/User/Commands/Handlers/UpdateGroupMembersHandler.php b/app/Model/User/Commands/Handlers/UpdateGroupMembersHandler.php
new file mode 100644
index 000000000..eb7a5d9ce
--- /dev/null
+++ b/app/Model/User/Commands/Handlers/UpdateGroupMembersHandler.php
@@ -0,0 +1,146 @@
+em->wrapInTransaction(function () use ($command): void {
+ $troop = $this->queryBus->handle(new TroopByIdQuery($command->getTroopId()));
+
+ if ($command->getType() === 'patrol') {
+ if ($command->getPatrolId() !== null) {
+ $patrol = $this->queryBus->handle(new PatrolByIdQuery($command->getPatrolId()));
+ } else {
+ $patrolNumber = $troop->getPatrols()->count() + 1;
+ $patrol = new Patrol($troop, $troop->getName() . '-' . sprintf('%02d', $patrolNumber));
+ $this->patrolRepository->save($patrol);
+ }
+ } else {
+ $patrol = null;
+ }
+
+ foreach ($command->getPersons() as $person) {
+ $personId = $person['personId'];
+ $roleId = $person['roleId'];
+
+ $personDetail = $this->skautIsService->getPersonDetail($personId);
+
+ $user = $this->userRepository->findBySkautISPersonId($personId);
+ if ($user == null) {
+ $user = new User();
+ $user->setSkautISPersonId($personId);
+ }
+
+ $user->setFirstName($personDetail->FirstName);
+ $user->setLastName($personDetail->LastName);
+ $user->setNickName($personDetail->NickName);
+
+ $user->setMember($personDetail->HasMembership);
+
+ $birthdate = new DateTimeImmutable($personDetail->Birthday);
+ $user->setBirthdate($birthdate);
+ $user->setSex($personDetail->ID_Sex);
+
+ if (property_exists($personDetail, 'Phone')) {
+ $user->setPhone($personDetail->Phone);
+ }
+
+ if (property_exists($personDetail, 'Email')) {
+ $user->setEmail($personDetail->Email);
+ }
+
+ $user->setStreet($personDetail->Street);
+ $user->setCity($personDetail->City);
+ $user->setPostcode($personDetail->Postcode);
+ $user->setState($personDetail->State);
+
+ if ((new DateTimeImmutable())->diff($birthdate)->y < 18) {
+ $personContacts = $this->skautIsService->getPersonContactAllParent($personId);
+
+ foreach ($personContacts as $contact) {
+ if ($contact->ID_ContactType === 'telefon_hlavni') {
+ switch ($contact->ID_ParentType) {
+ case 'mother':
+ $user->setMotherName($contact->PersonPersonParent);
+ $user->setMotherPhone($contact->Value);
+ break;
+ case 'father':
+ $user->setFatherName($contact->PersonPersonParent);
+ $user->setFatherPhone($contact->Value);
+ break;
+ }
+ }
+ }
+ }
+
+ $this->userRepository->save($user);
+
+ $role = $this->roleRepository->findById($roleId);
+
+ if ($command->getType() === 'patrol') {
+ foreach ($patrol->getUsersRoles() as $usersRole) {
+ $this->userGroupRoleRepository->remove($usersRole);
+ }
+
+ $userGroupRoles = $this->userGroupRoleRepository->findByUserAndPatrol($user->getId(), $patrol->getId());
+ if ($userGroupRoles->isEmpty()) {
+ $userGroupRole = new UserGroupRole($user, $role, $patrol);
+ } else {
+ $userGroupRole = $userGroupRoles[0];
+ $userGroupRole->setRole($role);
+ }
+
+ $this->userGroupRoleRepository->save($userGroupRole);
+ } elseif ($command->getType() === 'troop') {
+ foreach ($troop->getUsersRoles() as $usersRole) {
+ $this->userGroupRoleRepository->remove($usersRole);
+ }
+
+ $userGroupRoles = $this->userGroupRoleRepository->findByUserAndTroop($user->getId(), $troop->getId());
+ if ($userGroupRoles->isEmpty()) {
+ $userGroupRole = new UserGroupRole($user, $role, null, $troop);
+ } else {
+ $userGroupRole = $userGroupRoles[0];
+ $userGroupRole->setRole($role);
+ }
+
+ $this->userGroupRoleRepository->save($userGroupRole);
+ }
+ }
+ });
+ }
+}
diff --git a/app/Model/User/Commands/RegisterTroop.php b/app/Model/User/Commands/RegisterTroop.php
new file mode 100644
index 000000000..b27d45406
--- /dev/null
+++ b/app/Model/User/Commands/RegisterTroop.php
@@ -0,0 +1,19 @@
+leader;
+ }
+}
diff --git a/app/Model/User/Commands/UpdateGroupMembers.php b/app/Model/User/Commands/UpdateGroupMembers.php
new file mode 100644
index 000000000..f03c5aaf3
--- /dev/null
+++ b/app/Model/User/Commands/UpdateGroupMembers.php
@@ -0,0 +1,42 @@
+type;
+ }
+
+ public function getTroopId(): int
+ {
+ return $this->troopId;
+ }
+
+ public function getPatrolId(): ?int
+ {
+ return $this->patrolId;
+ }
+
+ /**
+ * @return int[][]
+ */
+ public function getPersons(): array
+ {
+ return $this->persons;
+ }
+}
diff --git a/app/Model/User/Patrol.php b/app/Model/User/Patrol.php
index 8172ede41..16104ed36 100644
--- a/app/Model/User/Patrol.php
+++ b/app/Model/User/Patrol.php
@@ -8,6 +8,8 @@
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
+use function in_array;
+
/**
* Entita družina.
*/
@@ -40,9 +42,18 @@ class Patrol
#[ORM\OneToMany(mappedBy: 'patrol', targetEntity: UserGroupRole::class, cascade: ['persist'])]
protected Collection $usersRoles;
- public function __construct()
+ /**
+ * Stav přihlášky - nepotvrzená přihláška slouží pro předávání údajů mezi kroky formuláře.
+ */
+ #[ORM\Column(type: 'boolean')]
+ protected bool $confirmed = false;
+
+ public function __construct(Troop $troop, string $name)
{
$this->usersRoles = new ArrayCollection();
+
+ $this->troop = $troop;
+ $this->name = $name;
}
public function getId(): ?int
@@ -55,26 +66,41 @@ public function getName(): string
return $this->name;
}
- public function setName(string $name): void
+ public function getTroop(): Troop
{
- $this->name = $name;
+ return $this->troop;
}
- public function getTroop(): Troop
+ /**
+ * @return Collection
+ */
+ public function getUsersRoles(): Collection
{
- return $this->troop;
+ return $this->usersRoles;
}
- public function setTroop(Troop $troop): void
+ public function isConfirmed(): bool
{
- $this->troop = $troop;
+ return $this->confirmed;
+ }
+
+ public function setConfirmed(bool $confirmed): void
+ {
+ $this->confirmed = $confirmed;
}
/**
- * @return Collection
+ * @param string[] $roleNames
*/
- public function getUsersRoles(): Collection
+ public function countUsersInRoles(array $roleNames): int
{
- return $this->usersRoles;
+ $counter = 0;
+ foreach ($this->usersRoles as $userRole) {
+ if (in_array($userRole->getRole()->getSystemName(), $roleNames)) {
+ $counter++;
+ }
+ }
+
+ return $counter;
}
}
diff --git a/app/Model/User/Queries/Handlers/PatrolByIdQueryHandler.php b/app/Model/User/Queries/Handlers/PatrolByIdQueryHandler.php
new file mode 100644
index 000000000..f5affe12f
--- /dev/null
+++ b/app/Model/User/Queries/Handlers/PatrolByIdQueryHandler.php
@@ -0,0 +1,22 @@
+patrolRepository->findById($query->getPatrolId());
+ }
+}
diff --git a/app/Model/User/Queries/Handlers/PatrolByTroopAndNotConfirmedQueryHandler.php b/app/Model/User/Queries/Handlers/PatrolByTroopAndNotConfirmedQueryHandler.php
new file mode 100644
index 000000000..b14044cef
--- /dev/null
+++ b/app/Model/User/Queries/Handlers/PatrolByTroopAndNotConfirmedQueryHandler.php
@@ -0,0 +1,22 @@
+patrolRepository->findByTroopAndNotConfirmed($query->getTroopId());
+ }
+}
diff --git a/app/Model/User/Queries/Handlers/TroopByIdQueryHandler.php b/app/Model/User/Queries/Handlers/TroopByIdQueryHandler.php
new file mode 100644
index 000000000..3d2042122
--- /dev/null
+++ b/app/Model/User/Queries/Handlers/TroopByIdQueryHandler.php
@@ -0,0 +1,22 @@
+troopRepository->findById($query->getTroopId());
+ }
+}
diff --git a/app/Model/User/Queries/Handlers/TroopByLeaderQueryHandler.php b/app/Model/User/Queries/Handlers/TroopByLeaderQueryHandler.php
new file mode 100644
index 000000000..d2757575e
--- /dev/null
+++ b/app/Model/User/Queries/Handlers/TroopByLeaderQueryHandler.php
@@ -0,0 +1,22 @@
+troopRepository->findByLeaderId($query->getLeaderId());
+ }
+}
diff --git a/app/Model/User/Queries/PatrolByIdQuery.php b/app/Model/User/Queries/PatrolByIdQuery.php
new file mode 100644
index 000000000..b0dee5c99
--- /dev/null
+++ b/app/Model/User/Queries/PatrolByIdQuery.php
@@ -0,0 +1,17 @@
+patrolId;
+ }
+}
diff --git a/app/Model/User/Queries/PatrolByTroopAndNotConfirmedQuery.php b/app/Model/User/Queries/PatrolByTroopAndNotConfirmedQuery.php
new file mode 100644
index 000000000..6a26e8ebb
--- /dev/null
+++ b/app/Model/User/Queries/PatrolByTroopAndNotConfirmedQuery.php
@@ -0,0 +1,17 @@
+troopId;
+ }
+}
diff --git a/app/Model/User/Queries/TroopByIdQuery.php b/app/Model/User/Queries/TroopByIdQuery.php
new file mode 100644
index 000000000..6c62b4373
--- /dev/null
+++ b/app/Model/User/Queries/TroopByIdQuery.php
@@ -0,0 +1,17 @@
+troopId;
+ }
+}
diff --git a/app/Model/User/Queries/TroopByLeaderQuery.php b/app/Model/User/Queries/TroopByLeaderQuery.php
new file mode 100644
index 000000000..59748efff
--- /dev/null
+++ b/app/Model/User/Queries/TroopByLeaderQuery.php
@@ -0,0 +1,17 @@
+leaderId;
+ }
+}
diff --git a/app/Model/User/Repositories/PatrolRepository.php b/app/Model/User/Repositories/PatrolRepository.php
new file mode 100644
index 000000000..b162dd7bb
--- /dev/null
+++ b/app/Model/User/Repositories/PatrolRepository.php
@@ -0,0 +1,36 @@
+getRepository()->findOneBy(['id' => $id]);
+ }
+
+ public function findByTroopAndNotConfirmed(int $troopId): ?Patrol
+ {
+ return $this->getRepository()->findOneBy(['troop' => $troopId, 'confirmed' => false]);
+ }
+
+ public function save(Patrol $patrol): void
+ {
+ $this->em->persist($patrol);
+ $this->em->flush();
+ }
+}
diff --git a/app/Model/User/Repositories/TroopRepository.php b/app/Model/User/Repositories/TroopRepository.php
new file mode 100644
index 000000000..7f716245f
--- /dev/null
+++ b/app/Model/User/Repositories/TroopRepository.php
@@ -0,0 +1,41 @@
+getRepository()->findOneBy(['id' => $id]);
+ }
+
+ public function findByLeaderId(int $leaderId): ?Troop
+ {
+ return $this->getRepository()
+ ->createQueryBuilder('t')
+ ->where('t.leader = :leader_id')
+ ->setParameter('leader_id', $leaderId)
+ ->getQuery()
+ ->getOneOrNullResult();
+ }
+
+ public function save(Troop $troop): void
+ {
+ $this->em->persist($troop);
+ $this->em->flush();
+ }
+}
diff --git a/app/Model/User/Repositories/UserGroupRoleRepository.php b/app/Model/User/Repositories/UserGroupRoleRepository.php
new file mode 100644
index 000000000..e884f428c
--- /dev/null
+++ b/app/Model/User/Repositories/UserGroupRoleRepository.php
@@ -0,0 +1,54 @@
+
+ */
+ public function findByUserAndPatrol(int $user_id, int $patrol_id): Collection
+ {
+ $result = $this->getRepository()->findBy(['user' => $user_id, 'patrol' => $patrol_id]);
+
+ return new ArrayCollection($result);
+ }
+
+ /**
+ * @return Collection
+ */
+ public function findByUserAndTroop(int $user_id, int $troop_id): Collection
+ {
+ $result = $this->getRepository()->findBy(['user' => $user_id, 'troop' => $troop_id]);
+
+ return new ArrayCollection($result);
+ }
+
+ public function save(UserGroupRole $userGroupRole): void
+ {
+ $this->em->persist($userGroupRole);
+ $this->em->flush();
+ }
+
+ public function remove(UserGroupRole $userGroupRole): void
+ {
+ $this->em->remove($userGroupRole);
+ $this->em->flush();
+ }
+}
diff --git a/app/Model/User/Repositories/UserRepository.php b/app/Model/User/Repositories/UserRepository.php
index c913f7b17..b8d23399c 100644
--- a/app/Model/User/Repositories/UserRepository.php
+++ b/app/Model/User/Repositories/UserRepository.php
@@ -55,6 +55,14 @@ public function findBySkautISUserId(int $skautISUserId): ?User
return $this->getRepository()->findOneBy(['skautISUserId' => $skautISUserId]);
}
+ /**
+ * Vrací uživatele podle skautISPersonId.
+ */
+ public function findBySkautISPersonId(int $skautISUserId): ?User
+ {
+ return $this->getRepository()->findOneBy(['skautISPersonId' => $skautISUserId]);
+ }
+
/**
* Vrací uživatele podle id.
*
diff --git a/app/Model/User/Troop.php b/app/Model/User/Troop.php
index deb6bc8c8..9fe9d5fdc 100644
--- a/app/Model/User/Troop.php
+++ b/app/Model/User/Troop.php
@@ -6,12 +6,21 @@
use App\Model\Application\IncomeProof;
use App\Model\Application\VariableSymbol;
+use App\Model\Enums\TroopApplicationState;
use App\Model\Payment\Payment;
use DateTimeImmutable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
+use function array_key_exists;
+use function count;
+use function in_array;
+use function md5;
+use function mt_rand;
+use function substr;
+use function uniqid;
+
/**
* Entita oddíl.
*/
@@ -61,8 +70,8 @@ class Troop
/**
* Datum podání přihlášky.
*/
- #[ORM\Column(type: 'datetime_immutable')]
- protected DateTimeImmutable $applicationDate;
+ #[ORM\Column(type: 'datetime_immutable', nullable: true)]
+ protected ?DateTimeImmutable $applicationDate;
/**
* Datum splatnosti.
@@ -98,12 +107,38 @@ class Troop
* Stav přihlášky.
*/
#[ORM\Column(type: 'string')]
- protected ?string $state = null;
+ protected string $state;
+
+ /**
+ * Vedoucí oddílu.
+ */
+ #[ORM\OneToOne(targetEntity: User::class, inversedBy: 'troop', cascade: ['persist'])]
+ protected User $leader;
- public function __construct()
+ /**
+ * Kód pro párování oddílů.
+ */
+ #[ORM\Column(type: 'string')]
+ protected string $pairingCode;
+
+ /**
+ * Spárovaný oddíl.
+ */
+ #[ORM\Column(type: 'string', nullable: true)]
+ protected ?string $pairedTroopCode = null;
+
+ public function __construct(User $leader, VariableSymbol $variableSymbol)
{
$this->patrols = new ArrayCollection();
$this->usersRoles = new ArrayCollection();
+
+ $this->leader = $leader;
+ $this->variableSymbol = $variableSymbol;
+
+ $this->name = 'S-' . $variableSymbol->getVariableSymbol();
+ $this->pairingCode = substr(md5(uniqid((string) mt_rand(), true)), 0, 20);
+ $this->state = TroopApplicationState::DRAFT;
+ $this->fee = 0;
}
public function getId(): ?int
@@ -152,17 +187,12 @@ public function getVariableSymbol(): VariableSymbol
return $this->variableSymbol;
}
- public function setVariableSymbol(VariableSymbol $variableSymbol): void
- {
- $this->variableSymbol = $variableSymbol;
- }
-
- public function getApplicationDate(): DateTimeImmutable
+ public function getApplicationDate(): ?DateTimeImmutable
{
return $this->applicationDate;
}
- public function setApplicationDate(DateTimeImmutable $applicationDate): void
+ public function setApplicationDate(?DateTimeImmutable $applicationDate): void
{
$this->applicationDate = $applicationDate;
}
@@ -217,13 +247,116 @@ public function setIncomeProof(?IncomeProof $incomeProof): void
$this->incomeProof = $incomeProof;
}
- public function getState(): ?string
+ public function getState(): string
{
return $this->state;
}
- public function setState(?string $state): void
+ public function setState(string $state): void
{
$this->state = $state;
}
+
+ public function getLeader(): User
+ {
+ return $this->leader;
+ }
+
+ public function getPairingCode(): string
+ {
+ return $this->pairingCode;
+ }
+
+ public function getPairedTroopCode(): ?string
+ {
+ return $this->pairedTroopCode;
+ }
+
+ public function setPairedTroopCode(?string $pairedTroopCode): void
+ {
+ $this->pairedTroopCode = $pairedTroopCode;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getConfirmedPatrols(): Collection
+ {
+ return $this->patrols->filter(static fn ($p) => $p->isConfirmed());
+ }
+
+ public function getMaxEscortsCount(): int
+ {
+ return $this->getMaxAdultsCount() - $this->countUsersInRoles(['leader']);
+ }
+
+ public function getMaxAdultsCount(): int
+ {
+ return $this->getConfirmedPatrols()->count() * 2;
+ }
+
+ /**
+ * @param string[] $roleNames
+ */
+ public function countUsersInRoles(array $roleNames): int
+ {
+ $users = [];
+
+ // uzivatele v oddile
+ foreach ($this->usersRoles as $userRole) {
+ if (in_array($userRole->getRole()->getSystemName(), $roleNames)) {
+ $users[$userRole->getUser()->getId()] = true;
+ }
+ }
+
+ // uzivatele v druzinach
+ foreach ($this->getConfirmedPatrols() as $patrol) {
+ foreach ($patrol->getUsersRoles() as $userRole) {
+ if (in_array($userRole->getRole()->getSystemName(), $roleNames)) {
+ $users[$userRole->getUser()->getId()] = true;
+ }
+ }
+ }
+
+ return count($users);
+ }
+
+ public function countFee(): int
+ {
+ $rolesFees = [];
+ $rolesUsers = [];
+
+ // uzivatele v oddile
+ foreach ($this->usersRoles as $userRole) {
+ $role = $userRole->getRole();
+ $user = $userRole->getUser();
+ if (! array_key_exists($role->getId(), $rolesFees)) {
+ $rolesFees[$role->getId()] = $role->getFee();
+ $rolesUsers[$role->getId()] = [];
+ }
+
+ $rolesUsers[$role->getId()][$user->getId()] = true;
+ }
+
+ // uzivatele v druzinach
+ foreach ($this->getConfirmedPatrols() as $patrol) {
+ foreach ($patrol->getUsersRoles() as $userRole) {
+ $role = $userRole->getRole();
+ $user = $userRole->getUser();
+ if (! array_key_exists($role->getId(), $rolesFees)) {
+ $rolesFees[$role->getId()] = $role->getFee();
+ $rolesUsers[$role->getId()] = [];
+ }
+
+ $rolesUsers[$role->getId()][$user->getId()] = true;
+ }
+ }
+
+ $totalFee = 0;
+ foreach ($rolesFees as $key => $value) {
+ $totalFee += $value * count($rolesUsers[$key]);
+ }
+
+ return $totalFee;
+ }
}
diff --git a/app/Model/User/User.php b/app/Model/User/User.php
index 6c6db3a89..ef4cfd9fe 100644
--- a/app/Model/User/User.php
+++ b/app/Model/User/User.php
@@ -294,6 +294,52 @@ class User
#[ORM\Column(type: 'datetime_immutable', nullable: true)]
protected ?DateTimeImmutable $photoUpdate = null;
+ /**
+ * Přihláška oddílu.
+ */
+ #[ORM\OneToOne(targetEntity: Troop::class, mappedBy: 'leader', cascade: ['persist'])]
+ protected Troop $troop;
+
+ /**
+ * Telefon.
+ */
+ #[ORM\Column(type: 'string', nullable: true)]
+ protected ?string $phone = null;
+
+ /**
+ * Jméno matky.
+ */
+ #[ORM\Column(type: 'string', nullable: true)]
+ protected ?string $motherName = null;
+
+ /**
+ * Telefon matky.
+ */
+ #[ORM\Column(type: 'string', nullable: true)]
+ protected ?string $motherPhone = null;
+
+ /**
+ * Jméno otce.
+ */
+ #[ORM\Column(type: 'string', nullable: true)]
+ protected ?string $fatherName = null;
+
+ /**
+ * Telefon otce.
+ */
+ #[ORM\Column(type: 'string', nullable: true)]
+ protected ?string $fatherPhone = null;
+
+ /**
+ * Informace o zdravotním stavu - omezení, alergie, léky.
+ */
+ #[ORM\Column(type: 'text', nullable: true)]
+ protected ?string $healthInfo = null;
+
+ /** @var Collection */
+ #[ORM\OneToMany(targetEntity: UserGroupRole::class, cascade: ['persist'], mappedBy: 'user')]
+ protected Collection $groupRoles;
+
public function __construct()
{
$this->applications = new ArrayCollection();
@@ -302,6 +348,7 @@ public function __construct()
$this->lecturersBlocks = new ArrayCollection();
$this->notRegisteredMandatoryBlocks = new ArrayCollection();
$this->customInputValues = new ArrayCollection();
+ $this->groupRoles = new ArrayCollection();
}
public function getId(): ?int
@@ -1121,4 +1168,77 @@ public function isAlternate(Program $program): bool
)
)->isEmpty();
}
+
+ public function getTroop(): Troop
+ {
+ return $this->troop;
+ }
+
+ public function getPhone(): ?string
+ {
+ return $this->phone;
+ }
+
+ public function setPhone(?string $phone): void
+ {
+ $this->phone = $phone;
+ }
+
+ public function getMotherName(): ?string
+ {
+ return $this->motherName;
+ }
+
+ public function setMotherName(?string $motherName): void
+ {
+ $this->motherName = $motherName;
+ }
+
+ public function getMotherPhone(): ?string
+ {
+ return $this->motherPhone;
+ }
+
+ public function setMotherPhone(?string $motherPhone): void
+ {
+ $this->motherPhone = $motherPhone;
+ }
+
+ public function getFatherName(): ?string
+ {
+ return $this->fatherName;
+ }
+
+ public function setFatherName(?string $fatherName): void
+ {
+ $this->fatherName = $fatherName;
+ }
+
+ public function getFatherPhone(): ?string
+ {
+ return $this->fatherPhone;
+ }
+
+ public function setFatherPhone(?string $fatherPhone): void
+ {
+ $this->fatherPhone = $fatherPhone;
+ }
+
+ public function getHealthInfo(): ?string
+ {
+ return $this->healthInfo;
+ }
+
+ public function setHealthInfo(?string $healthInfo): void
+ {
+ $this->healthInfo = $healthInfo;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getGroupRoles(): Collection
+ {
+ return $this->groupRoles;
+ }
}
diff --git a/app/Model/User/UserGroupRole.php b/app/Model/User/UserGroupRole.php
index e138039c8..fc3444b3f 100644
--- a/app/Model/User/UserGroupRole.php
+++ b/app/Model/User/UserGroupRole.php
@@ -22,7 +22,7 @@ class UserGroupRole
/**
* Uživatel.
*/
- #[ORM\ManyToOne(targetEntity: User::class, cascade: ['persist'])]
+ #[ORM\ManyToOne(targetEntity: User::class, cascade: ['persist'], inversedBy: 'groupRoles')]
protected User $user;
/**
@@ -43,6 +43,14 @@ class UserGroupRole
#[ORM\ManyToOne(targetEntity: Role::class, cascade: ['persist'])]
protected Role $role;
+ public function __construct(User $user, Role $role, ?Patrol $patrol = null, ?Troop $troop = null)
+ {
+ $this->user = $user;
+ $this->role = $role;
+ $this->patrol = $patrol;
+ $this->troop = $troop;
+ }
+
public function getId(): ?int
{
return $this->id;
diff --git a/app/Services/SkautIsService.php b/app/Services/SkautIsService.php
index 1993acd36..f2b75c873 100644
--- a/app/Services/SkautIsService.php
+++ b/app/Services/SkautIsService.php
@@ -12,6 +12,9 @@
use stdClass;
use Throwable;
+use function array_filter;
+use function in_array;
+
/**
* Služba pro komunikaci se skautIS.
*/
@@ -66,11 +69,13 @@ public function setLoginData(array $data): void
/**
* Vrátí skautIS role uživatele.
*
+ * @param ?string[] $allowedRoleTypes
+ *
* @return stdClass[]
*
* @throws Throwable
*/
- public function getUserRoles(int $userId): array
+ public function getUserRoles(int $userId, ?array $allowedRoleTypes = null): array
{
$roles = $this->userRolesCache->load($userId);
@@ -82,7 +87,13 @@ public function getUserRoles(int $userId): array
$this->userRolesCache->save($userId, $roles);
}
- return $roles instanceof stdClass ? [] : $roles;
+ $rolesArray = $roles instanceof stdClass ? [] : $roles;
+
+ if ($allowedRoleTypes !== null) {
+ return array_filter($rolesArray, static fn (stdClass $r) => isset($r->Key) && in_array($r->Key, $allowedRoleTypes));
+ }
+
+ return $rolesArray;
}
/**
@@ -221,4 +232,57 @@ public function getUnitDetail(int $unitId): stdClass
'ID' => $unitId,
]);
}
+
+ /**
+ * @param ?string[] $allowedUnitTypes
+ *
+ * @return stdClass[]
+ */
+ public function getUnitAllUnit(?array $allowedUnitTypes = null): array
+ {
+ $units = $this->skautIs->org->UnitAllUnit([
+ 'ID_Login' => $this->skautIs->getUser()->getLoginId(),
+ 'ID_Unit' => $this->skautIs->getUser()->getUnitId(),
+ ], 'unitAllUnitInput');
+
+ $unitsArray = $units instanceof stdClass ? [] : $units;
+
+ if ($allowedUnitTypes !== null) {
+ return array_filter($unitsArray, static fn (stdClass $r) => in_array($r->ID_UnitType, $allowedUnitTypes));
+ }
+
+ return $unitsArray;
+ }
+
+ /**
+ * @return stdClass[]
+ */
+ public function getMembershipAll(int $unitId, ?int $minimalAge = null, ?DateTimeImmutable $date = null): array
+ {
+ $memberships = $this->skautIs->org->MembershipAll([
+ 'ID_Login' => $this->skautIs->getUser()->getLoginId(),
+ 'ID_Unit' => $unitId,
+ 'ID_MembershipType' => 'radne',
+ 'OnlyDirectMember' => false,
+ ], 'membershipAllInput');
+
+ if ($minimalAge !== null) {
+ return array_filter($memberships, static fn (stdClass $m) => $date->diff(new DateTimeImmutable($m->Birthday))->y >= $minimalAge);
+ }
+
+ return $memberships instanceof stdClass ? [] : $memberships;
+ }
+
+ /**
+ * @return stdClass[]
+ */
+ public function getPersonContactAllParent(int $personId): array
+ {
+ $contacts = $this->skautIs->org->PersonContactAllParent([
+ 'ID_Login' => $this->skautIs->getUser()->getLoginId(),
+ 'ID_Person' => $personId,
+ ], 'personContactAllParentInput');
+
+ return $contacts instanceof stdClass ? [] : $contacts;
+ }
}
diff --git a/app/WebModule/Components/ITroopApplicationContentControlFactory.php b/app/WebModule/Components/ITroopApplicationContentControlFactory.php
new file mode 100644
index 000000000..0cc9ac2af
--- /dev/null
+++ b/app/WebModule/Components/ITroopApplicationContentControlFactory.php
@@ -0,0 +1,13 @@
+template;
+ $template->setFile(__DIR__ . '/templates/troop_application_content.latte');
+
+ if ($content) {
+ $template->heading = $content->getHeading();
+ }
+
+ $template->backlink = $this->getPresenter()->getHttpRequest()->getUrl()->getPath();
+
+ $user = $this->getPresenter()->user;
+ $template->guestRole = $user->isInRole($this->roleRepository->findBySystemName(Role::GUEST)->getName());
+ $template->testRole = Role::TEST;
+
+ $step = $this->getPresenter()->getParameter('step');
+ $template->step = $step;
+
+ if ($user->isLoggedIn()) {
+ $dbuser = $this->userRepository->findById($user->id);
+ $template->dbuser = $dbuser;
+
+ $skautIsUserId = $dbuser->getSkautISUserId();
+ $skautIsRoles = $this->skautIsService->getUserRoles($skautIsUserId, self::$ALLOWED_ROLE_TYPES);
+ $template->skautIsRoles = $skautIsRoles;
+
+ $troop = $this->queryBus->handle(new TroopByLeaderQuery($dbuser->getId()));
+ if ($troop == null) {
+ $this->commandBus->handle(new RegisterTroop($dbuser));
+ $troop = $this->queryBus->handle(new TroopByLeaderQuery($dbuser->getId()));
+ }
+
+ $template->troop = $troop;
+
+ if ($step === null) {
+ $skautIsRoleSelectedId = $this->skautIsService->getUserRoleId();
+ $skautIsRoleSelected = array_filter($skautIsRoles, static fn (stdClass $r) => $r->ID === $skautIsRoleSelectedId);
+ if (empty($skautIsRoleSelected)) {
+ $template->skautIsRoleSelected = null;
+ } else {
+ $template->skautIsRoleSelected = $skautIsRoleSelected[array_keys($skautIsRoleSelected)[0]];
+ }
+ }
+ }
+
+ $template->render();
+ }
+
+ public function renderScripts(): void
+ {
+ }
+
+ protected function createComponentGroupMembersForm(): GroupMembersForm
+ {
+ $type = $this->getPresenter()->getParameter('type');
+ $patrolId = $this->getPresenter()->getParameter('patrol_id');
+ if ($patrolId !== null) {
+ $patrolId = (int) $patrolId;
+ }
+
+ $form = $this->groupMembersFormFactory->create($type, $patrolId);
+
+ $form->onSave[] = function () use ($type, $patrolId): void {
+ $this->getPresenter()->redirect('this', ['step' => 'additional_info', 'type' => $type, 'patrol_id' => $patrolId]);
+ };
+
+ $form->onError[] = function () use ($type, $patrolId): void {
+ $p = $this->getPresenter();
+ $p->flashMessage('Po potvrzení přihlášky nelze měnit počet účastníků.', 'danger');
+ $p->redirect('this', ['step' => 'members', 'type' => $type, 'patrol_id' => $patrolId]);
+ };
+
+ return $form;
+ }
+
+ protected function createComponentGroupAdditionalInfoForm(): GroupAdditionalInfoForm
+ {
+ $type = $this->getPresenter()->getParameter('type');
+ $patrolId = $this->getPresenter()->getParameter('patrol_id');
+ if ($patrolId !== null) {
+ $patrolId = (int) $patrolId;
+ }
+
+ $form = $this->groupAdditionalInfoFormFactory->create($type, $patrolId);
+
+ $form->onSave[] = function () use ($type, $patrolId): void {
+ $this->getPresenter()->redirect('this', ['step' => 'confirm', 'type' => $type, 'patrol_id' => $patrolId]);
+ };
+
+ return $form;
+ }
+
+ protected function createComponentGroupConfirmForm(): GroupConfirmForm
+ {
+ $type = $this->getPresenter()->getParameter('type');
+ $patrolId = $this->getPresenter()->getParameter('patrol_id');
+ if ($patrolId !== null) {
+ $patrolId = (int) $patrolId;
+ }
+
+ $form = $this->groupConfirmFormFactory->create($type, $patrolId);
+
+ $form->onSave[] = function (): void {
+ $this->getPresenter()->redirect('this');
+ };
+
+ return $form;
+ }
+
+ protected function createComponentTroopConfirmForm(): TroopConfirmForm
+ {
+ $form = $this->troopConfirmFormFactory->create();
+
+ $form->onSave[] = function (): void {
+ $p = $this->getPresenter();
+ $p->flashMessage('Přihláška skupiny byla úspěšně odeslána.', 'success');
+ $p->redirect('this');
+ };
+
+ return $form;
+ }
+
+ public function handleChangeRole(int $roleId): void
+ {
+ $this->skautIsService->updateUserRole($roleId);
+ $this->redirect('this');
+ }
+}
diff --git a/app/WebModule/Components/templates/troop_application_content.latte b/app/WebModule/Components/templates/troop_application_content.latte
new file mode 100644
index 000000000..e54dbb2fd
--- /dev/null
+++ b/app/WebModule/Components/templates/troop_application_content.latte
@@ -0,0 +1,204 @@
+
+
+
+
+
+ {if $guestRole}
+
+ {elseif empty($skautIsRoles)}
+ Nemáš správnou roli
+ {elseif $step === 'members'}
+ {control groupMembersForm}
+ {elseif $step === 'additional_info'}
+ {control groupAdditionalInfoForm}
+ {elseif $step === 'confirm'}
+ {control groupConfirmForm}
+ {else}
+
+
+
Skupina
+
+
+
+
+
+
+
+
+ {foreach $troop->getConfirmedPatrols() as $patrol}
+
+
+
{$patrol->getName()}
+
+
+
+
+
+
+
+ Jméno |
+ Role |
+
+ {foreach $patrol->getUsersRoles() as $userRole}
+
+ {$userRole->getUser()->getDisplayName()} |
+ {$userRole->getRole()->getName()} |
+
+ {/foreach}
+
+
+
+ Družina má {$patrol->countUsersInRoles(['attendee', 'patrol_leader'])}/12 účastníků
+ (z toho {$patrol->countUsersInRoles(['patrol_leader'])}/1 rádců)
+ a {$patrol->countUsersInRoles(['leader'])}/1 vedoucích.
+
+
+
+ {else}
+
+
+
+
+ Začni tím, že přidáš družinu.
+
+
+
+ {/foreach}
+
+
+
+
Dospělí průvodci
+
+
+
+
+ {if !$troop->getConfirmedPatrols()->isEmpty()}
+
+
+
+
+ Jméno |
+ Role |
+
+ {foreach $troop->getUsersRoles() as $userRole}
+
+ {$userRole->getUser()->getDisplayName()} |
+ {$userRole->getRole()->getName()} |
+
+ {/foreach}
+
+
+
+ Doprovod má {$troop->countUsersInRoles(['escort'])}/{$troop->getMaxEscortsCount()} členů.
+ Můžeš mít 2× počet družin dospělých (tedy {$troop->getMaxAdultsCount()},
+ z toho máš {$troop->countUsersInRoles(['leader'])} vedoucí
+ a {$troop->countUsersInRoles(['escort'])} doprovody).
+
+
+
+ {else}
+
+
+
+
+ Zatím nemáš družinu, bez ní to nepůjde.
+
+
+
+ {/if}
+
+
+
+
Shrnutí
+ {control troopConfirmForm}
+
+
+ {/if}
+
diff --git a/app/WebModule/Forms/GroupAdditionalInfoForm.php b/app/WebModule/Forms/GroupAdditionalInfoForm.php
new file mode 100644
index 000000000..92f83b045
--- /dev/null
+++ b/app/WebModule/Forms/GroupAdditionalInfoForm.php
@@ -0,0 +1,143 @@
+template->setFile(__DIR__ . '/templates/group_additional_info_form.latte');
+
+ $this->resolveUsersRoles();
+
+ $this->template->type = $this->type;
+ $this->template->patrolId = $this->patrolId;
+ $this->template->usersRoles = $this->usersRoles;
+
+ $this->template->attendeesCountError = $this->attendeesCountError;
+ $this->template->groupLeadersCountError = $this->groupLeadersCountError;
+ $this->template->leadersCountError = $this->leadersCountError;
+ $this->template->escortsCountError = $this->escortsCountError;
+
+ $this->template->render();
+ }
+
+ /**
+ * Vytvoří formulář.
+ */
+ public function createComponentForm(): Form
+ {
+ $this->resolveUsersRoles();
+
+ $form = $this->baseFormFactory->create();
+
+ foreach ($this->usersRoles as $userRole) {
+ $form->addTextArea('health_info_' . $userRole->getUser()->getId(), null, null, 3)
+ ->setDefaultValue($userRole->getUser()->getHealthInfo());
+ }
+
+ $form->addSubmit('submit', 'Pokračovat')->setDisabled($this->attendeesCountError || $this->groupLeadersCountError || $this->leadersCountError || $this->escortsCountError);
+
+ $form->setAction($this->getPresenter()->link('this', ['step' => 'additional_info', 'type' => $this->type, 'patrol_id' => $this->patrolId]));
+
+ $form->onSuccess[] = [$this, 'processForm'];
+
+ return $form;
+ }
+
+ /**
+ * Zpracuje formulář.
+ *
+ * @throws SettingsItemNotFoundException
+ * @throws Throwable
+ * @throws MailingMailCreationException
+ */
+ public function processForm(Form $form, stdClass $values): void
+ {
+ foreach ($this->usersRoles as $userRole) {
+ $user = $userRole->getUser();
+ $healthInfoInputName = 'health_info_' . $user->getId();
+ $user->setHealthInfo($values->$healthInfoInputName);
+ $this->userRepository->save($user);
+ }
+
+ $this->onSave();
+ }
+
+ private function resolveUsersRoles(): void
+ {
+ $user = $this->queryBus->handle(new UserByIdQuery($this->presenter->user->getId()));
+ $troop = $this->queryBus->handle(new TroopByLeaderQuery($user->getId()));
+
+ if ($this->type == 'patrol') {
+ if ($this->patrolId !== null) {
+ $patrol = $this->queryBus->handle(new PatrolByIdQuery($this->patrolId));
+ $this->usersRoles = $patrol->getUsersRoles()->toArray();
+ } else {
+ $patrol = $this->queryBus->handle(new PatrolByTroopAndNotConfirmedQuery($troop->getId()));
+ $this->patrolId = $patrol->getId();
+ $this->usersRoles = $patrol->getUsersRoles()->toArray();
+ }
+
+ $attendeesCount = $patrol->countUsersInRoles([Role::ATTENDEE, Role::PATROL_LEADER]);
+ $this->attendeesCountError = $attendeesCount < 4 || $attendeesCount > 12;
+ $this->groupLeadersCountError = $patrol->countUsersInRoles([Role::PATROL_LEADER]) > 1;
+ $this->leadersCountError = $patrol->countUsersInRoles([Role::LEADER]) != 1;
+ } elseif ($this->type === 'troop') {
+ $this->usersRoles = $troop->getUsersRoles()->toArray();
+ $this->escortsCountError = $troop->countUsersInRoles([Role::ESCORT]) > $troop->getMaxEscortsCount();
+ }
+
+ $collator = new Collator('cs_CZ');
+ usort($this->usersRoles, static fn ($a, $b) => $collator->compare($a->getUser()->getDisplayName(), $b->getUser()->getDisplayName()));
+ }
+}
diff --git a/app/WebModule/Forms/GroupConfirmForm.php b/app/WebModule/Forms/GroupConfirmForm.php
new file mode 100644
index 000000000..7c7adf240
--- /dev/null
+++ b/app/WebModule/Forms/GroupConfirmForm.php
@@ -0,0 +1,120 @@
+template->setFile(__DIR__ . '/templates/group_confirm_form.latte');
+
+ $this->resolveUsersRoles();
+
+ $this->template->type = $this->type;
+ $this->template->patrolId = $this->patrolId;
+ $this->template->usersRoles = $this->usersRoles;
+
+ $this->template->render();
+ }
+
+ /**
+ * Vytvoří formulář.
+ */
+ public function createComponentForm(): Form
+ {
+ $this->resolveUsersRoles();
+
+ $form = $this->baseFormFactory->create();
+
+ $form->addSubmit('submit', 'Pokračovat');
+
+ $form->setAction($this->getPresenter()->link('this', ['step' => 'confirm', 'type' => $this->type, 'patrol_id' => $this->patrolId]));
+
+ $form->onSuccess[] = [$this, 'processForm'];
+
+ return $form;
+ }
+
+ /**
+ * Zpracuje formulář.
+ *
+ * @throws SettingsItemNotFoundException
+ * @throws Throwable
+ * @throws MailingMailCreationException
+ */
+ public function processForm(Form $form, stdClass $values): void
+ {
+ if ($this->type == 'patrol') {
+ $this->commandBus->handle(new ConfirmPatrol($this->patrolId));
+ }
+
+ $this->onSave();
+ }
+
+ private function resolveUsersRoles(): void
+ {
+ $user = $this->queryBus->handle(new UserByIdQuery($this->presenter->user->getId()));
+ $troop = $this->queryBus->handle(new TroopByLeaderQuery($user->getId()));
+
+ if ($this->type == 'patrol') {
+ if ($this->patrolId !== null) {
+ $patrol = $this->queryBus->handle(new PatrolByIdQuery($this->patrolId));
+ $this->usersRoles = $patrol->getUsersRoles()->toArray();
+ } else {
+ $patrol = $this->queryBus->handle(new PatrolByTroopAndNotConfirmedQuery($troop->getId()));
+ $this->patrolId = $patrol->getId();
+ $this->usersRoles = $patrol->getUsersRoles()->toArray();
+ }
+ } elseif ($this->type === 'troop') {
+ $this->usersRoles = $troop->getUsersRoles()->toArray();
+ }
+
+ $collator = new Collator('cs_CZ');
+ usort($this->usersRoles, static fn ($a, $b) => $collator->compare($a->getUser()->getDisplayName(), $b->getUser()->getDisplayName()));
+ }
+}
diff --git a/app/WebModule/Forms/GroupMembersForm.php b/app/WebModule/Forms/GroupMembersForm.php
new file mode 100644
index 000000000..8f4657b0b
--- /dev/null
+++ b/app/WebModule/Forms/GroupMembersForm.php
@@ -0,0 +1,247 @@
+seminarStart = $this->queryBus->handle(new SettingDateValueQuery(Settings::SEMINAR_FROM_DATE));
+
+ $this->units = $this->skautIsService->getUnitAllUnit(self::$ALLOWED_UNIT_TYPES);
+ $this->members = [];
+
+ $collator = new Collator('cs_CZ');
+ foreach ($this->units as $unit) {
+ $unitMembers = $this->skautIsService->getMembershipAll($unit->ID, $this->type === 'troop' ? 18 : null, $this->seminarStart);
+ usort($unitMembers, static fn ($a, $b) => $collator->compare($a->Person, $b->Person));
+ $this->members[$unit->ID] = $unitMembers;
+ }
+ }
+
+ /**
+ * Vykreslí komponentu.
+ */
+ public function render(): void
+ {
+ $this->template->setFile(__DIR__ . '/templates/group_members_form.latte');
+
+ $this->template->units = $this->units;
+ $this->template->members = $this->members;
+
+ $this->template->render();
+ }
+
+ /**
+ * Vytvoří formulář.
+ */
+ public function createComponentForm(): Form
+ {
+ $user = $this->queryBus->handle(new UserByIdQuery($this->presenter->user->getId()));
+
+ $roles = $this->queryBus->handle(new RolesByTypeQuery($this->type));
+ $roleSelectOptions = $this->getRoleSelectOptions($roles);
+
+ $troop = $this->queryBus->handle(new TroopByLeaderQuery($user->getId()));
+ $this->troop = $troop;
+ $usersRoles = null;
+ if ($this->type === 'patrol') {
+ if ($this->patrolId !== null) {
+ $patrol = $this->queryBus->handle(new PatrolByIdQuery($this->patrolId));
+ } else {
+ $patrol = $this->queryBus->handle(new PatrolByTroopAndNotConfirmedQuery($troop->getId()));
+ }
+
+ if ($patrol != null) {
+ $usersRoles = $patrol->getUsersRoles();
+ $this->patrolId = $patrol->getId();
+ }
+ } elseif ($this->type === 'troop') {
+ $usersRoles = $troop->getUsersRoles();
+ }
+
+ $form = $this->baseFormFactory->create();
+
+ foreach ($this->units as $unit) {
+ foreach ($this->members[$unit->ID] as $member) {
+ $register = false;
+ $role = null;
+
+ if ($usersRoles !== null) {
+ foreach ($usersRoles as $usersRole) {
+ if ($usersRole->getUser()->getSkautISPersonId() === $member->ID_Person) {
+ $register = true;
+ $role = $usersRole->getRole();
+ break;
+ }
+ }
+ }
+
+ $memberId = $member->ID;
+ $registerCheckbox = $form->addCheckbox('register_' . $memberId)
+ ->setDefaultValue($register);
+ $registerCheckbox
+ ->addCondition(Form::EQUAL, true)
+ ->toggle('roleselect-' . $memberId);
+
+ $roleSelect = $form->addSelect('role_' . $memberId, null, $roleSelectOptions)
+ ->setHtmlId('roleselect-' . $memberId)
+ ->setHtmlAttribute('class', 'form-control-sm ignore-bs-select');
+ if ($role != null) {
+ $roleSelect->setDefaultValue($role->getId());
+ }
+
+ $birthdate = new DateTimeImmutable($member->Birthday);
+ $age = $this->countAgeAt($birthdate, $this->seminarStart);
+ foreach ($roles as $r) {
+ if ($age < $r->getMinimumAge()) {
+ $roleSelect
+ ->addConditionOn($registerCheckbox, Form::FILLED)
+ ->addCondition(Form::EQUAL, $r->getId())
+ ->addRule(Form::NOT_EQUAL, $r->getMinimumAgeWarning() ? $r->getMinimumAgeWarning() : 'Příliš nízký věk.', $r->getId());
+ } elseif ($age > $r->getMaximumAge()) {
+ $roleSelect
+ ->addConditionOn($registerCheckbox, Form::FILLED)
+ ->addCondition(Form::EQUAL, $r->getId())
+ ->addRule(Form::NOT_EQUAL, $r->getMinimumAgeWarning() ? $r->getMinimumAgeWarning() : 'Příliš vysoký věk.', $r->getId());
+ }
+ }
+ }
+ }
+
+ $form->addSubmit('submit', 'Pokračovat');
+
+ $form->setAction($this->getPresenter()->link('this', ['step' => 'members', 'type' => $this->type, 'patrol_id' => $this->patrolId]));
+
+ $form->onSuccess[] = [$this, 'processForm'];
+
+ return $form;
+ }
+
+ /**
+ * Zpracuje formulář.
+ *
+ * @throws SettingsItemNotFoundException
+ * @throws Throwable
+ * @throws MailingMailCreationException
+ */
+ public function processForm(Form $form, stdClass $values): void
+ {
+ $selectedPersons = [];
+
+ foreach ($this->units as $unit) {
+ foreach ($this->members[$unit->ID] as $member) {
+ $memberId = $member->ID;
+ $registerInputName = 'register_' . $memberId;
+ $roleInputName = 'role_' . $memberId;
+ if ($values->$registerInputName) {
+ $selectedPersons[] = ['roleId' => $values->$roleInputName, 'personId' => $member->ID_Person];
+ }
+ }
+ }
+
+ if ($this->troop->getState() !== TroopApplicationState::DRAFT) {
+ if ($this->type === 'patrol') {
+ $patrol = $this->queryBus->handle(new PatrolByIdQuery($this->patrolId));
+ $usersCount = $patrol->getUsersRoles()->count();
+ } else {
+ $usersCount = $this->troop->getUsersRoles()->count();
+ }
+
+ if ($usersCount !== count($selectedPersons)) {
+ $this->onError();
+
+ return;
+ }
+ }
+
+ $this->commandBus->handle(new UpdateGroupMembers($this->type, $this->troop->getId(), $this->patrolId, $selectedPersons));
+
+ $this->onSave();
+ }
+
+ /**
+ * @param Role[] $roles
+ *
+ * @return string[]
+ */
+ private function getRoleSelectOptions($roles): array
+ {
+ $options = [];
+
+ foreach ($roles as $role) {
+ $options[$role->getId()] = $role->getName();
+ }
+
+ return $options;
+ }
+
+ private function countAgeAt(DateTimeImmutable $birthdate, DateTimeImmutable $seminarStart): int
+ {
+ return $seminarStart->diff($birthdate)->y;
+ }
+}
diff --git a/app/WebModule/Forms/IGroupAdditionalInfoFormFactory.php b/app/WebModule/Forms/IGroupAdditionalInfoFormFactory.php
new file mode 100644
index 000000000..3bcc8fdcc
--- /dev/null
+++ b/app/WebModule/Forms/IGroupAdditionalInfoFormFactory.php
@@ -0,0 +1,16 @@
+template->setFile(__DIR__ . '/templates/contact_form.latte');
+ $this->template->render();
+ }
+
+ /**
+ * Vytvoří formulář.
+ */
+ public function createComponentForm(): Form
+ {
+ $this->user = $this->userRepository->findById($this->presenter->user->getId());
+
+ $form = $this->baseFormFactory->create();
+
+ $nameText = $form->addText('name', 'web.contact_form_content.name')
+ ->addRule(Form::FILLED, 'web.contact_form_content.name_empty');
+
+ $emailText = $form->addText('email', 'web.contact_form_content.email')
+ ->addRule(Form::FILLED, 'web.contact_form_content.email_empty')
+ ->addRule(Form::EMAIL, 'web.contact_form_content.email_format');
+
+ $form->addTextArea('message', 'web.contact_form_content.message')
+ ->addRule(Form::FILLED, 'web.contact_form_content.message_empty');
+
+ $form->addCheckbox('sendCopy', 'web.contact_form_content.send_copy');
+
+ if ($this->user === null) {
+ $field = new ReCaptchaField($this->recaptchaProvider);
+ $field->addRule(Form::FILLED, 'web.contact_form_content.recaptcha_empty');
+ $form->addComponent($field, 'recaptcha');
+ }
+
+ $form->addSubmit('submit', 'web.contact_form_content.send_message');
+
+ if ($this->user !== null) {
+ $nameText->setDisabled();
+ $emailText->setDisabled();
+
+ $form->setDefaults([
+ 'name' => $this->user->getDisplayName(),
+ 'email' => $this->user->getEmail(),
+ ]);
+ }
+
+ $form->onSuccess[] = [$this, 'processForm'];
+
+ return $form;
+ }
+
+ /**
+ * Zpracuje formulář.
+ *
+ * @throws SettingsItemNotFoundException
+ * @throws Throwable
+ * @throws MailingMailCreationException
+ */
+ public function processForm(Form $form, stdClass $values): void
+ {
+ $recipientsUsers = new ArrayCollection();
+ $recipientsEmails = new ArrayCollection();
+
+ if ($this->user) {
+ $senderName = $this->user->getDisplayName();
+ $senderEmail = $this->user->getEmail();
+ if ($values->sendCopy) {
+ $recipientsUsers->add($this->user);
+ }
+ } else {
+ $senderName = $values->name;
+ $senderEmail = $values->email;
+ if ($values->sendCopy) {
+ $recipientsEmails->add($senderEmail);
+ }
+ }
+
+ $recipients = $this->queryBus->handle(new SettingArrayValueQuery(Settings::CONTACT_FORM_RECIPIENTS));
+ foreach ($recipients as $recipient) {
+ $recipientsEmails->add($recipient);
+ }
+
+ $this->mailService->sendMailFromTemplate(
+ $recipientsUsers,
+ $recipientsEmails,
+ Template::CONTACT_FORM,
+ [
+ TemplateVariable::SEMINAR_NAME => $this->queryBus->handle(new SettingStringValueQuery(Settings::SEMINAR_NAME)),
+ TemplateVariable::SENDER_NAME => $senderName,
+ TemplateVariable::SENDER_EMAIL => $senderEmail,
+ TemplateVariable::MESSAGE => str_replace(["\n", "\r"], '', nl2br($values->message, false)),
+ ]
+ );
+
+ $this->onSave();
+ }
+}
diff --git a/app/WebModule/Forms/TroopConfirmForm.php b/app/WebModule/Forms/TroopConfirmForm.php
new file mode 100644
index 000000000..83e017fcf
--- /dev/null
+++ b/app/WebModule/Forms/TroopConfirmForm.php
@@ -0,0 +1,134 @@
+template->setFile(__DIR__ . '/templates/troop_confirm_form.latte');
+
+ $this->resolveTroop();
+
+ $this->template->troop = $this->troop;
+ $this->template->agreement = $this->queryBus->handle(new SettingStringValueQuery(Settings::APPLICATION_AGREEMENT));
+
+ $this->template->allCountError = $this->allCountError;
+ $this->template->duplicitUsersError = $this->duplicitUsersError;
+ $this->template->userNotLeaderError = $this->userNotLeaderError;
+
+ $this->template->render();
+ }
+
+ /**
+ * Vytvoří formulář.
+ */
+ public function createComponentForm(): Form
+ {
+ $this->resolveTroop();
+
+ $form = $this->baseFormFactory->create();
+
+ $pairedTroopCodeText = $form->addText('pairedTroopCode')
+ ->setDefaultValue($this->troop->getPairedTroopCode());
+
+ $agreementCheckbox = $form->addCheckbox('agreement', 'Souhlasím s podmínkami akce.')
+ ->addRule(Form::FILLED, 'Musíš souhlasit s podmínkami akce.');
+
+ $submit = $form->addSubmit('submit', 'Závazně registrovat')
+ ->setDisabled($this->allCountError || $this->duplicitUsersError || $this->userNotLeaderError);
+
+ if ($this->troop->getState() !== TroopApplicationState::DRAFT) {
+ $pairedTroopCodeText->setHtmlAttribute('readonly');
+ $agreementCheckbox->setDisabled();
+ $submit->setDisabled();
+ }
+
+ $form->setAction($this->getPresenter()->link('this'));
+
+ $form->onSuccess[] = [$this, 'processForm'];
+
+ return $form;
+ }
+
+ /**
+ * Zpracuje formulář.
+ *
+ * @throws SettingsItemNotFoundException
+ * @throws Throwable
+ * @throws MailingMailCreationException
+ */
+ public function processForm(Form $form, stdClass $values): void
+ {
+ $this->commandBus->handle(new ConfirmTroop($this->troop->getId(), $values->pairedTroopCode));
+
+ $this->onSave();
+ }
+
+ private function resolveTroop(): void
+ {
+ $user = $this->queryBus->handle(new UserByIdQuery($this->presenter->user->getId()));
+ $this->troop = $this->queryBus->handle(new TroopByLeaderQuery($user->getId()));
+
+ $allCount = $this->troop->countUsersInRoles([Role::ATTENDEE, Role::PATROL_LEADER, Role::LEADER, Role::ESCORT]);
+ $this->allCountError = $allCount > 42;
+
+ $countFromPatrols = 0;
+ foreach ($this->troop->getConfirmedPatrols() as $patrol) {
+ $countFromPatrols += $patrol->countUsersInRoles([Role::ATTENDEE, Role::PATROL_LEADER]);
+ }
+
+ $countFromTroops = $this->troop->countUsersInRoles([Role::ATTENDEE, Role::PATROL_LEADER]);
+ $this->duplicitUsersError = $countFromPatrols !== $countFromTroops;
+
+ $this->userNotLeaderError = $user->getGroupRoles()
+ ->filter(static fn (UserGroupRole $groupRole) => $groupRole->getRole()->getSystemName() === Role::LEADER && $groupRole->getPatrol()->isConfirmed())
+ ->count() === 0;
+ }
+}
diff --git a/app/WebModule/Forms/templates/group_additional_info_form.latte b/app/WebModule/Forms/templates/group_additional_info_form.latte
new file mode 100644
index 000000000..8a7f5cdad
--- /dev/null
+++ b/app/WebModule/Forms/templates/group_additional_info_form.latte
@@ -0,0 +1,73 @@
+{form form class => form-horizontal}
+
+
+
+
+
+
+ Počet účastníků musí být 4-12.
+
+
+
+
+
+ Počet rádců musí být 0-1.
+
+
+
+
+
+ Vedoucí musí být právě 1.
+
+
+
+
+
+ Počet doprovodů je příliš vysoký.
+
+
+
+
+ {foreach $usersRoles as $userRole}
+ {var $user = $userRole->getUser()}
+
+
+
{$user->getDisplayName()}
+
{$userRole->getRole()->getName()}
+
+
+ {$user->getStreet()},
+ {$user->getPostcode()} {$user->getCity()}
+
+ Telefon: {$user->getPhone()}
+ E-mail: {$user->getEmail()}
+ Datum narození: {$user->getBirthdate()|date:'j. n. Y'}
+ {if $user->getMotherPhone() !== null}
+
+ Telefon matky: {$user->getMotherPhone()} ({$user->getMotherName()})
+ {/if}
+ {if $user->getFatherPhone() !== null}
+
+ Telefon otce: {$user->getFatherPhone()} ({$user->getFatherName()})
+ {/if}
+
+
+ Zdravotní omezení, alergie, pravidelně užívané léky
+ {input 'health_info_' . $user->getId()}
+
+
+
+
+ {/foreach}
+
+
+
+
Zpět
+ {input submit class => 'btn-primary button'}
+
+
+{/form}
diff --git a/app/WebModule/Forms/templates/group_confirm_form.latte b/app/WebModule/Forms/templates/group_confirm_form.latte
new file mode 100644
index 000000000..8acb7bf14
--- /dev/null
+++ b/app/WebModule/Forms/templates/group_confirm_form.latte
@@ -0,0 +1,47 @@
+{form form class => form-horizontal}
+
+
+
+ {foreach $usersRoles as $userRole}
+ {var $user = $userRole->getUser()}
+
+
+
+
{$user->getDisplayName()}
+
{$userRole->getRole()->getName()}
+ {$user->getStreet()},
+ {$user->getPostcode()} {$user->getCity()}
+
+
Telefon: {$user->getPhone()}
+
E-mail: {$user->getEmail()}
+
Datum narození: {$user->getBirthdate()|date:'j. n. Y'}
+ {if $user->getMotherPhone() !== null}
+
+
Telefon matky: {$user->getMotherPhone()} ({$user->getMotherName()})
+ {/if}
+ {if $user->getFatherPhone() !== null}
+
+
Telefon otce: {$user->getFatherPhone()} ({$user->getFatherName()})
+ {/if}
+ {if $user->getHealthInfo()}
+
+
Zdravotní omezení, alergie, pravidelně užívané léky:
+
{$user->getHealthInfo()|breakLines}
+ {/if}
+
+
+
+ {/foreach}
+
+
+
+
+
Zpět
+ {input submit class => 'btn-primary button'}
+
+
+ {/form}
diff --git a/app/WebModule/Forms/templates/group_members_form.latte b/app/WebModule/Forms/templates/group_members_form.latte
new file mode 100644
index 000000000..0b2791b2f
--- /dev/null
+++ b/app/WebModule/Forms/templates/group_members_form.latte
@@ -0,0 +1,59 @@
+{form form class => form-horizontal}
+
+
+
+
+
1. Vyber účastníky
+
+
+
+
+
+ {foreach $units as $unit}
+
+
+ Registrovat |
+ Družina |
+ Jméno |
+ Role |
+
+ {foreach $members[$unit->ID] as $member}
+
+ {input 'register_' . $member->ID} |
+ {if $unit->ID !== $member->ID_Unit}{$member->Unit}{/if} |
+ {$member->Person} |
+ {input 'role_' . $member->ID} |
+
+ {else}
+
+ Oddíl nemá žádné členy, které je možné vybrat. |
+
+ {/foreach}
+
+ {/foreach}
+
+
+
+
Zpět
+ {input submit class => 'btn-primary button'}
+
+
+ {/form}
diff --git a/app/WebModule/Forms/templates/troop_confirm_form.latte b/app/WebModule/Forms/templates/troop_confirm_form.latte
new file mode 100644
index 000000000..34f8bdf45
--- /dev/null
+++ b/app/WebModule/Forms/templates/troop_confirm_form.latte
@@ -0,0 +1,81 @@
+{form form class => form-horizontal}
+
+
+
+
+
+
+
+
+
+
+
+
+ {input submit class => 'btn-primary button'}
+
+
+ {/form}
diff --git a/app/WebModule/Presenters/PagePresenter.php b/app/WebModule/Presenters/PagePresenter.php
index 5008119fb..a8d006957 100644
--- a/app/WebModule/Presenters/PagePresenter.php
+++ b/app/WebModule/Presenters/PagePresenter.php
@@ -27,6 +27,7 @@
use App\WebModule\Components\IProgramsContentControlFactory;
use App\WebModule\Components\ISlideshowContentControlFactory;
use App\WebModule\Components\ITextContentControlFactory;
+use App\WebModule\Components\ITroopApplicationContentControlFactory;
use App\WebModule\Components\IUsersContentControlFactory;
use App\WebModule\Components\LectorsContentControl;
use App\WebModule\Components\NewsContentControl;
@@ -35,6 +36,7 @@
use App\WebModule\Components\ProgramsContentControl;
use App\WebModule\Components\SlideshowContentControl;
use App\WebModule\Components\TextContentControl;
+use App\WebModule\Components\TroopApplicationContentControl;
use App\WebModule\Components\UsersContentControl;
use Nette\Application\BadRequestException;
use Nette\DI\Attributes\Inject;
@@ -48,6 +50,9 @@ class PagePresenter extends WebBasePresenter
#[Inject]
public IApplicationContentControlFactory $applicationContentControlFactory;
+ #[Inject]
+ public ITroopApplicationContentControlFactory $troopApplicationContentControlFactory;
+
#[Inject]
public IBlocksContentControlFactory $blocksContentControlFactory;
@@ -132,6 +137,11 @@ protected function createComponentApplicationContent(): ApplicationContentContro
return $this->applicationContentControlFactory->create();
}
+ protected function createComponentTroopApplicationContent(): TroopApplicationContentControl
+ {
+ return $this->troopApplicationContentControlFactory->create();
+ }
+
protected function createComponentBlocksContent(): BlocksContentControl
{
return $this->blocksContentControlFactory->create();
diff --git a/app/assets/common/main.js b/app/assets/common/main.js
index 43dd6dfa2..79247a19c 100644
--- a/app/assets/common/main.js
+++ b/app/assets/common/main.js
@@ -98,6 +98,7 @@ function initSelects() {
.not('.datagrid .row-group-actions select')
.not('.datagrid .col-per-page select')
.not('.modal-body select')
+ .not('.ignore-bs-select')
.add('select[multiple]')
.selectpicker({
noneSelectedText: 'Nic není vybráno',
diff --git a/app/lang/common.cs_CZ.neon b/app/lang/common.cs_CZ.neon
index 187c69e62..245f192fe 100644
--- a/app/lang/common.cs_CZ.neon
+++ b/app/lang/common.cs_CZ.neon
@@ -34,6 +34,7 @@ content:
image: "Obrázek"
document: "Dokumenty"
application: "Přihlašovací formulář"
+ troop_application: "Přihlašovací formulář skupiny"
html: "HTML box"
faq: "FAQ"
news: "Aktuality"
@@ -52,6 +53,7 @@ content:
image: ""
document: "Dokumenty"
application: "Přihlašovací formulář"
+ troop_application: "Přihlašovací formulář skupiny"
html: ""
faq: "FAQ"
news: "Aktuality"
diff --git a/migrations/Version20221015133440.php b/migrations/Version20221015133440.php
index e95137183..8b36b5d89 100644
--- a/migrations/Version20221015133440.php
+++ b/migrations/Version20221015133440.php
@@ -19,7 +19,7 @@ public function up(Schema $schema): void
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('CREATE TABLE patrol (id INT AUTO_INCREMENT NOT NULL, troop_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, INDEX IDX_BFB2371263060AC (troop_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
- $this->addSql('CREATE TABLE troop (id INT AUTO_INCREMENT NOT NULL, variable_symbol_id INT DEFAULT NULL, payment_id INT DEFAULT NULL, income_proof_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, fee INT NOT NULL, application_date DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', maturity_date DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', payment_method VARCHAR(255) DEFAULT NULL, payment_date DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', state VARCHAR(255) NOT NULL, INDEX IDX_FAAD534C25813A9D (variable_symbol_id), INDEX IDX_FAAD534C4C3A3BB (payment_id), INDEX IDX_FAAD534CFE69EDFB (income_proof_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+ $this->addSql('CREATE TABLE troop (id INT AUTO_INCREMENT NOT NULL, variable_symbol_id INT DEFAULT NULL, payment_id INT DEFAULT NULL, income_proof_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, fee INT NOT NULL, application_date DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', maturity_date DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', payment_method VARCHAR(255) DEFAULT NULL, payment_date DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', state VARCHAR(255) NOT NULL, INDEX IDX_FAAD534C25813A9D (variable_symbol_id), INDEX IDX_FAAD534C4C3A3BB (payment_id), INDEX IDX_FAAD534CFE69EDFB (income_proof_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE user_group_role (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, troop_id INT DEFAULT NULL, patrol_id INT DEFAULT NULL, role_id INT DEFAULT NULL, INDEX IDX_D95417F6A76ED395 (user_id), INDEX IDX_D95417F6263060AC (troop_id), INDEX IDX_D95417F6A7B49BA9 (patrol_id), INDEX IDX_D95417F6D60322AC (role_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE patrol ADD CONSTRAINT FK_BFB2371263060AC FOREIGN KEY (troop_id) REFERENCES troop (id)');
$this->addSql('ALTER TABLE troop ADD CONSTRAINT FK_FAAD534C25813A9D FOREIGN KEY (variable_symbol_id) REFERENCES variable_symbol (id)');
diff --git a/migrations/Version20221017191553.php b/migrations/Version20221017191553.php
new file mode 100644
index 000000000..27e23c8fc
--- /dev/null
+++ b/migrations/Version20221017191553.php
@@ -0,0 +1,28 @@
+abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
+
+ $this->addSql('CREATE TABLE troop_application_content (id INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+ $this->addSql('ALTER TABLE troop_application_content ADD CONSTRAINT FK_9E479B84BF396750 FOREIGN KEY (id) REFERENCES content (id) ON DELETE CASCADE');
+ }
+
+ public function down(Schema $schema): void
+ {
+ }
+}
diff --git a/migrations/Version20221021195625.php b/migrations/Version20221021195625.php
new file mode 100644
index 000000000..9a08ad5fa
--- /dev/null
+++ b/migrations/Version20221021195625.php
@@ -0,0 +1,30 @@
+abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
+
+ $this->addSql('ALTER TABLE troop ADD leader_id INT DEFAULT NULL, ADD pairing_code VARCHAR(255) NOT NULL, ADD paired_troop_code VARCHAR(255) DEFAULT NULL');
+ $this->addSql('ALTER TABLE troop ADD CONSTRAINT FK_FAAD534C73154ED4 FOREIGN KEY (leader_id) REFERENCES user (id)');
+ $this->addSql('CREATE UNIQUE INDEX UNIQ_FAAD534C73154ED4 ON troop (leader_id)');
+ $this->addSql('ALTER TABLE user ADD phone VARCHAR(255) DEFAULT NULL, ADD mother_name VARCHAR(255) DEFAULT NULL, ADD mother_phone VARCHAR(255) DEFAULT NULL, ADD father_name VARCHAR(255) DEFAULT NULL, ADD father_phone VARCHAR(255) DEFAULT NULL, ADD health_info LONGTEXT DEFAULT NULL');
+ }
+
+ public function down(Schema $schema): void
+ {
+ }
+}
diff --git a/migrations/Version20221022182325.php b/migrations/Version20221022182325.php
new file mode 100644
index 000000000..3f031f9ee
--- /dev/null
+++ b/migrations/Version20221022182325.php
@@ -0,0 +1,32 @@
+abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
+
+ $this->addSql('ALTER TABLE patrol ADD confirmed TINYINT(1) NOT NULL');
+ $this->addSql('UPDATE role SET type=\'individual\', maximum_age=150');
+ $this->addSql('UPDATE role SET type=\'patrol\', minimum_age=10, maximum_age=16 WHERE name=\'Účastník\'');
+ $this->addSql('INSERT INTO `role` (`name`, `system_name`, `system_role`, `registerable`, `approved_after_registration`, `synced_with_skaut_is`, `occupancy`, `minimum_age`, `type`, `maximum_age`) VALUES (\'Rádce\', \'patrol_leader\', 1, 1, 1, 0, 0, 10, \'patrol\', 17)');
+ $this->addSql('INSERT INTO `role` (`name`, `system_name`, `system_role`, `registerable`, `approved_after_registration`, `synced_with_skaut_is`, `occupancy`, `minimum_age`, `type`, `maximum_age`) VALUES (\'Vedoucí\', \'leader\', 1, 1, 1, 0, 0, 18, \'patrol\', 150)');
+ $this->addSql('INSERT INTO `role` (`name`, `system_name`, `system_role`, `registerable`, `approved_after_registration`, `synced_with_skaut_is`, `occupancy`, `minimum_age`, `type`, `maximum_age`) VALUES (\'Dospělý doprovod\', \'escort\', 1, 1, 1, 0, 0, 18, \'troop\', 150)');
+ }
+
+ public function down(Schema $schema): void
+ {
+ }
+}
From a9796fc41e0e9f1b5de4f336e41cd57415c06384 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Tue, 1 Nov 2022 17:05:55 +0100
Subject: [PATCH 07/41] =?UTF-8?q?Opravy=20registra=C4=8Dn=C3=ADho=20formul?=
=?UTF-8?q?=C3=A1=C5=99e=20(#893)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* fee fix
* min/max age warning fixed
* allow role change with registered patrols
* group name in headline
* agreement text
* reformat html
* confirmed application warning
* confirm email
* phpstan
* headline changed
* quotes removed
* database dump detailed error
* skip db backup
Co-authored-by: Jan Staněk
---
app/Commands/BackupDatabaseCommand.php | 4 +-
app/Model/Mailing/Template.php | 5 +
.../Commands/Handlers/ConfirmTroopHandler.php | 16 +-
app/Model/User/Troop.php | 6 +
.../templates/troop_application_content.latte | 10 +-
.../Forms/GroupAdditionalInfoForm.php | 4 +
app/WebModule/Forms/GroupConfirmForm.php | 5 +
app/WebModule/Forms/GroupMembersForm.php | 70 +--
app/WebModule/Forms/TroopConfirmForm.php | 2 +-
.../group_additional_info_form.latte | 5 +
.../Forms/templates/group_confirm_form.latte | 5 +
.../Forms/templates/group_members_form.latte | 5 +
.../Forms/templates/troop_confirm_form.latte | 403 +++++++++++++++++-
app/lang/common.cs_CZ.neon | 1 +
migrations/Version20221101001721.php | 34 ++
15 files changed, 539 insertions(+), 36 deletions(-)
create mode 100644 migrations/Version20221101001721.php
diff --git a/app/Commands/BackupDatabaseCommand.php b/app/Commands/BackupDatabaseCommand.php
index 5ed52b505..6d2f3d6fe 100644
--- a/app/Commands/BackupDatabaseCommand.php
+++ b/app/Commands/BackupDatabaseCommand.php
@@ -50,8 +50,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$output->writeln('Database dump created successfully.');
return 0;
- } catch (Throwable) {
- $output->writeln('Database dump creation failed.');
+ } catch (Throwable $e) {
+ $output->writeln('Database dump creation failed: ' . $e);
return 1;
}
diff --git a/app/Model/Mailing/Template.php b/app/Model/Mailing/Template.php
index d8a8703a7..49376ac0c 100644
--- a/app/Model/Mailing/Template.php
+++ b/app/Model/Mailing/Template.php
@@ -25,6 +25,11 @@ class Template
*/
public const REGISTRATION = 'registration';
+ /**
+ * Potvrzení registrace oddílu.
+ */
+ public const TROOP_REGISTRATION = 'troop_registration';
+
/**
* Odhlášení ze semináře.
*/
diff --git a/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php b/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php
index 5866c747b..1a7dc9c40 100644
--- a/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php
+++ b/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php
@@ -6,20 +6,24 @@
use App\Model\Enums\MaturityType;
use App\Model\Enums\TroopApplicationState;
+use App\Model\Mailing\Template;
+use App\Model\Mailing\TemplateVariable;
use App\Model\Settings\Queries\SettingDateValueQuery;
use App\Model\Settings\Queries\SettingIntValueQuery;
use App\Model\Settings\Queries\SettingStringValueQuery;
use App\Model\Settings\Settings;
use App\Model\User\Commands\ConfirmTroop;
use App\Model\User\Repositories\TroopRepository;
+use App\Services\MailService;
use App\Services\QueryBus;
use DateTimeImmutable;
+use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Yasumi\Yasumi;
class ConfirmTroopHandler implements MessageHandlerInterface
{
- public function __construct(private QueryBus $queryBus, private TroopRepository $troopRepository)
+ public function __construct(private QueryBus $queryBus, private TroopRepository $troopRepository, private MailService $mailService)
{
}
@@ -32,6 +36,16 @@ public function __invoke(ConfirmTroop $command): void
$troop->setApplicationDate(new DateTimeImmutable());
$troop->setMaturityDate($this->countMaturityDate());
$this->troopRepository->save($troop);
+
+ $this->mailService->sendMailFromTemplate(new ArrayCollection([$troop->getLeader()]), null, Template::TROOP_REGISTRATION, [
+ TemplateVariable::SEMINAR_NAME => $this->queryBus->handle(new SettingStringValueQuery(Settings::SEMINAR_NAME)),
+ TemplateVariable::APPLICATION_FEE => (string) $troop->getFee(),
+ TemplateVariable::APPLICATION_VARIABLE_SYMBOL => $troop->getVariableSymbol()->getVariableSymbol(),
+ TemplateVariable::APPLICATION_MATURITY => $troop->getMaturityDateText(),
+ TemplateVariable::BANK_ACCOUNT => $this->queryBus->handle(
+ new SettingStringValueQuery(Settings::ACCOUNT_NUMBER)
+ ),
+ ]);
}
/**
diff --git a/app/Model/User/Troop.php b/app/Model/User/Troop.php
index 9fe9d5fdc..57544f9ea 100644
--- a/app/Model/User/Troop.php
+++ b/app/Model/User/Troop.php
@@ -8,6 +8,7 @@
use App\Model\Application\VariableSymbol;
use App\Model\Enums\TroopApplicationState;
use App\Model\Payment\Payment;
+use App\Utils\Helpers;
use DateTimeImmutable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
@@ -202,6 +203,11 @@ public function getMaturityDate(): ?DateTimeImmutable
return $this->maturityDate;
}
+ public function getMaturityDateText(): ?string
+ {
+ return $this->maturityDate?->format(Helpers::DATE_FORMAT);
+ }
+
public function setMaturityDate(?DateTimeImmutable $maturityDate): void
{
$this->maturityDate = $maturityDate;
diff --git a/app/WebModule/Components/templates/troop_application_content.latte b/app/WebModule/Components/templates/troop_application_content.latte
index e54dbb2fd..c6229331e 100644
--- a/app/WebModule/Components/templates/troop_application_content.latte
+++ b/app/WebModule/Components/templates/troop_application_content.latte
@@ -25,13 +25,21 @@
{elseif $step === 'confirm'}
{control groupConfirmForm}
{else}
+
+
+
+ Přihláška tvé skupiny byla úspěšně odeslána. Nyní můžeš pouze měnit osobu za osobu.
+
+
+
+
-{/block}
\ No newline at end of file
+{/block}
diff --git a/app/AdminModule/Presenters/templates/Users/groups.latte b/app/AdminModule/Presenters/templates/Users/groups.latte
new file mode 100644
index 000000000..e5384b458
--- /dev/null
+++ b/app/AdminModule/Presenters/templates/Users/groups.latte
@@ -0,0 +1,4 @@
+{block main}
+ Skupiny
+ {control groupsGrid}
+{/block}
diff --git a/app/AdminModule/Presenters/templates/Users/patrols.latte b/app/AdminModule/Presenters/templates/Users/patrols.latte
new file mode 100644
index 000000000..491eca487
--- /dev/null
+++ b/app/AdminModule/Presenters/templates/Users/patrols.latte
@@ -0,0 +1,4 @@
+{block main}
+ Družiny
+ {control patrolsGrid}
+{/block}
diff --git a/app/lang/admin.cs_CZ.neon b/app/lang/admin.cs_CZ.neon
index 5f451a5cc..da7370077 100644
--- a/app/lang/admin.cs_CZ.neon
+++ b/app/lang/admin.cs_CZ.neon
@@ -381,6 +381,12 @@ program:
export_schedule: "Stáhnout harmonogram"
users:
+ menu:
+ persons: "Osoby"
+ groups: "Skupiny"
+ troops: "Oddíly"
+ patrols: "Družiny"
+
users_heading: "Uživatelé"
users_name: "Jméno"
users_photo: "Fotka"
From a5792c6d8867389f58f8be9791b0ca25b6a170f0 Mon Sep 17 00:00:00 2001
From: David Urban
Date: Tue, 1 Nov 2022 20:48:27 +0100
Subject: [PATCH 09/41] Update error message to be more descriptive (#896)
---
.../Components/templates/troop_application_content.latte | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/WebModule/Components/templates/troop_application_content.latte b/app/WebModule/Components/templates/troop_application_content.latte
index c6229331e..d990c7005 100644
--- a/app/WebModule/Components/templates/troop_application_content.latte
+++ b/app/WebModule/Components/templates/troop_application_content.latte
@@ -17,7 +17,8 @@
{elseif empty($skautIsRoles)}
- Nemáš správnou roli
+ Ve skautISu nemáš dostatečné oprávnění. Registrace vyžaduje přístup k informacím
+ o členech (typicky admin oddílu/střediska).
{elseif $step === 'members'}
{control groupMembersForm}
{elseif $step === 'additional_info'}
From 6be789e35ca6c6de077484fc226e5b9181e8bb9e Mon Sep 17 00:00:00 2001
From: bojovyletoun <662941+bojovyletoun@users.noreply.github.com>
Date: Thu, 3 Nov 2022 08:40:55 +0100
Subject: [PATCH 10/41] =?UTF-8?q?opravy=20p=C5=99ehled=C5=AF=20(#898)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Groups-state-translate
doladění sloupců datagrid prehled-Groups,ale chybí jedna věcička: kdyby se nejak povedlo udelat setFilterText na ne-sloupce
* -duplicitní sloupec Cena
Co-authored-by: bojovyletoun <>
---
app/AdminModule/Components/GroupsGridControl.php | 8 ++++++--
app/lang/common.cs_CZ.neon | 1 +
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/app/AdminModule/Components/GroupsGridControl.php b/app/AdminModule/Components/GroupsGridControl.php
index 5eea3074b..21ea1fae4 100644
--- a/app/AdminModule/Components/GroupsGridControl.php
+++ b/app/AdminModule/Components/GroupsGridControl.php
@@ -72,6 +72,10 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->addColumnText('id', 'ID')
->setSortable();
+ $grid->addColumnText('state', 'Stav')->setSortable()
+ ->setRenderer(fn ($t) => $this->translator->translate('common.application_state.' . $t->getState()))
+ ->setFilterText();
+
$grid->addColumnText('name', 'Název')
->setSortable()
->setFilterText();
@@ -100,8 +104,8 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->addColumnText('fee', 'Cena getFee')->setSortable()->setFilterText();
- $grid->addColumnText('fee2', 'Cena countFee')
- ->setRenderer(static fn (Troop $t) => $t->countFee());
+// $grid->addColumnText('fee2', 'Cena countFee')
+// ->setRenderer(static fn (Troop $t) => $t->countFee());
$grid->addColumnDateTime('paymentDate', 'Datum zaplacení')
->setRenderer(static function (Troop $p) {
diff --git a/app/lang/common.cs_CZ.neon b/app/lang/common.cs_CZ.neon
index 009d3ebdc..9364537c2 100644
--- a/app/lang/common.cs_CZ.neon
+++ b/app/lang/common.cs_CZ.neon
@@ -119,6 +119,7 @@ application_state:
canceled: "Zrušeno"
paid: "Zaplaceno"
paid_free: "Zaplaceno (zdarma)"
+ draft: "nepotvrzené (draft)"
calendar_view:
timeGridSeminar: "Na výšku"
From 64eade770ecec3d07058af69406928b8b01d1dab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Thu, 3 Nov 2022 08:41:07 +0100
Subject: [PATCH 11/41] copy menu file during deploy (#899)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Jan Staněk
---
build.xml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/build.xml b/build.xml
index 41ad07f5c..e891c3d1a 100644
--- a/build.xml
+++ b/build.xml
@@ -183,6 +183,11 @@
+
+
+
+
+
From ff4c39014b5ac39fbca0ca6805a6e0d850291bd2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Thu, 3 Nov 2022 09:27:49 +0100
Subject: [PATCH 12/41] =?UTF-8?q?Oprava=20p=C5=99ihl=C3=A1=C5=A1ky=20servi?=
=?UTF-8?q?s=20t=C3=BDmu=20(#900)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* application fix
* cs fix
Co-authored-by: Jan Staněk
---
app/WebModule/Forms/ApplicationFormFactory.php | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/WebModule/Forms/ApplicationFormFactory.php b/app/WebModule/Forms/ApplicationFormFactory.php
index f90858c33..f058d8cc2 100644
--- a/app/WebModule/Forms/ApplicationFormFactory.php
+++ b/app/WebModule/Forms/ApplicationFormFactory.php
@@ -176,8 +176,7 @@ public function create(int $id): Form
'state' => $this->user->getState(),
]);
- $form->onSuccess[] = [$this, 'processForm'];
- $form->onValidate[] = [$this, 'validateRolesAgeLimits'];
+ $form->onSuccess[] = [$this, 'processForm'];
return $form;
}
From 52dddd41a81d85f2247793e0360d14ddc3929447 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Tue, 8 Nov 2022 20:32:07 +0100
Subject: [PATCH 13/41] payment info in form, buttons hidden (#901)
---
.../TroopApplicationContentControl.php | 4 ++++
.../templates/troop_application_content.latte | 13 ++++++++++---
.../Forms/templates/troop_confirm_form.latte | 17 +++++++++--------
3 files changed, 23 insertions(+), 11 deletions(-)
diff --git a/app/WebModule/Components/TroopApplicationContentControl.php b/app/WebModule/Components/TroopApplicationContentControl.php
index bc1525805..3ddafc693 100644
--- a/app/WebModule/Components/TroopApplicationContentControl.php
+++ b/app/WebModule/Components/TroopApplicationContentControl.php
@@ -7,6 +7,8 @@
use App\Model\Acl\Repositories\RoleRepository;
use App\Model\Acl\Role;
use App\Model\Cms\Dto\ContentDto;
+use App\Model\Settings\Queries\SettingStringValueQuery;
+use App\Model\Settings\Settings;
use App\Model\User\Commands\RegisterTroop;
use App\Model\User\Queries\TroopByLeaderQuery;
use App\Model\User\Repositories\UserRepository;
@@ -68,6 +70,8 @@ public function render(?ContentDto $content = null): void
$template->guestRole = $user->isInRole($this->roleRepository->findBySystemName(Role::GUEST)->getName());
$template->testRole = Role::TEST;
+ $this->template->accountNumber = $this->queryBus->handle(new SettingStringValueQuery(Settings::ACCOUNT_NUMBER));
+
$step = $this->getPresenter()->getParameter('step');
$template->step = $step;
diff --git a/app/WebModule/Components/templates/troop_application_content.latte b/app/WebModule/Components/templates/troop_application_content.latte
index d990c7005..f9ec886fa 100644
--- a/app/WebModule/Components/templates/troop_application_content.latte
+++ b/app/WebModule/Components/templates/troop_application_content.latte
@@ -29,7 +29,15 @@
- Přihláška tvé skupiny byla úspěšně odeslána. Nyní můžeš pouze měnit osobu za osobu.
+
+ Přihláška tvé skupiny byla úspěšně odeslána. Nyní můžeš pouze měnit osobu za osobu.
+
+
+ Abychom s vámi mohli počítat je nutné nejpozději do 30 kalendářních dnů, nejpozději však do 15.
+ února 2023 (pokud by nastalo dříve) uhradit {$troop->getFee()} Kč účastnického
+ poplatku za celou skupinu na účet: {$accountNumber} s variabilním symbolem:
+ {$troop->getVariableSymbol()->getVariableSymbol()}.
+
@@ -104,8 +112,7 @@
Družiny
diff --git a/app/WebModule/Forms/templates/troop_confirm_form.latte b/app/WebModule/Forms/templates/troop_confirm_form.latte
index 347e432fd..900238e64 100644
--- a/app/WebModule/Forms/templates/troop_confirm_form.latte
+++ b/app/WebModule/Forms/templates/troop_confirm_form.latte
@@ -458,15 +458,16 @@
-
-
- {input agreement}
+
-
-
-
- {input submit class => 'btn-primary button'}
+
+
+ {input submit class => 'btn-primary button'}
+
{/form}
From ceb74ac2c33ade28ab87ad6bfee17412f19f7f90 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Tue, 8 Nov 2022 23:34:51 +0100
Subject: [PATCH 14/41] =?UTF-8?q?Nepovolen=C3=AD=20kombinace=20doprovodu?=
=?UTF-8?q?=20a=20vedouc=C3=ADho=20dru=C5=BEiny=20(#904)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* user and escort conflict
* cs fix
---
app/WebModule/Forms/TroopConfirmForm.php | 15 +++++++++++----
.../Forms/templates/troop_confirm_form.latte | 7 +++++++
2 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/app/WebModule/Forms/TroopConfirmForm.php b/app/WebModule/Forms/TroopConfirmForm.php
index dd835222f..6d3d3da35 100644
--- a/app/WebModule/Forms/TroopConfirmForm.php
+++ b/app/WebModule/Forms/TroopConfirmForm.php
@@ -32,6 +32,7 @@ class TroopConfirmForm extends UI\Control
private bool $allCountError;
private bool $duplicitUsersError;
private bool $userNotLeaderError;
+ private bool $userLeaderAndEscortError;
/**
* Událost při úspěšném odeslání formuláře.
@@ -59,9 +60,10 @@ public function render(): void
$this->template->troop = $this->troop;
$this->template->agreement = $this->queryBus->handle(new SettingStringValueQuery(Settings::APPLICATION_AGREEMENT));
- $this->template->allCountError = $this->allCountError;
- $this->template->duplicitUsersError = $this->duplicitUsersError;
- $this->template->userNotLeaderError = $this->userNotLeaderError;
+ $this->template->allCountError = $this->allCountError;
+ $this->template->duplicitUsersError = $this->duplicitUsersError;
+ $this->template->userNotLeaderError = $this->userNotLeaderError;
+ $this->template->userLeaderAndEscortError = $this->userLeaderAndEscortError;
$this->template->render();
}
@@ -82,7 +84,7 @@ public function createComponentForm(): Form
->addRule(Form::FILLED, 'Musíš souhlasit s podmínkami akce.');
$submit = $form->addSubmit('submit', 'Závazně registrovat')
- ->setDisabled($this->allCountError || $this->duplicitUsersError || $this->userNotLeaderError);
+ ->setDisabled($this->allCountError || $this->duplicitUsersError || $this->userNotLeaderError || $this->userLeaderAndEscortError);
if ($this->troop->getState() !== TroopApplicationState::DRAFT) {
$pairedTroopCodeText->setHtmlAttribute('readonly');
@@ -130,5 +132,10 @@ private function resolveTroop(): void
$this->userNotLeaderError = $user->getGroupRoles()
->filter(static fn (UserGroupRole $groupRole) => $groupRole->getRole()->getSystemName() === Role::LEADER && $groupRole->getPatrol()->isConfirmed())
->count() === 0;
+
+ $leadersCount = $this->troop->countUsersInRoles([Role::LEADER]);
+ $escortsCount = $this->troop->countUsersInRoles([Role::ESCORT]);
+ $leadersOrEscortsCount = $this->troop->countUsersInRoles([Role::LEADER, Role::ESCORT]);
+ $this->userLeaderAndEscortError = $leadersCount + $escortsCount !== $leadersOrEscortsCount;
}
}
diff --git a/app/WebModule/Forms/templates/troop_confirm_form.latte b/app/WebModule/Forms/templates/troop_confirm_form.latte
index 900238e64..7667a6a8c 100644
--- a/app/WebModule/Forms/templates/troop_confirm_form.latte
+++ b/app/WebModule/Forms/templates/troop_confirm_form.latte
@@ -42,6 +42,13 @@
Registrující uživatel musí být vedoucím některé družiny.
+
+
+
+
+ Vedoucí družiny nesmí být zároveň doprovod.
+
+
From f8af1367b14ca52dd904f57b3b5fdbde0b081377 Mon Sep 17 00:00:00 2001
From: bojovyletoun <662941+bojovyletoun@users.noreply.github.com>
Date: Thu, 10 Nov 2022 08:23:09 +0100
Subject: [PATCH 15/41] =?UTF-8?q?oprava=20po=C4=8Dtu=20dosp=C4=9Bl=C3=BDch?=
=?UTF-8?q?=20(#906)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: bojovyletoun <>
---
app/AdminModule/Components/GroupsGridControl.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/AdminModule/Components/GroupsGridControl.php b/app/AdminModule/Components/GroupsGridControl.php
index 21ea1fae4..9759f2e4e 100644
--- a/app/AdminModule/Components/GroupsGridControl.php
+++ b/app/AdminModule/Components/GroupsGridControl.php
@@ -139,7 +139,7 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->addColumnText('numAdults', '# dospělých')
// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
- ->setRenderer(static fn (Troop $p) => $p->countUsersInRoles([Role::LEADER]));
+ ->setRenderer(static fn (Troop $p) => $p->countUsersInRoles([Role::LEADER, Role::ESCORT]));
$grid->addColumnText('numPatrols', '# družin')
// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
From d4e7a807165df840e7db6f16e83676d7781b25a7 Mon Sep 17 00:00:00 2001
From: bojovyletoun <662941+bojovyletoun@users.noreply.github.com>
Date: Thu, 10 Nov 2022 08:23:25 +0100
Subject: [PATCH 16/41] Exporty (#903)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Exporty přes DataGrid
* lokalizace Export tlačítek a nápovědy
Co-authored-by: bojovyletoun <>
---
app/AdminModule/Components/GroupsGridControl.php | 5 +++++
app/AdminModule/Components/PatrolsGridControl.php | 5 +++++
app/AdminModule/Presenters/templates/Users/groups.latte | 4 ++++
app/AdminModule/Presenters/templates/Users/patrols.latte | 3 +++
app/lang/admin.cs_CZ.neon | 4 ++++
5 files changed, 21 insertions(+)
diff --git a/app/AdminModule/Components/GroupsGridControl.php b/app/AdminModule/Components/GroupsGridControl.php
index 9759f2e4e..a7e0f3033 100644
--- a/app/AdminModule/Components/GroupsGridControl.php
+++ b/app/AdminModule/Components/GroupsGridControl.php
@@ -23,6 +23,7 @@
use Ublaboo\DataGrid\Exception\DataGridException;
use function count;
+use function date;
/**
* Komponenta pro zobrazení datagridu družin.
@@ -66,6 +67,10 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->setItemsPerPageList([25, 50, 100, 250, 500]);
$grid->setStrictSessionFilterValues(false);
+ $stamp = date('Y-m-d H.m.s');
+ $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Skupiny ' . $stamp . 'csv');
+ $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Skupiny fi ' . $stamp . 'csv');
+
$grid->addGroupAction('Export seznamu skupin')
->onSelect[] = [$this, 'groupExportUsers'];
diff --git a/app/AdminModule/Components/PatrolsGridControl.php b/app/AdminModule/Components/PatrolsGridControl.php
index 0fc780e7c..cf73fdf18 100644
--- a/app/AdminModule/Components/PatrolsGridControl.php
+++ b/app/AdminModule/Components/PatrolsGridControl.php
@@ -22,6 +22,7 @@
use Ublaboo\DataGrid\Exception\DataGridException;
use function count;
+use function date;
/**
* Komponenta pro zobrazení datagridu družin.
@@ -65,6 +66,10 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->setItemsPerPageList([25, 50, 100, 250, 500]);
$grid->setStrictSessionFilterValues(false);
+ $stamp = date('Y-m-d H.m.s');
+ $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Druziny ' . $stamp . 'csv');
+ $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Druziny fi ' . $stamp . 'csv');
+
$grid->addGroupAction('Export seznamu družin')
->onSelect[] = [$this, 'groupExportUsers'];
diff --git a/app/AdminModule/Presenters/templates/Users/groups.latte b/app/AdminModule/Presenters/templates/Users/groups.latte
index e5384b458..6ef9b4c92 100644
--- a/app/AdminModule/Presenters/templates/Users/groups.latte
+++ b/app/AdminModule/Presenters/templates/Users/groups.latte
@@ -1,4 +1,8 @@
{block main}
+
Skupiny
+
{control groupsGrid}
{/block}
diff --git a/app/AdminModule/Presenters/templates/Users/patrols.latte b/app/AdminModule/Presenters/templates/Users/patrols.latte
index 491eca487..38663b633 100644
--- a/app/AdminModule/Presenters/templates/Users/patrols.latte
+++ b/app/AdminModule/Presenters/templates/Users/patrols.latte
@@ -1,4 +1,7 @@
{block main}
Družiny
+
{control patrolsGrid}
{/block}
diff --git a/app/lang/admin.cs_CZ.neon b/app/lang/admin.cs_CZ.neon
index da7370077..e5631a769 100644
--- a/app/lang/admin.cs_CZ.neon
+++ b/app/lang/admin.cs_CZ.neon
@@ -32,6 +32,10 @@ common:
role_option: "{0} %role% (%count% uživatelů)|{1} %role% (%count% uživatel)|[2,4] %role% (%count% uživatelé)|[5,Inf[ %role% (%count% uživatelů)"
subevent_option: "{0} %subevent% (%count% uživatelů)|{1} %subevent% (%count% uživatel)|[2,4] %subevent% (%count% uživatelé)|[5,Inf[ %subevent% (%count% uživatelů)"
+ export_note: "Exportují se všechny řádky nebo ty právě zobrazené. Sloupce viz tlačítko vpravo."
+ export_all: "Export všech"
+ export_filter: "Export zobrazených"
+
menu:
dashboard: "Úvod"
cms: "Web"
From 2d329e1ff3d1495522191ab7a5857d1e541eb7b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Sun, 13 Nov 2022 19:39:50 +0100
Subject: [PATCH 17/41] srs-dev deployment fix
---
.github/workflows/deploy-nsj-dev.yml | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/deploy-nsj-dev.yml b/.github/workflows/deploy-nsj-dev.yml
index 7fa0437a7..a3fc6c2b3 100644
--- a/.github/workflows/deploy-nsj-dev.yml
+++ b/.github/workflows/deploy-nsj-dev.yml
@@ -30,19 +30,19 @@ jobs:
CONFIG_RECAPTCHA_SITE_KEY: ${{ secrets.CONFIG_RECAPTCHA_SITE_KEY }}
CONFIG_RECAPTCHA_SECRET_KEY: ${{ secrets.CONFIG_RECAPTCHA_SECRET_KEY }}
DEPLOY_DIRECTORY: ${{ secrets.DEPLOY_DIRECTORY }}
+ DEPLOY_LEBEDA: ${{ secrets.DEPLOY_LEBEDA }}
DEPLOY_SSH_HOST: ${{ secrets.DEPLOY_SSH_HOST }}
DEPLOY_SSH_IP: ${{ secrets.DEPLOY_SSH_IP }}
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
DEPLOY_SSH_PORT: ${{ secrets.DEPLOY_SSH_PORT }}
DEPLOY_SSH_USERNAME: ${{ secrets.DEPLOY_SSH_USERNAME }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
# Copy & paste from https://github.com/actions/cache/blob/master/examples.md#php---composer
- name: Get composer cache
id: composer-cache
- run: |
- echo "::set-output name=dir::$(composer config cache-files-dir)"
- - uses: actions/cache@v1
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+ - uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
@@ -55,12 +55,11 @@ jobs:
npm install --global yarn
#Copy & paste from https://github.com/actions/cache/blob/master/examples.md#node---yarn
- name: Get yarn cache
- id: yarn-cache-dir-path
- run: echo "::set-output name=dir::$(yarn cache dir)"
- - uses: actions/cache@v1
id: yarn-cache
+ run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
+ - uses: actions/cache@v3
with:
- path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
+ path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
From 605dd09dd290e156e1ab2f3286032ef430fcd2fb Mon Sep 17 00:00:00 2001
From: bojovyletoun <662941+bojovyletoun@users.noreply.github.com>
Date: Sun, 27 Nov 2022 09:25:49 +0100
Subject: [PATCH 18/41] =?UTF-8?q?Exporty=20(upraven=C3=BD=20form=C3=A1t=20?=
=?UTF-8?q?data)=20(#907)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Opravy registračního formuláře (#893)
* fee fix
* min/max age warning fixed
* allow role change with registered patrols
* group name in headline
* agreement text
* reformat html
* confirmed application warning
* confirm email
* phpstan
* headline changed
* quotes removed
* database dump detailed error
* skip db backup
Co-authored-by: Jan Staněk
* date format fix
* datetime->date
windows problem s znakem :
Co-authored-by: Jan Staněk
Co-authored-by: Jan Staněk
Co-authored-by: bojovyletoun <>
---
app/AdminModule/Components/GroupsGridControl.php | 6 +++---
app/AdminModule/Components/PatrolsGridControl.php | 6 +++---
.../Components/templates/troop_application_content.latte | 4 ++--
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/app/AdminModule/Components/GroupsGridControl.php b/app/AdminModule/Components/GroupsGridControl.php
index a7e0f3033..af866c1fb 100644
--- a/app/AdminModule/Components/GroupsGridControl.php
+++ b/app/AdminModule/Components/GroupsGridControl.php
@@ -67,9 +67,9 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->setItemsPerPageList([25, 50, 100, 250, 500]);
$grid->setStrictSessionFilterValues(false);
- $stamp = date('Y-m-d H.m.s');
- $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Skupiny ' . $stamp . 'csv');
- $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Skupiny fi ' . $stamp . 'csv');
+ $stamp = date(Helpers::DATE_FORMAT);
+ $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Skupiny ' . $stamp . '.csv');
+ $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Skupiny fi ' . $stamp . '.csv');
$grid->addGroupAction('Export seznamu skupin')
->onSelect[] = [$this, 'groupExportUsers'];
diff --git a/app/AdminModule/Components/PatrolsGridControl.php b/app/AdminModule/Components/PatrolsGridControl.php
index cf73fdf18..9da51ebac 100644
--- a/app/AdminModule/Components/PatrolsGridControl.php
+++ b/app/AdminModule/Components/PatrolsGridControl.php
@@ -66,9 +66,9 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->setItemsPerPageList([25, 50, 100, 250, 500]);
$grid->setStrictSessionFilterValues(false);
- $stamp = date('Y-m-d H.m.s');
- $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Druziny ' . $stamp . 'csv');
- $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Druziny fi ' . $stamp . 'csv');
+ $stamp = date(Helpers::DATE_FORMAT);
+ $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Druziny ' . $stamp . '.csv');
+ $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Druziny fi ' . $stamp . '.csv');
$grid->addGroupAction('Export seznamu družin')
->onSelect[] = [$this, 'groupExportUsers'];
diff --git a/app/WebModule/Components/templates/troop_application_content.latte b/app/WebModule/Components/templates/troop_application_content.latte
index f9ec886fa..988c041e2 100644
--- a/app/WebModule/Components/templates/troop_application_content.latte
+++ b/app/WebModule/Components/templates/troop_application_content.latte
@@ -17,8 +17,8 @@
{elseif empty($skautIsRoles)}
- Ve skautISu nemáš dostatečné oprávnění. Registrace vyžaduje přístup k informacím
- o členech (typicky admin oddílu/střediska).
+ Ve skautISu nemáš dostatečné oprávnění. Registrace vyžaduje přístup k informacím
+ o členech (typicky admin oddílu/střediska).
{elseif $step === 'members'}
{control groupMembersForm}
{elseif $step === 'additional_info'}
From 21a7b7e47e368410c387ce3737b7dad2df3e3d45 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Sun, 27 Nov 2022 20:57:11 +0100
Subject: [PATCH 19/41] =?UTF-8?q?P=C3=A1rov=C3=A1n=C3=AD=20plateb=20(#909)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* payment pairing
* update payment
* paired troops column
* payment confirm
* email migration
* cleanup
* cleanup
* delete fix
* cs fix
---
.../Components/GroupsGridControl.php | 2 +-
.../Components/PaymentsGridControl.php | 2 +
.../Forms/EditPaymentFormFactory.php | 29 ++--
.../Repositories/ApplicationRepository.php | 15 +--
app/Model/Enums/TroopApplicationState.php | 5 -
app/Model/Mailing/Template.php | 5 +
app/Model/Payment/Payment.php | 7 +
.../Commands/Handlers/ConfirmTroopHandler.php | 2 +-
.../User/Repositories/TroopRepository.php | 94 +++++++++++++
app/Model/User/Troop.php | 8 ++
app/Services/ApplicationService.php | 125 +++++++++++++++++-
.../templates/troop_application_content.latte | 2 +-
app/lang/admin.cs_CZ.neon | 3 +-
app/lang/common.cs_CZ.neon | 1 +
migrations/Version20221101001721.php | 2 +-
migrations/Version20221126184510.php | 30 +++++
16 files changed, 296 insertions(+), 36 deletions(-)
create mode 100644 migrations/Version20221126184510.php
diff --git a/app/AdminModule/Components/GroupsGridControl.php b/app/AdminModule/Components/GroupsGridControl.php
index af866c1fb..1aa9ce0a9 100644
--- a/app/AdminModule/Components/GroupsGridControl.php
+++ b/app/AdminModule/Components/GroupsGridControl.php
@@ -86,7 +86,7 @@ public function createComponentPatrolsGrid(string $name): DataGrid
->setFilterText();
$grid->addColumnText('variableSymbol', 'VS ')->setSortable() // je stejný jako název skupiny
- ->setRenderer(static fn ($t) => $t->getVariableSymbol()->getVariableSymbol())
+ ->setRenderer(static fn ($t) => $t->getVariableSymbolText())
->setFilterText();
$grid->addColumnText('leader', 'Vedoucí')->setSortable()
diff --git a/app/AdminModule/PaymentsModule/Components/PaymentsGridControl.php b/app/AdminModule/PaymentsModule/Components/PaymentsGridControl.php
index 1a6023f56..27e5c8191 100644
--- a/app/AdminModule/PaymentsModule/Components/PaymentsGridControl.php
+++ b/app/AdminModule/PaymentsModule/Components/PaymentsGridControl.php
@@ -91,6 +91,8 @@ public function createComponentPaymentsGrid(string $name): void
$grid->addColumnText('pairedApplications', 'admin.payments.payments.paired_applications', 'pairedValidApplicationsText');
+ $grid->addColumnText('pairedTroops', 'admin.payments.payments.paired_troops', 'pairedTroopsText');
+
$grid->addColumnText('state', 'admin.payments.payments.state')
->setRenderer(fn (Payment $payment) => $this->translator->translate('common.payment_state.' . $payment->getState()))
->setFilterMultiSelect($this->preparePaymentStatesOptions())
diff --git a/app/AdminModule/PaymentsModule/Forms/EditPaymentFormFactory.php b/app/AdminModule/PaymentsModule/Forms/EditPaymentFormFactory.php
index ef66e3721..9f6ea7182 100644
--- a/app/AdminModule/PaymentsModule/Forms/EditPaymentFormFactory.php
+++ b/app/AdminModule/PaymentsModule/Forms/EditPaymentFormFactory.php
@@ -8,6 +8,7 @@
use App\Model\Application\Repositories\ApplicationRepository;
use App\Model\Payment\Payment;
use App\Model\Payment\Repositories\PaymentRepository;
+use App\Model\User\Repositories\TroopRepository;
use App\Model\User\Repositories\UserRepository;
use App\Services\ApplicationService;
use Nette;
@@ -33,6 +34,7 @@ public function __construct(
private PaymentRepository $paymentRepository,
private ApplicationRepository $applicationRepository,
private UserRepository $userRepository,
+ private TroopRepository $troopRepository,
private ApplicationService $applicationService
) {
}
@@ -44,6 +46,9 @@ public function create(int $id): Form
{
$this->payment = $this->paymentRepository->findById($id);
+ $pairedValidApplications = $this->payment->getPairedValidApplications();
+ $pairedTroops = $this->payment->getPairedTroops();
+
$form = $this->baseFormFactory->create();
$form->addHidden('id');
@@ -55,7 +60,19 @@ public function create(int $id): Form
$inputVariableSymbol = $form->addText('variableSymbol', 'admin.payments.payments.variable_symbol');
- $inputPairedApplication = $form->addMultiSelect('pairedApplications', 'admin.payments.payments.paired_applications', $this->applicationRepository->getApplicationsVariableSymbolsOptions())
+ $form->addMultiSelect(
+ 'pairedApplications',
+ 'admin.payments.payments.paired_applications',
+ $this->applicationRepository->getWaitingForPaymentOrPairedApplicationsVariableSymbolsOptions($pairedValidApplications)
+ )
+ ->setHtmlAttribute('class', 'datagrid-multiselect')
+ ->setHtmlAttribute('data-live-search', 'true');
+
+ $form->addMultiSelect(
+ 'pairedTroops',
+ 'admin.payments.payments.paired_troops',
+ $this->troopRepository->getWaitingForPaymentOrPairedTroopsVariableSymbolsOptions($pairedTroops)
+ )
->setHtmlAttribute('class', 'datagrid-multiselect')
->setHtmlAttribute('data-live-search', 'true');
@@ -81,18 +98,13 @@ public function create(int $id): Form
$inputVariableSymbol->setDisabled();
}
- $pairedValidApplications = $this->payment->getPairedValidApplications();
-
- $inputPairedApplication->setItems(
- $this->applicationRepository->getWaitingForPaymentOrPairedApplicationsVariableSymbolsOptions($pairedValidApplications)
- );
-
$form->setDefaults([
'id' => $id,
'date' => $this->payment->getDate(),
'amount' => $this->payment->getAmount(),
'variableSymbol' => $this->payment->getVariableSymbol(),
'pairedApplications' => $this->applicationRepository->findApplicationsIds($pairedValidApplications),
+ 'pairedTroops' => $this->troopRepository->findTroopsIds($pairedTroops),
]);
$form->onSuccess[] = [$this, 'processForm'];
@@ -111,8 +123,9 @@ public function processForm(Form $form, stdClass $values): void
$loggedUser = $this->userRepository->findById($form->getPresenter()->user->id);
$pairedApplications = $this->applicationRepository->findApplicationsByIds($values->pairedApplications);
+ $pairedTroops = $this->troopRepository->findTroopsByIds($values->pairedTroops);
- $this->applicationService->updatePayment($this->payment, $values->date, $values->amount, $values->variableSymbol, $pairedApplications, $loggedUser);
+ $this->applicationService->updatePayment($this->payment, $values->date, $values->amount, $values->variableSymbol, $pairedApplications, $pairedTroops, $loggedUser);
}
}
}
diff --git a/app/Model/Application/Repositories/ApplicationRepository.php b/app/Model/Application/Repositories/ApplicationRepository.php
index 0ef2e8daa..7f57859c9 100644
--- a/app/Model/Application/Repositories/ApplicationRepository.php
+++ b/app/Model/Application/Repositories/ApplicationRepository.php
@@ -135,19 +135,6 @@ public function findWaitingForPaymentOrPairedApplications(Collection $pairedAppl
return $this->getRepository()->matching($criteria);
}
- /**
- * @return string[]
- */
- public function getApplicationsVariableSymbolsOptions(): array
- {
- $options = [];
- foreach ($this->findValid() as $application) {
- $options[$application->getId()] = $application->getUser()->getLastName() . ' ' . $application->getUser()->getFirstName() . ' (' . $application->getVariableSymbolText() . ' - ' . $application->getFee() . ')';
- }
-
- return $options;
- }
-
/**
* @param Collection $pairedApplications
*
@@ -157,7 +144,7 @@ public function getWaitingForPaymentOrPairedApplicationsVariableSymbolsOptions(C
{
$options = [];
foreach ($this->findWaitingForPaymentOrPairedApplications($pairedApplications) as $application) {
- $options[$application->getId()] = $application->getUser()->getLastName() . ' ' . $application->getUser()->getFirstName() . ' (' . $application->getVariableSymbolText() . ' - ' . $application->getFee() . ')';
+ $options[$application->getId()] = $application->getUser()->getLastName() . ' ' . $application->getUser()->getFirstName() . ' (' . $application->getVariableSymbolText() . ' - ' . $application->getFee() . ' Kč)';
}
return $options;
diff --git a/app/Model/Enums/TroopApplicationState.php b/app/Model/Enums/TroopApplicationState.php
index dea0648a9..9e52aa375 100644
--- a/app/Model/Enums/TroopApplicationState.php
+++ b/app/Model/Enums/TroopApplicationState.php
@@ -21,11 +21,6 @@ class TroopApplicationState
*/
public const CANCELED_NOT_PAID = 'canceled_not_paid';
- /**
- * Zrušeno.
- */
- public const CANCELED = 'canceled';
-
/**
* Zaplaceno.
*/
diff --git a/app/Model/Mailing/Template.php b/app/Model/Mailing/Template.php
index 49376ac0c..16e1e7e25 100644
--- a/app/Model/Mailing/Template.php
+++ b/app/Model/Mailing/Template.php
@@ -60,6 +60,11 @@ class Template
*/
public const PAYMENT_CONFIRMED = 'payment_confirmed';
+ /**
+ * Potvrzení přijetí platby skupiny.
+ */
+ public const TROOP_PAYMENT_CONFIRMED = 'troop_payment_confirmed';
+
/**
* Upozornění na splatnost.
*/
diff --git a/app/Model/Payment/Payment.php b/app/Model/Payment/Payment.php
index 3ef440ca8..0070fbb4f 100644
--- a/app/Model/Payment/Payment.php
+++ b/app/Model/Payment/Payment.php
@@ -239,6 +239,13 @@ public function getPairedTroops(): Collection
return $this->pairedTroops;
}
+ public function getPairedTroopsText(): string
+ {
+ $pairedNames = $this->getPairedTroops()->map(static fn (Troop $troop) => $troop->getName())->toArray();
+
+ return implode(', ', $pairedNames);
+ }
+
public function getState(): string
{
return $this->state;
diff --git a/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php b/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php
index 1a7dc9c40..854d39b3c 100644
--- a/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php
+++ b/app/Model/User/Commands/Handlers/ConfirmTroopHandler.php
@@ -40,7 +40,7 @@ public function __invoke(ConfirmTroop $command): void
$this->mailService->sendMailFromTemplate(new ArrayCollection([$troop->getLeader()]), null, Template::TROOP_REGISTRATION, [
TemplateVariable::SEMINAR_NAME => $this->queryBus->handle(new SettingStringValueQuery(Settings::SEMINAR_NAME)),
TemplateVariable::APPLICATION_FEE => (string) $troop->getFee(),
- TemplateVariable::APPLICATION_VARIABLE_SYMBOL => $troop->getVariableSymbol()->getVariableSymbol(),
+ TemplateVariable::APPLICATION_VARIABLE_SYMBOL => $troop->getVariableSymbolText(),
TemplateVariable::APPLICATION_MATURITY => $troop->getMaturityDateText(),
TemplateVariable::BANK_ACCOUNT => $this->queryBus->handle(
new SettingStringValueQuery(Settings::ACCOUNT_NUMBER)
diff --git a/app/Model/User/Repositories/TroopRepository.php b/app/Model/User/Repositories/TroopRepository.php
index 7f716245f..25fdc84e4 100644
--- a/app/Model/User/Repositories/TroopRepository.php
+++ b/app/Model/User/Repositories/TroopRepository.php
@@ -4,9 +4,16 @@
namespace App\Model\User\Repositories;
+use App\Model\Enums\TroopApplicationState;
use App\Model\Infrastructure\Repositories\AbstractRepository;
use App\Model\User\Troop;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
+use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\NonUniqueResultException;
+
+use function array_map;
/**
* Třída spravující oddíly.
@@ -23,6 +30,19 @@ public function findById(int $id): Troop
return $this->getRepository()->findOneBy(['id' => $id]);
}
+ /**
+ * @return Collection
+ */
+ public function findAll(): Collection
+ {
+ $result = $this->getRepository()->findAll();
+
+ return new ArrayCollection($result);
+ }
+
+ /**
+ * @throws NonUniqueResultException
+ */
public function findByLeaderId(int $leaderId): ?Troop
{
return $this->getRepository()
@@ -33,6 +53,80 @@ public function findByLeaderId(int $leaderId): ?Troop
->getOneOrNullResult();
}
+ /**
+ * @throws NonUniqueResultException
+ */
+ public function findByVariableSymbol(?string $variableSymbol): ?Troop
+ {
+ $variableSymbolRegex = '^0*' . $variableSymbol . '$';
+
+ return $this->createQueryBuilder('t')
+ ->select('t')
+ ->join('t.variableSymbol', 'v')
+ ->where('REGEXP(v.variableSymbol, :variableSymbol) = 1')->setParameter('variableSymbol', $variableSymbolRegex)
+ ->getQuery()
+ ->getOneOrNullResult();
+ }
+
+ /**
+ * Vrací skupiny podle id.
+ *
+ * @param int[] $ids
+ *
+ * @return Collection
+ */
+ public function findTroopsByIds(array $ids): Collection
+ {
+ $criteria = Criteria::create()
+ ->where(Criteria::expr()->in('id', $ids));
+
+ return $this->getRepository()->matching($criteria);
+ }
+
+ /**
+ * Vrací id skupin.
+ *
+ * @param Collection $troops
+ *
+ * @return int[]
+ */
+ public function findTroopsIds(Collection $troops): array
+ {
+ return array_map(static fn (Troop $t) => $t->getId(), $troops->toArray());
+ }
+
+ /**
+ * @param Collection $pairedTroops
+ *
+ * @return Collection
+ */
+ public function findWaitingForPaymentOrPairedTroops(Collection $pairedTroops): Collection
+ {
+ $criteria = Criteria::create()
+ ->where(Criteria::expr()->orX(
+ Criteria::expr()->eq('state', TroopApplicationState::WAITING_FOR_PAYMENT),
+ Criteria::expr()->in('id', $pairedTroops->map(static fn (Troop $troop) => $troop->getId())
+ ->toArray())
+ ));
+
+ return $this->getRepository()->matching($criteria);
+ }
+
+ /**
+ * @param Collection $pairedTroops
+ *
+ * @return string[]
+ */
+ public function getWaitingForPaymentOrPairedTroopsVariableSymbolsOptions(Collection $pairedTroops): array
+ {
+ $options = [];
+ foreach ($this->findWaitingForPaymentOrPairedTroops($pairedTroops) as $troop) {
+ $options[$troop->getId()] = $troop->getName() . ' (' . $troop->getVariableSymbolText() . ' - ' . $troop->getFee() . ' Kč)';
+ }
+
+ return $options;
+ }
+
public function save(Troop $troop): void
{
$this->em->persist($troop);
diff --git a/app/Model/User/Troop.php b/app/Model/User/Troop.php
index 57544f9ea..ff5368b56 100644
--- a/app/Model/User/Troop.php
+++ b/app/Model/User/Troop.php
@@ -188,6 +188,14 @@ public function getVariableSymbol(): VariableSymbol
return $this->variableSymbol;
}
+ /**
+ * Vrací text variabilního symbolu.
+ */
+ public function getVariableSymbolText(): string
+ {
+ return $this->variableSymbol->getVariableSymbol();
+ }
+
public function getApplicationDate(): ?DateTimeImmutable
{
return $this->applicationDate;
diff --git a/app/Services/ApplicationService.php b/app/Services/ApplicationService.php
index ae6d41f56..a4fd8529f 100644
--- a/app/Services/ApplicationService.php
+++ b/app/Services/ApplicationService.php
@@ -19,6 +19,7 @@
use App\Model\Enums\MaturityType;
use App\Model\Enums\PaymentState;
use App\Model\Enums\PaymentType;
+use App\Model\Enums\TroopApplicationState;
use App\Model\Mailing\Template;
use App\Model\Mailing\TemplateVariable;
use App\Model\Payment\Payment;
@@ -32,7 +33,9 @@
use App\Model\Settings\Settings;
use App\Model\Structure\Repositories\SubeventRepository;
use App\Model\Structure\Subevent;
+use App\Model\User\Repositories\TroopRepository;
use App\Model\User\Repositories\UserRepository;
+use App\Model\User\Troop;
use App\Model\User\User;
use App\Utils\Helpers;
use DateTimeImmutable;
@@ -78,7 +81,8 @@ public function __construct(
private Translator $translator,
private PaymentRepository $paymentRepository,
private IncomeProofRepository $incomeProofRepository,
- private EventBus $eventBus
+ private EventBus $eventBus,
+ private TroopRepository $troopRepository,
) {
}
@@ -536,6 +540,52 @@ public function updateApplicationPayment(
}
}
+ /**
+ * Aktualizuje stav platby přihlášky oddílu.
+ *
+ * @throws Throwable
+ */
+ public function updateTroopApplicationPayment(
+ Troop $troop,
+ ?string $paymentMethod,
+ ?DateTimeImmutable $paymentDate,
+ ?DateTimeImmutable $maturityDate
+ ): void {
+ $oldPaymentMethod = $troop->getPaymentMethod();
+ $oldPaymentDate = $troop->getPaymentDate();
+ $oldMaturityDate = $troop->getMaturityDate();
+
+ // pokud neni zmena, nic se neprovede
+ if ($paymentMethod === $oldPaymentMethod && $paymentDate == $oldPaymentDate && $maturityDate == $oldMaturityDate) {
+ return;
+ }
+
+ $this->em->wrapInTransaction(function () use ($troop, $paymentMethod, $paymentDate, $maturityDate): void {
+ $troop->setPaymentMethod($paymentMethod);
+ $troop->setPaymentDate($paymentDate);
+ $troop->setMaturityDate($maturityDate);
+ $troop->setState($this->getTroopApplicationState($troop));
+ $this->troopRepository->save($troop);
+ });
+
+ if ($paymentDate !== null && $oldPaymentDate === null) {
+ $this->mailService->sendMailFromTemplate(
+ new ArrayCollection(
+ [
+ $troop->getLeader(),
+ ]
+ ),
+ null,
+ Template::TROOP_PAYMENT_CONFIRMED,
+ [
+ TemplateVariable::SEMINAR_NAME => $this->queryBus->handle(
+ new SettingStringValueQuery(Settings::SEMINAR_NAME)
+ ),
+ ]
+ );
+ }
+ }
+
/**
* Vytvoří platbu a označí spárované přihlášky jako zaplacené.
*
@@ -583,7 +633,27 @@ public function createPayment(
$this->updateApplicationPayment($pairedApplication, PaymentType::BANK, $date, $pairedApplication->getMaturityDate(), $createdBy);
}
} else {
- $payment->setState(PaymentState::NOT_PAIRED_VS);
+ $pairedTroopApplication = $this->troopRepository->findByVariableSymbol($variableSymbol);
+
+ if ($pairedTroopApplication) {
+ if (
+ $pairedTroopApplication->getState() === TroopApplicationState::PAID
+ ) {
+ $payment->setState(PaymentState::NOT_PAIRED_PAID);
+ } elseif (
+ $pairedTroopApplication->getState() === TroopApplicationState::CANCELED_NOT_PAID
+ ) {
+ $payment->setState(PaymentState::NOT_PAIRED_CANCELED);
+ } elseif (abs($pairedTroopApplication->getFee() - $amount) >= 0.01) {
+ $payment->setState(PaymentState::NOT_PAIRED_FEE);
+ } else {
+ $payment->setState(PaymentState::PAIRED_AUTO);
+ $pairedTroopApplication->setPayment($payment);
+ $this->updateTroopApplicationPayment($pairedTroopApplication, PaymentType::BANK, $date, $pairedTroopApplication->getMaturityDate());
+ }
+ } else {
+ $payment->setState(PaymentState::NOT_PAIRED_VS);
+ }
}
$this->paymentRepository->save($payment);
@@ -608,6 +678,7 @@ public function createPaymentManual(
* Aktualizuje platbu a stav spárovaných přihlášek.
*
* @param Collection $pairedApplications
+ * @param Collection $pairedTroops
*
* @throws Throwable
*/
@@ -617,9 +688,10 @@ public function updatePayment(
?float $amount,
?string $variableSymbol,
Collection $pairedApplications,
+ Collection $pairedTroops,
User $createdBy
): void {
- $this->em->wrapInTransaction(function () use ($payment, $date, $amount, $variableSymbol, $pairedApplications, $createdBy): void {
+ $this->em->wrapInTransaction(function () use ($payment, $date, $amount, $variableSymbol, $pairedApplications, $pairedTroops, $createdBy): void {
if ($date !== null) {
$payment->setDate($date);
}
@@ -653,8 +725,27 @@ public function updatePayment(
}
}
+ $oldPairedTroops = clone $payment->getPairedTroops();
+ $newPairedTroops = clone $pairedTroops;
+
+ foreach ($oldPairedTroops as $pairedTroop) {
+ if (! $newPairedTroops->contains($pairedTroop)) {
+ $pairedTroop->setPayment(null);
+ $this->updateTroopApplicationPayment($pairedTroop, null, null, $pairedTroop->getMaturityDate());
+ $pairedApplicationsModified = true;
+ }
+ }
+
+ foreach ($newPairedTroops as $pairedTroop) {
+ if (! $oldPairedTroops->contains($pairedTroop)) {
+ $pairedTroop->setPayment($payment);
+ $this->updateTroopApplicationPayment($pairedTroop, PaymentType::BANK, $payment->getDate(), $pairedTroop->getMaturityDate());
+ $pairedApplicationsModified = true;
+ }
+ }
+
if ($pairedApplicationsModified) {
- if ($pairedApplications->isEmpty()) {
+ if ($pairedApplications->isEmpty() && $pairedTroops->isEmpty()) {
$payment->setState(PaymentState::NOT_PAIRED);
} else {
$payment->setState(PaymentState::PAIRED_MANUAL);
@@ -677,6 +768,12 @@ public function removePayment(Payment $payment, User $createdBy): void
$this->updateApplicationPayment($pairedApplication, null, null, $pairedApplication->getMaturityDate(), $createdBy);
}
+ foreach ($payment->getPairedTroops() as $pairedTroop) {
+ $this->updateTroopApplicationPayment($pairedTroop, null, null, $pairedTroop->getMaturityDate());
+ $pairedTroop->setPayment(null);
+ $this->troopRepository->save($pairedTroop);
+ }
+
$this->paymentRepository->remove($payment);
});
}
@@ -999,6 +1096,26 @@ private function getApplicationState(Application $application): string
return ApplicationState::WAITING_FOR_PAYMENT;
}
+ /**
+ * Určí stav přihlášky skupiny.
+ */
+ private function getTroopApplicationState(Troop $troop): string
+ {
+ if ($troop->getState() === TroopApplicationState::DRAFT) {
+ return TroopApplicationState::DRAFT;
+ }
+
+ if ($troop->getState() === TroopApplicationState::CANCELED_NOT_PAID) {
+ return TroopApplicationState::CANCELED_NOT_PAID;
+ }
+
+ if ($troop->getPaymentDate()) {
+ return TroopApplicationState::PAID;
+ }
+
+ return TroopApplicationState::WAITING_FOR_PAYMENT;
+ }
+
/**
* Zvýší obsazenost rolí.
*
diff --git a/app/WebModule/Components/templates/troop_application_content.latte b/app/WebModule/Components/templates/troop_application_content.latte
index 988c041e2..7688c8c5f 100644
--- a/app/WebModule/Components/templates/troop_application_content.latte
+++ b/app/WebModule/Components/templates/troop_application_content.latte
@@ -36,7 +36,7 @@
Abychom s vámi mohli počítat je nutné nejpozději do 30 kalendářních dnů, nejpozději však do 15.
února 2023 (pokud by nastalo dříve) uhradit {$troop->getFee()} Kč účastnického
poplatku za celou skupinu na účet: {$accountNumber} s variabilním symbolem:
- {$troop->getVariableSymbol()->getVariableSymbol()}.
+ {$troop->getVariableSymbolText()}.
diff --git a/app/lang/admin.cs_CZ.neon b/app/lang/admin.cs_CZ.neon
index e5631a769..9a1a3a944 100644
--- a/app/lang/admin.cs_CZ.neon
+++ b/app/lang/admin.cs_CZ.neon
@@ -528,7 +528,8 @@ payments:
account_number: "Číslo protiúčtu"
account_name: "Název protiúčtu"
message: "Zpráva pro příjemce"
- paired_applications: "Spárované přihlášky"
+ paired_applications: "Spárované osoby"
+ paired_troops: "Spárované skupiny"
state: "Stav"
saved: "Platba byla úspěšně uložena."
delete_confirm: "Opravdu chcete platbu odstranit?"
diff --git a/app/lang/common.cs_CZ.neon b/app/lang/common.cs_CZ.neon
index 9364537c2..3b4a77a88 100644
--- a/app/lang/common.cs_CZ.neon
+++ b/app/lang/common.cs_CZ.neon
@@ -195,6 +195,7 @@ mailing:
roles_changed: "Potvrzení změny rolí"
subevents_changed: "Potvrzení změny podakcí"
payment_confirmed: "Potvrzení přijetí platby"
+ troop_payment_confirmed: "Potvrzení přijetí platby skupiny"
maturity_reminder: "Připomínka splatnosti"
program_registered: "Přihlášení na program"
program_unregistered: "Odhlášení z programu"
diff --git a/migrations/Version20221101001721.php b/migrations/Version20221101001721.php
index 3c13ba5ee..692bd520e 100644
--- a/migrations/Version20221101001721.php
+++ b/migrations/Version20221101001721.php
@@ -19,7 +19,7 @@ public function getDescription(): string
public function up(Schema $schema): void
{
- $this->addSql('INSERT INTO `mail_template` (`id`, `type`, `subject`, `text`, `active`, `system_template`) VALUES (NULL, \'troop_registration\', \'Registrace na akci NSJ2023!\', \'Ahoj, děkujeme za registraci na NSJ2023. Tento e-mail je potvrzením, že registrace tvé skupiny byla řádně uložena do systému. Abychom s vámi mohli počítat je nutné nejpozději do 30 kalendářních dnů, nejpozději však do 15. února 2023 (pokud by nastalo dříve) uhradit %poplatek% Kč účastnického poplatku za celou skupinu na účet: %cislo-uctu% s variabilním symbolem: %variabilni-symbol%. S platbou prosím neotálej. Svou skupinu můžeš spravovat po přihlášení na nsj2023.cz. Pokud by se vyskytly jakékoliv potíže, ozvi se nám na registrace@nsj2023.cz. Těšíme se! Tým NSJ2023\', \'1\', \'0\')');
+ $this->addSql('INSERT INTO `mail_template` (`id`, `type`, `subject`, `text`, `active`, `system_template`) VALUES (15, \'troop_registration\', \'Registrace na akci NSJ2023!\', \'Ahoj, děkujeme za registraci na NSJ2023. Tento e-mail je potvrzením, že registrace tvé skupiny byla řádně uložena do systému. Abychom s vámi mohli počítat je nutné nejpozději do 30 kalendářních dnů, nejpozději však do 15. února 2023 (pokud by nastalo dříve) uhradit %poplatek% Kč účastnického poplatku za celou skupinu na účet: %cislo-uctu% s variabilním symbolem: %variabilni-symbol%. S platbou prosím neotálej. Svou skupinu můžeš spravovat po přihlášení na nsj2023.cz. Pokud by se vyskytly jakékoliv potíže, ozvi se nám na registrace@nsj2023.cz. Těšíme se! Tým NSJ2023\', \'1\', \'0\')');
$this->addSql('INSERT INTO `template_template_variable` (`template_id`, `template_variable_id`) VALUES (\'15\', \'1\')');
$this->addSql('INSERT INTO `template_template_variable` (`template_id`, `template_variable_id`) VALUES (\'15\', \'5\')');
diff --git a/migrations/Version20221126184510.php b/migrations/Version20221126184510.php
new file mode 100644
index 000000000..574b6839c
--- /dev/null
+++ b/migrations/Version20221126184510.php
@@ -0,0 +1,30 @@
+addSql('INSERT INTO `mail_template` (`id`, `type`, `subject`, `text`, `active`, `system_template`) VALUES (16, \'troop_payment_confirmed\', \'Potvrzení platby akce NSJ2023!\', \'Ahoj, potvrzujeme přijetí platby za tvou skupinu na NSJ2023. Potvrzení platby si můžeš stáhnout po přihlášení.\', \'1\', \'0\')');
+
+ $this->addSql('INSERT INTO `template_template_variable` (`template_id`, `template_variable_id`) VALUES (\'16\', \'1\')');
+ }
+
+ public function down(Schema $schema): void
+ {
+ }
+}
From 9b480b2d7c7f4998c294a4c1adcc6d0e2fe90c9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Sun, 27 Nov 2022 23:00:37 +0100
Subject: [PATCH 20/41] =?UTF-8?q?Ru=C4=8Dn=C3=AD=20akce=20pro=20p=C3=A1rov?=
=?UTF-8?q?=C3=A1n=C3=AD=20nesp=C3=A1rovan=C3=BDch=20plateb=20(#923)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* pair not paired payments
* cs fix
---
.../Presenters/PaymentPresenter.php | 34 +++++++++
.../Repositories/PaymentRepository.php | 11 +++
app/Services/ApplicationService.php | 69 ++++++++++---------
3 files changed, 82 insertions(+), 32 deletions(-)
create mode 100644 app/ActionModule/Presenters/PaymentPresenter.php
diff --git a/app/ActionModule/Presenters/PaymentPresenter.php b/app/ActionModule/Presenters/PaymentPresenter.php
new file mode 100644
index 000000000..29ea542b8
--- /dev/null
+++ b/app/ActionModule/Presenters/PaymentPresenter.php
@@ -0,0 +1,34 @@
+paymentRepository->findNotPairedVs();
+
+ foreach ($notPairedPayments as $payment) {
+ $this->applicationService->pairPayment($payment);
+ }
+
+ $response = new TextResponse(null);
+ $this->sendResponse($response);
+ }
+}
diff --git a/app/Model/Payment/Repositories/PaymentRepository.php b/app/Model/Payment/Repositories/PaymentRepository.php
index f64df21ff..2aae51c75 100644
--- a/app/Model/Payment/Repositories/PaymentRepository.php
+++ b/app/Model/Payment/Repositories/PaymentRepository.php
@@ -4,8 +4,11 @@
namespace App\Model\Payment\Repositories;
+use App\Model\Enums\PaymentState;
use App\Model\Infrastructure\Repositories\AbstractRepository;
use App\Model\Payment\Payment;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface;
/**
@@ -34,6 +37,14 @@ public function findByTransactionId(string $transactionId): ?Payment
return $this->getRepository()->findOneBy(['transactionId' => $transactionId]);
}
+ /**
+ * @return Collection
+ */
+ public function findNotPairedVs(): Collection
+ {
+ return new ArrayCollection($this->getRepository()->findBy(['state' => PaymentState::NOT_PAIRED_VS]));
+ }
+
/**
* Uloží platbu.
*/
diff --git a/app/Services/ApplicationService.php b/app/Services/ApplicationService.php
index a4fd8529f..39140af44 100644
--- a/app/Services/ApplicationService.php
+++ b/app/Services/ApplicationService.php
@@ -612,52 +612,57 @@ public function createPayment(
$payment->setAccountName($accountName);
$payment->setMessage($message);
- $pairedApplication = $this->applicationRepository->findValidByVariableSymbol($variableSymbol);
+ $this->pairPayment($payment, $createdBy);
+ });
+ }
- if ($pairedApplication) {
+ public function pairPayment(Payment $payment, ?User $createdBy = null): void
+ {
+ $pairedApplication = $this->applicationRepository->findValidByVariableSymbol($payment->getVariableSymbol());
+
+ if ($pairedApplication) {
+ if (
+ $pairedApplication->getState() === ApplicationState::PAID ||
+ $pairedApplication->getState() === ApplicationState::PAID_FREE
+ ) {
+ $payment->setState(PaymentState::NOT_PAIRED_PAID);
+ } elseif (
+ $pairedApplication->getState() === ApplicationState::CANCELED ||
+ $pairedApplication->getState() === ApplicationState::CANCELED_NOT_PAID
+ ) {
+ $payment->setState(PaymentState::NOT_PAIRED_CANCELED);
+ } elseif (abs($pairedApplication->getFee() - $payment->getAmount()) >= 0.01) {
+ $payment->setState(PaymentState::NOT_PAIRED_FEE);
+ } else {
+ $payment->setState(PaymentState::PAIRED_AUTO);
+ $pairedApplication->setPayment($payment);
+ $this->updateApplicationPayment($pairedApplication, PaymentType::BANK, $payment->getDate(), $pairedApplication->getMaturityDate(), $createdBy);
+ }
+ } else {
+ $pairedTroopApplication = $this->troopRepository->findByVariableSymbol($payment->getVariableSymbol());
+
+ if ($pairedTroopApplication) {
if (
- $pairedApplication->getState() === ApplicationState::PAID ||
- $pairedApplication->getState() === ApplicationState::PAID_FREE
+ $pairedTroopApplication->getState() === TroopApplicationState::PAID
) {
$payment->setState(PaymentState::NOT_PAIRED_PAID);
} elseif (
- $pairedApplication->getState() === ApplicationState::CANCELED ||
- $pairedApplication->getState() === ApplicationState::CANCELED_NOT_PAID
+ $pairedTroopApplication->getState() === TroopApplicationState::CANCELED_NOT_PAID
) {
$payment->setState(PaymentState::NOT_PAIRED_CANCELED);
- } elseif (abs($pairedApplication->getFee() - $amount) >= 0.01) {
+ } elseif (abs($pairedTroopApplication->getFee() - $payment->getAmount()) >= 0.01) {
$payment->setState(PaymentState::NOT_PAIRED_FEE);
} else {
$payment->setState(PaymentState::PAIRED_AUTO);
- $pairedApplication->setPayment($payment);
- $this->updateApplicationPayment($pairedApplication, PaymentType::BANK, $date, $pairedApplication->getMaturityDate(), $createdBy);
+ $pairedTroopApplication->setPayment($payment);
+ $this->updateTroopApplicationPayment($pairedTroopApplication, PaymentType::BANK, $payment->getDate(), $pairedTroopApplication->getMaturityDate());
}
} else {
- $pairedTroopApplication = $this->troopRepository->findByVariableSymbol($variableSymbol);
-
- if ($pairedTroopApplication) {
- if (
- $pairedTroopApplication->getState() === TroopApplicationState::PAID
- ) {
- $payment->setState(PaymentState::NOT_PAIRED_PAID);
- } elseif (
- $pairedTroopApplication->getState() === TroopApplicationState::CANCELED_NOT_PAID
- ) {
- $payment->setState(PaymentState::NOT_PAIRED_CANCELED);
- } elseif (abs($pairedTroopApplication->getFee() - $amount) >= 0.01) {
- $payment->setState(PaymentState::NOT_PAIRED_FEE);
- } else {
- $payment->setState(PaymentState::PAIRED_AUTO);
- $pairedTroopApplication->setPayment($payment);
- $this->updateTroopApplicationPayment($pairedTroopApplication, PaymentType::BANK, $date, $pairedTroopApplication->getMaturityDate());
- }
- } else {
- $payment->setState(PaymentState::NOT_PAIRED_VS);
- }
+ $payment->setState(PaymentState::NOT_PAIRED_VS);
}
+ }
- $this->paymentRepository->save($payment);
- });
+ $this->paymentRepository->save($payment);
}
/**
From aa2efe4f48867f7efb4692deb70d2c6e395f0f8f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Mon, 28 Nov 2022 07:40:19 +0100
Subject: [PATCH 21/41] =?UTF-8?q?Potvrzen=C3=AD=20platby=20(#922)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* troop payment proof
* cs fix
---
.../Components/GroupsGridControl.php | 17 ++-
.../Presenters/TroopIncomeProofPresenter.php | 111 ++++++++++++++++++
.../templates/TroopIncomeProof/pdf.latte | 49 ++++++++
app/Model/User/Troop.php | 13 ++
.../TroopApplicationContentControl.php | 8 ++
.../templates/troop_application_content.latte | 6 +-
6 files changed, 201 insertions(+), 3 deletions(-)
create mode 100644 app/ExportModule/Presenters/TroopIncomeProofPresenter.php
create mode 100644 app/ExportModule/Presenters/templates/TroopIncomeProof/pdf.latte
diff --git a/app/AdminModule/Components/GroupsGridControl.php b/app/AdminModule/Components/GroupsGridControl.php
index 1aa9ce0a9..c38a1c8a8 100644
--- a/app/AdminModule/Components/GroupsGridControl.php
+++ b/app/AdminModule/Components/GroupsGridControl.php
@@ -150,8 +150,11 @@ public function createComponentPatrolsGrid(string $name): DataGrid
// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
->setRenderer(static fn (Troop $p) => count($p->getConfirmedPatrols()));
- $grid->addAction('detail', 'admin.common.detail', 'Users:groupDetail') // destinace ,todo group_detail.latte
- ->setClass('btn btn-xs btn-primary');
+ $grid->addAction('generatePaymentProof', 'Doklad', 'generatePaymentProof');
+ $grid->allowRowsAction('generatePaymentProof', static fn (Troop $troop) => $troop->getPaymentDate() !== null);
+
+// $grid->addAction('detail', 'admin.common.detail', 'Users:groupDetail') // destinace ,todo group_detail.latte
+// ->setClass('btn btn-xs btn-primary');
// $grid->addAction('delete', '', 'delete!')
// ->setIcon('trash')
@@ -181,6 +184,16 @@ public function createComponentPatrolsGrid(string $name): DataGrid
// $p->redirect('this');
// }
+ /**
+ * Vygeneruje potvrzení o přijetí platby.
+ *
+ * @throws AbortException
+ */
+ public function handleGeneratePaymentProof(int $id): void
+ {
+ $this->presenter->redirect(':Export:TroopIncomeProof:troop', ['id' => $id]);
+ }
+
/**
* Hromadně vyexportuje seznam družin.
*
diff --git a/app/ExportModule/Presenters/TroopIncomeProofPresenter.php b/app/ExportModule/Presenters/TroopIncomeProofPresenter.php
new file mode 100644
index 000000000..939cc12e5
--- /dev/null
+++ b/app/ExportModule/Presenters/TroopIncomeProofPresenter.php
@@ -0,0 +1,111 @@
+user->isLoggedIn()) {
+ throw new ForbiddenRequestException();
+ }
+ }
+
+ public function actionTroop(int $id): void
+ {
+ $troops = new ArrayCollection();
+ $troop = $this->troopRepository->findById($id);
+
+ if ($troop->getState() === TroopApplicationState::PAID) {
+ $troops->add($troop);
+ }
+
+ $this->generateTroopIncomeProofs($troops);
+ }
+
+ /**
+ * @param Collection $troops
+ *
+ * @throws AbortException
+ * @throws SettingsItemNotFoundException
+ * @throws Throwable
+ * @throws NonUniqueResultException
+ */
+ private function generateTroopIncomeProofs(Collection $troops): void
+ {
+ $template = $this->createTemplate();
+ assert($template instanceof Template);
+ $template->setFile(__DIR__ . '/templates/TroopIncomeProof/pdf.latte');
+
+ $template->troops = $troops;
+ $template->logo = $this->queryBus->handle(new SettingStringValueQuery(Settings::LOGO));
+ $template->seminarName = $this->queryBus->handle(new SettingStringValueQuery(Settings::SEMINAR_NAME));
+ $template->company = $this->queryBus->handle(new SettingStringValueQuery(Settings::COMPANY));
+ $template->ico = $this->queryBus->handle(new SettingStringValueQuery(Settings::ICO));
+ $template->accountNumber = $this->queryBus->handle(new SettingStringValueQuery(Settings::ACCOUNT_NUMBER));
+ $template->accountant = $this->queryBus->handle(new SettingStringValueQuery(Settings::ACCOUNTANT));
+ $template->date = (new DateTimeImmutable())->format(Helpers::DATE_FORMAT);
+
+ $this->pdfResponse->setTemplate($template);
+
+ $this->pdfResponse->documentTitle = 'potvrzeni-platby';
+ $this->pdfResponse->pageFormat = 'A4';
+ $this->pdfResponse->getMPDF()->SetProtection(['copy', 'print', 'print-highres'], '', bin2hex(random_bytes(40)));
+
+ $this->sendResponse($this->pdfResponse);
+ }
+}
diff --git a/app/ExportModule/Presenters/templates/TroopIncomeProof/pdf.latte b/app/ExportModule/Presenters/templates/TroopIncomeProof/pdf.latte
new file mode 100644
index 000000000..bd483fafb
--- /dev/null
+++ b/app/ExportModule/Presenters/templates/TroopIncomeProof/pdf.latte
@@ -0,0 +1,49 @@
+{block content}
+ {foreach $troops as $troop}
+
+
+
{_export.income_proof.bank.heading}
+
+
+
+
+
+
+
+
+
+ {$company|breakLines}
+
+ {_export.income_proof.ico} {$ico}
+
+
+
+
+
+
+ {_export.income_proof.bank.purpose} {$seminarName}
+
+
+
+
+
+ {_export.income_proof.bank.fee} {$troop->getFee()},00 {_export.income_proof.bank.fee_unit}
+ ({_export.income_proof.bank.fee_words} {$troop->getFeeWords()} {_export.income_proof.bank.fee_unit})
+
+
+
+
+
+ {_export.income_proof.bank.name} {$troop->getLeader()->getFirstName()} {$troop->getLeader()->getLastName()}
+ ({_export.income_proof.bank.address} {$troop->getLeader()->getStreet()}, {$troop->getLeader()->getPostcode()} {$troop->getLeader()->getCity()})
+
+
+
+
+
+ {_export.income_proof.bank.accountant} {$accountant} {_export.income_proof.bank.date} {$date}
+
+
+ {sep}{/sep}
+ {/foreach}
+{/block}
\ No newline at end of file
diff --git a/app/Model/User/Troop.php b/app/Model/User/Troop.php
index ff5368b56..43e2faa37 100644
--- a/app/Model/User/Troop.php
+++ b/app/Model/User/Troop.php
@@ -13,12 +13,14 @@
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
+use Numbers_Words;
use function array_key_exists;
use function count;
use function in_array;
use function md5;
use function mt_rand;
+use function str_replace;
use function substr;
use function uniqid;
@@ -178,6 +180,17 @@ public function getFee(): int
return $this->fee;
}
+ /**
+ * Vrací poplatek slovy.
+ */
+ public function getFeeWords(): string
+ {
+ $numbersWords = new Numbers_Words();
+ $feeWord = $numbersWords->toWords($this->getFee(), 'cs');
+
+ return str_replace(' ', '', $feeWord);
+ }
+
public function setFee(int $fee): void
{
$this->fee = $fee;
diff --git a/app/WebModule/Components/TroopApplicationContentControl.php b/app/WebModule/Components/TroopApplicationContentControl.php
index 3ddafc693..d10ab7092 100644
--- a/app/WebModule/Components/TroopApplicationContentControl.php
+++ b/app/WebModule/Components/TroopApplicationContentControl.php
@@ -184,4 +184,12 @@ public function handleChangeRole(int $roleId): void
$this->skautIsService->updateUserRole($roleId);
$this->redirect('this');
}
+
+ /**
+ * Vygeneruje potvrzení o přijetí platby.
+ */
+ public function handleGeneratePaymentProof(int $id): void
+ {
+ $this->getPresenter()->redirect(':Export:TroopIncomeProof:troop', ['id' => $id]);
+ }
}
diff --git a/app/WebModule/Components/templates/troop_application_content.latte b/app/WebModule/Components/templates/troop_application_content.latte
index 7688c8c5f..f273ab25e 100644
--- a/app/WebModule/Components/templates/troop_application_content.latte
+++ b/app/WebModule/Components/templates/troop_application_content.latte
@@ -32,12 +32,16 @@
Přihláška tvé skupiny byla úspěšně odeslána. Nyní můžeš pouze měnit osobu za osobu.
-
+
Abychom s vámi mohli počítat je nutné nejpozději do 30 kalendářních dnů, nejpozději však do 15.
února 2023 (pokud by nastalo dříve) uhradit {$troop->getFee()} Kč účastnického
poplatku za celou skupinu na účet: {$accountNumber} s variabilním symbolem:
{$troop->getVariableSymbolText()}.
+
+ Platba za vaši přihlášku byla přijata.
+ Stáhnout potvrzení platby.
+
From fde5823d31b8e78c275d2ef6729dfb4ced071190 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Mon, 28 Nov 2022 12:07:56 +0100
Subject: [PATCH 22/41] =?UTF-8?q?Oprava=20doklad=C5=AF=20(#924)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* payment proofs fix
* fixes
Co-authored-by: Jan Staněk
---
app/AdminModule/Components/GroupsGridControl.php | 2 +-
.../Components/PaymentsGridControl.php | 12 ++++++++----
.../Presenters/templates/TroopIncomeProof/pdf.latte | 9 ++++++++-
3 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/app/AdminModule/Components/GroupsGridControl.php b/app/AdminModule/Components/GroupsGridControl.php
index c38a1c8a8..252f8c4c1 100644
--- a/app/AdminModule/Components/GroupsGridControl.php
+++ b/app/AdminModule/Components/GroupsGridControl.php
@@ -150,7 +150,7 @@ public function createComponentPatrolsGrid(string $name): DataGrid
// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
->setRenderer(static fn (Troop $p) => count($p->getConfirmedPatrols()));
- $grid->addAction('generatePaymentProof', 'Doklad', 'generatePaymentProof');
+ $grid->addAction('generatePaymentProof', 'Stáhnout potvzrení o přijetí platby', 'generatePaymentProof');
$grid->allowRowsAction('generatePaymentProof', static fn (Troop $troop) => $troop->getPaymentDate() !== null);
// $grid->addAction('detail', 'admin.common.detail', 'Users:groupDetail') // destinace ,todo group_detail.latte
diff --git a/app/AdminModule/PaymentsModule/Components/PaymentsGridControl.php b/app/AdminModule/PaymentsModule/Components/PaymentsGridControl.php
index 27e5c8191..b78ab0618 100644
--- a/app/AdminModule/PaymentsModule/Components/PaymentsGridControl.php
+++ b/app/AdminModule/PaymentsModule/Components/PaymentsGridControl.php
@@ -175,10 +175,14 @@ public function handleDelete(int $id): void
*/
public function handleGeneratePaymentProofBank(int $id): void
{
- $this->session->getSection('srs')->applicationIds = Helpers::getIds(
- $this->paymentRepository->findById($id)->getPairedApplications()
- );
- $this->presenter->redirect(':Export:IncomeProof:applications');
+ $payment = $this->paymentRepository->findById($id);
+
+ if (! $payment->getPairedApplications()->isEmpty()) {
+ $this->session->getSection('srs')->applicationIds = Helpers::getIds($payment->getPairedApplications());
+ $this->presenter->redirect(':Export:IncomeProof:applications');
+ } elseif (! $payment->getPairedTroops()->isEmpty()) {
+ $this->presenter->redirect(':Export:TroopIncomeProof:troop', ['id' => $payment->getPairedTroops()->get(0)->getId()]);
+ }
}
/**
diff --git a/app/ExportModule/Presenters/templates/TroopIncomeProof/pdf.latte b/app/ExportModule/Presenters/templates/TroopIncomeProof/pdf.latte
index bd483fafb..40f79d66c 100644
--- a/app/ExportModule/Presenters/templates/TroopIncomeProof/pdf.latte
+++ b/app/ExportModule/Presenters/templates/TroopIncomeProof/pdf.latte
@@ -35,7 +35,14 @@
{_export.income_proof.bank.name} {$troop->getLeader()->getFirstName()} {$troop->getLeader()->getLastName()}
- ({_export.income_proof.bank.address} {$troop->getLeader()->getStreet()}, {$troop->getLeader()->getPostcode()} {$troop->getLeader()->getCity()})
+ ({_export.income_proof.bank.address} {$troop->getLeader()->getStreet()}, {$troop->getLeader()->getPostcode()} {$troop->getLeader()->getCity()}),
+ skupina: {$troop->getName()}
+
+
+
+
+
+ Zaplaceno z účtu: {$troop->getPayment()->getAccountNumber()}, variabilní symbol: {$troop->getPayment()->getVariableSymbol()}
From 5ea7c8da18951ffa8ce9abc31827cc1339e61840 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Fri, 2 Dec 2022 12:57:11 +0100
Subject: [PATCH 23/41] limit only for attendees (#932)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Jan Staněk
---
app/WebModule/Forms/TroopConfirmForm.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/WebModule/Forms/TroopConfirmForm.php b/app/WebModule/Forms/TroopConfirmForm.php
index 6d3d3da35..585a69a2d 100644
--- a/app/WebModule/Forms/TroopConfirmForm.php
+++ b/app/WebModule/Forms/TroopConfirmForm.php
@@ -118,7 +118,7 @@ private function resolveTroop(): void
$user = $this->queryBus->handle(new UserByIdQuery($this->presenter->user->getId()));
$this->troop = $this->queryBus->handle(new TroopByLeaderQuery($user->getId()));
- $allCount = $this->troop->countUsersInRoles([Role::ATTENDEE, Role::PATROL_LEADER, Role::LEADER, Role::ESCORT]);
+ $allCount = $this->troop->countUsersInRoles([Role::ATTENDEE, Role::PATROL_LEADER]);
$this->allCountError = $allCount > 42;
$countFromPatrols = 0;
From 65bf7340e62e69e9c73b953943dae61208c8b746 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Sat, 3 Dec 2022 11:11:45 +0100
Subject: [PATCH 24/41] =?UTF-8?q?Oprava=20odstran=C4=9Bn=C3=AD=20posledn?=
=?UTF-8?q?=C3=ADho=20=C4=8Dlena=20(#934)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* remove last member fix
* dump removal
* cs fix
* cs fix
---
.../Commands/Handlers/UpdateGroupMembersHandler.php | 12 ++++++++++++
.../Components/TroopApplicationContentControl.php | 5 +++++
app/WebModule/Forms/GroupMembersForm.php | 11 +++++++++++
3 files changed, 28 insertions(+)
diff --git a/app/Model/User/Commands/Handlers/UpdateGroupMembersHandler.php b/app/Model/User/Commands/Handlers/UpdateGroupMembersHandler.php
index eb7a5d9ce..17d5ad9fb 100644
--- a/app/Model/User/Commands/Handlers/UpdateGroupMembersHandler.php
+++ b/app/Model/User/Commands/Handlers/UpdateGroupMembersHandler.php
@@ -141,6 +141,18 @@ public function __invoke(UpdateGroupMembers $command): void
$this->userGroupRoleRepository->save($userGroupRole);
}
}
+
+ if (empty($command->getPersons())) {
+ if ($command->getType() === 'patrol') {
+ foreach ($patrol->getUsersRoles() as $usersRole) {
+ $this->userGroupRoleRepository->remove($usersRole);
+ }
+ } elseif ($command->getType() === 'troop') {
+ foreach ($troop->getUsersRoles() as $usersRole) {
+ $this->userGroupRoleRepository->remove($usersRole);
+ }
+ }
+ }
});
}
}
diff --git a/app/WebModule/Components/TroopApplicationContentControl.php b/app/WebModule/Components/TroopApplicationContentControl.php
index d10ab7092..9d2482a73 100644
--- a/app/WebModule/Components/TroopApplicationContentControl.php
+++ b/app/WebModule/Components/TroopApplicationContentControl.php
@@ -129,6 +129,11 @@ protected function createComponentGroupMembersForm(): GroupMembersForm
$p->redirect('this', ['step' => 'members', 'type' => $type, 'patrol_id' => $patrolId]);
};
+ $form->onRemoveAll[] = function (): void {
+ $p = $this->getPresenter();
+ $p->redirect('this');
+ };
+
return $form;
}
diff --git a/app/WebModule/Forms/GroupMembersForm.php b/app/WebModule/Forms/GroupMembersForm.php
index 14aef8743..18d0c2261 100644
--- a/app/WebModule/Forms/GroupMembersForm.php
+++ b/app/WebModule/Forms/GroupMembersForm.php
@@ -66,6 +66,13 @@ class GroupMembersForm extends UI\Control
*/
public array $onError = [];
+ /**
+ * Událost při odstranění všech členů.
+ *
+ * @var callable[]
+ */
+ public array $onRemoveAll = [];
+
private string $patrolName = '';
/** @var Collection */
@@ -216,6 +223,10 @@ public function processForm(Form $form, stdClass $values): void
$this->commandBus->handle(new UpdateGroupMembers($this->type, $this->troop->getId(), $this->patrolId, $selectedPersons));
+ if (empty($selectedPersons)) {
+ $this->onRemoveAll();
+ }
+
$this->onSave();
}
From bb6613692fe4fa7ed9b26b8533f43941201557b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Tue, 13 Dec 2022 19:35:33 +0100
Subject: [PATCH 25/41] login fix (#937)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Jan Staněk
---
app/Services/Authenticator.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/Services/Authenticator.php b/app/Services/Authenticator.php
index cfcba4643..c0d174895 100644
--- a/app/Services/Authenticator.php
+++ b/app/Services/Authenticator.php
@@ -49,7 +49,7 @@ public function authenticate(string $user, string $password): SimpleIdentity
{
$skautISUser = $this->skautIsService->getUserDetail();
- $user = $this->userRepository->findBySkautISUserId($skautISUser->ID);
+ $user = $this->userRepository->findBySkautISUserId($skautISUser->ID) ?? $this->userRepository->findBySkautISPersonId($skautISUser->ID_Person);
$firstLogin = false;
if ($user === null) {
From 8ad3f6325d0f9539d57541aad8546236e1e5dbf2 Mon Sep 17 00:00:00 2001
From: bojovyletoun <662941+bojovyletoun@users.noreply.github.com>
Date: Tue, 13 Dec 2022 19:35:45 +0100
Subject: [PATCH 26/41] Filtrovani,razeni VS (#936)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Použití atributu column pro sloupce místo Renderer
* Použití addColumnLink pro buňky s odkazem
* Správná filtrace řetězených sloupců
zatím jen VS
* Řazení řetězených sloupců
zatím jen VS
Co-authored-by: bojovyletoun <>
---
.../Components/GroupsGridControl.php | 29 ++++++++++++-------
1 file changed, 18 insertions(+), 11 deletions(-)
diff --git a/app/AdminModule/Components/GroupsGridControl.php b/app/AdminModule/Components/GroupsGridControl.php
index 252f8c4c1..aa34f68c2 100644
--- a/app/AdminModule/Components/GroupsGridControl.php
+++ b/app/AdminModule/Components/GroupsGridControl.php
@@ -10,13 +10,13 @@
use App\Services\ExcelExportService;
use App\Utils\Helpers;
use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\ORM\QueryBuilder;
use Exception;
use Nette\Application\AbortException;
use Nette\Application\UI\Control;
use Nette\Http\Session;
use Nette\Http\SessionSection;
use Nette\Localization\Translator;
-use Nette\Utils\Html;
use Throwable;
use Ublaboo\DataGrid\DataGrid;
use Ublaboo\DataGrid\Exception\DataGridColumnStatusException;
@@ -85,16 +85,23 @@ public function createComponentPatrolsGrid(string $name): DataGrid
->setSortable()
->setFilterText();
- $grid->addColumnText('variableSymbol', 'VS ')->setSortable() // je stejný jako název skupiny
- ->setRenderer(static fn ($t) => $t->getVariableSymbolText())
- ->setFilterText();
-
- $grid->addColumnText('leader', 'Vedoucí')->setSortable()
- ->setRenderer(function (Troop $t) {
- $leader = $t->getLeader();
-
- return Html::el('a')->setAttribute('href', $this->getPresenter()->link('detail', $leader->getId()))->setText($leader->getDisplayName());
- })
+ $grid->addColumnText('variableSymbol', 'VS ', 'variableSymbolText')
+ ->setSortable()
+ ->setSortableCallback(static function (QueryBuilder $qb, array $sort): void {
+ $sortRev = $sort['variableSymbolText'] === 'DESC' ? 'ASC' : 'DESC';
+ $qb->join('p.variableSymbol', 'VS')
+ ->orderBy('VS.variableSymbol', $sortRev);
+ })
+ ->setFilterText()
+ ->setCondition(static function (QueryBuilder $qb, string $value): void {
+// $qb->join('p.applications', 'uAVS')
+ $qb->join('p.variableSymbol', 'VS')
+ ->andWhere('VS.variableSymbol LIKE :variableSymbol')
+ ->setParameter(':variableSymbol', '%' . $value . '%');
+ });
+
+ $grid->addColumnLink('leader', 'Vedoucí', ':detail', 'leader.displayName', ['id' => 'leader.id'])->setSortable()
+// return Html::el('a')->setAttribute('href', $this->getPresenter()->link('detail', $leader->getId()))->setText($leader->getDisplayName());
->setFilterText();
$grid->addColumnDateTime('applicationDate', 'Datum založení')
From ad0775c257cded99f180e889774f199810a11270 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Fri, 16 Dec 2022 03:01:07 +0100
Subject: [PATCH 27/41] =?UTF-8?q?Oprava=20p=C5=99ihl=C3=A1=C5=A1en=C3=AD?=
=?UTF-8?q?=20pro=20p=C5=99idan=C3=A9=20=C3=BA=C4=8Dastn=C3=ADky=202=20(#9?=
=?UTF-8?q?39)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* group user login fix
* cs fix
---
app/Services/Authenticator.php | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/Services/Authenticator.php b/app/Services/Authenticator.php
index c0d174895..54bec4846 100644
--- a/app/Services/Authenticator.php
+++ b/app/Services/Authenticator.php
@@ -49,11 +49,12 @@ public function authenticate(string $user, string $password): SimpleIdentity
{
$skautISUser = $this->skautIsService->getUserDetail();
- $user = $this->userRepository->findBySkautISUserId($skautISUser->ID) ?? $this->userRepository->findBySkautISPersonId($skautISUser->ID_Person);
+ $user = $this->userRepository->findBySkautISUserId($skautISUser->ID);
$firstLogin = false;
if ($user === null) {
- $user = new User();
+ // nacten ze skautIS pres skupinu
+ $user = $this->userRepository->findBySkautISPersonId($skautISUser->ID_Person) ?? new User();
$roleNonregistered = $this->roleRepository->findBySystemName(Role::NONREGISTERED);
$user->addRole($roleNonregistered);
$firstLogin = true;
From 831968d55dc7606f8b2df29c872a4b2cf79b4e45 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Sun, 25 Dec 2022 23:37:03 +0100
Subject: [PATCH 28/41] Detail skupiny (#940)
* group detail
* group detail
* users module
* detail
* cleanup
* sort fix
* code style fix
* cleanup
* cleanup
* cleanup
---
.../Components/GroupsGridControl.php | 235 ------------------
.../Components/IGroupsGridControlFactory.php | 16 --
.../Components/PatrolsGridControl.php | 180 --------------
.../templates/Dashboard/default.latte | 8 +-
.../templates/includes/main_menu.latte | 7 +-
.../ProgramAttendeesGridControl.php | 2 +-
.../Components/ApplicationsGridControl.php | 2 +-
.../IApplicationsGridControlFactory.php | 2 +-
.../Components/IPatrolsGridControlFactory.php | 2 +-
.../Components/ITroopsGridControlFactory.php | 16 ++
.../Components/IUsersGridControlFactory.php | 2 +-
.../Components/PatrolsGridControl.php | 98 ++++++++
.../Components/TroopsGridControl.php | 175 +++++++++++++
.../Components/UsersGridControl.php | 2 +-
.../templates/applications_grid.latte | 0
.../templates/applications_grid_detail.latte | 0
.../Components/templates/patrols_grid.latte | 0
.../Components/templates/troops_grid.latte} | 0
.../Components/templates/users_grid.latte | 0
.../Forms/AddLectorFormFactory.php | 3 +-
.../EditUserPersonalDetailsFormFactory.php | 3 +-
.../Forms/EditUserSeminarFormFactory.php | 3 +-
.../Presenters/PatrolsPresenter.php | 26 ++
.../Presenters/TroopsPresenter.php | 36 +++
.../Presenters/UsersBasePresenter.php | 28 +++
.../Presenters/UsersPresenter.php | 47 +---
.../Presenters/templates/@layout.latte | 2 +
.../templates/Patrols/default.latte | 4 +
.../Presenters/templates/Troops/default.latte | 4 +
.../Presenters/templates/Troops/detail.latte | 93 +++++++
.../Presenters/templates/Users/add.latte | 0
.../Presenters/templates/Users/default.latte | 0
.../Presenters/templates/Users/detail.latte | 0
.../Presenters/templates/Users/sidebar.latte | 0
.../Presenters/templates/sidebar.latte | 15 ++
app/Router/RouterFactory.php | 7 +
app/lang/admin.cs_CZ.neon | 9 +-
app/lang/common.cs_CZ.neon | 2 +-
38 files changed, 543 insertions(+), 486 deletions(-)
delete mode 100644 app/AdminModule/Components/GroupsGridControl.php
delete mode 100644 app/AdminModule/Components/IGroupsGridControlFactory.php
delete mode 100644 app/AdminModule/Components/PatrolsGridControl.php
rename app/AdminModule/{ => UsersModule}/Components/ApplicationsGridControl.php (99%)
rename app/AdminModule/{ => UsersModule}/Components/IApplicationsGridControlFactory.php (82%)
rename app/AdminModule/{ => UsersModule}/Components/IPatrolsGridControlFactory.php (81%)
create mode 100644 app/AdminModule/UsersModule/Components/ITroopsGridControlFactory.php
rename app/AdminModule/{ => UsersModule}/Components/IUsersGridControlFactory.php (81%)
create mode 100644 app/AdminModule/UsersModule/Components/PatrolsGridControl.php
create mode 100644 app/AdminModule/UsersModule/Components/TroopsGridControl.php
rename app/AdminModule/{ => UsersModule}/Components/UsersGridControl.php (99%)
rename app/AdminModule/{ => UsersModule}/Components/templates/applications_grid.latte (100%)
rename app/AdminModule/{ => UsersModule}/Components/templates/applications_grid_detail.latte (100%)
rename app/AdminModule/{ => UsersModule}/Components/templates/patrols_grid.latte (100%)
rename app/AdminModule/{Components/templates/groups_grid.latte => UsersModule/Components/templates/troops_grid.latte} (100%)
rename app/AdminModule/{ => UsersModule}/Components/templates/users_grid.latte (100%)
rename app/AdminModule/{ => UsersModule}/Forms/AddLectorFormFactory.php (98%)
rename app/AdminModule/{ => UsersModule}/Forms/EditUserPersonalDetailsFormFactory.php (98%)
rename app/AdminModule/{ => UsersModule}/Forms/EditUserSeminarFormFactory.php (99%)
create mode 100644 app/AdminModule/UsersModule/Presenters/PatrolsPresenter.php
create mode 100644 app/AdminModule/UsersModule/Presenters/TroopsPresenter.php
create mode 100644 app/AdminModule/UsersModule/Presenters/UsersBasePresenter.php
rename app/AdminModule/{ => UsersModule}/Presenters/UsersPresenter.php (83%)
create mode 100644 app/AdminModule/UsersModule/Presenters/templates/@layout.latte
create mode 100644 app/AdminModule/UsersModule/Presenters/templates/Patrols/default.latte
create mode 100644 app/AdminModule/UsersModule/Presenters/templates/Troops/default.latte
create mode 100644 app/AdminModule/UsersModule/Presenters/templates/Troops/detail.latte
rename app/AdminModule/{ => UsersModule}/Presenters/templates/Users/add.latte (100%)
rename app/AdminModule/{ => UsersModule}/Presenters/templates/Users/default.latte (100%)
rename app/AdminModule/{ => UsersModule}/Presenters/templates/Users/detail.latte (100%)
rename app/AdminModule/{ => UsersModule}/Presenters/templates/Users/sidebar.latte (100%)
create mode 100644 app/AdminModule/UsersModule/Presenters/templates/sidebar.latte
diff --git a/app/AdminModule/Components/GroupsGridControl.php b/app/AdminModule/Components/GroupsGridControl.php
deleted file mode 100644
index aa34f68c2..000000000
--- a/app/AdminModule/Components/GroupsGridControl.php
+++ /dev/null
@@ -1,235 +0,0 @@
-sessionSection = $session->getSection('srs');
- }
-
- /**
- * Vykreslí komponentu.
- */
- public function render(): void
- {
- $this->template->setFile(__DIR__ . '/templates/groups_grid.latte');
- $this->template->render();
- }
-
- /**
- * Vytvoří komponentu.
- *
- * @throws Throwable
- * @throws DataGridColumnStatusException
- * @throws DataGridException
- */
- public function createComponentPatrolsGrid(string $name): DataGrid
- {
- $grid = new DataGrid($this, $name);
- $grid->setTranslator($this->translator);
- $grid->setDataSource($this->repository->createQueryBuilder('p'));
- $grid->setDefaultSort(['displayName' => 'ASC']);
- $grid->setColumnsHideable();
- $grid->setItemsPerPageList([25, 50, 100, 250, 500]);
- $grid->setStrictSessionFilterValues(false);
-
- $stamp = date(Helpers::DATE_FORMAT);
- $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Skupiny ' . $stamp . '.csv');
- $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Skupiny fi ' . $stamp . '.csv');
-
- $grid->addGroupAction('Export seznamu skupin')
- ->onSelect[] = [$this, 'groupExportUsers'];
-
- $grid->addColumnText('id', 'ID')
- ->setSortable();
-
- $grid->addColumnText('state', 'Stav')->setSortable()
- ->setRenderer(fn ($t) => $this->translator->translate('common.application_state.' . $t->getState()))
- ->setFilterText();
-
- $grid->addColumnText('name', 'Název')
- ->setSortable()
- ->setFilterText();
-
- $grid->addColumnText('variableSymbol', 'VS ', 'variableSymbolText')
- ->setSortable()
- ->setSortableCallback(static function (QueryBuilder $qb, array $sort): void {
- $sortRev = $sort['variableSymbolText'] === 'DESC' ? 'ASC' : 'DESC';
- $qb->join('p.variableSymbol', 'VS')
- ->orderBy('VS.variableSymbol', $sortRev);
- })
- ->setFilterText()
- ->setCondition(static function (QueryBuilder $qb, string $value): void {
-// $qb->join('p.applications', 'uAVS')
- $qb->join('p.variableSymbol', 'VS')
- ->andWhere('VS.variableSymbol LIKE :variableSymbol')
- ->setParameter(':variableSymbol', '%' . $value . '%');
- });
-
- $grid->addColumnLink('leader', 'Vedoucí', ':detail', 'leader.displayName', ['id' => 'leader.id'])->setSortable()
-// return Html::el('a')->setAttribute('href', $this->getPresenter()->link('detail', $leader->getId()))->setText($leader->getDisplayName());
- ->setFilterText();
-
- $grid->addColumnDateTime('applicationDate', 'Datum založení')
- ->setRenderer(static function (Troop $p) {
- $date = $p->getApplicationDate();
-
- return $date ? $date->format(Helpers::DATETIME_FORMAT) : '';
- })
- ->setSortable();
-
- $grid->addColumnText('pairingCode', 'Kód Jamoddílu')->setFilterText();
-
- $grid->addColumnText('fee', 'Cena getFee')->setSortable()->setFilterText();
-
-// $grid->addColumnText('fee2', 'Cena countFee')
-// ->setRenderer(static fn (Troop $t) => $t->countFee());
-
- $grid->addColumnDateTime('paymentDate', 'Datum zaplacení')
- ->setRenderer(static function (Troop $p) {
- $date = $p->getPaymentDate();
-
- return $date ? $date->format(Helpers::DATETIME_FORMAT) : '';
- })
- ->setSortable();
-
- $grid->addColumnDateTime('maturityDate', 'Datum splatnosti')
- ->setRenderer(static function (Troop $p) {
- $date = $p->getmaturityDate();
-
- return $date ? $date->format(Helpers::DATETIME_FORMAT) : '';
- })
- ->setSortable();
-
-// $grid->addColumnText('troop', 'Oddíl - přidat link')
-// ->setRenderer( function (Patrol $p) { $troop = $p->getTroop();
-// return Html::el("a")->setAttribute("href",$this->link("troopDetail",$troop->getId()))->setText($troop->getName());
-
-//}); // link na oddíl
-
- $grid->addColumnText('numPersons', '# osob')
-// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
- ->setRenderer(static fn (Troop $p) => $p->countUsersInRoles([Role::PATROL_LEADER, Role::LEADER, Role::ESCORT, Role::ATTENDEE]));
-
- $grid->addColumnText('numChilder', '# rádců')
-// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
- ->setRenderer(static fn (Troop $p) => $p->countUsersInRoles([Role::PATROL_LEADER]));
-
- $grid->addColumnText('numAdults', '# dospělých')
-// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
- ->setRenderer(static fn (Troop $p) => $p->countUsersInRoles([Role::LEADER, Role::ESCORT]));
-
- $grid->addColumnText('numPatrols', '# družin')
-// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
- ->setRenderer(static fn (Troop $p) => count($p->getConfirmedPatrols()));
-
- $grid->addAction('generatePaymentProof', 'Stáhnout potvzrení o přijetí platby', 'generatePaymentProof');
- $grid->allowRowsAction('generatePaymentProof', static fn (Troop $troop) => $troop->getPaymentDate() !== null);
-
-// $grid->addAction('detail', 'admin.common.detail', 'Users:groupDetail') // destinace ,todo group_detail.latte
-// ->setClass('btn btn-xs btn-primary');
-
-// $grid->addAction('delete', '', 'delete!')
-// ->setIcon('trash')
-// ->setTitle('admin.common.delete')
-// ->setClass('btn btn-xs btn-danger')
-// ->addAttributes([
-// 'data-toggle' => 'confirmation',
-// 'data-content' => $this->translator->translate('admin.users.users_delete_confirm'),
-// ]);
-
- return $grid;
- }
-
-// /**
-// * Zpracuje odstranění externí skupiny.
-// *
-// * @throws AbortException
-// */
-// public function handleDelete(int $id): void
-// {
-// $rec = $this->repository->findById($id);
-//
-// $this->repository->remove($rec);
-//
-// $p = $this->getPresenter();
-// $p->flashMessage('Skupina smazána.', 'success');
-// $p->redirect('this');
-// }
-
- /**
- * Vygeneruje potvrzení o přijetí platby.
- *
- * @throws AbortException
- */
- public function handleGeneratePaymentProof(int $id): void
- {
- $this->presenter->redirect(':Export:TroopIncomeProof:troop', ['id' => $id]);
- }
-
- /**
- * Hromadně vyexportuje seznam družin.
- *
- * @param int[] $ids
- *
- * @throws AbortException
- */
- public function groupExportUsers(array $ids): void
- {
- $this->sessionSection->patrolIds = $ids;
- $this->redirect('exportusers');
- }
-
- /**
- * Zpracuje export seznamu družin.
- *
- * @throws AbortException
- * @throws Exception
- */
- public function handleExportUsers(): void
- {
- $ids = $this->session->getSection('srs')->patrolIds;
-
- $res = $this->repository->createQueryBuilder('p')->where('p.id IN (:ids)') // stejne se v teto class querybuilder pouziva
- ->setParameter('ids', $ids)->getQuery()->getResult(); // otestovat , podivat se na vzor (export uzivatelu)
-
- $users = new ArrayCollection($res);
- $response = $this->excelExportService->exportUsersList($users, 'seznam-uzivatelu.xlsx'); // nutna nova metoda
-
- $this->getPresenter()->sendResponse($response);
- }
-}
diff --git a/app/AdminModule/Components/IGroupsGridControlFactory.php b/app/AdminModule/Components/IGroupsGridControlFactory.php
deleted file mode 100644
index a05426e9c..000000000
--- a/app/AdminModule/Components/IGroupsGridControlFactory.php
+++ /dev/null
@@ -1,16 +0,0 @@
-sessionSection = $session->getSection('srs');
- }
-
- /**
- * Vykreslí komponentu.
- */
- public function render(): void
- {
- $this->template->setFile(__DIR__ . '/templates/patrols_grid.latte');
- $this->template->render();
- }
-
- /**
- * Vytvoří komponentu.
- *
- * @throws Throwable
- * @throws DataGridColumnStatusException
- * @throws DataGridException
- */
- public function createComponentPatrolsGrid(string $name): DataGrid
- {
- $grid = new DataGrid($this, $name);
- $grid->setTranslator($this->translator);
- $grid->setDataSource($this->repository->createQueryBuilder('p')->where('p.confirmed = true'));
- $grid->setDefaultSort(['displayName' => 'ASC']);
- $grid->setColumnsHideable();
- $grid->setItemsPerPageList([25, 50, 100, 250, 500]);
- $grid->setStrictSessionFilterValues(false);
-
- $stamp = date(Helpers::DATE_FORMAT);
- $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Druziny ' . $stamp . '.csv');
- $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Druziny fi ' . $stamp . '.csv');
-
- $grid->addGroupAction('Export seznamu družin')
- ->onSelect[] = [$this, 'groupExportUsers'];
-
- $grid->addColumnText('id', 'ID')
- ->setSortable();
-
- $grid->addColumnText('name', 'Název')
- ->setSortable()
- ->setFilterText();
-
-// $grid->addColumnText('leader', 'Vedoucí')
-// ->setRenderer(static function (Patrol $p) {
-// return $leader = $p->countUsersInRoles([Role::LEADER]); {{ todo
-//
-// })
-// ->setFilterText();
-
- $grid->addColumnDateTime('created', 'Datum založení')
- ->setRenderer(static function (Patrol $p) {
- $date = $p->getTroop()->getApplicationDate();
-
- return $date ? $date->format(Helpers::DATETIME_FORMAT) : '';
- })
- ->setSortable();
-
- $grid->addColumnText('troop', 'Oddíl - přidat link')
- ->setRenderer(function (Patrol $p) {
- $troop = $p->getTroop();
-
- return Html::el('a')->setAttribute('href', $this->link('troopDetail', $troop->getId()))->setText($troop->getName());
- }); // link na oddíl
-
- $grid->addColumnText('userRoles', 'Počet 1')
- ->setRenderer(static fn (Patrol $p) => count($p->getUsersRoles())); // je to správné číslo?
-
-// $grid->addColumnText('notRegisteredMandatoryBlocksCount', 'admin.users.users_not_registered_mandatory_blocks')
-// ->setRenderer(static function (User $user) {
-// return Html::el('span')
-// ->setAttribute('data-toggle', 'tooltip')
-// ->setAttribute('title', $user->getNotRegisteredMandatoryBlocksText())
-// ->setText($user->getNotRegisteredMandatoryBlocksCount());
-// })
-// ->setSortable();
-
-
- $grid->addAction('detail', 'admin.common.detail', 'Users:detail') // destinace
- ->setClass('btn btn-xs btn-primary');
-
-// $grid->addAction('delete', '', 'delete!')
-// ->setIcon('trash')
-// ->setTitle('admin.common.delete')
-// ->setClass('btn btn-xs btn-danger')
-// ->addAttributes([
-// 'data-toggle' => 'confirmation',
-// 'data-content' => $this->translator->translate('admin.users.users_delete_confirm'),
-// ]);
-
- return $grid;
- }
-
-// /**
-// * Zpracuje odstranění externího uživatele.
-// *
-// * @throws AbortException
-// */
-// public function handleDelete(int $id): void
-// {
-// $patrol = $this->repository->findById($id);
-//
-// $this->repository->remove($patrol);
-//
-// $p = $this->getPresenter();
-// $p->flashMessage('Družina smazána', 'success');
-// $p->redirect('this');
-// }
-
- /**
- * Hromadně vyexportuje seznam družin.
- *
- * @param int[] $ids
- *
- * @throws AbortException
- */
- public function groupExportUsers(array $ids): void
- {
- $this->sessionSection->patrolIds = $ids;
- $this->redirect('exportusers');
- }
-
- /**
- * Zpracuje export seznamu družin.
- *
- * @throws AbortException
- * @throws Exception
- */
- public function handleExportUsers(): void
- {
- $ids = $this->session->getSection('srs')->patrolIds;
-
- $res = $this->repository->createQueryBuilder('p')->where('p.id IN (:ids)') // stejne se v teto class querybuilder pouziva
- ->setParameter('ids', $ids)->getQuery()->getResult(); // otestovat , podivat se na vzor (export uzivatelu)
-
- $users = new ArrayCollection($res);
- $response = $this->excelExportService->exportUsersList($users, 'seznam-uzivatelu.xlsx'); // nutna nova metoda
-
- $this->getPresenter()->sendResponse($response);
- }
-}
diff --git a/app/AdminModule/Presenters/templates/Dashboard/default.latte b/app/AdminModule/Presenters/templates/Dashboard/default.latte
index 70d1d4822..eb825a122 100644
--- a/app/AdminModule/Presenters/templates/Dashboard/default.latte
+++ b/app/AdminModule/Presenters/templates/Dashboard/default.latte
@@ -67,18 +67,18 @@
{_admin.menu.users}
diff --git a/app/AdminModule/Presenters/templates/includes/main_menu.latte b/app/AdminModule/Presenters/templates/includes/main_menu.latte
index 68f22ea40..08f700d77 100644
--- a/app/AdminModule/Presenters/templates/includes/main_menu.latte
+++ b/app/AdminModule/Presenters/templates/includes/main_menu.latte
@@ -23,8 +23,11 @@
{_admin.menu.program}
-
- {_admin.menu.users}
+
+ {_admin.menu.users}
diff --git a/app/AdminModule/ProgramModule/Components/ProgramAttendeesGridControl.php b/app/AdminModule/ProgramModule/Components/ProgramAttendeesGridControl.php
index 32912a2ee..9a4f338ff 100644
--- a/app/AdminModule/ProgramModule/Components/ProgramAttendeesGridControl.php
+++ b/app/AdminModule/ProgramModule/Components/ProgramAttendeesGridControl.php
@@ -149,7 +149,7 @@ public function createComponentProgramAttendeesGrid(string $name): void
$grid->setDefaultFilter(['attends' => 'yes'], false);
if ($user->isAllowed(SrsResource::USERS, Permission::MANAGE)) {
- $grid->addAction('detail', 'admin.common.detail', ':Admin:Users:detail')
+ $grid->addAction('detail', 'admin.common.detail', ':Admin:Users:Users:detail')
->setClass('btn btn-xs btn-primary')
->addAttributes(['target' => '_blank']);
}
diff --git a/app/AdminModule/Components/ApplicationsGridControl.php b/app/AdminModule/UsersModule/Components/ApplicationsGridControl.php
similarity index 99%
rename from app/AdminModule/Components/ApplicationsGridControl.php
rename to app/AdminModule/UsersModule/Components/ApplicationsGridControl.php
index 30488b6bd..45bfbb790 100644
--- a/app/AdminModule/Components/ApplicationsGridControl.php
+++ b/app/AdminModule/UsersModule/Components/ApplicationsGridControl.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace App\AdminModule\Components;
+namespace App\AdminModule\UsersModule\Components;
use App\Model\Application\Application;
use App\Model\Application\Repositories\ApplicationRepository;
diff --git a/app/AdminModule/Components/IApplicationsGridControlFactory.php b/app/AdminModule/UsersModule/Components/IApplicationsGridControlFactory.php
similarity index 82%
rename from app/AdminModule/Components/IApplicationsGridControlFactory.php
rename to app/AdminModule/UsersModule/Components/IApplicationsGridControlFactory.php
index 827874ad0..41deaf012 100644
--- a/app/AdminModule/Components/IApplicationsGridControlFactory.php
+++ b/app/AdminModule/UsersModule/Components/IApplicationsGridControlFactory.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace App\AdminModule\Components;
+namespace App\AdminModule\UsersModule\Components;
/**
* Factory komponenty pro správu přihlášek.
diff --git a/app/AdminModule/Components/IPatrolsGridControlFactory.php b/app/AdminModule/UsersModule/Components/IPatrolsGridControlFactory.php
similarity index 81%
rename from app/AdminModule/Components/IPatrolsGridControlFactory.php
rename to app/AdminModule/UsersModule/Components/IPatrolsGridControlFactory.php
index de8d1fbf5..9c77a6562 100644
--- a/app/AdminModule/Components/IPatrolsGridControlFactory.php
+++ b/app/AdminModule/UsersModule/Components/IPatrolsGridControlFactory.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace App\AdminModule\Components;
+namespace App\AdminModule\UsersModule\Components;
/**
* Factory komponenty pro správu družin.
diff --git a/app/AdminModule/UsersModule/Components/ITroopsGridControlFactory.php b/app/AdminModule/UsersModule/Components/ITroopsGridControlFactory.php
new file mode 100644
index 000000000..53f7a5420
--- /dev/null
+++ b/app/AdminModule/UsersModule/Components/ITroopsGridControlFactory.php
@@ -0,0 +1,16 @@
+template->setFile(__DIR__ . '/templates/patrols_grid.latte');
+ $this->template->render();
+ }
+
+ /**
+ * Vytvoří komponentu.
+ *
+ * @throws Throwable
+ * @throws DataGridColumnStatusException
+ * @throws DataGridException
+ */
+ public function createComponentPatrolsGrid(string $name): DataGrid
+ {
+ $grid = new DataGrid($this, $name);
+ $grid->setTranslator($this->translator);
+ $grid->setDataSource($this->repository->createQueryBuilder('p')->where('p.confirmed = true'));
+ $grid->setDefaultSort(['displayName' => 'ASC']);
+ $grid->setColumnsHideable();
+ $grid->setItemsPerPageList([25, 50, 100, 250, 500]);
+ $grid->setStrictSessionFilterValues(false);
+
+ $stamp = date(Helpers::DATE_FORMAT);
+ $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Druziny ' . $stamp . '.csv');
+ $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Druziny fi ' . $stamp . '.csv');
+
+ $grid->addColumnText('name', 'Název')
+ ->setSortable()
+ ->setFilterText();
+
+ $grid->addColumnText('troop', 'Skupina')
+ ->setRenderer(function (Patrol $p) {
+ $troop = $p->getTroop();
+
+ return Html::el('a')->setAttribute('href', $this->getPresenter()->link('Troops:detail', $troop->getId()))->setText($troop->getName());
+ });
+
+ $grid->addColumnDateTime('created', 'Datum založení')
+ ->setRenderer(static function (Patrol $p) {
+ $date = $p->getTroop()->getApplicationDate();
+
+ return $date ? $date->format(Helpers::DATETIME_FORMAT) : '';
+ })
+ ->setSortable();
+
+ $grid->addColumnText('userRoles', 'Počet osob')
+ ->setRenderer(static fn (Patrol $p) => count($p->getUsersRoles())); // je to správné číslo?
+
+// $grid->addAction('detail', 'admin.common.detail', 'Patrols:detail') // destinace
+// ->setClass('btn btn-xs btn-primary');
+
+// $grid->addAction('delete', '', 'delete!')
+// ->setIcon('trash')
+// ->setTitle('admin.common.delete')
+// ->setClass('btn btn-xs btn-danger')
+// ->addAttributes([
+// 'data-toggle' => 'confirmation',
+// 'data-content' => $this->translator->translate('admin.users.users_delete_confirm'),
+// ]);
+
+ return $grid;
+ }
+}
diff --git a/app/AdminModule/UsersModule/Components/TroopsGridControl.php b/app/AdminModule/UsersModule/Components/TroopsGridControl.php
new file mode 100644
index 000000000..c64587342
--- /dev/null
+++ b/app/AdminModule/UsersModule/Components/TroopsGridControl.php
@@ -0,0 +1,175 @@
+template->setFile(__DIR__ . '/templates/troops_grid.latte');
+ $this->template->render();
+ }
+
+ /**
+ * Vytvoří komponentu.
+ *
+ * @throws Throwable
+ * @throws DataGridColumnStatusException
+ * @throws DataGridException
+ */
+ public function createComponentPatrolsGrid(string $name): DataGrid
+ {
+ $grid = new DataGrid($this, $name);
+ $grid->setTranslator($this->translator);
+ $grid->setDataSource($this->repository->createQueryBuilder('p'));
+ $grid->setDefaultSort(['displayName' => 'ASC']);
+ $grid->setColumnsHideable();
+ $grid->setItemsPerPageList([25, 50, 100, 250, 500]);
+ $grid->setStrictSessionFilterValues(false);
+
+ $stamp = date(Helpers::DATE_FORMAT);
+ $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Skupiny ' . $stamp . '.csv');
+ $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Skupiny fi ' . $stamp . '.csv');
+
+ $grid->addColumnText('name', 'Název')
+ ->setSortable()
+ ->setFilterText();
+
+ $grid->addColumnText('state', 'Stav')
+ ->setSortable()
+ ->setRenderer(fn ($t) => $this->translator->translate('common.application_state.' . $t->getState()))
+ ->setFilterText();
+
+ $grid->addColumnText('variableSymbol', 'Variabilní symbol', 'variableSymbolText')
+ ->setSortable()
+ ->setSortableCallback(static function (QueryBuilder $qb, array $sort): void {
+ $sortRev = $sort['variableSymbolText'] === 'DESC' ? 'DESC' : 'ASC';
+ $qb->join('p.variableSymbol', 'pVS')
+ ->orderBy('pVS.variableSymbol', $sortRev);
+ })
+ ->setFilterText()
+ ->setCondition(static function (QueryBuilder $qb, string $value): void {
+ $qb->join('p.variableSymbol', 'pVS')
+ ->andWhere('pVS.variableSymbol LIKE :variableSymbol')
+ ->setParameter(':variableSymbol', '%' . $value . '%');
+ });
+
+ $grid->addColumnText('leader', 'Vedoucí')
+ ->setRenderer(function (Troop $t) {
+ $leader = $t->getLeader();
+
+ return Html::el('a')->setAttribute('href', $this->getPresenter()->link('Users:detail', $leader->getId()))->setText($leader->getDisplayName());
+ });
+
+ $grid->addColumnDateTime('applicationDate', 'Datum založení')
+ ->setRenderer(static function (Troop $p) {
+ $date = $p->getApplicationDate();
+
+ return $date ? $date->format(Helpers::DATETIME_FORMAT) : '';
+ })
+ ->setSortable();
+
+ $grid->addColumnNumber('fee', 'Cena')->setSortable()->setFilterText();
+
+ $grid->addColumnDateTime('maturityDate', 'Datum splatnosti')
+ ->setFormat(Helpers::DATE_FORMAT)
+ ->setSortable();
+
+ $grid->addColumnDateTime('paymentDate', 'Datum platby')
+ ->setFormat(Helpers::DATE_FORMAT)
+ ->setSortable();
+
+ $grid->addColumnText('pairingCode', 'Kód jamoddílu')
+ ->setFilterText();
+
+ $grid->addColumnNumber('numPersons', '# osob')
+// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
+ ->setRenderer(static fn (Troop $p) => $p->countUsersInRoles([Role::PATROL_LEADER, Role::LEADER, Role::ESCORT, Role::ATTENDEE]));
+
+ $grid->addColumnNumber('numChilder', '# rádců')
+// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
+ ->setRenderer(static fn (Troop $p) => $p->countUsersInRoles([Role::PATROL_LEADER]));
+
+ $grid->addColumnNumber('numAdults', '# dospělých')
+// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
+ ->setRenderer(static fn (Troop $p) => $p->countUsersInRoles([Role::LEADER, Role::ESCORT]));
+
+ $grid->addColumnNumber('numPatrols', '# družin')
+// ->setSortableCallback(static fn($qb,$vals) =>sort($vals))
+ ->setRenderer(static fn (Troop $p) => count($p->getConfirmedPatrols()));
+
+ $grid->addAction('generatePaymentProof', 'Stáhnout potvzrení o přijetí platby', 'generatePaymentProof');
+ $grid->allowRowsAction('generatePaymentProof', static fn (Troop $troop) => $troop->getPaymentDate() !== null);
+
+ $grid->addAction('detail', 'admin.common.detail', 'Troops:detail')
+ ->setClass('btn btn-xs btn-primary');
+
+// $grid->addAction('delete', '', 'delete!')
+// ->setIcon('trash')
+// ->setTitle('admin.common.delete')
+// ->setClass('btn btn-xs btn-danger')
+// ->addAttributes([
+// 'data-toggle' => 'confirmation',
+// 'data-content' => $this->translator->translate('admin.users.users_delete_confirm'),
+// ]);
+
+ return $grid;
+ }
+
+// /**
+// * Zpracuje odstranění externí skupiny.
+// *
+// * @throws AbortException
+// */
+// public function handleDelete(int $id): void
+// {
+// $rec = $this->repository->findById($id);
+//
+// $this->repository->remove($rec);
+//
+// $p = $this->getPresenter();
+// $p->flashMessage('Skupina smazána.', 'success');
+// $p->redirect('this');
+// }
+
+ /**
+ * Vygeneruje potvrzení o přijetí platby.
+ *
+ * @throws AbortException
+ */
+ public function handleGeneratePaymentProof(int $id): void
+ {
+ $this->presenter->redirect(':Export:TroopIncomeProof:troop', ['id' => $id]);
+ }
+}
diff --git a/app/AdminModule/Components/UsersGridControl.php b/app/AdminModule/UsersModule/Components/UsersGridControl.php
similarity index 99%
rename from app/AdminModule/Components/UsersGridControl.php
rename to app/AdminModule/UsersModule/Components/UsersGridControl.php
index 1d8181ac8..d7b3c4b1d 100644
--- a/app/AdminModule/Components/UsersGridControl.php
+++ b/app/AdminModule/UsersModule/Components/UsersGridControl.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace App\AdminModule\Components;
+namespace App\AdminModule\UsersModule\Components;
use App\Model\Acl\Repositories\RoleRepository;
use App\Model\Acl\Role;
diff --git a/app/AdminModule/Components/templates/applications_grid.latte b/app/AdminModule/UsersModule/Components/templates/applications_grid.latte
similarity index 100%
rename from app/AdminModule/Components/templates/applications_grid.latte
rename to app/AdminModule/UsersModule/Components/templates/applications_grid.latte
diff --git a/app/AdminModule/Components/templates/applications_grid_detail.latte b/app/AdminModule/UsersModule/Components/templates/applications_grid_detail.latte
similarity index 100%
rename from app/AdminModule/Components/templates/applications_grid_detail.latte
rename to app/AdminModule/UsersModule/Components/templates/applications_grid_detail.latte
diff --git a/app/AdminModule/Components/templates/patrols_grid.latte b/app/AdminModule/UsersModule/Components/templates/patrols_grid.latte
similarity index 100%
rename from app/AdminModule/Components/templates/patrols_grid.latte
rename to app/AdminModule/UsersModule/Components/templates/patrols_grid.latte
diff --git a/app/AdminModule/Components/templates/groups_grid.latte b/app/AdminModule/UsersModule/Components/templates/troops_grid.latte
similarity index 100%
rename from app/AdminModule/Components/templates/groups_grid.latte
rename to app/AdminModule/UsersModule/Components/templates/troops_grid.latte
diff --git a/app/AdminModule/Components/templates/users_grid.latte b/app/AdminModule/UsersModule/Components/templates/users_grid.latte
similarity index 100%
rename from app/AdminModule/Components/templates/users_grid.latte
rename to app/AdminModule/UsersModule/Components/templates/users_grid.latte
diff --git a/app/AdminModule/Forms/AddLectorFormFactory.php b/app/AdminModule/UsersModule/Forms/AddLectorFormFactory.php
similarity index 98%
rename from app/AdminModule/Forms/AddLectorFormFactory.php
rename to app/AdminModule/UsersModule/Forms/AddLectorFormFactory.php
index 9bbcbd9c1..73bfd9d90 100644
--- a/app/AdminModule/Forms/AddLectorFormFactory.php
+++ b/app/AdminModule/UsersModule/Forms/AddLectorFormFactory.php
@@ -2,8 +2,9 @@
declare(strict_types=1);
-namespace App\AdminModule\Forms;
+namespace App\AdminModule\UsersModule\Forms;
+use App\AdminModule\Forms\BaseFormFactory;
use App\Model\Acl\Repositories\RoleRepository;
use App\Model\Acl\Role;
use App\Model\User\Repositories\UserRepository;
diff --git a/app/AdminModule/Forms/EditUserPersonalDetailsFormFactory.php b/app/AdminModule/UsersModule/Forms/EditUserPersonalDetailsFormFactory.php
similarity index 98%
rename from app/AdminModule/Forms/EditUserPersonalDetailsFormFactory.php
rename to app/AdminModule/UsersModule/Forms/EditUserPersonalDetailsFormFactory.php
index 27de00856..594cca030 100644
--- a/app/AdminModule/Forms/EditUserPersonalDetailsFormFactory.php
+++ b/app/AdminModule/UsersModule/Forms/EditUserPersonalDetailsFormFactory.php
@@ -2,8 +2,9 @@
declare(strict_types=1);
-namespace App\AdminModule\Forms;
+namespace App\AdminModule\UsersModule\Forms;
+use App\AdminModule\Forms\BaseFormFactory;
use App\Model\User\Repositories\UserRepository;
use App\Model\User\User;
use App\Services\FilesService;
diff --git a/app/AdminModule/Forms/EditUserSeminarFormFactory.php b/app/AdminModule/UsersModule/Forms/EditUserSeminarFormFactory.php
similarity index 99%
rename from app/AdminModule/Forms/EditUserSeminarFormFactory.php
rename to app/AdminModule/UsersModule/Forms/EditUserSeminarFormFactory.php
index ce0683ba2..4ca83eb67 100644
--- a/app/AdminModule/Forms/EditUserSeminarFormFactory.php
+++ b/app/AdminModule/UsersModule/Forms/EditUserSeminarFormFactory.php
@@ -2,8 +2,9 @@
declare(strict_types=1);
-namespace App\AdminModule\Forms;
+namespace App\AdminModule\UsersModule\Forms;
+use App\AdminModule\Forms\BaseFormFactory;
use App\Model\Acl\Repositories\RoleRepository;
use App\Model\Acl\Role;
use App\Model\CustomInput\CustomCheckbox;
diff --git a/app/AdminModule/UsersModule/Presenters/PatrolsPresenter.php b/app/AdminModule/UsersModule/Presenters/PatrolsPresenter.php
new file mode 100644
index 000000000..33a14ef43
--- /dev/null
+++ b/app/AdminModule/UsersModule/Presenters/PatrolsPresenter.php
@@ -0,0 +1,26 @@
+patrolsGridControlFactory->create();
+ }
+}
diff --git a/app/AdminModule/UsersModule/Presenters/TroopsPresenter.php b/app/AdminModule/UsersModule/Presenters/TroopsPresenter.php
new file mode 100644
index 000000000..7cc7912c8
--- /dev/null
+++ b/app/AdminModule/UsersModule/Presenters/TroopsPresenter.php
@@ -0,0 +1,36 @@
+troopsGridControlFactory->create();
+ }
+
+ public function renderDetail(int $id): void
+ {
+ $troop = $this->troopRepository->findById($id);
+ $this->template->troop = $troop;
+ }
+}
diff --git a/app/AdminModule/UsersModule/Presenters/UsersBasePresenter.php b/app/AdminModule/UsersModule/Presenters/UsersBasePresenter.php
new file mode 100644
index 000000000..033cbb365
--- /dev/null
+++ b/app/AdminModule/UsersModule/Presenters/UsersBasePresenter.php
@@ -0,0 +1,28 @@
+checkPermission(Permission::MANAGE);
+ }
+}
diff --git a/app/AdminModule/Presenters/UsersPresenter.php b/app/AdminModule/UsersModule/Presenters/UsersPresenter.php
similarity index 83%
rename from app/AdminModule/Presenters/UsersPresenter.php
rename to app/AdminModule/UsersModule/Presenters/UsersPresenter.php
index aaaaae822..eba13dff6 100644
--- a/app/AdminModule/Presenters/UsersPresenter.php
+++ b/app/AdminModule/UsersModule/Presenters/UsersPresenter.php
@@ -2,20 +2,15 @@
declare(strict_types=1);
-namespace App\AdminModule\Presenters;
-
-use App\AdminModule\Components\ApplicationsGridControl;
-use App\AdminModule\Components\GroupsGridControl;
-use App\AdminModule\Components\IApplicationsGridControlFactory;
-use App\AdminModule\Components\IGroupsGridControlFactory;
-use App\AdminModule\Components\IPatrolsGridControlFactory;
-use App\AdminModule\Components\IUsersGridControlFactory;
-use App\AdminModule\Components\PatrolsGridControl;
-use App\AdminModule\Components\UsersGridControl;
-use App\AdminModule\Forms\AddLectorFormFactory;
-use App\AdminModule\Forms\EditUserPersonalDetailsFormFactory;
-use App\AdminModule\Forms\EditUserSeminarFormFactory;
-use App\Model\Acl\Permission;
+namespace App\AdminModule\UsersModule\Presenters;
+
+use App\AdminModule\UsersModule\Components\ApplicationsGridControl;
+use App\AdminModule\UsersModule\Components\IApplicationsGridControlFactory;
+use App\AdminModule\UsersModule\Components\IUsersGridControlFactory;
+use App\AdminModule\UsersModule\Components\UsersGridControl;
+use App\AdminModule\UsersModule\Forms\AddLectorFormFactory;
+use App\AdminModule\UsersModule\Forms\EditUserPersonalDetailsFormFactory;
+use App\AdminModule\UsersModule\Forms\EditUserSeminarFormFactory;
use App\Model\Acl\Role;
use App\Model\Acl\SrsResource;
use App\Model\CustomInput\CustomInput;
@@ -24,7 +19,6 @@
use App\Model\Enums\PaymentType;
use App\Model\User\Queries\UserAttendsProgramsQuery;
use App\Services\ApplicationService;
-use App\Services\ExcelExportService;
use Nette\Application\AbortException;
use Nette\Application\UI\Form;
use Nette\DI\Attributes\Inject;
@@ -34,19 +28,13 @@
/**
* Presenter obsluhující správu uživatelů.
*/
-class UsersPresenter extends AdminBasePresenter
+class UsersPresenter extends UsersBasePresenter
{
protected string $resource = SrsResource::USERS;
#[Inject]
public IUsersGridControlFactory $usersGridControlFactory;
- #[Inject]
- public IPatrolsGridControlFactory $patrolsGridControlFactory;
-
- #[Inject]
- public IGroupsGridControlFactory $GroupsGridControlFactory;
-
#[Inject]
public AddLectorFormFactory $addLectorFormFactory;
@@ -59,9 +47,6 @@ class UsersPresenter extends AdminBasePresenter
#[Inject]
public IApplicationsGridControlFactory $applicationsGridControlFactory;
- #[Inject]
- public ExcelExportService $excelExportService;
-
#[Inject]
public CustomInputRepository $customInputRepository;
@@ -75,8 +60,6 @@ public function startup(): void
{
parent::startup();
- $this->checkPermission(Permission::MANAGE);
-
$this->template->results = [];
$this->template->editPersonalDetails = false;
$this->template->editSeminar = false;
@@ -189,16 +172,6 @@ protected function createComponentUsersGrid(): UsersGridControl
return $this->usersGridControlFactory->create();
}
- protected function createComponentPatrolsGrid(): PatrolsGridControl
- {
- return $this->patrolsGridControlFactory->create();
- }
-
- protected function createComponentGroupsGrid(): GroupsGridControl
- {
- return $this->GroupsGridControlFactory->create();
- }
-
protected function createComponentAddLectorForm(): Form
{
$form = $this->addLectorFormFactory->create();
diff --git a/app/AdminModule/UsersModule/Presenters/templates/@layout.latte b/app/AdminModule/UsersModule/Presenters/templates/@layout.latte
new file mode 100644
index 000000000..1cc7452c7
--- /dev/null
+++ b/app/AdminModule/UsersModule/Presenters/templates/@layout.latte
@@ -0,0 +1,2 @@
+{layout '../../../Presenters/templates/@layout.latte'}
+{import 'sidebar.latte'}
\ No newline at end of file
diff --git a/app/AdminModule/UsersModule/Presenters/templates/Patrols/default.latte b/app/AdminModule/UsersModule/Presenters/templates/Patrols/default.latte
new file mode 100644
index 000000000..125f2de60
--- /dev/null
+++ b/app/AdminModule/UsersModule/Presenters/templates/Patrols/default.latte
@@ -0,0 +1,4 @@
+{block main}
+ {_admin.users.patrols.heading}
+ {control patrolsGrid}
+{/block}
\ No newline at end of file
diff --git a/app/AdminModule/UsersModule/Presenters/templates/Troops/default.latte b/app/AdminModule/UsersModule/Presenters/templates/Troops/default.latte
new file mode 100644
index 000000000..b355f1152
--- /dev/null
+++ b/app/AdminModule/UsersModule/Presenters/templates/Troops/default.latte
@@ -0,0 +1,4 @@
+{block main}
+ {_admin.users.troops.heading}
+ {control troopsGrid}
+{/block}
\ No newline at end of file
diff --git a/app/AdminModule/UsersModule/Presenters/templates/Troops/detail.latte b/app/AdminModule/UsersModule/Presenters/templates/Troops/detail.latte
new file mode 100644
index 000000000..464e8f240
--- /dev/null
+++ b/app/AdminModule/UsersModule/Presenters/templates/Troops/detail.latte
@@ -0,0 +1,93 @@
+{block main}
+ Detail skupiny: {$troop->getName()}
+
+ Základní údaje
+
+
+ Seznam členů
+
+
+
+ Jméno |
+ Role |
+ Datum narození |
+ Zdravotní údaje |
+
+
+
+
+
+
+ {$userRole->getUser()->getDisplayName()}
+
+ |
+ {$userRole->getRole()->getName()} |
+ {$userRole->getUser()->getBirthdate()|date:"j. n. Y"} |
+ {$userRole->getUser()->getHealthInfo()} |
+
+
+
+
+
+
Družina: {$patrol->getName()}
+
+
+
+ Jméno |
+ Role |
+ Datum narození |
+ Zdravotní údaje |
+
+
+
+
+
+
+ {$userRole->getUser()->getDisplayName()}
+
+ |
+ {$userRole->getRole()->getName()} |
+ {$userRole->getUser()->getBirthdate()|date:"j. n. Y"} |
+ {$userRole->getUser()->getHealthInfo()} |
+
+
+
+
+{/block}
diff --git a/app/AdminModule/Presenters/templates/Users/add.latte b/app/AdminModule/UsersModule/Presenters/templates/Users/add.latte
similarity index 100%
rename from app/AdminModule/Presenters/templates/Users/add.latte
rename to app/AdminModule/UsersModule/Presenters/templates/Users/add.latte
diff --git a/app/AdminModule/Presenters/templates/Users/default.latte b/app/AdminModule/UsersModule/Presenters/templates/Users/default.latte
similarity index 100%
rename from app/AdminModule/Presenters/templates/Users/default.latte
rename to app/AdminModule/UsersModule/Presenters/templates/Users/default.latte
diff --git a/app/AdminModule/Presenters/templates/Users/detail.latte b/app/AdminModule/UsersModule/Presenters/templates/Users/detail.latte
similarity index 100%
rename from app/AdminModule/Presenters/templates/Users/detail.latte
rename to app/AdminModule/UsersModule/Presenters/templates/Users/detail.latte
diff --git a/app/AdminModule/Presenters/templates/Users/sidebar.latte b/app/AdminModule/UsersModule/Presenters/templates/Users/sidebar.latte
similarity index 100%
rename from app/AdminModule/Presenters/templates/Users/sidebar.latte
rename to app/AdminModule/UsersModule/Presenters/templates/Users/sidebar.latte
diff --git a/app/AdminModule/UsersModule/Presenters/templates/sidebar.latte b/app/AdminModule/UsersModule/Presenters/templates/sidebar.latte
new file mode 100644
index 000000000..be6cca607
--- /dev/null
+++ b/app/AdminModule/UsersModule/Presenters/templates/sidebar.latte
@@ -0,0 +1,15 @@
+{block sidebar}
+
+{/block}
\ No newline at end of file
diff --git a/app/Router/RouterFactory.php b/app/Router/RouterFactory.php
index 3997ce19a..7e02de8e0 100644
--- a/app/Router/RouterFactory.php
+++ b/app/Router/RouterFactory.php
@@ -58,6 +58,13 @@ public function createRouter(): RouteList
'id' => null,
]);
+ $router->addRoute('admin/users//[/]', [
+ 'module' => 'Admin:Users',
+ 'presenter' => 'Users',
+ 'action' => 'default',
+ 'id' => null,
+ ]);
+
$router->addRoute('admin/payments//[/]', [
'module' => 'Admin:Payments',
'presenter' => 'Payments',
diff --git a/app/lang/admin.cs_CZ.neon b/app/lang/admin.cs_CZ.neon
index 9a1a3a944..3e3a601c1 100644
--- a/app/lang/admin.cs_CZ.neon
+++ b/app/lang/admin.cs_CZ.neon
@@ -387,8 +387,7 @@ program:
users:
menu:
persons: "Osoby"
- groups: "Skupiny"
- troops: "Oddíly"
+ troops: "Skupiny"
patrols: "Družiny"
users_heading: "Uživatelé"
@@ -515,6 +514,12 @@ users:
users_detail_schedule: "Harmonogram"
users_detail_birthdate_age: "%birthdate% (%age% let)"
+ troops:
+ heading: Skupiny
+
+ patrols:
+ heading: Družiny
+
payments:
payments:
heading: "Platby"
diff --git a/app/lang/common.cs_CZ.neon b/app/lang/common.cs_CZ.neon
index 3b4a77a88..824208f48 100644
--- a/app/lang/common.cs_CZ.neon
+++ b/app/lang/common.cs_CZ.neon
@@ -119,7 +119,7 @@ application_state:
canceled: "Zrušeno"
paid: "Zaplaceno"
paid_free: "Zaplaceno (zdarma)"
- draft: "nepotvrzené (draft)"
+ draft: "Nepotvrzeno"
calendar_view:
timeGridSeminar: "Na výšku"
From fa80ca9b2cdc0c7ded96ded1b88be1fcb06f0e28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Mon, 26 Dec 2022 09:43:38 +0100
Subject: [PATCH 29/41] =?UTF-8?q?Maz=C3=A1n=C3=AD=20skupin=20a=20dru=C5=BE?=
=?UTF-8?q?in=20(#941)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* remove groups
* format
---
.../Components/PatrolsGridControl.php | 40 +++++++++++----
.../Components/TroopsGridControl.php | 51 ++++++++++---------
.../Commands/Handlers/RemovePatrolHandler.php | 38 ++++++++++++++
.../Commands/Handlers/RemoveTroopHandler.php | 45 ++++++++++++++++
app/Model/User/Commands/RemovePatrol.php | 19 +++++++
app/Model/User/Commands/RemoveTroop.php | 19 +++++++
.../User/Repositories/PatrolRepository.php | 6 +++
.../User/Repositories/TroopRepository.php | 6 +++
8 files changed, 188 insertions(+), 36 deletions(-)
create mode 100644 app/Model/User/Commands/Handlers/RemovePatrolHandler.php
create mode 100644 app/Model/User/Commands/Handlers/RemoveTroopHandler.php
create mode 100644 app/Model/User/Commands/RemovePatrol.php
create mode 100644 app/Model/User/Commands/RemoveTroop.php
diff --git a/app/AdminModule/UsersModule/Components/PatrolsGridControl.php b/app/AdminModule/UsersModule/Components/PatrolsGridControl.php
index d1d2449c3..9ae6e4a8b 100644
--- a/app/AdminModule/UsersModule/Components/PatrolsGridControl.php
+++ b/app/AdminModule/UsersModule/Components/PatrolsGridControl.php
@@ -4,9 +4,12 @@
namespace App\AdminModule\UsersModule\Components;
+use App\Model\User\Commands\RemovePatrol;
use App\Model\User\Patrol;
use App\Model\User\Repositories\PatrolRepository;
+use App\Services\CommandBus;
use App\Utils\Helpers;
+use Nette\Application\AbortException;
use Nette\Application\UI\Control;
use Nette\Localization\Translator;
use Nette\Utils\Html;
@@ -24,8 +27,9 @@
class PatrolsGridControl extends Control
{
public function __construct(
+ private CommandBus $commandBus,
private Translator $translator,
- private PatrolRepository $repository
+ private PatrolRepository $patrolRepository
) {
}
@@ -49,7 +53,7 @@ public function createComponentPatrolsGrid(string $name): DataGrid
{
$grid = new DataGrid($this, $name);
$grid->setTranslator($this->translator);
- $grid->setDataSource($this->repository->createQueryBuilder('p')->where('p.confirmed = true'));
+ $grid->setDataSource($this->patrolRepository->createQueryBuilder('p')->where('p.confirmed = true'));
$grid->setDefaultSort(['displayName' => 'ASC']);
$grid->setColumnsHideable();
$grid->setItemsPerPageList([25, 50, 100, 250, 500]);
@@ -78,21 +82,35 @@ public function createComponentPatrolsGrid(string $name): DataGrid
})
->setSortable();
- $grid->addColumnText('userRoles', 'Počet osob')
+ $grid->addColumnNumber('userRoles', 'Počet osob')
->setRenderer(static fn (Patrol $p) => count($p->getUsersRoles())); // je to správné číslo?
// $grid->addAction('detail', 'admin.common.detail', 'Patrols:detail') // destinace
// ->setClass('btn btn-xs btn-primary');
-// $grid->addAction('delete', '', 'delete!')
-// ->setIcon('trash')
-// ->setTitle('admin.common.delete')
-// ->setClass('btn btn-xs btn-danger')
-// ->addAttributes([
-// 'data-toggle' => 'confirmation',
-// 'data-content' => $this->translator->translate('admin.users.users_delete_confirm'),
-// ]);
+ $grid->addAction('delete', '', 'delete!')
+ ->setIcon('trash')
+ ->setTitle('admin.common.delete')
+ ->setClass('btn btn-xs btn-danger')
+ ->addAttributes([
+ 'data-toggle' => 'confirmation',
+ 'data-content' => $this->translator->translate('Opravdu chcete družinu odstranit?'),
+ ]);
return $grid;
}
+
+ /**
+ * Zpracuje odstranění družiny.
+ *
+ * @throws AbortException
+ */
+ public function handleDelete(int $id): void
+ {
+ $patrol = $this->patrolRepository->findById($id);
+ $this->commandBus->handle(new RemovePatrol($patrol));
+ $p = $this->getPresenter();
+ $p->flashMessage('Družina byla úspěšně odstraněna.', 'success');
+ $p->redirect('this');
+ }
}
diff --git a/app/AdminModule/UsersModule/Components/TroopsGridControl.php b/app/AdminModule/UsersModule/Components/TroopsGridControl.php
index c64587342..f02f24d6e 100644
--- a/app/AdminModule/UsersModule/Components/TroopsGridControl.php
+++ b/app/AdminModule/UsersModule/Components/TroopsGridControl.php
@@ -5,8 +5,10 @@
namespace App\AdminModule\UsersModule\Components;
use App\Model\Acl\Role;
+use App\Model\User\Commands\RemoveTroop;
use App\Model\User\Repositories\TroopRepository;
use App\Model\User\Troop;
+use App\Services\CommandBus;
use App\Utils\Helpers;
use Doctrine\ORM\QueryBuilder;
use Nette\Application\AbortException;
@@ -27,8 +29,9 @@
class TroopsGridControl extends Control
{
public function __construct(
+ private CommandBus $commandBus,
private Translator $translator,
- private TroopRepository $repository
+ private TroopRepository $troopRepository
) {
}
@@ -52,7 +55,7 @@ public function createComponentPatrolsGrid(string $name): DataGrid
{
$grid = new DataGrid($this, $name);
$grid->setTranslator($this->translator);
- $grid->setDataSource($this->repository->createQueryBuilder('p'));
+ $grid->setDataSource($this->troopRepository->createQueryBuilder('p'));
$grid->setDefaultSort(['displayName' => 'ASC']);
$grid->setColumnsHideable();
$grid->setItemsPerPageList([25, 50, 100, 250, 500]);
@@ -135,33 +138,31 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->addAction('detail', 'admin.common.detail', 'Troops:detail')
->setClass('btn btn-xs btn-primary');
-// $grid->addAction('delete', '', 'delete!')
-// ->setIcon('trash')
-// ->setTitle('admin.common.delete')
-// ->setClass('btn btn-xs btn-danger')
-// ->addAttributes([
-// 'data-toggle' => 'confirmation',
-// 'data-content' => $this->translator->translate('admin.users.users_delete_confirm'),
-// ]);
+ $grid->addAction('delete', '', 'delete!')
+ ->setIcon('trash')
+ ->setTitle('admin.common.delete')
+ ->setClass('btn btn-xs btn-danger')
+ ->addAttributes([
+ 'data-toggle' => 'confirmation',
+ 'data-content' => $this->translator->translate('Opravdu chcete skupinu odstranit?'),
+ ]);
return $grid;
}
-// /**
-// * Zpracuje odstranění externí skupiny.
-// *
-// * @throws AbortException
-// */
-// public function handleDelete(int $id): void
-// {
-// $rec = $this->repository->findById($id);
-//
-// $this->repository->remove($rec);
-//
-// $p = $this->getPresenter();
-// $p->flashMessage('Skupina smazána.', 'success');
-// $p->redirect('this');
-// }
+ /**
+ * Zpracuje odstranění skupiny.
+ *
+ * @throws AbortException
+ */
+ public function handleDelete(int $id): void
+ {
+ $troop = $this->troopRepository->findById($id);
+ $this->commandBus->handle(new RemoveTroop($troop));
+ $p = $this->getPresenter();
+ $p->flashMessage('Skupina byla úspěšně odstraněna.', 'success');
+ $p->redirect('this');
+ }
/**
* Vygeneruje potvrzení o přijetí platby.
diff --git a/app/Model/User/Commands/Handlers/RemovePatrolHandler.php b/app/Model/User/Commands/Handlers/RemovePatrolHandler.php
new file mode 100644
index 000000000..05767f7de
--- /dev/null
+++ b/app/Model/User/Commands/Handlers/RemovePatrolHandler.php
@@ -0,0 +1,38 @@
+em->wrapInTransaction(function () use ($command): void {
+ foreach ($command->getPatrol()->getUsersRoles() as $userRole) {
+ $user = $userRole->getUser();
+ $this->userGroupRoleRepository->remove($userRole);
+ if ($user->getRoles()->isEmpty() && $user->getGroupRoles()->isEmpty()) {
+ $this->userRepository->remove($user);
+ }
+ }
+
+ $this->patrolRepository->remove($command->getPatrol());
+ });
+ }
+}
diff --git a/app/Model/User/Commands/Handlers/RemoveTroopHandler.php b/app/Model/User/Commands/Handlers/RemoveTroopHandler.php
new file mode 100644
index 000000000..6b707bafe
--- /dev/null
+++ b/app/Model/User/Commands/Handlers/RemoveTroopHandler.php
@@ -0,0 +1,45 @@
+em->wrapInTransaction(function () use ($command): void {
+ foreach ($command->getTroop()->getPatrols() as $patrol) {
+ $this->commandBus->handle(new RemovePatrol($patrol));
+ }
+
+ foreach ($command->getTroop()->getUsersRoles() as $userRole) {
+ $user = $userRole->getUser();
+ $this->userGroupRoleRepository->remove($userRole);
+ if ($user->getRoles()->isEmpty() && $user->getGroupRoles()->isEmpty()) {
+ $this->userRepository->remove($user);
+ }
+ }
+
+ $this->troopRepository->remove($command->getTroop());
+ });
+ }
+}
diff --git a/app/Model/User/Commands/RemovePatrol.php b/app/Model/User/Commands/RemovePatrol.php
new file mode 100644
index 000000000..f59e78218
--- /dev/null
+++ b/app/Model/User/Commands/RemovePatrol.php
@@ -0,0 +1,19 @@
+patrol;
+ }
+}
diff --git a/app/Model/User/Commands/RemoveTroop.php b/app/Model/User/Commands/RemoveTroop.php
new file mode 100644
index 000000000..40f2df601
--- /dev/null
+++ b/app/Model/User/Commands/RemoveTroop.php
@@ -0,0 +1,19 @@
+troop;
+ }
+}
diff --git a/app/Model/User/Repositories/PatrolRepository.php b/app/Model/User/Repositories/PatrolRepository.php
index b162dd7bb..2c18d0a6c 100644
--- a/app/Model/User/Repositories/PatrolRepository.php
+++ b/app/Model/User/Repositories/PatrolRepository.php
@@ -33,4 +33,10 @@ public function save(Patrol $patrol): void
$this->em->persist($patrol);
$this->em->flush();
}
+
+ public function remove(Patrol $patrol): void
+ {
+ $this->em->remove($patrol);
+ $this->em->flush();
+ }
}
diff --git a/app/Model/User/Repositories/TroopRepository.php b/app/Model/User/Repositories/TroopRepository.php
index 25fdc84e4..5ca5ecc1d 100644
--- a/app/Model/User/Repositories/TroopRepository.php
+++ b/app/Model/User/Repositories/TroopRepository.php
@@ -132,4 +132,10 @@ public function save(Troop $troop): void
$this->em->persist($troop);
$this->em->flush();
}
+
+ public function remove(Troop $troop): void
+ {
+ $this->em->remove($troop);
+ $this->em->flush();
+ }
}
From 148463b0c52deeddb9ece57f95761d325b053526 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Tue, 10 Jan 2023 21:22:37 +0100
Subject: [PATCH 30/41] =?UTF-8?q?Skupinov=C3=A9=20role=20v=20tabulce=20u?=
=?UTF-8?q?=C5=BEivatel=C5=AF=20(#944)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* group roles in users table
* return non draft only
* coalesce
* export
* cs fix
---
.../Components/UsersGridControl.php | 2 +
app/Model/User/User.php | 17 +++
app/Services/ExcelExportService.php | 119 +++++++++---------
3 files changed, 81 insertions(+), 57 deletions(-)
diff --git a/app/AdminModule/UsersModule/Components/UsersGridControl.php b/app/AdminModule/UsersModule/Components/UsersGridControl.php
index d7b3c4b1d..118c98658 100644
--- a/app/AdminModule/UsersModule/Components/UsersGridControl.php
+++ b/app/AdminModule/UsersModule/Components/UsersGridControl.php
@@ -166,6 +166,8 @@ public function createComponentUsersGrid(string $name): DataGrid
->setParameter('rids', (array) $values);
});
+ $grid->addColumnText('groupRoles', 'Skupinové role', 'groupRolesText');
+
$grid->addColumnText('subevents', 'admin.users.users_subevents', 'subeventsText')
->setFilterMultiSelect($this->subeventService->getSubeventsOptions())
->setCondition(static function (QueryBuilder $qb, ArrayHash $values): void {
diff --git a/app/Model/User/User.php b/app/Model/User/User.php
index ef4cfd9fe..63aa50694 100644
--- a/app/Model/User/User.php
+++ b/app/Model/User/User.php
@@ -13,6 +13,7 @@
use App\Model\CustomInput\CustomInput;
use App\Model\CustomInput\CustomInputValue;
use App\Model\Enums\ApplicationState;
+use App\Model\Enums\TroopApplicationState;
use App\Model\Program\Block;
use App\Model\Program\Program;
use App\Model\Program\ProgramApplication;
@@ -1241,4 +1242,20 @@ public function getGroupRoles(): Collection
{
return $this->groupRoles;
}
+
+ /**
+ * Vrátí skupinové role uživatele oddělené čárkou.
+ */
+ public function getGroupRolesText(): string
+ {
+ $rolesNames = [];
+ foreach ($this->groupRoles as $groupRole) {
+ $troop = $groupRole->getTroop() ?? $groupRole->getPatrol()->getTroop();
+ if ($troop->getState() !== TroopApplicationState::DRAFT) {
+ $rolesNames[] = $groupRole->getRole()->getName();
+ }
+ }
+
+ return implode(', ', $rolesNames);
+ }
}
diff --git a/app/Services/ExcelExportService.php b/app/Services/ExcelExportService.php
index 47006f208..dafbcf619 100644
--- a/app/Services/ExcelExportService.php
+++ b/app/Services/ExcelExportService.php
@@ -244,83 +244,88 @@ public function exportUsersList(Collection $users, string $filename): ExcelRespo
$row = 1;
$column = 1;
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.display_name'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.display_name'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(30);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.username'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.username'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(20);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.roles'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.roles'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(30);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.subevents'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Skupinové role'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(30);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.approved'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.subevents'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(30);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.approved'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(10);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.membership'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.membership'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(20);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.age'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.age'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(10);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.email'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.email'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(30);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.city'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.city'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(20);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.fee'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.fee'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(15);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.fee_remaining'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.fee_remaining'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(15);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.variable_symbol'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.variable_symbol'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(25);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.payment_method'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.payment_method'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(15);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.payment_date'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.payment_date'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(20);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.first_application_date'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.first_application_date'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(20);
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.attended'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.attended'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(10);
@@ -348,14 +353,14 @@ public function exportUsersList(Collection $users, string $filename): ExcelRespo
throw new InvalidArgumentException();
}
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate($customInput->getName()));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate($customInput->getName()));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth($width);
}
- $sheet->setCellValueByColumnAndRow($column, $row, $this->translator->translate('common.export.user.private_note'));
- $sheet->getStyleByColumnAndRow($column, $row)->getFont()->setBold(true);
+ $sheet->setCellValue([$column, $row], $this->translator->translate('common.export.user.private_note'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(60);
@@ -363,41 +368,41 @@ public function exportUsersList(Collection $users, string $filename): ExcelRespo
$row++;
$column = 1;
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getDisplayName());
+ $sheet->setCellValue([$column++, $row], $user->getDisplayName());
+
+ $sheet->setCellValue([$column++, $row], $user->getUsername());
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getUsername());
+ $sheet->setCellValue([$column++, $row], $user->getRolesText());
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getRolesText());
+ $sheet->setCellValue([$column++, $row], $user->getGroupRolesText());
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getSubeventsText());
+ $sheet->setCellValue([$column++, $row], $user->getSubeventsText());
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->isApproved()
+ $sheet->setCellValue([$column++, $row], $user->isApproved()
? $this->translator->translate('common.export.common.yes')
: $this->translator->translate('common.export.common.no'));
- $sheet->getCellByColumnAndRow($column++, $row)
- ->setValueExplicit($this->userService->getMembershipText($user), DataType::TYPE_STRING);
+ $sheet->getCell([$column++, $row])->setValueExplicit($this->userService->getMembershipText($user));
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getAge());
+ $sheet->setCellValue([$column++, $row], $user->getAge());
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getEmail());
+ $sheet->setCellValue([$column++, $row], $user->getEmail());
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getCity());
+ $sheet->setCellValue([$column++, $row], $user->getCity());
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getFee());
+ $sheet->setCellValue([$column++, $row], $user->getFee());
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getFeeRemaining());
+ $sheet->setCellValue([$column++, $row], $user->getFeeRemaining());
- $sheet->getCellByColumnAndRow($column++, $row)
- ->setValueExplicit($user->getVariableSymbolsText(), DataType::TYPE_STRING);
+ $sheet->getCell([$column++, $row])->setValueExplicit($user->getVariableSymbolsText());
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getPaymentMethod() ? $this->translator->translate('common.payment.' . $user->getPaymentMethod()) : '');
+ $sheet->setCellValue([$column++, $row], $user->getPaymentMethod() ? $this->translator->translate('common.payment.' . $user->getPaymentMethod()) : '');
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getLastPaymentDate() !== null ? $user->getLastPaymentDate()->format(Helpers::DATE_FORMAT) : '');
+ $sheet->setCellValue([$column++, $row], $user->getLastPaymentDate() !== null ? $user->getLastPaymentDate()->format(Helpers::DATE_FORMAT) : '');
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->getRolesApplicationDate() !== null ? $user->getRolesApplicationDate()->format(Helpers::DATE_FORMAT) : '');
+ $sheet->setCellValue([$column++, $row], $user->getRolesApplicationDate() !== null ? $user->getRolesApplicationDate()->format(Helpers::DATE_FORMAT) : '');
- $sheet->setCellValueByColumnAndRow($column++, $row, $user->isAttended()
+ $sheet->setCellValue([$column++, $row], $user->isAttended()
? $this->translator->translate('common.export.common.yes')
: $this->translator->translate('common.export.common.no'));
@@ -417,11 +422,11 @@ public function exportUsersList(Collection $users, string $filename): ExcelRespo
$value = $customInputValue->getValueText();
}
- $sheet->setCellValueByColumnAndRow($column++, $row, $value);
+ $sheet->setCellValue([$column++, $row], $value);
}
- $sheet->setCellValueByColumnAndRow($column, $row, $user->getNote());
- $sheet->getStyleByColumnAndRow($column++, $row)->getAlignment()->setWrapText(true);
+ $sheet->setCellValue([$column, $row], $user->getNote());
+ $sheet->getStyle([$column++, $row])->getAlignment()->setWrapText(true);
}
return new ExcelResponse($this->spreadsheet, $filename);
From 78c0a729931cbc39b7375f0aab52ace8d9a569f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Tue, 10 Jan 2023 22:03:58 +0100
Subject: [PATCH 31/41] remove troop fix (#947)
---
.../UsersModule/Components/PatrolsGridControl.php | 3 +--
app/Model/User/Commands/Handlers/RemoveTroopHandler.php | 2 ++
app/Model/User/User.php | 9 +++++++--
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/app/AdminModule/UsersModule/Components/PatrolsGridControl.php b/app/AdminModule/UsersModule/Components/PatrolsGridControl.php
index 9ae6e4a8b..367a4fa66 100644
--- a/app/AdminModule/UsersModule/Components/PatrolsGridControl.php
+++ b/app/AdminModule/UsersModule/Components/PatrolsGridControl.php
@@ -79,8 +79,7 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$date = $p->getTroop()->getApplicationDate();
return $date ? $date->format(Helpers::DATETIME_FORMAT) : '';
- })
- ->setSortable();
+ });
$grid->addColumnNumber('userRoles', 'Počet osob')
->setRenderer(static fn (Patrol $p) => count($p->getUsersRoles())); // je to správné číslo?
diff --git a/app/Model/User/Commands/Handlers/RemoveTroopHandler.php b/app/Model/User/Commands/Handlers/RemoveTroopHandler.php
index 6b707bafe..4afc11981 100644
--- a/app/Model/User/Commands/Handlers/RemoveTroopHandler.php
+++ b/app/Model/User/Commands/Handlers/RemoveTroopHandler.php
@@ -39,6 +39,8 @@ public function __invoke(RemoveTroop $command): void
}
}
+ $command->getTroop()->getLeader()->setTroop(null);
+
$this->troopRepository->remove($command->getTroop());
});
}
diff --git a/app/Model/User/User.php b/app/Model/User/User.php
index 63aa50694..45fe0e512 100644
--- a/app/Model/User/User.php
+++ b/app/Model/User/User.php
@@ -299,7 +299,7 @@ class User
* Přihláška oddílu.
*/
#[ORM\OneToOne(targetEntity: Troop::class, mappedBy: 'leader', cascade: ['persist'])]
- protected Troop $troop;
+ protected ?Troop $troop;
/**
* Telefon.
@@ -1170,11 +1170,16 @@ public function isAlternate(Program $program): bool
)->isEmpty();
}
- public function getTroop(): Troop
+ public function getTroop(): ?Troop
{
return $this->troop;
}
+ public function setTroop(?Troop $troop): void
+ {
+ $this->troop = $troop;
+ }
+
public function getPhone(): ?string
{
return $this->phone;
From de81920c36afc8b915a241d7e869df4e0c1a82c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Wed, 18 Jan 2023 20:52:28 +0100
Subject: [PATCH 32/41] =?UTF-8?q?E-mail=20vedouc=C3=ADho=20skupiny=20(#948?=
=?UTF-8?q?)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* troop leader email
* link
---
.../UsersModule/Components/TroopsGridControl.php | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/app/AdminModule/UsersModule/Components/TroopsGridControl.php b/app/AdminModule/UsersModule/Components/TroopsGridControl.php
index f02f24d6e..c71b7760d 100644
--- a/app/AdminModule/UsersModule/Components/TroopsGridControl.php
+++ b/app/AdminModule/UsersModule/Components/TroopsGridControl.php
@@ -95,9 +95,16 @@ public function createComponentPatrolsGrid(string $name): DataGrid
return Html::el('a')->setAttribute('href', $this->getPresenter()->link('Users:detail', $leader->getId()))->setText($leader->getDisplayName());
});
+ $grid->addColumnText('leaderEmail', 'E-mail vedoucího')
+ ->setRenderer(static function (Troop $t) {
+ $email = $t->getLeader()->getEmail();
+
+ return Html::el('a')->href('mailto:' . $email)->setText($email);
+ });
+
$grid->addColumnDateTime('applicationDate', 'Datum založení')
- ->setRenderer(static function (Troop $p) {
- $date = $p->getApplicationDate();
+ ->setRenderer(static function (Troop $t) {
+ $date = $t->getApplicationDate();
return $date ? $date->format(Helpers::DATETIME_FORMAT) : '';
})
From 700b1f01ef5e0c8cf7782dcb99be149bc1ac812a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Sat, 21 Jan 2023 13:38:50 +0100
Subject: [PATCH 33/41] =?UTF-8?q?Na=C4=8Dten=C3=AD=20=C4=8Dlenstv=C3=AD=20?=
=?UTF-8?q?ze=20skautIS=20(#949)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* update membership
* fix
* fix cs
---
.../Components/UsersGridControl.php | 33 +++++++++++++++++++
app/Services/Authenticator.php | 6 +---
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/app/AdminModule/UsersModule/Components/UsersGridControl.php b/app/AdminModule/UsersModule/Components/UsersGridControl.php
index 118c98658..b09818fdf 100644
--- a/app/AdminModule/UsersModule/Components/UsersGridControl.php
+++ b/app/AdminModule/UsersModule/Components/UsersGridControl.php
@@ -30,6 +30,7 @@
use App\Services\QueryBus;
use App\Services\SkautIsEventEducationService;
use App\Services\SkautIsEventGeneralService;
+use App\Services\SkautIsService;
use App\Services\SubeventService;
use App\Services\UserService;
use App\Utils\Helpers;
@@ -46,6 +47,7 @@
use Nette\Localization\Translator;
use Nette\Utils\ArrayHash;
use Nette\Utils\Html;
+use Skaut\Skautis\Wsdl\WsdlException;
use Throwable;
use Ublaboo\DataGrid\DataGrid;
use Ublaboo\DataGrid\Exception\DataGridColumnStatusException;
@@ -72,6 +74,7 @@ public function __construct(
private AclService $aclService,
private ApplicationService $applicationService,
private UserService $userService,
+ private SkautIsService $skautIsService,
private SkautIsEventEducationService $skautIsEventEducationService,
private SkautIsEventGeneralService $skautIsEventGeneralService,
private SubeventService $subeventService
@@ -150,6 +153,9 @@ public function createComponentUsersGrid(string $name): DataGrid
$grid->addGroupAction('admin.users.users_group_action_export_schedules')
->onSelect[] = [$this, 'groupExportSchedules'];
+ $grid->addGroupAction('Načíst členství ze skautIS (admin)')
+ ->onSelect[] = [$this, 'groupUpdateMembership'];
+
$grid->addColumnText('displayName', 'admin.users.users_name')
->setSortable()
->setFilterText();
@@ -714,6 +720,33 @@ public function groupExportUsers(array $ids): void
$this->redirect('exportusers');
}
+ /**
+ * @param int[] $ids
+ */
+ public function groupUpdateMembership(array $ids): void
+ {
+ $users = $this->userRepository->findUsersByIds($ids);
+ $errors = 0;
+
+ foreach ($users as $user) {
+ try {
+ $membership = $this->skautIsService->getValidMembership($user->getSkautISPersonId());
+ $user->setUnit($membership?->RegistrationNumber);
+ $this->userRepository->save($user);
+ } catch (WsdlException $e) {
+ $errors++;
+ }
+ }
+
+ if ($errors > 0) {
+ $this->getPresenter()->flashMessage('Členství některých účastníků se nepodařilo načíst (oprávnění).', 'warning');
+ } else {
+ $this->getPresenter()->flashMessage('Členství byla úspěšně načtena.', 'success');
+ }
+
+ $this->reload();
+ }
+
/**
* Zpracuje export seznamu uživatelů.
*
diff --git a/app/Services/Authenticator.php b/app/Services/Authenticator.php
index 54bec4846..862e46fe6 100644
--- a/app/Services/Authenticator.php
+++ b/app/Services/Authenticator.php
@@ -108,11 +108,7 @@ private function updateUserFromSkautIS(User $user, stdClass $skautISUser): void
$user->setMember($skautISUser->HasMembership);
$validMembership = $this->skautIsService->getValidMembership($user->getSkautISPersonId());
- if ($validMembership === null) {
- $user->setUnit(null);
- } else {
- $user->setUnit($validMembership->RegistrationNumber);
- }
+ $user->setUnit($validMembership?->RegistrationNumber);
$photoUpdate = new DateTimeImmutable($skautISPerson->PhotoUpdate);
if ($user->getPhotoUpdate() === null || $photoUpdate->diff($user->getPhotoUpdate())->s > 0) {
From 1bd53da49c23749b0984f3c60b90a21b1d3d5151 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Sat, 21 Jan 2023 14:52:24 +0100
Subject: [PATCH 34/41] =?UTF-8?q?Filtrov=C3=A1n=C3=AD=20skupinov=C3=BDch?=
=?UTF-8?q?=20rol=C3=AD=20(#951)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* filter group roles
* filter fix
* filter fix
---
.../UsersModule/Components/UsersGridControl.php | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/app/AdminModule/UsersModule/Components/UsersGridControl.php b/app/AdminModule/UsersModule/Components/UsersGridControl.php
index b09818fdf..3d8be9a29 100644
--- a/app/AdminModule/UsersModule/Components/UsersGridControl.php
+++ b/app/AdminModule/UsersModule/Components/UsersGridControl.php
@@ -19,6 +19,7 @@
use App\Model\Enums\ApplicationState;
use App\Model\Enums\PaymentType;
use App\Model\Enums\SkautIsEventType;
+use App\Model\Enums\TroopApplicationState;
use App\Model\Settings\Queries\SettingIntValueQuery;
use App\Model\Settings\Queries\SettingStringValueQuery;
use App\Model\Settings\Settings;
@@ -172,7 +173,19 @@ public function createComponentUsersGrid(string $name): DataGrid
->setParameter('rids', (array) $values);
});
- $grid->addColumnText('groupRoles', 'Skupinové role', 'groupRolesText');
+ $grid->addColumnText('groupRoles', 'Skupinové role', 'groupRolesText')
+ ->setFilterMultiSelect($this->aclService->getRolesWithoutRolesOptions([Role::GUEST, Role::UNAPPROVED]))
+ ->setCondition(static function (QueryBuilder $qb, ArrayHash $values): void {
+ $qb->join('u.groupRoles', 'uGR')
+ ->join('uGR.role', 'uGRR')
+ ->leftJoin('uGR.troop', 'uGRT')
+ ->leftJoin('uGR.patrol', 'uGRP')
+ ->leftJoin('uGRP.troop', 'uGRPT')
+ ->andWhere('uGRR.id IN (:grids)')
+ ->andWhere('(uGRT.state IS NULL OR uGRT.state != :tas) AND (uGRPT.state IS NULL OR uGRPT.state != :tas)')
+ ->setParameter('grids', (array) $values)
+ ->setParameter('tas', TroopApplicationState::DRAFT);
+ });
$grid->addColumnText('subevents', 'admin.users.users_subevents', 'subeventsText')
->setFilterMultiSelect($this->subeventService->getSubeventsOptions())
From ecc6c58b00a5b603e36eb719d46f7c57c38c2b9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Tue, 31 Jan 2023 22:51:19 +0100
Subject: [PATCH 35/41] Export troops, patrols to excel (#953)
* export to excel
* session fix
* phpstan fix
* cs fix
* fixes
* cell size
---
.../Components/PatrolsGridControl.php | 47 ++++-
.../Components/TroopsGridControl.php | 47 ++++-
.../User/Repositories/PatrolRepository.php | 15 ++
app/Services/ExcelExportService.php | 168 ++++++++++++++++++
4 files changed, 267 insertions(+), 10 deletions(-)
diff --git a/app/AdminModule/UsersModule/Components/PatrolsGridControl.php b/app/AdminModule/UsersModule/Components/PatrolsGridControl.php
index 367a4fa66..c998cddc4 100644
--- a/app/AdminModule/UsersModule/Components/PatrolsGridControl.php
+++ b/app/AdminModule/UsersModule/Components/PatrolsGridControl.php
@@ -8,9 +8,13 @@
use App\Model\User\Patrol;
use App\Model\User\Repositories\PatrolRepository;
use App\Services\CommandBus;
+use App\Services\ExcelExportService;
use App\Utils\Helpers;
+use Exception;
use Nette\Application\AbortException;
use Nette\Application\UI\Control;
+use Nette\Http\Session;
+use Nette\Http\SessionSection;
use Nette\Localization\Translator;
use Nette\Utils\Html;
use Throwable;
@@ -19,18 +23,22 @@
use Ublaboo\DataGrid\Exception\DataGridException;
use function count;
-use function date;
/**
* Komponenta pro zobrazení datagridu družin.
*/
class PatrolsGridControl extends Control
{
+ private SessionSection $sessionSection;
+
public function __construct(
private CommandBus $commandBus,
private Translator $translator,
- private PatrolRepository $patrolRepository
+ private PatrolRepository $patrolRepository,
+ private ExcelExportService $excelExportService,
+ private Session $session
) {
+ $this->sessionSection = $session->getSection('srs');
}
/**
@@ -59,9 +67,8 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->setItemsPerPageList([25, 50, 100, 250, 500]);
$grid->setStrictSessionFilterValues(false);
- $stamp = date(Helpers::DATE_FORMAT);
- $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Druziny ' . $stamp . '.csv');
- $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Druziny fi ' . $stamp . '.csv');
+ $grid->addGroupAction('Export seznamu družin')
+ ->onSelect[] = [$this, 'groupExportPatrols'];
$grid->addColumnText('name', 'Název')
->setSortable()
@@ -112,4 +119,34 @@ public function handleDelete(int $id): void
$p->flashMessage('Družina byla úspěšně odstraněna.', 'success');
$p->redirect('this');
}
+
+ /**
+ * Hromadně vyexportuje seznam družin.
+ *
+ * @param int[] $ids
+ *
+ * @throws AbortException
+ */
+ public function groupExportPatrols(array $ids): void
+ {
+ $this->sessionSection->patrolIds = $ids;
+ $this->redirect('exportpatrols');
+ }
+
+ /**
+ * Zpracuje export seznamu družin.
+ *
+ * @throws AbortException
+ * @throws Exception
+ */
+ public function handleExportPatrols(): void
+ {
+ $ids = $this->session->getSection('srs')->patrolIds;
+
+ $patrols = $this->patrolRepository->findPatrolsByIds($ids);
+
+ $response = $this->excelExportService->exportPatrolsList($patrols, 'seznam-druzin.xlsx');
+
+ $this->getPresenter()->sendResponse($response);
+ }
}
diff --git a/app/AdminModule/UsersModule/Components/TroopsGridControl.php b/app/AdminModule/UsersModule/Components/TroopsGridControl.php
index c71b7760d..ce4f71e96 100644
--- a/app/AdminModule/UsersModule/Components/TroopsGridControl.php
+++ b/app/AdminModule/UsersModule/Components/TroopsGridControl.php
@@ -9,10 +9,14 @@
use App\Model\User\Repositories\TroopRepository;
use App\Model\User\Troop;
use App\Services\CommandBus;
+use App\Services\ExcelExportService;
use App\Utils\Helpers;
use Doctrine\ORM\QueryBuilder;
+use Exception;
use Nette\Application\AbortException;
use Nette\Application\UI\Control;
+use Nette\Http\Session;
+use Nette\Http\SessionSection;
use Nette\Localization\Translator;
use Nette\Utils\Html;
use Throwable;
@@ -21,18 +25,22 @@
use Ublaboo\DataGrid\Exception\DataGridException;
use function count;
-use function date;
/**
* Komponenta pro zobrazení datagridu družin.
*/
class TroopsGridControl extends Control
{
+ private SessionSection $sessionSection;
+
public function __construct(
private CommandBus $commandBus,
private Translator $translator,
- private TroopRepository $troopRepository
+ private TroopRepository $troopRepository,
+ private ExcelExportService $excelExportService,
+ private Session $session
) {
+ $this->sessionSection = $session->getSection('srs');
}
/**
@@ -61,9 +69,8 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->setItemsPerPageList([25, 50, 100, 250, 500]);
$grid->setStrictSessionFilterValues(false);
- $stamp = date(Helpers::DATE_FORMAT);
- $grid->addExportCsv('admin.common.export_all', 'NSJ2023 Skupiny ' . $stamp . '.csv');
- $grid->addExportCsvFiltered('admin.common.export_filter', 'NSJ2023 Skupiny fi ' . $stamp . '.csv');
+ $grid->addGroupAction('Export seznamu skupin')
+ ->onSelect[] = [$this, 'groupExportTroops'];
$grid->addColumnText('name', 'Název')
->setSortable()
@@ -180,4 +187,34 @@ public function handleGeneratePaymentProof(int $id): void
{
$this->presenter->redirect(':Export:TroopIncomeProof:troop', ['id' => $id]);
}
+
+ /**
+ * Hromadně vyexportuje seznam skupin.
+ *
+ * @param int[] $ids
+ *
+ * @throws AbortException
+ */
+ public function groupExportTroops(array $ids): void
+ {
+ $this->sessionSection->troopIds = $ids;
+ $this->redirect('exporttroops');
+ }
+
+ /**
+ * Zpracuje export seznamu skupin.
+ *
+ * @throws AbortException
+ * @throws Exception
+ */
+ public function handleExportTroops(): void
+ {
+ $ids = $this->session->getSection('srs')->troopIds;
+
+ $troops = $this->troopRepository->findTroopsByIds($ids);
+
+ $response = $this->excelExportService->exportTroopsList($troops, 'seznam-skupin.xlsx');
+
+ $this->getPresenter()->sendResponse($response);
+ }
}
diff --git a/app/Model/User/Repositories/PatrolRepository.php b/app/Model/User/Repositories/PatrolRepository.php
index 2c18d0a6c..f7b8be640 100644
--- a/app/Model/User/Repositories/PatrolRepository.php
+++ b/app/Model/User/Repositories/PatrolRepository.php
@@ -6,6 +6,8 @@
use App\Model\Infrastructure\Repositories\AbstractRepository;
use App\Model\User\Patrol;
+use Doctrine\Common\Collections\Collection;
+use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\EntityManagerInterface;
/**
@@ -28,6 +30,19 @@ public function findByTroopAndNotConfirmed(int $troopId): ?Patrol
return $this->getRepository()->findOneBy(['troop' => $troopId, 'confirmed' => false]);
}
+ /**
+ * @param int[] $ids
+ *
+ * @return Collection
+ */
+ public function findPatrolsByIds(array $ids): Collection
+ {
+ $criteria = Criteria::create()
+ ->where(Criteria::expr()->in('id', $ids));
+
+ return $this->getRepository()->matching($criteria);
+ }
+
public function save(Patrol $patrol): void
{
$this->em->persist($patrol);
diff --git a/app/Services/ExcelExportService.php b/app/Services/ExcelExportService.php
index dafbcf619..7104e6e23 100644
--- a/app/Services/ExcelExportService.php
+++ b/app/Services/ExcelExportService.php
@@ -14,7 +14,9 @@
use App\Model\Program\Repositories\ProgramRepository;
use App\Model\Program\Room;
use App\Model\Structure\Repositories\SubeventRepository;
+use App\Model\User\Patrol;
use App\Model\User\Queries\UserAttendsProgramsQuery;
+use App\Model\User\Troop;
use App\Model\User\User;
use App\Utils\Helpers;
use Doctrine\Common\Collections\ArrayCollection;
@@ -518,6 +520,172 @@ public function exportUsersSubeventsAndCategories(Collection $users, string $fil
return new ExcelResponse($this->spreadsheet, $filename);
}
+ /**
+ * @param Collection $patrols
+ *
+ * @throws Exception
+ */
+ public function exportPatrolsList(Collection $patrols, string $filename): ExcelResponse
+ {
+ $sheet = $this->spreadsheet->getSheet(0);
+
+ $row = 1;
+ $column = 1;
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Název'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Skupina'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Datum založení'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Počet osob'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ foreach ($patrols as $patrol) {
+ $row++;
+ $column = 1;
+
+ $sheet->setCellValue([$column++, $row], $patrol->getName());
+
+ $sheet->setCellValue([$column++, $row], $patrol->getTroop()->getName());
+
+ $sheet->setCellValue([$column++, $row], $patrol->getTroop()->getApplicationDate() !== null ? $patrol->getTroop()->getApplicationDate()->format(Helpers::DATETIME_FORMAT) : '');
+
+ $sheet->setCellValue([$column++, $row], $patrol->getUsersRoles()->count());
+ }
+
+ return new ExcelResponse($this->spreadsheet, $filename);
+ }
+
+ /**
+ * @param Collection $troops
+ *
+ * @throws Exception
+ */
+ public function exportTroopsList(Collection $troops, string $filename): ExcelResponse
+ {
+ $sheet = $this->spreadsheet->getSheet(0);
+
+ $row = 1;
+ $column = 1;
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Název'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Stav'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Variabilní symbol'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Vedoucí'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(30);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('E-mail vedoucího'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(30);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Datum založení'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Cena'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(10);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Datum splatnosti'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Datum platby'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Kód jamoddílu'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(30);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Počet osob'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Počet rádců'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Počet dospělých'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Počet družin'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ foreach ($troops as $troop) {
+ $row++;
+ $column = 1;
+
+ $sheet->setCellValue([$column++, $row], $troop->getName());
+
+ $sheet->setCellValue([$column++, $row], $this->translator->translate('common.application_state.' . $troop->getState()));
+
+ $sheet->setCellValue([$column++, $row], $troop->getVariableSymbolText());
+
+ $sheet->setCellValue([$column++, $row], $troop->getLeader()->getDisplayName());
+
+ $sheet->setCellValue([$column++, $row], $troop->getLeader()->getEmail());
+
+ $sheet->setCellValue([$column++, $row], $troop->getApplicationDate() !== null ? $troop->getApplicationDate()->format(Helpers::DATETIME_FORMAT) : '');
+
+ $sheet->setCellValue([$column++, $row], $troop->getFee());
+
+ $sheet->setCellValue([$column++, $row], $troop->getMaturityDate() !== null ? $troop->getMaturityDate()->format(Helpers::DATE_FORMAT) : '');
+
+ $sheet->setCellValue([$column++, $row], $troop->getPaymentDate() !== null ? $troop->getPaymentDate()->format(Helpers::DATE_FORMAT) : '');
+
+ $sheet->setCellValue([$column++, $row], $troop->getPairingCode());
+
+ $sheet->setCellValue([$column++, $row], $troop->countUsersInRoles([Role::PATROL_LEADER, Role::LEADER, Role::ESCORT, Role::ATTENDEE]));
+
+ $sheet->setCellValue([$column++, $row], $troop->countUsersInRoles([Role::PATROL_LEADER]));
+
+ $sheet->setCellValue([$column++, $row], $troop->countUsersInRoles([Role::LEADER, Role::ESCORT]));
+
+ $sheet->setCellValue([$column++, $row], $troop->getConfirmedPatrols()->count());
+ }
+
+ return new ExcelResponse($this->spreadsheet, $filename);
+ }
+
/**
* @param Collection $blocks
*
From b2387c07318f5123b01d786e16a714f4d14c061b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Fri, 3 Mar 2023 23:21:19 +0100
Subject: [PATCH 36/41] =?UTF-8?q?Vyp=C3=ADnateln=C3=A1=20registrace=20(#95?=
=?UTF-8?q?8)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* disable registration
* config property
---
.../ConfigurationModule/Forms/SeminarFormFactory.php | 6 ++++++
app/Model/Settings/Settings.php | 5 +++++
.../Components/TroopApplicationContentControl.php | 3 +++
.../templates/troop_application_content.latte | 10 ++++++++++
4 files changed, 24 insertions(+)
diff --git a/app/AdminModule/ConfigurationModule/Forms/SeminarFormFactory.php b/app/AdminModule/ConfigurationModule/Forms/SeminarFormFactory.php
index 28c8957b1..0ebf16d1a 100644
--- a/app/AdminModule/ConfigurationModule/Forms/SeminarFormFactory.php
+++ b/app/AdminModule/ConfigurationModule/Forms/SeminarFormFactory.php
@@ -5,8 +5,10 @@
namespace App\AdminModule\ConfigurationModule\Forms;
use App\AdminModule\Forms\BaseFormFactory;
+use App\Model\Settings\Commands\SetSettingBoolValue;
use App\Model\Settings\Commands\SetSettingDateValue;
use App\Model\Settings\Commands\SetSettingStringValue;
+use App\Model\Settings\Queries\SettingBoolValueQuery;
use App\Model\Settings\Queries\SettingDateValueQuery;
use App\Model\Settings\Queries\SettingStringValueQuery;
use App\Model\Settings\Settings;
@@ -71,6 +73,8 @@ public function create(): Form
$seminarToDate->addRule([$this, 'validateSeminarToDate'], 'admin.configuration.seminar_to_date_before_from', [$seminarToDate, $seminarFromDate]);
$editRegistrationTo->addRule([$this, 'validateEditRegistrationTo'], 'admin.configuration.edit_registration_to_after_from', [$editRegistrationTo, $seminarFromDate]);
+ $form->addCheckbox('groupRegistrationAllowed', 'povolit registraci nových skupin');
+
$form->addSubmit('submit', 'admin.common.save');
$form->setDefaults([
@@ -78,6 +82,7 @@ public function create(): Form
'seminarFromDate' => $this->queryBus->handle(new SettingDateValueQuery(Settings::SEMINAR_FROM_DATE)),
'seminarToDate' => $this->queryBus->handle(new SettingDateValueQuery(Settings::SEMINAR_TO_DATE)),
'editRegistrationTo' => $this->queryBus->handle(new SettingDateValueQuery(Settings::EDIT_REGISTRATION_TO)),
+ 'groupRegistrationAllowed' => $this->queryBus->handle(new SettingBoolValueQuery(Settings::GROUP_REGISTRATION_ALLOWED)),
]);
$form->onSuccess[] = [$this, 'processForm'];
@@ -100,6 +105,7 @@ public function processForm(Form $form, stdClass $values): void
$this->commandBus->handle(new SetSettingDateValue(Settings::SEMINAR_FROM_DATE, $values->seminarFromDate));
$this->commandBus->handle(new SetSettingDateValue(Settings::SEMINAR_TO_DATE, $values->seminarToDate));
$this->commandBus->handle(new SetSettingDateValue(Settings::EDIT_REGISTRATION_TO, $values->editRegistrationTo));
+ $this->commandBus->handle(new SetSettingBoolValue(Settings::GROUP_REGISTRATION_ALLOWED, $values->groupRegistrationAllowed));
}
/**
diff --git a/app/Model/Settings/Settings.php b/app/Model/Settings/Settings.php
index 255cde18f..de5e9771d 100644
--- a/app/Model/Settings/Settings.php
+++ b/app/Model/Settings/Settings.php
@@ -234,6 +234,11 @@ class Settings
*/
public const CONTACT_FORM_GUESTS_ALLOWED = 'contact_form_guests_allowed';
+ /**
+ * Povolit registraci skupiny.
+ */
+ public const GROUP_REGISTRATION_ALLOWED = 'group_registration_allowed';
+
/**
* Název položky nastavení.
*/
diff --git a/app/WebModule/Components/TroopApplicationContentControl.php b/app/WebModule/Components/TroopApplicationContentControl.php
index 9d2482a73..7b5c3e228 100644
--- a/app/WebModule/Components/TroopApplicationContentControl.php
+++ b/app/WebModule/Components/TroopApplicationContentControl.php
@@ -7,6 +7,7 @@
use App\Model\Acl\Repositories\RoleRepository;
use App\Model\Acl\Role;
use App\Model\Cms\Dto\ContentDto;
+use App\Model\Settings\Queries\SettingBoolValueQuery;
use App\Model\Settings\Queries\SettingStringValueQuery;
use App\Model\Settings\Settings;
use App\Model\User\Commands\RegisterTroop;
@@ -79,6 +80,8 @@ public function render(?ContentDto $content = null): void
$dbuser = $this->userRepository->findById($user->id);
$template->dbuser = $dbuser;
+ $template->registrationAllowed = $this->queryBus->handle(new SettingBoolValueQuery(Settings::GROUP_REGISTRATION_ALLOWED));
+
$skautIsUserId = $dbuser->getSkautISUserId();
$skautIsRoles = $this->skautIsService->getUserRoles($skautIsUserId, self::$ALLOWED_ROLE_TYPES);
$template->skautIsRoles = $skautIsRoles;
diff --git a/app/WebModule/Components/templates/troop_application_content.latte b/app/WebModule/Components/templates/troop_application_content.latte
index f273ab25e..7b0a7d94d 100644
--- a/app/WebModule/Components/templates/troop_application_content.latte
+++ b/app/WebModule/Components/templates/troop_application_content.latte
@@ -46,6 +46,7 @@
+ {if $registrationAllowed || $troop->getState() !== 'draft'}
+ {else}
+
+
+
+ Registrace nové skupiny není v tuto chvíli povolena.
+
+
+
+ {/if}
{/if}
From d6ef49e2c0a55888bf117a73b5eb9c202116157b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Sat, 4 Mar 2023 00:21:57 +0100
Subject: [PATCH 37/41] =?UTF-8?q?Zapomenut=C3=A1=20migrace=20(#959)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* disable registration
* config property
* migration added
---
migrations/Version20230303221015.php | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 migrations/Version20230303221015.php
diff --git a/migrations/Version20230303221015.php b/migrations/Version20230303221015.php
new file mode 100644
index 000000000..540369ff8
--- /dev/null
+++ b/migrations/Version20230303221015.php
@@ -0,0 +1,27 @@
+abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
+
+ $this->addSql('INSERT INTO `settings` (`item`, `value`) VALUES (\'group_registration_allowed\', \'0\')');
+ }
+
+ public function down(Schema $schema): void
+ {
+ }
+}
From a0720477bb1f245a721ea985b33a7c5b4cbc68b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Sun, 9 Apr 2023 20:34:17 +0200
Subject: [PATCH 38/41] Oprava deploymentu (NSJ) (#964)
* deployment fix
* deployment fix
---
.github/workflows/deploy-dev.yml | 3 ++-
.github/workflows/deploy-manual.yml | 3 ++-
.github/workflows/deploy-nsj-dev.yml | 3 ++-
.github/workflows/deploy-staging.yml | 3 ++-
.github/workflows/release.yml | 2 +-
.github/workflows/test.yml | 2 +-
6 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml
index c7b7d3f3c..acb2121d5 100644
--- a/.github/workflows/deploy-dev.yml
+++ b/.github/workflows/deploy-dev.yml
@@ -12,7 +12,7 @@ jobs:
deploy:
name: "Deploy to test-srs.skauting.cz"
environment: test-srs.skauting.cz
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
container:
image: skaut/lebeda:8.1
env:
@@ -39,6 +39,7 @@ jobs:
DEPLOY_SSH_USERNAME: ${{ secrets.DEPLOY_SSH_USERNAME }}
steps:
- uses: actions/checkout@v3
+ - run: git config --global --add safe.directory '*'
# Copy & paste from https://github.com/actions/cache/blob/master/examples.md#php---composer
- name: Get composer cache
id: composer-cache
diff --git a/.github/workflows/deploy-manual.yml b/.github/workflows/deploy-manual.yml
index 0af3e4f7c..956b6a3b5 100644
--- a/.github/workflows/deploy-manual.yml
+++ b/.github/workflows/deploy-manual.yml
@@ -15,7 +15,7 @@ jobs:
deploy:
name: "Manual deploy to ${{ github.event.inputs.environment }}"
environment: ${{ github.event.inputs.environment }}
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
container:
image: skaut/lebeda:8.1
env:
@@ -42,6 +42,7 @@ jobs:
DEPLOY_SSH_USERNAME: ${{ secrets.DEPLOY_SSH_USERNAME }}
steps:
- uses: actions/checkout@v3
+ - run: git config --global --add safe.directory '*'
# Copy & paste from https://github.com/actions/cache/blob/master/examples.md#php---composer
- name: Get composer cache
id: composer-cache
diff --git a/.github/workflows/deploy-nsj-dev.yml b/.github/workflows/deploy-nsj-dev.yml
index a3fc6c2b3..9a00798a8 100644
--- a/.github/workflows/deploy-nsj-dev.yml
+++ b/.github/workflows/deploy-nsj-dev.yml
@@ -11,7 +11,7 @@ jobs:
deploy:
name: "Deploy to srs-dev.skauting.cz"
environment: srs-dev.skauting.cz
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
container:
image: skaut/lebeda:8.1
env:
@@ -38,6 +38,7 @@ jobs:
DEPLOY_SSH_USERNAME: ${{ secrets.DEPLOY_SSH_USERNAME }}
steps:
- uses: actions/checkout@v3
+ - run: git config --global --add safe.directory '*'
# Copy & paste from https://github.com/actions/cache/blob/master/examples.md#php---composer
- name: Get composer cache
id: composer-cache
diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml
index 16087a885..637294871 100644
--- a/.github/workflows/deploy-staging.yml
+++ b/.github/workflows/deploy-staging.yml
@@ -11,7 +11,7 @@ jobs:
deploy:
name: "Deploy to srs.skauting.cz"
environment: srs.skauting.cz
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
container:
image: skaut/lebeda:8.1
env:
@@ -38,6 +38,7 @@ jobs:
DEPLOY_SSH_USERNAME: ${{ secrets.DEPLOY_SSH_USERNAME }}
steps:
- uses: actions/checkout@v3
+ - run: git config --global --add safe.directory '*'
# Copy & paste from https://github.com/actions/cache/blob/master/examples.md#php---composer
- name: Get composer cache
id: composer-cache
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 00186b065..ad770853d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -7,7 +7,7 @@ on:
jobs:
package:
name: "Create release package"
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
container:
image: skaut/lebeda:8.1
steps:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 41ec20390..7914ed50b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -13,7 +13,7 @@ concurrency:
jobs:
workdir:
name: "Build"
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
container:
image: skaut/lebeda:8.1
steps:
From ab240b25be2454adede53c8b2b7dd924f6b300e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Tue, 11 Apr 2023 19:13:14 +0200
Subject: [PATCH 39/41] =?UTF-8?q?Export=20=C3=BA=C4=8Dastn=C3=ADk=C5=AF=20?=
=?UTF-8?q?NSJ=20(#965)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* attendees export
* skip leaders
* export all attendees
* export others
---
.../Components/UsersGridControl.php | 26 ++
.../Handlers/TroopsByStateQueryHandler.php | 26 ++
app/Model/User/Queries/TroopsByStateQuery.php | 17 ++
.../User/Repositories/TroopRepository.php | 8 +
app/Services/ExcelExportService.php | 241 ++++++++++++++++++
5 files changed, 318 insertions(+)
create mode 100644 app/Model/User/Queries/Handlers/TroopsByStateQueryHandler.php
create mode 100644 app/Model/User/Queries/TroopsByStateQuery.php
diff --git a/app/AdminModule/UsersModule/Components/UsersGridControl.php b/app/AdminModule/UsersModule/Components/UsersGridControl.php
index 3d8be9a29..1157aea43 100644
--- a/app/AdminModule/UsersModule/Components/UsersGridControl.php
+++ b/app/AdminModule/UsersModule/Components/UsersGridControl.php
@@ -23,6 +23,7 @@
use App\Model\Settings\Queries\SettingIntValueQuery;
use App\Model\Settings\Queries\SettingStringValueQuery;
use App\Model\Settings\Settings;
+use App\Model\User\Queries\TroopsByStateQuery;
use App\Model\User\Repositories\UserRepository;
use App\Model\User\User;
use App\Services\AclService;
@@ -157,6 +158,10 @@ public function createComponentUsersGrid(string $name): DataGrid
$grid->addGroupAction('Načíst členství ze skautIS (admin)')
->onSelect[] = [$this, 'groupUpdateMembership'];
+ $grid->addToolbarButton('exportNsjAttendees', 'Export NSJ - účastníci');
+
+ $grid->addToolbarButton('exportNsjOthers', 'Export NSJ - ostatní');
+
$grid->addColumnText('displayName', 'admin.users.users_name')
->setSortable()
->setFilterText();
@@ -868,6 +873,27 @@ public function handleExportSchedules(): void
$this->getPresenter()->sendResponse($response);
}
+ public function handleExportNsjAttendees(): void
+ {
+ $troops = $this->queryBus->handle(new TroopsByStateQuery(TroopApplicationState::PAID));
+
+ $response = $this->excelExportService->exportNsjAttendees($troops, 'nsj-ucastnici.xlsx');
+
+ $this->getPresenter()->sendResponse($response);
+ }
+
+ public function handleExportNsjOthers(): void
+ {
+ $nonRegisteredRole = $this->roleRepository->findBySystemName(Role::NONREGISTERED);
+ $users = $this->userRepository->findAll()
+ ->filter(static fn (User $u) => $u->getRoles()->count() > 0 && ! $u->isInRole($nonRegisteredRole))
+ ->toArray();
+
+ $response = $this->excelExportService->exportNsjOthers($users, 'nsj-ostatni.xlsx');
+
+ $this->getPresenter()->sendResponse($response);
+ }
+
/**
* Vrátí platební metody jako možnosti pro select. Bez prázdné možnosti.
*
diff --git a/app/Model/User/Queries/Handlers/TroopsByStateQueryHandler.php b/app/Model/User/Queries/Handlers/TroopsByStateQueryHandler.php
new file mode 100644
index 000000000..e0fb212f7
--- /dev/null
+++ b/app/Model/User/Queries/Handlers/TroopsByStateQueryHandler.php
@@ -0,0 +1,26 @@
+
+ */
+ public function __invoke(TroopsByStateQuery $query): Collection
+ {
+ return $this->troopRepository->findByState($query->getTroopState());
+ }
+}
diff --git a/app/Model/User/Queries/TroopsByStateQuery.php b/app/Model/User/Queries/TroopsByStateQuery.php
new file mode 100644
index 000000000..8bc2e0d4b
--- /dev/null
+++ b/app/Model/User/Queries/TroopsByStateQuery.php
@@ -0,0 +1,17 @@
+troopState;
+ }
+}
diff --git a/app/Model/User/Repositories/TroopRepository.php b/app/Model/User/Repositories/TroopRepository.php
index 5ca5ecc1d..a60e90dc3 100644
--- a/app/Model/User/Repositories/TroopRepository.php
+++ b/app/Model/User/Repositories/TroopRepository.php
@@ -40,6 +40,14 @@ public function findAll(): Collection
return new ArrayCollection($result);
}
+ /**
+ * @return Collection
+ */
+ public function findByState(string $state): Collection
+ {
+ return $this->getRepository()->matching(Criteria::create()->where(Criteria::expr()->eq('state', $state)));
+ }
+
/**
* @throws NonUniqueResultException
*/
diff --git a/app/Services/ExcelExportService.php b/app/Services/ExcelExportService.php
index 7104e6e23..c0990c1e1 100644
--- a/app/Services/ExcelExportService.php
+++ b/app/Services/ExcelExportService.php
@@ -32,6 +32,10 @@
use function implode;
use function preg_replace;
+use function str_pad;
+use function substr;
+
+use const STR_PAD_LEFT;
/**
* Služba pro export do formátu XLSX.
@@ -686,6 +690,243 @@ public function exportTroopsList(Collection $troops, string $filename): ExcelRes
return new ExcelResponse($this->spreadsheet, $filename);
}
+ /**
+ * @param Troop[] $troops
+ */
+ public function exportNsjAttendees($troops, string $filename): ExcelResponse
+ {
+ $sheet = $this->spreadsheet->getSheet(0);
+
+ $row = 1;
+ $column = 1;
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Jméno'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Příjmení'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Přezdívka'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Datum narození'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Adresa'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(50);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('E-mail'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(30);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Telefon'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Telefon matky'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Telefon otce'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('O mně'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Poznámka'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Zdravotní informace'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(50);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Role'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Skupina'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Družina'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Kód'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ foreach ($troops as $troop) {
+ $i = 0;
+
+ foreach ($troop->getUsersRoles() as $usersRole) {
+ $user = $usersRole->getUser();
+ $i++;
+
+ $row++;
+ $column = 1;
+
+ $sheet->setCellValue([$column++, $row], $user->getFirstName());
+ $sheet->setCellValue([$column++, $row], $user->getLastName());
+ $sheet->setCellValue([$column++, $row], $user->getNickName());
+ $sheet->setCellValue([$column++, $row], $user->getBirthdate()->format(Helpers::DATE_FORMAT));
+ $sheet->setCellValue([$column++, $row], $user->getAddress());
+ $sheet->setCellValue([$column++, $row], $user->getEmail());
+ $sheet->setCellValue([$column++, $row], $user->getPhone());
+ $sheet->setCellValue([$column++, $row], $user->getMotherPhone());
+ $sheet->setCellValue([$column++, $row], $user->getFatherPhone());
+ $sheet->setCellValue([$column++, $row], $user->getAbout());
+ $sheet->setCellValue([$column++, $row], $user->getNote());
+ $sheet->setCellValue([$column++, $row], $user->getHealthInfo());
+ $sheet->setCellValue([$column++, $row], $usersRole->getRole()->getName());
+ $sheet->setCellValue([$column++, $row], $troop->getName());
+ $sheet->setCellValue([$column++, $row], '');
+
+ $code = substr($troop->getVariableSymbolText(), -4) . '-00-'
+ . str_pad((string) $i, 2, '0', STR_PAD_LEFT);
+ $sheet->setCellValue([$column++, $row], $code);
+ }
+
+ foreach ($troop->getConfirmedPatrols() as $patrol) {
+ $i = 0;
+
+ foreach ($patrol->getUsersRoles() as $usersRole) {
+ $user = $usersRole->getUser();
+ $i++;
+
+ $row++;
+ $column = 1;
+
+ $sheet->setCellValue([$column++, $row], $user->getFirstName());
+ $sheet->setCellValue([$column++, $row], $user->getLastName());
+ $sheet->setCellValue([$column++, $row], $user->getNickName());
+ $sheet->setCellValue([$column++, $row], $user->getBirthdate()->format(Helpers::DATE_FORMAT));
+ $sheet->setCellValue([$column++, $row], $user->getAddress());
+ $sheet->setCellValue([$column++, $row], $user->getEmail());
+ $sheet->setCellValue([$column++, $row], $user->getPhone());
+ $sheet->setCellValue([$column++, $row], $user->getMotherPhone());
+ $sheet->setCellValue([$column++, $row], $user->getFatherPhone());
+ $sheet->setCellValue([$column++, $row], $user->getAbout());
+ $sheet->setCellValue([$column++, $row], $user->getNote());
+ $sheet->setCellValue([$column++, $row], $user->getHealthInfo());
+ $sheet->setCellValue([$column++, $row], $usersRole->getRole()->getName());
+ $sheet->setCellValue([$column++, $row], $troop->getName());
+ $sheet->setCellValue([$column++, $row], $patrol->getName());
+
+ $code = substr($troop->getVariableSymbolText(), -4) . '-'
+ . substr($patrol->getName(), -2) . '-'
+ . str_pad((string) $i, 2, '0', STR_PAD_LEFT);
+ $sheet->setCellValue([$column++, $row], $code);
+ }
+ }
+ }
+
+ return new ExcelResponse($this->spreadsheet, $filename);
+ }
+
+ /**
+ * @param User[] $users
+ */
+ public function exportNsjOthers($users, string $filename): ExcelResponse
+ {
+ $sheet = $this->spreadsheet->getSheet(0);
+
+ $row = 1;
+ $column = 1;
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Jméno'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Příjmení'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Přezdívka'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Datum narození'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Adresa'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(50);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('E-mail'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(30);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('O mně'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Poznámka'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Role'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Kód'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+
+ foreach ($users as $user) {
+ $row++;
+ $column = 1;
+
+ $sheet->setCellValue([$column++, $row], $user->getFirstName());
+ $sheet->setCellValue([$column++, $row], $user->getLastName());
+ $sheet->setCellValue([$column++, $row], $user->getNickName());
+ $sheet->setCellValue([$column++, $row], $user->getBirthdate()->format(Helpers::DATE_FORMAT));
+ $sheet->setCellValue([$column++, $row], $user->getAddress());
+ $sheet->setCellValue([$column++, $row], $user->getEmail());
+ $sheet->setCellValue([$column++, $row], $user->getAbout());
+ $sheet->setCellValue([$column++, $row], $user->getNote());
+ $sheet->setCellValue([$column++, $row], $user->getRolesText());
+ $sheet->setCellValue([$column++, $row], substr($user->getRolesApplication()?->getVariableSymbolText() ?: '', -4));
+ }
+
+ return new ExcelResponse($this->spreadsheet, $filename);
+ }
+
/**
* @param Collection $blocks
*
From 71ac4bf703a989719d624021d57293a0fe06a789 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Wed, 12 Apr 2023 19:41:00 +0200
Subject: [PATCH 40/41] =?UTF-8?q?Export=20vlastn=C3=ADch=20pol=C3=AD=20pro?=
=?UTF-8?q?=20NSJ=20(#966)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* skip leaders
* custom inputs export
* rebase fix
---
app/Services/ExcelExportService.php | 49 +++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/app/Services/ExcelExportService.php b/app/Services/ExcelExportService.php
index c0990c1e1..f47fbcc1f 100644
--- a/app/Services/ExcelExportService.php
+++ b/app/Services/ExcelExportService.php
@@ -908,6 +908,36 @@ public function exportNsjOthers($users, string $filename): ExcelResponse
$sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
$sheet->getColumnDimensionByColumn($column++)->setWidth(15);
+ foreach ($this->customInputRepository->findAllOrderedByPosition() as $customInput) {
+ switch ($customInput->getType()) {
+ case CustomInput::TEXT:
+ case CustomInput::SELECT:
+ case CustomInput::MULTISELECT:
+ case CustomInput::DATETIME:
+ $width = 30;
+ break;
+
+ case CustomInput::DATE:
+ $width = 20;
+ break;
+
+ case CustomInput::CHECKBOX:
+ $width = 15;
+ break;
+
+ case CustomInput::FILE:
+ continue 2;
+
+ default:
+ throw new InvalidArgumentException();
+ }
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate($customInput->getName()));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth($width);
+ }
+
foreach ($users as $user) {
$row++;
$column = 1;
@@ -922,6 +952,25 @@ public function exportNsjOthers($users, string $filename): ExcelResponse
$sheet->setCellValue([$column++, $row], $user->getNote());
$sheet->setCellValue([$column++, $row], $user->getRolesText());
$sheet->setCellValue([$column++, $row], substr($user->getRolesApplication()?->getVariableSymbolText() ?: '', -4));
+
+ foreach ($this->customInputRepository->findAllOrderedByPosition() as $customInput) {
+ $customInputValue = $user->getCustomInputValue($customInput);
+
+ if ($customInputValue === null) {
+ $column++;
+ continue;
+ }
+
+ if ($customInputValue instanceof CustomCheckboxValue) {
+ $value = $customInputValue->getValue()
+ ? $this->translator->translate('common.export.common.yes')
+ : $this->translator->translate('common.export.common.no');
+ } else {
+ $value = $customInputValue->getValueText();
+ }
+
+ $sheet->setCellValue([$column++, $row], $value);
+ }
}
return new ExcelResponse($this->spreadsheet, $filename);
From 3c7d3a430db1dc55377d36b56711656b58f39875 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Stan=C4=9Bk?=
Date: Thu, 13 Apr 2023 19:09:16 +0200
Subject: [PATCH 41/41] =?UTF-8?q?Export=20skupin=20pro=20NSJ,=20deduplikac?=
=?UTF-8?q?e=20exportu=20u=C5=BEivatel=C5=AF=20(#967)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* export group skautis ids
* ignore phpstan bug
* ignore phpstan bug
---
.../Components/TroopsGridControl.php | 15 ++++
app/Services/ExcelExportService.php | 85 +++++++++++++++++--
phpstan.neon | 3 +
3 files changed, 98 insertions(+), 5 deletions(-)
diff --git a/app/AdminModule/UsersModule/Components/TroopsGridControl.php b/app/AdminModule/UsersModule/Components/TroopsGridControl.php
index ce4f71e96..bdc1bffb4 100644
--- a/app/AdminModule/UsersModule/Components/TroopsGridControl.php
+++ b/app/AdminModule/UsersModule/Components/TroopsGridControl.php
@@ -5,11 +5,14 @@
namespace App\AdminModule\UsersModule\Components;
use App\Model\Acl\Role;
+use App\Model\Enums\TroopApplicationState;
use App\Model\User\Commands\RemoveTroop;
+use App\Model\User\Queries\TroopsByStateQuery;
use App\Model\User\Repositories\TroopRepository;
use App\Model\User\Troop;
use App\Services\CommandBus;
use App\Services\ExcelExportService;
+use App\Services\QueryBus;
use App\Utils\Helpers;
use Doctrine\ORM\QueryBuilder;
use Exception;
@@ -34,6 +37,7 @@ class TroopsGridControl extends Control
private SessionSection $sessionSection;
public function __construct(
+ private QueryBus $queryBus,
private CommandBus $commandBus,
private Translator $translator,
private TroopRepository $troopRepository,
@@ -72,6 +76,8 @@ public function createComponentPatrolsGrid(string $name): DataGrid
$grid->addGroupAction('Export seznamu skupin')
->onSelect[] = [$this, 'groupExportTroops'];
+ $grid->addToolbarButton('exportNsjTroops', 'Export NSJ - skupiny');
+
$grid->addColumnText('name', 'Název')
->setSortable()
->setFilterText();
@@ -217,4 +223,13 @@ public function handleExportTroops(): void
$this->getPresenter()->sendResponse($response);
}
+
+ public function handleExportNsjTroops(): void
+ {
+ $troops = $this->queryBus->handle(new TroopsByStateQuery(TroopApplicationState::PAID));
+
+ $response = $this->excelExportService->exportNsjTroops($troops, 'nsj-skupiny.xlsx');
+
+ $this->getPresenter()->sendResponse($response);
+ }
}
diff --git a/app/Services/ExcelExportService.php b/app/Services/ExcelExportService.php
index f47fbcc1f..6557dfd98 100644
--- a/app/Services/ExcelExportService.php
+++ b/app/Services/ExcelExportService.php
@@ -690,6 +690,51 @@ public function exportTroopsList(Collection $troops, string $filename): ExcelRes
return new ExcelResponse($this->spreadsheet, $filename);
}
+ /**
+ * @param Collection $troops
+ *
+ * @throws Exception
+ */
+ public function exportNsjTroops(Collection $troops, string $filename): ExcelResponse
+ {
+ $sheet = $this->spreadsheet->getSheet(0);
+
+ $row = 1;
+ $column = 1;
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Název'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Vedoucí - jméno'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(30);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Vedoucí - userId'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ $sheet->setCellValue([$column, $row], $this->translator->translate('Vedoucí - personId'));
+ $sheet->getStyle([$column, $row])->getFont()->setBold(true);
+ $sheet->getColumnDimensionByColumn($column)->setAutoSize(false);
+ $sheet->getColumnDimensionByColumn($column++)->setWidth(20);
+
+ foreach ($troops as $troop) {
+ $row++;
+ $column = 1;
+
+ $sheet->setCellValue([$column++, $row], $troop->getName());
+ $sheet->setCellValue([$column++, $row], $troop->getLeader()->getDisplayName());
+ $sheet->setCellValue([$column++, $row], $troop->getLeader()->getSkautISUserId());
+ $sheet->setCellValue([$column++, $row], $troop->getLeader()->getSkautISPersonId());
+ }
+
+ return new ExcelResponse($this->spreadsheet, $filename);
+ }
+
/**
* @param Troop[] $troops
*/
@@ -811,6 +856,22 @@ public function exportNsjAttendees($troops, string $filename): ExcelResponse
$sheet->setCellValue([$column++, $row], $code);
}
+ $allUsers = new ArrayCollection();
+ $duplicitUsers = new ArrayCollection();
+ $exportedUsers = new ArrayCollection();
+
+ foreach ($troop->getConfirmedPatrols() as $patrol) {
+ foreach ($patrol->getUsersRoles() as $usersRole) {
+ $userId = $usersRole->getUser()->getId();
+
+ if ($allUsers->contains($userId)) {
+ $duplicitUsers->add($userId);
+ } else {
+ $allUsers->add($userId);
+ }
+ }
+ }
+
foreach ($troop->getConfirmedPatrols() as $patrol) {
$i = 0;
@@ -818,6 +879,12 @@ public function exportNsjAttendees($troops, string $filename): ExcelResponse
$user = $usersRole->getUser();
$i++;
+ if ($exportedUsers->contains($user->getId())) {
+ continue;
+ }
+
+ $exportedUsers->add($user->getId());
+
$row++;
$column = 1;
@@ -835,12 +902,20 @@ public function exportNsjAttendees($troops, string $filename): ExcelResponse
$sheet->setCellValue([$column++, $row], $user->getHealthInfo());
$sheet->setCellValue([$column++, $row], $usersRole->getRole()->getName());
$sheet->setCellValue([$column++, $row], $troop->getName());
- $sheet->setCellValue([$column++, $row], $patrol->getName());
- $code = substr($troop->getVariableSymbolText(), -4) . '-'
- . substr($patrol->getName(), -2) . '-'
- . str_pad((string) $i, 2, '0', STR_PAD_LEFT);
- $sheet->setCellValue([$column++, $row], $code);
+ if ($duplicitUsers->contains($user->getId())) {
+ $sheet->setCellValue([$column++, $row], '');
+
+ $code = substr($troop->getVariableSymbolText(), -4) . '-00-00';
+ $sheet->setCellValue([$column++, $row], $code);
+ } else {
+ $sheet->setCellValue([$column++, $row], $patrol->getName());
+
+ $code = substr($troop->getVariableSymbolText(), -4) . '-'
+ . substr($patrol->getName(), -2) . '-'
+ . str_pad((string) $i, 2, '0', STR_PAD_LEFT);
+ $sheet->setCellValue([$column++, $row], $code);
+ }
}
}
}
diff --git a/phpstan.neon b/phpstan.neon
index 333e1067c..6ca9b53a3 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -14,6 +14,9 @@ parameters:
-
message: '#^Parameter \#1 \$translator of method Ublaboo\\DataGrid\\DataGrid::setTranslator\(\) expects Nette\\Localization\\ITranslator, Nette\\Localization\\Translator given.$#'
path: app/*/*GridControl.php
+ -
+ message: '#^If condition is always false.$#'
+ path: app/Services/ExcelExportService.php
services:
- class: CodeQuality\ObjectIdentityComparisonRule