Failed Conditions
Push — master ( cd6515...587c3f )
by Sylvain
14:03
created

AccountingReport::writeDataAssets()   B

Complexity

Conditions 9
Paths 73

Size

Total Lines 58
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 36
c 1
b 0
f 1
dl 0
loc 58
rs 8.0555
cc 9
nc 73
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Service\Exporter;
6
7
use Application\DBAL\Types\AccountTypeType;
8
use Application\Model\Account;
9
use Cake\Chronos\ChronosDate;
10
use Ecodev\Felix\Format;
11
use Money\Money;
12
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
13
use PhpOffice\PhpSpreadsheet\Style\Alignment;
14
use PhpOffice\PhpSpreadsheet\Style\Color;
15
use PhpOffice\PhpSpreadsheet\Style\Fill;
16
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
17
18
/**
19
 * @extends AbstractExcel<Account>
20
 *
21
 * @phpstan-type Data array{
22
 *     account: ?Account,
23
 *     code: int | '',
24
 *     name: string,
25
 *     depth: int,
26
 *     balance: Money,
27
 *     balancePrevious: ?Money,
28
 *     budgetAllowed: ?Money,
29
 *     budgetBalance: ?Money,
30
 *     format?: array,
31
 *     formatPrevious?: array,
32
 * }
33
 */
34
class AccountingReport extends AbstractExcel
35
{
36
    private ChronosDate $date;
37
38
    private bool $showBudget;
39
40
    /**
41
     * @var Data[]
42
     */
43
    private array $assets = [];
44
45
    private array $liabilities = [];
46
47
    private array $expenses = [];
48
49
    private array $revenues = [];
50
51
    private static array $balanceFormat = [
52
        'fill' => [
53
            'fillType' => Fill::FILL_SOLID,
54
            'startColor' => [
55
                'argb' => 'FFDDDDDD',
56
            ],
57
        ],
58
        'numberFormat' => [
59
            'formatCode' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1, // eg. 12'345.67
60
        ],
61
    ];
62
63
    private static array $columnWidth = [
64
        'accountCode' => 11,
65
        'accountName' => 35,
66
        'balance' => 12,
67
    ];
68
69
    /**
70
     * Row where the numerical data start.
71
     */
72
    protected int $initialRow;
73
74
    /**
75
     * Column where the numerical data start.
76
     */
77
    protected int $initialColumn;
78
79
    public function __construct(string $hostname, private readonly array $accountingConfig)
80
    {
81
        parent::__construct($hostname);
82
83
        $this->date = ChronosDate::today();
84
85
        $this->sheet->setTitle('Bilan + PP');
86
        $this->zebra = false;
87
        $this->autoFilter = false;
88
    }
89
90
    protected function getTitleForFilename(): string
91
    {
92
        return sprintf('compta_rapport_%s', $this->date->format('Y-m-d'));
93
    }
94
95
    public function setDate(ChronosDate $date): void
96
    {
97
        $this->date = $date;
98
    }
99
100
    public function showBudget(bool $showBudget): void
101
    {
102
        $this->showBudget = $showBudget;
103
    }
104
105
    protected function writeTitle(): void
106
    {
107
        $this->column = 1;
108
        $this->sheet->mergeCells([$this->column, $this->row, $this->column + 14, $this->row]);
109
        $this->write(
110
            sprintf($this->hostname . ': rapport comptable au %s', $this->date->format('d.m.Y')),
111
            self::$titleFormat,
112
            self::$centerFormat
113
        );
114
        $this->sheet->getRowDimension($this->row)->setRowHeight(35);
115
        ++$this->row;
116
117
        $this->column = 1;
118
        $this->sheet->mergeCells([$this->column, $this->row, $this->column + 6, $this->row]);
119
        $this->write(
120
            'Bilan',
121
            self::$titleFormat,
122
            self::$centerFormat
123
        );
124
        $this->column = 9;
125
        $this->sheet->mergeCells([$this->column, $this->row, $this->column + 6, $this->row]);
126
        $this->write(
127
            'Résultat',
128
            self::$titleFormat,
129
            self::$centerFormat
130
        );
131
132
        $this->sheet->getRowDimension($this->row)->setRowHeight(35);
133
        ++$this->row;
134
    }
135
136
    /**
137
     * @param array<Account> $accounts
138
     */
139
    private function processAccounts(array $accounts, int $depth): void
140
    {
141
        foreach ($accounts as $account) {
142
            $balance = $account->getBalanceAtDate($this->date);
143
            if ($this->accountingConfig['report']['showAccountsWithZeroBalance'] === false && $depth > 1 && $balance->isZero()) {
144
                continue;
145
            }
146
            if ($account->getType() === AccountTypeType::EQUITY) {
147
                // Don't show special accounts since it's an interim statement, their balance will be computed manually
148
                continue;
149
            }
150
            $data = [
151
                'code' => $account->getCode(),
152
                'name' => Format::truncate($account->getName(), 55),
153
                'depth' => $depth,
154
                'balance' => $balance,
155
                'account' => $account,
156
            ];
157
            if ($this->showBudget) {
158
                $data['balancePrevious'] = $account->getTotalBalanceFormer();
159
                $data['budgetAllowed'] = $account->getBudgetAllowed();
160
                $data['budgetBalance'] = $account->getBudgetBalance();
161
            }
162
            if ($account->getType() === AccountTypeType::ASSET || ($account->getType() === AccountTypeType::GROUP && mb_substr((string) $account->getCode(), 0, 1) === '1')) {
163
                $this->assets[] = $data;
164
            } elseif ($account->getType() === AccountTypeType::LIABILITY || ($account->getType() === AccountTypeType::GROUP && mb_substr((string) $account->getCode(), 0, 1) === '2')) {
165
                $this->liabilities[] = $data;
166
            } elseif ($account->getType() === AccountTypeType::REVENUE || ($account->getType() === AccountTypeType::GROUP && mb_substr((string) $account->getCode(), 0, 1) === '3')) {
167
                $this->revenues[] = $data;
168
            } elseif ($account->getType() === AccountTypeType::EXPENSE || ($account->getType() === AccountTypeType::GROUP && in_array(mb_substr((string) $account->getCode(), 0, 1), ['4', '5', '6'], true))) {
169
                $this->expenses[] = $data;
170
            }
171
            if ($account->getType() === AccountTypeType::GROUP && $depth <= $this->accountingConfig['report']['maxAccountDepth'] && $account->getCode() !== $this->accountingConfig['customerDepositsAccountCode']) {
172
                $children = $account->getChildren()->toArray();
173
                $this->processAccounts($children, $depth + 1);
0 ignored issues
show
Bug introduced by
The method processAccounts() does not exist on Application\Service\Exporter\AccountingReport. Did you maybe mean processAccount()? ( Ignorable by Annotation )

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

173
                $this->/** @scrutinizer ignore-call */ 
174
                       processAccounts($children, $depth + 1);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
174
            }
175
        }
176
    }
177
178
    protected function writeDataAssets(): void
179
    {
180
        $this->column = $this->initialColumn = 1;
181
        $this->initialRow = $this->row;
182
        $firstLine = true;
183
        $this->lastDataRow = $this->row;
184
        foreach ($this->assets as $index => $data) {
185
            // Account code
186
            if ($firstLine) {
187
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['accountCode']);
188
            }
189
            $format = ['font' => ['bold' => $data['depth'] <= 2]];
190
            $this->write(
191
                str_repeat('  ', $data['depth'] - 1) . $data['code'],
192
                ['alignment' => ['horizontal' => Alignment::HORIZONTAL_LEFT, 'indent' => 1]],
193
                $format
194
            );
195
            // Account name
196
            if ($firstLine) {
197
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['accountName']);
198
            }
199
            $this->write($data['name'], ['alignment' => ['wrapText' => true]], $format, $data['format'] ?? []);
200
            // Account balance
201
            if ($firstLine) {
202
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']);
203
            }
204
            // Store the coordinate of the cell to later compute totals
205
            $this->assets[$index]['cell'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate();
206
            $this->write($data['balance'], self::$balanceFormat);
207
208
            if ($this->showBudget) {
209
                // Account balance former
210
                if ($firstLine) {
211
                    $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']);
212
                }
213
                $this->assets[$index]['balancePreviousCell'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate();
214
                $this->write($data['balancePrevious'], self::$balanceFormat);
215
216
                // Account budget allowed
217
                if ($firstLine) {
218
                    $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']);
219
                }
220
                $this->assets[$index]['budgetAllowedCell'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate();
221
                $this->write($data['budgetAllowed'], self::$balanceFormat);
222
223
                // Account budget balance
224
                if ($firstLine) {
225
                    $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']);
226
                }
227
                $this->assets[$index]['budgetBalanceCell'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate();
228
                $this->write($data['budgetBalance'], self::$balanceFormat);
229
            }
230
231
            $firstLine = false;
232
            ++$this->row;
233
            $this->column = $this->initialColumn;
0 ignored issues
show
Bug Best Practice introduced by
The property initialColumn does not exist on Application\Service\Exporter\AccountingReport. Did you maybe forget to declare it?
Loading history...
234
235
            $this->lastDataRow = max($this->lastDataRow, $this->row);
236
        }
237
    }
238
239
    protected function writeDataLiabilities(): void
240
    {
241
        $this->row = $this->initialRow;
0 ignored issues
show
Bug Best Practice introduced by
The property initialRow does not exist on Application\Service\Exporter\AccountingReport. Did you maybe forget to declare it?
Loading history...
242
        $this->column = $this->initialColumn = $this->initialColumn + 4;
0 ignored issues
show
Bug Best Practice introduced by
The property initialColumn does not exist on Application\Service\Exporter\AccountingReport. Did you maybe forget to declare it?
Loading history...
243
        $firstLine = true;
244
        foreach ($this->liabilities as $index => $data) {
245
            // Account code
246
            $format = ['font' => ['bold' => $data['depth'] <= 2]];
247
            if ($firstLine) {
248
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['accountCode']);
249
            }
250
            $this->write(
251
                str_repeat('  ', $data['depth'] - 1) . $data['code'],
252
                ['alignment' => ['horizontal' => Alignment::HORIZONTAL_LEFT, 'indent' => 1]],
253
                $format
254
            );
255
256
            // Account name
257
            if ($firstLine) {
258
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['accountName']);
259
            }
260
            $this->write($data['name'], ['alignment' => ['wrapText' => true]], $format, $data['format'] ?? []);
261
            if ($firstLine) {
262
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']);
263
            }
264
265
            // Account balance
266
            // Store the coordinate of the cell to later compute totals
267
            $this->liabilities[$index]['cell'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate();
268
            $this->write($data['balance'], self::$balanceFormat);
269
            $firstLine = false;
270
            ++$this->row;
271
            $this->column = $this->initialColumn;
272
273
            $this->lastDataRow = max($this->lastDataRow, $this->row);
274
        }
275
    }
276
277
    protected function writeDataExpenses(): void
278
    {
279
        $this->row = $this->initialRow;
0 ignored issues
show
Bug Best Practice introduced by
The property initialRow does not exist on Application\Service\Exporter\AccountingReport. Did you maybe forget to declare it?
Loading history...
280
        $this->column = $this->initialColumn = $this->initialColumn + 4;
0 ignored issues
show
Bug Best Practice introduced by
The property initialColumn does not exist on Application\Service\Exporter\AccountingReport. Did you maybe forget to declare it?
Loading history...
281
        $firstLine = true;
282
        foreach ($this->expenses as $index => $data) {
283
            // Account code
284
            if ($firstLine) {
285
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['accountCode']);
286
            }
287
            $format = ['font' => ['bold' => $data['depth'] === 1]];
288
            $this->write(
289
                str_repeat('  ', $data['depth'] - 1) . $data['code'],
290
                ['alignment' => ['horizontal' => Alignment::HORIZONTAL_LEFT, 'indent' => 1]],
291
                $format
292
            );
293
294
            // Account name
295
            if ($firstLine) {
296
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['accountName']);
297
            }
298
            $this->write($data['name'], ['alignment' => ['wrapText' => true]], $format, $data['format'] ?? []);
299
300
            // Account balance
301
            if ($firstLine) {
302
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']);
303
            }
304
            // Store the coordinate of the cell to later compute totals
305
            $this->expenses[$index]['cell'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate();
306
            $this->write($data['balance'], self::$balanceFormat);
307
308
            $firstLine = false;
309
            ++$this->row;
310
            $this->column = $this->initialColumn;
311
312
            $this->lastDataRow = max($this->lastDataRow, $this->row);
313
        }
314
    }
315
316
    protected function writeDataRevenues(): void
317
    {
318
        $this->row = $this->initialRow;
0 ignored issues
show
Bug Best Practice introduced by
The property initialRow does not exist on Application\Service\Exporter\AccountingReport. Did you maybe forget to declare it?
Loading history...
319
        $this->column = $this->initialColumn = $this->initialColumn + 4;
0 ignored issues
show
Bug Best Practice introduced by
The property initialColumn does not exist on Application\Service\Exporter\AccountingReport. Did you maybe forget to declare it?
Loading history...
320
        $firstLine = true;
321
        foreach ($this->revenues as $index => $data) {
322
            // Account code
323
            $format = ['font' => ['bold' => $data['depth'] === 1]];
324
            $this->write(
325
                str_repeat('  ', $data['depth'] - 1) . $data['code'],
326
                ['alignment' => ['horizontal' => Alignment::HORIZONTAL_LEFT, 'indent' => 1]],
327
                $format
328
            );
329
330
            // Account name
331
            if ($firstLine) {
332
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['accountName']);
333
            }
334
            $this->write($data['name'], ['alignment' => ['wrapText' => true]], $format, $data['format'] ?? []);
335
336
            // Account balance
337
            if ($firstLine) {
338
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']);
339
            }
340
            // Store the coordinate of the cell to later compute totals
341
            $this->revenues[$index]['cell'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate();
342
            $this->write($data['balance'], self::$balanceFormat);
343
            $format = ['font' => ['bold' => $data['depth'] === 1]];
0 ignored issues
show
Unused Code introduced by
The assignment to $format is dead and can be removed.
Loading history...
344
            if ($firstLine) {
345
                $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['accountCode']);
346
            }
347
348
            $firstLine = false;
349
            ++$this->row;
350
            $this->column = $this->initialColumn;
351
352
            $this->lastDataRow = max($this->lastDataRow, $this->row);
353
        }
354
    }
355
356
    /**
357
     * @param Account[] $items
358
     */
359
    protected function writeData(array $items): void
360
    {
361
        $this->processAccounts($items, 1);
362
363
        $this->sheet->setShowGridlines(false);
364
365
        $profitOrLoss = $this->getProfitOrLoss();
0 ignored issues
show
Bug introduced by
The call to Application\Service\Expo...port::getProfitOrLoss() has too few arguments starting with isPreviousDate. ( Ignorable by Annotation )

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

365
        /** @scrutinizer ignore-call */ 
366
        $profitOrLoss = $this->getProfitOrLoss();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
366
367
        if ($profitOrLoss->isNegative()) {
368
            // A loss is written at the end of the Revenues and Assets columns
369
            $data = [
370
                'code' => '',
371
                'name' => 'Résultat intermédiaire (perte)',
372
                'depth' => 1,
373
                'balance' => $profitOrLoss->absolute(),
374
                'account' => null,
375
                'format' => ['font' => ['color' => ['argb' => Color::COLOR_RED]]],
376
            ];
377
            $this->revenues[] = $data;
378
            $this->assets[] = $data;
379
        } else {
380
            // A profit is written at the end of the Expenses and Liabilities columns
381
            $data = [
382
                'code' => '',
383
                'name' => 'Résultat intermédiaire (bénéfice)',
384
                'depth' => 1,
385
                'balance' => $profitOrLoss,
386
                'account' => null,
387
                'format' => ['font' => ['color' => ['argb' => Color::COLOR_DARKGREEN]]],
388
            ];
389
            $this->expenses[] = $data;
390
            $this->liabilities[] = $data;
391
        }
392
393
        $this->writeDataAssets();
0 ignored issues
show
Bug introduced by
The method writeDataAssets() does not exist on Application\Service\Exporter\AccountingReport. ( Ignorable by Annotation )

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

393
        $this->/** @scrutinizer ignore-call */ 
394
               writeDataAssets();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
394
395
        $this->writeDataLiabilities();
0 ignored issues
show
Bug introduced by
The method writeDataLiabilities() does not exist on Application\Service\Exporter\AccountingReport. ( Ignorable by Annotation )

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

395
        $this->/** @scrutinizer ignore-call */ 
396
               writeDataLiabilities();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
396
397
        $this->writeDataExpenses();
0 ignored issues
show
Bug introduced by
The method writeDataExpenses() does not exist on Application\Service\Exporter\AccountingReport. ( Ignorable by Annotation )

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

397
        $this->/** @scrutinizer ignore-call */ 
398
               writeDataExpenses();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
398
399
        $this->writeDataRevenues();
0 ignored issues
show
Bug introduced by
The method writeDataRevenues() does not exist on Application\Service\Exporter\AccountingReport. ( Ignorable by Annotation )

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

399
        $this->/** @scrutinizer ignore-call */ 
400
               writeDataRevenues();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
400
401
        $this->applyExtraFormatting();
0 ignored issues
show
Bug introduced by
The call to Application\Service\Expo...:applyExtraFormatting() has too few arguments starting with startRow. ( Ignorable by Annotation )

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

401
        $this->/** @scrutinizer ignore-call */ 
402
               applyExtraFormatting();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
402
    }
403
404
    private function getProfitOrLoss(): Money
405
    {
406
        // Sum the profit and loss root accounts
407
        $totalRevenues = $this->sumBalance($this->revenues);
0 ignored issues
show
Bug introduced by
The call to Application\Service\Expo...ingReport::sumBalance() has too few arguments starting with isPreviousDate. ( Ignorable by Annotation )

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

407
        /** @scrutinizer ignore-call */ 
408
        $totalRevenues = $this->sumBalance($this->revenues);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
408
409
        $totalExpenses = $this->sumBalance($this->expenses);
410
411
        return $totalRevenues->subtract($totalExpenses);
412
    }
413
414
    protected function getHeaders(): array
415
    {
416
        $headers = [];
417
        $headers[] = ['label' => 'Actifs', 'formats' => [self::$headerFormat, self::$centerFormat], 'colspan' => 2];
418
        $headers[] = ['label' => 'Solde', 'formats' => [self::$headerFormat, self::$centerFormat]];
419
        if ($this->showBudget) {
420
            $headers[] = ['label' => 'Solde précédent', 'formats' => [self::$headerFormat, self::$centerFormat]];
421
            $headers[] = ['label' => 'Budget prévu', 'formats' => [self::$headerFormat, self::$centerFormat]];
422
            $headers[] = ['label' => 'Budget restant', 'formats' => [self::$headerFormat, self::$centerFormat]];
423
        }
424
        $headers[] = ['label' => '', 'width' => 3, 'formats' => []]; // margin
425
        $headers[] = ['label' => 'Passifs', 'formats' => [self::$headerFormat, self::$centerFormat], 'colspan' => 2];
426
        $headers[] = ['label' => 'Solde', 'formats' => [self::$headerFormat, self::$centerFormat]];
427
        if ($this->showBudget) {
428
            $headers[] = ['label' => 'Solde précédent', 'formats' => [self::$headerFormat, self::$centerFormat]];
429
            $headers[] = ['label' => 'Budget prévu', 'formats' => [self::$headerFormat, self::$centerFormat]];
430
            $headers[] = ['label' => 'Budget restant', 'formats' => [self::$headerFormat, self::$centerFormat]];
431
        }
432
433
        $headers[] = ['label' => '', 'width' => 5, 'formats' => []]; // margin
434
435
        $headers[] = ['label' => 'Charges', 'formats' => [self::$headerFormat, self::$centerFormat], 'colspan' => 2];
436
        $headers[] = ['label' => 'Solde', 'formats' => [self::$headerFormat, self::$centerFormat]];
437
        if ($this->showBudget) {
438
            $headers[] = ['label' => 'Solde précédent', 'formats' => [self::$headerFormat, self::$centerFormat]];
439
            $headers[] = ['label' => 'Budget prévu', 'formats' => [self::$headerFormat, self::$centerFormat]];
440
            $headers[] = ['label' => 'Budget restant', 'formats' => [self::$headerFormat, self::$centerFormat]];
441
        }
442
        $headers[] = ['label' => '', 'width' => 3, 'formats' => []]; // margin
443
        $headers[] = ['label' => 'Profits', 'formats' => [self::$headerFormat, self::$centerFormat], 'colspan' => 2];
444
        $headers[] = ['label' => 'Solde', 'formats' => [self::$headerFormat, self::$centerFormat]];
445
        if ($this->showBudget) {
446
            $headers[] = ['label' => 'Solde précédent', 'formats' => [self::$headerFormat, self::$centerFormat]];
447
            $headers[] = ['label' => 'Budget prévu', 'formats' => [self::$headerFormat, self::$centerFormat]];
448
            $headers[] = ['label' => 'Budget restant', 'formats' => [self::$headerFormat, self::$centerFormat]];
449
        }
450
451
        return $headers;
452
    }
453
454
    protected function writeFooter(): void
455
    {
456
        $this->initialColumn = $this->column;
457
458
        // BALANCE SHEET
459
460
        // Assets
461
        // Account.code
462
        $this->write('');
463
        // Account.name
464
        $this->write('');
465
        // Account.balance
466
        $this->writeSum($this->assets);
0 ignored issues
show
Bug introduced by
The method writeSum() does not exist on Application\Service\Exporter\AccountingReport. Did you maybe mean write()? ( Ignorable by Annotation )

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

466
        $this->/** @scrutinizer ignore-call */ 
467
               writeSum($this->assets);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
467
        if ($this->showBudget) {
468
            $this->write('');
469
            $this->write('');
470
            $this->write('');
471
        }
472
473
        // Margin
474
        $this->write('');
475
476
        // Liabilities
477
        // Account.code
478
        $this->write('');
479
        // Account.name
480
        $this->write('');
481
        // Account.balance
482
        $this->writeSum($this->liabilities);
483
        if ($this->showBudget) {
484
            $this->write('');
485
            $this->write('');
486
            $this->write('');
487
        }
488
489
        // Margin
490
        $this->write('');
491
492
        // INCOME STATEMENT
493
494
        // Expenses
495
        // Account.code
496
        $this->write('');
497
        // Account.name
498
        $this->write('');
499
        // Account.balance
500
        $this->writeSum($this->expenses);
501
        if ($this->showBudget) {
502
            $this->write('');
503
            $this->write('');
504
            $this->write('');
505
        }
506
507
        // Margin
508
        $this->write('');
509
510
        // Revenues
511
        // Account.code
512
        $this->write('');
513
        // Account.name
514
        $this->write('');
515
        // Account.balance
516
        $this->writeSum($this->revenues);
517
        if ($this->showBudget) {
518
            $this->write('');
519
            $this->write('');
520
            $this->write('');
521
        }
522
523
        // Apply style
524
        $range = Coordinate::stringFromColumnIndex($this->initialColumn) . $this->row . ':' . Coordinate::stringFromColumnIndex($this->column - 1) . $this->row;
0 ignored issues
show
Bug Best Practice introduced by
The property initialColumn does not exist on Application\Service\Exporter\AccountingReport. Did you maybe forget to declare it?
Loading history...
525
        $this->sheet->getStyle($range)->applyFromArray(self::$totalFormat);
526
    }
527
528
    private function applyExtraFormatting(): void
529
    {
530
        // Format balance numbers
531
        $numericCols = $this->showBudget ? [3, 4, 5, 10, 11, 12, 17, 18, 19, 24, 25, 26] : [3, 7, 11, 15];
532
        foreach ($numericCols as $colIndex) {
533
            $range = Coordinate::stringFromColumnIndex($colIndex) . 4 . ':' . Coordinate::stringFromColumnIndex($colIndex) . $this->lastDataRow;
534
            $this->sheet->getStyle($range)->applyFromArray(self::$balanceFormat);
535
        }
536
537
        // Increase row height since account names can wrap on multiple lines
538
        for ($r = 4; $r <= $this->lastDataRow; ++$r) {
539
            $this->sheet->getRowDimension($r)->setRowHeight(30);
540
        }
541
    }
542
543
    private function writeSum(array $data): void
544
    {
545
        $cellsToSum = $this->cellsToSum($data);
0 ignored issues
show
Bug introduced by
The call to Application\Service\Expo...ingReport::cellsToSum() has too few arguments starting with depth. ( Ignorable by Annotation )

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

545
        /** @scrutinizer ignore-call */ 
546
        $cellsToSum = $this->cellsToSum($data);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
546
        $sum = $cellsToSum ? '=SUM(' . implode(',', $cellsToSum) . ')' : '';
547
        $this->write($sum, self::$balanceFormat, self::$totalFormat);
548
    }
549
550
    private function cellsToSum(array $data): array
551
    {
552
        $cells = array_reduce($data, function (array $carry, $data) {
553
            if (isset($data['cell']) && ($data['depth'] === 1 || (int) mb_substr((string) $data['code'], 0, 1) > 6)) {
554
                $carry[] = $data['cell'];
555
            }
556
557
            return $carry;
558
        }, []);
559
560
        return $cells;
561
    }
562
563
    private function sumBalance(array $data): Money
564
    {
565
        $sum = array_reduce($data, function (Money $carry, $data) {
566
            if ($data['depth'] === 1 || (int) mb_substr((string) $data['code'], 0, 1) > 6) {
567
                return $carry->add($data['balance']);
568
            }
569
570
            return $carry;
571
        }, Money::CHF(0));
572
573
        return $sum;
574
    }
575
}
576