| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  | <?php | 
					
						
							| 
									
										
										
										
											2020-06-30 19:05:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * GroupValidation.php | 
					
						
							| 
									
										
										
										
											2020-06-30 19:05:35 +02:00
										 |  |  |  * Copyright (c) 2020 james@firefly-iii.org | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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/>. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-30 19:05:35 +02:00
										 |  |  | declare(strict_types=1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  | namespace FireflyIII\Validation; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-26 05:55:31 +02:00
										 |  |  | use FireflyIII\Exceptions\FireflyException; | 
					
						
							| 
									
										
										
										
											2023-10-22 18:44:30 +02:00
										 |  |  | use FireflyIII\Models\Transaction; | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  | use FireflyIII\Models\TransactionGroup; | 
					
						
							| 
									
										
										
										
											2025-10-05 12:59:43 +02:00
										 |  |  | use Illuminate\Validation\Validator; | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Trait GroupValidation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This trait combines some of the validation methods used to validate if journal and group data is submitted correctly. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | trait GroupValidation | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-10-22 18:44:30 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * A catch when users submit splits with no source or destination info at all. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * TODO This should prevent errors down the road but I'm not yet sure what I'm validating here | 
					
						
							|  |  |  |      * TODO so I disabled this on 2023-10-22 to see if it causes any issues. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2023-07-04 13:29:19 +02:00
										 |  |  |      * @throws FireflyException | 
					
						
							| 
									
										
										
										
											2022-03-29 15:00:29 +02:00
										 |  |  |      */ | 
					
						
							|  |  |  |     protected function preventNoAccountInfo(Validator $validator): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $transactions = $this->getTransactionsArray($validator); | 
					
						
							| 
									
										
										
										
											2022-12-29 19:42:40 +01:00
										 |  |  |         $keys         = [ | 
					
						
							|  |  |  |             'source_id', | 
					
						
							|  |  |  |             'destination_id', | 
					
						
							|  |  |  |             'source_name', | 
					
						
							|  |  |  |             'destination_name', | 
					
						
							|  |  |  |             'source_iban', | 
					
						
							|  |  |  |             'destination_iban', | 
					
						
							|  |  |  |             'source_number', | 
					
						
							|  |  |  |             'destination_number', | 
					
						
							|  |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /** @var null|array $transaction */ | 
					
						
							| 
									
										
										
										
											2022-03-29 15:00:29 +02:00
										 |  |  |         foreach ($transactions as $index => $transaction) { | 
					
						
							| 
									
										
										
										
											2023-05-29 13:56:55 +02:00
										 |  |  |             if (!is_array($transaction)) { | 
					
						
							| 
									
										
										
										
											2023-04-26 05:55:31 +02:00
										 |  |  |                 throw new FireflyException('Invalid data submitted: transaction is not array.'); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-03-29 15:00:29 +02:00
										 |  |  |             $hasAccountInfo = false; | 
					
						
							|  |  |  |             $hasJournalId   = array_key_exists('transaction_journal_id', $transaction); | 
					
						
							|  |  |  |             foreach ($keys as $key) { | 
					
						
							| 
									
										
										
										
											2024-12-22 08:43:12 +01:00
										 |  |  |                 if (array_key_exists($key, $transaction) && '' !== (string) $transaction[$key]) { | 
					
						
							| 
									
										
										
										
											2022-03-29 15:00:29 +02:00
										 |  |  |                     $hasAccountInfo = true; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // set errors:
 | 
					
						
							|  |  |  |             if (false === $hasAccountInfo && !$hasJournalId) { | 
					
						
							|  |  |  |                 $validator->errors()->add( | 
					
						
							| 
									
										
										
										
											2022-10-30 14:24:37 +01:00
										 |  |  |                     sprintf('transactions.%d.source_id', $index), | 
					
						
							| 
									
										
										
										
											2024-12-22 08:43:12 +01:00
										 |  |  |                     (string) trans('validation.generic_no_source') | 
					
						
							| 
									
										
										
										
											2022-03-29 15:00:29 +02:00
										 |  |  |                 ); | 
					
						
							|  |  |  |                 $validator->errors()->add( | 
					
						
							| 
									
										
										
										
											2022-10-30 14:24:37 +01:00
										 |  |  |                     sprintf('transactions.%d.destination_id', $index), | 
					
						
							| 
									
										
										
										
											2024-12-22 08:43:12 +01:00
										 |  |  |                     (string) trans('validation.generic_no_destination') | 
					
						
							| 
									
										
										
										
											2022-03-29 15:00:29 +02:00
										 |  |  |                 ); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // only an issue if there is no transaction_journal_id
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 12:34:58 +02:00
										 |  |  |     abstract protected function getTransactionsArray(Validator $validator): array; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-28 15:03:33 +02:00
										 |  |  |     protected function preventUpdateReconciled(Validator $validator, TransactionGroup $transactionGroup): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         app('log')->debug(sprintf('Now in %s', __METHOD__)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |         $count     = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id') | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  |             ->leftJoin('transaction_groups', 'transaction_groups.id', 'transaction_journals.transaction_group_id') | 
					
						
							|  |  |  |             ->where('transaction_journals.transaction_group_id', $transactionGroup->id) | 
					
						
							|  |  |  |             ->where('transactions.reconciled', 1)->where('transactions.amount', '<', 0)->count('transactions.id') | 
					
						
							|  |  |  |         ; | 
					
						
							| 
									
										
										
										
											2023-10-28 15:03:33 +02:00
										 |  |  |         if (0 === $count) { | 
					
						
							|  |  |  |             app('log')->debug(sprintf('Transaction is not reconciled, done with %s', __METHOD__)); | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-28 15:03:33 +02:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $data      = $validator->getData(); | 
					
						
							|  |  |  |         $forbidden = ['amount', 'foreign_amount', 'currency_code', 'currency_id', 'foreign_currency_code', 'foreign_currency_id', | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  |             'source_id', 'source_name', 'source_number', 'source_iban', | 
					
						
							|  |  |  |             'destination_id', 'destination_name', 'destination_number', 'destination_iban', | 
					
						
							| 
									
										
										
										
											2023-10-28 15:03:33 +02:00
										 |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2024-06-15 09:25:41 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // stop protesting when reconciliation is set to FALSE.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-28 15:03:33 +02:00
										 |  |  |         foreach ($data['transactions'] as $index => $row) { | 
					
						
							| 
									
										
										
										
											2024-06-15 09:25:41 +02:00
										 |  |  |             if (false === ($row['reconciled'] ?? false)) { | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-10-28 15:03:33 +02:00
										 |  |  |             foreach ($forbidden as $key) { | 
					
						
							|  |  |  |                 if (array_key_exists($key, $row)) { | 
					
						
							|  |  |  |                     $validator->errors()->add( | 
					
						
							|  |  |  |                         sprintf('transactions.%d.%s', $index, $key), | 
					
						
							| 
									
										
										
										
											2024-12-22 08:43:12 +01:00
										 |  |  |                         (string) trans('validation.reconciled_forbidden_field', ['field' => $key]) | 
					
						
							| 
									
										
										
										
											2023-10-28 15:03:33 +02:00
										 |  |  |                     ); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         app('log')->debug(sprintf('Done with %s', __METHOD__)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2021-03-21 09:15:40 +01:00
										 |  |  |      * Adds an error to the "description" field when the user has submitted no descriptions and no | 
					
						
							|  |  |  |      * journal description. | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-03-21 09:15:40 +01:00
										 |  |  |     protected function validateDescriptions(Validator $validator): void | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-03-09 06:33:23 +01:00
										 |  |  |         if ($validator->errors()->count() > 0) { | 
					
						
							| 
									
										
										
										
											2023-03-07 19:36:54 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-29 06:33:43 +01:00
										 |  |  |         app('log')->debug('Now in GroupValidation::validateDescriptions()'); | 
					
						
							| 
									
										
										
										
											2021-03-21 09:15:40 +01:00
										 |  |  |         $transactions      = $this->getTransactionsArray($validator); | 
					
						
							|  |  |  |         $validDescriptions = 0; | 
					
						
							|  |  |  |         foreach ($transactions as $transaction) { | 
					
						
							| 
									
										
										
										
											2024-12-22 08:43:12 +01:00
										 |  |  |             if ('' !== (string) ($transaction['description'] ?? null)) { | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  |                 ++$validDescriptions; | 
					
						
							| 
									
										
										
										
											2021-03-21 09:15:40 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // no valid descriptions?
 | 
					
						
							|  |  |  |         if (0 === $validDescriptions) { | 
					
						
							|  |  |  |             $validator->errors()->add( | 
					
						
							| 
									
										
										
										
											2022-10-30 14:24:37 +01:00
										 |  |  |                 'transactions.0.description', | 
					
						
							| 
									
										
										
										
											2024-12-22 08:43:12 +01:00
										 |  |  |                 (string) trans('validation.filled', ['attribute' => (string) trans('validation.attributes.description')]) | 
					
						
							| 
									
										
										
										
											2021-03-21 09:15:40 +01:00
										 |  |  |             ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected function validateGroupDescription(Validator $validator): void | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-03-09 06:33:23 +01:00
										 |  |  |         if ($validator->errors()->count() > 0) { | 
					
						
							| 
									
										
										
										
											2023-03-07 19:36:54 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-29 06:33:43 +01:00
										 |  |  |         app('log')->debug('Now in validateGroupDescription()'); | 
					
						
							| 
									
										
										
										
											2021-03-21 09:15:40 +01:00
										 |  |  |         $data         = $validator->getData(); | 
					
						
							|  |  |  |         $transactions = $this->getTransactionsArray($validator); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |         $groupTitle   = $data['group_title'] ?? ''; | 
					
						
							| 
									
										
										
										
											2021-03-21 09:15:40 +01:00
										 |  |  |         if ('' === $groupTitle && count($transactions) > 1) { | 
					
						
							| 
									
										
										
										
											2024-12-22 08:43:12 +01:00
										 |  |  |             $validator->errors()->add('group_title', (string) trans('validation.group_title_mandatory')); | 
					
						
							| 
									
										
										
										
											2021-03-21 09:15:40 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2023-06-21 12:34:58 +02:00
										 |  |  |      * This method validates if the user has submitted transaction journal ID's for each array they submit, if they've | 
					
						
							|  |  |  |      * submitted more than 1 transaction journal. This check is necessary because Firefly III isn't able to distinguish | 
					
						
							|  |  |  |      * between journals without the ID. | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  |      */ | 
					
						
							|  |  |  |     protected function validateJournalIds(Validator $validator, TransactionGroup $transactionGroup): void | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-10-29 06:33:43 +01:00
										 |  |  |         app('log')->debug(sprintf('Now in GroupValidation::validateJournalIds(%d)', $transactionGroup->id)); | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  |         $transactions = $this->getTransactionsArray($validator); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (count($transactions) < 2) { | 
					
						
							|  |  |  |             // no need for validation.
 | 
					
						
							| 
									
										
										
										
											2023-10-29 06:33:43 +01:00
										 |  |  |             app('log')->debug(sprintf('%d transaction(s) in submission, can skip this check.', count($transactions))); | 
					
						
							| 
									
										
										
										
											2021-03-21 09:15:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  |         // check each array:
 | 
					
						
							|  |  |  |         /** | 
					
						
							| 
									
										
										
										
											2023-06-21 12:34:58 +02:00
										 |  |  |          * @var int   $index | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  |          * @var array $transaction | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         foreach ($transactions as $index => $transaction) { | 
					
						
							|  |  |  |             $this->validateJournalId($validator, $index, $transaction, $transactionGroup); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Do the validation required by validateJournalIds. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-03-10 06:34:03 +01:00
										 |  |  |     private function validateJournalId(Validator $validator, int $index, array $transaction, TransactionGroup $transactionGroup): void | 
					
						
							| 
									
										
										
										
											2020-03-21 05:56:27 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-04-04 08:31:15 +02:00
										 |  |  |         $journalId = 0; | 
					
						
							|  |  |  |         if (array_key_exists('transaction_journal_id', $transaction)) { | 
					
						
							|  |  |  |             $journalId = $transaction['transaction_journal_id']; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-29 06:33:43 +01:00
										 |  |  |         app('log')->debug(sprintf('Now in validateJournalId(%d, %d)', $index, $journalId)); | 
					
						
							| 
									
										
										
										
											2023-06-24 08:27:28 +02:00
										 |  |  |         if (0 === $journalId || '' === $journalId || '0' === $journalId) { | 
					
						
							| 
									
										
										
										
											2023-10-29 06:33:43 +01:00
										 |  |  |             app('log')->debug('Submitted 0, will accept to be used in a new transaction.'); | 
					
						
							| 
									
										
										
										
											2021-03-10 06:34:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-04 08:31:15 +02:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-12-22 08:43:12 +01:00
										 |  |  |         $journalId = (int) $journalId; | 
					
						
							| 
									
										
										
										
											2023-07-15 16:02:42 +02:00
										 |  |  |         $count     = $transactionGroup->transactionJournals()->where('transaction_journals.id', $journalId)->count(); | 
					
						
							| 
									
										
										
										
											2023-11-04 07:18:03 +01:00
										 |  |  |         if (0 === $journalId || 0 === $count) { | 
					
						
							| 
									
										
										
										
											2022-10-30 14:44:49 +01:00
										 |  |  |             app('log')->warning(sprintf('Transaction group #%d has %d journals with ID %d', $transactionGroup->id, $count, $journalId)); | 
					
						
							|  |  |  |             app('log')->warning('Invalid submission: Each split must have transaction_journal_id (either valid ID or 0).'); | 
					
						
							| 
									
										
										
										
											2024-12-22 08:43:12 +01:00
										 |  |  |             $validator->errors()->add(sprintf('transactions.%d.source_name', $index), (string) trans('validation.need_id_in_edit')); | 
					
						
							| 
									
										
										
										
											2020-03-21 06:01:27 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-05-30 07:33:06 +02:00
										 |  |  | } |