Failed Conditions
Push — master ( a0775a...0669e5 )
by Adrien
09:15
created

AccountRepository::clearCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Repository;
6
7
use Application\DBAL\Types\AccountTypeType;
8
use Application\Model\Account;
9
use Application\Model\User;
10
use Money\Money;
11
12
class AccountRepository extends AbstractRepository implements LimitedAccessSubQueryInterface
13
{
14
    private const PARENT_ACCOUNT_ID_FOR_USER = 10011;
15
    const ACCOUNT_ID_FOR_BANK = 10025;
16
17
    /**
18
     * Returns pure SQL to get ID of all objects that are accessible to given user.
19
     *
20
     * @param null|User $user
21
     *
22
     * @return string
23
     */
24 12
    public function getAccessibleSubQuery(?User $user): string
25
    {
26 12
        if (!$user) {
27 1
            return '-1';
28
        }
29
30 11
        if (in_array($user->getRole(), [User::ROLE_RESPONSIBLE, User::ROLE_ADMINISTRATOR], true)) {
31 2
            return $this->getAllIdsQuery();
32
        }
33
34 9
        return $this->getAllIdsForOwnerQuery($user);
35
    }
36
37
    /**
38
     * Unsecured way to get a account from its ID.
39
     *
40
     * This should only be used in tests or controlled environment.
41
     *
42
     * @param int $id
43
     *
44
     * @throws \Exception
45
     *
46
     * @return Account
47
     */
48 14
    public function getOneById(int $id): Account
49
    {
50
        $account = $this->getAclFilter()->runWithoutAcl(function () use ($id) {
51 14
            return $this->findOneById($id);
52 14
        });
53
54 14
        if (!$account) {
55 1
            throw new \Exception('Account #' . $id . ' not found');
56
        }
57
58 14
        return $account;
59
    }
60
61
    /**
62
     * This will return, and potentially create, an account for the given user
63
     *
64
     * @param User $user
65
     *
66
     * @return Account
67
     */
68 17
    public function getOrCreate(User $user): Account
69
    {
70
        // If an account already exists, because getOrCreate was called once before without flushing in between,
71
        // then can return immediately
72 17
        if ($user->getAccount()) {
73 12
            return $user->getAccount();
74
        }
75
76
        $account = $this->getAclFilter()->runWithoutAcl(function () use ($user) {
77 8
            return $this->findOneByOwner($user);
0 ignored issues
show
Bug introduced by
The method findOneByOwner() does not exist on Application\Repository\AccountRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

77
            return $this->/** @scrutinizer ignore-call */ findOneByOwner($user);
Loading history...
78 8
        });
79
80 8
        if (!$account) {
81 8
            $account = new Account();
82 8
            $this->getEntityManager()->persist($account);
83 8
            $account->setOwner($user);
84 8
            $account->setType(AccountTypeType::LIABILITY);
85 8
            $account->setName($user->getName());
86
87
            // Find the next available account code, using the liability parent code as prefix
88 8
            $nextQuery = 'SELECT MAX(children.code)+1 from account parent, account children where parent.id=' . self::PARENT_ACCOUNT_ID_FOR_USER . ' and children.code LIKE CONCAT(parent.code, \'%\')';
89 8
            $nextCode = (int) $this->getEntityManager()->getConnection()->fetchColumn($nextQuery);
90 8
            $account->setCode($nextCode);
91
92 8
            $parent = $this->getOneById(self::PARENT_ACCOUNT_ID_FOR_USER);
93 8
            $account->setParent($parent);
94
        }
95
96 8
        return $account;
97
    }
98
99
    /**
100
     * Sum balance by account type
101
     *
102
     * @API\Input(type="AccountType")
103
     *
104
     * @param string $accountType
105
     *
106
     * @return Money
107
     */
108 1
    public function totalBalanceByType(string $accountType): Money
109
    {
110 1
        $qb = $this->getEntityManager()->getConnection()->createQueryBuilder()
111 1
            ->select('SUM(balance)')
112 1
            ->from($this->getClassMetadata()->getTableName())
113 1
            ->where('type = :type');
114
115 1
        $qb->setParameter('type', $accountType);
116
117 1
        $result = $qb->execute();
118
119 1
        return Money::CHF($result->fetchColumn());
0 ignored issues
show
Bug introduced by
It seems like $result->fetchColumn() can also be of type false; however, parameter $amount of Money\Money::CHF() does only seem to accept integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
        return Money::CHF(/** @scrutinizer ignore-type */ $result->fetchColumn());
Loading history...
120
    }
121
122
    /**
123
     * Update accounts' balance
124
     *
125
     * @param null|Account $account the account to update, or null for all accounts
126
     *
127
     * @throws \Doctrine\DBAL\DBALException
128
     */
129
    public function updateAccountBalance(?Account $account = null): void
130
    {
131
        $connection = $this->getEntityManager()->getConnection();
132
        $sql = 'CALL update_account_balance(?)';
133
134
        if ($account) {
135
            $connection->executeQuery($sql, [$account->getId()]);
136
        } else {
137
            foreach ($this->findAll() as $a) {
138
                $connection->executeQuery($sql, [$a->getId()]);
139
            }
140
        }
141
    }
142
143
    /**
144
     * Return the next available Account code
145
     *
146
     * @return int
147
     */
148
    public function getNextCodeAvailable(): int
149
    {
150
        $qb = _em()->getConnection()->createQueryBuilder()
151
            ->select('IFNULL(MAX(a.code) + 1, 1)')
152
            ->from('account', 'a');
153
154
        return (int) $qb->execute()->fetchColumn();
155
    }
156
}
157