Total Complexity | 97 |
Total Lines | 600 |
Duplicated Lines | 0 % |
Coverage | 99.05% |
Changes | 2 | ||
Bugs | 1 | Features | 0 |
Complex classes like AccountingReport often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AccountingReport, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
34 | class AccountingReport extends AbstractExcel |
||
35 | { |
||
36 | private ChronosDate $date; |
||
37 | |||
38 | private bool $showBudget = false; |
||
39 | |||
40 | /** |
||
41 | * @var Data[] |
||
42 | */ |
||
43 | private array $assets = []; |
||
44 | |||
45 | /** |
||
46 | * @var Data[] |
||
47 | */ |
||
48 | private array $liabilities = []; |
||
49 | |||
50 | /** |
||
51 | * @var Data[] |
||
52 | */ |
||
53 | private array $expenses = []; |
||
54 | |||
55 | /** |
||
56 | * @var Data[] |
||
57 | */ |
||
58 | private array $revenues = []; |
||
59 | |||
60 | private static array $balanceFormat = [ |
||
61 | 'fill' => [ |
||
62 | 'fillType' => Fill::FILL_SOLID, |
||
63 | 'startColor' => [ |
||
64 | 'argb' => 'FFDDDDDD', |
||
65 | ], |
||
66 | ], |
||
67 | 'numberFormat' => [ |
||
68 | 'formatCode' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1, // eg. 12'345.67 |
||
69 | ], |
||
70 | ]; |
||
71 | |||
72 | private static array $wrappedFormat = [ |
||
73 | 'alignment' => [ |
||
74 | 'wrapText' => true, |
||
75 | ], |
||
76 | ]; |
||
77 | |||
78 | private static array $indentFormat = [ |
||
79 | 'alignment' => [ |
||
80 | 'horizontal' => Alignment::HORIZONTAL_LEFT, |
||
81 | 'indent' => 1, |
||
82 | ], |
||
83 | ]; |
||
84 | |||
85 | private static array $columnWidth = [ |
||
86 | 'accountCode' => 11, |
||
87 | 'accountName' => 38, |
||
88 | 'balance' => 12, |
||
89 | ]; |
||
90 | |||
91 | 1 | public function __construct(string $hostname, private readonly array $accountingConfig) |
|
100 | } |
||
101 | |||
102 | 1 | protected function getTitleForFilename(): string |
|
103 | { |
||
104 | 1 | return _tr('compta_rapport_%date%', ['date' => $this->date->format('Y-m-d')]); |
|
105 | } |
||
106 | |||
107 | 1 | public function setDate(ChronosDate $date): void |
|
108 | { |
||
109 | 1 | $this->date = $date; |
|
110 | } |
||
111 | |||
112 | 1 | public function showBudget(bool $showBudget): void |
|
113 | { |
||
114 | 1 | $this->showBudget = $showBudget; |
|
115 | } |
||
116 | |||
117 | 1 | protected function writeTitle(): void |
|
128 | } |
||
129 | |||
130 | 1 | private function processAccount(Account $account, int $depth): void |
|
131 | { |
||
132 | 1 | $balance = $account->getBalanceAtDate($this->date); |
|
133 | 1 | if ($this->accountingConfig['report']['showAccountsWithZeroBalance'] === false && $depth > 1 && $balance->isZero()) { |
|
134 | return; |
||
135 | } |
||
136 | |||
137 | 1 | if ($account->getType() === AccountTypeType::EQUITY) { |
|
138 | // Don't show special accounts since it's an interim statement, their balance will be computed manually |
||
139 | 1 | return; |
|
140 | } |
||
141 | |||
142 | 1 | $data = [ |
|
143 | 1 | 'code' => $account->getCode(), |
|
144 | 1 | 'name' => Format::truncate($account->getName(), 55), |
|
145 | 1 | 'depth' => $depth, |
|
146 | 1 | 'balance' => $balance, |
|
147 | 1 | 'balancePrevious' => $this->showBudget ? $account->getTotalBalanceFormer() : null, |
|
148 | 1 | 'budgetAllowed' => $this->showBudget ? $account->getBudgetAllowed() : null, |
|
149 | 1 | 'budgetBalance' => $this->showBudget ? $account->getBudgetBalance() : null, |
|
150 | 1 | 'account' => $account, |
|
151 | 1 | ]; |
|
152 | |||
153 | 1 | $accountClasses = $this->accountingConfig['report']['accountClasses']; |
|
154 | 1 | if ($account->getType() === AccountTypeType::ASSET || ($account->getType() === AccountTypeType::GROUP && in_array(mb_substr((string) $account->getCode(), 0, 1), $accountClasses['assets'], true))) { |
|
155 | 1 | $this->assets[] = $data; |
|
156 | 1 | } elseif ($account->getType() === AccountTypeType::LIABILITY || ($account->getType() === AccountTypeType::GROUP && in_array(mb_substr((string) $account->getCode(), 0, 1), $accountClasses['liabilities'], true))) { |
|
157 | 1 | $this->liabilities[] = $data; |
|
158 | 1 | } elseif ($account->getType() === AccountTypeType::REVENUE || ($account->getType() === AccountTypeType::GROUP && in_array(mb_substr((string) $account->getCode(), 0, 1), $accountClasses['revenues'], true))) { |
|
159 | 1 | $this->revenues[] = $data; |
|
160 | 1 | } elseif ($account->getType() === AccountTypeType::EXPENSE || ($account->getType() === AccountTypeType::GROUP && in_array(mb_substr((string) $account->getCode(), 0, 1), $accountClasses['expenses'], true))) { |
|
161 | 1 | $this->expenses[] = $data; |
|
162 | } |
||
163 | |||
164 | 1 | if ($account->getType() === AccountTypeType::GROUP && $depth <= $this->accountingConfig['report']['maxAccountDepth']) { |
|
165 | 1 | foreach ($account->getChildren() as $child) { |
|
166 | 1 | $this->processAccount($child, $depth + 1); |
|
167 | } |
||
168 | } |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * @param Account $item |
||
173 | */ |
||
174 | 1 | protected function writeItem($item): void |
|
175 | { |
||
176 | // This is unusual because we don't write anything but only collect data for later |
||
177 | 1 | $this->processAccount($item, 1); |
|
178 | } |
||
179 | |||
180 | /** |
||
181 | * Compute the profit or loss (at current and previous dates) and insert the result into the list of accounts. |
||
182 | */ |
||
183 | 1 | private function insertProfitOrLoss(): void |
|
184 | { |
||
185 | 1 | $profitOrLoss = $this->getProfitOrLoss(false); |
|
186 | |||
187 | 1 | if ($profitOrLoss->isZero()) { |
|
188 | return; // If financial result is balanced, it likely a final accounting report so we don't show the intermediate result |
||
189 | } |
||
190 | |||
191 | 1 | $data = [ |
|
192 | 1 | 'depth' => 1, |
|
193 | 1 | 'code' => '', |
|
194 | 1 | 'name' => _tr('Résultat intermédiaire (bénéfice / -perte)'), |
|
195 | 1 | 'account' => null, |
|
196 | 1 | 'balance' => $profitOrLoss, // can be positive of negative |
|
197 | 1 | 'balancePrevious' => null, |
|
198 | 1 | 'format' => $this->color($profitOrLoss), |
|
199 | 1 | ]; |
|
200 | |||
201 | // A profit is reported as a POSITIVE green number, and a loss is reported a NEGATIVE red number. |
||
202 | // They are identical lines at the end of both LIABILITIES and EXPENSES columns |
||
203 | 1 | $this->liabilities[] = $data; |
|
204 | 1 | $this->expenses[] = $data; |
|
205 | } |
||
206 | |||
207 | /** |
||
208 | * @param Data $data |
||
1 ignored issue
–
show
|
|||
209 | */ |
||
210 | 1 | private function maybeBold(array $data, int $untilDepth): array |
|
211 | { |
||
212 | 1 | return $data['depth'] <= $untilDepth ? ['font' => ['bold' => true]] : []; |
|
213 | } |
||
214 | |||
215 | 1 | private function realWrite(): void |
|
216 | { |
||
217 | 1 | $this->insertProfitOrLoss(); |
|
218 | |||
219 | /* |
||
220 | * Page 1 |
||
221 | * BALANCE SHEET (Asset vs Liabilities) |
||
222 | */ |
||
223 | |||
224 | 1 | $this->lastDataColumn = $this->showBudget ? 13 : 7; |
|
225 | 1 | $this->column = $initialColumn = 1; |
|
226 | |||
227 | 1 | $this->balanceSheetHeaders($initialColumn); |
|
228 | |||
229 | // Assets |
||
230 | 1 | $this->column = $initialColumn; |
|
231 | 1 | $initialRow = $this->row; |
|
232 | 1 | $this->lastDataRow = $this->row; |
|
233 | 1 | $this->balanceSheet($initialColumn, $this->assets); |
|
234 | |||
235 | // Liabilities |
||
236 | 1 | $this->row = $initialRow; |
|
237 | 1 | $this->column = $initialColumn = $initialColumn + ($this->showBudget ? 7 : 4); |
|
238 | 1 | $this->balanceSheet($initialColumn, $this->liabilities); |
|
239 | |||
240 | 1 | $this->applyExtraFormatting(6); |
|
241 | // set printing area for page 1 |
||
242 | 1 | $this->sheet->getPageSetup()->setPrintAreaByColumnAndRow(1, 1, $this->lastDataColumn, $this->lastDataRow, 0, 'I'); |
|
243 | |||
244 | /* |
||
245 | * Page 2 |
||
246 | * INCOME STATEMENT (Profit vs Loss) |
||
247 | */ |
||
248 | |||
249 | // start at the bottom of the balance sheet |
||
250 | 1 | $this->row = $this->lastDataRow + 1; |
|
251 | 1 | $this->column = $initialColumn = 1; |
|
252 | 1 | $this->incomeStatementHeaders($initialColumn); |
|
253 | |||
254 | // Expenses |
||
255 | 1 | $initialRow = ++$this->row; |
|
256 | 1 | $this->column = $initialColumn; |
|
257 | 1 | $this->incomeStatement($initialColumn, $this->expenses); |
|
258 | |||
259 | // Revenues |
||
260 | 1 | $this->row = $initialRow; |
|
261 | 1 | $this->column = $initialColumn = $initialColumn + ($this->showBudget ? 7 : 4); |
|
262 | 1 | $this->incomeStatement($initialColumn, $this->revenues); |
|
263 | |||
264 | 1 | $this->row = $this->lastDataRow + 1; |
|
265 | 1 | $this->applyExtraFormatting($initialRow); |
|
266 | // set printing area for page 2 |
||
267 | 1 | $this->sheet->getPageSetup()->setPrintAreaByColumnAndRow(1, $initialRow - 3, $this->lastDataColumn, $this->lastDataRow + 1, 1, 'I'); |
|
268 | } |
||
269 | |||
270 | 1 | private function getProfitOrLoss(bool $isPreviousDate): Money |
|
278 | } |
||
279 | |||
280 | 1 | protected function finalize(string $path): void |
|
281 | { |
||
282 | // Once we collected all data, we can actually write them all |
||
283 | 1 | $this->realWrite(); |
|
284 | |||
285 | // Print on A4 portrait, scale to full page width, variable height (depending on number of accounts) |
||
286 | 1 | $pageSetup = $this->sheet->getPageSetup(); |
|
287 | 1 | $pageSetup->setOrientation(PageSetup::ORIENTATION_PORTRAIT); |
|
288 | 1 | $pageSetup->setPaperSize(PageSetup::PAPERSIZE_A4); |
|
289 | 1 | $pageSetup->setFitToWidth(1); |
|
290 | 1 | $pageSetup->setFitToHeight(0); |
|
291 | 1 | $pageSetup->setHorizontalCentered(true); |
|
292 | 1 | $margins = $this->sheet->getPageMargins(); |
|
293 | 1 | $margins->setTop(0.5); |
|
294 | 1 | $margins->setRight(0.2); |
|
295 | 1 | $margins->setLeft(0.2); |
|
296 | 1 | $margins->setBottom(0.5); |
|
297 | |||
298 | 1 | parent::finalize($path); |
|
299 | } |
||
300 | |||
301 | 1 | protected function writeFooter(): void |
|
302 | { |
||
303 | // EXPENSES |
||
304 | // Account.code |
||
305 | 1 | $this->write(''); |
|
306 | // Account.name |
||
307 | 1 | $this->write(''); |
|
308 | // Account.balance |
||
309 | 1 | $cellsToSum = $this->cellsToSum($this->expenses, 1, 'cellBalance'); |
|
310 | 1 | $this->write($cellsToSum ? '=SUM(' . implode(',', $cellsToSum) . ')' : '', self::$balanceFormat, self::$totalFormat); |
|
311 | // Budget columns (optional) |
||
312 | 1 | if ($this->showBudget) { |
|
313 | 1 | $cellsToSum = $this->cellsToSum($this->expenses, 1, 'cellBalancePrevious'); |
|
314 | 1 | $this->write($cellsToSum ? '=SUM(' . implode(',', $cellsToSum) . ')' : '', self::$balanceFormat, self::$totalFormat); |
|
315 | 1 | $cellsToSum = $this->cellsToSum($this->expenses, 1, 'cellBudgetAllowed'); |
|
316 | 1 | $this->write($cellsToSum ? '=SUM(' . implode(',', $cellsToSum) . ')' : '', self::$balanceFormat, self::$totalFormat); |
|
317 | 1 | $cellsToSum = $this->cellsToSum($this->expenses, 1, 'cellBudgetBalance'); |
|
318 | 1 | $this->write($cellsToSum ? '=SUM(' . implode(',', $cellsToSum) . ')' : '', self::$balanceFormat, self::$totalFormat); |
|
319 | } |
||
320 | |||
321 | // Margin |
||
322 | 1 | $this->write(''); |
|
323 | |||
324 | // REVENUES |
||
325 | // Account.code |
||
326 | 1 | $this->write(''); |
|
327 | // Account.name |
||
328 | 1 | $this->write(''); |
|
329 | // Account.balance |
||
330 | 1 | $cellsToSum = $this->cellsToSum($this->revenues, 1, 'cellBalance'); |
|
331 | 1 | $this->write($cellsToSum ? '=SUM(' . implode(',', $cellsToSum) . ')' : '', self::$balanceFormat, self::$totalFormat); |
|
332 | // Account previous balance (optional) |
||
333 | 1 | if ($this->showBudget) { |
|
334 | 1 | $cellsToSum = $this->cellsToSum($this->revenues, 1, 'cellBalancePrevious'); |
|
335 | 1 | $this->write($cellsToSum ? '=SUM(' . implode(',', $cellsToSum) . ')' : '', self::$balanceFormat, self::$totalFormat); |
|
336 | 1 | $cellsToSum = $this->cellsToSum($this->revenues, 1, 'cellBudgetAllowed'); |
|
337 | 1 | $this->write($cellsToSum ? '=SUM(' . implode(',', $cellsToSum) . ')' : '', self::$balanceFormat, self::$totalFormat); |
|
338 | 1 | $cellsToSum = $this->cellsToSum($this->revenues, 1, 'cellBudgetBalance'); |
|
339 | 1 | $this->write($cellsToSum ? '=SUM(' . implode(',', $cellsToSum) . ')' : '', self::$balanceFormat, self::$totalFormat); |
|
340 | } |
||
341 | |||
342 | // Apply style |
||
343 | 1 | $range = Coordinate::stringFromColumnIndex($this->firstDataColumn) . $this->row . ':' . Coordinate::stringFromColumnIndex($this->column - 1) . $this->row; |
|
344 | 1 | $this->sheet->getStyle($range)->applyFromArray(self::$totalFormat); |
|
345 | } |
||
346 | |||
347 | 1 | private function applyExtraFormatting(int $startRow): void |
|
348 | { |
||
349 | 1 | $columnsToFormat = $this->showBudget ? [3, 4, 5, 6, 10, 11, 12, 13] : [3, 7]; |
|
350 | 1 | foreach ($columnsToFormat as $colIndex) { |
|
351 | // Format balance numbers |
||
352 | 1 | $range = Coordinate::stringFromColumnIndex($colIndex) . $startRow . ':' . Coordinate::stringFromColumnIndex($colIndex) . $this->lastDataRow; |
|
353 | 1 | $this->sheet->getStyle($range)->applyFromArray(self::$balanceFormat); |
|
354 | } |
||
355 | |||
356 | // Increase row height since account names can wrap on multiple lines |
||
357 | 1 | for ($r = $startRow; $r <= $this->lastDataRow; ++$r) { |
|
358 | 1 | $this->sheet->getRowDimension($r)->setRowHeight(30); |
|
359 | } |
||
360 | } |
||
361 | |||
362 | 1 | private function cellsToSum(array $data, int $depth, string $dataIndex = 'cell'): array |
|
363 | { |
||
364 | 1 | $equityAccountsClasses = $this->accountingConfig['report']['accountClasses']['equity']; |
|
365 | 1 | $cells = array_reduce($data, function (array $carry, $data) use ($equityAccountsClasses, $depth, $dataIndex) { |
|
366 | // We only sum accounts at the given depth, plus equity special accounts |
||
367 | 1 | if (isset($data[$dataIndex]) && ($data['depth'] === $depth || in_array(mb_substr((string) $data['code'], 0, 1), $equityAccountsClasses, true))) { |
|
368 | 1 | $carry[] = $data[$dataIndex]; |
|
369 | } |
||
370 | |||
371 | 1 | return $carry; |
|
372 | 1 | }, []); |
|
373 | |||
374 | 1 | return $cells; |
|
375 | } |
||
376 | |||
377 | /** |
||
378 | * Sum root or special accounts balance (for the profit and loss calculation) |
||
379 | * - Root accounts have depth = 1 |
||
380 | * - Special accounts have code 7xxx, 8xxx, 9xxx. |
||
381 | * |
||
382 | * @param Data[] $data profits or expenses |
||
383 | */ |
||
384 | 1 | private function sumBalance(array $data, bool $isPreviousDate): Money |
|
385 | { |
||
386 | 1 | $sum = array_reduce($data, function (Money $carry, $data) use ($isPreviousDate) { |
|
387 | 1 | if ($data['depth'] === 1 || (int) mb_substr((string) $data['code'], 0, 1) > 6) { |
|
388 | 1 | return $carry->add($isPreviousDate ? $data['balancePrevious'] : $data['balance']); |
|
389 | } |
||
390 | |||
391 | 1 | return $carry; |
|
392 | 1 | }, Money::CHF(0)); |
|
393 | |||
394 | 1 | return $sum; |
|
395 | } |
||
396 | |||
397 | 1 | private function balanceSheetHeaders(int $initialColumn): void |
|
398 | { |
||
399 | 1 | $this->sheet->mergeCells([$this->column, $this->row, $this->column + ($this->showBudget ? 12 : 6), $this->row]); |
|
400 | 1 | $this->write( |
|
401 | 1 | _tr('Bilan'), |
|
402 | 1 | self::$titleFormat, |
|
403 | 1 | self::$centerFormat |
|
404 | 1 | ); |
|
405 | 1 | $this->sheet->getRowDimension($this->row)->setRowHeight(40); |
|
406 | 1 | ++$this->row; |
|
407 | |||
408 | // Header line 1 |
||
409 | 1 | $headers = [ |
|
410 | 1 | ['label' => _tr('Actifs'), 'formats' => [self::$headerFormat, self::$centerFormat], 'colspan' => $this->showBudget ? 6 : 3], |
|
411 | 1 | ['label' => '', 'width' => 3, 'formats' => []], // gap |
|
412 | 1 | ['label' => _tr('Passifs'), 'formats' => [self::$headerFormat, self::$centerFormat], 'colspan' => $this->showBudget ? 6 : 3], |
|
413 | 1 | ]; |
|
414 | 1 | $this->column = $initialColumn; |
|
415 | 1 | $this->writeHeaders($headers); |
|
416 | 1 | ++$this->row; |
|
417 | |||
418 | // Header line 2: date(s) of balance |
||
419 | 1 | $headers = [ |
|
420 | 1 | ['label' => '', 'colspan' => 2], // empty margin |
|
421 | 1 | ['label' => 'Solde', 'formats' => [self::$headerFormat, self::$centerFormat]], |
|
422 | 1 | ]; |
|
423 | 1 | if ($this->showBudget) { |
|
424 | 1 | $headers[] = ['label' => 'Solde précédent', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
425 | 1 | $headers[] = ['label' => 'Budget prévu', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
426 | 1 | $headers[] = ['label' => 'Budget restant', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
427 | } |
||
428 | |||
429 | 1 | $headers[] = ['label' => '', 'formats' => []]; // gap |
|
430 | |||
431 | 1 | $headers[] = ['label' => '', 'colspan' => 2]; // empty margin |
|
432 | 1 | $headers[] = ['label' => 'Solde', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
433 | 1 | if ($this->showBudget) { |
|
434 | 1 | $headers[] = ['label' => 'Solde précédent', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
435 | 1 | $headers[] = ['label' => 'Budget prévu', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
436 | 1 | $headers[] = ['label' => 'Budget restant', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
437 | } |
||
438 | 1 | $this->column = $initialColumn; |
|
439 | 1 | $this->writeHeaders($headers); |
|
440 | 1 | $this->sheet->getRowDimension($this->row)->setRowHeight(1.2, 'cm'); |
|
441 | 1 | ++$this->row; |
|
442 | } |
||
443 | |||
444 | /** |
||
445 | * @param Data[] $allData |
||
446 | */ |
||
447 | 1 | private function balanceSheet(int $initialColumn, array &$allData): void |
|
448 | { |
||
449 | // Coordinates (i.e. E3) of the cells with the totals |
||
450 | 1 | $currentTotalCells = ''; |
|
451 | 1 | $previousTotalCells = ''; |
|
452 | 1 | $budgetAllowedTotalCells = ''; |
|
453 | 1 | $budgetBalanceTotalCells = ''; |
|
454 | 1 | $firstLine = true; |
|
455 | |||
456 | 1 | foreach ($allData as $index => $data) { |
|
457 | // Column: account code |
||
458 | 1 | if ($firstLine) { |
|
459 | 1 | $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['accountCode']); |
|
460 | } |
||
461 | 1 | $maybeBold = $this->maybeBold($data, 2); |
|
1 ignored issue
–
show
|
|||
462 | 1 | if (!$firstLine && $maybeBold) { |
|
463 | 1 | ++$this->row; |
|
464 | } |
||
465 | |||
466 | 1 | $this->write( |
|
467 | 1 | str_repeat(' ', $data['depth'] - 1) . $data['code'], |
|
468 | 1 | self::$indentFormat, |
|
469 | 1 | $maybeBold |
|
470 | 1 | ); |
|
471 | |||
472 | // Column: account name |
||
473 | 1 | if ($firstLine) { |
|
474 | 1 | $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['accountName']); |
|
475 | } |
||
476 | 1 | $this->write($data['name'], self::$wrappedFormat, $maybeBold); |
|
477 | |||
478 | // Column: balance at date |
||
479 | 1 | if ($firstLine) { |
|
480 | 1 | $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']); |
|
481 | 1 | $currentTotalCells = Coordinate::stringFromColumnIndex($this->column) . $this->row; |
|
482 | } |
||
483 | // Store the coordinate of the cell to later compute totals |
||
484 | 1 | $allData[$index]['cell'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate(); |
|
485 | 1 | $this->write($data['balance'], self::$balanceFormat, $maybeBold, $data['format'] ?? []); |
|
486 | |||
487 | // Budget columns (optional) |
||
488 | 1 | if ($this->showBudget) { |
|
489 | 1 | $allData[$index]['cellBalancePrevious'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate(); |
|
490 | 1 | if ($firstLine) { |
|
491 | 1 | $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']); |
|
492 | 1 | $previousTotalCells = Coordinate::stringFromColumnIndex($this->column) . $this->row; |
|
493 | } |
||
494 | 1 | $this->write($data['balancePrevious'] ?? '', self::$balanceFormat, $maybeBold); |
|
495 | |||
496 | 1 | $allData[$index]['cellBudgetAllowed'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate(); |
|
497 | 1 | if ($firstLine) { |
|
498 | 1 | $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']); |
|
499 | } |
||
500 | 1 | $this->write($data['budgetAllowed'] ?? '', self::$balanceFormat, $maybeBold); |
|
501 | |||
502 | 1 | $allData[$index]['cellBudgetBalance'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate(); |
|
503 | 1 | if ($firstLine) { |
|
504 | 1 | $this->sheet->getColumnDimensionByColumn($this->column)->setWidth(self::$columnWidth['balance']); |
|
505 | 1 | $budgetBalanceTotalCells = Coordinate::stringFromColumnIndex($this->column) . $this->row; |
|
506 | } |
||
507 | 1 | $this->write($data['budgetBalance'] ?? '', self::$balanceFormat, $maybeBold); |
|
508 | } |
||
509 | |||
510 | 1 | $firstLine = false; |
|
511 | 1 | ++$this->row; |
|
512 | 1 | $this->column = $initialColumn; |
|
513 | |||
514 | 1 | $this->lastDataRow = max($this->lastDataRow, $this->row); |
|
515 | } |
||
516 | |||
517 | // Replace the total value computed from database by a formula computed from the child accounts cells |
||
518 | // Level 2 (= direct child accounts) |
||
519 | 1 | $cellsToSum = $this->cellsToSum($allData, 2, 'cellBalance'); |
|
520 | 1 | if ($cellsToSum) { |
|
521 | $this->sheet->setCellValue($currentTotalCells, '=SUM(' . implode(',', $cellsToSum) . ')'); |
||
522 | } |
||
523 | 1 | if ($this->showBudget) { |
|
524 | 1 | $cellsToSum = $this->cellsToSum($allData, 2, 'cellBalancePrevious'); |
|
525 | 1 | if ($cellsToSum) { |
|
526 | 1 | $this->sheet->setCellValue($previousTotalCells, '=SUM(' . implode(',', $cellsToSum) . ')'); |
|
527 | } |
||
528 | 1 | $cellsToSum = $this->cellsToSum($allData, 2, 'cellBudgetBalance'); |
|
529 | 1 | if ($cellsToSum) { |
|
530 | 1 | $this->sheet->setCellValue($budgetBalanceTotalCells, '=SUM(' . implode(',', $cellsToSum) . ')'); |
|
531 | } |
||
532 | } |
||
533 | } |
||
534 | |||
535 | 1 | private function incomeStatementHeaders(int $initialColumn): void |
|
536 | { |
||
537 | 1 | $this->sheet->mergeCells([$this->column, $this->row, $this->column + ($this->showBudget ? 12 : 6), $this->row]); |
|
538 | 1 | $this->write( |
|
539 | 1 | _tr('Compte de résultat'), |
|
540 | 1 | self::$titleFormat, |
|
541 | 1 | self::$centerFormat |
|
542 | 1 | ); |
|
543 | 1 | $this->sheet->getRowDimension($this->row)->setRowHeight(40); |
|
544 | 1 | ++$this->row; |
|
545 | |||
546 | // Header line 1 |
||
547 | 1 | $headers = [ |
|
548 | 1 | ['label' => _tr('Charges'), 'formats' => [self::$headerFormat, self::$centerFormat], 'colspan' => $this->showBudget ? 6 : 3], |
|
549 | 1 | ['label' => '', 'width' => 3, 'formats' => []], // gap |
|
550 | 1 | ['label' => _tr('Profits'), 'formats' => [self::$headerFormat, self::$centerFormat], 'colspan' => $this->showBudget ? 6 : 3], |
|
551 | 1 | ]; |
|
552 | 1 | $this->column = $initialColumn; |
|
553 | 1 | $this->writeHeaders($headers); |
|
554 | 1 | ++$this->row; |
|
555 | |||
556 | // Header line 2: date(s) of balance |
||
557 | 1 | $headers = [ |
|
558 | 1 | ['label' => '', 'colspan' => 2], // empty margin |
|
559 | 1 | ['label' => 'Solde', 'formats' => [self::$headerFormat, self::$centerFormat]], |
|
560 | 1 | ]; |
|
561 | 1 | if ($this->showBudget) { |
|
562 | 1 | $headers[] = ['label' => 'Solde précédent', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
563 | 1 | $headers[] = ['label' => 'Budget prévu', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
564 | 1 | $headers[] = ['label' => 'Budget restant', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
565 | } |
||
566 | |||
567 | 1 | $headers[] = ['label' => '', 'formats' => []]; // gap |
|
568 | |||
569 | 1 | $headers[] = ['label' => '', 'colspan' => 2]; // empty margin |
|
570 | 1 | $headers[] = ['label' => 'Solde', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
571 | 1 | if ($this->showBudget) { |
|
572 | 1 | $headers[] = ['label' => 'Solde précédent', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
573 | 1 | $headers[] = ['label' => 'Budget prévu', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
574 | 1 | $headers[] = ['label' => 'Budget restant', 'formats' => [self::$headerFormat, self::$centerFormat]]; |
|
575 | } |
||
576 | 1 | $this->column = $initialColumn; |
|
577 | 1 | $this->writeHeaders($headers); |
|
578 | 1 | $this->sheet->getRowDimension($this->row)->setRowHeight(1.2, 'cm'); |
|
579 | } |
||
580 | |||
581 | /** |
||
582 | * @param Data[] $allData |
||
583 | */ |
||
584 | 1 | private function incomeStatement(int $initialColumn, array &$allData): void |
|
585 | { |
||
586 | 1 | $firstLine = true; |
|
587 | 1 | foreach ($allData as $index => $data) { |
|
588 | // Column: account code |
||
589 | 1 | $maybeBold = $this->maybeBold($data, 1); |
|
1 ignored issue
–
show
|
|||
590 | 1 | if (!$firstLine && $maybeBold) { |
|
591 | 1 | ++$this->row; |
|
592 | } |
||
593 | |||
594 | 1 | $this->write( |
|
595 | 1 | str_repeat(' ', $data['depth'] - 1) . $data['code'], |
|
596 | 1 | self::$indentFormat, |
|
597 | 1 | $maybeBold |
|
598 | 1 | ); |
|
599 | |||
600 | // Column: account name |
||
601 | 1 | $this->write($data['name'], self::$wrappedFormat, $maybeBold); |
|
602 | |||
603 | // Column: balance at date |
||
604 | // Store the coordinate of the cell to later compute totals |
||
605 | 1 | $allData[$index]['cellBalance'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate(); |
|
606 | 1 | $this->write($data['balance'], self::$balanceFormat, $maybeBold, $data['format'] ?? []); |
|
607 | |||
608 | // Budget columns (optional) |
||
609 | 1 | if ($this->showBudget) { |
|
610 | 1 | $allData[$index]['cellBalancePrevious'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate(); |
|
611 | 1 | $this->write($data['balancePrevious'] ?? '', self::$balanceFormat, $maybeBold); |
|
612 | |||
613 | 1 | $allData[$index]['cellBudgetAllowed'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate(); |
|
614 | 1 | $this->write($data['budgetAllowed'] ?? '', self::$balanceFormat, $maybeBold); |
|
615 | |||
616 | 1 | $allData[$index]['cellBudgetBalance'] = $this->sheet->getCell([$this->column, $this->row])->getCoordinate(); |
|
617 | 1 | $this->write($data['budgetBalance'] ?? '', self::$balanceFormat, $maybeBold); |
|
618 | } |
||
619 | |||
620 | 1 | ++$this->row; |
|
621 | 1 | $this->column = $initialColumn; |
|
622 | |||
623 | 1 | $this->lastDataRow = max($this->lastDataRow, $this->row); |
|
624 | 1 | $firstLine = false; |
|
625 | } |
||
626 | } |
||
627 | |||
628 | 1 | private function color(Money $profitOrLoss): array |
|
634 | 1 | ], |
|
635 | 1 | ], |
|
636 | 1 | ]; |
|
637 | } |
||
638 | } |
||
639 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths