Failed Conditions
Push — master ( 7e6aaf...81a376 )
by Adrien
03:56
created

AccountRepositoryTest::testGetOrCreate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 26
rs 9.6333
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ApplicationTest\Repository;
6
7
use Application\Enum\AccountType;
8
use Application\Model\Account;
9
use Application\Model\User;
10
use Application\Repository\AccountRepository;
11
use ApplicationTest\Traits\LimitedAccessSubQuery;
12
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
13
use Money\Money;
14
15
class AccountRepositoryTest extends AbstractRepositoryTest
16
{
17
    use LimitedAccessSubQuery;
18
19
    private AccountRepository $repository;
20
21
    protected function setUp(): void
22
    {
23
        parent::setUp();
24
        $this->repository = $this->getEntityManager()->getRepository(Account::class);
25
    }
26
27
    public function providerGetAccessibleSubQuery(): iterable
28
    {
29
        $all = range(10000, 10107);
30
        yield ['anonymous', []];
31
        yield ['bookingonly', []];
32
        yield ['individual', [10096]];
33
        yield ['member', [10096]];
34
        yield ['formationresponsible', $all];
35
        yield ['responsible', $all];
36
        yield ['administrator', $all];
37
    }
38
39
    public function testOneUserCanHaveOnlyOneAccount(): void
40
    {
41
        $this->expectException(UniqueConstraintViolationException::class);
42
        $this->getEntityManager()->getConnection()->insert('account', ['owner_id' => 1000, 'iban' => uniqid()]);
43
    }
44
45
    public function testGetOrCreate(): void
46
    {
47
        $user = new User();
48
        $user->setFirstName('Foo');
49
        $user->setLastName('Bar');
50
51
        $account = $this->repository->getOrCreate($user);
52
53
        self::assertSame($user, $account->getOwner());
54
        self::assertSame('Foo Bar', $account->getName());
55
        self::assertSame(AccountType::Liability, $account->getType());
56
        self::assertSame(20300010, $account->getCode());
57
        self::assertSame('Acomptes de clients', $account->getParent()->getName());
58
        self::assertSame($account, $user->getAccount());
59
60
        $account2 = $this->repository->getOrCreate($user);
61
        self::assertSame($account, $account2, 'should return the same account if called more than once for same user');
62
63
        $user2 = new User();
64
        $user2->setFirstName('Alice');
65
        $user2->setLastName('Stark');
66
67
        $account3 = $this->repository->getOrCreate($user2);
68
        self::assertNotSame($account, $account3, 'creating a second account for a second user should not give same code');
69
        self::assertNotSame($account->getCode(), $account3->getCode(), 'creating a second account for a second user should not give same code');
70
        self::assertSame(20300011, $account3->getCode(), 'should have been incremented from maxCode in memory');
71
    }
72
73
    public function testGetOrCreateForTheFirstTime(): void
74
    {
75
        $this->getEntityManager()->getConnection()->executeQuery('DELETE FROM transaction');
76
        $this->getEntityManager()->getConnection()->executeQuery('DELETE FROM account WHERE code LIKE "20300%"');
77
        $user = new User();
78
79
        $account = $this->repository->getOrCreate($user);
80
        self::assertSame(20300001, $account->getCode());
81
    }
82
83
    public function testGetOrCreateInMemory(): void
84
    {
85
        $user = new User();
86
        $account = new Account();
87
        $account->setOwner($user);
88
89
        $actualAccount = $this->repository->getOrCreate($user);
90
91
        self::assertSame($account, $actualAccount, 'should return the in-memory account if existing');
92
    }
93
94
    public function testTotalBalance(): void
95
    {
96
        $totalAssets = $this->repository->totalBalanceByType(AccountType::Asset);
97
        $totalLiabilities = $this->repository->totalBalanceByType(AccountType::Liability);
98
        $totalRevenue = $this->repository->totalBalanceByType(AccountType::Revenue);
99
        $totalExpense = $this->repository->totalBalanceByType(AccountType::Expense);
100
        $totalEquity = $this->repository->totalBalanceByType(AccountType::Equity);
101
102
        self::assertTrue(Money::CHF(3518750)->equals($totalAssets));
103
        self::assertTrue(Money::CHF(6000)->equals($totalLiabilities));
104
        self::assertTrue(Money::CHF(24000)->equals($totalRevenue));
105
        self::assertTrue(Money::CHF(11250)->equals($totalExpense));
106
        self::assertTrue(Money::CHF(3500000)->equals($totalEquity));
107
108
        $groupAccount = $this->repository->getOneById(10001); // 2. Passifs
109
        self::assertSame(AccountType::Group, $groupAccount->getType(), 'is a group');
110
        self::assertTrue(Money::CHF(0)->equals($groupAccount->getBalance()), 'balance for group account is always 0');
111
        self::assertTrue(Money::CHF(3506000)->equals($groupAccount->getTotalBalance()), 'total balance for group account should have been computed via DB triggers');
112
113
        $otherAccount = $this->repository->getOneById(10025); // 10201. PostFinance
114
        self::assertNotSame(AccountType::Group, $otherAccount->getType(), 'not a group');
115
        self::assertTrue(Money::CHF(818750)->equals($otherAccount->getBalance()), 'balance for non-group should have been computed via DB triggers');
116
        self::assertTrue($otherAccount->getBalance()->equals($otherAccount->getTotalBalance()), 'total balance for non-group should be equal to balance');
117
    }
118
119
    public function testGetOneById(): void
120
    {
121
        $account = $this->repository->getOneById(10025); // Poste
122
        self::assertNotNull($account);
123
        self::assertSame(10025, $account->getId());
124
        $this->expectExceptionMessage('Account #-9999 not found');
125
        $this->repository->getOneById(-9999);
126
    }
127
128
    public function testDeleteAccountOfNonFamilyOwnerWithoutAnyTransactions(): void
129
    {
130
        self::assertSame(0, $this->repository->deleteAccountOfNonFamilyOwnerWithoutAnyTransactions(), 'nothing should be deleted from fixture data');
131
        $connection = $this->getEntityManager()->getConnection();
132
133
        $connection->insert('account', [
134
            'code' => '999001',
135
        ]);
136
        self::assertSame(0, $this->repository->deleteAccountOfNonFamilyOwnerWithoutAnyTransactions(), 'orphan account without any owner should not be deleted');
137
138
        $connection->insert('account', [
139
            'code' => '999003',
140
            'owner_id' => '1008',
141
        ]);
142
        self::assertSame(1, $this->repository->deleteAccountOfNonFamilyOwnerWithoutAnyTransactions(), 'account of son (not family owner) without any transactionLine should be deleted');
143
        self::assertSame(0, $this->repository->deleteAccountOfNonFamilyOwnerWithoutAnyTransactions(), 'nothing left to delete');
144
145
        $connection->insert('account', [
146
            'code' => '999003',
147
            'owner_id' => '1008',
148
        ]);
149
        $accountId = $connection->lastInsertId();
150
151
        $connection->insert('transaction_line', [
152
            'transaction_id' => 8000,
153
            'credit_id' => $accountId,
154
        ]);
155
        $transactionLineId = $connection->lastInsertId();
156
        self::assertSame(0, $this->repository->deleteAccountOfNonFamilyOwnerWithoutAnyTransactions(), 'same as before, but with transaction to credit, should not delete');
157
158
        // Delete temp records
159
        $connection->delete('transaction_line', ['id' => $transactionLineId]);
160
        $connection->delete('account', ['id' => $accountId]);
161
162
        $connection->insert('account', [
163
            'code' => '999003',
164
            'owner_id' => '1008',
165
        ]);
166
        $id = $connection->lastInsertId();
167
        $connection->insert('transaction_line', [
168
            'transaction_id' => 8000,
169
            'debit_id' => $id,
170
        ]);
171
        self::assertSame(0, $this->repository->deleteAccountOfNonFamilyOwnerWithoutAnyTransactions(), 'same as before, but with transaction to debit, should not delete');
172
    }
173
174
    /**
175
     * @dataProvider providerGetNextCode
176
     */
177
    public function testGetNextCode(?int $parentId, int $expected): void
178
    {
179
        $parent = $parentId ? $this->getEntityManager()->getReference(Account::class, $parentId) : null;
180
        $actual = $this->repository->getNextCodeAvailable($parent);
181
        self::assertSame($expected, $actual);
182
    }
183
184
    public function providerGetNextCode(): iterable
185
    {
186
        yield [null, 10];
187
        yield [10011, 20300010];
188
        yield [10007, 8511];
189
    }
190
191
    public function testNewAccountHasNoTransaction(): void
192
    {
193
        self::assertFalse($this->repository->hasTransaction(new Account()));
194
    }
195
196
    /**
197
     * @dataProvider providerHasTransaction
198
     */
199
    public function testHasTransaction(int $id, bool $expected): void
200
    {
201
        $account = $this->getEntityManager()->getReference(Account::class, $id);
202
203
        self::assertSame($expected, $this->repository->hasTransaction($account));
204
    }
205
206
    public function providerHasTransaction(): iterable
207
    {
208
        yield [10000, true];
209
        yield [10096, true];
210
        yield [10008, false];
211
    }
212
}
213