| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  | <?php | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * BillTransformer.php | 
					
						
							|  |  |  |  * Copyright (c) 2019 james@firefly-iii.org | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This file is part of Firefly III (https://github.com/firefly-iii). | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU Affero General Public License as | 
					
						
							|  |  |  |  * published by the Free Software Foundation, either version 3 of the | 
					
						
							|  |  |  |  * License, or (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU Affero General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  |  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | declare(strict_types=1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace FireflyIII\Transformers\V2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | use Carbon\Carbon; | 
					
						
							|  |  |  | use Carbon\CarbonInterface; | 
					
						
							|  |  |  | use FireflyIII\Models\Bill; | 
					
						
							|  |  |  | use FireflyIII\Models\Note; | 
					
						
							|  |  |  | use FireflyIII\Models\ObjectGroup; | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  | use FireflyIII\Models\Transaction; | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  | use FireflyIII\Models\TransactionCurrency; | 
					
						
							|  |  |  | use FireflyIII\Models\TransactionJournal; | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  | use FireflyIII\Support\Http\Api\ExchangeRateConverter; | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  | use Illuminate\Support\Collection; | 
					
						
							|  |  |  | use Illuminate\Support\Facades\DB; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Class BillTransformer | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | class BillTransformer extends AbstractTransformer | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-04 07:18:03 +01:00
										 |  |  |     private ExchangeRateConverter $converter; | 
					
						
							|  |  |  |     private array                 $currencies; | 
					
						
							|  |  |  |     private TransactionCurrency   $default; | 
					
						
							|  |  |  |     private array                 $groups; | 
					
						
							|  |  |  |     private array                 $notes; | 
					
						
							|  |  |  |     private array                 $paidDates; | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * @inheritDoc | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function collectMetaData(Collection $objects): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $currencies      = []; | 
					
						
							|  |  |  |         $bills           = []; | 
					
						
							|  |  |  |         $this->notes     = []; | 
					
						
							|  |  |  |         $this->groups    = []; | 
					
						
							|  |  |  |         $this->paidDates = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // start with currencies:
 | 
					
						
							|  |  |  |         /** @var Bill $object */ | 
					
						
							|  |  |  |         foreach ($objects as $object) { | 
					
						
							| 
									
										
										
										
											2023-11-05 19:55:39 +01:00
										 |  |  |             $id              = $object->transaction_currency_id; | 
					
						
							|  |  |  |             $bills[]         = $object->id; | 
					
						
							| 
									
										
										
										
											2023-12-10 06:45:59 +01:00
										 |  |  |             $currencies[$id] ??= TransactionCurrency::find($id); | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         $this->currencies = $currencies; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // continue with notes
 | 
					
						
							|  |  |  |         $notes = Note::whereNoteableType(Bill::class)->whereIn('noteable_id', array_keys($bills))->get(); | 
					
						
							|  |  |  |         /** @var Note $note */ | 
					
						
							|  |  |  |         foreach ($notes as $note) { | 
					
						
							| 
									
										
										
										
											2023-11-05 19:55:39 +01:00
										 |  |  |             $id               = $note->noteable_id; | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |             $this->notes[$id] = $note; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // grab object groups:
 | 
					
						
							|  |  |  |         $set = DB::table('object_groupables') | 
					
						
							|  |  |  |                  ->leftJoin('object_groups', 'object_groups.id', '=', 'object_groupables.object_group_id') | 
					
						
							|  |  |  |                  ->where('object_groupables.object_groupable_type', Bill::class) | 
					
						
							|  |  |  |                  ->get(['object_groupables.*', 'object_groups.title', 'object_groups.order']); | 
					
						
							|  |  |  |         /** @var ObjectGroup $entry */ | 
					
						
							|  |  |  |         foreach ($set as $entry) { | 
					
						
							|  |  |  |             $billId                = (int)$entry->object_groupable_id; | 
					
						
							|  |  |  |             $id                    = (int)$entry->object_group_id; | 
					
						
							| 
									
										
										
										
											2023-11-05 19:55:39 +01:00
										 |  |  |             $order                 = $entry->order; | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |             $this->groups[$billId] = [ | 
					
						
							|  |  |  |                 'object_group_id'    => $id, | 
					
						
							|  |  |  |                 'object_group_title' => $entry->title, | 
					
						
							|  |  |  |                 'object_group_order' => $order, | 
					
						
							|  |  |  |             ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |         $this->default   = app('amount')->getDefaultCurrency(); | 
					
						
							|  |  |  |         $this->converter = new ExchangeRateConverter(); | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |         // grab all paid dates:
 | 
					
						
							|  |  |  |         if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |             $journals   = TransactionJournal::whereIn('bill_id', $bills) | 
					
						
							|  |  |  |                                             ->where('date', '>=', $this->parameters->get('start')) | 
					
						
							|  |  |  |                                             ->where('date', '<=', $this->parameters->get('end')) | 
					
						
							|  |  |  |                                             ->get(['transaction_journals.id', 'transaction_journals.transaction_group_id', 'transaction_journals.date', 'transaction_journals.bill_id']); | 
					
						
							|  |  |  |             $journalIds = $journals->pluck('id')->toArray(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // grab transactions for amount:
 | 
					
						
							|  |  |  |             $set = Transaction::whereIn('transaction_journal_id', $journalIds) | 
					
						
							|  |  |  |                               ->where('transactions.amount', '<', 0) | 
					
						
							|  |  |  |                               ->get(['transactions.id', 'transactions.transaction_journal_id', 'transactions.amount', 'transactions.foreign_amount', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']); | 
					
						
							|  |  |  |             // convert to array for easy finding:
 | 
					
						
							|  |  |  |             $transactions = []; | 
					
						
							|  |  |  |             /** @var Transaction $transaction */ | 
					
						
							|  |  |  |             foreach ($set as $transaction) { | 
					
						
							| 
									
										
										
										
											2023-11-05 19:41:37 +01:00
										 |  |  |                 $journalId                = $transaction->transaction_journal_id; | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |                 $transactions[$journalId] = $transaction->toArray(); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |             /** @var TransactionJournal $journal */ | 
					
						
							|  |  |  |             foreach ($journals as $journal) { | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  |                 app('log')->debug(sprintf('Processing journal #%d', $journal->id)); | 
					
						
							| 
									
										
										
										
											2023-11-05 19:41:37 +01:00
										 |  |  |                 $transaction             = $transactions[$journal->id] ?? []; | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |                 $billId                  = (int)$journal->bill_id; | 
					
						
							| 
									
										
										
										
											2023-11-04 11:31:14 +01:00
										 |  |  |                 $currencyId              = (int)($transaction['transaction_currency_id'] ?? 0); | 
					
						
							| 
									
										
										
										
											2023-12-10 06:45:59 +01:00
										 |  |  |                 $currencies[$currencyId] ??= TransactionCurrency::find($currencyId); | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 // foreign currency
 | 
					
						
							|  |  |  |                 $foreignCurrencyId     = null; | 
					
						
							|  |  |  |                 $foreignCurrencyCode   = null; | 
					
						
							|  |  |  |                 $foreignCurrencyName   = null; | 
					
						
							|  |  |  |                 $foreignCurrencySymbol = null; | 
					
						
							|  |  |  |                 $foreignCurrencyDp     = null; | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  |                 app('log')->debug('Foreign currency is NULL'); | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |                 if (null !== $transaction['foreign_currency_id']) { | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  |                     app('log')->debug(sprintf('Foreign currency is #%d', $transaction['foreign_currency_id'])); | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |                     $foreignCurrencyId              = (int)$transaction['foreign_currency_id']; | 
					
						
							| 
									
										
										
										
											2023-12-10 06:45:59 +01:00
										 |  |  |                     $currencies[$foreignCurrencyId] ??= TransactionCurrency::find($foreignCurrencyId); | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |                     $foreignCurrencyCode            = $currencies[$foreignCurrencyId]->code; | 
					
						
							|  |  |  |                     $foreignCurrencyName            = $currencies[$foreignCurrencyId]->name; | 
					
						
							|  |  |  |                     $foreignCurrencySymbol          = $currencies[$foreignCurrencyId]->symbol; | 
					
						
							| 
									
										
										
										
											2023-11-26 12:24:37 +01:00
										 |  |  |                     $foreignCurrencyDp              = $currencies[$foreignCurrencyId]->decimal_places; | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |                 $this->paidDates[$billId][] = [ | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |                     'transaction_group_id'            => (string)$journal->id, | 
					
						
							|  |  |  |                     'transaction_journal_id'          => (string)$journal->transaction_group_id, | 
					
						
							|  |  |  |                     'date'                            => $journal->date->toAtomString(), | 
					
						
							| 
									
										
										
										
											2023-11-05 19:55:39 +01:00
										 |  |  |                     'currency_id'                     => $currencies[$currencyId]->id, | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |                     'currency_code'                   => $currencies[$currencyId]->code, | 
					
						
							|  |  |  |                     'currency_name'                   => $currencies[$currencyId]->name, | 
					
						
							|  |  |  |                     'currency_symbol'                 => $currencies[$currencyId]->symbol, | 
					
						
							| 
									
										
										
										
											2023-11-26 12:24:37 +01:00
										 |  |  |                     'currency_decimal_places'         => $currencies[$currencyId]->decimal_places, | 
					
						
							| 
									
										
										
										
											2023-11-05 19:55:39 +01:00
										 |  |  |                     'native_currency_id'              => $currencies[$currencyId]->id, | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  |                     'native_currency_code'            => $currencies[$currencyId]->code, | 
					
						
							|  |  |  |                     'native_currency_symbol'          => $currencies[$currencyId]->symbol, | 
					
						
							| 
									
										
										
										
											2023-11-26 12:24:37 +01:00
										 |  |  |                     'native_currency_decimal_places'  => $currencies[$currencyId]->decimal_places, | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |                     'foreign_currency_id'             => $foreignCurrencyId, | 
					
						
							|  |  |  |                     'foreign_currency_code'           => $foreignCurrencyCode, | 
					
						
							|  |  |  |                     'foreign_currency_name'           => $foreignCurrencyName, | 
					
						
							|  |  |  |                     'foreign_currency_symbol'         => $foreignCurrencySymbol, | 
					
						
							|  |  |  |                     'foreign_currency_decimal_places' => $foreignCurrencyDp, | 
					
						
							|  |  |  |                     'amount'                          => $transaction['amount'], | 
					
						
							|  |  |  |                     'foreign_amount'                  => $transaction['foreign_amount'], | 
					
						
							|  |  |  |                     'native_amount'                   => $this->converter->convert($currencies[$currencyId], $this->default, $journal->date, $transaction['amount']), | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  |                     'foreign_native_amount'           => '' === (string)$transaction['foreign_amount'] ? null : $this->converter->convert( | 
					
						
							|  |  |  |                         $currencies[$foreignCurrencyId], | 
					
						
							| 
									
										
										
										
											2023-09-20 06:36:43 +02:00
										 |  |  |                         $this->default, | 
					
						
							|  |  |  |                         $journal->date, | 
					
						
							|  |  |  |                         $transaction['foreign_amount'] | 
					
						
							|  |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |                 ]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Transform the bill. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param Bill $bill | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return array | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function transform(Bill $bill): array | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-11-05 19:55:39 +01:00
										 |  |  |         $paidData          = $this->paidDates[$bill->id] ?? []; | 
					
						
							|  |  |  |         $nextExpectedMatch = $this->nextExpectedMatch($bill, $this->paidDates[$bill->id] ?? []); | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |         $payDates          = $this->payDates($bill); | 
					
						
							| 
									
										
										
										
											2023-11-05 19:41:37 +01:00
										 |  |  |         $currency          = $this->currencies[$bill->transaction_currency_id]; | 
					
						
							| 
									
										
										
										
											2023-11-05 19:55:39 +01:00
										 |  |  |         $group             = $this->groups[$bill->id] ?? null; | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // date for currency conversion
 | 
					
						
							|  |  |  |         /** @var Carbon|null $startParam */ | 
					
						
							|  |  |  |         $startParam = $this->parameters->get('start'); | 
					
						
							| 
									
										
										
										
											2023-10-30 19:11:02 +01:00
										 |  |  |         /** @var Carbon|null $date */ | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  |         $date = null === $startParam ? today() : clone $startParam; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |         $nextExpectedMatchDiff = $this->getNextExpectedMatchDiff($nextExpectedMatch, $payDates); | 
					
						
							|  |  |  |         return [ | 
					
						
							| 
									
										
										
										
											2023-11-05 19:55:39 +01:00
										 |  |  |             'id'                             => $bill->id, | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  |             'created_at'                     => $bill->created_at->toAtomString(), | 
					
						
							|  |  |  |             'updated_at'                     => $bill->updated_at->toAtomString(), | 
					
						
							|  |  |  |             'name'                           => $bill->name, | 
					
						
							|  |  |  |             'amount_min'                     => app('steam')->bcround($bill->amount_min, $currency->decimal_places), | 
					
						
							|  |  |  |             'amount_max'                     => app('steam')->bcround($bill->amount_max, $currency->decimal_places), | 
					
						
							|  |  |  |             'native_amount_min'              => $this->converter->convert($currency, $this->default, $date, $bill->amount_min), | 
					
						
							|  |  |  |             'native_amount_max'              => $this->converter->convert($currency, $this->default, $date, $bill->amount_max), | 
					
						
							|  |  |  |             'currency_id'                    => (string)$bill->transaction_currency_id, | 
					
						
							|  |  |  |             'currency_code'                  => $currency->code, | 
					
						
							|  |  |  |             'currency_name'                  => $currency->name, | 
					
						
							|  |  |  |             'currency_symbol'                => $currency->symbol, | 
					
						
							| 
									
										
										
										
											2023-11-26 12:10:42 +01:00
										 |  |  |             'currency_decimal_places'        => $currency->decimal_places, | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  |             'native_currency_id'             => $this->default->id, | 
					
						
							|  |  |  |             'native_currency_code'           => $this->default->code, | 
					
						
							|  |  |  |             'native_currency_name'           => $this->default->name, | 
					
						
							|  |  |  |             'native_currency_symbol'         => $this->default->symbol, | 
					
						
							| 
									
										
										
										
											2023-11-26 12:24:37 +01:00
										 |  |  |             'native_currency_decimal_places' => $this->default->decimal_places, | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  |             'date'                           => $bill->date->toAtomString(), | 
					
						
							|  |  |  |             'end_date'                       => $bill->end_date?->toAtomString(), | 
					
						
							|  |  |  |             'extension_date'                 => $bill->extension_date?->toAtomString(), | 
					
						
							|  |  |  |             'repeat_freq'                    => $bill->repeat_freq, | 
					
						
							| 
									
										
										
										
											2023-11-28 17:18:31 +01:00
										 |  |  |             'skip'                           => $bill->skip, | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  |             'active'                         => $bill->active, | 
					
						
							| 
									
										
										
										
											2023-11-05 19:55:39 +01:00
										 |  |  |             'order'                          => $bill->order, | 
					
						
							|  |  |  |             'notes'                          => $this->notes[$bill->id] ?? null, | 
					
						
							| 
									
										
										
										
											2023-09-17 09:45:34 +02:00
										 |  |  |             'object_group_id'                => $group ? $group['object_group_id'] : null, | 
					
						
							|  |  |  |             'object_group_order'             => $group ? $group['object_group_order'] : null, | 
					
						
							|  |  |  |             'object_group_title'             => $group ? $group['object_group_title'] : null, | 
					
						
							|  |  |  |             'next_expected_match'            => $nextExpectedMatch->toAtomString(), | 
					
						
							|  |  |  |             'next_expected_match_diff'       => $nextExpectedMatchDiff, | 
					
						
							|  |  |  |             'pay_dates'                      => $payDates, | 
					
						
							|  |  |  |             'paid_dates'                     => $paidData, | 
					
						
							|  |  |  |             'links'                          => [ | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |                 [ | 
					
						
							|  |  |  |                     'rel' => 'self', | 
					
						
							|  |  |  |                     'uri' => sprintf('/bills/%d', $bill->id), | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |         ]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get the data the bill was paid and predict the next expected match. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param Bill  $bill | 
					
						
							|  |  |  |      * @param array $dates | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return Carbon | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function nextExpectedMatch(Bill $bill, array $dates): Carbon | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // 2023-07-1 sub one day from the start date to fix a possible bug (see #7704)
 | 
					
						
							|  |  |  |         // 2023-07-18 this particular date is used to search for the last paid date.
 | 
					
						
							|  |  |  |         // 2023-07-18 the cloned $searchDate is used to grab the correct transactions.
 | 
					
						
							| 
									
										
										
										
											2023-09-03 17:38:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /** @var Carbon|null $startParam */ | 
					
						
							|  |  |  |         $startParam = $this->parameters->get('start'); | 
					
						
							|  |  |  |         /** @var Carbon|null $start */ | 
					
						
							|  |  |  |         $start = null === $startParam ? today() : clone $startParam; | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |         $start->subDay(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $lastPaidDate = $this->lastPaidDate($dates, $start); | 
					
						
							|  |  |  |         $nextMatch    = clone $bill->date; | 
					
						
							|  |  |  |         while ($nextMatch < $lastPaidDate) { | 
					
						
							|  |  |  |             /* | 
					
						
							|  |  |  |              * As long as this date is smaller than the last time the bill was paid, keep jumping ahead. | 
					
						
							|  |  |  |              * For example: 1 jan, 1 feb, etc. | 
					
						
							|  |  |  |              */ | 
					
						
							|  |  |  |             $nextMatch = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if ($nextMatch->isSameDay($lastPaidDate)) { | 
					
						
							|  |  |  |             /* | 
					
						
							|  |  |  |              * Add another period because it's the same day as the last paid date. | 
					
						
							|  |  |  |              */ | 
					
						
							|  |  |  |             $nextMatch = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return $nextMatch; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Returns the latest date in the set, or start when set is empty. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2023-10-30 19:11:02 +01:00
										 |  |  |      * @param array  $dates | 
					
						
							|  |  |  |      * @param Carbon $default | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @return Carbon | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function lastPaidDate(array $dates, Carbon $default): Carbon | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (0 === count($dates)) { | 
					
						
							|  |  |  |             return $default; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $latest = $dates[0]['date']; | 
					
						
							|  |  |  |         /** @var array $row */ | 
					
						
							|  |  |  |         foreach ($dates as $row) { | 
					
						
							|  |  |  |             $carbon = new Carbon($row['date']); | 
					
						
							|  |  |  |             if ($carbon->gte($latest)) { | 
					
						
							|  |  |  |                 $latest = $row['date']; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return new Carbon($latest); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * @param Bill $bill | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return array | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function payDates(Bill $bill): array | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-10-06 18:21:49 +02:00
										 |  |  |         //app('log')->debug(sprintf('Now in payDates() for bill #%d', $bill->id));
 | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |         if (null === $this->parameters->get('start') || null === $this->parameters->get('end')) { | 
					
						
							| 
									
										
										
										
											2023-10-06 18:21:49 +02:00
										 |  |  |             //app('log')->debug('No start or end date, give empty array.');
 | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             return []; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $set          = new Collection(); | 
					
						
							|  |  |  |         $currentStart = clone $this->parameters->get('start'); | 
					
						
							|  |  |  |         // 2023-06-23 subDay to fix 7655
 | 
					
						
							|  |  |  |         $currentStart->subDay(); | 
					
						
							|  |  |  |         $loop = 0; | 
					
						
							|  |  |  |         while ($currentStart <= $this->parameters->get('end')) { | 
					
						
							|  |  |  |             $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); | 
					
						
							|  |  |  |             // If nextExpectedMatch is after end, we continue:
 | 
					
						
							|  |  |  |             if ($nextExpectedMatch > $this->parameters->get('end')) { | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // add to set
 | 
					
						
							|  |  |  |             $set->push(clone $nextExpectedMatch); | 
					
						
							|  |  |  |             $nextExpectedMatch->addDay(); | 
					
						
							|  |  |  |             $currentStart = clone $nextExpectedMatch; | 
					
						
							|  |  |  |             $loop++; | 
					
						
							|  |  |  |             if ($loop > 4) { | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $simple = $set->map( | 
					
						
							|  |  |  |             static function (Carbon $date) { | 
					
						
							|  |  |  |                 return $date->toAtomString(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $simple->toArray(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Given a bill and a date, this method will tell you at which moment this bill expects its next | 
					
						
							|  |  |  |      * transaction. Whether or not it is there already, is not relevant. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2023-10-28 17:17:09 +02:00
										 |  |  |      * TODO this method is bad compared to the v1 one. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |      * @param Bill   $bill | 
					
						
							|  |  |  |      * @param Carbon $date | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return Carbon | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function nextDateMatch(Bill $bill, Carbon $date): Carbon | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-10-06 18:21:49 +02:00
										 |  |  |         //app('log')->debug(sprintf('Now in nextDateMatch(%d, %s)', $bill->id, $date->format('Y-m-d')));
 | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |         $start = clone $bill->date; | 
					
						
							| 
									
										
										
										
											2023-10-06 18:21:49 +02:00
										 |  |  |         //app('log')->debug(sprintf('Bill start date is %s', $start->format('Y-m-d')));
 | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  |         while ($start < $date) { | 
					
						
							|  |  |  |             $start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-06 18:21:49 +02:00
										 |  |  |         //app('log')->debug(sprintf('End of loop, bill start date is now %s', $start->format('Y-m-d')));
 | 
					
						
							| 
									
										
										
										
											2023-08-09 14:14:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return $start; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * @param Carbon $next | 
					
						
							|  |  |  |      * @param array  $dates | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return string | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private function getNextExpectedMatchDiff(Carbon $next, array $dates): string | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if ($next->isToday()) { | 
					
						
							|  |  |  |             return trans('firefly.today'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $current = $dates[0] ?? null; | 
					
						
							|  |  |  |         if (null === $current) { | 
					
						
							|  |  |  |             return trans('firefly.not_expected_period'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $carbon = new Carbon($current); | 
					
						
							|  |  |  |         return $carbon->diffForHumans(today(config('app.timezone')), CarbonInterface::DIFF_RELATIVE_TO_NOW); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |