From b10a567336156924cb22e85586a5b44341fcbc57 Mon Sep 17 00:00:00 2001 From: Fabio Ventura Date: Sat, 25 Sep 2021 22:04:58 +0200 Subject: [PATCH 1/7] #160 sort account by usage in movement form select --- .../Repository/AccountRepository.php | 19 +++++++++++++++++++ .../src/MoneyLog/Form/MovementForm.php | 7 ++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/module/Application/src/Application/Repository/AccountRepository.php b/module/Application/src/Application/Repository/AccountRepository.php index 74cc90bd..bcf3e2ff 100644 --- a/module/Application/src/Application/Repository/AccountRepository.php +++ b/module/Application/src/Application/Repository/AccountRepository.php @@ -70,4 +70,23 @@ public function getTotals(int $userId, bool $onlyRecap = false, $date = null): a return $qb->orderBy('total', 'DESC')->groupBy('a.id')->getQuery()->getResult(); } + + /** + * @param int $userId + * @return array + */ + public function getByUsage(int $userId): array + { + $qb = $this->getEntityManager()->createQueryBuilder() + ->select('a') + ->from(Account::class, 'a') + ->leftJoin('a.movements', 'm') + ->where('a.user=:userId') + ->andWhere('a.closed=:closed') + ->groupBy('m.account') + ->orderBy('COUNT(m.account)', 'DESC') + ->setParameters([':closed' => false, ':userId' => $userId]); + + return $qb->getQuery()->getResult(); + } } diff --git a/module/MoneyLog/src/MoneyLog/Form/MovementForm.php b/module/MoneyLog/src/MoneyLog/Form/MovementForm.php index 18e66a15..e4f71bb4 100644 --- a/module/MoneyLog/src/MoneyLog/Form/MovementForm.php +++ b/module/MoneyLog/src/MoneyLog/Form/MovementForm.php @@ -82,11 +82,8 @@ public function __construct(string $name, EntityManagerInterface $em, int $userI 'disable_inarray_validator' => true, 'display_empty_item' => false, 'find_method' => [ - 'name' => 'findBy', - 'params' => [ - 'criteria' => ['user' => $userId, 'closed' => false], - 'orderBy' => ['name' => 'ASC'] - ] + 'name' => 'getByUsage', + 'params' => ['userId' => $userId], ], 'label' => 'Conto', 'object_manager' => $this->em, From c6ccdd27f59ea115b6ecb31c5642c064fe42b394 Mon Sep 17 00:00:00 2001 From: Fabio Ventura Date: Sun, 26 Sep 2021 13:21:00 +0200 Subject: [PATCH 2/7] #159 add EnumAccountStatusType --- composer.json | 2 + config/autoload/global.php | 11 +++- data/Migrations/Version20210926172529.php | 41 +++++++++++++ .../src/Application/Entity/Account.php | 35 +++++------ .../Repository/AccountRepository.php | 26 +++++---- .../Repository/MovementRepository.php | 16 +++-- .../Application/test/Entity/AccountTest.php | 14 ++--- .../Auth/src/Auth/Form/RegistrationForm.php | 13 ----- .../MoneyLog/Controller/AccountController.php | 29 ++++------ .../Controller/MovementController.php | 2 +- .../src/MoneyLog/Form/AccountForm.php | 40 ++++--------- .../MoneyLog/Form/Filter/AccountFilter.php | 10 +--- module/MoneyLog/test/Form/AccountFormTest.php | 2 +- .../MoneyLog/view/money-log/account/add.phtml | 46 +++++++-------- .../view/money-log/account/edit.phtml | 58 +++++++++---------- .../view/money-log/account/index.phtml | 35 ++++++----- 16 files changed, 196 insertions(+), 184 deletions(-) create mode 100644 data/Migrations/Version20210926172529.php diff --git a/composer.json b/composer.json index 86d601e1..b19c3c87 100644 --- a/composer.json +++ b/composer.json @@ -83,6 +83,8 @@ "migrations-diff": "vendor/bin/doctrine-module migrations:diff", "migrations-migrate": "vendor/bin/doctrine-module migrations:migrate", "migrations-status": "vendor/bin/doctrine-module migrations:status", + "phpstan-analyze": "vendor/bin/phpstan analyze", + "phpstan-clear-cache": "rm -rf $(php -r 'echo sys_get_temp_dir() . \"/phpstan\";')", "schema-create": "vendor/bin/doctrine-module orm:schema-tool:create", "schema-drop": "vendor/bin/doctrine-module orm:schema-tool:drop --force" } diff --git a/config/autoload/global.php b/config/autoload/global.php index 8312f0db..aa2b1703 100644 --- a/config/autoload/global.php +++ b/config/autoload/global.php @@ -11,6 +11,13 @@ return [ 'db' => ['driver' => 'Pdo'], 'doctrine' => [ + 'connection' => [ + 'orm_default' => [ + 'doctrine_type_mappings' => [ + 'enum' => 'string', + ], + ], + ], // migrations configuration 'migrations_configuration' => [ 'orm_default' => [ @@ -22,7 +29,9 @@ ], ], 'service_manager' => [ - 'factories' => ['Laminas\Db\Adapter\Adapter' => 'Laminas\Db\Adapter\AdapterServiceFactory'], + 'factories' => [ + 'Laminas\Db\Adapter\Adapter' => 'Laminas\Db\Adapter\AdapterServiceFactory' + ], ], 'session_config' => [ 'cookie_lifetime' => 3600, // Session cookie will expire in 1 hour. diff --git a/data/Migrations/Version20210926172529.php b/data/Migrations/Version20210926172529.php new file mode 100644 index 00000000..a2b59c04 --- /dev/null +++ b/data/Migrations/Version20210926172529.php @@ -0,0 +1,41 @@ +abortIf( + $this->connection->getDatabasePlatform()->getName() !== 'mysql', + 'Migration can only be executed safely on \'mysql\'.' + ); + + $this->addSql('ALTER TABLE account ADD status ENUM(\'closed\', \'open\', \'highlight\') NOT NULL DEFAULT \'open\''); + + $this->addSql('UPDATE account SET status=\'HIGHLIGHT\' WHERE recap=1'); + $this->addSql('UPDATE account SET status=\'CLOSED\' WHERE closed=1'); + + $this->addSql('ALTER TABLE account DROP recap, DROP closed'); + } + + public function down(Schema $schema): void + { + $this->abortIf( + $this->connection->getDatabasePlatform()->getName() !== 'mysql', + 'Migration can only be executed safely on \'mysql\'.' + ); + + $this->addSql('ALTER TABLE account ADD recap INT NOT NULL, ADD closed TINYINT(1) DEFAULT \'0\' NOT NULL'); + + $this->addSql('UPDATE account SET recap=1 WHERE status=\'highlight\''); + $this->addSql('UPDATE account SET closed=1 WHERE status=\'closed\''); + + $this->addSql('ALTER TABLE account DROP status'); + } +} diff --git a/module/Application/src/Application/Entity/Account.php b/module/Application/src/Application/Entity/Account.php index 2cf12126..3c05498f 100644 --- a/module/Application/src/Application/Entity/Account.php +++ b/module/Application/src/Application/Entity/Account.php @@ -14,6 +14,10 @@ */ class Account { + public const STATUS_OPEN = 'open'; + public const STATUS_CLOSED = 'closed'; + public const STATUS_HIGHLIGHT = 'highlight'; + /** * @ORM\Id * @ORM\Column(name="id", type="integer", options={"unsigned"=true}); @@ -34,26 +38,20 @@ class Account private string $name; /** - * @ORM\Column(type="integer", nullable=false) - */ - private int $recap; - - /** - * @ORM\Column(name="closed", type="boolean", nullable=false, options={"default": false}) + * @ORM\Column(type="string", columnDefinition="ENUM('closed', 'open', 'highlight') NOT NULL DEFAULT 'open'") */ - private bool $closed; + private string $status; /** * @ORM\OneToMany(targetEntity="Movement", mappedBy="account") */ private Collection $movements; - public function __construct(User $user, string $name, int $recap = 0, bool $closed = false) + public function __construct(User $user, string $name, string $status = self::STATUS_OPEN) { $this->user = $user; $this->name = $name; - $this->recap = $recap; - $this->closed = $closed; + $this->status = $status; $this->movements = new ArrayCollection(); } @@ -77,23 +75,18 @@ public function getUser(): User return $this->user; } - public function isClosed(): bool - { - return $this->closed; - } - - public function setClosed(bool $closed): void + public function setStatus(string $status): void { - $this->closed = $closed; + $this->status = $status; } - public function getRecap(): int + public function getStatus(): string { - return $this->recap; + return $this->status; } - public function setRecap(int $recap): void + public function getMovements(): Collection { - $this->recap = $recap; + return $this->movements; } } diff --git a/module/Application/src/Application/Repository/AccountRepository.php b/module/Application/src/Application/Repository/AccountRepository.php index bcf3e2ff..d8a01b00 100644 --- a/module/Application/src/Application/Repository/AccountRepository.php +++ b/module/Application/src/Application/Repository/AccountRepository.php @@ -15,7 +15,7 @@ class AccountRepository extends EntityRepository public function getUserAccountBalances(int $userId): array { $qb = $this->getEntityManager()->createQueryBuilder() - ->select('a.id', 'a.name', 'a.recap', 'COALESCE(SUM(m.amount), 0) AS balance') + ->select('a.id', 'a.name', 'a.status', 'COALESCE(SUM(m.amount), 0) AS balance') ->from(Account::class, 'a') ->join('a.movements', 'm', Join::WITH, 'a.user=:userId') ->where("a.closed=:closed") @@ -27,10 +27,10 @@ public function getUserAccountBalances(int $userId): array /** * @param int $userId - * @param bool $onlyRecap + * @param bool $onlyHighlight * @return array */ - public function getUserAccounts(int $userId, bool $onlyRecap = false): array + public function getUserAccounts(int $userId, bool $onlyHighlight = false): array { $qb = $this->createQueryBuilder('a') ->select('a') @@ -38,8 +38,10 @@ public function getUserAccounts(int $userId, bool $onlyRecap = false): array ->orderBy('a.name', 'ASC') ->setParameter(':userId', $userId); - if ($onlyRecap) { - $qb->andWhere('a.recap=1'); + if ($onlyHighlight) { + $qb->andWhere('a.status=:status')->setParameter(':status', Account::STATUS_HIGHLIGHT); + } else { + $qb->andWhere('a.status<>:status')->setParameter(':status', Account::STATUS_CLOSED); } return $qb->getQuery()->getResult(); @@ -47,14 +49,14 @@ public function getUserAccounts(int $userId, bool $onlyRecap = false): array /** * @param int $userId - * @param bool $onlyRecap + * @param bool $onlyHighlight * @param \DateTime|null|string $date * @return array */ - public function getTotals(int $userId, bool $onlyRecap = false, $date = null): array + public function getTotals(int $userId, bool $onlyHighlight = false, $date = null): array { $qb = $this->getEntityManager()->createQueryBuilder() - ->select('a.id', 'a.name', 'a.recap', 'a.closed', 'COALESCE(SUM(m.amount), 0) AS total') + ->select('a.id', 'a.name', 'a.status', 'COALESCE(SUM(m.amount), 0) AS total') ->from(Account::class, 'a') ->leftJoin('a.movements', 'm') ->where("a.user=$userId"); @@ -64,8 +66,8 @@ public function getTotals(int $userId, bool $onlyRecap = false, $date = null): a ->setParameter(':date', $date instanceof \DateTime ? $date->format('Y-m-d') : $date); } - if ($onlyRecap) { - $qb->andWhere('a.recap=1'); + if ($onlyHighlight) { + $qb->andWhere('a.status=:status')->setParameter(':status', Account::STATUS_HIGHLIGHT); } return $qb->orderBy('total', 'DESC')->groupBy('a.id')->getQuery()->getResult(); @@ -82,10 +84,10 @@ public function getByUsage(int $userId): array ->from(Account::class, 'a') ->leftJoin('a.movements', 'm') ->where('a.user=:userId') - ->andWhere('a.closed=:closed') + ->andWhere('a.status<>:status') ->groupBy('m.account') ->orderBy('COUNT(m.account)', 'DESC') - ->setParameters([':closed' => false, ':userId' => $userId]); + ->setParameters([':status' => Account::STATUS_CLOSED, ':userId' => $userId]); return $qb->getQuery()->getResult(); } diff --git a/module/Application/src/Application/Repository/MovementRepository.php b/module/Application/src/Application/Repository/MovementRepository.php index 34b0fe3c..e4aad30a 100644 --- a/module/Application/src/Application/Repository/MovementRepository.php +++ b/module/Application/src/Application/Repository/MovementRepository.php @@ -4,6 +4,7 @@ namespace Application\Repository; +use Application\Entity\Account; use Application\Entity\Movement; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; @@ -87,18 +88,21 @@ public function getTotalExpense(int $userId): float */ public function getMovementByDay(int $userId, string $minDate, string $maxDate): array { - // SELECT date, SUM(amount) AS amount FROM movement INNER JOIN account ON movement.accountId=account.id - // WHERE userId=1 AND recap=1 AND date >= '2019-02-10' AND date<='2019-02-25' GROUP BY date $qb = $this->getEntityManager() ->createQueryBuilder() ->select('m.date, SUM(m.amount) AS amount') ->from(Movement::class, 'm') ->innerJoin('m.account', 'a') - ->where("a.user=$userId AND m.amount < 0 AND a.recap=1 AND m.date BETWEEN :minDate AND :maxDate") - ->setParameter(':minDate', $minDate) - ->setParameter(':maxDate', $maxDate) + ->where("a.user=:userId") + ->andWhere('m.amount < 0 AND a.status=:status AND m.date BETWEEN :minDate AND :maxDate') ->groupBy('m.date') - ->orderBy('m.date'); + ->orderBy('m.date') + ->setParameters([ + ':maxDate' => $maxDate, + ':minDate' => $minDate, + ':status' => Account::STATUS_HIGHLIGHT, + ':userId' => $userId, + ]); return $qb->getQuery()->getResult(); } diff --git a/module/Application/test/Entity/AccountTest.php b/module/Application/test/Entity/AccountTest.php index e492d088..27d91559 100644 --- a/module/Application/test/Entity/AccountTest.php +++ b/module/Application/test/Entity/AccountTest.php @@ -4,6 +4,7 @@ use Application\Entity\Account; use Application\Entity\User; +use Doctrine\Common\Collections\Collection; use PHPUnit\Framework\TestCase; class AccountTest extends TestCase @@ -29,19 +30,16 @@ public function testSetterAndGetter(): void self::assertSame($user, $account->getUser()); self::assertSame($name, $account->getName()); - self::assertSame(0, $account->getRecap()); - self::assertSame(false, $account->isClosed()); + self::assertSame(Account::STATUS_OPEN, $account->getStatus()); $name = 'test2'; $account->setName($name); self::assertSame($name, $account->getName()); - $recap = 1; - $account->setRecap($recap); - self::assertSame($recap, $account->getRecap()); + $status = Account::STATUS_CLOSED; + $account->setStatus($status); + self::assertSame($status, $account->getStatus()); - $closed = true; - $account->setClosed($closed); - self::assertSame($closed, $account->isClosed()); + self::assertInstanceOf(Collection::class, $account->getMovements()); } } diff --git a/module/Auth/src/Auth/Form/RegistrationForm.php b/module/Auth/src/Auth/Form/RegistrationForm.php index 6d65633a..f45cfd52 100644 --- a/module/Auth/src/Auth/Form/RegistrationForm.php +++ b/module/Auth/src/Auth/Form/RegistrationForm.php @@ -56,19 +56,6 @@ public function __construct() ], ]); -// $this->add(array( -// 'type' => 'Laminas\Form\Element\Captcha', -// 'name' => 'captcha', -// 'options' => array( -// 'label' => 'Please verify you are human', -// 'captcha' => new \Laminas\Captcha\ReCaptcha(array( -// 'pubKey' => '6LdjpBIUAAAAAGDwoUOVWGSZSudz1U2EYWDOafD7', -// 'privKey' => '6LdjpBIUAAAAANHJptlPZIB5Ty3kXKPS6tmSbc1j', -// )), -// 'class' => 'form-control', -// ), -// )); - $this->add([ 'name' => 'submit', 'attributes' => [ diff --git a/module/MoneyLog/src/MoneyLog/Controller/AccountController.php b/module/MoneyLog/src/MoneyLog/Controller/AccountController.php index 3593159f..4eed5a16 100644 --- a/module/MoneyLog/src/MoneyLog/Controller/AccountController.php +++ b/module/MoneyLog/src/MoneyLog/Controller/AccountController.php @@ -46,7 +46,7 @@ public function addAction() /** @var array $data */ $data = $form->getData(); - $account = new Account($user, $data['name'], $data['recap'], false); + $account = new Account($user, $data['name']); $this->em->persist($account); $this->em->flush(); @@ -68,20 +68,18 @@ public function indexAction(): ViewModel $accountAvailable = $accountRepository->getTotals($this->user->getId(), false, new \DateTime()); foreach ($accountAvailable as $i) { - $data[$i['id']]['id'] = $i['id']; - $data[$i['id']]['name'] = $i['name']; - $data[$i['id']]['closed'] = $i['closed']; - $data[$i['id']]['recap'] = $i['recap']; + $data[$i['id']]['id'] = $i['id']; + $data[$i['id']]['name'] = $i['name']; + $data[$i['id']]['status'] = $i['status']; $data[$i['id']]['available'] = $i['total']; } - $accountBalances = $accountRepository->getTotals($this->user->getId(), false); + $accountBalances = $accountRepository->getTotals($this->user->getId(), false); foreach ($accountBalances as $i) { - $data[$i['id']]['id'] = $i['id']; - $data[$i['id']]['name'] = $i['name']; - $data[$i['id']]['closed'] = $i['closed']; - $data[$i['id']]['recap'] = $i['recap']; - $data[$i['id']]['balance'] = $i['total']; + $data[$i['id']]['id'] = $i['id']; + $data[$i['id']]['name'] = $i['name']; + $data[$i['id']]['status'] = $i['status']; + $data[$i['id']]['balance'] = $i['total']; } return new ViewModel(['rows' => $data]); } @@ -101,11 +99,7 @@ public function editAction() } $form = new AccountForm(); - $form->setData([ - 'name' => $account->getName(), - 'recap' => $account->getRecap(), - 'closed' => $account->isClosed(), - ]); + $form->setData(['name' => $account->getName(), 'status' => $account->getStatus()]); $request = $this->getRequest(); if ($request->isPost()) { @@ -119,8 +113,7 @@ public function editAction() $validatedData = $form->getData(); $account->setName($validatedData['name']); - $account->setRecap($validatedData['recap']); - $account->setClosed((bool) $validatedData['closed']); + $account->setStatus($validatedData['status']); $this->em->flush(); return $this->redirect()->toRoute('accantonaAccount'); // Redirect to list diff --git a/module/MoneyLog/src/MoneyLog/Controller/MovementController.php b/module/MoneyLog/src/MoneyLog/Controller/MovementController.php index 78343ce5..280605f1 100644 --- a/module/MoneyLog/src/MoneyLog/Controller/MovementController.php +++ b/module/MoneyLog/src/MoneyLog/Controller/MovementController.php @@ -206,7 +206,7 @@ public function moveAction() $accountOptions = ['' => '']; foreach ($accountRepository->getUserAccounts($this->user->getId()) as $account) { - if ($account->getId() != $sourceAccount->getId() && !$account->isClosed()) { + if ($account->getId() != $sourceAccount->getId() && $account->getStatus() !== Account::STATUS_CLOSED) { $accountOptions[$account->getId()] = $account->getName(); } } diff --git a/module/MoneyLog/src/MoneyLog/Form/AccountForm.php b/module/MoneyLog/src/MoneyLog/Form/AccountForm.php index 8a92012b..a4fa5c0f 100644 --- a/module/MoneyLog/src/MoneyLog/Form/AccountForm.php +++ b/module/MoneyLog/src/MoneyLog/Form/AccountForm.php @@ -4,6 +4,9 @@ namespace MoneyLog\Form; +use Application\Entity\Account; +use Laminas\Form\Element\Select; +use Laminas\Form\Element\Text; use Laminas\Form\Form; class AccountForm extends Form @@ -16,42 +19,23 @@ public function __construct($name = 'account') { parent::__construct($name); - $this->add([ - 'name' => 'id', - 'type' => 'Hidden', - ]); $this->add([ 'attributes' => ['type' => 'text', 'class' => 'form-control'], 'name' => 'name', 'options' => ['label' => 'Name'], - 'type' => 'Text', - ]); - $this->add([ - 'attributes' => [], - 'name' => 'recap', - 'options' => [ - 'label' => 'Includi nel riepilogo', - 'checked_value' => 1, - 'unchecked_value' => 0, - 'use_hidden_element' => true, - ], - 'type' => 'checkbox', + 'type' => Text::class, ]); $this->add([ - 'attributes' => [], - 'name' => 'closed', + 'name' => 'status', 'options' => [ - 'label' => 'Chiudi il conto', - 'checked_value' => 1, - 'unchecked_value' => 0, - 'use_hidden_element' => true, + 'label' => 'Stato', + 'value_options' => [ + Account::STATUS_CLOSED => 'Chiuso', + Account::STATUS_OPEN => 'Aperto', + Account::STATUS_HIGHLIGHT => 'In evidenza', + ], ], - 'type' => 'checkbox', - ]); - $this->add([ - 'attributes' => ['class' => 'btn btn-primary', 'id' => 'submitbutton', 'value' => 'Salva'], - 'name' => 'submit', - 'type' => 'Submit', + 'type' => Select::class, ]); } } diff --git a/module/MoneyLog/src/MoneyLog/Form/Filter/AccountFilter.php b/module/MoneyLog/src/MoneyLog/Form/Filter/AccountFilter.php index bc0afd53..23430a15 100644 --- a/module/MoneyLog/src/MoneyLog/Form/Filter/AccountFilter.php +++ b/module/MoneyLog/src/MoneyLog/Form/Filter/AccountFilter.php @@ -5,7 +5,6 @@ namespace MoneyLog\Form\Filter; use Laminas\Filter\StringTrim; -use Laminas\Filter\ToInt; use Laminas\InputFilter\InputFilter; class AccountFilter extends InputFilter @@ -18,15 +17,8 @@ public function __construct() 'filters' => [['name' => StringTrim::class]], ]); $this->add([ - 'name' => 'recap', + 'name' => 'status', 'required' => false, - 'filters' => [['name' => ToInt::class]], ]); - // with following filter the validation fails -// $this->inputFilter->add([ -// 'name' => 'closed', -// 'required' => false, -// 'filters' => [['name' => Boolean::class]], -// ]); } } diff --git a/module/MoneyLog/test/Form/AccountFormTest.php b/module/MoneyLog/test/Form/AccountFormTest.php index 3395c72e..916b4bb6 100644 --- a/module/MoneyLog/test/Form/AccountFormTest.php +++ b/module/MoneyLog/test/Form/AccountFormTest.php @@ -13,6 +13,6 @@ public static function testElements(): void { $form = new AccountForm(); - self::assertCount(5, $form->getElements()); + self::assertCount(2, $form->getElements()); } } diff --git a/module/MoneyLog/view/money-log/account/add.phtml b/module/MoneyLog/view/money-log/account/add.phtml index c1179aca..759bedbc 100644 --- a/module/MoneyLog/view/money-log/account/add.phtml +++ b/module/MoneyLog/view/money-log/account/add.phtml @@ -10,31 +10,31 @@ $form->setAttribute('action', $this->url('accantonaAccount', array('action' => ' $form->prepare(); ?> pageHeader($title); ?> -form()->openTag($form); ?> -formHidden($form->get('id')); ?> +
+
+
+
+ +
+
+ form()->openTag($form); ?> - get('name'); ?> - formElementErrors($element); ?> -
- formLabel($element); ?> - formInput($element); ?> - -
+ get('name'); ?> + formElementErrors($element); ?> +
+ formLabel($element); ?> + formInput($element); ?> + +
- get('recap'); ?> - formElementErrors($element); ?> -
- - -
+ - +
+ +
- get('submit'); ?> - formElementErrors($element); ?> -
- formInput($element); ?> - + form()->closeTag(); ?> +
+
- -form()->closeTag(); ?> +
diff --git a/module/MoneyLog/view/money-log/account/edit.phtml b/module/MoneyLog/view/money-log/account/edit.phtml index 3bb2b6a7..e24317d7 100644 --- a/module/MoneyLog/view/money-log/account/edit.phtml +++ b/module/MoneyLog/view/money-log/account/edit.phtml @@ -10,36 +10,36 @@ $form->setAttribute('action', $this->url('accantonaAccount', array('action' => ' $form->prepare(); ?> pageHeader($title); ?> -form()->openTag($form); ?> -formHidden($form->get('id')); ?> - - get('name'); ?> - formElementErrors($element); ?> -
- formLabel($element); ?> - formInput($element); ?> - -
+
+
- get('recap'); ?> - formElementErrors($element); ?> -
- - -
- get('closed'); ?> - formElementErrors($element); ?> -
- - -
+
- get('submit'); ?> - formElementErrors($element); ?> -
- formInput($element); ?> - -
+
+ +
+
+ form()->openTag($form); ?> + + get('name'); ?> + formElementErrors($element); ?> +
+ formLabel($element); ?> + formInput($element); ?> + +
-form()->closeTag(); ?> + sbaFormRow($form->get('status')); ?> + +
+ +
+ + form()->closeTag(); ?> +
+
+ + +
+
diff --git a/module/MoneyLog/view/money-log/account/index.phtml b/module/MoneyLog/view/money-log/account/index.phtml index e31d3d53..10110603 100644 --- a/module/MoneyLog/view/money-log/account/index.phtml +++ b/module/MoneyLog/view/money-log/account/index.phtml @@ -6,11 +6,16 @@ $dataChart = []; $totalAvailable = 0; $totalBalance = 0; +$stateMap = [ + \Application\Entity\Account::STATUS_CLOSED => 'Chiuso', + \Application\Entity\Account::STATUS_OPEN => 'Aperto', + \Application\Entity\Account::STATUS_HIGHLIGHT => 'In evidenza', +]; ?> pageHeader('Conti'); ?> +
- -
+
Lista conti @@ -21,7 +26,6 @@ $totalBalance = 0; Nome Stato - Includi nel riepilogo Saldo disponibile Saldo contabile Operazioni @@ -33,8 +37,8 @@ $totalBalance = 0; if (!empty($row['available']) && $row['available'] > 0) { $dataChart[] = array($row['name'], (float) $row['available']); } - $totalAvailable += isset($row['available']) ? $row['available'] : 0; - $totalBalance += isset($row['balance']) ? $row['balance'] : 0; + $totalAvailable += $row['available'] ?? 0; + $totalBalance += $row['balance'] ?? 0; ?> @@ -42,10 +46,9 @@ $totalBalance = 0; escapeHtml($row['name']); ?> - - - currencyForma($row['available']); ?> - currencyForma($row['balance']); ?> + + currencyForma($row['available'] ?? 0); ?> + currencyForma($row['balance'] ?? 0); ?> @@ -70,7 +73,7 @@ $totalBalance = 0; - Totale + Totale currencyForma($totalAvailable); ?> currencyForma($totalBalance); ?> @@ -80,18 +83,22 @@ $totalBalance = 0;
-
+
+
+
Ripartizione conti
- +
+ +
- Nessun conto definito, clicca sul tasto Aggiungi per crearne uno. + Nessun conto definito, clicca sul tasto Aggiungi per crearne uno.
-
+ balanceModalForm(0, 'accantonaAccount'); $js = <<< js From b3193de3124252f33dcedd3bf39cd9bfd6673d1f Mon Sep 17 00:00:00 2001 From: Fabio Ventura Date: Wed, 29 Sep 2021 21:32:14 +0200 Subject: [PATCH 3/7] fix account repository and movement controller --- .../src/Application/Repository/AccountRepository.php | 4 +--- .../MoneyLog/src/MoneyLog/Controller/MovementController.php | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/module/Application/src/Application/Repository/AccountRepository.php b/module/Application/src/Application/Repository/AccountRepository.php index d8a01b00..2ca0f1f2 100644 --- a/module/Application/src/Application/Repository/AccountRepository.php +++ b/module/Application/src/Application/Repository/AccountRepository.php @@ -17,9 +17,7 @@ public function getUserAccountBalances(int $userId): array $qb = $this->getEntityManager()->createQueryBuilder() ->select('a.id', 'a.name', 'a.status', 'COALESCE(SUM(m.amount), 0) AS balance') ->from(Account::class, 'a') - ->join('a.movements', 'm', Join::WITH, 'a.user=:userId') - ->where("a.closed=:closed") - ->setParameters([':closed' => false, ':userId' => $userId]) + ->join('a.movements', 'm', Join::WITH, "a.user=$userId") ->groupBy('a.id'); return $qb->getQuery()->getResult(); diff --git a/module/MoneyLog/src/MoneyLog/Controller/MovementController.php b/module/MoneyLog/src/MoneyLog/Controller/MovementController.php index 280605f1..29e5f28a 100644 --- a/module/MoneyLog/src/MoneyLog/Controller/MovementController.php +++ b/module/MoneyLog/src/MoneyLog/Controller/MovementController.php @@ -54,7 +54,7 @@ public function indexAction(): ViewModel ]; return new ViewModel([ - 'accounts' => $accountRepository->findBy(['closed' => false, 'user' => $userId], ['name' => 'ASC']), + 'accounts' => $accountRepository->findBy(['user' => $userId], ['name' => 'ASC']), 'balances' => $accountRepository->getUserAccountBalances($userId), 'categories' => $categoryRepository->getUserCategories($userId), 'page' => $page, From c2e0735af00c1f527542b090f9773083344ca58d Mon Sep 17 00:00:00 2001 From: Fabio Ventura Date: Wed, 29 Sep 2021 22:10:18 +0200 Subject: [PATCH 4/7] add coverage report --- .githooks/pre-commit | 2 +- coverage.txt | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 coverage.txt diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 5b11b28f..e19d81c8 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -74,7 +74,7 @@ if [[ -n "$PHP_STAGED_FILES" ]]; then done fi - docker-compose exec web vendor/bin/phpunit < /dev/tty + docker-compose exec web vendor/bin/phpunit --coverage-text=coverage.txt < /dev/tty STATUS=$? if [[ "$STATUS" -eq 0 ]]; then diff --git a/coverage.txt b/coverage.txt new file mode 100644 index 00000000..cc69604b --- /dev/null +++ b/coverage.txt @@ -0,0 +1,9 @@ + + +Code Coverage Report: + 2021-09-29 19:50:50 + + Summary: + Classes: 50.82% (31/61) + Methods: 54.23% (109/201) + Lines: 33.97% (391/1151) From d834864142ff7bff7d5b0e4532ceec1e9dd2e783 Mon Sep 17 00:00:00 2001 From: Fabio Ventura Date: Thu, 30 Sep 2021 22:06:04 +0200 Subject: [PATCH 5/7] #162 add movment sorting --- module/Application/config/module.config.php | 1 + .../Repository/MovementRepository.php | 41 +++++++++++-------- .../src/Application/ViewHelper/SortLink.php | 40 ++++++++++++++++++ .../Controller/MovementController.php | 2 + .../view/money-log/movement/index.phtml | 8 ++-- 5 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 module/Application/src/Application/ViewHelper/SortLink.php diff --git a/module/Application/config/module.config.php b/module/Application/config/module.config.php index 064a065e..5f7d3c7e 100644 --- a/module/Application/config/module.config.php +++ b/module/Application/config/module.config.php @@ -91,6 +91,7 @@ 'pagination' => Application\ViewHelper\Pagination::class, 'richInlineScript' => Application\ViewHelper\RichInlineScript::class, 'sbaFormRow' => Application\ViewHelper\SbaFormRow::class, + 'sortLink' => Application\ViewHelper\SortLink::class, 'userData' => Application\ViewHelper\UserData::class, ], ], diff --git a/module/Application/src/Application/Repository/MovementRepository.php b/module/Application/src/Application/Repository/MovementRepository.php index e4aad30a..b04ac65c 100644 --- a/module/Application/src/Application/Repository/MovementRepository.php +++ b/module/Application/src/Application/Repository/MovementRepository.php @@ -113,42 +113,49 @@ public function getMovementByDay(int $userId, string $minDate, string $maxDate): */ private function getQuery(array $params): Query { - $cleanParams = ['user' => (int)$params['user']]; $qb = $this->getEntityManager() ->createQueryBuilder() ->select('m') ->from(Movement::class, 'm') - ->join('m.account', 'a', Join::WITH, 'a.user=:user'); + ->join('m.account', 'a', Join::WITH, 'a.user=:user') + ->setParameter('user', (int) $params['user']); if (!empty($params['account'])) { - $qb->andWhere('m.account = :account'); - $cleanParams['account'] = (int)$params['account']; + $qb->andWhere('m.account = :account')->setParameter('account', (int) $params['account']); } if (!empty($params['dateMin'])) { - $qb->andWhere('m.date >= :dateMin'); - $cleanParams['dateMin'] = $params['dateMin']; + $qb->andWhere('m.date >= :dateMin')->setParameter('dateMin', $params['dateMin']); } if (!empty($params['dateMax'])) { - $qb->andWhere('m.date <= :dateMax'); - $cleanParams['dateMax'] = $params['dateMax']; + $qb->andWhere('m.date <= :dateMax')->setParameter('dateMax', $params['dateMax']); } if (!empty($params['category'])) { - $qb->andWhere('m.category = :category'); - $cleanParams['category'] = (int)$params['category']; + $qb->andWhere('m.category = :category')->setParameter('category', (int) $params['category']); } if (!empty($params['description'])) { - $qb->andWhere('m.description LIKE :description'); - $cleanParams['description'] = '%' . $params['description'] . '%'; + $description = '%' . $params['description'] . '%'; + $qb->andWhere('m.description LIKE :description')->setParameter('description', $description); } if (is_numeric($params['amountMin'])) { - $qb->andWhere('m.amount >= :amountMin'); - $cleanParams['amountMin'] = (float)$params['amountMin']; + $qb->andWhere('m.amount >= :amountMin')->setParameter('amountMin', (float) $params['amountMin']); } if (is_numeric($params['amountMax'])) { - $qb->andWhere('m.amount <= :amountMax'); - $cleanParams['amountMax'] = (float)$params['amountMax']; + $qb->andWhere('m.amount <= :amountMax')->setParameter('amountMax', (float) $params['amountMax']); } - return $qb->setParameters($cleanParams)->orderBy('m.date', 'DESC')->getQuery(); + switch ($params['orderField']) { + case 'amount': + $qb->orderBy('m.amount', $params['orderType'] === 'DESC' ? 'DESC' : 'ASC'); + break; + case 'description': + $qb->orderBy('m.description', $params['orderType'] === 'DESC' ? 'DESC' : 'ASC'); + break; + case 'date': + default: + $qb->orderBy('m.date', $params['orderType'] === 'ASC' ? 'ASC' : 'DESC'); + break; + } + + return $qb->getQuery(); } } diff --git a/module/Application/src/Application/ViewHelper/SortLink.php b/module/Application/src/Application/ViewHelper/SortLink.php new file mode 100644 index 00000000..cc368f6a --- /dev/null +++ b/module/Application/src/Application/ViewHelper/SortLink.php @@ -0,0 +1,40 @@ + $queryData + * @return string + */ + public function __invoke(string $text, string $field, string $routeName, array $queryData): string + { + $content = $this->view->escapeHtml($text); + if ($queryData['orderField'] === $field) { + if (strcasecmp($queryData['orderType'], 'ASC') === 0) { + $icon = 'top'; + $orderType = 'DESC'; + } else { + $icon = 'bottom'; + $orderType = 'ASC'; + } + + $content .= " "; + } else { + $orderType = 'ASC'; + } + + $newQueryData = array_merge($queryData, ['orderField' => $field, 'orderType' => $orderType]); + $url = $this->view->url($routeName, [], ['query' => $newQueryData]); + return "
$content"; + } +} diff --git a/module/MoneyLog/src/MoneyLog/Controller/MovementController.php b/module/MoneyLog/src/MoneyLog/Controller/MovementController.php index 29e5f28a..bad95d62 100644 --- a/module/MoneyLog/src/MoneyLog/Controller/MovementController.php +++ b/module/MoneyLog/src/MoneyLog/Controller/MovementController.php @@ -51,6 +51,8 @@ public function indexAction(): ViewModel 'dateMax' => $params->fromQuery('dateMax', date('Y-m-d')), 'dateMin' => $params->fromQuery('dateMin', date('Y-m-d', strtotime('-3 months'))), 'description' => $params->fromQuery('description'), + 'orderField' => $params->fromQuery('orderField', 'date'), + 'orderType' => $params->fromQuery('orderType', 'DESC'), ]; return new ViewModel([ diff --git a/module/MoneyLog/view/money-log/movement/index.phtml b/module/MoneyLog/view/money-log/movement/index.phtml index bf965161..efefeb35 100644 --- a/module/MoneyLog/view/money-log/movement/index.phtml +++ b/module/MoneyLog/view/money-log/movement/index.phtml @@ -140,12 +140,12 @@ - + - - + + - + From e6d65e81338e1a972bccd094c829c014d7e90531 Mon Sep 17 00:00:00 2001 From: Fabio Ventura Date: Thu, 30 Sep 2021 23:16:10 +0200 Subject: [PATCH 6/7] #162 add SortLinkTest --- .../test/ViewHelper/SortLinkTest.php | 68 +++++++++++++++++++ phpstan.neon.dist | 1 + 2 files changed, 69 insertions(+) create mode 100644 module/Application/test/ViewHelper/SortLinkTest.php diff --git a/module/Application/test/ViewHelper/SortLinkTest.php b/module/Application/test/ViewHelper/SortLinkTest.php new file mode 100644 index 00000000..26db85d7 --- /dev/null +++ b/module/Application/test/ViewHelper/SortLinkTest.php @@ -0,0 +1,68 @@ +prophesize(PhpRenderer::class); + $viewProphesize + ->escapeHtml($text) + ->willReturn($text); + $viewProphesize + ->url($routeName, [], ["query" => ["orderField" => "date", "orderType" => "DESC"]]) + ->willReturn("/?orderField=$field&orderType=DESC"); + + $sortLink = new SortLink(); + + /** @var \Laminas\View\Renderer\RendererInterface */ + $view = $viewProphesize->reveal(); + $sortLink->setView($view); + + $url = ($sortLink)('Data', 'date', $routeName, ['orderField' => 'date', 'orderType' => 'ASC']); + + self::assertSame('Data ', $url); + } + + public function testSameFieldDesc(): void + { + $viewProphesize = $this->prophesize(PhpRenderer::class); + $sortLink = new SortLink(); + + /** @var \Laminas\View\Renderer\RendererInterface */ + $view = $viewProphesize->reveal(); + $sortLink->setView($view); + + $url = ($sortLink)('Data', 'date', 'routeName', ['orderField' => 'date', 'orderType' => 'DESC']); + + self::assertSame(' ', $url); + } + + public function testDifferentField(): void + { + $viewProphesize = $this->prophesize(PhpRenderer::class); + $sortLink = new SortLink(); + + /** @var \Laminas\View\Renderer\RendererInterface */ + $view = $viewProphesize->reveal(); + $sortLink->setView($view); + + $url = ($sortLink)('Data', 'date', 'routeName', ['orderField' => 'amount', 'orderType' => 'DESC']); + + self::assertSame('', $url); + } +} diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4eea141b..d0745210 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -16,6 +16,7 @@ parameters: - '/Call to an undefined method Laminas\\View\\Helper\\HeadTitle::setSeparator\(\)\./' - '/Call to an undefined method Laminas\\View\\Renderer\\PhpRenderer::\w+\(\)\./' - '/Call to an undefined method Laminas\\View\\Renderer\\RendererInterface::\w+\(\)\./' + - '/Call to an undefined method Prophecy\\Prophecy\\ObjectProphecy::\w+\(\)\./' - '/Call to an undefined method Traversable::count\(\)\./' - '/Method MoneyLog\\Controller\\\w+::\w+\(\) return type has no value type specified in iterable type Laminas\\View\\Model\\ViewModel\./' - '/PHPDoc tag @var for variable \$form has no value type specified in iterable type \w+\\Form\\\w+\./' From 966f07f58d7a53437ae5caed3ba1f0836696650b Mon Sep 17 00:00:00 2001 From: Fabio Ventura Date: Thu, 30 Sep 2021 23:17:27 +0200 Subject: [PATCH 7/7] #162 update coverage report --- coverage.txt | 133 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 4 deletions(-) diff --git a/coverage.txt b/coverage.txt index cc69604b..92e7a7f0 100644 --- a/coverage.txt +++ b/coverage.txt @@ -1,9 +1,134 @@ Code Coverage Report: - 2021-09-29 19:50:50 + 2021-09-30 21:16:19 Summary: - Classes: 50.82% (31/61) - Methods: 54.23% (109/201) - Lines: 33.97% (391/1151) + Classes: 51.61% (32/62) + Methods: 54.46% (110/202) + Lines: 34.53% (403/1167) + +Application\Controller\PageController + Methods: 100.00% ( 2/ 2) Lines: 100.00% ( 2/ 2) +Application\Entity\Account + Methods: 100.00% ( 8/ 8) Lines: 100.00% ( 14/ 14) +Application\Entity\Category + Methods: 100.00% ( 7/ 7) Lines: 100.00% ( 12/ 12) +Application\Entity\Movement + Methods: 100.00% (12/12) Lines: 100.00% ( 22/ 22) +Application\Entity\Provision + Methods: 100.00% ( 9/ 9) Lines: 100.00% ( 16/ 16) +Application\Entity\Setting + Methods: 100.00% ( 7/ 7) Lines: 100.00% ( 16/ 16) +Application\Entity\User + Methods: 100.00% (22/22) Lines: 100.00% ( 42/ 42) +Application\Repository\AccountRepository + Methods: 0.00% ( 0/ 4) Lines: 0.00% ( 0/ 16) +Application\Repository\CategoryRepository + Methods: 0.00% ( 0/ 4) Lines: 0.00% ( 0/ 21) +Application\Repository\MovementRepository + Methods: 0.00% ( 0/ 6) Lines: 0.00% ( 0/ 40) +Application\Repository\ProvisionRepository + Methods: 0.00% ( 0/ 3) Lines: 0.00% ( 0/ 23) +Application\ViewHelper\CommonJavascript + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 1/ 1) +Application\ViewHelper\Footer + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 4/ 4) +Application\ViewHelper\HelpTooltip + Methods: 0.00% ( 0/ 1) Lines: 0.00% ( 0/ 3) +Application\ViewHelper\Pagination + Methods: 0.00% ( 0/ 2) Lines: 0.00% ( 0/ 22) +Application\ViewHelper\RichInlineScript + Methods: 25.00% ( 1/ 4) Lines: 46.67% ( 7/ 15) +Application\ViewHelper\SbaFormRow + Methods: 0.00% ( 0/ 2) Lines: 0.00% ( 0/ 18) +Application\ViewHelper\SortLink + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 12/ 12) +Application\ViewHelper\UserData + Methods: 0.00% ( 0/ 4) Lines: 0.00% ( 0/ 6) +Auth\Controller\RegistrationController + Methods: 10.00% ( 1/10) Lines: 8.51% ( 8/ 94) +Auth\Controller\UserController + Methods: 20.00% ( 1/ 5) Lines: 15.71% ( 11/ 70) +Auth\Form\AuthForm + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 11/ 11) +Auth\Form\ChangePasswordForm + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 9/ 9) +Auth\Form\Filter\ChangePasswordFilter + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 11/ 11) +Auth\Form\Filter\LoginFilter + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 5/ 5) +Auth\Form\Filter\RegistrationFilter + Methods: 0.00% ( 0/ 1) Lines: 0.00% ( 0/ 3) +Auth\Form\Filter\UserFilter + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 5/ 5) +Auth\Form\ForgottenPasswordFilter + Methods: 0.00% ( 0/ 1) Lines: 0.00% ( 0/ 1) +Auth\Form\ForgottenPasswordForm + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 7/ 7) +Auth\Form\RegistrationForm + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 15/ 15) +Auth\Form\UserForm + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 7/ 7) +Auth\Model\LoggedUser + Methods: 100.00% ( 7/ 7) Lines: 100.00% ( 13/ 13) +Auth\Model\LoggedUserSettings + Methods: 100.00% ( 4/ 4) Lines: 100.00% ( 7/ 7) +Auth\Service\AuthAdapter + Methods: 25.00% ( 1/ 4) Lines: 8.00% ( 2/ 25) +Auth\Service\AuthManager + Methods: 33.33% ( 1/ 3) Lines: 22.22% ( 2/ 9) +Authorize\Acl\Acl + Methods: 66.67% ( 2/ 3) Lines: 82.76% ( 24/ 29) +MoneyLog\Controller\AccountController + Methods: 0.00% ( 0/ 6) Lines: 0.00% ( 0/ 76) +MoneyLog\Controller\CategoryController + Methods: 0.00% ( 0/ 5) Lines: 0.00% ( 0/ 46) +MoneyLog\Controller\DashboardController + Methods: 0.00% ( 0/ 2) Lines: 0.00% ( 0/ 46) +MoneyLog\Controller\MovementController + Methods: 0.00% ( 0/11) Lines: 0.00% ( 0/133) +MoneyLog\Controller\ProvisionController + Methods: 0.00% ( 0/ 5) Lines: 0.00% ( 0/ 43) +MoneyLog\Controller\SettingsController + Methods: 0.00% ( 0/ 2) Lines: 0.00% ( 0/ 18) +MoneyLog\Form\AccountForm + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 6/ 6) +MoneyLog\Form\CategoryForm + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 10/ 10) +MoneyLog\Form\Filter\AccountFilter + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 5/ 5) +MoneyLog\Form\Filter\CategoryFilter + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 5/ 5) +MoneyLog\Form\Filter\MovementFilter + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 11/ 11) +MoneyLog\Form\Filter\ProvisionFilter + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 7/ 7) +MoneyLog\Form\Filter\SettingFilter + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 7/ 7) +MoneyLog\Form\MoveForm + Methods: 100.00% ( 2/ 2) Lines: 100.00% ( 14/ 14) +MoneyLog\Form\MovementForm + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 28/ 28) +MoneyLog\Form\ProvisionForm + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 10/ 10) +MoneyLog\Form\SettingForm + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 8/ 8) +MoneyLog\View\Helper\BalanceModalForm + Methods: 0.00% ( 0/ 1) Lines: 0.00% ( 0/ 2) +MoneyLog\View\Helper\CurrencyForma + Methods: 0.00% ( 0/ 1) Lines: 0.00% ( 0/ 1) +MoneyLog\View\Helper\DataTable + Methods: 0.00% ( 0/ 1) Lines: 0.00% ( 0/ 2) +MoneyLog\View\Helper\DateForma + Methods: 0.00% ( 0/ 1) Lines: 0.00% ( 0/ 1) +MoneyLog\View\Helper\FloatingButtons + Methods: 25.00% ( 1/ 4) Lines: 26.67% ( 4/ 15) +MoneyLog\View\Helper\Morris + Methods: 0.00% ( 0/ 2) Lines: 0.00% ( 0/ 12) +MoneyLog\View\Helper\PageHeader + Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 3/ 3) +MoneyLog\View\Helper\SynopsisFilters + Methods: 0.00% ( 0/ 1) Lines: 0.00% ( 0/ 24) +MoneyLog\View\Helper\WidgetText + Methods: 0.00% ( 0/ 1) Lines: 0.00% ( 0/ 8)
DatasortLink('Data', 'date', 'accantonaMovement', $searchParams); ?> ContoImportoDescrizionesortLink('Importo', 'amount', 'accantonaMovement', $searchParams); ?>sortLink('Descrizione', 'description', 'accantonaMovement', $searchParams); ?> CategoriaAzioni