From c14ec8b006ea5a65dc3326c568597be6add5e75f Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 7 Jun 2016 12:22:30 +0200 Subject: [PATCH 01/94] New change log. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b5e4cf7af..d3ddc1bd4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - No unreleased changes yet. +[3.9.1] +### Fixed +- Fixed a bug where removing money from a piggy bank would not work. See issue #265 and #269 + [3.9.0] ### Added - @zjean has added code that allows you to force "https://"-URL's. From c619b8730b5d421160277cc9e65049c4a3fd7e9a Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 7 Jun 2016 12:22:46 +0200 Subject: [PATCH 02/94] Implemented #264 --- app/Http/Controllers/BudgetController.php | 2 ++ app/Repositories/Budget/BudgetRepository.php | 12 ++++++++++++ .../Budget/BudgetRepositoryInterface.php | 5 +++++ 3 files changed, 19 insertions(+) diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index a858e82252..e6f517c9a8 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -164,6 +164,8 @@ class BudgetController extends Controller */ public function index(BudgetRepositoryInterface $repository, AccountCrudInterface $crud) { + $repository->cleanupBudgets(); + $budgets = $repository->getActiveBudgets(); $inactive = $repository->getInactiveBudgets(); $spent = '0'; diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 04d63893a5..b104c8f292 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -156,6 +156,18 @@ class BudgetRepository implements BudgetRepositoryInterface return $set; } + /** + * @return bool + */ + public function cleanupBudgets(): bool + { + // delete limits with amount 0: + BudgetLimit::where('amount', 0)->delete(); + + return true; + + } + /** * @return Collection */ diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index 6feeb5336c..31bf8d4ed2 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -111,6 +111,11 @@ interface BudgetRepositoryInterface */ public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) : string; + /** + * @return bool + */ + public function cleanupBudgets(): bool; + /** * @param array $data * From beda6ec3a9f3d38673ce1ebfb6e4a2059c3d0f6e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 10 Jun 2016 15:25:24 +0200 Subject: [PATCH 03/94] Removed old CSV stuff. --- app/Helpers/Csv/Converter/AccountId.php | 35 -- app/Helpers/Csv/Converter/Amount.php | 32 -- app/Helpers/Csv/Converter/AmountComma.php | 36 -- .../Csv/Converter/AssetAccountIban.php | 89 ---- .../Csv/Converter/AssetAccountName.php | 65 --- .../Csv/Converter/AssetAccountNumber.php | 77 --- app/Helpers/Csv/Converter/BasicConverter.php | 114 ----- app/Helpers/Csv/Converter/BillId.php | 36 -- app/Helpers/Csv/Converter/BillName.php | 47 -- app/Helpers/Csv/Converter/BudgetId.php | 36 -- app/Helpers/Csv/Converter/BudgetName.php | 44 -- app/Helpers/Csv/Converter/CategoryId.php | 36 -- app/Helpers/Csv/Converter/CategoryName.php | 49 -- .../Csv/Converter/ConverterInterface.php | 52 -- app/Helpers/Csv/Converter/CurrencyCode.php | 43 -- app/Helpers/Csv/Converter/CurrencyId.php | 36 -- app/Helpers/Csv/Converter/CurrencyName.php | 42 -- app/Helpers/Csv/Converter/CurrencySymbol.php | 43 -- app/Helpers/Csv/Converter/Date.php | 46 -- app/Helpers/Csv/Converter/Description.php | 31 -- app/Helpers/Csv/Converter/INGDebetCredit.php | 34 -- app/Helpers/Csv/Converter/Ignore.php | 28 -- .../Csv/Converter/OpposingAccountIban.php | 64 --- .../Csv/Converter/OpposingAccountId.php | 35 -- .../Csv/Converter/OpposingAccountName.php | 41 -- .../Csv/Converter/RabobankDebetCredit.php | 34 -- app/Helpers/Csv/Converter/TagsComma.php | 53 --- app/Helpers/Csv/Converter/TagsSpace.php | 54 --- app/Helpers/Csv/Data.php | 336 ------------- app/Helpers/Csv/Importer.php | 384 --------------- app/Helpers/Csv/Mapper/AnyAccount.php | 42 -- app/Helpers/Csv/Mapper/AssetAccount.php | 54 --- app/Helpers/Csv/Mapper/Bill.php | 42 -- app/Helpers/Csv/Mapper/Budget.php | 42 -- app/Helpers/Csv/Mapper/Category.php | 42 -- app/Helpers/Csv/Mapper/MapperInterface.php | 24 - app/Helpers/Csv/Mapper/Tag.php | 42 -- .../Csv/Mapper/TransactionCurrency.php | 40 -- app/Helpers/Csv/PostProcessing/Amount.php | 44 -- .../Csv/PostProcessing/AssetAccount.php | 274 ----------- app/Helpers/Csv/PostProcessing/Bill.php | 45 -- app/Helpers/Csv/PostProcessing/Currency.php | 49 -- .../Csv/PostProcessing/Description.php | 47 -- .../Csv/PostProcessing/OpposingAccount.php | 210 --------- .../PostProcessing/PostProcessorInterface.php | 31 -- .../Csv/Specifix/AbnAmroDescription.php | 226 --------- app/Helpers/Csv/Specifix/Dummy.php | 60 --- .../Csv/Specifix/RabobankDescription.php | 76 --- app/Helpers/Csv/Specifix/Specifix.php | 46 -- .../Csv/Specifix/SpecifixInterface.php | 49 -- app/Helpers/Csv/Wizard.php | 202 -------- app/Helpers/Csv/WizardInterface.php | 68 --- app/Http/Controllers/CsvController.php | 444 ------------------ app/Http/breadcrumbs.php | 38 -- app/Http/routes.php | 15 - app/Providers/FireflyServiceProvider.php | 1 - 56 files changed, 4305 deletions(-) delete mode 100644 app/Helpers/Csv/Converter/AccountId.php delete mode 100644 app/Helpers/Csv/Converter/Amount.php delete mode 100644 app/Helpers/Csv/Converter/AmountComma.php delete mode 100644 app/Helpers/Csv/Converter/AssetAccountIban.php delete mode 100644 app/Helpers/Csv/Converter/AssetAccountName.php delete mode 100644 app/Helpers/Csv/Converter/AssetAccountNumber.php delete mode 100644 app/Helpers/Csv/Converter/BasicConverter.php delete mode 100644 app/Helpers/Csv/Converter/BillId.php delete mode 100644 app/Helpers/Csv/Converter/BillName.php delete mode 100644 app/Helpers/Csv/Converter/BudgetId.php delete mode 100644 app/Helpers/Csv/Converter/BudgetName.php delete mode 100644 app/Helpers/Csv/Converter/CategoryId.php delete mode 100644 app/Helpers/Csv/Converter/CategoryName.php delete mode 100644 app/Helpers/Csv/Converter/ConverterInterface.php delete mode 100644 app/Helpers/Csv/Converter/CurrencyCode.php delete mode 100644 app/Helpers/Csv/Converter/CurrencyId.php delete mode 100644 app/Helpers/Csv/Converter/CurrencyName.php delete mode 100644 app/Helpers/Csv/Converter/CurrencySymbol.php delete mode 100644 app/Helpers/Csv/Converter/Date.php delete mode 100644 app/Helpers/Csv/Converter/Description.php delete mode 100644 app/Helpers/Csv/Converter/INGDebetCredit.php delete mode 100644 app/Helpers/Csv/Converter/Ignore.php delete mode 100644 app/Helpers/Csv/Converter/OpposingAccountIban.php delete mode 100644 app/Helpers/Csv/Converter/OpposingAccountId.php delete mode 100644 app/Helpers/Csv/Converter/OpposingAccountName.php delete mode 100644 app/Helpers/Csv/Converter/RabobankDebetCredit.php delete mode 100644 app/Helpers/Csv/Converter/TagsComma.php delete mode 100644 app/Helpers/Csv/Converter/TagsSpace.php delete mode 100644 app/Helpers/Csv/Data.php delete mode 100644 app/Helpers/Csv/Importer.php delete mode 100644 app/Helpers/Csv/Mapper/AnyAccount.php delete mode 100644 app/Helpers/Csv/Mapper/AssetAccount.php delete mode 100644 app/Helpers/Csv/Mapper/Bill.php delete mode 100644 app/Helpers/Csv/Mapper/Budget.php delete mode 100644 app/Helpers/Csv/Mapper/Category.php delete mode 100644 app/Helpers/Csv/Mapper/MapperInterface.php delete mode 100644 app/Helpers/Csv/Mapper/Tag.php delete mode 100644 app/Helpers/Csv/Mapper/TransactionCurrency.php delete mode 100644 app/Helpers/Csv/PostProcessing/Amount.php delete mode 100644 app/Helpers/Csv/PostProcessing/AssetAccount.php delete mode 100644 app/Helpers/Csv/PostProcessing/Bill.php delete mode 100644 app/Helpers/Csv/PostProcessing/Currency.php delete mode 100644 app/Helpers/Csv/PostProcessing/Description.php delete mode 100644 app/Helpers/Csv/PostProcessing/OpposingAccount.php delete mode 100644 app/Helpers/Csv/PostProcessing/PostProcessorInterface.php delete mode 100644 app/Helpers/Csv/Specifix/AbnAmroDescription.php delete mode 100644 app/Helpers/Csv/Specifix/Dummy.php delete mode 100644 app/Helpers/Csv/Specifix/RabobankDescription.php delete mode 100644 app/Helpers/Csv/Specifix/Specifix.php delete mode 100644 app/Helpers/Csv/Specifix/SpecifixInterface.php delete mode 100644 app/Helpers/Csv/Wizard.php delete mode 100644 app/Helpers/Csv/WizardInterface.php delete mode 100644 app/Http/Controllers/CsvController.php diff --git a/app/Helpers/Csv/Converter/AccountId.php b/app/Helpers/Csv/Converter/AccountId.php deleted file mode 100644 index 908cd132de..0000000000 --- a/app/Helpers/Csv/Converter/AccountId.php +++ /dev/null @@ -1,35 +0,0 @@ -mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value; - $account = $crud->find($var); - - return $account; - } -} diff --git a/app/Helpers/Csv/Converter/Amount.php b/app/Helpers/Csv/Converter/Amount.php deleted file mode 100644 index 8e2cfc4d0e..0000000000 --- a/app/Helpers/Csv/Converter/Amount.php +++ /dev/null @@ -1,32 +0,0 @@ -value)) { - return strval($this->value); - } - - return '0'; - } -} diff --git a/app/Helpers/Csv/Converter/AmountComma.php b/app/Helpers/Csv/Converter/AmountComma.php deleted file mode 100644 index 8cddad6dc8..0000000000 --- a/app/Helpers/Csv/Converter/AmountComma.php +++ /dev/null @@ -1,36 +0,0 @@ -value)); - - if (is_numeric($value)) { - return strval($value); - } - - return '0'; - } -} diff --git a/app/Helpers/Csv/Converter/AssetAccountIban.php b/app/Helpers/Csv/Converter/AssetAccountIban.php deleted file mode 100644 index 4da3a73625..0000000000 --- a/app/Helpers/Csv/Converter/AssetAccountIban.php +++ /dev/null @@ -1,89 +0,0 @@ -mapped[$this->index][$this->value])) { - $account = $crud->find(intval($this->mapped[$this->index][$this->value])); - - return $account; - } - - - if (strlen($this->value) > 0) { - $account = $this->searchOrCreate($crud); - - return $account; - } - - return new Account; - } - - /** - * @param AccountCrudInterface $crud - * - * @return Account - */ - private function searchOrCreate(AccountCrudInterface $crud) - { - // find or create new account: - $set = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); - /** @var Account $entry */ - foreach ($set as $entry) { - if ($entry->iban == $this->value) { - - return $entry; - } - } - - - // create it if doesn't exist. - $accountData = [ - 'name' => $this->value, - 'accountType' => 'asset', - 'virtualBalance' => 0, - 'virtualBalanceCurrency' => 1, // hard coded. - 'active' => true, - 'user' => Auth::user()->id, - 'iban' => $this->value, - 'accountNumber' => $this->value, - 'accountRole' => null, - 'openingBalance' => 0, - 'openingBalanceDate' => new Carbon, - 'openingBalanceCurrency' => 1, // hard coded. - ]; - - $account = $crud->store($accountData); - - return $account; - } -} diff --git a/app/Helpers/Csv/Converter/AssetAccountName.php b/app/Helpers/Csv/Converter/AssetAccountName.php deleted file mode 100644 index e56620415e..0000000000 --- a/app/Helpers/Csv/Converter/AssetAccountName.php +++ /dev/null @@ -1,65 +0,0 @@ -mapped[$this->index][$this->value])) { - $account = $crud->find(intval($this->mapped[$this->index][$this->value])); - - return $account; - } - - $set = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); - /** @var Account $entry */ - foreach ($set as $entry) { - if ($entry->name == $this->value) { - return $entry; - } - } - $accountData = [ - 'name' => $this->value, - 'accountType' => 'asset', - 'virtualBalance' => 0, - 'virtualBalanceCurrency' => 1, // hard coded. - 'active' => true, - 'user' => Auth::user()->id, - 'iban' => null, - 'accountNumber' => $this->value, - 'accountRole' => null, - 'openingBalance' => 0, - 'openingBalanceDate' => new Carbon, - 'openingBalanceCurrency' => 1, // hard coded. - ]; - - $account = $crud->store($accountData); - - return $account; - } -} diff --git a/app/Helpers/Csv/Converter/AssetAccountNumber.php b/app/Helpers/Csv/Converter/AssetAccountNumber.php deleted file mode 100644 index 5b8efed50a..0000000000 --- a/app/Helpers/Csv/Converter/AssetAccountNumber.php +++ /dev/null @@ -1,77 +0,0 @@ -mapped[$this->index][$this->value])) { - $account = $crud->find(intval($this->mapped[$this->index][$this->value])); - - return $account; - } - // if not, search for it (or create it): - $value = $this->value ?? ''; - if (strlen($value) > 0) { - // find or create new account: - $set = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); - /** @var Account $entry */ - foreach ($set as $entry) { - $accountNumber = $entry->getMeta('accountNumber'); - if ($accountNumber == $this->value) { - - return $entry; - } - } - - $accountData = [ - 'name' => $this->value, - 'accountType' => 'asset', - 'virtualBalance' => 0, - 'virtualBalanceCurrency' => 1, // hard coded. - 'active' => true, - 'user' => Auth::user()->id, - 'iban' => null, - 'accountNumber' => $this->value, - 'accountRole' => null, - 'openingBalance' => 0, - 'openingBalanceDate' => new Carbon, - 'openingBalanceCurrency' => 1, // hard coded. - - ]; - - $account = $crud->store($accountData); - - return $account; - } - - return null; // is this accepted? - } - -} diff --git a/app/Helpers/Csv/Converter/BasicConverter.php b/app/Helpers/Csv/Converter/BasicConverter.php deleted file mode 100644 index 63e5f8bb96..0000000000 --- a/app/Helpers/Csv/Converter/BasicConverter.php +++ /dev/null @@ -1,114 +0,0 @@ -data; - } - - /** - * @param array $data - */ - public function setData(array $data) - { - $this->data = $data; - } - - /** - * @return string - */ - public function getField(): string - { - return $this->field; - } - - /** - * @param string $field - */ - public function setField(string $field) - { - $this->field = $field; - } - - /** - * @return int - */ - public function getIndex(): int - { - return $this->index; - } - - /** - * @param int $index - */ - public function setIndex(int $index) - { - $this->index = $index; - } - - /** - * @return array - */ - public function getMapped(): array - { - return $this->mapped; - } - - /** - * @param array $mapped - */ - public function setMapped(array $mapped) - { - $this->mapped = $mapped; - } - - /** - * @return string - */ - public function getValue(): string - { - return $this->value; - } - - /** - * @param string $value - */ - public function setValue(string $value) - { - $this->value = $value; - } - - -} diff --git a/app/Helpers/Csv/Converter/BillId.php b/app/Helpers/Csv/Converter/BillId.php deleted file mode 100644 index 3e9102ccec..0000000000 --- a/app/Helpers/Csv/Converter/BillId.php +++ /dev/null @@ -1,36 +0,0 @@ -mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value; - $bill = $repository->find($value); - - return $bill; - } -} diff --git a/app/Helpers/Csv/Converter/BillName.php b/app/Helpers/Csv/Converter/BillName.php deleted file mode 100644 index a1328ef6c1..0000000000 --- a/app/Helpers/Csv/Converter/BillName.php +++ /dev/null @@ -1,47 +0,0 @@ -mapped[$this->index][$this->value])) { - return $repository->find($this->mapped[$this->index][$this->value]); - } - $bills = $repository->getBills(); - - /** @var Bill $bill */ - foreach ($bills as $bill) { - if ($bill->name == $this->value) { - return $bill; - } - } - - return new Bill; - } -} diff --git a/app/Helpers/Csv/Converter/BudgetId.php b/app/Helpers/Csv/Converter/BudgetId.php deleted file mode 100644 index d9302d7734..0000000000 --- a/app/Helpers/Csv/Converter/BudgetId.php +++ /dev/null @@ -1,36 +0,0 @@ -mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value; - $budget = $repository->find($value); - - return $budget; - } -} diff --git a/app/Helpers/Csv/Converter/BudgetName.php b/app/Helpers/Csv/Converter/BudgetName.php deleted file mode 100644 index 84f777775b..0000000000 --- a/app/Helpers/Csv/Converter/BudgetName.php +++ /dev/null @@ -1,44 +0,0 @@ -mapped[$this->index][$this->value])) { - $budget = $repository->find($this->mapped[$this->index][$this->value]); - - return $budget; - } - $budget = $repository->store(['name' => $this->value, 'user' => Auth::user()->id]); - - - return $budget; - } -} diff --git a/app/Helpers/Csv/Converter/CategoryId.php b/app/Helpers/Csv/Converter/CategoryId.php deleted file mode 100644 index 8ffedd9ae1..0000000000 --- a/app/Helpers/Csv/Converter/CategoryId.php +++ /dev/null @@ -1,36 +0,0 @@ -mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value; - $category = $repository->find($value); - - return $category; - } -} diff --git a/app/Helpers/Csv/Converter/CategoryName.php b/app/Helpers/Csv/Converter/CategoryName.php deleted file mode 100644 index d322f7b02f..0000000000 --- a/app/Helpers/Csv/Converter/CategoryName.php +++ /dev/null @@ -1,49 +0,0 @@ -mapped[$this->index][$this->value])) { - $category = $repository->find($this->mapped[$this->index][$this->value]); - - return $category; - } - - $data = [ - 'name' => $this->value, - 'user' => Auth::user()->id, - ]; - - $category = $repository->store($data); - - return $category; - } -} diff --git a/app/Helpers/Csv/Converter/ConverterInterface.php b/app/Helpers/Csv/Converter/ConverterInterface.php deleted file mode 100644 index c8fb526b83..0000000000 --- a/app/Helpers/Csv/Converter/ConverterInterface.php +++ /dev/null @@ -1,52 +0,0 @@ -mapped[$this->index][$this->value])) { - $currency = $repository->find(intval($this->mapped[$this->index][$this->value])); - - return $currency; - } - - $currency = $repository->findByCode($this->value); - - - return $currency; - } -} diff --git a/app/Helpers/Csv/Converter/CurrencyId.php b/app/Helpers/Csv/Converter/CurrencyId.php deleted file mode 100644 index a18082872a..0000000000 --- a/app/Helpers/Csv/Converter/CurrencyId.php +++ /dev/null @@ -1,36 +0,0 @@ -mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value; - $currency = $repository->find($value); - - return $currency; - } -} diff --git a/app/Helpers/Csv/Converter/CurrencyName.php b/app/Helpers/Csv/Converter/CurrencyName.php deleted file mode 100644 index 62876fa9ba..0000000000 --- a/app/Helpers/Csv/Converter/CurrencyName.php +++ /dev/null @@ -1,42 +0,0 @@ -mapped[$this->index][$this->value])) { - $currency = $repository->find($this->mapped[$this->index][$this->value]); - - return $currency; - } - $currency = $repository->findByName($this->value); - - - return $currency; - } -} diff --git a/app/Helpers/Csv/Converter/CurrencySymbol.php b/app/Helpers/Csv/Converter/CurrencySymbol.php deleted file mode 100644 index 51037ece2e..0000000000 --- a/app/Helpers/Csv/Converter/CurrencySymbol.php +++ /dev/null @@ -1,43 +0,0 @@ -mapped[$this->index][$this->value])) { - $currency = $repository->find($this->mapped[$this->index][$this->value]); - - return $currency; - } - - $currency = $repository->findBySymbol($this->value); - - - return $currency; - } -} diff --git a/app/Helpers/Csv/Converter/Date.php b/app/Helpers/Csv/Converter/Date.php deleted file mode 100644 index b164e9df5f..0000000000 --- a/app/Helpers/Csv/Converter/Date.php +++ /dev/null @@ -1,46 +0,0 @@ -value); - } catch (InvalidArgumentException $e) { - Log::error('Date conversion error: ' . $e->getMessage() . '. Value was "' . $this->value . '", format was "' . $format . '".'); - - $message = trans('firefly.csv_date_parse_error', ['format' => $format, 'value' => $this->value]); - - throw new FireflyException($message); - } - - - return $date; - } -} diff --git a/app/Helpers/Csv/Converter/Description.php b/app/Helpers/Csv/Converter/Description.php deleted file mode 100644 index ea864a3e3e..0000000000 --- a/app/Helpers/Csv/Converter/Description.php +++ /dev/null @@ -1,31 +0,0 @@ -data['description'] ?? ''; - - return trim($description . ' ' . $this->value); - } -} diff --git a/app/Helpers/Csv/Converter/INGDebetCredit.php b/app/Helpers/Csv/Converter/INGDebetCredit.php deleted file mode 100644 index 59fd76248a..0000000000 --- a/app/Helpers/Csv/Converter/INGDebetCredit.php +++ /dev/null @@ -1,34 +0,0 @@ -value === 'Af') { - return -1; - } - - return 1; - } -} diff --git a/app/Helpers/Csv/Converter/Ignore.php b/app/Helpers/Csv/Converter/Ignore.php deleted file mode 100644 index c2da8c3033..0000000000 --- a/app/Helpers/Csv/Converter/Ignore.php +++ /dev/null @@ -1,28 +0,0 @@ -mapped[$this->index][$this->value])) { - $account = $crud->find($this->mapped[$this->index][$this->value]); - - return $account; - } - - return $this->findAccount($crud); - } - - /** - * @param AccountCrudInterface $crud - * - * @return Account|string - */ - private function findAccount(AccountCrudInterface $crud) - { - if (strlen($this->value) > 0) { - - $set = $crud->getAccountsByType([]); - /** @var Account $account */ - foreach ($set as $account) { - if ($account->iban == $this->value) { - - return $account; - } - } - } - - return $this->value; - } - -} diff --git a/app/Helpers/Csv/Converter/OpposingAccountId.php b/app/Helpers/Csv/Converter/OpposingAccountId.php deleted file mode 100644 index 17836907bf..0000000000 --- a/app/Helpers/Csv/Converter/OpposingAccountId.php +++ /dev/null @@ -1,35 +0,0 @@ -mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value; - $account = $crud->find($value); - - return $account; - } -} diff --git a/app/Helpers/Csv/Converter/OpposingAccountName.php b/app/Helpers/Csv/Converter/OpposingAccountName.php deleted file mode 100644 index 7c3d392985..0000000000 --- a/app/Helpers/Csv/Converter/OpposingAccountName.php +++ /dev/null @@ -1,41 +0,0 @@ -mapped[$this->index][$this->value])) { - $account = $crud->find($this->mapped[$this->index][$this->value]); - - return $account; - } - - return $this->value; - - } -} diff --git a/app/Helpers/Csv/Converter/RabobankDebetCredit.php b/app/Helpers/Csv/Converter/RabobankDebetCredit.php deleted file mode 100644 index 854f812d45..0000000000 --- a/app/Helpers/Csv/Converter/RabobankDebetCredit.php +++ /dev/null @@ -1,34 +0,0 @@ -value == 'D') { - return -1; - } - - return 1; - } -} diff --git a/app/Helpers/Csv/Converter/TagsComma.php b/app/Helpers/Csv/Converter/TagsComma.php deleted file mode 100644 index c9477e7ada..0000000000 --- a/app/Helpers/Csv/Converter/TagsComma.php +++ /dev/null @@ -1,53 +0,0 @@ -value); - foreach ($strings as $string) { - $data = [ - 'tag' => $string, - 'date' => null, - 'description' => null, - 'latitude' => null, - 'longitude' => null, - 'zoomLevel' => null, - 'tagMode' => 'nothing', - ]; - if (strlen($string) > 0) { - $tag = $repository->store($data); // should validate first? - $tags->push($tag); - } - } - $tags = $tags->merge($this->data['tags']); - - return $tags; - } -} diff --git a/app/Helpers/Csv/Converter/TagsSpace.php b/app/Helpers/Csv/Converter/TagsSpace.php deleted file mode 100644 index aa2694a330..0000000000 --- a/app/Helpers/Csv/Converter/TagsSpace.php +++ /dev/null @@ -1,54 +0,0 @@ -value); - foreach ($strings as $string) { - $data = [ - 'tag' => $string, - 'date' => null, - 'description' => null, - 'latitude' => null, - 'longitude' => null, - 'zoomLevel' => null, - 'tagMode' => 'nothing', - ]; - if (strlen($string) > 0) { - $tag = $repository->store($data); // should validate first? - $tags->push($tag); - } - } - $tags = $tags->merge($this->data['tags']); - - return $tags; - } -} diff --git a/app/Helpers/Csv/Data.php b/app/Helpers/Csv/Data.php deleted file mode 100644 index d56f790204..0000000000 --- a/app/Helpers/Csv/Data.php +++ /dev/null @@ -1,336 +0,0 @@ -sessionHasHeaders(); - $this->sessionDateFormat(); - $this->sessionCsvFileLocation(); - $this->sessionMap(); - $this->sessionRoles(); - $this->sessionMapped(); - $this->sessionSpecifix(); - $this->sessionImportAccount(); - $this->sessionDelimiter(); - } - - /** - * - * @return string - */ - public function getCsvFileContent(): string - { - return $this->csvFileContent ?? ''; - } - - /** - * - * @param string $csvFileContent - */ - public function setCsvFileContent(string $csvFileContent) - { - $this->csvFileContent = $csvFileContent; - } - - /** - * FIXxME may return null - * - * @return string - */ - public function getCsvFileLocation(): string - { - return $this->csvFileLocation; - } - - /** - * - * @param string $csvFileLocation - */ - public function setCsvFileLocation(string $csvFileLocation) - { - Session::put('csv-file', $csvFileLocation); - $this->csvFileLocation = $csvFileLocation; - } - - /** - * FIXxME may return null - * - * @return string - */ - public function getDateFormat(): string - { - return $this->dateFormat; - } - - /** - * - * @param string $dateFormat - */ - public function setDateFormat(string $dateFormat) - { - Session::put('csv-date-format', $dateFormat); - $this->dateFormat = $dateFormat; - } - - /** - * FIXxME may return null - * - * @return string - */ - public function getDelimiter(): string - { - return $this->delimiter; - } - - /** - * - * @param string $delimiter - */ - public function setDelimiter(string $delimiter) - { - Session::put('csv-delimiter', $delimiter); - $this->delimiter = $delimiter; - } - - /** - * - * @return array - */ - public function getMap(): array - { - return $this->map; - } - - /** - * - * @param array $map - */ - public function setMap(array $map) - { - Session::put('csv-map', $map); - $this->map = $map; - } - - /** - * - * @return array - */ - public function getMapped(): array - { - return $this->mapped; - } - - /** - * - * @param array $mapped - */ - public function setMapped(array $mapped) - { - Session::put('csv-mapped', $mapped); - $this->mapped = $mapped; - } - - /** - * - * @return Reader - */ - public function getReader(): Reader - { - if (!is_null($this->csvFileContent) && strlen($this->csvFileContent) === 0) { - $this->loadCsvFile(); - } - - if (is_null($this->reader)) { - $this->reader = Reader::createFromString($this->getCsvFileContent()); - $this->reader->setDelimiter($this->delimiter); - } - - return $this->reader; - } - - /** - * - * @return array - */ - public function getRoles(): array - { - return $this->roles; - } - - /** - * - * @param array $roles - */ - public function setRoles(array $roles) - { - Session::put('csv-roles', $roles); - $this->roles = $roles; - } - - /** - * - * @return array - */ - public function getSpecifix(): array - { - return is_array($this->specifix) ? $this->specifix : []; - } - - /** - * - * @param array $specifix - */ - public function setSpecifix(array $specifix) - { - Session::put('csv-specifix', $specifix); - $this->specifix = $specifix; - } - - /** - * - * @return bool - */ - public function hasHeaders(): bool - { - return $this->hasHeaders; - } - - /** - * - * @param bool $hasHeaders - */ - public function setHasHeaders(bool $hasHeaders) - { - Session::put('csv-has-headers', $hasHeaders); - $this->hasHeaders = $hasHeaders; - } - - /** - * - * @param int $importAccount - */ - public function setImportAccount(int $importAccount) - { - Session::put('csv-import-account', $importAccount); - $this->importAccount = $importAccount; - } - - protected function loadCsvFile() - { - $file = $this->getCsvFileLocation(); - $disk = Storage::disk('upload'); - $content = $disk->get($file); - $contentDecrypted = Crypt::decrypt($content); - $this->setCsvFileContent($contentDecrypted); - } - - protected function sessionCsvFileLocation() - { - if (Session::has('csv-file')) { - $this->csvFileLocation = (string)session('csv-file'); - } - } - - protected function sessionDateFormat() - { - if (Session::has('csv-date-format')) { - $this->dateFormat = (string)session('csv-date-format'); - } - } - - protected function sessionDelimiter() - { - if (Session::has('csv-delimiter')) { - $this->delimiter = session('csv-delimiter'); - } - } - - protected function sessionHasHeaders() - { - if (Session::has('csv-has-headers')) { - $this->hasHeaders = (bool)session('csv-has-headers'); - } - } - - protected function sessionImportAccount() - { - if (Session::has('csv-import-account')) { - $this->importAccount = intval(session('csv-import-account')); - } - } - - protected function sessionMap() - { - if (Session::has('csv-map')) { - $this->map = (array)session('csv-map'); - } - } - - protected function sessionMapped() - { - if (Session::has('csv-mapped')) { - $this->mapped = (array)session('csv-mapped'); - } - } - - protected function sessionRoles() - { - if (Session::has('csv-roles')) { - $this->roles = (array)session('csv-roles'); - } - } - - protected function sessionSpecifix() - { - if (Session::has('csv-specifix')) { - $this->specifix = (array)session('csv-specifix'); - } - } -} diff --git a/app/Helpers/Csv/Importer.php b/app/Helpers/Csv/Importer.php deleted file mode 100644 index 9e50f02a46..0000000000 --- a/app/Helpers/Csv/Importer.php +++ /dev/null @@ -1,384 +0,0 @@ -errors; - } - - /** - * Used by CsvController - * - * @return int - */ - public function getImported(): int - { - return $this->imported; - } - - /** - * @return Collection - */ - public function getJournals(): Collection - { - return $this->journals; - } - - /** - * Used by CsvController - * - * @return int - */ - public function getRows(): int - { - return $this->rows; - } - - /** - * @return array - */ - public function getSpecifix(): array - { - return is_array($this->specifix) ? $this->specifix : []; - } - - /** - * @throws FireflyException - */ - public function run() - { - set_time_limit(0); - - $this->journals = new Collection; - $this->map = $this->data->getMap(); - $this->roles = $this->data->getRoles(); - $this->mapped = $this->data->getMapped(); - $this->specifix = $this->data->getSpecifix(); - - foreach ($this->data->getReader() as $index => $row) { - if ($this->parseRow($index)) { - $this->rows++; - $result = $this->importRow($row); - if (!($result instanceof TransactionJournal)) { - Log::error('Caught error at row #' . $index . ': ' . $result); - $this->errors[$index] = $result; - } else { - $this->imported++; - $this->journals->push($result); - event(new TransactionJournalStored($result, 0)); - } - } - } - } - - /** - * @param Data $data - */ - public function setData(Data $data) - { - $this->data = $data; - } - - /** - * @return TransactionJournal|string - */ - protected function createTransactionJournal() - { - $date = $this->importData['date']; - if (is_null($this->importData['date'])) { - $date = $this->importData['date-rent']; - } - - - $transactionType = $this->getTransactionType(); // defaults to deposit - $errors = new MessageBag; - $journal = TransactionJournal::create( - [ - 'user_id' => Auth::user()->id, - 'transaction_type_id' => $transactionType->id, - 'transaction_currency_id' => $this->importData['currency']->id, - 'description' => $this->importData['description'], - 'completed' => 0, - 'date' => $date, - 'bill_id' => $this->importData['bill-id'], - ] - ); - if ($journal->getErrors()->count() == 0) { - // first transaction - $accountId = $this->importData['asset-account-object']->id; // create first transaction: - $amount = $this->importData['amount']; - $transaction = Transaction::create(['transaction_journal_id' => $journal->id, 'account_id' => $accountId, 'amount' => $amount]); - $errors = $transaction->getErrors(); - - // second transaction - $accountId = $this->importData['opposing-account-object']->id; // create second transaction: - $amount = bcmul($this->importData['amount'], '-1'); - $transaction = Transaction::create(['transaction_journal_id' => $journal->id, 'account_id' => $accountId, 'amount' => $amount]); - $errors = $transaction->getErrors()->merge($errors); - } - if ($errors->count() == 0) { - $journal->completed = 1; - $journal->save(); - } else { - $text = join(',', $errors->all()); - - return $text; - } - $this->saveBudget($journal); - $this->saveCategory($journal); - $this->saveTags($journal); - - // some debug info: - $journalId = $journal->id; - $type = $journal->transaction_type_type ?? $journal->transactionType->type; - /** @var Account $asset */ - $asset = $this->importData['asset-account-object']; - /** @var Account $opposing */ - $opposing = $this->importData['opposing-account-object']; - - Log::info('Created journal #' . $journalId . ' of type ' . $type . '!'); - Log::info('Asset account #' . $asset->id . ' lost/gained: ' . $this->importData['amount']); - Log::info($opposing->accountType->type . ' #' . $opposing->id . ' lost/gained: ' . bcmul($this->importData['amount'], '-1')); - - return $journal; - } - - /** - * @return TransactionType - */ - protected function getTransactionType() - { - $transactionType = TransactionType::where('type', TransactionType::DEPOSIT)->first(); - if ($this->importData['amount'] < 0) { - $transactionType = TransactionType::where('type', TransactionType::WITHDRAWAL)->first(); - } - - if (in_array($this->importData['opposing-account-object']->accountType->type, ['Asset account', 'Default account'])) { - $transactionType = TransactionType::where('type', TransactionType::TRANSFER)->first(); - } - - return $transactionType; - } - - /** - * @param array $row - * - * @throws FireflyException - * @return string|bool - */ - protected function importRow(array $row) - { - - $data = $this->getFiller(); // These fields are necessary to create a new transaction journal. Some are optional - foreach ($row as $index => $value) { - $role = $this->roles[$index] ?? '_ignore'; - $class = config('csv.roles.' . $role . '.converter'); - $field = config('csv.roles.' . $role . '.field'); - - - // here would be the place where preprocessors would fire. - - /** @var ConverterInterface $converter */ - $converter = app('FireflyIII\Helpers\Csv\Converter\\' . $class); - $converter->setData($data); // the complete array so far. - $converter->setField($field); - $converter->setIndex($index); - $converter->setMapped($this->mapped); - $converter->setValue($value); - $data[$field] = $converter->convert(); - } - // move to class vars. - $this->importData = $data; - $this->importRow = $row; - unset($data, $row); - // post processing and validating. - $this->postProcess(); - $result = $this->validateData(); - - if (!($result === true)) { - return $result; // return error. - } - $journal = $this->createTransactionJournal(); - - return $journal; - } - - /** - * @param int $index - * - * @return bool - */ - protected function parseRow(int $index) - { - return (($this->data->hasHeaders() && $index >= 1) || !$this->data->hasHeaders()); - } - - /** - * Row denotes the original data. - * - * @return void - */ - protected function postProcess() - { - // do bank specific fixes (must be enabled but now all of them. - - foreach ($this->getSpecifix() as $className) { - /** @var SpecifixInterface $specifix */ - $specifix = app('FireflyIII\Helpers\Csv\Specifix\\' . $className); - if ($specifix->getProcessorType() == SpecifixInterface::POST_PROCESSOR) { - $specifix->setData($this->importData); - $specifix->setRow($this->importRow); - $this->importData = $specifix->fix(); - } - } - - - $set = config('csv.post_processors'); - foreach ($set as $className) { - /** @var PostProcessorInterface $postProcessor */ - $postProcessor = app('FireflyIII\Helpers\Csv\PostProcessing\\' . $className); - $array = $this->importData ?? []; - $postProcessor->setData($array); - $this->importData = $postProcessor->process(); - } - - } - - /** - * @param TransactionJournal $journal - */ - protected function saveBudget(TransactionJournal $journal) - { - // add budget: - if (!is_null($this->importData['budget'])) { - $journal->budgets()->save($this->importData['budget']); - } - } - - /** - * @param TransactionJournal $journal - */ - protected function saveCategory(TransactionJournal $journal) - { - // add category: - if (!is_null($this->importData['category'])) { - $journal->categories()->save($this->importData['category']); - } - } - - /** - * @param TransactionJournal $journal - */ - protected function saveTags(TransactionJournal $journal) - { - if (!is_null($this->importData['tags'])) { - foreach ($this->importData['tags'] as $tag) { - $journal->tags()->save($tag); - } - } - } - - /** - * - * @return bool|string - */ - protected function validateData() - { - $date = $this->importData['date'] ?? null; - $rentDate = $this->importData['date-rent'] ?? null; - if (is_null($date) && is_null($rentDate)) { - return 'No date value for this row.'; - } - if (is_null($this->importData['opposing-account-object'])) { - return 'Opposing account is null'; - } - - if (!($this->importData['asset-account-object'] instanceof Account)) { - return 'No asset account to import into.'; - } - - return true; - } - - /** - * @return array - */ - private function getFiller() - { - $filler = []; - foreach (config('csv.roles') as $role) { - if (isset($role['field'])) { - $fieldName = $role['field']; - $filler[$fieldName] = null; - } - } - // some extra's: - $filler['bill-id'] = null; - $filler['opposing-account-object'] = null; - $filler['asset-account-object'] = null; - $filler['amount-modifier'] = '1'; - - return $filler; - - } - -} diff --git a/app/Helpers/Csv/Mapper/AnyAccount.php b/app/Helpers/Csv/Mapper/AnyAccount.php deleted file mode 100644 index 05c5bab9ea..0000000000 --- a/app/Helpers/Csv/Mapper/AnyAccount.php +++ /dev/null @@ -1,42 +0,0 @@ -accounts()->with('accountType')->orderBy('accounts.name', 'ASC')->get(['accounts.*']); - - $list = []; - /** @var Account $account */ - foreach ($result as $account) { - $list[$account->id] = $account->name . ' (' . $account->accountType->type . ')'; - } - asort($list); - - $list = [0 => trans('firefly.csv_do_not_map')] + $list; - - return $list; - } -} diff --git a/app/Helpers/Csv/Mapper/AssetAccount.php b/app/Helpers/Csv/Mapper/AssetAccount.php deleted file mode 100644 index 8f32bbad57..0000000000 --- a/app/Helpers/Csv/Mapper/AssetAccount.php +++ /dev/null @@ -1,54 +0,0 @@ -accounts()->with( - ['accountmeta' => function (HasMany $query) { - $query->where('name', 'accountRole'); - }] - )->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']); - - $list = []; - - /** @var Account $account */ - foreach ($result as $account) { - $name = $account->name; - $iban = $account->iban ?? ''; - if (strlen($iban) > 0) { - $name .= ' (' . $account->iban . ')'; - } - $list[$account->id] = $name; - } - - asort($list); - - $list = [0 => trans('firefly.csv_do_not_map')] + $list; - - return $list; - } -} diff --git a/app/Helpers/Csv/Mapper/Bill.php b/app/Helpers/Csv/Mapper/Bill.php deleted file mode 100644 index 37946bf394..0000000000 --- a/app/Helpers/Csv/Mapper/Bill.php +++ /dev/null @@ -1,42 +0,0 @@ -bills()->get(['bills.*']); - $list = []; - - /** @var BillModel $bill */ - foreach ($result as $bill) { - $list[$bill->id] = $bill->name . ' [' . $bill->match . ']'; - } - asort($list); - - $list = [0 => trans('firefly.csv_do_not_map')] + $list; - - return $list; - } -} diff --git a/app/Helpers/Csv/Mapper/Budget.php b/app/Helpers/Csv/Mapper/Budget.php deleted file mode 100644 index 7664cc3378..0000000000 --- a/app/Helpers/Csv/Mapper/Budget.php +++ /dev/null @@ -1,42 +0,0 @@ -budgets()->get(['budgets.*']); - $list = []; - - /** @var BudgetModel $budget */ - foreach ($result as $budget) { - $list[$budget->id] = $budget->name; - } - asort($list); - - $list = [0 => trans('firefly.csv_do_not_map')] + $list; - - return $list; - } -} diff --git a/app/Helpers/Csv/Mapper/Category.php b/app/Helpers/Csv/Mapper/Category.php deleted file mode 100644 index d0a104523a..0000000000 --- a/app/Helpers/Csv/Mapper/Category.php +++ /dev/null @@ -1,42 +0,0 @@ -categories()->get(['categories.*']); - $list = []; - - /** @var CategoryModel $category */ - foreach ($result as $category) { - $list[$category->id] = $category->name; - } - asort($list); - - $list = [0 => trans('firefly.csv_do_not_map')] + $list; - - return $list; - } -} diff --git a/app/Helpers/Csv/Mapper/MapperInterface.php b/app/Helpers/Csv/Mapper/MapperInterface.php deleted file mode 100644 index eb6286d4f2..0000000000 --- a/app/Helpers/Csv/Mapper/MapperInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -budgets()->get(['tags.*']); - $list = []; - - /** @var TagModel $tag */ - foreach ($result as $tag) { - $list[$tag->id] = $tag->tag; - } - asort($list); - - $list = [0 => trans('firefly.csv_do_not_map')] + $list; - - return $list; - } -} diff --git a/app/Helpers/Csv/Mapper/TransactionCurrency.php b/app/Helpers/Csv/Mapper/TransactionCurrency.php deleted file mode 100644 index f4257b1510..0000000000 --- a/app/Helpers/Csv/Mapper/TransactionCurrency.php +++ /dev/null @@ -1,40 +0,0 @@ -id] = $currency->name . ' (' . $currency->code . ')'; - } - - asort($list); - - $list = [0 => trans('firefly.csv_do_not_map')] + $list; - - return $list; - } -} diff --git a/app/Helpers/Csv/PostProcessing/Amount.php b/app/Helpers/Csv/PostProcessing/Amount.php deleted file mode 100644 index 504c887331..0000000000 --- a/app/Helpers/Csv/PostProcessing/Amount.php +++ /dev/null @@ -1,44 +0,0 @@ -data['amount'] ?? '0'; - $modifier = strval($this->data['amount-modifier']); - $this->data['amount'] = bcmul($amount, $modifier); - - return $this->data; - } - - /** - * @param array $data - */ - public function setData(array $data) - { - $this->data = $data; - } -} diff --git a/app/Helpers/Csv/PostProcessing/AssetAccount.php b/app/Helpers/Csv/PostProcessing/AssetAccount.php deleted file mode 100644 index e57e0f73f1..0000000000 --- a/app/Helpers/Csv/PostProcessing/AssetAccount.php +++ /dev/null @@ -1,274 +0,0 @@ -checkIdNameObject(); // has object in ID or Name? - if (!is_null($result)) { - return $result; - } - - // no object? maybe asset-account-iban is a string and we can find the matching account. - $result = $this->checkIbanString(); - if (!is_null($result)) { - return $result; - } - - // no object still? maybe we can find the account by name. - $result = $this->checkNameString(); - if (!is_null($result)) { - return $result; - } - // still nothing? Perhaps the account number can lead us to an account: - $result = $this->checkAccountNumberString(); - if (!is_null($result)) { - return $result; - } - - return null; - } - - /** - * @param array $data - */ - public function setData(array $data) - { - $this->data = $data; - } - - /** - * @return array|null - */ - protected function checkAccountNumberString() - { - $accountNumber = $this->data['asset-account-number'] ?? null; - if ($accountNumber instanceof Account) { // fourth: try to find account based on name, if any. - $this->data['asset-account-object'] = $accountNumber; - - return $this->data; - } - if (is_string($accountNumber)) { // it's an actual account number - $this->data['asset-account-object'] = $this->parseAccountNumberString(); - - return $this->data; - } - - return null; - } - - /** - * @return array|null - */ - protected function checkIbanString() - { - $iban = $this->data['asset-account-iban'] ?? ''; - $rules = ['iban' => 'iban']; - $check = ['iban' => $iban]; - $validator = Validator::make($check, $rules); - if (!$validator->fails()) { - $this->data['asset-account-object'] = $this->parseIbanString(); - - return $this->data; - } - - return null; - } - - /** - * @return array - */ - protected function checkIdNameObject() - { - $accountId = $this->data['asset-account-id'] ?? null; - $accountIban = $this->data['asset-account-iban'] ?? null; - $accountNumber = $this->data['asset-account-number'] ?? null; - if ($accountId instanceof Account) { // first priority. try to find the account based on ID, if any - $this->data['asset-account-object'] = $accountId; - - return $this->data; - } - if ($accountIban instanceof Account) { // second: try to find the account based on IBAN, if any. - $this->data['asset-account-object'] = $accountIban; - - return $this->data; - } - - if ($accountNumber instanceof Account) { // second: try to find the account based on account number, if any. - $this->data['asset-account-object'] = $accountNumber; - - return $this->data; - } - - - return null; - } - - /** - * @return array|null - */ - protected function checkNameString() - { - $accountName = $this->data['asset-account-name'] ?? null; - if ($accountName instanceof Account) { // third: try to find account based on name, if any. - $this->data['asset-account-object'] = $accountName; - - return $this->data; - } - if (is_string($accountName)) { - $this->data['asset-account-object'] = $this->parseNameString(); - - return $this->data; - } - - return null; - } - - /** - * @return Account|null - */ - protected function createAccount() - { - $accountType = $this->getAccountType(); - $name = $this->data['asset-account-name'] ?? ''; - $iban = $this->data['asset-account-iban'] ?? ''; - - // create if not exists: // See issue #180 - $name = strlen($name) > 0 ? $name : $iban; - $account = Account::firstOrCreateEncrypted( - [ - 'user_id' => Auth::user()->id, - 'account_type_id' => $accountType->id, - 'name' => $name, - 'iban' => $iban, - 'active' => true, - ] - ); - - return $account; - } - - /** - * - * @return AccountType - */ - protected function getAccountType() - { - return AccountType::where('type', 'Asset account')->first(); - } - - /** - * @return Account|null - */ - protected function parseIbanString() - { - // create by name and/or iban. - $iban = $this->data['asset-account-iban'] ?? ''; - $accounts = Auth::user()->accounts()->get(); - foreach ($accounts as $entry) { - if ($iban !== '' && $entry->iban === $iban) { - - return $entry; - } - } - $account = $this->createAccount(); - - return $account; - } - - /** - * @return Account|null - */ - protected function parseNameString() - { - $accountType = $this->getAccountType(); - $accounts = Auth::user()->accounts()->where('account_type_id', $accountType->id)->get(); - foreach ($accounts as $entry) { - if ($entry->name == $this->data['asset-account-name']) { - - return $entry; - } - } - // create if not exists: - // See issue #180 - $account = Account::firstOrCreateEncrypted( - [ - 'user_id' => Auth::user()->id, - 'account_type_id' => $accountType->id, - 'name' => $this->data['asset-account-name'], - 'iban' => '', - 'active' => true, - ] - ); - - return $account; - } - - /** - * @return Account|null - */ - private function parseAccountNumberString() - { - /** @var AccountCrudInterface $crud */ - $crud = app(AccountCrudInterface::class); - - $accountNumber = $this->data['asset-account-number'] ?? ''; - $accountType = $this->getAccountType(); - $accounts = Auth::user()->accounts()->with(['accountmeta'])->where('account_type_id', $accountType->id)->get(); - /** @var Account $entry */ - foreach ($accounts as $entry) { - $metaFieldValue = $entry->getMeta('accountNumber'); - if ($metaFieldValue === $accountNumber && $metaFieldValue !== '') { - - return $entry; - } - } - // create new if not exists and return that one: - $accountData = [ - 'name' => $accountNumber, - 'accountType' => 'asset', - 'virtualBalance' => 0, - 'virtualBalanceCurrency' => 1, // hard coded. - 'active' => true, - 'user' => Auth::user()->id, - 'iban' => null, - 'accountNumber' => $accountNumber, - 'accountRole' => null, - 'openingBalance' => 0, - 'openingBalanceDate' => new Carbon, - 'openingBalanceCurrency' => 1, // hard coded. - ]; - $account = $crud->store($accountData); - - return $account; - } -} diff --git a/app/Helpers/Csv/PostProcessing/Bill.php b/app/Helpers/Csv/PostProcessing/Bill.php deleted file mode 100644 index 60392b2446..0000000000 --- a/app/Helpers/Csv/PostProcessing/Bill.php +++ /dev/null @@ -1,45 +0,0 @@ -data['bill']) && !is_null($this->data['bill']->id)) { - $this->data['bill-id'] = $this->data['bill']->id; - } - - return $this->data; - } - - /** - * @param array $data - */ - public function setData(array $data) - { - $this->data = $data; - } -} diff --git a/app/Helpers/Csv/PostProcessing/Currency.php b/app/Helpers/Csv/PostProcessing/Currency.php deleted file mode 100644 index 2a6f46cfd5..0000000000 --- a/app/Helpers/Csv/PostProcessing/Currency.php +++ /dev/null @@ -1,49 +0,0 @@ -data['currency'])) { - $currencyPreference = Preferences::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR')); - $this->data['currency'] = TransactionCurrency::whereCode($currencyPreference->data)->first(); - } - - return $this->data; - } - - /** - * @param array $data - */ - public function setData(array $data) - { - $this->data = $data; - } -} diff --git a/app/Helpers/Csv/PostProcessing/Description.php b/app/Helpers/Csv/PostProcessing/Description.php deleted file mode 100644 index 8c4843a58b..0000000000 --- a/app/Helpers/Csv/PostProcessing/Description.php +++ /dev/null @@ -1,47 +0,0 @@ -data['description'] ?? ''; - $this->data['description'] = trim($description); - if (strlen($this->data['description']) == 0) { - $this->data['description'] = trans('firefly.csv_empty_description'); - } - - - return $this->data; - } - - /** - * @param array $data - */ - public function setData(array $data) - { - - $this->data = $data; - } -} diff --git a/app/Helpers/Csv/PostProcessing/OpposingAccount.php b/app/Helpers/Csv/PostProcessing/OpposingAccount.php deleted file mode 100644 index 279328b24e..0000000000 --- a/app/Helpers/Csv/PostProcessing/OpposingAccount.php +++ /dev/null @@ -1,210 +0,0 @@ -checkIdNameObject(); - if (!is_null($result)) { - return $result; - } - - $result = $this->checkIbanString(); - if (!is_null($result)) { - return $result; - } - - $result = $this->checkNameString(); - if (!is_null($result)) { - return $result; - } - - return null; - } - - /** - * @param array $data - */ - public function setData(array $data) - { - $this->data = $data; - } - - /** - * @return array|null - */ - protected function checkIbanString() - { - $rules = ['iban' => 'iban']; - $iban = $this->data['opposing-account-iban']; - $check = ['iban' => $iban]; - $validator = Validator::make($check, $rules); - if (is_string($iban) && strlen($iban) > 0 && !$validator->fails()) { - - $this->data['opposing-account-object'] = $this->parseIbanString(); - - return $this->data; - } - - return null; - } - - /** - * @return array - */ - protected function checkIdNameObject() - { - if ($this->data['opposing-account-id'] instanceof Account) { // first priority. try to find the account based on ID, if any - $this->data['opposing-account-object'] = $this->data['opposing-account-id']; - - return $this->data; - } - if ($this->data['opposing-account-iban'] instanceof Account) { // second: try to find the account based on IBAN, if any. - $this->data['opposing-account-object'] = $this->data['opposing-account-iban']; - - return $this->data; - } - - return null; - } - - /** - * @return array|null - */ - protected function checkNameString() - { - if ($this->data['opposing-account-name'] instanceof Account) { // third: try to find account based on name, if any. - $this->data['opposing-account-object'] = $this->data['opposing-account-name']; - - return $this->data; - } - if (is_string($this->data['opposing-account-name'])) { - - $this->data['opposing-account-object'] = $this->parseNameString(); - - return $this->data; - } - - return null; - } - - /** - * @return Account|null - */ - protected function createAccount() - { - $accountType = $this->getAccountType(); - - // create if not exists: - $name = is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) > 0 ? $this->data['opposing-account-name'] - : $this->data['opposing-account-iban']; - $account = Account::firstOrCreateEncrypted( // See issue #180 - [ - 'user_id' => Auth::user()->id, - 'account_type_id' => $accountType->id, - 'name' => $name, - 'iban' => $this->data['opposing-account-iban'], - 'active' => true, - ] - ); - - return $account; - } - - /** - * - * @return AccountType - */ - protected function getAccountType() - { - // opposing account type: - if ($this->data['amount'] < 0) { - // create expense account: - - return AccountType::where('type', 'Expense account')->first(); - } - - // create revenue account: - - return AccountType::where('type', 'Revenue account')->first(); - - - } - - /** - * @return Account|null - */ - protected function parseIbanString() - { - // create by name and/or iban. - $accounts = Auth::user()->accounts()->get(); - foreach ($accounts as $entry) { - if ($entry->iban == $this->data['opposing-account-iban']) { - - return $entry; - } - } - $account = $this->createAccount(); - - - return $account; - } - - /** - * @return Account|null - */ - protected function parseNameString() - { - $accountType = $this->getAccountType(); - $accounts = Auth::user()->accounts()->where('account_type_id', $accountType->id)->get(); - foreach ($accounts as $entry) { - if ($entry->name == $this->data['opposing-account-name']) { - - return $entry; - } - } - // create if not exists: - $account = Account::firstOrCreateEncrypted( // See issue #180 - [ - 'user_id' => Auth::user()->id, - 'account_type_id' => $accountType->id, - 'name' => $this->data['opposing-account-name'], - 'iban' => '', - 'active' => true, - ] - ); - - return $account; - } -} diff --git a/app/Helpers/Csv/PostProcessing/PostProcessorInterface.php b/app/Helpers/Csv/PostProcessing/PostProcessorInterface.php deleted file mode 100644 index 58f6ff09d0..0000000000 --- a/app/Helpers/Csv/PostProcessing/PostProcessorInterface.php +++ /dev/null @@ -1,31 +0,0 @@ -setProcessorType(self::POST_PROCESSOR); - } - - - /** - * @return array - */ - public function fix(): array - { - // Try to parse the description in known formats. - $parsed = $this->parseSepaDescription() || $this->parseTRTPDescription() || $this->parseGEABEADescription() || $this->parseABNAMRODescription(); - - // If the description could not be parsed, specify an unknown opposing - // account, as an opposing account is required - if (!$parsed) { - $this->data['opposing-account-name'] = trans('firefly.unknown'); - } - - return $this->data; - } - - /** - * @param array $data - */ - public function setData(array $data) - { - $this->data = $data; - } - - /** - * @param array $row - */ - public function setRow(array $row) - { - $this->row = $row; - } - - /** - * Parses the current description with costs from ABN AMRO itself - * - * @return bool true if the description is GEA/BEA-format, false otherwise - */ - protected function parseABNAMRODescription() - { - // See if the current description is formatted in ABN AMRO format - if (preg_match('/ABN AMRO.{24} (.*)/', $this->data['description'], $matches)) { - - $this->data['opposing-account-name'] = 'ABN AMRO'; - $this->data['description'] = $matches[1]; - - return true; - } - - return false; - } - - /** - * Parses the current description in GEA/BEA format - * - * @return bool true if the description is GEA/BEAformat, false otherwise - */ - protected function parseGEABEADescription() - { - // See if the current description is formatted in GEA/BEA format - if (preg_match('/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/', $this->data['description'], $matches)) { - - // description and opposing account will be the same. - $this->data['opposing-account-name'] = $matches[4]; - $this->data['description'] = $matches[4]; - - if ($matches[1] == 'GEA') { - $this->data['description'] = 'GEA ' . $matches[4]; - } - - return true; - } - - return false; - } - - /** - * Parses the current description in SEPA format - * - * @return bool true if the description is SEPA format, false otherwise - */ - protected function parseSepaDescription() - { - // See if the current description is formatted as a SEPA plain description - if (preg_match('/^SEPA(.{28})/', $this->data['description'], $matches)) { - - $type = $matches[1]; - $reference = ''; - $name = ''; - $newDescription = ''; - - // SEPA plain descriptions contain several key-value pairs, split by a colon - preg_match_all('/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9._#-]+(?=\s|$))/', $this->data['description'], $matches, PREG_SET_ORDER); - - if (is_array($matches)) { - foreach ($matches as $match) { - $key = $match[1]; - $value = trim($match[2]); - switch (strtoupper($key)) { - case 'OMSCHRIJVING': - $newDescription = $value; - break; - case 'NAAM': - $this->data['opposing-account-name'] = $value; - $name = $value; - break; - case 'KENMERK': - $reference = $value; - break; - case 'IBAN': - $this->data['opposing-account-iban'] = $value; - break; - default: - // Ignore the rest - } - } - } - - // Set a new description for the current transaction. If none was given - // set the description to type, name and reference - $this->data['description'] = $newDescription; - if (strlen($newDescription) === 0) { - $this->data['description'] = sprintf('%s - %s (%s)', $type, $name, $reference); - } - - return true; - } - - return false; - } - - /** - * Parses the current description in TRTP format - * - * @return bool true if the description is TRTP format, false otherwise - */ - protected function parseTRTPDescription() - { - // See if the current description is formatted in TRTP format - if (preg_match_all('!\/([A-Z]{3,4})\/([^/]*)!', $this->data['description'], $matches, PREG_SET_ORDER)) { - - $type = ''; - $name = ''; - $reference = ''; - $newDescription = ''; - - // Search for properties specified in the TRTP format. If no description - // is provided, use the type, name and reference as new description - if (is_array($matches)) { - foreach ($matches as $match) { - $key = $match[1]; - $value = trim($match[2]); - - switch (strtoupper($key)) { - case 'NAME': - $this->data['opposing-account-name'] = $name = $value; - break; - case 'REMI': - $newDescription = $value; - break; - case 'IBAN': - $this->data['opposing-account-iban'] = $value; - break; - case 'EREF': - $reference = $value; - break; - case 'TRTP': - $type = $value; - break; - default: - // Ignore the rest - } - } - - // Set a new description for the current transaction. If none was given - // set the description to type, name and reference - $this->data['description'] = $newDescription; - if (strlen($newDescription) === 0) { - $this->data['description'] = sprintf('%s - %s (%s)', $type, $name, $reference); - } - } - - return true; - } - - return false; - } - -} diff --git a/app/Helpers/Csv/Specifix/Dummy.php b/app/Helpers/Csv/Specifix/Dummy.php deleted file mode 100644 index 447bafadd9..0000000000 --- a/app/Helpers/Csv/Specifix/Dummy.php +++ /dev/null @@ -1,60 +0,0 @@ -setProcessorType(self::POST_PROCESSOR); - } - - /** - * @return array - */ - public function fix(): array - { - return $this->data; - - } - - /** - * @param array $data - */ - public function setData(array $data) - { - $this->data = $data; - } - - /** - * @param array $row - */ - public function setRow(array $row) - { - $this->row = $row; - } - - -} diff --git a/app/Helpers/Csv/Specifix/RabobankDescription.php b/app/Helpers/Csv/Specifix/RabobankDescription.php deleted file mode 100644 index 67dbdb6c49..0000000000 --- a/app/Helpers/Csv/Specifix/RabobankDescription.php +++ /dev/null @@ -1,76 +0,0 @@ -setProcessorType(self::POST_PROCESSOR); - } - - - /** - * @return array - */ - public function fix(): array - { - $this->rabobankFixEmptyOpposing(); - - return $this->data; - - } - - /** - * @param array $data - */ - public function setData(array $data) - { - $this->data = $data; - } - - /** - * @param array $row - */ - public function setRow(array $row) - { - $this->row = $row; - } - - /** - * Fixes Rabobank specific thing. - */ - protected function rabobankFixEmptyOpposing() - { - if (is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) == 0) { - $this->data['opposing-account-name'] = $this->row[10]; - - $this->data['description'] = trim(str_replace($this->row[10], '', $this->data['description'])); - } - - } - - -} diff --git a/app/Helpers/Csv/Specifix/Specifix.php b/app/Helpers/Csv/Specifix/Specifix.php deleted file mode 100644 index 8626d38dcb..0000000000 --- a/app/Helpers/Csv/Specifix/Specifix.php +++ /dev/null @@ -1,46 +0,0 @@ -processorType; - } - - /** - * @param int $processorType - * - * @return $this - */ - public function setProcessorType(int $processorType) - { - $this->processorType = $processorType; - - return $this; - } - - -} diff --git a/app/Helpers/Csv/Specifix/SpecifixInterface.php b/app/Helpers/Csv/Specifix/SpecifixInterface.php deleted file mode 100644 index e37d4d3ec4..0000000000 --- a/app/Helpers/Csv/Specifix/SpecifixInterface.php +++ /dev/null @@ -1,49 +0,0 @@ - $row) { - if ($this->useRow($hasHeaders, $index)) { - // collect all map values - - foreach ($keys as $column) { - $values[$column][] = $row[$column]; - } - } - } - /* - * Make each one unique. - */ - $values = $this->uniqueRecursive($values); - - return $values; - } - - /** - * @param array $roles - * @param array $map - * - * @return array - */ - public function processSelectedMapping(array $roles, array $map): array - { - $configRoles = config('csv.roles'); - $maps = []; - $keys = array_keys($map); - foreach ($keys as $index) { - if (isset($roles[$index])) { - $name = $roles[$index]; - if ($configRoles[$name]['mappable']) { - $maps[$index] = $name; - } - } - } - - return $maps; - - } - - /** - * @param array $input - * - * @return array - */ - public function processSelectedRoles(array $input): array - { - $roles = []; - - - /* - * Store all rows for each column: - */ - if (is_array($input)) { - foreach ($input as $index => $role) { - if ($role != '_ignore') { - $roles[$index] = $role; - } - } - } - - return $roles; - } - - /** - * @param array $fields - * - * @return bool - */ - public function sessionHasValues(array $fields): bool - { - foreach ($fields as $field) { - if (!Session::has($field)) { - Log::error('Session is missing field: ' . $field); - - return false; - } - } - - return true; - } - - /** - * @param array $map - * - * @return array - * @throws FireflyException - */ - public function showOptions(array $map): array - { - $options = []; - foreach ($map as $index => $columnRole) { - - $mapper = config('csv.roles.' . $columnRole . '.mapper'); - if (is_null($mapper)) { - throw new FireflyException('Cannot map field of type "' . $columnRole . '".'); - } - $class = 'FireflyIII\Helpers\Csv\Mapper\\' . $mapper; - try { - /** @var MapperInterface $mapObject */ - $mapObject = app($class); - } catch (ReflectionException $e) { - throw new FireflyException('Column "' . $columnRole . '" cannot be mapped because mapper class ' . $mapper . ' does not exist.'); - } - $set = $mapObject->getMap(); - $options[$index] = $set; - } - - return $options; - } - - /** - * @param string $path - * - * @return string - */ - public function storeCsvFile(string $path): string - { - $time = str_replace(' ', '-', microtime()); - $fileName = 'csv-upload-' . Auth::user()->id . '-' . $time . '.csv.encrypted'; - $disk = Storage::disk('upload'); - $file = new SplFileObject($path, 'r'); - $content = $file->fread($file->getSize()); - $contentEncrypted = Crypt::encrypt($content); - $disk->put($fileName, $contentEncrypted); - - return $fileName; - - - } - - /** - * @param array $array - * - * @return array - */ - protected function uniqueRecursive(array $array) - { - foreach ($array as $column => $found) { - $array[$column] = array_unique($found); - } - - return $array; - } - - /** - * @param bool $hasHeaders - * @param int $index - * - * @return bool - */ - protected function useRow(bool $hasHeaders, int $index) - { - return ($hasHeaders && $index > 1) || !$hasHeaders; - } -} diff --git a/app/Helpers/Csv/WizardInterface.php b/app/Helpers/Csv/WizardInterface.php deleted file mode 100644 index cedcfbe5f6..0000000000 --- a/app/Helpers/Csv/WizardInterface.php +++ /dev/null @@ -1,68 +0,0 @@ -wizard = app(WizardInterface::class); - $this->data = app(Data::class); - - } - - /** - * Define column roles and mapping. - * - * STEP THREE - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View - */ - public function columnRoles() - { - - $fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-import-account', 'csv-specifix', 'csv-delimiter']; - if (!$this->wizard->sessionHasValues($fields)) { - Log::error('Could not recover upload.'); - Session::flash('warning', strval(trans('firefly.could_not_recover'))); - - return redirect(route('csv.index')); - } - - $subTitle = trans('firefly.csv_define_column_roles'); - $firstRow = $this->data->getReader()->fetchOne(); - $count = count($firstRow); - $headers = []; - $example = $this->data->getReader()->fetchOne(1); - $availableRoles = []; - $roles = $this->data->getRoles(); - $map = $this->data->getMap(); - - for ($i = 1; $i <= $count; $i++) { - $headers[] = trans('firefly.csv_column') . ' #' . $i; - } - if ($this->data->hasHeaders()) { - $headers = $firstRow; - } - $keys = array_keys(config('csv.roles')); - foreach ($keys as $name) { - $availableRoles[$name] = trans('firefly.csv_column_' . $name); - } - asort($availableRoles); - - return view('csv.column-roles', compact('availableRoles', 'map', 'roles', 'headers', 'example', 'subTitle')); - } - - /** - * Optional download of mapping. - * - * STEP FOUR THREE-A - * - * @return \Illuminate\Http\RedirectResponse|string - */ - public function downloadConfig() - { - $fields = ['csv-date-format', 'csv-has-headers', 'csv-delimiter']; - if (!$this->wizard->sessionHasValues($fields)) { - Session::flash('warning', strval(trans('firefly.could_not_recover'))); - - return redirect(route('csv.index')); - } - $data = [ - 'date-format' => session('csv-date-format'), - 'has-headers' => session('csv-has-headers'), - ]; - if (Session::has('csv-map')) { - $data['map'] = session('csv-map'); - } - if (Session::has('csv-roles')) { - $data['roles'] = session('csv-roles'); - } - if (Session::has('csv-mapped')) { - $data['mapped'] = session('csv-mapped'); - } - - if (Session::has('csv-specifix')) { - $data['specifix'] = session('csv-specifix'); - } - - $result = json_encode($data, JSON_PRETTY_PRINT); - $name = sprintf('"%s"', addcslashes('csv-configuration-' . date('Y-m-d') . '.json', '"\\')); - - return response($result, 200) - ->header('Content-disposition', 'attachment; filename=' . $name) - ->header('Content-Type', 'application/json') - ->header('Content-Description', 'File Transfer') - ->header('Connection', 'Keep-Alive') - ->header('Expires', '0') - ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') - ->header('Pragma', 'public') - ->header('Content-Length', strlen($result)); - } - - /** - * @return \Illuminate\View\View - */ - public function downloadConfigPage() - { - $fields = ['csv-date-format', 'csv-has-headers', 'csv-delimiter']; - if (!$this->wizard->sessionHasValues($fields)) { - Session::flash('warning', strval(trans('firefly.could_not_recover'))); - - return redirect(route('csv.index')); - } - - $subTitle = trans('firefly.csv_download_config_title'); - - return view('csv.download-config', compact('subTitle')); - } - - /** - * This method shows the initial upload form. - * - * STEP ONE - * - * @param AccountCrudInterface $crud - * - * @return \Illuminate\View\View - */ - public function index(AccountCrudInterface $crud) - { - $subTitle = trans('firefly.csv_import'); - - Session::forget('csv-date-format'); - Session::forget('csv-has-headers'); - Session::forget('csv-file'); - Session::forget('csv-import-account'); - Session::forget('csv-map'); - Session::forget('csv-roles'); - Session::forget('csv-mapped'); - Session::forget('csv-specifix'); - Session::forget('csv-delimiter'); - - // get list of supported specifix - $specifix = []; - foreach (config('csv.specifix') as $entry) { - $specifix[$entry] = trans('firefly.csv_specifix_' . $entry); - } - - // get a list of delimiters: - $delimiters = [ - ',' => trans('form.csv_comma'), - ';' => trans('form.csv_semicolon'), - 'tab' => trans('form.csv_tab'), - ]; - - // get a list of asset accounts: - $accounts = ExpandedForm::makeSelectList($crud->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT])); - - // can actually upload? - $uploadPossible = is_writable(storage_path('upload')); - $path = storage_path('upload'); - - return view('csv.index', compact('subTitle', 'uploadPossible', 'path', 'specifix', 'accounts', 'delimiters')); - } - - /** - * Parse the file. - * - * STEP FOUR - * - * @return \Illuminate\Http\RedirectResponse - */ - public function initialParse() - { - $fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-delimiter']; - if (!$this->wizard->sessionHasValues($fields)) { - Session::flash('warning', strval(trans('firefly.could_not_recover'))); - - return redirect(route('csv.index')); - } - - // process given roles and mapping: - $inputMap = Input::get('map') ?? []; - $inputRoles = Input::get('role') ?? []; - $roles = $this->wizard->processSelectedRoles($inputRoles); - $maps = $this->wizard->processSelectedMapping($roles, $inputMap); - - Session::put('csv-map', $maps); - Session::put('csv-roles', $roles); - - // Go back when no roles defined: - if (count($roles) === 0) { - Session::flash('warning', strval(trans('firefly.must_select_roles'))); - - return redirect(route('csv.column-roles')); - } - - /* - * Continue with map specification when necessary. - */ - if (count($maps) > 0) { - return redirect(route('csv.map')); - } - - /* - * Or simply start processing. - */ - - // proceed to download config - return redirect(route('csv.download-config-page')); - - } - - /** - * - * Map first if necessary, - * - * STEP FIVE. - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View - * @throws FireflyException - */ - public function map() - { - - // Make sure all fields we need are accounted for. - $fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles', 'csv-delimiter']; - if (!$this->wizard->sessionHasValues($fields)) { - Session::flash('warning', strval(trans('firefly.could_not_recover'))); - - return redirect(route('csv.index')); - } - /* - * The "options" array contains all options the user has - * per column, where the key represents the column. - * - * For each key there is an array which in turn represents - * all the options available: grouped by ID. - * - * options[column index] = [ - * field id => field identifier. - * ] - */ - $options = $this->wizard->showOptions($this->data->getMap()); - - // After these values are prepped, read the actual CSV file - $reader = $this->data->getReader(); - $map = $this->data->getMap(); - $hasHeaders = $this->data->hasHeaders(); - $values = $this->wizard->getMappableValues($reader, $map, $hasHeaders); - $map = $this->data->getMap(); - $mapped = $this->data->getMapped(); - $subTitle = trans('firefly.csv_map_values'); - - return view('csv.map', compact('map', 'options', 'values', 'mapped', 'subTitle')); - } - - /** - * - * Finally actually process the CSV file. - * - * STEP SEVEN - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View - */ - public function process() - { - /* - * Make sure all fields we need are accounted for. - */ - $fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles', 'csv-mapped', 'csv-delimiter']; - if (!$this->wizard->sessionHasValues($fields)) { - Session::flash('warning', strval(trans('firefly.could_not_recover'))); - - return redirect(route('csv.index')); - } - - /** @var Importer $importer */ - $importer = app(Importer::class); - $importer->setData($this->data); - $importer->run(); - - $rows = $importer->getRows(); - $errors = $importer->getErrors(); - $imported = $importer->getImported(); - $journals = $importer->getJournals(); - - Preferences::mark(); - - $subTitle = trans('firefly.csv_process_title'); - - return view('csv.process', compact('rows', 'errors', 'imported', 'subTitle', 'journals')); - - } - - /** - * Store the mapping the user has made. This is - * - * STEP SIX - * - * - * @return \Illuminate\Http\RedirectResponse - */ - public function saveMapping() - { - /* - * Make sure all fields we need are accounted for. - */ - $fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles', 'csv-delimiter']; - if (!$this->wizard->sessionHasValues($fields)) { - Session::flash('warning', strval(trans('firefly.could_not_recover'))); - - return redirect(route('csv.index')); - } - - // save mapping to session. - $mapped = []; - if (!is_array(Input::get('mapping'))) { - Session::flash('warning', strval(trans('firefly.invalid_mapping'))); - - return redirect(route('csv.map')); - } - - foreach (Input::get('mapping') as $index => $data) { - $mapped[$index] = []; - foreach ($data as $value => $mapping) { - if (intval($mapping) !== 0) { - $mapped[$index][$value] = $mapping; - } - } - } - Session::put('csv-mapped', $mapped); - - // proceed to process. - return redirect(route('csv.download-config-page')); - - } - - /** - * - * This method processes the file, puts it away somewhere safe - * and sends you onwards. - * - * STEP TWO - * - * @param Request $request - * - * @return \Illuminate\Http\RedirectResponse - */ - public function upload(Request $request) - { - if (!$request->hasFile('csv')) { - Session::flash('warning', strval(trans('firefly.no_file_uploaded'))); - - return redirect(route('csv.index')); - } - - $path = $this->wizard->storeCsvFile($request->file('csv')->getRealPath()); - $settings = []; - $settings['date-format'] = Input::get('date_format'); - $settings['has-headers'] = intval(Input::get('has_headers')) === 1; - $settings['specifix'] = is_array(Input::get('specifix')) ? Input::get('specifix') : []; - $settings['import-account'] = intval(Input::get('csv_import_account')); - $settings['delimiter'] = Input::get('csv_delimiter', ','); - - // A tab character cannot be used itself as option value in HTML - // See http://stackoverflow.com/questions/6064135/valid-characters-in-option-value - if ($settings['delimiter'] == 'tab') { - $settings['delimiter'] = "\t"; - } - - $settings['map'] = []; - $settings['mapped'] = []; - $settings['roles'] = []; - - if ($request->hasFile('csv_config')) { // Process config file if present. - - $size = $request->file('csv_config')->getSize(); - $data = $request->file('csv_config')->openFile()->fread($size); - $json = json_decode($data, true); - if (is_array($json)) { - $settings = array_merge($settings, $json); - } - } - - $this->data->setCsvFileLocation($path); - $this->data->setDateFormat($settings['date-format']); - $this->data->setHasHeaders($settings['has-headers']); - $this->data->setMap($settings['map']); - $this->data->setMapped($settings['mapped']); - $this->data->setRoles($settings['roles']); - $this->data->setSpecifix($settings['specifix']); - $this->data->setImportAccount($settings['import-account']); - $this->data->setDelimiter($settings['delimiter']); - - return redirect(route('csv.column-roles')); - - } -} diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index cc42ebbe64..f1925bc906 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -269,44 +269,6 @@ Breadcrumbs::register( } ); -/** - * CSV CONTROLLER - */ -Breadcrumbs::register( - 'csv.index', function (BreadCrumbGenerator $breadcrumbs) { - $breadcrumbs->parent('home'); - $breadcrumbs->push(trans('firefly.csv_index_title'), route('csv.index')); -} -); - -Breadcrumbs::register( - 'csv.column-roles', function (BreadCrumbGenerator $breadcrumbs) { - $breadcrumbs->parent('csv.index'); - $breadcrumbs->push(trans('firefly.csv_define_column_roles'), route('csv.column-roles')); -} -); - -Breadcrumbs::register( - 'csv.map', function (BreadCrumbGenerator $breadcrumbs) { - $breadcrumbs->parent('csv.index'); - $breadcrumbs->push(trans('firefly.csv_map_values'), route('csv.map')); -} -); - -Breadcrumbs::register( - 'csv.download-config-page', function (BreadCrumbGenerator $breadcrumbs) { - $breadcrumbs->parent('csv.index'); - $breadcrumbs->push(trans('firefly.csv_download_config'), route('csv.download-config-page')); -} -); - -Breadcrumbs::register( - 'csv.process', function (BreadCrumbGenerator $breadcrumbs) { - $breadcrumbs->parent('csv.index'); - $breadcrumbs->push(trans('firefly.csv_process_title'), route('csv.process')); -} -); - /** * CURRENCIES */ diff --git a/app/Http/routes.php b/app/Http/routes.php index 159931d398..43b7577f3f 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -157,21 +157,6 @@ Route::group( Route::post('/categories/update/{category}', ['uses' => 'CategoryController@update', 'as' => 'categories.update']); Route::post('/categories/destroy/{category}', ['uses' => 'CategoryController@destroy', 'as' => 'categories.destroy']); - /** - * CSV controller - */ - Route::get('/csv', ['uses' => 'CsvController@index', 'as' => 'csv.index']); - Route::post('/csv/upload', ['uses' => 'CsvController@upload', 'as' => 'csv.upload']); - Route::get('/csv/column_roles', ['uses' => 'CsvController@columnRoles', 'as' => 'csv.column-roles']); - Route::post('/csv/initial_parse', ['uses' => 'CsvController@initialParse', 'as' => 'csv.initial_parse']); - Route::get('/csv/map', ['uses' => 'CsvController@map', 'as' => 'csv.map']); - Route::get('/csv/download-config', ['uses' => 'CsvController@downloadConfig', 'as' => 'csv.download-config']); - Route::get('/csv/download', ['uses' => 'CsvController@downloadConfigPage', 'as' => 'csv.download-config-page']); - Route::post('/csv/save_mapping', ['uses' => 'CsvController@saveMapping', 'as' => 'csv.save_mapping']); - - Route::get('/csv/process', ['uses' => 'CsvController@process', 'as' => 'csv.process']); - - /** * Currency Controller */ diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 47ad19c301..6ea3d33f3e 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -87,7 +87,6 @@ class FireflyServiceProvider extends ServiceProvider $this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository'); $this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search'); $this->app->bind('FireflyIII\Repositories\User\UserRepositoryInterface', 'FireflyIII\Repositories\User\UserRepository'); - $this->app->bind('FireflyIII\Helpers\Csv\WizardInterface', 'FireflyIII\Helpers\Csv\Wizard'); $this->app->bind('FireflyIII\Helpers\Attachments\AttachmentHelperInterface', 'FireflyIII\Helpers\Attachments\AttachmentHelper'); $this->app->bind( 'FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface', 'FireflyIII\Generator\Chart\Account\ChartJsAccountChartGenerator' From 5a79bc0a990bd1b95573d19b5d8fff49e889970a Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 10 Jun 2016 21:00:00 +0200 Subject: [PATCH 04/94] Initial code base for new CSV import. --- app/Http/Controllers/ImportController.php | 50 ++++++++++ app/Http/Requests/ImportUploadRequest.php | 46 +++++++++ app/Http/routes.php | 5 +- app/Import/Importer/CsvImporter.php | 81 ++++++++++++++++ app/Import/Importer/ImporterInterface.php | 52 ++++++++++ app/Import/Role/Map.php | 18 ++++ app/Import/Role/Role.php | 18 ++++ app/Models/ImportJob.php | 75 ++++++++++++++ app/Providers/ExportJobServiceProvider.php | 42 ++++++-- .../ImportJob/ImportJobRepository.php | 81 ++++++++++++++++ .../ImportJobRepositoryInterface.php | 36 +++++++ app/User.php | 9 ++ config/app.php | 4 +- config/firefly.php | 1 + .../2016_05_23_173524_changes_for_v391.php | 44 +++++++++ resources/lang/en_US/firefly.php | 97 ++----------------- resources/lang/en_US/form.php | 7 -- resources/lang/en_US/help.php | 5 - resources/views/import/csv/configure.twig | 84 ++++++++++++++++ resources/views/import/index.twig | 2 +- resources/views/partials/menu-sidebar.twig | 12 +-- 21 files changed, 646 insertions(+), 123 deletions(-) create mode 100644 app/Http/Requests/ImportUploadRequest.php create mode 100644 app/Import/Importer/CsvImporter.php create mode 100644 app/Import/Importer/ImporterInterface.php create mode 100644 app/Import/Role/Map.php create mode 100644 app/Import/Role/Role.php create mode 100644 app/Models/ImportJob.php create mode 100644 app/Repositories/ImportJob/ImportJobRepository.php create mode 100644 app/Repositories/ImportJob/ImportJobRepositoryInterface.php create mode 100644 database/migrations/2016_05_23_173524_changes_for_v391.php create mode 100644 resources/views/import/csv/configure.twig diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 0319bdb87f..45524c1714 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -2,7 +2,14 @@ namespace FireflyIII\Http\Controllers; +use Crypt; use FireflyIII\Http\Requests; +use FireflyIII\Http\Requests\ImportUploadRequest; +use FireflyIII\Import\Importer\ImporterInterface; +use FireflyIII\Models\ImportJob; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use SplFileObject; +use Storage; use View; /** @@ -22,6 +29,26 @@ class ImportController extends Controller View::share('title', trans('firefly.import_data')); } + /** + * @param ImportJob $job + * + * @return View + */ + public function configure(ImportJob $job) + { + // create proper importer (depends on job) + $type = $job->file_type; + /** @var ImporterInterface $importer */ + $importer = app('FireflyIII\Import\Importer\\' . ucfirst($type) . 'Importer'); + $importer->setJob($job); + $importer->configure(); + $data = $importer->getConfigurationData(); + + return view('import.' . $type . '.configure', compact('data', 'job')); + + + } + /** * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ @@ -38,4 +65,27 @@ class ImportController extends Controller return view('import.index', compact('subTitle', 'subTitleIcon', 'importFileTypes', 'defaultImportType')); } + + /** + * @param ImportUploadRequest $request + * @param ImportJobRepositoryInterface $repository + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository) + { + // create import job: + $type = $request->get('import_file_type'); + $job = $repository->create($type); + $upload = $request->files->get('import_file'); + $newName = $job->key . '.upload'; + $uploaded = new SplFileObject($upload->getRealPath()); + $content = $uploaded->fread($uploaded->getSize()); + $contentEncrypted = Crypt::encrypt($content); + $disk = Storage::disk('upload'); + $disk->put($newName, $contentEncrypted); + + return redirect(route('import.configure', [$job->key])); + + } } diff --git a/app/Http/Requests/ImportUploadRequest.php b/app/Http/Requests/ImportUploadRequest.php new file mode 100644 index 0000000000..0f1e39155b --- /dev/null +++ b/app/Http/Requests/ImportUploadRequest.php @@ -0,0 +1,46 @@ + 'required|file', + 'import_file_type' => 'required|in:' . join(',', $types), + ]; + + } +} diff --git a/app/Http/routes.php b/app/Http/routes.php index 43b7577f3f..abf3bbcd17 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -223,8 +223,9 @@ Route::group( /** * IMPORT CONTROLLER */ - Route::get('/import', ['uses' => 'ImportController@index', 'as' => 'import.index']); - Route::post('/import/upload', ['uses' => 'ImportController@upload', 'as' => 'import.upload']); + Route::get('/import', ['uses' => 'ImportController@index','as' => 'import.index']); + Route::post('/import/upload', ['uses' => 'ImportController@upload','as' => 'import.upload']); + Route::get('/import/configure/{importJob}', ['uses' => 'ImportController@configure','as' => 'import.configure']); /** * Help Controller diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php new file mode 100644 index 0000000000..801c61f0ee --- /dev/null +++ b/app/Import/Importer/CsvImporter.php @@ -0,0 +1,81 @@ +getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + $delimiters = [ + ',' => trans('form.csv_comma'), + ';' => trans('form.csv_semicolon'), + 'tab' => trans('form.csv_tab'), + ]; + + $data = [ + 'accounts' => ExpandedForm::makeSelectList($accounts), + 'specifix' => [], + 'delimiters' => $delimiters, + ]; + + return $data; + } + + /** + * @param ImportJob $job + */ + public function setJob(ImportJob $job) + { + $this->job = $job; + } + + /** + * Returns a Map thing used to allow the user to + * define roles for each entry. + * + * @return Map + */ + public function prepareRoles(): Map + { + return 'do not work'; + exit; + } +} \ No newline at end of file diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php new file mode 100644 index 0000000000..4df74f85ba --- /dev/null +++ b/app/Import/Importer/ImporterInterface.php @@ -0,0 +1,52 @@ +where('user_id', Auth::user()->id)->first(); + if (!is_null($model)) { + return $model; + } + } + throw new NotFoundHttpException; + } + + /** + * @param $status + */ + public function change($status) + { + $this->status = $status; + $this->save(); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user() + { + return $this->belongsTo('FireflyIII\User'); + } +} diff --git a/app/Providers/ExportJobServiceProvider.php b/app/Providers/ExportJobServiceProvider.php index 380b14a2e9..f39e24cd1e 100644 --- a/app/Providers/ExportJobServiceProvider.php +++ b/app/Providers/ExportJobServiceProvider.php @@ -31,6 +31,27 @@ class ExportJobServiceProvider extends ServiceProvider */ public function boot() { + $this->exportJob(); + $this->importJob(); + + } + + /** + * Register the application services. + * + * @return void + */ + public function register() + { + // + } + + /** + * + */ + private function exportJob() + { + $this->app->bind( 'FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface', function (Application $app, array $arguments) { @@ -46,13 +67,20 @@ class ExportJobServiceProvider extends ServiceProvider ); } - /** - * Register the application services. - * - * @return void - */ - public function register() + private function importJob() { - // + $this->app->bind( + 'FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface', + function (Application $app, array $arguments) { + if (!isset($arguments[0]) && $app->auth->check()) { + return app('FireflyIII\Repositories\ImportJob\ImportJobRepository', [$app->auth->user()]); + } + if (!isset($arguments[0]) && !$app->auth->check()) { + throw new FireflyException('There is no user present.'); + } + + return app('FireflyIII\Repositories\ImportJob\ImportJobRepository', $arguments); + } + ); } } diff --git a/app/Repositories/ImportJob/ImportJobRepository.php b/app/Repositories/ImportJob/ImportJobRepository.php new file mode 100644 index 0000000000..4e281d83c6 --- /dev/null +++ b/app/Repositories/ImportJob/ImportJobRepository.php @@ -0,0 +1,81 @@ +user = $user; + } + + /** + * @param string $fileType + * + * @return ImportJob + */ + public function create(string $fileType): ImportJob + { + $count = 0; + while ($count < 30) { + $key = Str::random(12); + $existing = $this->findByKey($key); + if (is_null($existing->id)) { + $importJob = new ImportJob; + $importJob->user()->associate($this->user); + $importJob->file_type = $fileType; + $importJob->key = Str::random(12); + $importJob->status = 'import_status_never_started'; + $importJob->save(); + + // breaks the loop: + return $importJob; + } + $count++; + + } + + return new ImportJob; + } + + /** + * @param string $key + * + * @return ImportJob + */ + public function findByKey(string $key): ImportJob + { + $result = $this->user->importJobs()->where('key', $key)->first(); + if (is_null($result)) { + return new ImportJob; + } + + return $result; + } +} \ No newline at end of file diff --git a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php new file mode 100644 index 0000000000..24cdd2aca9 --- /dev/null +++ b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php @@ -0,0 +1,36 @@ +hasMany('FireflyIII\Models\ExportJob'); } + /** + * @return HasMany + */ + public function importjobs(): HasMany + { + return $this->hasMany('FireflyIII\Models\ImportJob'); + } + /** * Checks if the user has a role by its name. * diff --git a/config/app.php b/config/app.php index 79a9e67ed6..731400f5b1 100644 --- a/config/app.php +++ b/config/app.php @@ -180,8 +180,8 @@ return [ // own stuff: -// Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, -// Barryvdh\Debugbar\ServiceProvider::class, + Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, + Barryvdh\Debugbar\ServiceProvider::class, 'DaveJamesMiller\Breadcrumbs\ServiceProvider', 'TwigBridge\ServiceProvider', 'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider', diff --git a/config/firefly.php b/config/firefly.php index 78a1b62634..e19fd5d75b 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -126,6 +126,7 @@ return [ 'rule' => 'FireflyIII\Models\Rule', 'ruleGroup' => 'FireflyIII\Models\RuleGroup', 'jobKey' => 'FireflyIII\Models\ExportJob', + 'importJob' => 'FireflyIII\Models\ImportJob', // lists 'accountList' => 'FireflyIII\Support\Binder\AccountList', 'budgetList' => 'FireflyIII\Support\Binder\BudgetList', diff --git a/database/migrations/2016_05_23_173524_changes_for_v391.php b/database/migrations/2016_05_23_173524_changes_for_v391.php new file mode 100644 index 0000000000..b8efd50b4d --- /dev/null +++ b/database/migrations/2016_05_23_173524_changes_for_v391.php @@ -0,0 +1,44 @@ +increments('id'); + $table->timestamps(); + $table->integer('user_id')->unsigned(); + $table->string('key', 12)->unique(); + $table->string('file_type', 12); + $table->string('status', 45); + + // connect rule groups to users + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + } + ); + } +} diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 6d3f31f8b0..61ed2cd803 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -326,97 +326,12 @@ return [ 'title_transfer' => 'Transfers', 'title_transfers' => 'Transfers', - - // csv import: - 'csv_import' => 'Import CSV file', - 'csv' => 'CSV', - 'csv_index_title' => 'Upload and import a CSV file', - 'csv_define_column_roles' => 'Define column roles', - 'csv_map_values' => 'Map found values to existing values', - 'csv_download_config' => 'Download CSV configuration file.', - 'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by the folks at Atlassian. Simply upload your CSV file and follow the instructions. If you would like to learn more, please click on the button at the top of this page.', - 'csv_index_beta_warning' => 'This tool is very much in beta. Please proceed with caution', - 'csv_header_help' => 'Check this box when your CSV file\'s first row consists of column names, not actual data', - 'csv_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', - 'csv_csv_file_help' => 'Select the CSV file here. You can only upload one file at a time', - 'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', - 'csv_upload_button' => 'Start importing CSV', - 'csv_column_roles_title' => 'Define column roles', - 'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. The next step will show you what this button does.', - 'csv_column_roles_table' => 'Column roles', - 'csv_column' => 'CSV column', - 'csv_column_name' => 'CSV column name', - 'csv_column_example' => 'Column example data', - 'csv_column_role' => 'Column contains?', - 'csv_do_map_value' => 'Map value?', - 'csv_continue' => 'Continue to the next step', - 'csv_go_back' => 'Go back to the previous step', - 'csv_map_title' => 'Map found values to existing values', - 'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other things won\'t be created twice.', - 'csv_field_value' => 'Field value from CSV', - 'csv_field_mapped_to' => 'Must be mapped to...', - 'csv_do_not_map' => 'Do not map this value', - 'csv_download_config_title' => 'Download CSV configuration', - 'csv_download_config_text' => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.', - 'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all over again. But, if the import succeeds, it will be easier to upload similar CSV files.', - 'csv_do_download_config' => 'Download configuration file.', - 'csv_empty_description' => '(empty description)', - 'csv_upload_form' => 'CSV upload form', - 'csv_index_unsupported_warning' => 'The CSV importer is yet incapable of doing the following:', - 'csv_unsupported_map' => 'The importer cannot map the column ":columnRole" to existing values in the database.', - 'csv_unsupported_value' => 'The importer does not know how to handle values in columns marked as ":columnRole".', - 'csv_cannot_store_value' => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.', - 'csv_process_title' => 'CSV import finished!', - 'csv_process_text' => 'The CSV importer has finished and has processed :rows rows', - 'csv_row' => 'Row', - 'csv_import_with_errors' => 'There was one error.|There were :errors errors.', - 'csv_error_see_logs' => 'Check the log files to see details.', - 'csv_process_new_entries' => 'Firefly has created :imported new transaction(s).', - 'csv_start_over' => 'Import again', - 'csv_to_index' => 'Back home', - 'csv_upload_not_writeable' => 'Cannot write to the path mentioned here. Cannot upload', - 'csv_column__ignore' => '(ignore this column)', - 'csv_column_account-iban' => 'Asset account (IBAN)', - 'csv_column_account-id' => 'Asset account ID (matching Firefly)', - 'csv_column_account-name' => 'Asset account (name)', - 'csv_column_amount' => 'Amount', - 'csv_column_amount-comma-separated' => 'Amount (comma as decimal separator)', - 'csv_column_bill-id' => 'Bill ID (matching Firefly)', - 'csv_column_bill-name' => 'Bill name', - 'csv_column_budget-id' => 'Budget ID (matching Firefly)', - 'csv_column_budget-name' => 'Budget name', - 'csv_column_category-id' => 'Category ID (matching Firefly)', - 'csv_column_category-name' => 'Category name', - 'csv_column_currency-code' => 'Currency code (ISO 4217)', - 'csv_column_currency-id' => 'Currency ID (matching Firefly)', - 'csv_column_currency-name' => 'Currency name (matching Firefly)', - 'csv_column_currency-symbol' => 'Currency symbol (matching Firefly)', - 'csv_column_date-rent' => 'Rent calculation date', - 'csv_column_date-transaction' => 'Date', - 'csv_column_description' => 'Description', - 'csv_column_opposing-iban' => 'Opposing account (IBAN)', - 'csv_column_opposing-id' => 'Opposing account ID (matching Firefly)', - 'csv_column_opposing-name' => 'Opposing account (name)', - 'csv_column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', - 'csv_column_ing-debet-credit' => 'ING specific debet/credit indicator', - 'csv_column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', - 'csv_column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', - 'csv_column_sepa-db' => 'SEPA Direct Debet', - 'csv_column_tags-comma' => 'Tags (comma separated)', - 'csv_column_tags-space' => 'Tags (space separated)', - 'csv_column_account-number' => 'Asset account (account number)', - 'csv_column_opposing-number' => 'Opposing account (account number)', - 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', - 'csv_specifix_AbnAmroDescription' => 'Select this when you\'re importing ABN AMRO CSV export files.', - 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', - 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', - 'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?', - 'could_not_recover' => 'Could not continue from the previous step. Your progress has been lost :(. The log files will tell you what happened.', - 'must_select_roles' => 'You must select some roles for your file content, or the process cannot continue.', - 'invalid_mapping' => 'You have submitted an invalid mapping. The process cannot continue.', - 'no_file_uploaded' => 'It seems you did not upload a file.', - + // import routine + 'import_data' => 'Import data', + 'import' => 'Import', + 'import_intro_text' => 'Some intro here', + 'import_file_help' => 'Select your file', + // create new stuff: 'create_new_withdrawal' => 'Create new withdrawal', diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index b60433f727..d261d04399 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -71,13 +71,9 @@ return [ 'code' => 'Code', 'iban' => 'IBAN', 'accountNumber' => 'Account number', - 'csv' => 'CSV file', 'has_headers' => 'Headers', 'date_format' => 'Date format', - 'csv_config' => 'CSV import configuration', 'specifix' => 'Bank- or file specific fixes', - 'csv_import_account' => 'Default import account', - 'csv_delimiter' => 'CSV field delimiter', 'attachments[]' => 'Attachments', 'store_new_withdrawal' => 'Store new withdrawal', 'store_new_deposit' => 'Store new deposit', @@ -102,9 +98,6 @@ return [ 'include_config' => 'Include configuration file', 'include_old_uploads' => 'Include imported data', 'accounts' => 'Export transactions from these accounts', - 'csv_comma' => 'A comma (,)', - 'csv_semicolon' => 'A semicolon (;)', - 'csv_tab' => 'A tab (invisible)', 'delete_account' => 'Delete account ":name"', 'delete_bill' => 'Delete bill ":name"', 'delete_budget' => 'Delete budget ":name"', diff --git a/resources/lang/en_US/help.php b/resources/lang/en_US/help.php index bf2fff9e38..d1fa572177 100644 --- a/resources/lang/en_US/help.php +++ b/resources/lang/en_US/help.php @@ -55,11 +55,6 @@ return [ 'categories-show' => 'categories.show', 'categories-show-date' => 'categories.show.date', 'categories-noCategory' => 'categories.noCategory', - 'csv-index' => 'csv.index', - 'csv-column-roles' => 'csv.column-roles', - 'csv-map' => 'csv.map', - 'csv-download-config-page' => 'csv.download-config-page', - 'csv-process' => 'csv.process', 'currency-index' => 'currency.index', 'currency-create' => 'currency.create', 'currency-edit' => 'currency.edit', diff --git a/resources/views/import/csv/configure.twig b/resources/views/import/csv/configure.twig new file mode 100644 index 0000000000..1a4e95f42b --- /dev/null +++ b/resources/views/import/csv/configure.twig @@ -0,0 +1,84 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, job) }} +{% endblock %} + +{% block content %} + +
+
+
+
+

{{ 'import_csv_configure_title'|_ }}

+
+
+

+ {{ 'import_csv_configure_intro' }} +

+
+
+ +
+
+ +
+ + +
+
+
+
+

{{ 'import_csv_configure_form'|_ }}

+
+
+ + {{ ExpandedForm.checkbox('has_headers',1,null,{helpText: 'csv_header_help'|_}) }} + {{ ExpandedForm.text('date_format','Ymd',{helpText: trans('firefly.csv_date_help', {dateExample: phpdate('Ymd')}) }) }} + {{ ExpandedForm.select('csv_delimiter', data.delimiters, 0, {helpText: 'csv_delimiter_help'|_} ) }} + + {{ ExpandedForm.file('csv_config',{helpText: 'csv_csv_config_file_help'|_}) }} + + {{ ExpandedForm.select('csv_import_account', data.accounts, 0, {helpText: 'csv_import_account_help'|_} ) }} + + {{ ExpandedForm.multiCheckbox('specifix', data.specifix) }} + + + + {% if not uploadPossible %} +
+
+   +
+ +
+
{{ path }}
+

+ {{ 'csv_upload_not_writeable'|_ }} +

+
+
+ {% endif %} + +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ + + + +{% endblock %} diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index 16ea17886e..8449e5a93f 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -25,7 +25,7 @@
- {{ ExpandedForm.file('import_file',{helpText: 'import_file_help'|_}) }} + {{ ExpandedForm.file('import_file', {helpText: 'import_file_help'|_}) }} {{ ExpandedForm.select('import_file_type', importFileTypes, defaultImportType, {'helpText' : 'import_file_type_help'|_}) }} diff --git a/resources/views/partials/menu-sidebar.twig b/resources/views/partials/menu-sidebar.twig index 54de5a7a93..4920233f04 100644 --- a/resources/views/partials/menu-sidebar.twig +++ b/resources/views/partials/menu-sidebar.twig @@ -110,8 +110,7 @@ -
  • - +
  • @@ -120,12 +119,9 @@
      - {% if Config.get('firefly.csv_import_enabled') %} -
    • - {{ 'csv_import'|_ }} -
    • - {% endif %} - +
    • + {{ 'import_data'|_ }} +
    • {{ 'export_data'|_ }}
    • From 13b92c47d9967a93da3e45d173e439945e4aa3f5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 11 Jun 2016 06:31:40 +0200 Subject: [PATCH 05/94] Translations and fixes. --- app/Import/Importer/CsvImporter.php | 24 +++++---- resources/lang/en_US/firefly.php | 64 +++++++++++++---------- resources/lang/en_US/form.php | 13 +++++ resources/views/import/csv/configure.twig | 9 ++-- 4 files changed, 68 insertions(+), 42 deletions(-) diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 801c61f0ee..abd18cb913 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -51,22 +51,16 @@ class CsvImporter implements ImporterInterface ]; $data = [ - 'accounts' => ExpandedForm::makeSelectList($accounts), - 'specifix' => [], - 'delimiters' => $delimiters, + 'accounts' => ExpandedForm::makeSelectList($accounts), + 'specifix' => [], + 'delimiters' => $delimiters, + 'upload_path' => storage_path('upload'), + 'is_upload_possible' => is_writable(storage_path('upload')), ]; return $data; } - /** - * @param ImportJob $job - */ - public function setJob(ImportJob $job) - { - $this->job = $job; - } - /** * Returns a Map thing used to allow the user to * define roles for each entry. @@ -78,4 +72,12 @@ class CsvImporter implements ImporterInterface return 'do not work'; exit; } + + /** + * @param ImportJob $job + */ + public function setJob(ImportJob $job) + { + $this->job = $job; + } } \ No newline at end of file diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 61ed2cd803..e2a0bfac5a 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -327,11 +327,11 @@ return [ 'title_transfers' => 'Transfers', // import routine - 'import_data' => 'Import data', - 'import' => 'Import', - 'import_intro_text' => 'Some intro here', - 'import_file_help' => 'Select your file', - + 'import_data' => 'Import data', + 'import' => 'Import', + 'import_intro_text' => 'Some intro here', + 'import_file_help' => 'Select your file', + // create new stuff: 'create_new_withdrawal' => 'Create new withdrawal', @@ -727,29 +727,39 @@ return [ 'split_table_intro_withdrawal' => 'Split your withdrawal in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', 'store_splitted_withdrawal' => 'Store splitted withdrawal', 'update_splitted_withdrawal' => 'Update splitted withdrawal', + 'split_title_deposit' => 'Split your new deposit', + 'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.', + 'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.', + 'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.', + 'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_deposit' => 'Store splitted deposit', + 'split_title_transfer' => 'Split your new transfer', + 'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.', + 'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.', + 'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.', + 'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_transfer' => 'Store splitted transfer', + 'add_another_split' => 'Add another split', + 'split-transactions' => 'Split transactions', + 'split-new-transaction' => 'Split a new transaction', + 'do_split' => 'Do a split', + 'split_this_withdrawal' => 'Split this withdrawal', + 'split_this_deposit' => 'Split this deposit', + 'split_this_transfer' => 'Split this transfer', - 'split_title_deposit' => 'Split your new deposit', - 'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.', - 'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.', - 'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.', - 'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', - 'store_splitted_deposit' => 'Store splitted deposit', - - 'split_title_transfer' => 'Split your new transfer', - 'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.', - 'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.', - 'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.', - 'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', - 'store_splitted_transfer' => 'Store splitted transfer', - - 'add_another_split' => 'Add another split', - 'split-transactions' => 'Split transactions', - 'split-new-transaction' => 'Split a new transaction', - - 'do_split' => 'Do a split', - 'split_this_withdrawal' => 'Split this withdrawal', - 'split_this_deposit' => 'Split this deposit', - 'split_this_transfer' => 'Split this transfer', + // import + 'import_file_type_csv' => 'CSV (comma separated values)', + 'import_file_type_help' => 'Select the type of file you will upload', + 'import_start' => 'Start the import', + 'import_csv_configure_title' => 'Configure your import', + 'import_csv_configure_intro' => 'There are some options for your CSV import.', + 'import_csv_configure_form' => 'Form', + 'csv_header_help' => 'Check this if the first row of your CSV file are the column titles', + 'csv_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', + 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'csv_upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', ]; diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index d261d04399..c2bf0cb5e9 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -130,4 +130,17 @@ return [ 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.', 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.', 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.', + + // import + 'import_file' => 'Import file', + 'import_file_type' => 'Import file type', + 'csv_comma' => 'A comma (,)', + 'csv_semicolon' => 'A semicolon (;)', + 'csv_tab' => 'A tab (invisible)', + 'csv_delimiter' => 'CSV field delimiter', + 'csv_import_account' => 'Default import account', + 'csv_config' => 'CSV import configuration', + + + ]; diff --git a/resources/views/import/csv/configure.twig b/resources/views/import/csv/configure.twig index 1a4e95f42b..d9645cd7d1 100644 --- a/resources/views/import/csv/configure.twig +++ b/resources/views/import/csv/configure.twig @@ -14,7 +14,7 @@
  • - {{ 'import_csv_configure_intro' }} + {{ 'import_csv_configure_intro'|_ }}

    @@ -45,14 +45,14 @@ - {% if not uploadPossible %} + {% if not data.is_upload_possible %}
     
    -
    {{ path }}
    +
    {{ data.upload_path }}

    {{ 'csv_upload_not_writeable'|_ }}

    @@ -64,7 +64,7 @@
    - + {% if data.is_upload_possible %}
    @@ -76,6 +76,7 @@
    + {% endif %} From 7a5ef6013a1349f2097ff151dc5dc391acb4f2d3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 11 Jun 2016 06:31:56 +0200 Subject: [PATCH 06/94] Fix #271 --- resources/views/transactions/show.twig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index 74b1685f13..ffedce51dd 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -13,6 +13,10 @@
    + + + + From b80d8cf77487165008e39ccdc64e1f43db579d30 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 11 Jun 2016 06:33:51 +0200 Subject: [PATCH 07/94] Move date picker stuff to new method. --- app/Http/Middleware/Range.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 2c9638ec97..5277610f79 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -84,16 +84,22 @@ class Range } Session::put('first', $first); } - $current = Carbon::now()->formatLocalized('%B %Y'); - $next = Carbon::now()->endOfMonth()->addDay()->formatLocalized('%B %Y'); - $prev = Carbon::now()->startOfMonth()->subDay()->formatLocalized('%B %Y'); - View::share('currentMonthName', $current); - View::share('previousMonthName', $prev); - View::share('nextMonthName', $next); + } + $this->datePicker(); return $theNext($request); } + private function datePicker() + { + $current = Carbon::now()->formatLocalized('%B %Y'); + $next = Carbon::now()->endOfMonth()->addDay()->formatLocalized('%B %Y'); + $prev = Carbon::now()->startOfMonth()->subDay()->formatLocalized('%B %Y'); + View::share('currentMonthName', $current); + View::share('previousMonthName', $prev); + View::share('nextMonthName', $next); + } + } From 307e6a2337db6874d7ee165d00cafc827b5e2216 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 11 Jun 2016 06:36:46 +0200 Subject: [PATCH 08/94] Renamed fields #267 --- app/Http/Middleware/Range.php | 8 +++++--- public/js/ff/firefly.js | 16 ++++++++-------- resources/views/layout/default.twig | 6 +++--- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 5277610f79..3d6345563c 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -94,12 +94,14 @@ class Range private function datePicker() { + + $current = Carbon::now()->formatLocalized('%B %Y'); $next = Carbon::now()->endOfMonth()->addDay()->formatLocalized('%B %Y'); $prev = Carbon::now()->startOfMonth()->subDay()->formatLocalized('%B %Y'); - View::share('currentMonthName', $current); - View::share('previousMonthName', $prev); - View::share('nextMonthName', $next); + View::share('currentPeriodName', $current); + View::share('previousPeriodName', $prev); + View::share('nextPeriodName', $next); } } diff --git a/public/js/ff/firefly.js b/public/js/ff/firefly.js index 5ce868262a..eb8ce819b6 100644 --- a/public/js/ff/firefly.js +++ b/public/js/ff/firefly.js @@ -6,14 +6,14 @@ $(function () { $('.currency-option').click(currencySelect); var ranges = {}; - // range for the current month: - ranges[dateRangeConfig.currentMonth] = [moment().startOf('month'), moment().endOf('month')]; + // range for the current period: + ranges[dateRangeConfig.currentPeriod] = [moment().startOf('month'), moment().endOf('month')]; - // range for the previous month: - ranges[dateRangeConfig.previousMonth] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]; + // range for the previous period: + ranges[dateRangeConfig.previousPeriod] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]; - // range for the next month: - ranges[dateRangeConfig.nextMonth] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')]; + // range for the next period: + ranges[dateRangeConfig.nextPeriod] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')]; // range for everything: ranges[dateRangeConfig.everything] = [dateRangeConfig.firstDate, moment()]; @@ -48,10 +48,10 @@ $(function () { label: label, _token: token }).done(function () { - console.log('Succesfully sent new date range.'); + console.log('Succesfully sent new date range [' + start.format('YYYY-MM-DD') + '-' + end.format('YYYY-MM-DD') + '].'); window.location.reload(true); }).fail(function () { - console.log('Could not send new date range.'); + console.log('Could not send new date range [' + start.format('YYYY-MM-DD') + '-' + end.format('YYYY-MM-DD') + ']'); alert('Could not change date range'); }); diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 16ee742cef..3e7aee50da 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -172,9 +172,9 @@ linkTitle: "{{ Session.get('start').formatLocalized(monthAndDayFormat) }} - {{ Session.get('end').formatLocalized(monthAndDayFormat) }}", URL: "{{ route('daterange') }}", firstDate: moment("{{ Session.get('first').format('Y-m-d') }}"), - currentMonth: "{{ currentMonthName }}", - previousMonth: "{{ previousMonthName }}", - nextMonth: "{{ nextMonthName }}", + currentPeriod: "{{ currentPeriodName }}", + previousPeriod: "{{ previousPeriodName }}", + nextPeriod: "{{ nextPeriodName }}", everything: '{{ 'everything'|_ }}', customRangeLabel: '{{ 'customRange'|_ }}', applyLabel: '{{ 'apply'|_ }}', From ec1816569852a65a24aaec88523a79609cde92b9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 11 Jun 2016 07:38:30 +0200 Subject: [PATCH 09/94] Fixed #267 --- app/Http/Controllers/HomeController.php | 21 ++++++---- app/Http/Middleware/Range.php | 55 +++++++++++++++++++++---- public/js/ff/firefly.js | 13 ++---- resources/views/layout/default.twig | 13 +++--- 4 files changed, 71 insertions(+), 31 deletions(-) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 172b4c6695..48ad6319a0 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -18,8 +18,9 @@ use FireflyIII\Models\AccountType; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use Illuminate\Http\Request; use Illuminate\Support\Collection; -use Input; +use Log; use Preferences; use Route; use Session; @@ -41,18 +42,24 @@ class HomeController extends Controller parent::__construct(); } - public function dateRange() + /** + * @param Request $request + */ + public function dateRange(Request $request) { - $start = new Carbon(Input::get('start')); - $end = new Carbon(Input::get('end')); - $label = Input::get('label'); + $start = new Carbon($request->get('start')); + $end = new Carbon($request->get('end')); + $label = $request->get('label'); $isCustomRange = false; + Log::debug('Received dateRange', ['start' => $request->get('start'), 'end' => $request->get('end'), 'label' => $request->get('label')]); + // check if the label is "everything" or "Custom range" which will betray // a possible problem with the budgets. if ($label === strval(trans('firefly.everything')) || $label === strval(trans('firefly.customRange'))) { $isCustomRange = true; + Log::debug('Range is now marked as "custom".'); } $diff = $start->diffInDays($end); @@ -173,11 +180,11 @@ class HomeController extends Controller $search = [ '{account}', '{what}', '{rule}', '{tj}', '{category}', '{budget}', '{code}', '{date}', '{attachment}', '{bill}', '{limitrepetition}', '{currency}', '{jobKey}', '{piggyBank}', '{ruleGroup}', '{rule}', '{route}', '{unfinishedJournal}', - '{reportType}', '{start_date}', '{end_date}', '{accountList}','{tag}','{journalList}' + '{reportType}', '{start_date}', '{end_date}', '{accountList}', '{tag}', '{journalList}', ]; $replace = [1, 'asset', 1, 1, 1, 1, 'abc', '2016-01-01', 1, 1, 1, 1, 1, 1, 1, 1, 'index', 1, - 'default', '20160101', '20160131', '1,2',1,'1,2' + 'default', '20160101', '20160131', '1,2', 1, '1,2', ]; if (count($search) != count($replace)) { echo 'count'; diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 3d6345563c..4673cbe39c 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -13,6 +13,7 @@ namespace FireflyIII\Http\Middleware; use Carbon\Carbon; use Closure; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Contracts\Auth\Guard; use Illuminate\Http\Request; @@ -64,7 +65,6 @@ class Range // ignore preference. set the range to be the current month: if (!Session::has('start') && !Session::has('end')) { - /** @var \FireflyIII\Models\Preference $viewRange */ $viewRange = Preferences::get('viewRange', '1M')->data; $start = new Carbon; $start = Navigation::updateStartDate($viewRange, $start); @@ -94,14 +94,51 @@ class Range private function datePicker() { - - - $current = Carbon::now()->formatLocalized('%B %Y'); - $next = Carbon::now()->endOfMonth()->addDay()->formatLocalized('%B %Y'); - $prev = Carbon::now()->startOfMonth()->subDay()->formatLocalized('%B %Y'); - View::share('currentPeriodName', $current); - View::share('previousPeriodName', $prev); - View::share('nextPeriodName', $next); + $viewRange = Preferences::get('viewRange', '1M')->data; + $start = Session::get('start'); + $end = Session::get('end'); + $prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period + $prevEnd = Navigation::subtractPeriod($end, $viewRange); + $nextStart = Navigation::addPeriod($start, $viewRange, 0);// add for previous period + $nextEnd = Navigation::addPeriod($end, $viewRange, 0); + $ranges = []; + $ranges['current'] = [$start->format('Y-m-d'), $end->format('Y-m-d')]; + $ranges['previous'] = [$prevStart->format('Y-m-d'), $prevEnd->format('Y-m-d')]; + $ranges['next'] = [$nextStart->format('Y-m-d'), $nextEnd->format('Y-m-d')]; + + switch ($viewRange) { + case '1D': + $format = (string)trans('config.month_and_day'); + break; + case '3M': + $format = (string)trans('config.quarter_in_year'); + break; + case '6M': + $format = (string)trans('config.half_year'); + break; + case '1Y': + $format = (string)trans('config.year'); + break; + case '1M': + $format = (string)trans('config.month'); + break; + default: + throw new FireflyException('The date picker does not yet support "' . $viewRange . '".'); + case '1W': + $format = (string)trans('config.week_in_year'); + break; + } + + + $current = $start->formatLocalized($format); + $next = $nextStart->formatLocalized($format); + $prev = $prevStart->formatLocalized($format); + View::share('dpStart', $start->format('Y-m-d')); + View::share('dpEnd', $end->format('Y-m-d')); + View::share('dpCurrent', $current); + View::share('dpPrevious', $prev); + View::share('dpNext', $next); + View::share('dpRanges', $ranges); } } diff --git a/public/js/ff/firefly.js b/public/js/ff/firefly.js index eb8ce819b6..d63eddbcc3 100644 --- a/public/js/ff/firefly.js +++ b/public/js/ff/firefly.js @@ -6,14 +6,9 @@ $(function () { $('.currency-option').click(currencySelect); var ranges = {}; - // range for the current period: - ranges[dateRangeConfig.currentPeriod] = [moment().startOf('month'), moment().endOf('month')]; - - // range for the previous period: - ranges[dateRangeConfig.previousPeriod] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]; - - // range for the next period: - ranges[dateRangeConfig.nextPeriod] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')]; + ranges[dateRangeConfig.currentPeriod] = [moment(dateRangeConfig.ranges.current[0]), moment(dateRangeConfig.ranges.current[1])]; + ranges[dateRangeConfig.previousPeriod] = [moment(dateRangeConfig.ranges.previous[0]), moment(dateRangeConfig.ranges.previous[1])]; + ranges[dateRangeConfig.nextPeriod] = [moment(dateRangeConfig.ranges.next[0]), moment(dateRangeConfig.ranges.next[1])]; // range for everything: ranges[dateRangeConfig.everything] = [dateRangeConfig.firstDate, moment()]; @@ -51,7 +46,7 @@ $(function () { console.log('Succesfully sent new date range [' + start.format('YYYY-MM-DD') + '-' + end.format('YYYY-MM-DD') + '].'); window.location.reload(true); }).fail(function () { - console.log('Could not send new date range [' + start.format('YYYY-MM-DD') + '-' + end.format('YYYY-MM-DD') + ']'); + console.log('Could not send new date range.'); alert('Could not change date range'); }); diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 3e7aee50da..b6e07e01a6 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -167,20 +167,21 @@ // date range picker configuration: var dateRangeConfig = { - startDate: moment("{{ Session.get('start').format('Y-m-d') }}"), - endDate: moment("{{ Session.get('end').format('Y-m-d') }}"), + startDate: moment("{{ dpStart }}"), + endDate: moment("{{ dpEnd }}"), linkTitle: "{{ Session.get('start').formatLocalized(monthAndDayFormat) }} - {{ Session.get('end').formatLocalized(monthAndDayFormat) }}", URL: "{{ route('daterange') }}", firstDate: moment("{{ Session.get('first').format('Y-m-d') }}"), - currentPeriod: "{{ currentPeriodName }}", - previousPeriod: "{{ previousPeriodName }}", - nextPeriod: "{{ nextPeriodName }}", + currentPeriod: "{{ dpCurrent }}", + previousPeriod: "{{ dpPrevious }}", + nextPeriod: "{{ dpNext }}", everything: '{{ 'everything'|_ }}', customRangeLabel: '{{ 'customRange'|_ }}', applyLabel: '{{ 'apply'|_ }}', cancelLabel: '{{ 'cancel'|_ }}', fromLabel: '{{ 'from'|_ }}', - toLabel: '{{ 'to'|_ }}' + toLabel: '{{ 'to'|_ }}', + ranges: {{ dpRanges|json_encode|raw }} }; var token = "{{ csrf_token() }}"; From e63f2169054e34d0046bb0a3474b39d43442d199 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Jun 2016 09:58:33 +0200 Subject: [PATCH 10/94] Fix small bug in reorder routine. --- app/Http/Controllers/TransactionController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 3250147493..9243717407 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -223,7 +223,7 @@ class TransactionController extends Controller if (count($ids) > 0) { $order = 0; foreach ($ids as $id) { - $journal = $repository->find($id); + $journal = $repository->find(intval($id)); if ($journal && $journal->date->format('Y-m-d') == $date->format('Y-m-d')) { $journal->order = $order; $order++; From bdee8cde776a18649a6b5e250ff715b5993ce989 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 16 Jun 2016 08:04:22 +0200 Subject: [PATCH 11/94] This fixes #273 --- app/Crud/Account/AccountCrud.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Crud/Account/AccountCrud.php b/app/Crud/Account/AccountCrud.php index b09c413407..27de351833 100644 --- a/app/Crud/Account/AccountCrud.php +++ b/app/Crud/Account/AccountCrud.php @@ -52,9 +52,9 @@ class AccountCrud implements AccountCrudInterface * * @return bool */ - public function destroy(Account $account, Account $moveTo = null): bool + public function destroy(Account $account, Account $moveTo): bool { - if (!is_null($moveTo)) { + if (!is_null($moveTo->id)) { // update all transactions: DB::table('transactions')->where('account_id', $account->id)->update(['account_id' => $moveTo->id]); } @@ -73,7 +73,7 @@ class AccountCrud implements AccountCrudInterface { $account = $this->user->accounts()->find($accountId); if (is_null($account)) { - $account = new Account; + return new Account; } return $account; From 6267930938b8d585e2805f35450b673a085da723 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 16 Jun 2016 20:52:30 +0200 Subject: [PATCH 12/94] Work on new chart for year report. --- app/Helpers/Report/BudgetReportHelper.php | 68 +++++++++++++++++++ .../Report/BudgetReportHelperInterface.php | 9 +++ public/js/ff/reports/default/year.js | 19 +++--- resources/views/reports/default/year.twig | 58 ++++++++++++---- 4 files changed, 134 insertions(+), 20 deletions(-) diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index cf447ea4a6..547840f962 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -18,6 +18,7 @@ use FireflyIII\Helpers\Collection\BudgetLine; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; /** @@ -40,6 +41,73 @@ class BudgetReportHelper implements BudgetReportHelperInterface $this->repository = $repository; } + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return Collection + */ + public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection + { + // chart properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('budget-year'); + $cache->addProperty($accounts->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); + } + + $headers = []; + $current = clone $start; + $return = new Collection; + $set = $this->repository->getBudgets(); + $budgets = []; + $spent = []; + while ($current < $end) { + $short = $current->format('m-Y'); + $headers[$short] = $current->formatLocalized((string)trans('config.month')); + $current->addMonth(); + } + + + /** @var Budget $budget */ + foreach ($set as $budget) { + $id = $budget->id; + $budgets[$id] = $budget->name; + $spent[$id] = []; + $current = clone $start; + $sum = '0'; + + + while ($current < $end) { + $currentEnd = clone $current; + $currentEnd->endOfMonth(); + $format = $current->format('m-Y'); + $budgetSpent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $current, $currentEnd); + $spent[$id][$format] = $budgetSpent; + $sum = bcadd($sum, $budgetSpent); + $current->addMonth(); + } + + if (bccomp('0', $sum) === 0) { + // not spent anything. + unset($spent[$id]); + unset($budgets[$id]); + } + } + + $return->put('headers', $headers); + $return->put('budgets', $budgets); + $return->put('spent', $spent); + + $cache->store($return); + + return $return; + } + /** * @param Carbon $start * @param Carbon $end diff --git a/app/Helpers/Report/BudgetReportHelperInterface.php b/app/Helpers/Report/BudgetReportHelperInterface.php index dd0290f6ae..d776da2ead 100644 --- a/app/Helpers/Report/BudgetReportHelperInterface.php +++ b/app/Helpers/Report/BudgetReportHelperInterface.php @@ -23,6 +23,15 @@ use Illuminate\Support\Collection; */ interface BudgetReportHelperInterface { + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return Collection + */ + public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection; + /** * @param Carbon $start * @param Carbon $end diff --git a/public/js/ff/reports/default/year.js b/public/js/ff/reports/default/year.js index f342c7e1dc..79a3b50faa 100644 --- a/public/js/ff/reports/default/year.js +++ b/public/js/ff/reports/default/year.js @@ -19,16 +19,19 @@ function drawChart() { columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); - // in a loop - $.each($('.budget_year_chart'), function (i, v) { - var holder = $(v); - var id = holder.attr('id'); - var budgetId = holder.data('budget'); - columnChart('chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, id); - - }); + $('.budget-chart-activate').on('click', clickBudgetChart); } +function clickBudgetChart(e) { + "use strict"; + var link = $(e.target); + var budgetId = link.data('budget'); + console.log('Budget id is ' + budgetId); + $('#budget_chart').empty(); + columnChart('chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budget_chart'); + + return false; +} function showIncomes() { "use strict"; diff --git a/resources/views/reports/default/year.twig b/resources/views/reports/default/year.twig index 89a2403ca2..9f605f9f42 100644 --- a/resources/views/reports/default/year.twig +++ b/resources/views/reports/default/year.twig @@ -61,21 +61,55 @@ - {% for budget in budgets %} -
    -
    -
    -
    -

    {{ 'budget'|_ }} {{ budget.name }}

    -
    -
    - -
    +
    +
    +
    +
    +

    {{ 'budgets'|_ }}

    +
    +
    +
    {{ trans('list.amount') }}{{ journal|formatJournal }}
    {{ trans('list.date') }} {{ journal.date.formatLocalized(monthAndDayFormat) }}
    + + + + {% for date, header in budgets.get('headers') %} + + {% endfor %} + + + + {% set spentData = budgets.get('spent') %} + {% for budgetId, budgetName in budgets.get('budgets') %} + + + {% for date, header in budgets.get('headers') %} + + {% endfor %} + + + {% endfor %} + + +
     {{ header }}
    + {{ budgetName }} + {{ spentData[budgetId][date]|formatAmount }}
    - {% endfor %} + +
    +
    +
    +
    +

    {{ 'chart'|_ }}

    +
    +
    + + +
    +
    +
    +
    {% endblock %} {% block scripts %} From ae649223d8b8a0badb55f567df385a09d05846ed Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 16 Jun 2016 20:52:59 +0200 Subject: [PATCH 13/94] Mobile add money to piggy routine --- app/Http/Controllers/AccountController.php | 1 + app/Http/Controllers/PiggyBankController.php | 20 ++++++++++ app/Http/Controllers/ReportController.php | 4 +- app/Http/routes.php | 7 ++++ app/Repositories/Budget/BudgetRepository.php | 25 +++++++------ resources/lang/en_US/firefly.php | 1 + resources/views/list/piggy-banks.twig | 6 +++ resources/views/piggy-banks/add-mobile.twig | 39 ++++++++++++++++++++ 8 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 resources/views/piggy-banks/add-mobile.twig diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 71ea51855e..734a5008a1 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -251,6 +251,7 @@ class AccountController extends Controller $end = Navigation::subtractPeriod($end, $range, 1); } + $cache->store($entries); return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle')); } diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index a3ec4d3d52..b1a1b910b9 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -48,6 +48,26 @@ class PiggyBankController extends Controller View::share('mainTitleIcon', 'fa-sort-amount-asc'); } + /** + * Add money to piggy bank (for mobile devices) + * + * @param ARI $repository + * @param PiggyBank $piggyBank + * + * @return $this + */ + public function addMobile(ARI $repository, PiggyBank $piggyBank) + { + /** @var Carbon $date */ + $date = session('end', Carbon::now()->endOfMonth()); + $leftOnAccount = $repository->leftOnAccount($piggyBank->account, $date); + $savedSoFar = $piggyBank->currentRelevantRep()->currentamount; + $leftToSave = bcsub($piggyBank->targetamount, $savedSoFar); + $maxAmount = min($leftOnAccount, $leftToSave); + + return view('piggy-banks.add-mobile', compact('piggyBank', 'maxAmount')); + } + /** * Add money to piggy bank * diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 099aa84518..037d3c2ab1 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -320,9 +320,7 @@ class ReportController extends Controller $incomes = $this->helper->getIncomeReport($start, $end, $accounts); $expenses = $this->helper->getExpenseReport($start, $end, $accounts); $tags = $this->helper->tagReport($start, $end, $accounts); - - // find the budgets we've spent money on this period with these accounts: - $budgets = $this->budgetHelper->getBudgetsWithExpenses($start, $end, $accounts); + $budgets = $this->budgetHelper->budgetYearOverview($start, $end, $accounts); Session::flash('gaEventCategory', 'report'); Session::flash('gaEventAction', 'year'); diff --git a/app/Http/routes.php b/app/Http/routes.php index 159931d398..1921e0d017 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -277,6 +277,13 @@ Route::group( Route::get('/piggy-banks', ['uses' => 'PiggyBankController@index', 'as' => 'piggy-banks.index']); Route::get('/piggy-banks/add/{piggyBank}', ['uses' => 'PiggyBankController@add', 'as' => 'piggy-banks.addMoney']); Route::get('/piggy-banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@remove', 'as' => 'piggy-banks.removeMoney']); + + Route::get('/piggy-banks/add-money/{piggyBank}', ['uses' => 'PiggyBankController@addMobile', 'as' => 'piggy-banks.add-money-mobile']); + Route::get('/piggy-banks/remove-money/{piggyBank}', ['uses' => 'PiggyBankController@removeMobile', 'as' => 'piggy-banks.remove-money-mobile']); + + Route::post('/piggy-banks/add-money/{piggyBank}', ['uses' => 'PiggyBankController@postAddMobile', 'as' => 'piggy-banks.post-add-mobile']); + Route::post('/piggy-banks/remove-money/{piggyBank}', ['uses' => 'PiggyBankController@postRemoveMobile', 'as' => 'piggy-banks.post-remove-mobile']); + Route::get('/piggy-banks/create', ['uses' => 'PiggyBankController@create', 'as' => 'piggy-banks.create']); Route::get('/piggy-banks/edit/{piggyBank}', ['uses' => 'PiggyBankController@edit', 'as' => 'piggy-banks.edit']); Route::get('/piggy-banks/delete/{piggyBank}', ['uses' => 'PiggyBankController@delete', 'as' => 'piggy-banks.delete']); diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index b104c8f292..c12d3a3fb4 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -23,6 +23,7 @@ use FireflyIII\User; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; +use Log; /** * Class BudgetRepository @@ -44,6 +45,18 @@ class BudgetRepository implements BudgetRepositoryInterface $this->user = $user; } + /** + * @return bool + */ + public function cleanupBudgets(): bool + { + // delete limits with amount 0: + BudgetLimit::where('amount', 0)->delete(); + + return true; + + } + /** * @param Budget $budget * @@ -156,18 +169,6 @@ class BudgetRepository implements BudgetRepositoryInterface return $set; } - /** - * @return bool - */ - public function cleanupBudgets(): bool - { - // delete limits with amount 0: - BudgetLimit::where('amount', 0)->delete(); - - return true; - - } - /** * @return Collection */ diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 6d3f31f8b0..4b66462e54 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -739,6 +739,7 @@ return [ 'balanceFor' => 'Balance for :name', // piggy banks: + 'add_money_to_piggy' => 'Add money to piggy bank ":name"', 'piggy_bank' => 'Piggy bank', 'new_piggy_bank' => 'Create new piggy bank', 'store_piggy_bank' => 'Store new piggy bank', diff --git a/resources/views/list/piggy-banks.twig b/resources/views/list/piggy-banks.twig index 753fae7a47..3a049b0e1f 100644 --- a/resources/views/list/piggy-banks.twig +++ b/resources/views/list/piggy-banks.twig @@ -2,6 +2,12 @@ {% for piggyBank in piggyBanks %} + +
    + + +
    + diff --git a/resources/views/piggy-banks/add-mobile.twig b/resources/views/piggy-banks/add-mobile.twig new file mode 100644 index 0000000000..3a4854efe3 --- /dev/null +++ b/resources/views/piggy-banks/add-mobile.twig @@ -0,0 +1,39 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} +{% endblock %} + +{% block content %} + {{ Form.open({'class' : 'form-horizontal','id' : 'store','url' : route('piggy-banks.post-add-mobile', piggyBank.id)}) }} +
    +
    +
    +
    +

    {{ trans('firefly.add_money_to_piggy', {name: piggyBank.name}) }}

    +
    +
    + +

    + {{ 'max_amount_add'|_ }}: {{ maxAmount|formatAmount }}. +

    + +
    +
    {{ getCurrencySymbol()|raw }}
    + +
    +

    +   +

    + + + +
    +
    +
    + +
    + {{ Form.close|raw }} +{% endblock %} \ No newline at end of file From d2733a4df072848ab8918585064de785c511af13 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 17 Jun 2016 06:03:42 +0200 Subject: [PATCH 14/94] First attempt at rewriting all migrations. --- .../2014_06_27_163032_create_users_table.php | 46 -- ...6_27_163145_create_account_types_table.php | 47 -- ...014_06_27_163259_create_accounts_table.php | 57 -- ...4_06_27_163817_create_components_table.php | 54 -- ...4_06_27_163818_create_piggybanks_table.php | 63 --- ...42_create_transaction_currencies_table.php | 48 -- ..._164512_create_transaction_types_table.php | 48 -- ...19_create_recurring_transactions_table.php | 62 --- ...4620_create_transaction_journals_table.php | 65 --- ...06_27_164836_create_transactions_table.php | 60 --- ...344_create_component_transaction_table.php | 53 -- ...te_component_transaction_journal_table.php | 52 -- ..._07_06_123842_create_preferences_table.php | 52 -- ...2014_07_09_204843_create_session_table.php | 47 -- .../2014_07_17_183717_create_limits_table.php | 54 -- ...07_19_055011_create_limit_repeat_table.php | 52 -- ..._component_recurring_transaction_table.php | 55 -- ...919_create_piggybank_repetitions_table.php | 52 -- ...8_100330_create_piggybank_events_table.php | 54 -- ...14_08_23_113221_create_reminders_table.php | 53 -- ...12_100000_create_password_resets_table.php | 42 -- ...11_10_172053_create_account_meta_table.php | 53 -- ...135749_create_transaction_groups_table.php | 50 -- ...action_group_transaction_journal_table.php | 51 -- .../2014_12_13_190730_changes_for_v321.php | 502 ------------------ .../2014_12_24_191544_changes_for_v322.php | 182 ------- .../2015_01_18_082406_changes_for_v325.php | 60 --- .../2015_02_27_210653_changes_for_v332.php | 50 -- .../2015_03_27_061038_changes_for_v333.php | 40 -- .../2015_03_29_174140_changes_for_v336.php | 244 --------- .../2015_04_26_054507_changes_for_v3310.php | 85 --- .../2015_04_28_075215_changes_for_v3310a.php | 43 -- .../2015_04_28_075317_changes_for_v3310b.php | 35 -- .../2015_05_22_172026_changes_for_v3409.php | 50 -- ...2015_05_28_041652_entrust_setup_tables.php | 87 --- .../2015_06_14_093841_changes_for_v345.php | 44 -- .../2015_07_03_102450_changes_for_v3462.php | 43 -- .../2015_07_14_204645_changes_for_v349.php | 38 -- .../2015_07_17_190438_changes_for_v3410.php | 60 --- .../2016_01_11_193428_changes_for_v370.php | 135 ----- .../2016_02_04_144117_changes_for_v380.php | 70 --- .../2016_02_24_172426_create_jobs_table.php | 42 -- .../2016_04_08_181054_changes_for_v383.php | 38 -- .../2016_04_25_093451_changes_for_v390.php | 194 ------- ...016_06_16_192032_create_support_tables.php | 183 +++++++ 45 files changed, 183 insertions(+), 3312 deletions(-) delete mode 100644 database/migrations/2014_06_27_163032_create_users_table.php delete mode 100644 database/migrations/2014_06_27_163145_create_account_types_table.php delete mode 100644 database/migrations/2014_06_27_163259_create_accounts_table.php delete mode 100644 database/migrations/2014_06_27_163817_create_components_table.php delete mode 100644 database/migrations/2014_06_27_163818_create_piggybanks_table.php delete mode 100644 database/migrations/2014_06_27_164042_create_transaction_currencies_table.php delete mode 100644 database/migrations/2014_06_27_164512_create_transaction_types_table.php delete mode 100644 database/migrations/2014_06_27_164619_create_recurring_transactions_table.php delete mode 100644 database/migrations/2014_06_27_164620_create_transaction_journals_table.php delete mode 100644 database/migrations/2014_06_27_164836_create_transactions_table.php delete mode 100644 database/migrations/2014_06_27_165344_create_component_transaction_table.php delete mode 100644 database/migrations/2014_07_05_171326_create_component_transaction_journal_table.php delete mode 100644 database/migrations/2014_07_06_123842_create_preferences_table.php delete mode 100644 database/migrations/2014_07_09_204843_create_session_table.php delete mode 100644 database/migrations/2014_07_17_183717_create_limits_table.php delete mode 100644 database/migrations/2014_07_19_055011_create_limit_repeat_table.php delete mode 100644 database/migrations/2014_08_06_044416_create_component_recurring_transaction_table.php delete mode 100644 database/migrations/2014_08_12_173919_create_piggybank_repetitions_table.php delete mode 100644 database/migrations/2014_08_18_100330_create_piggybank_events_table.php delete mode 100644 database/migrations/2014_08_23_113221_create_reminders_table.php delete mode 100644 database/migrations/2014_10_12_100000_create_password_resets_table.php delete mode 100644 database/migrations/2014_11_10_172053_create_account_meta_table.php delete mode 100644 database/migrations/2014_11_29_135749_create_transaction_groups_table.php delete mode 100644 database/migrations/2014_11_29_140217_create_transaction_group_transaction_journal_table.php delete mode 100644 database/migrations/2014_12_13_190730_changes_for_v321.php delete mode 100644 database/migrations/2014_12_24_191544_changes_for_v322.php delete mode 100644 database/migrations/2015_01_18_082406_changes_for_v325.php delete mode 100644 database/migrations/2015_02_27_210653_changes_for_v332.php delete mode 100644 database/migrations/2015_03_27_061038_changes_for_v333.php delete mode 100644 database/migrations/2015_03_29_174140_changes_for_v336.php delete mode 100644 database/migrations/2015_04_26_054507_changes_for_v3310.php delete mode 100644 database/migrations/2015_04_28_075215_changes_for_v3310a.php delete mode 100644 database/migrations/2015_04_28_075317_changes_for_v3310b.php delete mode 100644 database/migrations/2015_05_22_172026_changes_for_v3409.php delete mode 100644 database/migrations/2015_05_28_041652_entrust_setup_tables.php delete mode 100644 database/migrations/2015_06_14_093841_changes_for_v345.php delete mode 100644 database/migrations/2015_07_03_102450_changes_for_v3462.php delete mode 100644 database/migrations/2015_07_14_204645_changes_for_v349.php delete mode 100644 database/migrations/2015_07_17_190438_changes_for_v3410.php delete mode 100644 database/migrations/2016_01_11_193428_changes_for_v370.php delete mode 100644 database/migrations/2016_02_04_144117_changes_for_v380.php delete mode 100644 database/migrations/2016_02_24_172426_create_jobs_table.php delete mode 100644 database/migrations/2016_04_08_181054_changes_for_v383.php delete mode 100644 database/migrations/2016_04_25_093451_changes_for_v390.php create mode 100644 database/migrations/2016_06_16_192032_create_support_tables.php diff --git a/database/migrations/2014_06_27_163032_create_users_table.php b/database/migrations/2014_06_27_163032_create_users_table.php deleted file mode 100644 index 57f44c51cd..0000000000 --- a/database/migrations/2014_06_27_163032_create_users_table.php +++ /dev/null @@ -1,46 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->string('email', 100)->unique(); - $table->string('password', 60); - $table->rememberToken(); - $table->string('reset', 32)->nullable(); - } - ); - } - -} diff --git a/database/migrations/2014_06_27_163145_create_account_types_table.php b/database/migrations/2014_06_27_163145_create_account_types_table.php deleted file mode 100644 index 85b0742038..0000000000 --- a/database/migrations/2014_06_27_163145_create_account_types_table.php +++ /dev/null @@ -1,47 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->string('type', 30); - $table->boolean('editable'); - - $table->unique('type'); - } - ); - } - -} diff --git a/database/migrations/2014_06_27_163259_create_accounts_table.php b/database/migrations/2014_06_27_163259_create_accounts_table.php deleted file mode 100644 index 972bf66fcb..0000000000 --- a/database/migrations/2014_06_27_163259_create_accounts_table.php +++ /dev/null @@ -1,57 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('user_id')->unsigned(); - $table->integer('account_type_id')->unsigned(); - $table->string('name', 100); - $table->boolean('active'); - - // connect accounts to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - // connect accounts to account_types - $table->foreign('account_type_id')->references('id')->on('account_types')->onDelete('cascade'); - - // for a user, the account name must be unique. - $table->unique(['user_id', 'account_type_id', 'name']); - } - ); - } - -} diff --git a/database/migrations/2014_06_27_163817_create_components_table.php b/database/migrations/2014_06_27_163817_create_components_table.php deleted file mode 100644 index 795f14685f..0000000000 --- a/database/migrations/2014_06_27_163817_create_components_table.php +++ /dev/null @@ -1,54 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->string('name', 50); - $table->integer('user_id')->unsigned(); - $table->string('class', 20); - - // connect components to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - // for a user, the component type & name must be unique. - $table->unique(['user_id', 'class', 'name']); - } - ); - - } - -} diff --git a/database/migrations/2014_06_27_163818_create_piggybanks_table.php b/database/migrations/2014_06_27_163818_create_piggybanks_table.php deleted file mode 100644 index cffda131c9..0000000000 --- a/database/migrations/2014_06_27_163818_create_piggybanks_table.php +++ /dev/null @@ -1,63 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('account_id')->unsigned(); - $table->string('name', 100); - $table->decimal('targetamount', 10, 2); - $table->date('startdate')->nullable(); - $table->date('targetdate')->nullable(); - $table->boolean('repeats'); - $table->enum('rep_length', ['day', 'week', 'quarter', 'month', 'year'])->nullable(); - $table->smallInteger('rep_every')->unsigned(); - $table->smallInteger('rep_times')->unsigned()->nullable(); - $table->enum('reminder', ['day', 'week', 'quarter', 'month', 'year'])->nullable(); - $table->smallInteger('reminder_skip')->unsigned(); - $table->boolean('remind_me'); - $table->integer('order')->unsigned(); - - // connect account to piggy bank. - $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); - - // for an account, the name must be unique. - $table->unique(['account_id', 'name']); - - } - ); - } - -} diff --git a/database/migrations/2014_06_27_164042_create_transaction_currencies_table.php b/database/migrations/2014_06_27_164042_create_transaction_currencies_table.php deleted file mode 100644 index 4c0f9b7ef0..0000000000 --- a/database/migrations/2014_06_27_164042_create_transaction_currencies_table.php +++ /dev/null @@ -1,48 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->string('code', 3); - - // code must be unique. - $table->unique(['code']); - } - ); - } - -} diff --git a/database/migrations/2014_06_27_164512_create_transaction_types_table.php b/database/migrations/2014_06_27_164512_create_transaction_types_table.php deleted file mode 100644 index 46d82089c0..0000000000 --- a/database/migrations/2014_06_27_164512_create_transaction_types_table.php +++ /dev/null @@ -1,48 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->string('type', 50); - - // type must be unique. - $table->unique(['type']); - } - ); - } - -} diff --git a/database/migrations/2014_06_27_164619_create_recurring_transactions_table.php b/database/migrations/2014_06_27_164619_create_recurring_transactions_table.php deleted file mode 100644 index 1b6e637279..0000000000 --- a/database/migrations/2014_06_27_164619_create_recurring_transactions_table.php +++ /dev/null @@ -1,62 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('user_id')->unsigned(); - $table->string('name', 50); - $table->string('match', 255); - $table->decimal('amount_min', 10, 2); - $table->decimal('amount_max', 10, 2); - $table->date('date'); - $table->boolean('active'); - - $table->boolean('automatch'); - $table->enum('repeat_freq', ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly']); - $table->smallInteger('skip')->unsigned(); - - // connect user id to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - // for a user, the name must be unique - $table->unique(['user_id', 'name']); - - - } - ); - } - -} diff --git a/database/migrations/2014_06_27_164620_create_transaction_journals_table.php b/database/migrations/2014_06_27_164620_create_transaction_journals_table.php deleted file mode 100644 index fdb56228b1..0000000000 --- a/database/migrations/2014_06_27_164620_create_transaction_journals_table.php +++ /dev/null @@ -1,65 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('user_id')->unsigned(); - $table->integer('transaction_type_id')->unsigned(); - $table->integer('recurring_transaction_id')->unsigned()->nullable(); - $table->integer('transaction_currency_id')->unsigned(); - $table->string('description', 255)->nullable(); - $table->boolean('completed'); - $table->date('date'); - - // connect users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - // connect transaction journals to transaction types - $table->foreign('transaction_type_id')->references('id')->on('transaction_types')->onDelete('cascade'); - - // connect transaction journals to recurring transactions - $table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('set null'); - - // connect transaction journals to transaction currencies - $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade'); - - - } - ); - } - -} diff --git a/database/migrations/2014_06_27_164836_create_transactions_table.php b/database/migrations/2014_06_27_164836_create_transactions_table.php deleted file mode 100644 index 0ce8e2adab..0000000000 --- a/database/migrations/2014_06_27_164836_create_transactions_table.php +++ /dev/null @@ -1,60 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('account_id')->unsigned(); - $table->integer('piggybank_id')->nullable()->unsigned(); - $table->integer('transaction_journal_id')->unsigned(); - $table->string('description', 255)->nullable(); - $table->decimal('amount', 10, 2); - - // connect account id: - $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); - - // connect piggy banks - $table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('set null'); - - // connect transactions to transaction journals - $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - - - } - ); - } - -} diff --git a/database/migrations/2014_06_27_165344_create_component_transaction_table.php b/database/migrations/2014_06_27_165344_create_component_transaction_table.php deleted file mode 100644 index 8d76199427..0000000000 --- a/database/migrations/2014_06_27_165344_create_component_transaction_table.php +++ /dev/null @@ -1,53 +0,0 @@ -increments('id'); - $table->integer('component_id')->unsigned(); - $table->integer('transaction_id')->unsigned(); - - // connect to components - $table->foreign('component_id')->references('id')->on('components')->onDelete('cascade'); - - // connect to transactions - $table->foreign('transaction_id')->references('id')->on('transactions')->onDelete('cascade'); - - // combo must be unique: - $table->unique(['component_id', 'transaction_id']); - } - ); - } - -} diff --git a/database/migrations/2014_07_05_171326_create_component_transaction_journal_table.php b/database/migrations/2014_07_05_171326_create_component_transaction_journal_table.php deleted file mode 100644 index 06b2a89b4a..0000000000 --- a/database/migrations/2014_07_05_171326_create_component_transaction_journal_table.php +++ /dev/null @@ -1,52 +0,0 @@ -increments('id'); - $table->integer('component_id')->unsigned(); - $table->integer('transaction_journal_id')->unsigned(); - - // link components with component_id - $table->foreign('component_id')->references('id')->on('components')->onDelete('cascade'); - - // link transaction journals with transaction_journal_id - $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - - // combo must be unique: - $table->unique(['component_id', 'transaction_journal_id'], 'cid_tjid_unique'); - } - ); - } - -} diff --git a/database/migrations/2014_07_06_123842_create_preferences_table.php b/database/migrations/2014_07_06_123842_create_preferences_table.php deleted file mode 100644 index a71f0205a4..0000000000 --- a/database/migrations/2014_07_06_123842_create_preferences_table.php +++ /dev/null @@ -1,52 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('user_id')->unsigned(); - $table->string('name'); - $table->text('data'); - - // connect preferences to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - // only one preference per name per user - $table->unique(['user_id', 'name']); - } - ); - } - -} diff --git a/database/migrations/2014_07_09_204843_create_session_table.php b/database/migrations/2014_07_09_204843_create_session_table.php deleted file mode 100644 index 08a9a98f75..0000000000 --- a/database/migrations/2014_07_09_204843_create_session_table.php +++ /dev/null @@ -1,47 +0,0 @@ -string('id')->unique(); - $table->integer('user_id')->nullable(); - $table->string('ip_address', 45)->nullable(); - $table->text('user_agent')->nullable(); - $table->text('payload'); - $table->integer('last_activity'); - } - ); - } - -} diff --git a/database/migrations/2014_07_17_183717_create_limits_table.php b/database/migrations/2014_07_17_183717_create_limits_table.php deleted file mode 100644 index d94065e832..0000000000 --- a/database/migrations/2014_07_17_183717_create_limits_table.php +++ /dev/null @@ -1,54 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('component_id')->unsigned(); - $table->date('startdate'); - $table->decimal('amount', 10, 2); - $table->boolean('repeats'); - $table->enum('repeat_freq', ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly']); - - $table->unique(['component_id', 'startdate', 'repeat_freq'], 'unique_ci_combi'); - - // connect component - $table->foreign('component_id')->references('id')->on('components')->onDelete('cascade'); - } - ); - } - -} diff --git a/database/migrations/2014_07_19_055011_create_limit_repeat_table.php b/database/migrations/2014_07_19_055011_create_limit_repeat_table.php deleted file mode 100644 index 83a655c5a0..0000000000 --- a/database/migrations/2014_07_19_055011_create_limit_repeat_table.php +++ /dev/null @@ -1,52 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('limit_id')->unsigned(); - $table->date('startdate'); - $table->date('enddate'); - $table->decimal('amount', 10, 2); - - $table->unique(['limit_id', 'startdate', 'enddate']); - - // connect limit - $table->foreign('limit_id')->references('id')->on('limits')->onDelete('cascade'); - } - ); - } - -} diff --git a/database/migrations/2014_08_06_044416_create_component_recurring_transaction_table.php b/database/migrations/2014_08_06_044416_create_component_recurring_transaction_table.php deleted file mode 100644 index 21d0b8b6c3..0000000000 --- a/database/migrations/2014_08_06_044416_create_component_recurring_transaction_table.php +++ /dev/null @@ -1,55 +0,0 @@ -increments('id'); - $table->integer('component_id')->unsigned(); - $table->integer('recurring_transaction_id')->unsigned(); - $table->boolean('optional'); - - // link components with component_id - $table->foreign('component_id')->references('id')->on('components')->onDelete('cascade'); - - // link transaction journals with transaction_journal_id - $table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('cascade'); - - // component and recurring transaction must be unique. - $table->unique(['component_id', 'recurring_transaction_id'], 'cid_rtid_unique'); - - } - ); - } - -} diff --git a/database/migrations/2014_08_12_173919_create_piggybank_repetitions_table.php b/database/migrations/2014_08_12_173919_create_piggybank_repetitions_table.php deleted file mode 100644 index e8dfda76b6..0000000000 --- a/database/migrations/2014_08_12_173919_create_piggybank_repetitions_table.php +++ /dev/null @@ -1,52 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('piggybank_id')->unsigned(); - $table->date('startdate')->nullable(); - $table->date('targetdate')->nullable(); - $table->decimal('currentamount', 10, 2); - - $table->unique(['piggybank_id', 'startdate', 'targetdate']); - - // connect instance to piggybank. - $table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('cascade'); - } - ); - } - -} diff --git a/database/migrations/2014_08_18_100330_create_piggybank_events_table.php b/database/migrations/2014_08_18_100330_create_piggybank_events_table.php deleted file mode 100644 index 54f57e90aa..0000000000 --- a/database/migrations/2014_08_18_100330_create_piggybank_events_table.php +++ /dev/null @@ -1,54 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('piggybank_id')->unsigned(); - $table->integer('transaction_journal_id')->unsigned()->nullable(); - - $table->date('date'); - $table->decimal('amount', 10, 2); - - // connect instance to piggybank. - $table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('cascade'); - - // connect to journal: - $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('set null'); - } - ); - } - -} diff --git a/database/migrations/2014_08_23_113221_create_reminders_table.php b/database/migrations/2014_08_23_113221_create_reminders_table.php deleted file mode 100644 index a7d769a2fc..0000000000 --- a/database/migrations/2014_08_23_113221_create_reminders_table.php +++ /dev/null @@ -1,53 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('user_id')->unsigned(); - $table->date('startdate'); - $table->date('enddate')->nullable(); - $table->boolean('active'); - $table->boolean('notnow')->default(0); - $table->integer('remindersable_id')->unsigned()->nullable(); - $table->string('remindersable_type')->nullable(); - - // connect reminders to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - } - ); - } - -} diff --git a/database/migrations/2014_10_12_100000_create_password_resets_table.php b/database/migrations/2014_10_12_100000_create_password_resets_table.php deleted file mode 100644 index c9da2ac1cc..0000000000 --- a/database/migrations/2014_10_12_100000_create_password_resets_table.php +++ /dev/null @@ -1,42 +0,0 @@ -string('email')->index(); - $table->string('token')->index(); - $table->timestamp('created_at'); - } - ); - } - -} diff --git a/database/migrations/2014_11_10_172053_create_account_meta_table.php b/database/migrations/2014_11_10_172053_create_account_meta_table.php deleted file mode 100644 index 044d1e28e6..0000000000 --- a/database/migrations/2014_11_10_172053_create_account_meta_table.php +++ /dev/null @@ -1,53 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('account_id')->unsigned(); - $table->string('name'); - $table->text('data'); - - $table->unique(['account_id', 'name']); - - // link to account! - $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); - - - } - ); - } - -} diff --git a/database/migrations/2014_11_29_135749_create_transaction_groups_table.php b/database/migrations/2014_11_29_135749_create_transaction_groups_table.php deleted file mode 100644 index a312a3d7d9..0000000000 --- a/database/migrations/2014_11_29_135749_create_transaction_groups_table.php +++ /dev/null @@ -1,50 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('user_id')->unsigned(); - $table->enum('relation', ['balance']); - - // connect groups to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - } - ); - - - } - -} diff --git a/database/migrations/2014_11_29_140217_create_transaction_group_transaction_journal_table.php b/database/migrations/2014_11_29_140217_create_transaction_group_transaction_journal_table.php deleted file mode 100644 index b615d7b102..0000000000 --- a/database/migrations/2014_11_29_140217_create_transaction_group_transaction_journal_table.php +++ /dev/null @@ -1,51 +0,0 @@ -increments('id'); - $table->integer('transaction_group_id')->unsigned(); - $table->integer('transaction_journal_id')->unsigned(); - - // link to foreign tables. - $table->foreign('transaction_group_id', 'tr_grp_id')->references('id')->on('transaction_groups')->onDelete('cascade'); - $table->foreign('transaction_journal_id', 'tr_trj_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - - // add unique. - $table->unique(['transaction_group_id', 'transaction_journal_id'], 'tt_joined'); - } - ); - } - -} diff --git a/database/migrations/2014_12_13_190730_changes_for_v321.php b/database/migrations/2014_12_13_190730_changes_for_v321.php deleted file mode 100644 index 13b4715fcc..0000000000 --- a/database/migrations/2014_12_13_190730_changes_for_v321.php +++ /dev/null @@ -1,502 +0,0 @@ -moveBudgetsBack(); // 1. - $this->moveCategoriesBack(); // 2. - $this->createComponentId(); // 3. - $this->updateComponentInBudgetLimits(); // 4. - $this->createComponentIdForeignKey(); // 5. - $this->dropBudgetIdColumnInBudgetLimits(); // 6. - $createComponents = new CreateComponentTransactionJournalTable; // 7. - $createComponents->up(); - $this->moveBackEntriesForBudgetsInJoinedTable(); // 8. - $this->moveBackEntriesForCategoriesInJoinedTable(); // 9. - $this->dropBudgetJournalTable(); // 10. - $this->dropCategoryJournalTable(); // 11. - $this->dropBudgetTable(); // 12. - $this->dropCategoryTable(); // 13. - $this->renameBudgetLimits(); // 14. - $this->renamePiggyBankEvents(); // 15. - $this->renameBudgetLimitToBudgetInRepetitions(); // 16. - // 17 and then 18 and then 19 - $this->dropFieldsFromCurrencyTable(); // 20. - - - } - - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - $this->createBudgetTable(); // 1. - $this->createCategoryTable(); // 2. - $this->createBudgetJournalTable(); // 3 - $this->createCategoryJournalTable(); // 4. - $this->moveBudgets(); // 5. - $this->moveCategories(); // 6. - $this->correctNameForBudgetLimits(); // 7. - $this->correctNameForPiggyBankEvents(); // 8. - $this->renameBudgetToBudgetLimitInRepetitions(); // 9. - $this->addBudgetIdFieldToBudgetLimits(); // 10. - $this->moveComponentIdToBudgetId(); // 11. - $this->dropComponentJournalTable(); // 12. - $this->dropComponentRecurringTransactionTable(); // 13. - $this->dropComponentTransactionTable(); // 14. - $this->dropPiggyBankIdFromTransactions(); // 15. - $this->dropComponentIdFromBudgetLimits(); // 16. - $this->expandCurrencyTable(); // 17. - - } - - private function addBudgetIdFieldToBudgetLimits() - { - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->integer('budget_id', false, true)->nullable()->after('updated_at'); - $table->foreign('budget_id', 'bid_foreign')->references('id')->on('budgets')->onDelete('cascade'); - } - ); - } - - private function correctNameForBudgetLimits() - { - Schema::rename('limits', 'budget_limits'); - } - - private function correctNameForPiggyBankEvents() - { - Schema::rename('piggybank_events', 'piggy_bank_events'); - - } - - private function createBudgetJournalTable() - { - Schema::create( - 'budget_transaction_journal', function (Blueprint $table) { - $table->increments('id'); - $table->integer('budget_id')->unsigned(); - $table->integer('transaction_journal_id')->unsigned(); - $table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade'); - $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - $table->unique(['budget_id', 'transaction_journal_id'], 'budid_tjid_unique'); - } - ); - } - - private function createBudgetTable() - { - Schema::create( - 'budgets', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->string('name', 50); - $table->integer('user_id')->unsigned(); - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - $table->unique(['user_id', 'name']); - } - ); - - - } - - private function createCategoryJournalTable() - { - Schema::create( - 'category_transaction_journal', function (Blueprint $table) { - $table->increments('id'); - $table->integer('category_id')->unsigned(); - $table->integer('transaction_journal_id')->unsigned(); - $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade'); - $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - $table->unique(['category_id', 'transaction_journal_id'], 'catid_tjid_unique'); - } - ); - } - - private function createCategoryTable() - { - Schema::create( - 'categories', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->string('name', 50); - $table->integer('user_id')->unsigned(); - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - $table->unique(['user_id', 'name']); - } - ); - } - - private function createComponentId() - { - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->integer('component_id')->unsigned(); - } - ); - } - - private function createComponentIdForeignKey() - { - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->foreign('component_id', 'limits_component_id_foreign')->references('id')->on('components')->onDelete('cascade'); - } - ); - } - - private function dropBudgetIdColumnInBudgetLimits() - { - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->dropForeign('bid_foreign'); - $table->dropColumn('budget_id'); // also drop foreign key! - } - ); - } - - private function dropBudgetJournalTable() - { - Schema::dropIfExists('budget_transaction_journal'); - } - - private function dropBudgetTable() - { - Schema::dropIfExists('budgets'); - } - - private function dropCategoryJournalTable() - { - Schema::dropIfExists('category_transaction_journal'); - } - - private function dropCategoryTable() - { - Schema::dropIfExists('categories'); - } - - private function dropComponentIdFromBudgetLimits() - { - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->dropForeign('limits_component_id_foreign'); - $table->dropColumn('component_id'); - } - ); - } - - private function dropComponentJournalTable() - { - Schema::dropIfExists('component_transaction_journal'); - } - - private function dropComponentRecurringTransactionTable() - { - Schema::dropIfExists('component_recurring_transaction'); - } - - private function dropComponentTransactionTable() - { - Schema::dropIfExists('component_transaction'); - } - - private function dropFieldsFromCurrencyTable() - { - - Schema::table( - 'transaction_currencies', function (Blueprint $table) { - $table->dropColumn('symbol'); - $table->dropColumn('name'); - } - ); - } - - private function dropPiggyBankIdFromTransactions() - { - - Schema::table( - 'transactions', function (Blueprint $table) { - if (Schema::hasColumn('transactions', 'piggybank_id')) { - $table->dropForeign('transactions_piggybank_id_foreign'); - $table->dropColumn('piggybank_id'); - } - } - ); - } - - private function expandCurrencyTable() - { - Schema::table( - 'transaction_currencies', function (Blueprint $table) { - $table->string('name', 48)->nullable(); - $table->string('symbol', 8)->nullable(); - } - ); - \DB::update('UPDATE `transaction_currencies` SET `symbol` = "€", `name` = "Euro" WHERE `code` = "EUR";'); - } - - private function moveBackEntriesForBudgetsInJoinedTable() - { - $set = DB::table('budget_transaction_journal')->get(); - /** @var \stdClass $entry */ - foreach ($set as $entry) { - $budget = Budget::find($entry->budget_id); - if ($budget) { - /** @var \FireflyIII\Models\Component $component */ - $component = Component::where('class', 'Budget')->where('name', $budget->name)->where('user_id', $budget->user_id)->first(); - if ($component) { - DB::table('component_transaction_journal')->insert( - [ - 'component_id' => $component->id, - 'transaction_journal_id' => $entry->transaction_journal_id, - ] - ); - } - - } - } - - } - - private function moveBackEntriesForCategoriesInJoinedTable() - { - $set = DB::table('category_transaction_journal')->get(); - /** @var \stdClass $entry */ - foreach ($set as $entry) { - $category = Category::find($entry->category_id); - if ($category) { - /** @var \FireflyIII\Models\Component $component */ - $component = Component::where('class', 'Category')->where('name', $category->name)->where('user_id', $category->user_id)->first(); - if ($component) { - DB::table('component_transaction_journal')->insert( - [ - 'component_id' => $component->id, - 'transaction_journal_id' => $entry->transaction_journal_id, - ] - ); - } - - } - } - - } - - private function moveBudgets() - { - Component::where('class', 'Budget')->get()->each( - function (Component $c) { - $entry = [ - 'user_id' => $c->user_id, - 'name' => $c->name, - - ]; - $budget = Budget::firstOrCreate($entry); - // create entry in budget_transaction_journal - $connections = DB::table('component_transaction_journal')->where('component_id', $c->id)->get(); - /** @var \stdClass $connection */ - foreach ($connections as $connection) { - DB::table('budget_transaction_journal')->insert( - [ - 'budget_id' => $budget->id, - 'transaction_journal_id' => $connection->transaction_journal_id, - ] - ); - } - } - ); - } - - private function moveBudgetsBack() - { - Budget::get()->each( - function (Budget $budget) { - Component::firstOrCreate( - [ - 'name' => $budget->name, - 'user_id' => $budget->user_id, - 'class' => 'Budget', - ] - ); - } - ); - } - - private function moveCategories() - { - Component::where('class', 'Category')->get()->each( - function (Component $c) { - $entry = [ - 'user_id' => $c->user_id, - 'name' => $c->name, - - ]; - $category = Category::firstOrCreate($entry); - // create entry in category_transaction_journal - $connections = DB::table('component_transaction_journal')->where('component_id', $c->id)->get(); - /** @var \stdClass $connection */ - foreach ($connections as $connection) { - DB::table('category_transaction_journal')->insert( - [ - 'category_id' => $category->id, - 'transaction_journal_id' => $connection->transaction_journal_id, - ] - ); - } - } - ); - } - - private function moveCategoriesBack() - { - Category::get()->each( - function (Category $category) { - Component::firstOrCreate( - [ - 'name' => $category->name, - 'user_id' => $category->user_id, - 'class' => 'Category', - ] - ); - } - ); - } - - private function moveComponentIdToBudgetId() - { - BudgetLimit::get()->each( - function (BudgetLimit $bl) { - $component = Component::find($bl->component_id); - if ($component) { - $budget = Budget::whereName($component->name)->whereUserId($component->user_id)->first(); - if ($budget) { - $bl->budget_id = $budget->id; - $bl->save(); - } - } - } - ); - - } - - private function renameBudgetLimitToBudgetInRepetitions() - { - Schema::table( - 'limit_repetitions', function (Blueprint $table) { - $table->dropForeign('limit_repetitions_budget_limit_id_foreign'); - $table->renameColumn('budget_limit_id', 'limit_id'); - $table->foreign('limit_id')->references('id')->on('limits')->onDelete('cascade'); - } - ); - } - - private function renameBudgetLimits() - { - Schema::rename('budget_limits', 'limits'); - } - - private function renameBudgetToBudgetLimitInRepetitions() - { - Schema::table( - 'limit_repetitions', function (Blueprint $table) { - $table->dropForeign('limit_repetitions_limit_id_foreign'); - $table->renameColumn('limit_id', 'budget_limit_id'); - $table->foreign('budget_limit_id')->references('id')->on('budget_limits')->onDelete('cascade'); - } - ); - } - - private function renamePiggyBankEvents() - { - Schema::rename('piggy_bank_events', 'piggybank_events'); - - } - - private function updateComponentInBudgetLimits() - { - BudgetLimit::get()->each( - function (BudgetLimit $bl) { - $budgetId = $bl->budget_id; - $budget = Budget::find($budgetId); - if ($budget) { - $component = Component::where('class', 'Budget')->where('user_id', $budget->user_id)->where('name', $budget->name)->first(); - if ($component) { - $bl->component_id = $component->id; - $bl->save(); - } - } - } - ); - } - - -} diff --git a/database/migrations/2014_12_24_191544_changes_for_v322.php b/database/migrations/2014_12_24_191544_changes_for_v322.php deleted file mode 100644 index 5cd658dc5c..0000000000 --- a/database/migrations/2014_12_24_191544_changes_for_v322.php +++ /dev/null @@ -1,182 +0,0 @@ -dropForeign('piggy_bank_events_piggy_bank_id_foreign'); - $table->renameColumn('piggy_bank_id', 'piggybank_id'); - $table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('cascade'); - } - ); - - Schema::table( - 'piggybank_repetitions', function (Blueprint $table) { - $table->dropForeign('piggy_bank_repetitions_piggy_bank_id_foreign'); - $table->renameColumn('piggy_bank_id', 'piggybank_id'); - $table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('cascade'); - } - ); - - // remove soft delete to piggy banks - Schema::table( - 'piggybanks', function (Blueprint $table) { - $table->dropSoftDeletes(); - } - ); - - // drop keys from bills (foreign bills_uid_for and unique uid_name_unique) - Schema::table( - 'bills', function (Blueprint $table) { - $table->dropForeign('bills_uid_for'); - $table->dropUnique('uid_name_unique'); - } - ); - // drop foreign key from transaction_journals (bill_id_foreign) - Schema::table( - 'transaction_journals', function (Blueprint $table) { - $table->dropForeign('bill_id_foreign'); - - } - ); - - // drop unique constraint from budget_limits: - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->dropForeign('bid_foreign'); - $table->dropUnique('unique_bl_combi'); - $table->foreign('budget_id', 'bid_foreign')->references('id')->on('budgets')->onDelete('cascade'); - } - ); - - // rename bills to recurring_transactions - Schema::rename('bills', 'recurring_transactions'); - // recreate foreign key recurring_transactions_user_id_foreign in recurring_transactions - // recreate unique recurring_transactions_user_id_name_unique in recurring_transactions - Schema::table( - 'recurring_transactions', function (Blueprint $table) { - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - $table->unique(['user_id', 'name']); - } - ); - - // rename bill_id to recurring_transaction_id - // recreate foreign transaction_journals_recurring_transaction_id_foreign in transaction_journals - Schema::table( - 'transaction_journals', function (Blueprint $table) { - $table->renameColumn('bill_id', 'recurring_transaction_id'); - $table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('set null'); - } - ); - - - } - - - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - // rename tables: - Schema::rename('piggybank_repetitions', 'piggy_bank_repetitions'); - Schema::rename('piggybanks', 'piggy_banks'); - - // recreate it the correct way: - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->unique(['budget_id', 'startdate', 'repeat_freq'], 'unique_bl_combi'); - } - ); - - // rename fields - Schema::table( - 'piggy_bank_events', function (Blueprint $table) { - $table->dropForeign('piggybank_events_piggybank_id_foreign'); - $table->renameColumn('piggybank_id', 'piggy_bank_id'); - $table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade'); - } - ); - - Schema::table( - 'piggy_bank_repetitions', function (Blueprint $table) { - $table->dropForeign('piggybank_repetitions_piggybank_id_foreign'); - $table->renameColumn('piggybank_id', 'piggy_bank_id'); - $table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade'); - } - ); - - // add soft delete to piggy banks - Schema::table( - 'piggy_banks', function (Blueprint $table) { - $table->softDeletes(); - } - ); - - // rename everything related to recurring transactions, aka bills: - Schema::table( - 'transaction_journals', function (Blueprint $table) { - - - // drop relation - $table->dropForeign('transaction_journals_recurring_transaction_id_foreign'); - // rename column - $table->renameColumn('recurring_transaction_id', 'bill_id'); - - } - ); - - Schema::table( - 'recurring_transactions', function (Blueprint $table) { - $table->dropForeign('recurring_transactions_user_id_foreign'); - $table->dropUnique('recurring_transactions_user_id_name_unique'); - } - ); - // rename table: - Schema::rename('recurring_transactions', 'bills'); - - // recreate foreign relation: - Schema::table( - 'transaction_journals', function (Blueprint $table) { - $table->foreign('bill_id', 'bill_id_foreign')->references('id')->on('bills')->onDelete('set null'); - } - ); - - // recreate more foreign relations. - Schema::table( - 'bills', function (Blueprint $table) { - // connect user id to users - $table->foreign('user_id', 'bills_uid_for')->references('id')->on('users')->onDelete('cascade'); - - // for a user, the name must be unique - $table->unique(['user_id', 'name'], 'uid_name_unique'); - } - ); - - - } - -} diff --git a/database/migrations/2015_01_18_082406_changes_for_v325.php b/database/migrations/2015_01_18_082406_changes_for_v325.php deleted file mode 100644 index b232103ca9..0000000000 --- a/database/migrations/2015_01_18_082406_changes_for_v325.php +++ /dev/null @@ -1,60 +0,0 @@ -dropUnique('unique_ci_combi'); - - } - ); - } catch (PDOException $e) { - // don't care. - } - - // allow journal descriptions to be encrypted. - Schema::table( - 'transaction_journals', function (Blueprint $table) { - $table->boolean('encrypted')->default(0); - - } - ); - try { - DB::update('ALTER TABLE `transaction_journals` MODIFY `description` VARCHAR(1024)'); - } catch (PDOException $e) { - // don't care. - } - - } - -} diff --git a/database/migrations/2015_02_27_210653_changes_for_v332.php b/database/migrations/2015_02_27_210653_changes_for_v332.php deleted file mode 100644 index 982bd02d42..0000000000 --- a/database/migrations/2015_02_27_210653_changes_for_v332.php +++ /dev/null @@ -1,50 +0,0 @@ -boolean('encrypted')->default(0); - - } - ); - - Schema::table( - 'reminders', function (Blueprint $table) { - $table->text('metadata')->nullable(); - - } - ); - - - } - -} diff --git a/database/migrations/2015_03_27_061038_changes_for_v333.php b/database/migrations/2015_03_27_061038_changes_for_v333.php deleted file mode 100644 index 3f67a72a27..0000000000 --- a/database/migrations/2015_03_27_061038_changes_for_v333.php +++ /dev/null @@ -1,40 +0,0 @@ -smallInteger('order', false, true)->default(0); - - } - ); - } - -} diff --git a/database/migrations/2015_03_29_174140_changes_for_v336.php b/database/migrations/2015_03_29_174140_changes_for_v336.php deleted file mode 100644 index 315f1ff570..0000000000 --- a/database/migrations/2015_03_29_174140_changes_for_v336.php +++ /dev/null @@ -1,244 +0,0 @@ -dropForeign('account_user_id'); - - } - ); - - - Schema::table( - 'accounts', function (Blueprint $table) { - $table->string('name', 255)->change(); - $table->dropColumn('virtual_balance'); - - // recreate foreign key - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - // recreate unique: - $table->unique(['user_id', 'account_type_id', 'name']); - } - ); - - - /** - * BILLS - */ - // change field to be cryptable. - Schema::table( - 'bills', function (Blueprint $table) { - // drop foreign key: - $table->dropForeign('bill_user_id'); - - // drop unique: - $table->dropUnique('bill_user_id'); - } - ); - // - Schema::table( - 'bills', function (Blueprint $table) { - // raw query: - - DB::insert('ALTER TABLE `bills` CHANGE `name` `name` varchar(255) NOT NULL'); - DB::insert('ALTER TABLE `bills` CHANGE `match` `match` varchar(255) NOT NULL'); - $table->foreign('user_id', 'bills_uid_for')->references('id')->on('users')->onDelete('cascade'); - $table->unique(['user_id', 'name'], 'uid_name_unique'); - } - ); - - // remove a long forgotten index: - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->dropUnique('unique_limit'); - } - ); - - } - - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - - /** - * ACCOUNTS - */ - // change field to be cryptable. - Schema::table( - 'accounts', function (Blueprint $table) { - // drop foreign key: - $table->dropForeign('accounts_user_id_foreign'); - - // drop unique: - $table->dropUnique('accounts_user_id_account_type_id_name_unique'); - } - ); - - Schema::table( - 'accounts', function (Blueprint $table) { - $table->text('name')->change(); - $table->decimal('virtual_balance', 10, 2)->default(0); - $table->foreign('user_id', 'account_user_id')->references('id')->on('users')->onDelete('cascade'); - } - ); - - /** - * BUDGETS - */ - // add active/inactive and encrypt. - Schema::table( - 'budgets', function (Blueprint $table) { - $table->smallInteger('active', false, true)->default(1); - $table->smallInteger('encrypted', false, true)->default(0); - - // drop foreign key: - $table->dropForeign('budgets_user_id_foreign'); - - // drop unique: - $table->dropUnique('budgets_user_id_name_unique'); - - } - ); - Schema::table( - 'budgets', function (Blueprint $table) { - $table->text('name')->change(); - $table->foreign('user_id', 'budget_user_id')->references('id')->on('users')->onDelete('cascade'); - } - ); - - // reinstate a long forgotten index: - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->unique(['budget_id', 'startdate'], 'unique_limit'); - } - ); - - - /** - * BILLS - */ - // change field to be cryptable. - Schema::table( - 'bills', function (Blueprint $table) { - // drop foreign key: - $table->dropForeign('bills_uid_for'); - - // drop unique: - $table->dropUnique('uid_name_unique'); - } - ); - - Schema::table( - 'bills', function (Blueprint $table) { - // raw query: - try { - DB::insert('ALTER TABLE `bills` CHANGE `name` `name` TEXT NOT NULL'); - } catch (PDOException $e) { - // don't care. - } - try { - DB::insert('ALTER TABLE `bills` CHANGE `match` `match` TEXT NOT NULL'); - } catch (PDOException $e) { - // don't care. - } - $table->smallInteger('name_encrypted', false, true)->default(0); - $table->smallInteger('match_encrypted', false, true)->default(0); - $table->foreign('user_id', 'bill_user_id')->references('id')->on('users')->onDelete('cascade'); - } - ); - - /** - * CATEGORIES - */ - Schema::table( - 'categories', function (Blueprint $table) { - $table->smallInteger('encrypted', false, true)->default(0); - - // drop foreign key: - $table->dropForeign('categories_user_id_foreign'); - - // drop unique: - $table->dropUnique('categories_user_id_name_unique'); - - } - ); - Schema::table( - 'categories', function (Blueprint $table) { - $table->text('name')->change(); - $table->foreign('user_id', 'category_user_id')->references('id')->on('users')->onDelete('cascade'); - } - ); - - /** - * PIGGY BANKS - */ - Schema::table( - 'piggy_banks', function (Blueprint $table) { - $table->smallInteger('encrypted', false, true)->default(0); - - // drop foreign: - $table->dropForeign('piggybanks_account_id_foreign'); - - // drop unique: - $table->dropUnique('piggybanks_account_id_name_unique'); - - } - ); - Schema::table( - 'piggy_banks', function (Blueprint $table) { - try { - DB::insert('ALTER TABLE `piggy_banks` CHANGE `name` `name` TEXT NOT NULL'); - } catch (PDOException $e) { - // don't care. - } - $table->dropColumn(['repeats', 'rep_length', 'rep_every', 'rep_times']); - - // create index again: - $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); - } - ); - - /** - * REMINDERS - */ - Schema::table( - 'reminders', function (Blueprint $table) { - $table->smallInteger('encrypted', false, true)->default(0); - - - } - ); - - } - -} diff --git a/database/migrations/2015_04_26_054507_changes_for_v3310.php b/database/migrations/2015_04_26_054507_changes_for_v3310.php deleted file mode 100644 index 2bd1c4f0ec..0000000000 --- a/database/migrations/2015_04_26_054507_changes_for_v3310.php +++ /dev/null @@ -1,85 +0,0 @@ -dropColumn('relation'); - } - ); - - /* - * New table! - */ - Schema::create( - 'tags', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('user_id')->unsigned(); - $table->string('tag', 1024); - $table->string('tagMode', 1024); - $table->date('date')->nullable(); - $table->text('description')->nullable(); - $table->decimal('latitude', 18, 12)->nullable(); - $table->decimal('longitude', 18, 12)->nullable(); - $table->smallInteger('zoomLevel', false, true)->nullable(); - - // connect tags to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - } - ); - - - Schema::create( - 'tag_transaction_journal', function (Blueprint $table) { - $table->increments('id'); - $table->integer('tag_id')->unsigned(); - $table->integer('transaction_journal_id')->unsigned(); - - // link to foreign tables. - $table->foreign('tag_id', 'tag_grp_id')->references('id')->on('tags')->onDelete('cascade'); - $table->foreign('transaction_journal_id', 'tag_trj_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - - // add unique. - $table->unique(['tag_id', 'transaction_journal_id'], 'tag_t_joined'); - - } - ); - } - -} diff --git a/database/migrations/2015_04_28_075215_changes_for_v3310a.php b/database/migrations/2015_04_28_075215_changes_for_v3310a.php deleted file mode 100644 index 81bdff7117..0000000000 --- a/database/migrations/2015_04_28_075215_changes_for_v3310a.php +++ /dev/null @@ -1,43 +0,0 @@ -string('relation', 50)->nullable(); - } - ); - // make new column "relation" - - } - -} diff --git a/database/migrations/2015_04_28_075317_changes_for_v3310b.php b/database/migrations/2015_04_28_075317_changes_for_v3310b.php deleted file mode 100644 index f950b19c21..0000000000 --- a/database/migrations/2015_04_28_075317_changes_for_v3310b.php +++ /dev/null @@ -1,35 +0,0 @@ -update(['relation' => 'balance']); - } - -} diff --git a/database/migrations/2015_05_22_172026_changes_for_v3409.php b/database/migrations/2015_05_22_172026_changes_for_v3409.php deleted file mode 100644 index 6fdbd1e607..0000000000 --- a/database/migrations/2015_05_22_172026_changes_for_v3409.php +++ /dev/null @@ -1,50 +0,0 @@ -dropColumn('name_encrypted'); - $table->dropColumn('data_encrypted'); - } - ); - } - - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - - // encrypt preference name (add field) - // encrypt preference data (add field) - Schema::table( - 'preferences', function (Blueprint $table) { - $table->text('name_encrypted')->nullable()->after('name'); - $table->text('data_encrypted')->nullable()->after('data'); - } - ); - - } - -} diff --git a/database/migrations/2015_05_28_041652_entrust_setup_tables.php b/database/migrations/2015_05_28_041652_entrust_setup_tables.php deleted file mode 100644 index ab63daedab..0000000000 --- a/database/migrations/2015_05_28_041652_entrust_setup_tables.php +++ /dev/null @@ -1,87 +0,0 @@ -increments('id'); - $table->string('name')->unique(); - $table->string('display_name')->nullable(); - $table->string('description')->nullable(); - $table->timestamps(); - } - ); - - // Create table for associating roles to users (Many-to-Many) - Schema::create( - 'role_user', function (Blueprint $table) { - $table->integer('user_id')->unsigned(); - $table->integer('role_id')->unsigned(); - - $table->foreign('user_id')->references('id')->on('users') - ->onUpdate('cascade')->onDelete('cascade'); - $table->foreign('role_id')->references('id')->on('roles') - ->onUpdate('cascade')->onDelete('cascade'); - - $table->primary(['user_id', 'role_id']); - } - ); - - // Create table for storing permissions - Schema::create( - 'permissions', function (Blueprint $table) { - $table->increments('id'); - $table->string('name')->unique(); - $table->string('display_name')->nullable(); - $table->string('description')->nullable(); - $table->timestamps(); - } - ); - - // Create table for associating permissions to roles (Many-to-Many) - Schema::create( - 'permission_role', function (Blueprint $table) { - $table->integer('permission_id')->unsigned(); - $table->integer('role_id')->unsigned(); - - $table->foreign('permission_id')->references('id')->on('permissions') - ->onUpdate('cascade')->onDelete('cascade'); - $table->foreign('role_id')->references('id')->on('roles') - ->onUpdate('cascade')->onDelete('cascade'); - - $table->primary(['permission_id', 'role_id']); - } - ); - } -} diff --git a/database/migrations/2015_06_14_093841_changes_for_v345.php b/database/migrations/2015_06_14_093841_changes_for_v345.php deleted file mode 100644 index bbcb205de1..0000000000 --- a/database/migrations/2015_06_14_093841_changes_for_v345.php +++ /dev/null @@ -1,44 +0,0 @@ -dropColumn('tag_count'); - } - ); - } - - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - // - Schema::table( - 'transaction_journals', function (Blueprint $table) { - $table->smallInteger('tag_count', false, true)->default(0); - } - ); - } -} diff --git a/database/migrations/2015_07_03_102450_changes_for_v3462.php b/database/migrations/2015_07_03_102450_changes_for_v3462.php deleted file mode 100644 index 3abeefe40e..0000000000 --- a/database/migrations/2015_07_03_102450_changes_for_v3462.php +++ /dev/null @@ -1,43 +0,0 @@ -dropColumn('iban'); - } - ); - } - - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - // add IBAN to accounts: - Schema::table( - 'accounts', function (Blueprint $table) { - $table->string('iban')->nullable(); - } - ); - } -} diff --git a/database/migrations/2015_07_14_204645_changes_for_v349.php b/database/migrations/2015_07_14_204645_changes_for_v349.php deleted file mode 100644 index 2f5c419438..0000000000 --- a/database/migrations/2015_07_14_204645_changes_for_v349.php +++ /dev/null @@ -1,38 +0,0 @@ -boolean('blocked')->default(0); - } - ); - } -} diff --git a/database/migrations/2015_07_17_190438_changes_for_v3410.php b/database/migrations/2015_07_17_190438_changes_for_v3410.php deleted file mode 100644 index dcaafee5cd..0000000000 --- a/database/migrations/2015_07_17_190438_changes_for_v3410.php +++ /dev/null @@ -1,60 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('attachable_id')->unsigned(); - $table->string('attachable_type'); - $table->integer('user_id')->unsigned(); - $table->string('md5', 32); - $table->text('filename'); - $table->text('title')->nullable(); - $table->text('description')->nullable(); - $table->text('notes')->nullable(); - $table->text('mime'); - $table->integer('size')->unsigned(); - $table->tinyInteger('uploaded', false, true)->default(0); - - } - ); - - // add "blocked_code" to users: - Schema::table( - 'users', function (Blueprint $table) { - $table->string('blocked_code', 25)->nullable(); - } - ); - } -} diff --git a/database/migrations/2016_01_11_193428_changes_for_v370.php b/database/migrations/2016_01_11_193428_changes_for_v370.php deleted file mode 100644 index e35dc60c15..0000000000 --- a/database/migrations/2016_01_11_193428_changes_for_v370.php +++ /dev/null @@ -1,135 +0,0 @@ -date('interest_date')->nullable()->after('date'); - $table->date('book_date')->nullable()->after('interest_date'); - } - ); - - - // new table "rule_groups" - Schema::create( - 'rule_groups', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('user_id')->unsigned(); - $table->unsignedSmallInteger('order'); - $table->string('title', 255); - $table->text('description')->nullable(); - $table->unsignedTinyInteger('active')->default(1); - - // connect rule groups to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - } - ); - - - Schema::create( - 'rules', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('user_id')->unsigned(); - $table->integer('rule_group_id')->unsigned(); - $table->unsignedSmallInteger('order'); - $table->unsignedTinyInteger('active')->default(1); - $table->unsignedTinyInteger('stop_processing')->default(0); - - $table->string('title', 255); - $table->text('description')->nullable(); - - - // connect rules to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - // connect rules to rule groups - $table->foreign('rule_group_id')->references('id')->on('rule_groups')->onDelete('cascade'); - - } - ); - - - // new table "rule_triggers" - Schema::create( - 'rule_triggers', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->integer('rule_id')->unsigned(); - $table->unsignedSmallInteger('order'); - $table->unsignedTinyInteger('active')->default(1); - $table->unsignedTinyInteger('stop_processing')->default(0); - - $table->string('trigger_type', 50); - $table->string('trigger_value', 255)->nullable(); - - // connect rule triggers to rules - $table->foreign('rule_id')->references('id')->on('rules')->onDelete('cascade'); - } - ); - - // new table "rule_actions" - Schema::create( - 'rule_actions', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->integer('rule_id')->unsigned(); - $table->unsignedSmallInteger('order'); - $table->unsignedTinyInteger('active')->default(1); - $table->unsignedTinyInteger('stop_processing')->default(0); - - $table->string('action_type', 50); - $table->string('action_value', 255)->nullable(); - - // connect rule actions to rules - $table->foreign('rule_id')->references('id')->on('rules')->onDelete('cascade'); - - } - ); - - } -} diff --git a/database/migrations/2016_02_04_144117_changes_for_v380.php b/database/migrations/2016_02_04_144117_changes_for_v380.php deleted file mode 100644 index f1aed9e55f..0000000000 --- a/database/migrations/2016_02_04_144117_changes_for_v380.php +++ /dev/null @@ -1,70 +0,0 @@ -date('process_date')->nullable()->after('book_date'); - } - ); - - // new table "export_jobs" - Schema::create( - 'export_jobs', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->integer('user_id')->unsigned(); - $table->string('key', 12)->unique(); - $table->string('status', 45); - - // connect rule groups to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - } - ); - - // new table for transaction journal meta, "journal_meta" - Schema::create( - 'journal_meta', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->integer('transaction_journal_id')->unsigned(); - $table->string('name'); - $table->text('data'); - - $table->unique(['transaction_journal_id', 'name']); - - // link to transaction journal - $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - } - ); - } -} diff --git a/database/migrations/2016_02_24_172426_create_jobs_table.php b/database/migrations/2016_02_24_172426_create_jobs_table.php deleted file mode 100644 index 34be24287c..0000000000 --- a/database/migrations/2016_02_24_172426_create_jobs_table.php +++ /dev/null @@ -1,42 +0,0 @@ -bigIncrements('id'); - $table->string('queue'); - $table->longText('payload'); - $table->tinyInteger('attempts')->unsigned(); - $table->tinyInteger('reserved')->unsigned(); - $table->unsignedInteger('reserved_at')->nullable(); - $table->unsignedInteger('available_at'); - $table->unsignedInteger('created_at'); - $table->index(['queue', 'reserved', 'reserved_at']); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('jobs'); - } -} diff --git a/database/migrations/2016_04_08_181054_changes_for_v383.php b/database/migrations/2016_04_08_181054_changes_for_v383.php deleted file mode 100644 index f550d2d18d..0000000000 --- a/database/migrations/2016_04_08_181054_changes_for_v383.php +++ /dev/null @@ -1,38 +0,0 @@ -string('hash', 64)->nullable(); - } - ); - } -} diff --git a/database/migrations/2016_04_25_093451_changes_for_v390.php b/database/migrations/2016_04_25_093451_changes_for_v390.php deleted file mode 100644 index 016573a4af..0000000000 --- a/database/migrations/2016_04_25_093451_changes_for_v390.php +++ /dev/null @@ -1,194 +0,0 @@ -unique(['budget_id', 'startdate', 'repeat_freq'], 'unique_bl_combi'); - } - ); - - - $backup = $this->backupRepeatFreqsFromString(); - - // drop string and create enum field - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->dropColumn('repeat_freq'); - } - ); - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->enum('repeat_freq', ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly']); - } - ); - - // restore backup. Change unknowns to "monthly". - $this->restoreRepeatFreqsToEnum($backup); - - // drop budget <> transaction table: - Schema::dropIfExists('budget_transaction'); - - // drop category <> transaction table: - Schema::dropIfExists('category_transaction'); - - } - - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - // // remove an index. - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->dropForeign('bid_foreign'); - } - ); - - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->dropUnique('unique_limit'); - $table->dropUnique('unique_bl_combi'); - } - ); - - - // recreate foreign key: - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->foreign('budget_id', 'bid_foreign')->references('id')->on('budgets')->onDelete('cascade'); - } - ); - - - // backup values - $backup = $this->backupRepeatFreqsFromEnum(); - - // drop enum and create varchar field - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->dropColumn('repeat_freq'); - } - ); - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->string('repeat_freq', 20)->default('monthly'); - } - ); - - // put data back: - $this->restoreRepeatFreqsToVarchar($backup); - - - // create it again, correctly. - Schema::table( - 'budget_limits', function (Blueprint $table) { - $table->unique(['budget_id', 'startdate', 'repeat_freq'], 'unique_limit'); - } - ); - - // create NEW table for transactions <> budgets - Schema::create( - 'budget_transaction', function (Blueprint $table) { - $table->increments('id'); - $table->integer('budget_id')->unsigned(); - $table->integer('transaction_id')->unsigned(); - $table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade'); - $table->foreign('transaction_id')->references('id')->on('transactions')->onDelete('cascade'); - $table->unique(['budget_id', 'transaction_id'], 'budid_tid_unique'); - } - ); - - // create NEW table for transactions <> categories - Schema::create( - 'category_transaction', function (Blueprint $table) { - $table->increments('id'); - $table->integer('category_id')->unsigned(); - $table->integer('transaction_id')->unsigned(); - $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade'); - $table->foreign('transaction_id')->references('id')->on('transactions')->onDelete('cascade'); - $table->unique(['category_id', 'transaction_id'], 'catid_tid_unique'); - } - ); - } - - /** - * @return array - */ - private function backupRepeatFreqsFromEnum(): array - { - $backup = []; - $set = BudgetLimit::get(); - /** @var BudgetLimit $entry */ - foreach ($set as $entry) { - $backup[$entry->id] = $entry->repeat_freq; - } - - return $backup; - } - - /** - * Same routine. - * - * @return array - */ - private function backupRepeatFreqsFromString() - { - return $this->backupRepeatFreqsFromEnum(); - } - - /** - * @param array $backup - * - * @return bool - */ - private function restoreRepeatFreqsToEnum(array $backup): bool - { - foreach ($backup as $id => $repeatFreq) { - $budgetLimit = BudgetLimit::find($id); - if (!in_array($repeatFreq, ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'])) { - $repeatFreq = 'monthly'; - } - $budgetLimit->repeat_freq = $repeatFreq; - $budgetLimit->save(); - } - - return true; - } - - /** - * @param array $backup - * - * @return bool - */ - private function restoreRepeatFreqsToVarchar(array $backup): bool - { - foreach ($backup as $id => $repeatFreq) { - $budgetLimit = BudgetLimit::find($id); - $budgetLimit->repeat_freq = $repeatFreq; - $budgetLimit->save(); - } - - return true; - } -} diff --git a/database/migrations/2016_06_16_192032_create_support_tables.php b/database/migrations/2016_06_16_192032_create_support_tables.php new file mode 100644 index 0000000000..bde6d8a07f --- /dev/null +++ b/database/migrations/2016_06_16_192032_create_support_tables.php @@ -0,0 +1,183 @@ +increments('id'); + $table->timestamps(); + $table->string('type', 50); + + // type must be unique. + $table->unique(['type']); + } + ); + } + /* + * transaction_currencies + */ + if (!Schema::hasTable('transaction_currencies')) { + Schema::create( + 'transaction_currencies', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->string('code', 3); + $table->string('name', 255); + $table->string('symbol', 12); + + // code must be unique. + $table->unique(['code']); + + }); + } + + /* + * transaction_types + */ + if (!Schema::hasTable('transaction_types')) { + Schema::create( + 'transaction_types', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->string('type', 50); + + // type must be unique. + $table->unique(['type']); + + }); + } + + /* + * jobs + */ + if (!Schema::hasTable('jobs')) { + Schema::create( + 'jobs', function (Blueprint $table) { + + // straight from Laravel + $table->bigIncrements('id'); + $table->string('queue'); + $table->longText('payload'); + $table->tinyInteger('attempts')->unsigned(); + $table->tinyInteger('reserved')->unsigned(); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + $table->index(['queue', 'reserved', 'reserved_at']); + + }); + } + + /* + * password_resets + */ + if (!Schema::hasTable('password_resets')) { + Schema::create( + 'password_resets', function (Blueprint $table) { + // straight from laravel + $table->string('email')->index(); + $table->string('token')->index(); + $table->timestamp('created_at'); + + }); + } + + /* + * permissions + */ + if (!Schema::hasTable('permissions')) { + Schema::create( + 'permissions', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->string('name')->unique(); + $table->string('display_name')->nullable(); + $table->string('description')->nullable(); + }); + } + + /* + * roles + */ + if (!Schema::hasTable('roles')) { + Schema::create( + 'roles', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->string('name')->unique(); + $table->string('display_name')->nullable(); + $table->string('description')->nullable(); + }); + } + + /* + * permission_role + */ + if (!Schema::hasTable('permission_role')) { + Schema::create( + 'permission_role', function (Blueprint $table) { + $table->integer('permission_id')->unsigned(); + $table->integer('role_id')->unsigned(); + $table->foreign('permission_id')->references('id')->on('permissions')->onUpdate('cascade')->onDelete('cascade'); + $table->foreign('role_id')->references('id')->on('roles')->onUpdate('cascade')->onDelete('cascade'); + + $table->primary(['permission_id', 'role_id']); + }); + } + + /* + * sessions + */ + if (!Schema::hasTable('sessions')) { + Schema::create( + 'sessions', function (Blueprint $table) { + $table->string('id')->unique(); + $table->integer('user_id')->nullable(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->text('payload'); + $table->integer('last_activity'); + }); + } + + } +} From 5dc8620c437f7433bc05030cc8f2c4f0a960002c Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 17 Jun 2016 14:06:38 +0200 Subject: [PATCH 15/94] More new migrations. --- ...016_06_16_192032_create_support_tables.php | 194 +++++--- .../2016_06_17_080435_create_users_table.php | 43 ++ .../2016_06_17_081759_create_main_tables.php | 427 ++++++++++++++++++ database/seeds/AccountTypeSeeder.php | 16 +- 4 files changed, 616 insertions(+), 64 deletions(-) create mode 100644 database/migrations/2016_06_17_080435_create_users_table.php create mode 100644 database/migrations/2016_06_17_081759_create_main_tables.php diff --git a/database/migrations/2016_06_16_192032_create_support_tables.php b/database/migrations/2016_06_16_192032_create_support_tables.php index bde6d8a07f..2e3fd98741 100644 --- a/database/migrations/2016_06_16_192032_create_support_tables.php +++ b/database/migrations/2016_06_16_192032_create_support_tables.php @@ -21,9 +21,9 @@ class CreateSupportTables extends Migration Schema::drop('transaction_types'); Schema::drop('jobs'); Schema::drop('password_resets'); + Schema::drop('permission_role'); Schema::drop('permissions'); Schema::drop('roles'); - Schema::drop('permission_role'); Schema::drop('sessions'); } @@ -38,6 +38,54 @@ class CreateSupportTables extends Migration /* * account_types */ + $this->createAccountTypeTable(); + /* + * transaction_currencies + */ + $this->createCurrencyTable(); + + /* + * transaction_types + */ + $this->createTransactionTypeTable(); + + /* + * jobs + */ + $this->createJobsTable(); + + /* + * password_resets + */ + $this->createPasswordTable(); + + /* + * permissions + */ + $this->createPermissionsTable(); + + /* + * roles + */ + $this->createRolesTable(); + + /* + * permission_role + */ + $this->createPermissionRoleTable(); + + /* + * sessions + */ + $this->createSessionsTable(); + + } + + /** + * + */ + private function createAccountTypeTable() + { if (!Schema::hasTable('account_types')) { Schema::create( 'account_types', function (Blueprint $table) { @@ -50,9 +98,13 @@ class CreateSupportTables extends Migration } ); } - /* - * transaction_currencies - */ + } + + /** + * + */ + private function createCurrencyTable() + { if (!Schema::hasTable('transaction_currencies')) { Schema::create( 'transaction_currencies', function (Blueprint $table) { @@ -66,29 +118,16 @@ class CreateSupportTables extends Migration // code must be unique. $table->unique(['code']); - }); + } + ); } + } - /* - * transaction_types - */ - if (!Schema::hasTable('transaction_types')) { - Schema::create( - 'transaction_types', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->string('type', 50); - - // type must be unique. - $table->unique(['type']); - - }); - } - - /* - * jobs - */ + /** + * + */ + private function createJobsTable() + { if (!Schema::hasTable('jobs')) { Schema::create( 'jobs', function (Blueprint $table) { @@ -104,12 +143,16 @@ class CreateSupportTables extends Migration $table->unsignedInteger('created_at'); $table->index(['queue', 'reserved', 'reserved_at']); - }); + } + ); } + } - /* - * password_resets - */ + /** + * + */ + private function createPasswordTable() + { if (!Schema::hasTable('password_resets')) { Schema::create( 'password_resets', function (Blueprint $table) { @@ -118,12 +161,36 @@ class CreateSupportTables extends Migration $table->string('token')->index(); $table->timestamp('created_at'); - }); + } + ); } + } - /* - * permissions - */ + /** + * + */ + private function createPermissionRoleTable() + { + if (!Schema::hasTable('permission_role')) { + Schema::create( + 'permission_role', function (Blueprint $table) { + $table->integer('permission_id')->unsigned(); + $table->integer('role_id')->unsigned(); + + $table->foreign('permission_id')->references('id')->on('permissions')->onUpdate('cascade')->onDelete('cascade'); + $table->foreign('role_id')->references('id')->on('roles')->onUpdate('cascade')->onDelete('cascade'); + + $table->primary(['permission_id', 'role_id']); + } + ); + } + } + + /** + * + */ + private function createPermissionsTable() + { if (!Schema::hasTable('permissions')) { Schema::create( 'permissions', function (Blueprint $table) { @@ -132,12 +199,16 @@ class CreateSupportTables extends Migration $table->string('name')->unique(); $table->string('display_name')->nullable(); $table->string('description')->nullable(); - }); + } + ); } + } - /* - * roles - */ + /** + * + */ + private function createRolesTable() + { if (!Schema::hasTable('roles')) { Schema::create( 'roles', function (Blueprint $table) { @@ -146,27 +217,17 @@ class CreateSupportTables extends Migration $table->string('name')->unique(); $table->string('display_name')->nullable(); $table->string('description')->nullable(); - }); + } + ); } + } - /* - * permission_role - */ - if (!Schema::hasTable('permission_role')) { - Schema::create( - 'permission_role', function (Blueprint $table) { - $table->integer('permission_id')->unsigned(); - $table->integer('role_id')->unsigned(); - $table->foreign('permission_id')->references('id')->on('permissions')->onUpdate('cascade')->onDelete('cascade'); - $table->foreign('role_id')->references('id')->on('roles')->onUpdate('cascade')->onDelete('cascade'); + /** + * + */ + private function createSessionsTable() + { - $table->primary(['permission_id', 'role_id']); - }); - } - - /* - * sessions - */ if (!Schema::hasTable('sessions')) { Schema::create( 'sessions', function (Blueprint $table) { @@ -176,8 +237,29 @@ class CreateSupportTables extends Migration $table->text('user_agent')->nullable(); $table->text('payload'); $table->integer('last_activity'); - }); + } + ); } + } + /** + * + */ + private function createTransactionTypeTable() + { + if (!Schema::hasTable('transaction_types')) { + Schema::create( + 'transaction_types', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->string('type', 50); + + // type must be unique. + $table->unique(['type']); + + } + ); + } } } diff --git a/database/migrations/2016_06_17_080435_create_users_table.php b/database/migrations/2016_06_17_080435_create_users_table.php new file mode 100644 index 0000000000..82e5af99fc --- /dev/null +++ b/database/migrations/2016_06_17_080435_create_users_table.php @@ -0,0 +1,43 @@ +increments('id'); + $table->timestamps(); + $table->string('email', 255); + $table->string('password', 60); + $table->string('remember_token', 100); + $table->string('reset', 32); + $table->tinyInteger('blocked', false, true)->default('0'); + $table->string('blocked_code', 25)->nullable(); + } + ); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('users'); + } +} diff --git a/database/migrations/2016_06_17_081759_create_main_tables.php b/database/migrations/2016_06_17_081759_create_main_tables.php new file mode 100644 index 0000000000..67ebd69f11 --- /dev/null +++ b/database/migrations/2016_06_17_081759_create_main_tables.php @@ -0,0 +1,427 @@ +createAccountTables(); + $this->createPiggyBanksTable(); + $this->createAttachmentsTable(); + $this->createBillsTable(); + $this->createBudgetTables(); + $this->createCategoriesTable(); + $this->createExportJobsTable(); + // $this->createImportJobsTable(); + $this->createPreferencesTable(); + $this->createRoleTable(); + $this->createRuleTables(); + // $this->createTagsTable(); + // $this->createTransactionTables(); + } + + /** + * Reverse the migrations. + */ + public function down() + { + // + Schema::drop('account_meta'); + Schema::drop('accounts'); + + Schema::drop('piggy_bank_repetitions'); + Schema::drop('piggy_banks'); + + Schema::drop('attachments'); + + Schema::drop('bills'); + + Schema::drop('limit_repetitions'); + Schema::drop('budget_limits'); + Schema::drop('budgets'); + + Schema::drop('categories'); + + Schema::drop('export_jobs'); + + Schema::drop('preferences'); + + Schema::drop('role_user'); + + Schema::drop('rule_actions'); + Schema::drop('rule_triggers'); + Schema::drop('rules'); + Schema::drop('rule_groups'); + + + } + + /** + * + */ + private function createAccountTables() + { + if (!Schema::hasTable('accounts')) { + Schema::create( + 'accounts', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->integer('account_type_id', false, true); + $table->string('name', 1024); + $table->decimal('virtual_balance', 10, 4); + $table->string('iban', 255); + + $table->tinyInteger('active', false, true)->default(1); + $table->tinyInteger('encrypted', false, true)->default(0); + + // link user id to users table + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + // link account type id to account types table + $table->foreign('account_type_id')->references('id')->on('account_types')->onDelete('cascade'); + } + ); + } + + if (!Schema::hasTable('account_meta')) { + Schema::create( + 'account_meta', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('account_id', false, true); + $table->string('name'); + $table->text('data'); + + // link account id to accounts: + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + } + ); + } + } + + private function createAttachmentsTable() + { + + if (!Schema::hasTable('attachments')) { + Schema::create( + 'attachments', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->integer('attachable_id', false, true); + $table->string('attachable_name', 255); + $table->string('md5', 32); + $table->string('filename', 1024); + $table->string('title', 1024); + $table->text('description'); + $table->text('notes'); + $table->string('mime', 200); + $table->integer('size', false, true); + $table->tinyInteger('uploaded', false, true)->default(1); + + // link user id to users table + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + + } + ); + } + } + + private function createBillsTable() + { + if (!Schema::hasTable('bills')) { + Schema::create( + 'bills', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->string('name', 1024); + $table->string('match', 1024); + $table->decimal('amount_min', 10, 4); + $table->decimal('amount_max', 10, 4); + $table->date('date'); + $table->string('repeat_freq', 30); + $table->smallInteger('skip', false, true)->default(0); + $table->tinyInteger('active', false, true)->default(1); + $table->tinyInteger('name_encrypted', false, true)->default(0); + $table->tinyInteger('match_encrypted', false, true)->default(0); + + // link user id to users table + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + } + ); + } + } + + private function createBudgetTables() + { + + + if (!Schema::hasTable('budgets')) { + Schema::create( + 'budgets', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->string('name', 1024); + $table->tinyInteger('active', false, true)->default(1); + $table->tinyInteger('encrypted', false, true)->default(0); + + // link user id to users table + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + + } + ); + } + if (!Schema::hasTable('budget_limits')) { + Schema::create( + 'budget_limits', function (Blueprint $table) { + + $table->increments('id'); + $table->timestamps(); + $table->integer('budget_id', false, true); + $table->date('startdate'); + $table->decimal('amount', 10, 4); + $table->string('repeat_freq', 30); + $table->tinyInteger('repeats', false, true)->default(0); + + // link budget id to budgets table + $table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade'); + + } + ); + } + if (!Schema::hasTable('limit_repetitions')) { + Schema::create( + 'limit_repetitions', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('budget_limit_id', false, true); + $table->date('startdate'); + $table->date('enddate'); + $table->decimal('amount', 10, 4); + + // link budget limit id to budget_limitss table + $table->foreign('budget_limit_id')->references('id')->on('budget_limits')->onDelete('cascade'); + } + ); + } + } + + private function createCategoriesTable() + { + if (!Schema::hasTable('categories')) { + Schema::create( + 'categories', function (Blueprint $table) { + + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->string('name', 1024); + $table->tinyInteger('encrypted', false, true)->default(0); + + // link user id to users table + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + } + ); + } + } + + private function createExportJobsTable() + { + if (!Schema::hasTable('export_jobs')) { + Schema::create( + 'export_jobs', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('user_id', false, true); + $table->string('key', 12); + $table->string('status', 255); + + // link user id to users table + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + } + ); + } + + + } + + private function createPiggyBanksTable() + { + if (!Schema::hasTable('piggy_banks')) { + Schema::create( + 'piggy_banks', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('account_id', false, true); + $table->string('name', 1024); + $table->decimal('targetamount', 10, 4); + $table->date('startdate'); + $table->date('targetdate'); + $table->integer('order', false, true); + $table->tinyInteger('active', false, true)->default(0); + + // link to account_id to accounts + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + } + ); + } + + if (!Schema::hasTable('piggy_bank_repetitions')) { + Schema::create( + 'piggy_bank_repetitions', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('piggy_bank_id', false, true); + $table->date('startdate'); + $table->date('targetdate'); + $table->decimal('currentamount', 10, 4); + + // link to account_id to accounts + $table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade'); + + } + ); + } + + } + + private function createPreferencesTable() + { + if (!Schema::hasTable('preferences')) { + Schema::create( + 'preferences', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('user_id', false, true); + $table->string('name', 1024); + $table->text('data'); + + // link user id to users table + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + } + ); + } + } + + private function createRoleTable() + { + + if (!Schema::hasTable('role_user')) { + Schema::create( + 'role_user', function (Blueprint $table) { + $table->integer('user_id', false, true); + $table->integer('role_id', false, true); + + $table->foreign('user_id')->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade'); + $table->foreign('role_id')->references('id')->on('roles')->onUpdate('cascade')->onDelete('cascade'); + + $table->primary(['user_id', 'role_id']); + + } + ); + } + + } + + private function createRuleTables() + { + if (!Schema::hasTable('rule_groups')) { + Schema::create( + 'rule_groups', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->string('title', 255); + $table->text('description'); + $table->integer('order', false, true); + $table->tinyInteger('active', false, true)->default(1); + + // link user id to users table + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + } + ); + } + if (!Schema::hasTable('rules')) { + Schema::create( + 'rules', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->integer('rule_group_id', false, true); + $table->string('title', 255); + $table->text('description'); + $table->integer('order', false, true); + $table->tinyInteger('active', false, true)->default(1); + $table->tinyInteger('stop_processing', false, true)->default(0); + + // link user id to users table + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + // link rule group id to rule group table + $table->foreign('rule_group_id')->references('id')->on('rule_groups')->onDelete('cascade'); + } + ); + } + if (!Schema::hasTable('rule_actions')) { + Schema::create( + 'rule_actions', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('rule_id', false, true); + + $table->string('action_type', 50); + $table->string('action_value', 255); + + $table->integer('order', false, true); + $table->tinyInteger('active', false, true)->default(1); + $table->tinyInteger('stop_processing', false, true)->default(0); + + + + // link rule id to rules table + $table->foreign('rule_id')->references('id')->on('rules')->onDelete('cascade'); + } + ); + } + if (!Schema::hasTable('rule_triggers')) { + Schema::create( + 'rule_triggers', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('rule_id', false, true); + + $table->string('trigger_type', 50); + $table->string('trigger_value', 255); + + $table->integer('order', false, true); + $table->tinyInteger('active', false, true)->default(1); + $table->tinyInteger('stop_processing', false, true)->default(0); + + + + // link rule id to rules table + $table->foreign('rule_id')->references('id')->on('rules')->onDelete('cascade'); + } + ); + } + } +} diff --git a/database/seeds/AccountTypeSeeder.php b/database/seeds/AccountTypeSeeder.php index 76e22d8c37..2ea3dab70e 100644 --- a/database/seeds/AccountTypeSeeder.php +++ b/database/seeds/AccountTypeSeeder.php @@ -13,14 +13,14 @@ class AccountTypeSeeder extends Seeder { DB::table('account_types')->delete(); - AccountType::create(['type' => 'Default account', 'editable' => true]); - AccountType::create(['type' => 'Cash account', 'editable' => false]); - AccountType::create(['type' => 'Asset account', 'editable' => true]); - AccountType::create(['type' => 'Expense account', 'editable' => true]); - AccountType::create(['type' => 'Revenue account', 'editable' => true]); - AccountType::create(['type' => 'Initial balance account', 'editable' => false]); - AccountType::create(['type' => 'Beneficiary account', 'editable' => true]); - AccountType::create(['type' => 'Import account', 'editable' => false]); + AccountType::create(['type' => 'Default account']); + AccountType::create(['type' => 'Cash account']); + AccountType::create(['type' => 'Asset account']); + AccountType::create(['type' => 'Expense account']); + AccountType::create(['type' => 'Revenue account']); + AccountType::create(['type' => 'Initial balance account']); + AccountType::create(['type' => 'Beneficiary account']); + AccountType::create(['type' => 'Import account']); } From 8ee1676f0a763f47b4d02a3b9ebaa2e0fd97e4c4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 18 Jun 2016 07:36:15 +0200 Subject: [PATCH 16/94] New migrations. --- app/Helpers/Csv/Importer.php | 1 + app/Http/Controllers/PiggyBankController.php | 4 - app/Models/PiggyBank.php | 4 +- .../PiggyBank/PiggyBankRepository.php | 3 - app/Support/Migration/TestData.php | 2 - .../2016_06_17_081759_create_main_tables.php | 307 ++++++++++++++---- 6 files changed, 248 insertions(+), 73 deletions(-) diff --git a/app/Helpers/Csv/Importer.php b/app/Helpers/Csv/Importer.php index 9e50f02a46..bdf1563709 100644 --- a/app/Helpers/Csv/Importer.php +++ b/app/Helpers/Csv/Importer.php @@ -151,6 +151,7 @@ class Importer $transactionType = $this->getTransactionType(); // defaults to deposit $errors = new MessageBag; + $journal = TransactionJournal::create( [ 'user_id' => Auth::user()->id, diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index b1a1b910b9..a7603d4bf2 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -355,8 +355,6 @@ class PiggyBankController extends Controller 'startdate' => new Carbon, 'account_id' => intval($request->get('account_id')), 'targetamount' => round($request->get('targetamount'), 2), - 'remind_me' => false, - 'reminder_skip' => 0, 'order' => $repository->getMaxOrder() + 1, 'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null, ]; @@ -391,8 +389,6 @@ class PiggyBankController extends Controller 'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate, 'account_id' => intval($request->get('account_id')), 'targetamount' => round($request->get('targetamount'), 2), - 'remind_me' => false, - 'reminder_skip' => 0, 'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null, ]; diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 78749a8e5e..6b96e32322 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -31,8 +31,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property \Carbon\Carbon $targetdate * @property integer $order * @property boolean $encrypted - * @property boolean $remind_me - * @property integer $reminder_skip * @property-read Account $account * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankRepetition[] $piggyBankRepetitions * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankEvent[] $piggyBankEvents @@ -59,7 +57,7 @@ class PiggyBank extends Model use SoftDeletes; protected $fillable - = ['name', 'account_id', 'order', 'targetamount', 'startdate', 'targetdate', 'remind_me', 'reminder_skip']; + = ['name', 'account_id', 'order', 'targetamount', 'startdate', 'targetdate']; protected $hidden = ['targetamount_encrypted', 'encrypted']; protected $dates = ['created_at', 'updated_at', 'deleted_at', 'startdate', 'targetdate']; diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index d0e9a90ce3..f1a693c0a6 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -171,9 +171,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function store(array $data): PiggyBank { - $data['remind_me'] = false; - $data['reminder_skip'] = 0; - $piggyBank = PiggyBank::create($data); return $piggyBank; diff --git a/app/Support/Migration/TestData.php b/app/Support/Migration/TestData.php index 63c65d4c1c..4b72873c21 100644 --- a/app/Support/Migration/TestData.php +++ b/app/Support/Migration/TestData.php @@ -646,8 +646,6 @@ class TestData 'name' => Crypt::encrypt($piggyBank['name']), 'targetamount' => $piggyBank['targetamount'], 'startdate' => $piggyBank['startdate'], - 'reminder_skip' => 0, - 'remind_me' => 0, 'order' => $piggyBank['order'], 'encrypted' => 1, ] diff --git a/database/migrations/2016_06_17_081759_create_main_tables.php b/database/migrations/2016_06_17_081759_create_main_tables.php index 67ebd69f11..4442e9101a 100644 --- a/database/migrations/2016_06_17_081759_create_main_tables.php +++ b/database/migrations/2016_06_17_081759_create_main_tables.php @@ -3,8 +3,46 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +/** + * Class CreateMainTables + */ class CreateMainTables extends Migration { + /** + * Reverse the migrations. + */ + public function down() + { + // + Schema::drop('account_meta'); + Schema::drop('piggy_bank_repetitions'); + Schema::drop('attachments'); + Schema::drop('limit_repetitions'); + Schema::drop('budget_limits'); + Schema::drop('export_jobs'); + Schema::drop('preferences'); + Schema::drop('role_user'); + Schema::drop('rule_actions'); + Schema::drop('rule_triggers'); + Schema::drop('rules'); + Schema::drop('rule_groups'); + Schema::drop('category_transaction'); + Schema::drop('budget_transaction'); + Schema::drop('transactions'); + Schema::drop('piggy_bank_events'); + Schema::drop('piggy_banks'); + Schema::drop('accounts'); + Schema::drop('category_transaction_journal'); + Schema::drop('budget_transaction_journal'); + Schema::drop('categories'); + Schema::drop('budgets'); + Schema::drop('tag_transaction_journal'); + Schema::drop('tags'); + Schema::drop('journal_meta'); + Schema::drop('transaction_journals'); + Schema::drop('bills'); + } + /** * Run the migrations. */ @@ -22,44 +60,8 @@ class CreateMainTables extends Migration $this->createPreferencesTable(); $this->createRoleTable(); $this->createRuleTables(); - // $this->createTagsTable(); - // $this->createTransactionTables(); - } - - /** - * Reverse the migrations. - */ - public function down() - { - // - Schema::drop('account_meta'); - Schema::drop('accounts'); - - Schema::drop('piggy_bank_repetitions'); - Schema::drop('piggy_banks'); - - Schema::drop('attachments'); - - Schema::drop('bills'); - - Schema::drop('limit_repetitions'); - Schema::drop('budget_limits'); - Schema::drop('budgets'); - - Schema::drop('categories'); - - Schema::drop('export_jobs'); - - Schema::drop('preferences'); - - Schema::drop('role_user'); - - Schema::drop('rule_actions'); - Schema::drop('rule_triggers'); - Schema::drop('rules'); - Schema::drop('rule_groups'); - - + $this->createTagsTable(); + $this->createTransactionTables(); } /** @@ -79,8 +81,8 @@ class CreateMainTables extends Migration $table->decimal('virtual_balance', 10, 4); $table->string('iban', 255); - $table->tinyInteger('active', false, true)->default(1); - $table->tinyInteger('encrypted', false, true)->default(0); + $table->boolean('active')->default(1); + $table->boolean('encrypted')->default(0); // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); @@ -118,7 +120,7 @@ class CreateMainTables extends Migration $table->softDeletes(); $table->integer('user_id', false, true); $table->integer('attachable_id', false, true); - $table->string('attachable_name', 255); + $table->string('attachable_type', 255); $table->string('md5', 32); $table->string('filename', 1024); $table->string('title', 1024); @@ -126,7 +128,7 @@ class CreateMainTables extends Migration $table->text('notes'); $table->string('mime', 200); $table->integer('size', false, true); - $table->tinyInteger('uploaded', false, true)->default(1); + $table->boolean('uploaded')->default(1); // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); @@ -153,9 +155,10 @@ class CreateMainTables extends Migration $table->date('date'); $table->string('repeat_freq', 30); $table->smallInteger('skip', false, true)->default(0); - $table->tinyInteger('active', false, true)->default(1); - $table->tinyInteger('name_encrypted', false, true)->default(0); - $table->tinyInteger('match_encrypted', false, true)->default(0); + $table->boolean('automatch')->default(1); + $table->boolean('active')->default(1); + $table->boolean('name_encrypted')->default(0); + $table->boolean('match_encrypted')->default(0); // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); @@ -176,8 +179,8 @@ class CreateMainTables extends Migration $table->softDeletes(); $table->integer('user_id', false, true); $table->string('name', 1024); - $table->tinyInteger('active', false, true)->default(1); - $table->tinyInteger('encrypted', false, true)->default(0); + $table->boolean('active')->default(1); + $table->boolean('encrypted')->default(0); // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); @@ -196,7 +199,7 @@ class CreateMainTables extends Migration $table->date('startdate'); $table->decimal('amount', 10, 4); $table->string('repeat_freq', 30); - $table->tinyInteger('repeats', false, true)->default(0); + $table->boolean('repeats')->default(0); // link budget id to budgets table $table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade'); @@ -232,7 +235,7 @@ class CreateMainTables extends Migration $table->softDeletes(); $table->integer('user_id', false, true); $table->string('name', 1024); - $table->tinyInteger('encrypted', false, true)->default(0); + $table->boolean('encrypted')->default(0); // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); @@ -275,7 +278,8 @@ class CreateMainTables extends Migration $table->date('startdate'); $table->date('targetdate'); $table->integer('order', false, true); - $table->tinyInteger('active', false, true)->default(0); + $table->boolean('active')->default(0); + $table->boolean('encrypted')->default(1); // link to account_id to accounts $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); @@ -293,7 +297,6 @@ class CreateMainTables extends Migration $table->date('targetdate'); $table->decimal('currentamount', 10, 4); - // link to account_id to accounts $table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade'); } @@ -313,7 +316,6 @@ class CreateMainTables extends Migration $table->string('name', 1024); $table->text('data'); - // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); } ); @@ -352,7 +354,7 @@ class CreateMainTables extends Migration $table->string('title', 255); $table->text('description'); $table->integer('order', false, true); - $table->tinyInteger('active', false, true)->default(1); + $table->boolean('active')->default(1); // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); @@ -370,8 +372,8 @@ class CreateMainTables extends Migration $table->string('title', 255); $table->text('description'); $table->integer('order', false, true); - $table->tinyInteger('active', false, true)->default(1); - $table->tinyInteger('stop_processing', false, true)->default(0); + $table->boolean('active')->default(1); + $table->boolean('stop_processing')->default(0); // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); @@ -392,9 +394,8 @@ class CreateMainTables extends Migration $table->string('action_value', 255); $table->integer('order', false, true); - $table->tinyInteger('active', false, true)->default(1); - $table->tinyInteger('stop_processing', false, true)->default(0); - + $table->boolean('active')->default(1); + $table->boolean('stop_processing')->default(0); // link rule id to rules table @@ -413,9 +414,8 @@ class CreateMainTables extends Migration $table->string('trigger_value', 255); $table->integer('order', false, true); - $table->tinyInteger('active', false, true)->default(1); - $table->tinyInteger('stop_processing', false, true)->default(0); - + $table->boolean('active')->default(1); + $table->boolean('stop_processing')->default(0); // link rule id to rules table @@ -424,4 +424,189 @@ class CreateMainTables extends Migration ); } } + + /** + * + */ + private function createTagsTable() + { + if (!Schema::hasTable('tags')) { + Schema::create( + 'tags', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + + $table->string('tag', 1024); + $table->string('tagMode', 1024); + $table->date('date'); + $table->text('description'); + $table->decimal('latitude', 18, 12); + $table->decimal('longitude', 18, 12); + $table->boolean('zoomLevel'); + + // link user id to users table + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + } + ); + } + } + + private function createTransactionTables() + { + + if (!Schema::hasTable('transaction_journals')) { + Schema::create( + 'transaction_journals', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + + $table->integer('user_id', false, true); + $table->integer('transaction_type_id', false, true); + $table->integer('bill_id', false, true)->nullable(); + $table->integer('transaction_currency_id', false, true); + + $table->string('description', 1024); + + $table->date('date'); + $table->date('interest_date')->nullable(); + $table->date('book_date')->nullable(); + $table->date('process_date')->nullable(); + + $table->integer('order', false, true); + $table->integer('tag_count', false, true); + + $table->boolean('encrypted')->default(1); + $table->boolean('completed')->default(1); + + // links to other tables: + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('transaction_type_id')->references('id')->on('transaction_types')->onDelete('cascade'); + $table->foreign('bill_id')->references('id')->on('bills')->onDelete('set null'); + $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade'); + } + ); + } + + if (!Schema::hasTable('journal_meta')) { + Schema::create( + 'journal_meta', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('transaction_journal_id', false, true); + $table->string('name', 255); + $table->text('data'); + $table->string('hash', 64); + + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); + } + ); + } + + if (!Schema::hasTable('tag_transaction_journal')) { + Schema::create( + 'tag_transaction_journal', function (Blueprint $table) { + $table->increments('id'); + $table->integer('tag_id', false, true); + $table->integer('transaction_journal_id', false, true); + + $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade'); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); + + + } + ); + } + + if (!Schema::hasTable('budget_transaction_journal')) { + Schema::create( + 'budget_transaction_journal', function (Blueprint $table) { + $table->increments('id'); + $table->integer('budget_id', false, true); + $table->integer('transaction_journal_id', false, true); + + $table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade'); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); + } + ); + } + + if (!Schema::hasTable('category_transaction_journal')) { + Schema::create( + 'category_transaction_journal', function (Blueprint $table) { + $table->increments('id'); + $table->integer('category_id', false, true); + $table->integer('transaction_journal_id', false, true); + + $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade'); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); + } + ); + } + + if (!Schema::hasTable('piggy_bank_events')) { + Schema::create( + 'piggy_bank_events', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('piggy_bank_id', false, true); + $table->integer('transaction_journal_id', false, true)->nullable(); + $table->date('date'); + $table->decimal('amount', 10, 4); + + $table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade'); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('set null'); + } + ); + } + + if (!Schema::hasTable('transactions')) { + Schema::create( + 'transactions', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('account_id', false, true); + $table->integer('transaction_journal_id', false, true); + $table->string('description', 255); + $table->decimal('amount', 10, 4); + + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); + + } + ); + } + + if (!Schema::hasTable('budget_transaction')) { + Schema::create( + 'budget_transaction', function (Blueprint $table) { + $table->increments('id'); + $table->integer('budget_id', false, true); + $table->integer('transaction_id', false, true); + + $table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade'); + $table->foreign('transaction_id')->references('id')->on('transactions')->onDelete('cascade'); + } + ); + } + + if (!Schema::hasTable('')) { + Schema::create( + 'category_transaction', function (Blueprint $table) { + $table->increments('id'); + $table->integer('category_id', false, true); + $table->integer('transaction_id', false, true); + + $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade'); + $table->foreign('transaction_id')->references('id')->on('transactions')->onDelete('cascade'); + } + ); + } + } + + } From 8331a7e34a9f014cec2c6dd06a068c489a4fbc6d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 18 Jun 2016 07:37:12 +0200 Subject: [PATCH 17/94] Rename stuff. --- ...ort_tables.php => 2016_06_16_000000_create_support_tables.php} | 0 ...e_users_table.php => 2016_06_16_000001_create_users_table.php} | 0 ...e_main_tables.php => 2016_06_16_000002_create_main_tables.php} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename database/migrations/{2016_06_16_192032_create_support_tables.php => 2016_06_16_000000_create_support_tables.php} (100%) rename database/migrations/{2016_06_17_080435_create_users_table.php => 2016_06_16_000001_create_users_table.php} (100%) rename database/migrations/{2016_06_17_081759_create_main_tables.php => 2016_06_16_000002_create_main_tables.php} (100%) diff --git a/database/migrations/2016_06_16_192032_create_support_tables.php b/database/migrations/2016_06_16_000000_create_support_tables.php similarity index 100% rename from database/migrations/2016_06_16_192032_create_support_tables.php rename to database/migrations/2016_06_16_000000_create_support_tables.php diff --git a/database/migrations/2016_06_17_080435_create_users_table.php b/database/migrations/2016_06_16_000001_create_users_table.php similarity index 100% rename from database/migrations/2016_06_17_080435_create_users_table.php rename to database/migrations/2016_06_16_000001_create_users_table.php diff --git a/database/migrations/2016_06_17_081759_create_main_tables.php b/database/migrations/2016_06_16_000002_create_main_tables.php similarity index 100% rename from database/migrations/2016_06_17_081759_create_main_tables.php rename to database/migrations/2016_06_16_000002_create_main_tables.php From 617a5c0606b0b7e4d87149528ed2121798b8103c Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 23 Jun 2016 08:01:15 +0200 Subject: [PATCH 18/94] Fix date range. --- app/Http/Middleware/Range.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 4673cbe39c..8470f1e38c 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -98,9 +98,9 @@ class Range $start = Session::get('start'); $end = Session::get('end'); $prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period - $prevEnd = Navigation::subtractPeriod($end, $viewRange); - $nextStart = Navigation::addPeriod($start, $viewRange, 0);// add for previous period - $nextEnd = Navigation::addPeriod($end, $viewRange, 0); + $prevEnd = Navigation::endOfPeriod($prevStart, $viewRange); + $nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period + $nextEnd = Navigation::endOfPeriod($nextStart, $viewRange); $ranges = []; $ranges['current'] = [$start->format('Y-m-d'), $end->format('Y-m-d')]; $ranges['previous'] = [$prevStart->format('Y-m-d'), $prevEnd->format('Y-m-d')]; From 2c826451d1e93ed0712209423de8fdff52aec4cf Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 23 Jun 2016 12:07:31 +0200 Subject: [PATCH 19/94] Fix delete routine and some NULLs --- app/Crud/Account/AccountCrud.php | 5 ++++- app/Http/Controllers/AccountController.php | 2 +- app/Providers/EventServiceProvider.php | 22 +++++++++++++++++-- .../Journal/JournalRepository.php | 5 ----- .../2016_06_16_000002_create_main_tables.php | 10 ++++----- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/app/Crud/Account/AccountCrud.php b/app/Crud/Account/AccountCrud.php index 27de351833..c7ffb509df 100644 --- a/app/Crud/Account/AccountCrud.php +++ b/app/Crud/Account/AccountCrud.php @@ -58,8 +58,11 @@ class AccountCrud implements AccountCrudInterface // update all transactions: DB::table('transactions')->where('account_id', $account->id)->update(['account_id' => $moveTo->id]); } + if (!is_null($account)) { + Log::debug('Now trigger account delete #' . $account->id); + $account->delete(); + } - $account->delete(); return true; } diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 734a5008a1..28a8aa3e25 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -103,7 +103,7 @@ class AccountController extends Controller $typeName = config('firefly.shortNamesByFullName.' . $type); $name = $account->name; $moveTo = $crud->find(intval(Input::get('move_account_before_delete'))); - + $crud->destroy($account, $moveTo); Session::flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name]))); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 2b91f62b09..dc6e71565e 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -15,8 +15,10 @@ use FireflyIII\Models\Account; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionJournal; use Illuminate\Contracts\Events\Dispatcher as DispatcherContract; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; +use Log; /** * Class EventServiceProvider @@ -107,11 +109,27 @@ class EventServiceProvider extends ServiceProvider { Account::deleted( function (Account $account) { - + Log::debug('Now trigger account delete response #' . $account->id); /** @var Transaction $transaction */ foreach ($account->transactions()->get() as $transaction) { + Log::debug('Now at transaction #' . $transaction->id); $journal = $transaction->transactionJournal()->first(); - $journal->delete(); + if (!is_null($journal)) { + Log::debug('Call for deletion of journal #' . $journal->id); + $journal->delete(); + } + } + } + ); + + TransactionJournal::deleted( + function (TransactionJournal $journal) { + Log::debug('Now triggered journal delete response #' . $journal->id); + + /** @var Transaction $transaction */ + foreach ($journal->transactions()->get() as $transaction) { + Log::debug('Will now delete transaction #' . $transaction->id); + $transaction->delete(); } } ); diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 51c1ebd969..a42e0cffcd 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -96,11 +96,6 @@ class JournalRepository implements JournalRepositoryInterface */ public function delete(TransactionJournal $journal): bool { - /** @var Transaction $transaction */ - foreach ($journal->transactions()->get() as $transaction) { - $transaction->delete(); - } - $journal->delete(); return true; diff --git a/database/migrations/2016_06_16_000002_create_main_tables.php b/database/migrations/2016_06_16_000002_create_main_tables.php index 4442e9101a..720c2c220d 100644 --- a/database/migrations/2016_06_16_000002_create_main_tables.php +++ b/database/migrations/2016_06_16_000002_create_main_tables.php @@ -440,11 +440,11 @@ class CreateMainTables extends Migration $table->string('tag', 1024); $table->string('tagMode', 1024); - $table->date('date'); - $table->text('description'); - $table->decimal('latitude', 18, 12); - $table->decimal('longitude', 18, 12); - $table->boolean('zoomLevel'); + $table->date('date')->nullable(); + $table->text('description')->nullable(); + $table->decimal('latitude', 18, 12)->nullable(); + $table->decimal('longitude', 18, 12)->nullable(); + $table->boolean('zoomLevel')->nullable(); // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); From fbf9e00208ea44e4007a24cf960f23dcdc106b59 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 23 Jun 2016 12:08:14 +0200 Subject: [PATCH 20/94] Change preferences info. --- app/Models/Preference.php | 10 ++-------- app/Support/Preferences.php | 4 ++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/app/Models/Preference.php b/app/Models/Preference.php index 33d7e22c33..44e5951cbf 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -24,7 +24,6 @@ use Illuminate\Database\Eloquent\Model; * @property string $name * @property string $name_encrypted * @property string $data - * @property string $data_encrypted * @property-read \FireflyIII\User $user * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Preference whereId($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Preference whereCreatedAt($value) @@ -41,7 +40,6 @@ class Preference extends Model protected $dates = ['created_at', 'updated_at']; protected $fillable = ['user_id', 'data', 'name']; - protected $hidden = ['data_encrypted', 'name_encrypted']; /** * @param $value @@ -50,10 +48,7 @@ class Preference extends Model */ public function getDataAttribute($value) { - if (is_null($this->data_encrypted)) { - return json_decode($value); - } - $data = Crypt::decrypt($this->data_encrypted); + $data = Crypt::decrypt($value); return json_decode($data); } @@ -63,8 +58,7 @@ class Preference extends Model */ public function setDataAttribute($value) { - $this->attributes['data'] = ''; - $this->attributes['data_encrypted'] = Crypt::encrypt(json_encode($value)); + $this->attributes['data'] = Crypt::encrypt(json_encode($value)); } /** diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 717c7d364b..96d935bc85 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -69,7 +69,7 @@ class Preferences return Cache::get($fullName); } - $preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data_encrypted']); + $preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data']); if ($preference) { Cache::forever($fullName, $preference); @@ -138,7 +138,7 @@ class Preferences { $fullName = 'preference' . $user->id . $name; Cache::forget($fullName); - $pref = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data_encrypted']); + $pref = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data']); if (!is_null($pref)) { $pref->data = $value; From 3d201db6fc693d5d1fb76932fb5c9b42061cf735 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 24 Jun 2016 14:24:34 +0200 Subject: [PATCH 21/94] More code for import. --- app/Http/Controllers/ImportController.php | 144 +++++++++++++++++- app/Http/routes.php | 9 +- app/Import/Importer/CsvImporter.php | 46 ++++++ app/Import/Importer/ImporterInterface.php | 7 + app/Import/Specifics/AbnAmroDescription.php | 37 +++++ app/Import/Specifics/RabobankDescription.php | 36 +++++ app/Import/Specifics/SpecificInterface.php | 31 ++++ app/Models/Bill.php | 2 + app/Models/Budget.php | 1 + app/Models/ImportJob.php | 43 ++++-- app/Models/PiggyBank.php | 2 + app/User.php | 2 + config/firefly.php | 8 +- .../2016_05_23_173524_changes_for_v391.php | 44 ------ .../2016_06_16_000002_create_main_tables.php | 50 +++++- resources/views/import/csv/configure.twig | 34 +++-- 16 files changed, 416 insertions(+), 80 deletions(-) create mode 100644 app/Import/Specifics/AbnAmroDescription.php create mode 100644 app/Import/Specifics/RabobankDescription.php create mode 100644 app/Import/Specifics/SpecificInterface.php delete mode 100644 database/migrations/2016_05_23_173524_changes_for_v391.php diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 45524c1714..4bd6173903 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -3,11 +3,13 @@ namespace FireflyIII\Http\Controllers; use Crypt; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Requests; use FireflyIII\Http\Requests\ImportUploadRequest; use FireflyIII\Import\Importer\ImporterInterface; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use Illuminate\Http\Request; use SplFileObject; use Storage; use View; @@ -30,26 +32,34 @@ class ImportController extends Controller } /** + * This is step 3. + * This is the first step in configuring the job. It can only be executed + * when the job is set to "import_status_never_started". + * * @param ImportJob $job * * @return View + * @throws FireflyException */ public function configure(ImportJob $job) { - // create proper importer (depends on job) - $type = $job->file_type; - /** @var ImporterInterface $importer */ - $importer = app('FireflyIII\Import\Importer\\' . ucfirst($type) . 'Importer'); - $importer->setJob($job); + if (!$this->jobInCorrectStep($job, 'configure')) { + return $this->redirectToCorrectStep($job); + } + + // actual code + $importer = $this->makeImporter($job); $importer->configure(); $data = $importer->getConfigurationData(); - return view('import.' . $type . '.configure', compact('data', 'job')); + return view('import.' . $job->file_type . '.configure', compact('data', 'job')); } /** + * This is step 1. Upload a file. + * * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function index() @@ -67,6 +77,68 @@ class ImportController extends Controller } /** + * Step 4. Save the configuration. + * + * @param Request $request + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + public function process(Request $request, ImportJob $job) + { + if (!$this->jobInCorrectStep($job, 'process')) { + return $this->redirectToCorrectStep($job); + } + + // actual code + $importer = $this->makeImporter($job); + $data = $request->all(); + $importer->saveImportConfiguration($data); + + // update job: + $job->status = 'import_configuration_saved'; + $job->save(); + + // return redirect to settings. + // this could loop until the user is done. + return redirect(route('import.settings', $job->key)); + } + + /** + * Step 5. Depending on the importer, this will show the user settings to + * fill in. + * + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + public function settings(ImportJob $job) + { + if (!$this->jobInCorrectStep($job, 'settings')) { + return $this->redirectToCorrectStep($job); + } + $importer = $this->makeImporter($job); + + // now + + + + echo 'now in settings'; + exit; + + // actual code + + + // ask the importer for the requested action. + // for example pick columns or map data. + // depends of course on the data in the job. + } + + /** + * This is step 2. It creates an Import Job. Stores the import. + * * @param ImportUploadRequest $request * @param ImportJobRepositoryInterface $repository * @@ -88,4 +160,64 @@ class ImportController extends Controller return redirect(route('import.configure', [$job->key])); } + + /** + * @param ImportJob $job + * @param string $method + * + * @return bool + */ + private function jobInCorrectStep(ImportJob $job, string $method): bool + { + switch ($method) { + case 'configure': + case 'process': + return $job->status === 'import_status_never_started'; + break; + case 'settings': + return $job->status === 'import_configuration_saved'; + break; + } + + return false; + + } + + /** + * @param ImportJob $job + * + * @return ImporterInterface + */ + private function makeImporter(ImportJob $job): ImporterInterface + { + // create proper importer (depends on job) + $type = $job->file_type; + /** @var ImporterInterface $importer */ + $importer = app('FireflyIII\Import\Importer\\' . ucfirst($type) . 'Importer'); + $importer->setJob($job); + + return $importer; + + } + + /** + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + private function redirectToCorrectStep(ImportJob $job) + { + switch ($job->status) { + case 'import_status_never_started': + return redirect(route('import.configure', [$job->key])); + break; + case 'import_configuration_saved': + return redirect(route('import.settings', [$job->key])); + break; + } + + throw new FireflyException('Cannot redirect for job state ' . $job->status); + + } } diff --git a/app/Http/routes.php b/app/Http/routes.php index 27e95d11ff..29f99c3f4f 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -223,9 +223,12 @@ Route::group( /** * IMPORT CONTROLLER */ - Route::get('/import', ['uses' => 'ImportController@index','as' => 'import.index']); - Route::post('/import/upload', ['uses' => 'ImportController@upload','as' => 'import.upload']); - Route::get('/import/configure/{importJob}', ['uses' => 'ImportController@configure','as' => 'import.configure']); + Route::get('/import', ['uses' => 'ImportController@index', 'as' => 'import.index']); + Route::post('/import/upload', ['uses' => 'ImportController@upload', 'as' => 'import.upload']); + Route::get('/import/configure/{importJob}', ['uses' => 'ImportController@configure', 'as' => 'import.configure']); + Route::post('/import/process/{importJob}', ['uses' => 'ImportController@process', 'as' => 'import.process_configuration']); + Route::get('/import/settings/{importJob}', ['uses' => 'ImportController@settings', 'as' => 'import.settings']); + /** * Help Controller diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index abd18cb913..82b83e1dc8 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -13,6 +13,7 @@ namespace FireflyIII\Import\Importer; use ExpandedForm; +use FireflyIII\Crud\Account\AccountCrud; use FireflyIII\Import\Role\Map; use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; @@ -50,12 +51,23 @@ class CsvImporter implements ImporterInterface 'tab' => trans('form.csv_tab'), ]; + $specifics = []; + + // collect specifics. + foreach (config('firefly.csv_import_specifics') as $name => $className) { + $specifics[$name] = [ + 'name' => $className::getName(), + 'description' => $className::getDescription(), + ]; + } + $data = [ 'accounts' => ExpandedForm::makeSelectList($accounts), 'specifix' => [], 'delimiters' => $delimiters, 'upload_path' => storage_path('upload'), 'is_upload_possible' => is_writable(storage_path('upload')), + 'specifics' => $specifics, ]; return $data; @@ -73,6 +85,40 @@ class CsvImporter implements ImporterInterface exit; } + /** + * @param array $data + * + * @return bool + */ + public function saveImportConfiguration(array $data): bool + { + /** @var AccountCrud $repository */ + $repository = app(AccountCrud::class); + $account = $repository->find(intval($data['csv_import_account'])); + $configuration = [ + 'date_format' => $data['date_format'], + 'csv_delimiter' => $data['csv_delimiter'], + 'csv_import_account' => 0, + 'specifics' => [], + ]; + + if (!is_null($account->id)) { + $configuration['csv_import_account'] = $account->id; + } + // loop specifics. + if (is_array($data['specifics'])) { + foreach ($data['specifics'] as $name => $enabled) { + $configuration['specifics'][] = $name; + } + } + $this->job->configuration = $configuration; + $this->job->save(); + + return true; + + + } + /** * @param ImportJob $job */ diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php index 4df74f85ba..8116f252a7 100644 --- a/app/Import/Importer/ImporterInterface.php +++ b/app/Import/Importer/ImporterInterface.php @@ -36,6 +36,13 @@ interface ImporterInterface */ public function getConfigurationData(): array; + /** + * @param array $data + * + * @return bool + */ + public function saveImportConfiguration(array $data): bool; + /** * Returns a Map thing used to allow the user to * define roles for each entry. diff --git a/app/Import/Specifics/AbnAmroDescription.php b/app/Import/Specifics/AbnAmroDescription.php new file mode 100644 index 0000000000..a3d3f058fa --- /dev/null +++ b/app/Import/Specifics/AbnAmroDescription.php @@ -0,0 +1,37 @@ +belongsTo('FireflyIII\User'); } + + + /** + * @param $value + * + * @return mixed + */ + public function getConfigurationAttribute($value) + { + if (strlen($value) == 0) { + return []; + } + + return json_decode($value); + } + + /** + * @param $value + */ + public function setConfigurationAttribute($value) + { + $this->attributes['configuration'] = json_encode($value); + } } diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 6b96e32322..355d4a135c 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -51,6 +51,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereDeletedAt($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereEncrypted($value) * @mixin \Eloquent + * @property boolean $active + * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereActive($value) */ class PiggyBank extends Model { diff --git a/app/User.php b/app/User.php index fb5305f80f..0c51395c41 100644 --- a/app/User.php +++ b/app/User.php @@ -55,6 +55,8 @@ use Illuminate\Foundation\Auth\User as Authenticatable; * @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereBlockedCode($value) * @mixin \Eloquent * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\ImportJob[] $importjobs + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\PiggyBank[] $piggyBanks + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions */ class User extends Authenticatable { diff --git a/config/firefly.php b/config/firefly.php index e19fd5d75b..0e93925850 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -11,11 +11,15 @@ return [ 'resend_confirmation' => 3600, 'confirmation_age' => 14400, // four hours - 'export_formats' => [ + 'export_formats' => [ 'csv' => 'FireflyIII\Export\Exporter\CsvExporter', // mt940 FireflyIII Export Exporter MtExporter ], - 'import_formats' => [ + 'csv_import_specifics' => [ + 'RabobankDescription' => 'FireflyIII\Import\Specifics\RabobankDescription', + 'AbnAmroDescription' => 'FireflyIII\Import\Specifics\AbnAmroDescription', + ], + 'import_formats' => [ 'csv' => 'FireflyIII\Import\Importer\CsvImporter', // mt940 FireflyIII Import Importer MtImporter ], diff --git a/database/migrations/2016_05_23_173524_changes_for_v391.php b/database/migrations/2016_05_23_173524_changes_for_v391.php deleted file mode 100644 index b8efd50b4d..0000000000 --- a/database/migrations/2016_05_23_173524_changes_for_v391.php +++ /dev/null @@ -1,44 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('user_id')->unsigned(); - $table->string('key', 12)->unique(); - $table->string('file_type', 12); - $table->string('status', 45); - - // connect rule groups to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - } - ); - } -} diff --git a/database/migrations/2016_06_16_000002_create_main_tables.php b/database/migrations/2016_06_16_000002_create_main_tables.php index 720c2c220d..b46f633232 100644 --- a/database/migrations/2016_06_16_000002_create_main_tables.php +++ b/database/migrations/2016_06_16_000002_create_main_tables.php @@ -13,13 +13,13 @@ class CreateMainTables extends Migration */ public function down() { - // Schema::drop('account_meta'); Schema::drop('piggy_bank_repetitions'); Schema::drop('attachments'); Schema::drop('limit_repetitions'); Schema::drop('budget_limits'); Schema::drop('export_jobs'); + Schema::drop('import_jobs'); Schema::drop('preferences'); Schema::drop('role_user'); Schema::drop('rule_actions'); @@ -109,6 +109,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createAttachmentsTable() { @@ -139,6 +142,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createBillsTable() { if (!Schema::hasTable('bills')) { @@ -167,6 +173,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createBudgetTables() { @@ -224,6 +233,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createCategoriesTable() { if (!Schema::hasTable('categories')) { @@ -244,6 +256,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createExportJobsTable() { if (!Schema::hasTable('export_jobs')) { @@ -254,16 +269,31 @@ class CreateMainTables extends Migration $table->integer('user_id', false, true); $table->string('key', 12); $table->string('status', 255); - - // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); } ); } + if (!Schema::hasTable('import_jobs')) { + Schema::create( + 'import_jobs', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('user_id')->unsigned(); + $table->string('key', 12)->unique(); + $table->string('file_type', 12); + $table->string('status', 45); + $table->text('configuration'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + } + ); + } } + /** + * + */ private function createPiggyBanksTable() { if (!Schema::hasTable('piggy_banks')) { @@ -305,6 +335,9 @@ class CreateMainTables extends Migration } + /** + * + */ private function createPreferencesTable() { if (!Schema::hasTable('preferences')) { @@ -322,6 +355,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createRoleTable() { @@ -342,6 +378,9 @@ class CreateMainTables extends Migration } + /** + * + */ private function createRuleTables() { if (!Schema::hasTable('rule_groups')) { @@ -454,6 +493,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createTransactionTables() { @@ -607,6 +649,4 @@ class CreateMainTables extends Migration ); } } - - } diff --git a/resources/views/import/csv/configure.twig b/resources/views/import/csv/configure.twig index d9645cd7d1..64adea984a 100644 --- a/resources/views/import/csv/configure.twig +++ b/resources/views/import/csv/configure.twig @@ -22,7 +22,7 @@ -
    +
    @@ -41,9 +41,21 @@ {{ ExpandedForm.select('csv_import_account', data.accounts, 0, {helpText: 'csv_import_account_help'|_} ) }} - {{ ExpandedForm.multiCheckbox('specifix', data.specifix) }} - + {% for type, specific in data.specifics %} +
    + +
    +
    +
    +
    +
    + {% endfor %} {% if not data.is_upload_possible %}
    @@ -65,17 +77,17 @@
    {% if data.is_upload_possible %} -
    -
    -
    -
    - +
    +
    +
    +
    + +
    -
    {% endif %} From 93a54780ab403179ed35f44536196f3388dfbd82 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 24 Jun 2016 21:58:57 +0200 Subject: [PATCH 22/94] Fixes a bug in the 2FA activation thing. --- app/Validation/FireflyValidator.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 76817224bc..636aea80ba 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -66,10 +66,8 @@ class FireflyValidator extends Validator } $secret = Session::get('two-factor-secret'); - /** @var Google2FA $google2fa */ - $google2fa = app(Google2FA::class); - return $google2fa->verifyKey($secret, $value); + return Google2FA::verifyKey($secret, $value); } /** From 18d274181407246ccf25d9def481e4e35face8e8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 27 Jun 2016 15:15:46 +0200 Subject: [PATCH 23/94] More code for the new CSV import --- app/Http/Controllers/ImportController.php | 18 +- app/Http/routes.php | 3 +- app/Import/Importer/CsvImporter.php | 93 +++++++++- app/Import/Importer/ImporterInterface.php | 30 +++- app/Models/ImportJob.php | 36 ++-- config/csv.php | 203 ++++++++++++++++++++++ config/firefly.php | 18 +- resources/lang/en_US/csv.php | 44 +++++ resources/views/import/csv/map.twig | 88 ++++++++++ 9 files changed, 499 insertions(+), 34 deletions(-) create mode 100644 resources/lang/en_US/csv.php create mode 100644 resources/views/import/csv/map.twig diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 4bd6173903..3416a5a6fd 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -85,7 +85,7 @@ class ImportController extends Controller * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @throws FireflyException */ - public function process(Request $request, ImportJob $job) + public function postConfigure(Request $request, ImportJob $job) { if (!$this->jobInCorrectStep($job, 'process')) { return $this->redirectToCorrectStep($job); @@ -94,7 +94,8 @@ class ImportController extends Controller // actual code $importer = $this->makeImporter($job); $data = $request->all(); - $importer->saveImportConfiguration($data); + $files = $request->files; + $importer->saveImportConfiguration($data, $files); // update job: $job->status = 'import_configuration_saved'; @@ -121,11 +122,18 @@ class ImportController extends Controller } $importer = $this->makeImporter($job); - // now + // now show settings screen to user. + if ($importer->requireUserSettings()) { + $data = $importer->getDataForSettings(); + $view = $importer->getViewForSettings(); + + return view($view, compact('data', 'job')); + } + + // if no more settings, save job and continue to process thing. - - echo 'now in settings'; + echo 'now in settings (done)'; exit; // actual code diff --git a/app/Http/routes.php b/app/Http/routes.php index 29f99c3f4f..662fafce7e 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -226,8 +226,9 @@ Route::group( Route::get('/import', ['uses' => 'ImportController@index', 'as' => 'import.index']); Route::post('/import/upload', ['uses' => 'ImportController@upload', 'as' => 'import.upload']); Route::get('/import/configure/{importJob}', ['uses' => 'ImportController@configure', 'as' => 'import.configure']); - Route::post('/import/process/{importJob}', ['uses' => 'ImportController@process', 'as' => 'import.process_configuration']); + Route::post('/import/configure/{importJob}', ['uses' => 'ImportController@postConfigure', 'as' => 'import.process_configuration']); Route::get('/import/settings/{importJob}', ['uses' => 'ImportController@settings', 'as' => 'import.settings']); + Route::post('/import/settings/{importJob}', ['uses' => 'ImportController@postSettings', 'as' => 'import.postSettings']); /** diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 82b83e1dc8..ece4c9e527 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -17,6 +17,8 @@ use FireflyIII\Crud\Account\AccountCrud; use FireflyIII\Import\Role\Map; use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; +use League\Csv\Reader; +use Symfony\Component\HttpFoundation\FileBag; /** * Class CsvImporter @@ -25,6 +27,7 @@ use FireflyIII\Models\ImportJob; */ class CsvImporter implements ImporterInterface { + const EXAMPLE_ROWS = 5; /** @var ImportJob */ public $job; @@ -54,7 +57,7 @@ class CsvImporter implements ImporterInterface $specifics = []; // collect specifics. - foreach (config('firefly.csv_import_specifics') as $name => $className) { + foreach (config('csv.import_specifics') as $name => $className) { $specifics[$name] = [ 'name' => $className::getName(), 'description' => $className::getDescription(), @@ -73,6 +76,68 @@ class CsvImporter implements ImporterInterface return $data; } + /** + * This method returns the data required for the view that will let the user add settings to the import job. + * + * @return array + */ + public function getDataForSettings(): array + { + $config = $this->job->configuration; + $data = [ + 'columns' => [], + 'columnCount' => 0, + ]; + + if (!isset($config['columns'])) { + + // show user column configuration. + $content = $this->job->uploadFileContents(); + + // create CSV reader. + $reader = Reader::createFromString($content); + $start = $config['has_headers'] ? 1 : 0; + $end = $start + self::EXAMPLE_ROWS; // first X rows + while ($start < $end) { + $row = $reader->fetchOne($start); + foreach ($row as $index => $value) { + $value = trim($value); + if (strlen($value) > 0) { + $data['columns'][$index][] = $value; + } + } + $start++; + $data['columnCount'] = count($row); + } + + // make unique + foreach ($data['columns'] as $index => $values) { + $data['columns'][$index] = array_unique($values); + } + // TODO preset roles from config + $data['set_roles'] = []; + // collect possible column roles: + $data['available_roles'] = []; + foreach (array_keys(config('csv.import_roles')) as $role) { + $data['available_roles'][$role] = trans('csv.csv_column_'.$role); + } + + return $data; + } + + } + + /** + * This method returns the name of the view that will be shown to the user to further configure + * the import job. + * + * @return string + */ + public function getViewForSettings(): string + { + return 'import.csv.map'; + } + /** * Returns a Map thing used to allow the user to * define roles for each entry. @@ -85,21 +150,45 @@ class CsvImporter implements ImporterInterface exit; } + /** + * This method returns whether or not the user must configure this import + * job further. + * + * @return bool + */ + public function requireUserSettings(): bool + { + // does the job have both a 'map' array and a 'columns' array. + $config = $this->job->configuration; + if (isset($config['map']) && isset($config['columns'])) { + return false; + } + + return true; + } + /** * @param array $data * * @return bool */ - public function saveImportConfiguration(array $data): bool + public function saveImportConfiguration(array $data, FileBag $files): bool { + /* + * TODO file upload is ignored for now. + */ + /** @var AccountCrud $repository */ $repository = app(AccountCrud::class); $account = $repository->find(intval($data['csv_import_account'])); + $hasHeaders = isset($data['has_headers']) && intval($data['has_headers']) === 1 ? true : false; $configuration = [ + 'has_headers' => $hasHeaders, 'date_format' => $data['date_format'], 'csv_delimiter' => $data['csv_delimiter'], 'csv_import_account' => 0, 'specifics' => [], + ]; if (!is_null($account->id)) { diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php index 8116f252a7..45135fc26b 100644 --- a/app/Import/Importer/ImporterInterface.php +++ b/app/Import/Importer/ImporterInterface.php @@ -13,6 +13,7 @@ namespace FireflyIII\Import\Importer; use FireflyIII\Import\Role\Map; use FireflyIII\Models\ImportJob; +use Symfony\Component\HttpFoundation\FileBag; /** * Interface ImporterInterface @@ -37,11 +38,19 @@ interface ImporterInterface public function getConfigurationData(): array; /** - * @param array $data + * This method returns the data required for the view that will let the user add settings to the import job. * - * @return bool + * @return array */ - public function saveImportConfiguration(array $data): bool; + public function getDataForSettings(): array; + + /** + * This method returns the name of the view that will be shown to the user to further configure + * the import job. + * + * @return string + */ + public function getViewForSettings(): string; /** * Returns a Map thing used to allow the user to @@ -51,6 +60,21 @@ interface ImporterInterface */ public function prepareRoles(): Map; + /** + * This method returns whether or not the user must configure this import + * job further. + * + * @return bool + */ + public function requireUserSettings(): bool; + + /** + * @param array $data + * + * @return bool + */ + public function saveImportConfiguration(array $data, FileBag $files): bool; + /** * @param ImportJob $job * diff --git a/app/Models/ImportJob.php b/app/Models/ImportJob.php index 6335d78c10..1f679e0d70 100644 --- a/app/Models/ImportJob.php +++ b/app/Models/ImportJob.php @@ -12,7 +12,9 @@ declare(strict_types = 1); namespace FireflyIII\Models; use Auth; +use Crypt; use Illuminate\Database\Eloquent\Model; +use Storage; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -67,15 +69,6 @@ class ImportJob extends Model $this->save(); } - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function user() - { - return $this->belongsTo('FireflyIII\User'); - } - - /** * @param $value * @@ -86,8 +79,8 @@ class ImportJob extends Model if (strlen($value) == 0) { return []; } - - return json_decode($value); + + return json_decode($value, true); } /** @@ -97,4 +90,25 @@ class ImportJob extends Model { $this->attributes['configuration'] = json_encode($value); } + + /** + * @return string + */ + public function uploadFileContents(): string + { + $fileName = $this->key . '.upload'; + $disk = Storage::disk('upload'); + $encryptedContent = $disk->get($fileName); + $content = Crypt::decrypt($encryptedContent); + + return $content; + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user() + { + return $this->belongsTo('FireflyIII\User'); + } } diff --git a/config/csv.php b/config/csv.php index 0419338367..3622624496 100644 --- a/config/csv.php +++ b/config/csv.php @@ -3,6 +3,206 @@ declare(strict_types = 1); return [ + + /* + * Configuration for the CSV specifics. + */ + 'import_specifics' => [ + 'RabobankDescription' => 'FireflyIII\Import\Specifics\RabobankDescription', + 'AbnAmroDescription' => 'FireflyIII\Import\Specifics\AbnAmroDescription', + ], + + /* + * Configuration for possible column roles. + */ + 'import_roles' => [ + '_ignore' => [ + 'mappable' => false, + 'converter' => 'Ignore', + 'field' => 'ignored', + ], + 'bill-id' => [ + 'mappable' => false, + 'field' => 'bill', + 'converter' => 'BillId', + 'mapper' => 'Bill', + ], + 'bill-name' => [ + 'mappable' => true, + 'converter' => 'BillName', + 'field' => 'bill', + 'mapper' => 'Bill', + ], + 'currency-id' => [ + 'mappable' => true, + 'converter' => 'CurrencyId', + 'field' => 'currency', + 'mapper' => 'TransactionCurrency' + ], + 'currency-name' => [ + 'mappable' => true, + 'converter' => 'CurrencyName', + 'field' => 'currency', + 'mapper' => 'TransactionCurrency' + ], + 'currency-code' => [ + 'mappable' => true, + 'converter' => 'CurrencyCode', + 'field' => 'currency', + 'mapper' => 'TransactionCurrency' + ], + 'currency-symbol' => [ + 'mappable' => true, + 'converter' => 'CurrencySymbol', + 'field' => 'currency', + 'mapper' => 'TransactionCurrency' + ], + 'description' => [ + 'mappable' => false, + 'converter' => 'Description', + 'field' => 'description', + ], + 'date-transaction' => [ + 'mappable' => false, + 'converter' => 'Date', + 'field' => 'date', + ], + 'date-rent' => [ + 'mappable' => false, + 'converter' => 'Date', + 'field' => 'date-rent', + ], + 'budget-id' => [ + 'mappable' => true, + 'converter' => 'BudgetId', + 'field' => 'budget', + 'mapper' => 'Budget', + ], + 'budget-name' => [ + 'mappable' => true, + 'converter' => 'BudgetName', + 'field' => 'budget', + 'mapper' => 'Budget', + ], + 'rabo-debet-credit' => [ + 'mappable' => false, + 'converter' => 'RabobankDebetCredit', + 'field' => 'amount-modifier', + ], + 'ing-debet-credit' => [ + 'mappable' => false, + 'converter' => 'INGDebetCredit', + 'field' => 'amount-modifier', + ], + 'category-id' => [ + 'mappable' => true, + 'converter' => 'CategoryId', + 'field' => 'category', + 'mapper' => 'Category', + ], + 'category-name' => [ + 'mappable' => true, + 'converter' => 'CategoryName', + 'field' => 'category', + 'mapper' => 'Category', + ], + 'tags-comma' => [ + 'mappable' => true, + 'field' => 'tags', + 'converter' => 'TagsComma', + 'mapper' => 'Tag', + ], + 'tags-space' => [ + 'mappable' => true, + 'field' => 'tags', + 'converter' => 'TagsSpace', + 'mapper' => 'Tag', + ], + 'account-id' => [ + 'mappable' => true, + 'mapper' => 'AssetAccount', + 'field' => 'asset-account-id', + 'converter' => 'AccountId' + ], + 'account-name' => [ + 'mappable' => true, + 'mapper' => 'AssetAccount', + 'field' => 'asset-account-name', + 'converter' => 'AssetAccountName' + ], + 'account-iban' => [ + 'mappable' => true, + 'converter' => 'AssetAccountIban', + 'field' => 'asset-account-iban', + 'mapper' => 'AssetAccount' + ], + 'account-number' => [ + 'mappable' => true, + 'converter' => 'AssetAccountNumber', + 'field' => 'asset-account-number', + 'mapper' => 'AssetAccount' + ], + 'opposing-id' => [ + 'mappable' => true, + 'field' => 'opposing-account-id', + 'converter' => 'OpposingAccountId', + 'mapper' => 'AnyAccount', + ], + 'opposing-name' => [ + 'mappable' => true, + 'field' => 'opposing-account-name', + 'converter' => 'OpposingAccountName', + 'mapper' => 'AnyAccount', + ], + 'opposing-iban' => [ + 'mappable' => true, + 'field' => 'opposing-account-iban', + 'converter' => 'OpposingAccountIban', + 'mapper' => 'AnyAccount', + ], + 'opposing-number' => [ + 'mappable' => true, + 'field' => 'opposing-account-number', + 'converter' => 'OpposingAccountNumber', + 'mapper' => 'AnyAccount', + ], + 'amount' => [ + 'mappable' => false, + 'converter' => 'Amount', + 'field' => 'amount', + ], + 'amount-comma-separated' => [ + 'mappable' => false, + 'converter' => 'AmountComma', + 'field' => 'amount', + ], + 'sepa-ct-id' => [ + 'mappable' => false, + 'converter' => 'Description', + 'field' => 'description', + ], + 'sepa-ct-op' => [ + 'mappable' => false, + 'converter' => 'Description', + 'field' => 'description', + ], + 'sepa-db' => [ + 'mappable' => false, + 'converter' => 'Description', + 'field' => 'description', + ], + ], + + + + + + + + + /* + + 'specifix' => [ 'RabobankDescription', 'AbnAmroDescription', @@ -194,4 +394,7 @@ return [ 'field' => 'description', ], ] + + + */ ]; diff --git a/config/firefly.php b/config/firefly.php index 0e93925850..f764212203 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -11,20 +11,14 @@ return [ 'resend_confirmation' => 3600, 'confirmation_age' => 14400, // four hours - 'export_formats' => [ + 'export_formats' => [ 'csv' => 'FireflyIII\Export\Exporter\CsvExporter', // mt940 FireflyIII Export Exporter MtExporter ], - 'csv_import_specifics' => [ - 'RabobankDescription' => 'FireflyIII\Import\Specifics\RabobankDescription', - 'AbnAmroDescription' => 'FireflyIII\Import\Specifics\AbnAmroDescription', - ], - 'import_formats' => [ + 'import_formats' => [ 'csv' => 'FireflyIII\Import\Importer\CsvImporter', // mt940 FireflyIII Import Importer MtImporter ], - - 'default_export_format' => 'csv', 'default_import_format' => 'csv', 'bill_periods' => ['weekly', 'monthly', 'quarterly', 'half-year', 'yearly'], @@ -142,7 +136,7 @@ return [ 'end_date' => 'FireflyIII\Support\Binder\Date', ], - 'rule-triggers' => [ + 'rule-triggers' => [ 'user_action' => 'FireflyIII\Rules\Triggers\UserAction', 'from_account_starts' => 'FireflyIII\Rules\Triggers\FromAccountStarts', 'from_account_ends' => 'FireflyIII\Rules\Triggers\FromAccountEnds', @@ -161,7 +155,7 @@ return [ 'description_contains' => 'FireflyIII\Rules\Triggers\DescriptionContains', 'description_is' => 'FireflyIII\Rules\Triggers\DescriptionIs', ], - 'rule-actions' => [ + 'rule-actions' => [ 'set_category' => 'FireflyIII\Rules\Actions\SetCategory', 'clear_category' => 'FireflyIII\Rules\Actions\ClearCategory', 'set_budget' => 'FireflyIII\Rules\Actions\SetBudget', @@ -174,7 +168,7 @@ return [ 'prepend_description' => 'FireflyIII\Rules\Actions\PrependDescription', ], // all rule actions that require text input: - 'rule-actions-text' => [ + 'rule-actions-text' => [ 'set_category', 'set_budget', 'add_tag', @@ -183,7 +177,7 @@ return [ 'append_description', 'prepend_description', ], - 'test-triggers' => [ + 'test-triggers' => [ // The maximum number of transactions shown when testing a list of triggers 'limit' => 10, diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php new file mode 100644 index 0000000000..f9878e6938 --- /dev/null +++ b/resources/lang/en_US/csv.php @@ -0,0 +1,44 @@ + '(ignore this column)', + 'csv_column_account-iban' => 'Asset account (IBAN)', + 'csv_column_account-id' => 'Asset account ID (matching Firefly)', + 'csv_column_account-name' => 'Asset account (name)', + 'csv_column_amount' => 'Amount', + 'csv_column_amount-comma-separated' => 'Amount (comma as decimal separator)', + 'csv_column_bill-id' => 'Bill ID (matching Firefly)', + 'csv_column_bill-name' => 'Bill name', + 'csv_column_budget-id' => 'Budget ID (matching Firefly)', + 'csv_column_budget-name' => 'Budget name', + 'csv_column_category-id' => 'Category ID (matching Firefly)', + 'csv_column_category-name' => 'Category name', + 'csv_column_currency-code' => 'Currency code (ISO 4217)', + 'csv_column_currency-id' => 'Currency ID (matching Firefly)', + 'csv_column_currency-name' => 'Currency name (matching Firefly)', + 'csv_column_currency-symbol' => 'Currency symbol (matching Firefly)', + 'csv_column_date-rent' => 'Rent calculation date', + 'csv_column_date-transaction' => 'Date', + 'csv_column_description' => 'Description', + 'csv_column_opposing-iban' => 'Opposing account (IBAN)', + 'csv_column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'csv_column_opposing-name' => 'Opposing account (name)', + 'csv_column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'csv_column_ing-debet-credit' => 'ING specific debet/credit indicator', + 'csv_column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'csv_column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'csv_column_sepa-db' => 'SEPA Direct Debet', + 'csv_column_tags-comma' => 'Tags (comma separated)', + 'csv_column_tags-space' => 'Tags (space separated)', + 'csv_column_account-number' => 'Asset account (account number)', + 'csv_column_opposing-number' => 'Opposing account (account number)', +]; \ No newline at end of file diff --git a/resources/views/import/csv/map.twig b/resources/views/import/csv/map.twig new file mode 100644 index 0000000000..fb141d4432 --- /dev/null +++ b/resources/views/import/csv/map.twig @@ -0,0 +1,88 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} +{% endblock %} + +{% block content %} + +
    +
    +
    +
    +

    {{ 'csv_column_roles_title'|_ }}

    +
    +
    +

    {{ 'csv_column_roles_text'|_ }}

    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    +

    {{ 'csv_column_roles_table'|_ }}

    +
    +
    + + + + + + + + + + + {% for i in 0..data.columnCount %} + + + + + + + {% endfor %} + + +
    {{ 'csv_column_name'|_ }}{{ 'csv_column_example'|_ }}{{ 'csv_column_role'|_ }}{{ 'csv_do_map_value'|_ }}
    Column #{{ loop.index }} + {% if data.columns[i]|length == 0 %} + No example data available + {% else %} + {% for example in data.columns[i] %} + {{ example }}
    + {% endfor %} + {% endif %} + +
    + {{ Form.select(('role['~index~']'), data.available_roles,data.set_roles[index],{class: 'form-control'}) }} + + {# Form.checkbox(('map['~index~']'),1,map[index]) #} +
    + + +
    +
    +
    +
    + +
    +
    +
    +
    + {{ 'csv_go_back'|_ }} + +
    +
    +
    +
    +
    + + +{% endblock %} From b947ff50eda348f2d57d598c7b726cc82079b15a Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 27 Jun 2016 16:11:49 +0200 Subject: [PATCH 24/94] Fix chart thing. --- app/Models/AccountType.php | 2 - app/Support/Binder/AccountList.php | 1 - public/js/ff/reports/default/year.js | 69 ++++++++++++++++++++++++++-- 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/app/Models/AccountType.php b/app/Models/AccountType.php index 2bb17e3400..88bc502ff6 100644 --- a/app/Models/AccountType.php +++ b/app/Models/AccountType.php @@ -21,13 +21,11 @@ use Illuminate\Database\Eloquent\Relations\HasMany; * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at * @property string $type - * @property boolean $editable * @property-read \Illuminate\Database\Eloquent\Collection|Account[] $accounts * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereId($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereUpdatedAt($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereType($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereEditable($value) * @mixin \Eloquent */ class AccountType extends Model diff --git a/app/Support/Binder/AccountList.php b/app/Support/Binder/AccountList.php index de024c609d..e1b351a459 100644 --- a/app/Support/Binder/AccountList.php +++ b/app/Support/Binder/AccountList.php @@ -42,7 +42,6 @@ class AccountList implements BinderInterface /** @var \Illuminate\Support\Collection $object */ $object = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->where('account_types.editable', 1) ->whereIn('accounts.id', $ids) ->where('user_id', Auth::user()->id) ->get(['accounts.*']); diff --git a/public/js/ff/reports/default/year.js b/public/js/ff/reports/default/year.js index 79a3b50faa..54627fc29d 100644 --- a/public/js/ff/reports/default/year.js +++ b/public/js/ff/reports/default/year.js @@ -1,8 +1,10 @@ /* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */ - +var chartDrawn; +var budgetChart; $(function () { "use strict"; + chartDrawn = false; drawChart(); // click open the top X income list: @@ -26,9 +28,68 @@ function clickBudgetChart(e) { "use strict"; var link = $(e.target); var budgetId = link.data('budget'); - console.log('Budget id is ' + budgetId); - $('#budget_chart').empty(); - columnChart('chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budget_chart'); + var URL = 'chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds; + var container = 'budget_chart'; + // if chart drawn is false, draw the first one, then + // set to true + if (chartDrawn == false) { + // do new chart: + + + $.getJSON(URL).done(function (data) { + console.log('Will draw new columnChart(' + URL + ')'); + + var ctx = document.getElementById(container).getContext("2d"); + var newData = {}; + newData.datasets = []; + + for (var i = 0; i < data.count; i++) { + newData.labels = data.labels; + var dataset = data.datasets[i]; + dataset.backgroundColor = fillColors[i]; + newData.datasets.push(dataset); + } + // completely new chart. + budgetChart = new Chart(ctx, { + type: 'bar', + data: data, + options: defaultColumnOptions + }); + + }).fail(function () { + $('#' + container).addClass('general-chart-error'); + }); + console.log('URL for column chart : ' + URL); + chartDrawn = true; + } else { + console.log('Will now handle remove data and add new!'); + $.getJSON(URL).done(function (data) { + console.log('Will draw updated columnChart(' + URL + ')'); + var newData = {}; + newData.datasets = []; + + for (var i = 0; i < data.count; i++) { + newData.labels = data.labels; + var dataset = data.datasets[i]; + dataset.backgroundColor = fillColors[i]; + newData.datasets.push(dataset); + } + // update the chart + console.log('Now update chart thing.'); + budgetChart.data.datasets = newData.datasets; + budgetChart.update(); + + }).fail(function () { + $('#' + container).addClass('general-chart-error'); + }); + + + } + + // if chart drawn is true, add new data to existing chart. + // console.log('Budget id is ' + budgetId); + // $('#budget_chart').empty(); + // columnChart('chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budget_chart'); return false; } From 3d58fbebecc1125cc238792a5e0b83615f13ab27 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 27 Jun 2016 16:25:17 +0200 Subject: [PATCH 25/94] Should not have committed this. --- config/app.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/app.php b/config/app.php index 731400f5b1..79a9e67ed6 100644 --- a/config/app.php +++ b/config/app.php @@ -180,8 +180,8 @@ return [ // own stuff: - Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, - Barryvdh\Debugbar\ServiceProvider::class, +// Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, +// Barryvdh\Debugbar\ServiceProvider::class, 'DaveJamesMiller\Breadcrumbs\ServiceProvider', 'TwigBridge\ServiceProvider', 'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider', From cbe3fb73a88a0dbd246467d26e452e7537bbb65c Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 27 Jun 2016 16:36:28 +0200 Subject: [PATCH 26/94] Catch decrypt exceptions. --- app/Models/Preference.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Models/Preference.php b/app/Models/Preference.php index 44e5951cbf..cbb7b0512a 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -12,6 +12,8 @@ declare(strict_types = 1); namespace FireflyIII\Models; use Crypt; +use FireflyIII\Exceptions\FireflyException; +use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Database\Eloquent\Model; /** @@ -48,7 +50,11 @@ class Preference extends Model */ public function getDataAttribute($value) { - $data = Crypt::decrypt($value); + try { + $data = Crypt::decrypt($value); + } catch (DecryptException $e) { + throw new FireflyException('Could not decrypt preference #' . $this->id . '.'); + } return json_decode($data); } From a56a5fc2288911ee1800be8859768c6b6c36f11c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 2 Jul 2016 17:33:57 +0200 Subject: [PATCH 27/94] New code for import routine. --- app/Http/Controllers/ImportController.php | 70 ++++++++- app/Import/Importer/CsvImporter.php | 137 +++++++++++++----- app/Import/Importer/ImporterInterface.php | 17 ++- config/csv.php | 10 +- resources/lang/en_US/csv.php | 85 +++++++---- resources/lang/en_US/firefly.php | 16 +- resources/lang/en_US/form.php | 2 +- resources/views/import/csv/configure.twig | 24 ++- .../views/import/csv/{map.twig => roles.twig} | 29 ++-- resources/views/import/index.twig | 1 + resources/views/index.twig | 2 +- 11 files changed, 264 insertions(+), 129 deletions(-) rename resources/views/import/csv/{map.twig => roles.twig} (65%) diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 3416a5a6fd..c3c1763bc9 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -10,8 +10,10 @@ use FireflyIII\Import\Importer\ImporterInterface; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use Illuminate\Http\Request; +use Log; use SplFileObject; use Storage; +use Symfony\Component\HttpFoundation\File\UploadedFile; use View; /** @@ -50,9 +52,11 @@ class ImportController extends Controller // actual code $importer = $this->makeImporter($job); $importer->configure(); - $data = $importer->getConfigurationData(); + $data = $importer->getConfigurationData(); + $subTitle = trans('firefly.configure_import'); + $subTitleIcon = 'fa-wrench'; - return view('import.' . $job->file_type . '.configure', compact('data', 'job')); + return view('import.' . $job->file_type . '.configure', compact('data', 'job', 'subTitle', 'subTitleIcon')); } @@ -106,6 +110,28 @@ class ImportController extends Controller return redirect(route('import.settings', $job->key)); } + /** + * This step 6. Depending on the importer, this will process the + * settings given and store them. + * + * @param Request $request + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + public function postSettings(Request $request, ImportJob $job) + { + if (!$this->jobInCorrectStep($job, 'store-settings')) { + return $this->redirectToCorrectStep($job); + } + $importer = $this->makeImporter($job); + $importer->storeSettings($request); + + // return redirect to settings (for more settings perhaps) + return redirect(route('import.settings', [$job->key])); + } + /** * Step 5. Depending on the importer, this will show the user settings to * fill in. @@ -120,18 +146,20 @@ class ImportController extends Controller if (!$this->jobInCorrectStep($job, 'settings')) { return $this->redirectToCorrectStep($job); } - $importer = $this->makeImporter($job); + $importer = $this->makeImporter($job); + $subTitle = trans('firefy.settings_for_import'); + $subTitleIcon = 'fa-wrench'; // now show settings screen to user. if ($importer->requireUserSettings()) { $data = $importer->getDataForSettings(); $view = $importer->getViewForSettings(); - return view($view, compact('data', 'job')); + return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon')); } // if no more settings, save job and continue to process thing. - + echo 'now in settings (done)'; exit; @@ -155,8 +183,11 @@ class ImportController extends Controller public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository) { // create import job: - $type = $request->get('import_file_type'); - $job = $repository->create($type); + $type = $request->get('import_file_type'); + $job = $repository->create($type); + Log::debug('Created new job', ['key' => $job->key, 'id' => $job->id]); + + /** @var UploadedFile $upload */ $upload = $request->files->get('import_file'); $newName = $job->key . '.upload'; $uploaded = new SplFileObject($upload->getRealPath()); @@ -165,6 +196,30 @@ class ImportController extends Controller $disk = Storage::disk('upload'); $disk->put($newName, $contentEncrypted); + Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]); + + // store configuration file's content into the job's configuration + // thing. + // otherwise, leave it empty. + if ($request->files->has('configuration_file')) { + /** @var UploadedFile $configFile */ + $configFile = $request->files->get('configuration_file'); + Log::debug( + 'Uploaded configuration file', + ['name' => $configFile->getClientOriginalName(), 'size' => $configFile->getSize(), 'mime' => $configFile->getClientMimeType()] + ); + + $configFileObject = new SplFileObject($configFile->getRealPath()); + $configRaw = $configFileObject->fread($configFileObject->getSize()); + $configuration = json_decode($configRaw, true); + + if (!is_null($configuration) && is_array($configuration)) { + Log::debug('Found configuration', $configuration); + $job->configuration = $configuration; + $job->save(); + } + } + return redirect(route('import.configure', [$job->key])); } @@ -183,6 +238,7 @@ class ImportController extends Controller return $job->status === 'import_status_never_started'; break; case 'settings': + case 'store-settings': return $job->status === 'import_configuration_saved'; break; } diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index ece4c9e527..7a592442a4 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -14,10 +14,11 @@ namespace FireflyIII\Import\Importer; use ExpandedForm; use FireflyIII\Crud\Account\AccountCrud; -use FireflyIII\Import\Role\Map; use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; +use Illuminate\Http\Request; use League\Csv\Reader; +use Log; use Symfony\Component\HttpFoundation\FileBag; /** @@ -32,11 +33,38 @@ class CsvImporter implements ImporterInterface public $job; /** + * Create initial (empty) configuration array. + * + * + * * @return bool */ public function configure(): bool { + if (is_null($this->job->configuration) || (is_array($this->job->configuration) && count($this->job->configuration) === 0)) { + Log::debug('No config detected, will create empty one.'); + + $config = [ + 'has-headers' => false, // assume + 'date-format' => 'Ymd', // assume + 'delimiter' => ',', // assume + 'import-account' => 0, // none, + 'specifics' => [], // none + 'column-count' => 0, // unknown + 'column-roles' => [], // unknown + 'column-do-mapping' => [], // not yet set which columns must be mapped + 'column-roles-complete' => false, // not yet configured roles for columns + 'column-mapping-config' => [], // no mapping made yet. + 'column-mapping-complete' => false, // so mapping is not complete. + ]; + $this->job->configuration = $config; + $this->job->save(); + + return true; + } + // need to do nothing, for now. + Log::debug('Detected config in upload, will use that one. ', $this->job->configuration); return true; } @@ -89,15 +117,17 @@ class CsvImporter implements ImporterInterface 'columnCount' => 0, ]; - if (!isset($config['columns'])) { + if ($this->doColumnRoles()) { - // show user column configuration. + // show user column role configuration. $content = $this->job->uploadFileContents(); // create CSV reader. $reader = Reader::createFromString($content); - $start = $config['has_headers'] ? 1 : 0; + $start = $config['has-headers'] ? 1 : 0; $end = $start + self::EXAMPLE_ROWS; // first X rows + + // collect example data in $data['columns'] while ($start < $end) { $row = $reader->fetchOne($start); foreach ($row as $index => $value) { @@ -110,20 +140,28 @@ class CsvImporter implements ImporterInterface $data['columnCount'] = count($row); } - // make unique + // make unique example data foreach ($data['columns'] as $index => $values) { $data['columns'][$index] = array_unique($values); } - // TODO preset roles from config + $data['set_roles'] = []; // collect possible column roles: $data['available_roles'] = []; foreach (array_keys(config('csv.import_roles')) as $role) { - $data['available_roles'][$role] = trans('csv.csv_column_'.$role); + $data['available_roles'][$role] = trans('csv.column_' . $role); } + $config['column-count'] = $data['columnCount']; + $this->job->configuration = $config; + $this->job->save(); + return $data; } + + + echo 'no settings to do.'; + exit; } @@ -135,18 +173,10 @@ class CsvImporter implements ImporterInterface */ public function getViewForSettings(): string { - return 'import.csv.map'; - } - - /** - * Returns a Map thing used to allow the user to - * define roles for each entry. - * - * @return Map - */ - public function prepareRoles(): Map - { - return 'do not work'; + if ($this->doColumnRoles()) { + return 'import.csv.roles'; + } + echo 'no view for settings'; exit; } @@ -174,33 +204,25 @@ class CsvImporter implements ImporterInterface */ public function saveImportConfiguration(array $data, FileBag $files): bool { - /* - * TODO file upload is ignored for now. - */ - /** @var AccountCrud $repository */ - $repository = app(AccountCrud::class); - $account = $repository->find(intval($data['csv_import_account'])); - $hasHeaders = isset($data['has_headers']) && intval($data['has_headers']) === 1 ? true : false; - $configuration = [ - 'has_headers' => $hasHeaders, - 'date_format' => $data['date_format'], - 'csv_delimiter' => $data['csv_delimiter'], - 'csv_import_account' => 0, - 'specifics' => [], - - ]; + $repository = app(AccountCrud::class); + $account = $repository->find(intval($data['csv_import_account'])); + $hasHeaders = isset($data['has_headers']) && intval($data['has_headers']) === 1 ? true : false; + $config = $this->job->configuration; + $config['has-headers'] = $hasHeaders; + $config['date-format'] = $data['date_format']; + $config['delimiter'] = $data['csv_delimiter']; if (!is_null($account->id)) { - $configuration['csv_import_account'] = $account->id; + $config['import-account'] = $account->id; } // loop specifics. - if (is_array($data['specifics'])) { + if (isset($data['specifics']) && is_array($data['specifics'])) { foreach ($data['specifics'] as $name => $enabled) { - $configuration['specifics'][] = $name; + $config['specifics'][$name] = 1; } } - $this->job->configuration = $configuration; + $this->job->configuration = $config; $this->job->save(); return true; @@ -215,4 +237,43 @@ class CsvImporter implements ImporterInterface { $this->job = $job; } + + /** + * Store the settings filled in by the user, if applicable. + * + * @param Request $request + * + */ + public function storeSettings(Request $request) + { + $config = $this->job->configuration; + $count = $config['column-count']; + $all = $request->all(); + $roleSet = 0; + for ($i = 0; $i < $count; $i++) { + $selectedRole = $all['role'][$i] ?? '_ignore'; + $doMapping = isset($all['map'][$i]) && $all['map'][$i] == '1' ? true : false; + if ($selectedRole == '_ignore' && $doMapping === true) { + $doMapping = false; // cannot map ignored columns. + } + if ($selectedRole != '_ignore') { + $roleSet++; + } + $config['column-roles'][$i] = $selectedRole; + $config['column-do-mapping'][$i] = $doMapping; + } + if ($roleSet > 0) { + $config['column-roles-complete'] = true; + $this->job->configuration = $config; + $this->job->save(); + } + } + + /** + * @return bool + */ + private function doColumnRoles(): bool + { + return $this->job->configuration['column-roles-complete'] === false; + } } \ No newline at end of file diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php index 45135fc26b..6e332da8fe 100644 --- a/app/Import/Importer/ImporterInterface.php +++ b/app/Import/Importer/ImporterInterface.php @@ -13,6 +13,7 @@ namespace FireflyIII\Import\Importer; use FireflyIII\Import\Role\Map; use FireflyIII\Models\ImportJob; +use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\FileBag; /** @@ -44,6 +45,14 @@ interface ImporterInterface */ public function getDataForSettings(): array; + /** + * Store the settings filled in by the user, if applicable. + * + * @param Request $request + * + */ + public function storeSettings(Request $request); + /** * This method returns the name of the view that will be shown to the user to further configure * the import job. @@ -52,14 +61,6 @@ interface ImporterInterface */ public function getViewForSettings(): string; - /** - * Returns a Map thing used to allow the user to - * define roles for each entry. - * - * @return Map - */ - public function prepareRoles(): Map; - /** * This method returns whether or not the user must configure this import * job further. diff --git a/config/csv.php b/config/csv.php index 3622624496..e235133f32 100644 --- a/config/csv.php +++ b/config/csv.php @@ -171,11 +171,11 @@ return [ 'converter' => 'Amount', 'field' => 'amount', ], - 'amount-comma-separated' => [ - 'mappable' => false, - 'converter' => 'AmountComma', - 'field' => 'amount', - ], +// 'amount-comma-separated' => [ +// 'mappable' => false, +// 'converter' => 'AmountComma', +// 'field' => 'amount', +// ], 'sepa-ct-id' => [ 'mappable' => false, 'converter' => 'Description', diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index f9878e6938..c27e945dce 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -10,35 +10,58 @@ declare(strict_types = 1); return [ - 'csv_column__ignore' => '(ignore this column)', - 'csv_column_account-iban' => 'Asset account (IBAN)', - 'csv_column_account-id' => 'Asset account ID (matching Firefly)', - 'csv_column_account-name' => 'Asset account (name)', - 'csv_column_amount' => 'Amount', - 'csv_column_amount-comma-separated' => 'Amount (comma as decimal separator)', - 'csv_column_bill-id' => 'Bill ID (matching Firefly)', - 'csv_column_bill-name' => 'Bill name', - 'csv_column_budget-id' => 'Budget ID (matching Firefly)', - 'csv_column_budget-name' => 'Budget name', - 'csv_column_category-id' => 'Category ID (matching Firefly)', - 'csv_column_category-name' => 'Category name', - 'csv_column_currency-code' => 'Currency code (ISO 4217)', - 'csv_column_currency-id' => 'Currency ID (matching Firefly)', - 'csv_column_currency-name' => 'Currency name (matching Firefly)', - 'csv_column_currency-symbol' => 'Currency symbol (matching Firefly)', - 'csv_column_date-rent' => 'Rent calculation date', - 'csv_column_date-transaction' => 'Date', - 'csv_column_description' => 'Description', - 'csv_column_opposing-iban' => 'Opposing account (IBAN)', - 'csv_column_opposing-id' => 'Opposing account ID (matching Firefly)', - 'csv_column_opposing-name' => 'Opposing account (name)', - 'csv_column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', - 'csv_column_ing-debet-credit' => 'ING specific debet/credit indicator', - 'csv_column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', - 'csv_column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', - 'csv_column_sepa-db' => 'SEPA Direct Debet', - 'csv_column_tags-comma' => 'Tags (comma separated)', - 'csv_column_tags-space' => 'Tags (space separated)', - 'csv_column_account-number' => 'Asset account (account number)', - 'csv_column_opposing-number' => 'Opposing account (account number)', + + 'import_configure_title' => 'Configure your import', + 'import_configure_intro' => 'There are some options for your CSV import.', + 'import_configure_form' => 'Form', + 'header_help' => 'Check this if the first row of your CSV file are the column titles', + 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', + 'import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', + + // roles + 'column_roles_title' => 'Define column roles', + 'column_roles_text' => 'Each column contains some data. What data?', + 'column_roles_table' => 'Table', + 'column_name' => 'Name of column', + 'column_example' => 'Column example data', + 'column_role' => 'Column data meaning', + 'do_map_value' => 'Map these values', + 'column' => 'Column', + 'no_example_data' => 'No example data available', + 'store_column_roles' => 'Continue import', + + 'column__ignore' => '(ignore this column)', + 'column_account-iban' => 'Asset account (IBAN)', + 'column_account-id' => 'Asset account ID (matching Firefly)', + 'column_account-name' => 'Asset account (name)', + 'column_amount' => 'Amount', + 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', + 'column_bill-id' => 'Bill ID (matching Firefly)', + 'column_bill-name' => 'Bill name', + 'column_budget-id' => 'Budget ID (matching Firefly)', + 'column_budget-name' => 'Budget name', + 'column_category-id' => 'Category ID (matching Firefly)', + 'column_category-name' => 'Category name', + 'column_currency-code' => 'Currency code (ISO 4217)', + 'column_currency-id' => 'Currency ID (matching Firefly)', + 'column_currency-name' => 'Currency name (matching Firefly)', + 'column_currency-symbol' => 'Currency symbol (matching Firefly)', + 'column_date-rent' => 'Rent calculation date', + 'column_date-transaction' => 'Date', + 'column_description' => 'Description', + 'column_opposing-iban' => 'Opposing account (IBAN)', + 'column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'column_opposing-name' => 'Opposing account (name)', + 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'column_ing-debet-credit' => 'ING specific debet/credit indicator', + 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'column_sepa-db' => 'SEPA Direct Debet', + 'column_tags-comma' => 'Tags (comma separated)', + 'column_tags-space' => 'Tags (space separated)', + 'column_account-number' => 'Asset account (account number)', + 'column_opposing-number' => 'Opposing account (account number)', ]; \ No newline at end of file diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index ede1f038f6..077c75cb33 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -749,18 +749,12 @@ return [ 'split_this_transfer' => 'Split this transfer', // import + 'configuration_file_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you.', + 'import_data_index' => 'Index', 'import_file_type_csv' => 'CSV (comma separated values)', 'import_file_type_help' => 'Select the type of file you will upload', 'import_start' => 'Start the import', - 'import_csv_configure_title' => 'Configure your import', - 'import_csv_configure_intro' => 'There are some options for your CSV import.', - 'import_csv_configure_form' => 'Form', - 'csv_header_help' => 'Check this if the first row of your CSV file are the column titles', - 'csv_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', - 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', - 'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', - 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'csv_upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', - - + 'configure_import' => 'Further configure your import', + 'import_finish_configuration' => 'Finish configuration', + 'settings_for_import' => 'Settings', ]; diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index c2bf0cb5e9..0caec98f1c 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -133,6 +133,7 @@ return [ // import 'import_file' => 'Import file', + 'configuration_file' => 'Configuration file', 'import_file_type' => 'Import file type', 'csv_comma' => 'A comma (,)', 'csv_semicolon' => 'A semicolon (;)', @@ -142,5 +143,4 @@ return [ 'csv_config' => 'CSV import configuration', - ]; diff --git a/resources/views/import/csv/configure.twig b/resources/views/import/csv/configure.twig index 64adea984a..0cbab054f3 100644 --- a/resources/views/import/csv/configure.twig +++ b/resources/views/import/csv/configure.twig @@ -10,11 +10,11 @@
    -

    {{ 'import_csv_configure_title'|_ }}

    +

    {{ trans('csv.import_configure_title') }}

    - {{ 'import_csv_configure_intro'|_ }} + {{ trans('csv.import_configure_intro') }}

    @@ -29,17 +29,14 @@
    -

    {{ 'import_csv_configure_form'|_ }}

    +

    {{ trans('csv.import_configure_form') }}

    - {{ ExpandedForm.checkbox('has_headers',1,null,{helpText: 'csv_header_help'|_}) }} - {{ ExpandedForm.text('date_format','Ymd',{helpText: trans('firefly.csv_date_help', {dateExample: phpdate('Ymd')}) }) }} - {{ ExpandedForm.select('csv_delimiter', data.delimiters, 0, {helpText: 'csv_delimiter_help'|_} ) }} - - {{ ExpandedForm.file('csv_config',{helpText: 'csv_csv_config_file_help'|_}) }} - - {{ ExpandedForm.select('csv_import_account', data.accounts, 0, {helpText: 'csv_import_account_help'|_} ) }} + {{ ExpandedForm.checkbox('has_headers',1,job.configuration['has-headers'],{helpText: trans('csv.header_help')}) }} + {{ ExpandedForm.text('date_format',job.configuration['date-format'],{helpText: trans('csv.date_help', {dateExample: phpdate('Ymd')}) }) }} + {{ ExpandedForm.select('csv_delimiter', data.delimiters, job.configuration['delimiter'], {helpText: trans('csv.delimiter_help') } ) }} + {{ ExpandedForm.select('csv_import_account', data.accounts, 0, {helpText: trans('csv.import_account_help')} ) }} {% for type, specific in data.specifics %}
    @@ -49,7 +46,8 @@
    @@ -66,7 +64,7 @@
    {{ data.upload_path }}

    - {{ 'csv_upload_not_writeable'|_ }} + {{ trans('csv.upload_not_writeable') }}

    @@ -82,7 +80,7 @@
    diff --git a/resources/views/import/csv/map.twig b/resources/views/import/csv/roles.twig similarity index 65% rename from resources/views/import/csv/map.twig rename to resources/views/import/csv/roles.twig index fb141d4432..c1f317cbbf 100644 --- a/resources/views/import/csv/map.twig +++ b/resources/views/import/csv/roles.twig @@ -10,10 +10,10 @@
    -

    {{ 'csv_column_roles_title'|_ }}

    +

    {{ trans('csv.column_roles_title') }}

    -

    {{ 'csv_column_roles_text'|_ }}

    +

    {{ trans('csv.column_roles_text') }}

    @@ -21,30 +21,32 @@
    +
    -

    {{ 'csv_column_roles_table'|_ }}

    +

    {{ trans('csv.column_roles_table') }}

    - - - - + + + + - {% for i in 0..data.columnCount %} + {% for i in 0..(data.columnCount-1) %} + - + @@ -74,9 +76,8 @@
    - {{ 'csv_go_back'|_ }}
    diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index 8449e5a93f..b69d953026 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -26,6 +26,7 @@
    {{ ExpandedForm.file('import_file', {helpText: 'import_file_help'|_}) }} + {{ ExpandedForm.file('configuration_file', {helpText: 'configuration_file_help'|_}) }} {{ ExpandedForm.select('import_file_type', importFileTypes, defaultImportType, {'helpText' : 'import_file_type_help'|_}) }} diff --git a/resources/views/index.twig b/resources/views/index.twig index 4363bbf924..64a8e1d873 100644 --- a/resources/views/index.twig +++ b/resources/views/index.twig @@ -59,7 +59,7 @@ {% for data in transactions %}
    -

    {{ data[1].name }}

    +

    {{ data[1].name }}

    From 189b11befab8d33b044f51abcb9385d50703f884 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 2 Jul 2016 17:36:46 +0200 Subject: [PATCH 28/94] Extra page number check for issue #276 --- app/Http/Controllers/AccountController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 28a8aa3e25..f5381b4512 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -103,7 +103,7 @@ class AccountController extends Controller $typeName = config('firefly.shortNamesByFullName.' . $type); $name = $account->name; $moveTo = $crud->find(intval(Input::get('move_account_before_delete'))); - + $crud->destroy($account, $moveTo); Session::flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name]))); @@ -271,6 +271,7 @@ class AccountController extends Controller $end = Navigation::endOfPeriod($carbon, $range); $subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')'; $page = intval(Input::get('page')); + $page = $page === 0 ? 1 : $page; $pageSize = Preferences::get('transactionPageSize', 50)->data; $offset = ($page - 1) * $pageSize; $set = $repository->journalsInPeriod(new Collection([$account]), [], $start, $end); From 275d19e71d9eb230c3a0c34d0cc1253e3b38c128 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 2 Jul 2016 17:39:58 +0200 Subject: [PATCH 29/94] Fix #266 for all-chart. --- app/Http/Controllers/Chart/CategoryController.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 9d1d579bce..e4fd807e0d 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -13,8 +13,10 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; +use FireflyIII\Crud\Account\AccountCrudInterface; use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\AccountType; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; use FireflyIII\Support\CacheProperties; @@ -48,12 +50,13 @@ class CategoryController extends Controller /** * Show an overview for a category for all time, per month/week/year. * - * @param CRI $repository - * @param Category $category + * @param CRI $repository + * @param AccountCrudInterface $crud + * @param Category $category * * @return \Symfony\Component\HttpFoundation\Response */ - public function all(CRI $repository, Category $category) + public function all(CRI $repository, AccountCrudInterface $crud, Category $category) { $start = $repository->firstUseDate($category, new Collection); $range = Preferences::get('viewRange', '1M')->data; @@ -62,6 +65,7 @@ class CategoryController extends Controller $end = new Carbon; $entries = new Collection; $cache = new CacheProperties; + $accounts = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('all'); @@ -72,8 +76,8 @@ class CategoryController extends Controller while ($start <= $end) { $currentEnd = Navigation::endOfPeriod($start, $range); - $spent = $repository->spentInPeriod($categoryCollection, new Collection, $start, $currentEnd); - $earned = $repository->earnedInPeriod($categoryCollection, new Collection, $start, $currentEnd); + $spent = $repository->spentInPeriod($categoryCollection, $accounts, $start, $currentEnd); + $earned = $repository->earnedInPeriod($categoryCollection, $accounts, $start, $currentEnd); $date = Navigation::periodShow($start, $range); $entries->push([clone $start, $date, $spent, $earned]); $start = Navigation::addPeriod($start, $range, 0); From 57b5981904f16468deadb2086aaa7e0aa446ae2d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 2 Jul 2016 17:42:27 +0200 Subject: [PATCH 30/94] Fix #266 for period-chart. --- app/Http/Controllers/Chart/CategoryController.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index e4fd807e0d..b585c0d1d2 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -254,18 +254,24 @@ class CategoryController extends Controller { $categoryCollection = new Collection([$category]); $cache = new CacheProperties; + /** @var AccountCrudInterface $crud */ + $crud = app(AccountCrudInterface::class); + $accounts = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + $cache->addProperty($start); $cache->addProperty($end); + $cache->addProperty($accounts); $cache->addProperty($category->id); $cache->addProperty('specific-period'); + if ($cache->has()) { return $cache->get(); } $entries = new Collection; while ($start <= $end) { - $spent = $repository->spentInPeriod($categoryCollection, new Collection, $start, $start); - $earned = $repository->earnedInPeriod($categoryCollection, new Collection, $start, $start); + $spent = $repository->spentInPeriod($categoryCollection, $accounts, $start, $start); + $earned = $repository->earnedInPeriod($categoryCollection, $accounts, $start, $start); $date = Navigation::periodShow($start, '1D'); $entries->push([clone $start, $date, $spent, $earned]); $start->addDay(); From 162c762973c1814ddab91c0b18d93fb1cb677129 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 2 Jul 2016 20:40:23 +0200 Subject: [PATCH 31/94] First set of data mappers. --- app/Import/Importer/CsvImporter.php | 154 ++++++++++++++------ app/Import/Mapper/AssetAccounts.php | 53 +++++++ app/Import/Mapper/MapperInterface.php | 26 ++++ app/Import/Mapper/OpposingAccounts.php | 57 ++++++++ app/Import/Mapper/TransactionCurrencies.php | 42 ++++++ app/Models/Account.php | 10 +- config/csv.php | 44 +++--- resources/lang/en_US/csv.php | 1 + resources/views/import/csv/map.twig | 84 +++++++++++ 9 files changed, 404 insertions(+), 67 deletions(-) create mode 100644 app/Import/Mapper/AssetAccounts.php create mode 100644 app/Import/Mapper/MapperInterface.php create mode 100644 app/Import/Mapper/OpposingAccounts.php create mode 100644 app/Import/Mapper/TransactionCurrencies.php create mode 100644 resources/views/import/csv/map.twig diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 7a592442a4..3b1f2fc704 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -14,6 +14,7 @@ namespace FireflyIII\Import\Importer; use ExpandedForm; use FireflyIII\Crud\Account\AccountCrud; +use FireflyIII\Import\Mapper\MapperInterface; use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; use Illuminate\Http\Request; @@ -111,54 +112,18 @@ class CsvImporter implements ImporterInterface */ public function getDataForSettings(): array { - $config = $this->job->configuration; - $data = [ - 'columns' => [], - 'columnCount' => 0, - ]; if ($this->doColumnRoles()) { - - // show user column role configuration. - $content = $this->job->uploadFileContents(); - - // create CSV reader. - $reader = Reader::createFromString($content); - $start = $config['has-headers'] ? 1 : 0; - $end = $start + self::EXAMPLE_ROWS; // first X rows - - // collect example data in $data['columns'] - while ($start < $end) { - $row = $reader->fetchOne($start); - foreach ($row as $index => $value) { - $value = trim($value); - if (strlen($value) > 0) { - $data['columns'][$index][] = $value; - } - } - $start++; - $data['columnCount'] = count($row); - } - - // make unique example data - foreach ($data['columns'] as $index => $values) { - $data['columns'][$index] = array_unique($values); - } - - $data['set_roles'] = []; - // collect possible column roles: - $data['available_roles'] = []; - foreach (array_keys(config('csv.import_roles')) as $role) { - $data['available_roles'][$role] = trans('csv.column_' . $role); - } - - $config['column-count'] = $data['columnCount']; - $this->job->configuration = $config; - $this->job->save(); + $data = $this->getDataForColumnRoles(); + + return $data; + } + + if ($this->doColumnMapping()) { + $data = $this->getDataForColumnMapping(); return $data; } - echo 'no settings to do.'; exit; @@ -176,6 +141,11 @@ class CsvImporter implements ImporterInterface if ($this->doColumnRoles()) { return 'import.csv.roles'; } + + if ($this->doColumnMapping()) { + return 'import.csv.map'; + } + echo 'no view for settings'; exit; } @@ -269,6 +239,14 @@ class CsvImporter implements ImporterInterface } } + /** + * @return bool + */ + private function doColumnMapping(): bool + { + return $this->job->configuration['column-mapping-complete'] === false; + } + /** * @return bool */ @@ -276,4 +254,94 @@ class CsvImporter implements ImporterInterface { return $this->job->configuration['column-roles-complete'] === false; } + + /** + * @return array + */ + private function getDataForColumnMapping(): array + { + $config = $this->job->configuration; + $data = []; + + foreach ($config['column-do-mapping'] as $index => $mustBeMapped) { + if ($mustBeMapped) { + $column = $config['column-roles'][$index] ?? '_ignore'; + $canBeMapped = config('csv.import_roles.' . $column . '.mappable'); + if ($canBeMapped) { + $mapperName = '\FireflyIII\Import\Mapper\\' . config('csv.import_roles.' . $column . '.mapper'); + /** @var MapperInterface $mapper */ + $mapper = new $mapperName; + $data[$index] = [ + 'name' => $column, + 'mapper' => $mapperName, + 'options' => $mapper->getMap(), + 'values' => [], + ]; + } + } + } + + + echo '
    ';
    +        var_dump($data);
    +        var_dump($config);
    +
    +
    +        exit;
    +
    +
    +    }
    +
    +    /**
    +     * @return array
    +     */
    +    private function getDataForColumnRoles():array
    +    {
    +        $config = $this->job->configuration;
    +        $data   = [
    +            'columns'     => [],
    +            'columnCount' => 0,
    +        ];
    +
    +        // show user column role configuration.
    +        $content = $this->job->uploadFileContents();
    +
    +        // create CSV reader.
    +        $reader = Reader::createFromString($content);
    +        $start  = $config['has-headers'] ? 1 : 0;
    +        $end    = $start + self::EXAMPLE_ROWS; // first X rows
    +
    +        // collect example data in $data['columns']
    +        while ($start < $end) {
    +            $row = $reader->fetchOne($start);
    +            foreach ($row as $index => $value) {
    +                $value = trim($value);
    +                if (strlen($value) > 0) {
    +                    $data['columns'][$index][] = $value;
    +                }
    +            }
    +            $start++;
    +            $data['columnCount'] = count($row);
    +        }
    +
    +        // make unique example data
    +        foreach ($data['columns'] as $index => $values) {
    +            $data['columns'][$index] = array_unique($values);
    +        }
    +
    +        $data['set_roles'] = [];
    +        // collect possible column roles:
    +        $data['available_roles'] = [];
    +        foreach (array_keys(config('csv.import_roles')) as $role) {
    +            $data['available_roles'][$role] = trans('csv.column_' . $role);
    +        }
    +
    +        $config['column-count']   = $data['columnCount'];
    +        $this->job->configuration = $config;
    +        $this->job->save();
    +
    +        return $data;
    +
    +
    +    }
     }
    \ No newline at end of file
    diff --git a/app/Import/Mapper/AssetAccounts.php b/app/Import/Mapper/AssetAccounts.php
    new file mode 100644
    index 0000000000..fdf0a64daa
    --- /dev/null
    +++ b/app/Import/Mapper/AssetAccounts.php
    @@ -0,0 +1,53 @@
    +getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
    +        $list = [];
    +
    +        /** @var Account $account */
    +        foreach ($set as $account) {
    +            $name = $account->name;
    +            $iban = $account->iban ?? '';
    +            if (strlen($iban) > 0) {
    +                $name .= ' (' . $account->iban . ')';
    +            }
    +            $list[$account->id] = $name;
    +        }
    +
    +        asort($list);
    +
    +        $list = [0 => trans('csv.do_not_map')] + $list;
    +
    +        return $list;
    +
    +    }
    +}
    \ No newline at end of file
    diff --git a/app/Import/Mapper/MapperInterface.php b/app/Import/Mapper/MapperInterface.php
    new file mode 100644
    index 0000000000..c285c9b31f
    --- /dev/null
    +++ b/app/Import/Mapper/MapperInterface.php
    @@ -0,0 +1,26 @@
    +getAccountsByType(
    +            [
    +                AccountType::DEFAULT, AccountType::ASSET,
    +                AccountType::EXPENSE, AccountType::BENEFICIARY,
    +                AccountType::REVENUE
    +            ]);
    +        $list = [];
    +
    +        /** @var Account $account */
    +        foreach ($set as $account) {
    +            $name = $account->name;
    +            $iban = $account->iban ?? '';
    +            if (strlen($iban) > 0) {
    +                $name .= ' (' . $account->iban . ')';
    +            }
    +            $list[$account->id] = $name;
    +        }
    +
    +        asort($list);
    +
    +        $list = [0 => trans('csv.do_not_map')] + $list;
    +
    +        return $list;
    +    }
    +}
    \ No newline at end of file
    diff --git a/app/Import/Mapper/TransactionCurrencies.php b/app/Import/Mapper/TransactionCurrencies.php
    new file mode 100644
    index 0000000000..6f21ebb355
    --- /dev/null
    +++ b/app/Import/Mapper/TransactionCurrencies.php
    @@ -0,0 +1,42 @@
    +id] = $currency->name . ' (' . $currency->code . ')';
    +        }
    +
    +        asort($list);
    +
    +        $list = [0 => trans('csv.do_not_map')] + $list;
    +
    +        return $list;
    +
    +    }
    +}
    \ No newline at end of file
    diff --git a/app/Models/Account.php b/app/Models/Account.php
    index 9070be24e4..fdb7b3c2e9 100644
    --- a/app/Models/Account.php
    +++ b/app/Models/Account.php
    @@ -13,6 +13,8 @@ namespace FireflyIII\Models;
     
     use Auth;
     use Crypt;
    +use FireflyIII\Exceptions\FireflyException;
    +use Illuminate\Contracts\Encryption\DecryptException;
     use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
     use Illuminate\Database\Eloquent\Model;
     use Illuminate\Database\Eloquent\Relations\BelongsTo;
    @@ -183,10 +185,14 @@ class Account extends Model
          */
         public function getIbanAttribute($value): string
         {
    -        if (is_null($value)) {
    +        if (is_null($value) || strlen(strval($value)) === 0) {
                 return '';
             }
    -        $result = Crypt::decrypt($value);
    +        try {
    +            $result = Crypt::decrypt($value);
    +        } catch (DecryptException $e) {
    +            throw new FireflyException('Cannot decrypt value "' . $value . '" for account #' . $this->id);
    +        }
             if (is_null($result)) {
                 return '';
             }
    diff --git a/config/csv.php b/config/csv.php
    index e235133f32..6e0a7edd6f 100644
    --- a/config/csv.php
    +++ b/config/csv.php
    @@ -25,37 +25,37 @@ return [
                 'mappable'  => false,
                 'field'     => 'bill',
                 'converter' => 'BillId',
    -            'mapper'    => 'Bill',
    +            'mapper'    => 'Bills',
             ],
             'bill-name'         => [
                 'mappable'  => true,
                 'converter' => 'BillName',
                 'field'     => 'bill',
    -            'mapper'    => 'Bill',
    +            'mapper'    => 'Bills',
             ],
             'currency-id'       => [
                 'mappable'  => true,
                 'converter' => 'CurrencyId',
                 'field'     => 'currency',
    -            'mapper'    => 'TransactionCurrency'
    +            'mapper'    => 'TransactionCurrencies'
             ],
             'currency-name'     => [
                 'mappable'  => true,
                 'converter' => 'CurrencyName',
                 'field'     => 'currency',
    -            'mapper'    => 'TransactionCurrency'
    +            'mapper'    => 'TransactionCurrencies'
             ],
             'currency-code'     => [
                 'mappable'  => true,
                 'converter' => 'CurrencyCode',
                 'field'     => 'currency',
    -            'mapper'    => 'TransactionCurrency'
    +            'mapper'    => 'TransactionCurrencies'
             ],
             'currency-symbol'   => [
                 'mappable'  => true,
                 'converter' => 'CurrencySymbol',
                 'field'     => 'currency',
    -            'mapper'    => 'TransactionCurrency'
    +            'mapper'    => 'TransactionCurrencies'
             ],
             'description'       => [
                 'mappable'  => false,
    @@ -76,13 +76,13 @@ return [
                 'mappable'  => true,
                 'converter' => 'BudgetId',
                 'field'     => 'budget',
    -            'mapper'    => 'Budget',
    +            'mapper'    => 'Budgets',
             ],
             'budget-name'       => [
                 'mappable'  => true,
                 'converter' => 'BudgetName',
                 'field'     => 'budget',
    -            'mapper'    => 'Budget',
    +            'mapper'    => 'Budgets',
             ],
             'rabo-debet-credit' => [
                 'mappable'  => false,
    @@ -98,73 +98,73 @@ return [
                 'mappable'  => true,
                 'converter' => 'CategoryId',
                 'field'     => 'category',
    -            'mapper'    => 'Category',
    +            'mapper'    => 'Categories',
             ],
             'category-name'     => [
                 'mappable'  => true,
                 'converter' => 'CategoryName',
                 'field'     => 'category',
    -            'mapper'    => 'Category',
    +            'mapper'    => 'Categories',
             ],
             'tags-comma'        => [
                 'mappable'  => true,
                 'field'     => 'tags',
                 'converter' => 'TagsComma',
    -            'mapper'    => 'Tag',
    +            'mapper'    => 'Tags',
             ],
             'tags-space'        => [
                 'mappable'  => true,
                 'field'     => 'tags',
                 'converter' => 'TagsSpace',
    -            'mapper'    => 'Tag',
    +            'mapper'    => 'Tags',
             ],
             'account-id'        => [
                 'mappable'  => true,
    -            'mapper'    => 'AssetAccount',
    +            'mapper'    => 'AssetAccountId',
                 'field'     => 'asset-account-id',
    -            'converter' => 'AccountId'
    +            'converter' => 'AssetAccounts'
             ],
             'account-name'      => [
                 'mappable'  => true,
    -            'mapper'    => 'AssetAccount',
    +            'mapper'    => 'AssetAccountName',
                 'field'     => 'asset-account-name',
    -            'converter' => 'AssetAccountName'
    +            'converter' => 'AssetAccounts'
             ],
             'account-iban'      => [
                 'mappable'  => true,
                 'converter' => 'AssetAccountIban',
                 'field'     => 'asset-account-iban',
    -            'mapper'    => 'AssetAccount'
    +            'mapper'    => 'AssetAccounts'
             ],
             'account-number'      => [
                 'mappable'  => true,
                 'converter' => 'AssetAccountNumber',
                 'field'     => 'asset-account-number',
    -            'mapper'    => 'AssetAccount'
    +            'mapper'    => 'AssetAccounts'
             ],
             'opposing-id'       => [
                 'mappable'  => true,
                 'field'     => 'opposing-account-id',
                 'converter' => 'OpposingAccountId',
    -            'mapper'    => 'AnyAccount',
    +            'mapper'    => 'OpposingAccounts',
             ],
             'opposing-name'     => [
                 'mappable'  => true,
                 'field'     => 'opposing-account-name',
                 'converter' => 'OpposingAccountName',
    -            'mapper'    => 'AnyAccount',
    +            'mapper'    => 'OpposingAccounts',
             ],
             'opposing-iban'     => [
                 'mappable'  => true,
                 'field'     => 'opposing-account-iban',
                 'converter' => 'OpposingAccountIban',
    -            'mapper'    => 'AnyAccount',
    +            'mapper'    => 'OpposingAccounts',
             ],
             'opposing-number'     => [
                 'mappable'  => true,
                 'field'     => 'opposing-account-number',
                 'converter' => 'OpposingAccountNumber',
    -            'mapper'    => 'AnyAccount',
    +            'mapper'    => 'OpposingAccounts',
             ],
             'amount'            => [
                 'mappable'  => false,
    diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php
    index c27e945dce..3f2003ff22 100644
    --- a/resources/lang/en_US/csv.php
    +++ b/resources/lang/en_US/csv.php
    @@ -32,6 +32,7 @@ return [
         'column'                 => 'Column',
         'no_example_data'        => 'No example data available',
         'store_column_roles'     => 'Continue import',
    +    'do_not_map'             => '(do not map)',
     
         'column__ignore'                => '(ignore this column)',
         'column_account-iban'           => 'Asset account (IBAN)',
    diff --git a/resources/views/import/csv/map.twig b/resources/views/import/csv/map.twig
    new file mode 100644
    index 0000000000..bc6a5cea31
    --- /dev/null
    +++ b/resources/views/import/csv/map.twig
    @@ -0,0 +1,84 @@
    +{% extends "./layout/default.twig" %}
    +
    +{% block breadcrumbs %}
    +    {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
    +{% endblock %}
    +
    +{% block content %}
    +
    +
    +    
    +
    +
    +
    +

    {{ trans('csv.map_title') }}

    +
    +
    +

    + {{ trans('csv.map_text') }} +

    +
    +
    + +
    +
    + + + + {# + + {% for index,columnName in map %} + +
    +
    +
    +
    +

    {{ Config.get('csv.roles.'~columnName~'.name') }}

    +
    +
    +
    {{ 'csv_column_name'|_ }}{{ 'csv_column_example'|_ }}{{ 'csv_column_role'|_ }}{{ 'csv_do_map_value'|_ }}{{ trans('csv.column_name') }}{{ trans('csv.column_example') }}{{ trans('csv.column_role') }}{{ trans('csv.do_map_value') }}
    Column #{{ loop.index }}{{ trans('csv.column') }} #{{ loop.index }} {% if data.columns[i]|length == 0 %} - No example data available + {{ trans('csv.no_example_data') }} {% else %} {% for example in data.columns[i] %} {{ example }}
    @@ -52,10 +54,10 @@ {% endif %}
    - {{ Form.select(('role['~index~']'), data.available_roles,data.set_roles[index],{class: 'form-control'}) }} + {{ Form.select(('role['~loop.index0~']'), data.available_roles,data.set_roles[index],{class: 'form-control'}) }} - {# Form.checkbox(('map['~index~']'),1,map[index]) #} + {{ Form.checkbox(('map['~loop.index0~']'),1,map[index]) }}
    + + + + + + + + {% for value in values[index] %} + + + + + {% endfor %} + + + +
    {{ 'csv_field_value'|_ }}{{ 'csv_field_mapped_to'|_ }}
    {{ value }} + {{ Form.select('mapping['~index~']['~value~']',options[index], mapped[index][value], {class: 'form-control'}) }} +
    + + +
    +
    +
    +
    + {% endfor %} + #} + + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    + + +{% endblock %} From ae768a8525c5b71a0c0ce748ea8cff01a793fb05 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 2 Jul 2016 23:08:47 +0200 Subject: [PATCH 32/94] Works up until actual import. --- app/Http/Controllers/ImportController.php | 89 ++++++++++++++++-- app/Http/routes.php | 2 + app/Import/Importer/CsvImporter.php | 105 +++++++++++++++------- app/Import/Mapper/Bills.php | 46 ++++++++++ app/Import/Mapper/Budgets.php | 47 ++++++++++ app/Import/Mapper/Categories.php | 47 ++++++++++ app/Import/Mapper/Tags.php | 46 ++++++++++ config/csv.php | 51 +++++------ resources/views/import/complete.twig | 33 +++++++ resources/views/import/csv/map.twig | 38 ++++++++ resources/views/import/csv/roles.twig | 10 ++- resources/views/import/index.twig | 1 - 12 files changed, 444 insertions(+), 71 deletions(-) create mode 100644 app/Import/Mapper/Bills.php create mode 100644 app/Import/Mapper/Budgets.php create mode 100644 app/Import/Mapper/Categories.php create mode 100644 app/Import/Mapper/Tags.php create mode 100644 resources/views/import/complete.twig diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index c3c1763bc9..589d7a1a5a 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -33,6 +33,27 @@ class ImportController extends Controller View::share('title', trans('firefly.import_data')); } + /** + * This is the last step before the import starts. + * + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + public function complete(ImportJob $job) + { + Log::debug('Now in complete()', ['job' => $job->key]); + if (!$this->jobInCorrectStep($job, 'complete')) { + return $this->redirectToCorrectStep($job); + } + $importer = $this->makeImporter($job); + $subTitle = trans('firefy.import_complete'); + $subTitleIcon = 'fa-star'; + + return view('import.complete', compact('job', 'subTitle', 'subTitleIcon')); + } + /** * This is step 3. * This is the first step in configuring the job. It can only be executed @@ -45,7 +66,10 @@ class ImportController extends Controller */ public function configure(ImportJob $job) { + Log::debug('Now at start of configure()'); if (!$this->jobInCorrectStep($job, 'configure')) { + Log::debug('Job is not in correct state for configure()', ['status' => $job->status]); + return $this->redirectToCorrectStep($job); } @@ -59,6 +83,35 @@ class ImportController extends Controller return view('import.' . $job->file_type . '.configure', compact('data', 'job', 'subTitle', 'subTitleIcon')); + } + + /** + * Generate a JSON file of the job's config and send it to the user. + * + * @param ImportJob $job + * + * @return mixed + */ + public function download(ImportJob $job) + { + Log::debug('Now in download()', ['job' => $job->key]); + $config = $job->configuration; + $config['column-roles-complete'] = false; + $config['column-mapping-complete'] = false; + $result = json_encode($config, JSON_PRETTY_PRINT); + $name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\')); + + return response($result, 200) + ->header('Content-disposition', 'attachment; filename=' . $name) + ->header('Content-Type', 'application/json') + ->header('Content-Description', 'File Transfer') + ->header('Connection', 'Keep-Alive') + ->header('Expires', '0') + ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') + ->header('Pragma', 'public') + ->header('Content-Length', strlen($result)); + + } /** @@ -68,6 +121,7 @@ class ImportController extends Controller */ public function index() { + Log::debug('Now at index'); $subTitle = trans('firefly.import_data_index'); $subTitleIcon = 'fa-home'; $importFileTypes = []; @@ -91,9 +145,11 @@ class ImportController extends Controller */ public function postConfigure(Request $request, ImportJob $job) { + Log::debug('Now in postConfigure()', ['job' => $job->key]); if (!$this->jobInCorrectStep($job, 'process')) { return $this->redirectToCorrectStep($job); } + Log::debug('Continue postConfigure()', ['job' => $job->key]); // actual code $importer = $this->makeImporter($job); @@ -122,6 +178,7 @@ class ImportController extends Controller */ public function postSettings(Request $request, ImportJob $job) { + Log::debug('Now in postSettings()', ['job' => $job->key]); if (!$this->jobInCorrectStep($job, 'store-settings')) { return $this->redirectToCorrectStep($job); } @@ -143,29 +200,32 @@ class ImportController extends Controller */ public function settings(ImportJob $job) { + Log::debug('Now in settings()', ['job' => $job->key]); if (!$this->jobInCorrectStep($job, 'settings')) { + Log::debug('Job should not be in settings()'); + return $this->redirectToCorrectStep($job); } + Log::debug('Continue in settings()'); $importer = $this->makeImporter($job); $subTitle = trans('firefy.settings_for_import'); $subTitleIcon = 'fa-wrench'; // now show settings screen to user. if ($importer->requireUserSettings()) { + Log::debug('Job requires user config.'); $data = $importer->getDataForSettings(); $view = $importer->getViewForSettings(); return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon')); } + Log::debug('Job does NOT require user config.'); + + $job->status = 'settings_complete'; + $job->save(); // if no more settings, save job and continue to process thing. - - - echo 'now in settings (done)'; - exit; - - // actual code - + return redirect(route('import.complete', [$job->key])); // ask the importer for the requested action. // for example pick columns or map data. @@ -182,6 +242,7 @@ class ImportController extends Controller */ public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository) { + Log::debug('Now in upload()'); // create import job: $type = $request->get('import_file_type'); $job = $repository->create($type); @@ -232,6 +293,7 @@ class ImportController extends Controller */ private function jobInCorrectStep(ImportJob $job, string $method): bool { + Log::debug('Now in jobInCorrectStep()', ['job' => $job->key, 'method' => $method]); switch ($method) { case 'configure': case 'process': @@ -241,6 +303,9 @@ class ImportController extends Controller case 'store-settings': return $job->status === 'import_configuration_saved'; break; + case 'complete': + return $job->status === 'settings_complete'; + break; } return false; @@ -272,13 +337,23 @@ class ImportController extends Controller */ private function redirectToCorrectStep(ImportJob $job) { + Log::debug('Now in redirectToCorrectStep()', ['job' => $job->key]); switch ($job->status) { case 'import_status_never_started': + Log::debug('Will redirect to configure()'); + return redirect(route('import.configure', [$job->key])); break; case 'import_configuration_saved': + Log::debug('Will redirect to settings()'); + return redirect(route('import.settings', [$job->key])); break; + case 'settings_complete': + Log::debug('Will redirect to complete()'); + + return redirect(route('import.complete', [$job->key])); + break; } throw new FireflyException('Cannot redirect for job state ' . $job->status); diff --git a/app/Http/routes.php b/app/Http/routes.php index 662fafce7e..b2496d42cd 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -229,6 +229,8 @@ Route::group( Route::post('/import/configure/{importJob}', ['uses' => 'ImportController@postConfigure', 'as' => 'import.process_configuration']); Route::get('/import/settings/{importJob}', ['uses' => 'ImportController@settings', 'as' => 'import.settings']); Route::post('/import/settings/{importJob}', ['uses' => 'ImportController@postSettings', 'as' => 'import.postSettings']); + Route::get('/import/complete/{importJob}', ['uses' => 'ImportController@complete', 'as' => 'import.complete']); + Route::get('/import/download/{importJob}', ['uses' => 'ImportController@download', 'as' => 'import.download']); /** diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 3b1f2fc704..cccfcdde74 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -158,13 +158,16 @@ class CsvImporter implements ImporterInterface */ public function requireUserSettings(): bool { - // does the job have both a 'map' array and a 'columns' array. - $config = $this->job->configuration; - if (isset($config['map']) && isset($config['columns'])) { - return false; - } + Log::debug('doColumnMapping is ' . ($this->doColumnMapping() ? 'true' : 'false')); + Log::debug('doColumnRoles is ' . ($this->doColumnRoles() ? 'true' : 'false')); + if ($this->doColumnMapping() || $this->doColumnRoles()) { + Log::debug('Return true'); - return true; + return true; + } + Log::debug('Return false'); + + return false; } /** @@ -216,25 +219,52 @@ class CsvImporter implements ImporterInterface */ public function storeSettings(Request $request) { - $config = $this->job->configuration; - $count = $config['column-count']; - $all = $request->all(); - $roleSet = 0; - for ($i = 0; $i < $count; $i++) { - $selectedRole = $all['role'][$i] ?? '_ignore'; - $doMapping = isset($all['map'][$i]) && $all['map'][$i] == '1' ? true : false; - if ($selectedRole == '_ignore' && $doMapping === true) { - $doMapping = false; // cannot map ignored columns. + $config = $this->job->configuration; + $all = $request->all(); + if ($request->get('settings') == 'roles') { + $count = $config['column-count']; + + $roleSet = 0; // how many roles have been defined + $mapSet = 0; // how many columns must be mapped + for ($i = 0; $i < $count; $i++) { + $selectedRole = $all['role'][$i] ?? '_ignore'; + $doMapping = isset($all['map'][$i]) && $all['map'][$i] == '1' ? true : false; + if ($selectedRole == '_ignore' && $doMapping === true) { + $doMapping = false; // cannot map ignored columns. + } + if ($selectedRole != '_ignore') { + $roleSet++; + } + if ($doMapping === true) { + $mapSet++; + } + $config['column-roles'][$i] = $selectedRole; + $config['column-do-mapping'][$i] = $doMapping; } - if ($selectedRole != '_ignore') { - $roleSet++; + if ($roleSet > 0) { + $config['column-roles-complete'] = true; + $this->job->configuration = $config; + $this->job->save(); + } + if ($mapSet === 0) { + // skip setting of map: + $config['column-mapping-complete'] = true; } - $config['column-roles'][$i] = $selectedRole; - $config['column-do-mapping'][$i] = $doMapping; } - if ($roleSet > 0) { - $config['column-roles-complete'] = true; - $this->job->configuration = $config; + if ($request->get('settings') == 'map') { + foreach ($all['mapping'] as $index => $data) { + $config['column-mapping-config'][$index] = []; + foreach ($data as $value => $mapId) { + $mapId = intval($mapId); + if ($mapId !== 0) { + $config['column-mapping-config'][$index][$value] = intval($mapId); + } + } + } + + // set thing to be completed. + $config['column-mapping-complete'] = true; + $this->job->configuration = $config; $this->job->save(); } } @@ -260,8 +290,9 @@ class CsvImporter implements ImporterInterface */ private function getDataForColumnMapping(): array { - $config = $this->job->configuration; - $data = []; + $config = $this->job->configuration; + $data = []; + $indexes = []; foreach ($config['column-do-mapping'] as $index => $mustBeMapped) { if ($mustBeMapped) { @@ -271,9 +302,11 @@ class CsvImporter implements ImporterInterface $mapperName = '\FireflyIII\Import\Mapper\\' . config('csv.import_roles.' . $column . '.mapper'); /** @var MapperInterface $mapper */ $mapper = new $mapperName; + $indexes[] = $index; $data[$index] = [ 'name' => $column, 'mapper' => $mapperName, + 'index' => $index, 'options' => $mapper->getMap(), 'values' => [], ]; @@ -281,15 +314,25 @@ class CsvImporter implements ImporterInterface } } + // in order to actually map we also need all possible values from the CSV file. + $content = $this->job->uploadFileContents(); + $reader = Reader::createFromString($content); + $results = $reader->fetch(); - echo '
    ';
    -        var_dump($data);
    -        var_dump($config);
    -
    -
    -        exit;
    -
    +        foreach ($results as $row) {
    +            //do something here
    +            foreach ($indexes as $index) {
    +                $value = $row[$index];
    +                if (strlen($value) > 0) {
    +                    $data[$index]['values'][] = $row[$index];
    +                }
    +            }
    +        }
    +        foreach ($data as $index => $entry) {
    +            $data[$index]['values'] = array_unique($data[$index]['values']);
    +        }
     
    +        return $data;
         }
     
         /**
    diff --git a/app/Import/Mapper/Bills.php b/app/Import/Mapper/Bills.php
    new file mode 100644
    index 0000000000..0dcdc06f10
    --- /dev/null
    +++ b/app/Import/Mapper/Bills.php
    @@ -0,0 +1,46 @@
    +getBills();
    +        $list       = [];
    +
    +        /** @var Bill $bill */
    +        foreach ($result as $bill) {
    +            $list[$bill->id] = $bill->name . ' [' . $bill->match . ']';
    +        }
    +        asort($list);
    +
    +        $list = [0 => trans('csv.do_not_map')] + $list;
    +
    +        return $list;
    +
    +    }
    +}
    \ No newline at end of file
    diff --git a/app/Import/Mapper/Budgets.php b/app/Import/Mapper/Budgets.php
    new file mode 100644
    index 0000000000..a1b9c2edd8
    --- /dev/null
    +++ b/app/Import/Mapper/Budgets.php
    @@ -0,0 +1,47 @@
    +getBudgets();
    +        $list       = [];
    +
    +        /** @var Budget $budget */
    +        foreach ($result as $budget) {
    +            $list[$budget->id] = $budget->name;
    +        }
    +        asort($list);
    +
    +        $list = [0 => trans('csv.do_not_map')] + $list;
    +
    +        return $list;
    +
    +    }
    +}
    \ No newline at end of file
    diff --git a/app/Import/Mapper/Categories.php b/app/Import/Mapper/Categories.php
    new file mode 100644
    index 0000000000..0f85392c8f
    --- /dev/null
    +++ b/app/Import/Mapper/Categories.php
    @@ -0,0 +1,47 @@
    +getCategories();
    +        $list       = [];
    +
    +        /** @var Category $category */
    +        foreach ($result as $category) {
    +            $list[$category->id] = $category->name;
    +        }
    +        asort($list);
    +
    +        $list = [0 => trans('csv.do_not_map')] + $list;
    +
    +        return $list;
    +
    +    }
    +}
    \ No newline at end of file
    diff --git a/app/Import/Mapper/Tags.php b/app/Import/Mapper/Tags.php
    new file mode 100644
    index 0000000000..4f608f2146
    --- /dev/null
    +++ b/app/Import/Mapper/Tags.php
    @@ -0,0 +1,46 @@
    +get();
    +        $list       = [];
    +
    +        /** @var Tag $tag */
    +        foreach ($result as $tag) {
    +            $list[$tag->id] = $tag->tag;
    +        }
    +        asort($list);
    +
    +        $list = [0 => trans('csv.do_not_map')] + $list;
    +
    +        return $list;
    +
    +    }
    +}
    \ No newline at end of file
    diff --git a/config/csv.php b/config/csv.php
    index 6e0a7edd6f..3481ca2769 100644
    --- a/config/csv.php
    +++ b/config/csv.php
    @@ -15,11 +15,12 @@ return [
         /*
          * Configuration for possible column roles.
          */
    -    'import_roles' => [
    +    'import_roles'     => [
             '_ignore'           => [
                 'mappable'  => false,
    -            'converter' => 'Ignore',
                 'field'     => 'ignored',
    +            'converter' => 'Ignore',
    +
             ],
             'bill-id'           => [
                 'mappable'  => false,
    @@ -29,33 +30,33 @@ return [
             ],
             'bill-name'         => [
                 'mappable'  => true,
    -            'converter' => 'BillName',
                 'field'     => 'bill',
    +            'converter' => 'BillName',
                 'mapper'    => 'Bills',
             ],
             'currency-id'       => [
                 'mappable'  => true,
    -            'converter' => 'CurrencyId',
                 'field'     => 'currency',
    -            'mapper'    => 'TransactionCurrencies'
    +            'converter' => 'CurrencyId',
    +            'mapper'    => 'TransactionCurrencies',
             ],
             'currency-name'     => [
                 'mappable'  => true,
                 'converter' => 'CurrencyName',
                 'field'     => 'currency',
    -            'mapper'    => 'TransactionCurrencies'
    +            'mapper'    => 'TransactionCurrencies',
             ],
             'currency-code'     => [
                 'mappable'  => true,
                 'converter' => 'CurrencyCode',
                 'field'     => 'currency',
    -            'mapper'    => 'TransactionCurrencies'
    +            'mapper'    => 'TransactionCurrencies',
             ],
             'currency-symbol'   => [
                 'mappable'  => true,
                 'converter' => 'CurrencySymbol',
                 'field'     => 'currency',
    -            'mapper'    => 'TransactionCurrencies'
    +            'mapper'    => 'TransactionCurrencies',
             ],
             'description'       => [
                 'mappable'  => false,
    @@ -89,7 +90,7 @@ return [
                 'converter' => 'RabobankDebetCredit',
                 'field'     => 'amount-modifier',
             ],
    -        'ing-debet-credit' => [
    +        'ing-debet-credit'  => [
                 'mappable'  => false,
                 'converter' => 'INGDebetCredit',
                 'field'     => 'amount-modifier',
    @@ -120,27 +121,28 @@ return [
             ],
             'account-id'        => [
                 'mappable'  => true,
    -            'mapper'    => 'AssetAccountId',
                 'field'     => 'asset-account-id',
    -            'converter' => 'AssetAccounts'
    +            'converter' => 'AssetAccountId',
    +            'mapper'    => 'AssetAccounts',
             ],
             'account-name'      => [
                 'mappable'  => true,
    -            'mapper'    => 'AssetAccountName',
                 'field'     => 'asset-account-name',
    -            'converter' => 'AssetAccounts'
    +            'converter' => 'AssetAccountName',
    +            'mapper'    => 'AssetAccounts',
             ],
             'account-iban'      => [
                 'mappable'  => true,
    -            'converter' => 'AssetAccountIban',
                 'field'     => 'asset-account-iban',
    -            'mapper'    => 'AssetAccounts'
    +            'converter' => 'AssetAccountIban',
    +            'mapper'    => 'AssetAccounts',
    +
             ],
    -        'account-number'      => [
    +        'account-number'    => [
                 'mappable'  => true,
    -            'converter' => 'AssetAccountNumber',
                 'field'     => 'asset-account-number',
    -            'mapper'    => 'AssetAccounts'
    +            'converter' => 'AssetAccountNumber',
    +            'mapper'    => 'AssetAccounts',
             ],
             'opposing-id'       => [
                 'mappable'  => true,
    @@ -160,7 +162,7 @@ return [
                 'converter' => 'OpposingAccountIban',
                 'mapper'    => 'OpposingAccounts',
             ],
    -        'opposing-number'     => [
    +        'opposing-number'   => [
                 'mappable'  => true,
                 'field'     => 'opposing-account-number',
                 'converter' => 'OpposingAccountNumber',
    @@ -171,11 +173,6 @@ return [
                 'converter' => 'Amount',
                 'field'     => 'amount',
             ],
    -//        'amount-comma-separated' => [
    -//            'mappable'  => false,
    -//            'converter' => 'AmountComma',
    -//            'field'     => 'amount',
    -//        ],
             'sepa-ct-id'        => [
                 'mappable'  => false,
                 'converter' => 'Description',
    @@ -194,12 +191,6 @@ return [
         ],
     
     
    -
    -
    -
    -
    -
    -
         /*
     
     
    diff --git a/resources/views/import/complete.twig b/resources/views/import/complete.twig
    new file mode 100644
    index 0000000000..0a7aaaff7b
    --- /dev/null
    +++ b/resources/views/import/complete.twig
    @@ -0,0 +1,33 @@
    +{% extends "./layout/default.twig" %}
    +
    +{% block breadcrumbs %}
    +    {{ Breadcrumbs.renderIfExists }}
    +{% endblock %}
    +{% block content %}
    +    
    +
    +
    +
    +

    {{ 'import_complete'|_ }}

    +
    +
    +

    + {{ 'import_complete_text'|_ }} +

    + +
    +
    +
    +
    +{% endblock %} +{% block scripts %} +{% endblock %} +{% block styles %} +{% endblock %} diff --git a/resources/views/import/csv/map.twig b/resources/views/import/csv/map.twig index bc6a5cea31..7119e382aa 100644 --- a/resources/views/import/csv/map.twig +++ b/resources/views/import/csv/map.twig @@ -24,6 +24,44 @@
    + + + {% for field in data %} +
    +
    +
    +
    +

    {{ field.name }}

    +
    +
    + + + + + + + + + {% for option in field.values %} + + + + + {% endfor %} + +
    {{ trans('csv.field_value') }}{{ trans('csv.field_mapped_to') }}
    + {{ option }} + + {{ Form.select('mapping['~field.index~']['~option~']', + field.options, + job.configuration['column-mapping-config'][field.index][option], {class: 'form-control'}) }} +
    +
    +
    +
    +
    + {% endfor %} + {# diff --git a/resources/views/import/csv/roles.twig b/resources/views/import/csv/roles.twig index c1f317cbbf..e73f172e43 100644 --- a/resources/views/import/csv/roles.twig +++ b/resources/views/import/csv/roles.twig @@ -54,10 +54,16 @@ {% endif %} - {{ Form.select(('role['~loop.index0~']'), data.available_roles,data.set_roles[index],{class: 'form-control'}) }} + {{ Form.select(('role['~loop.index0~']'), + data.available_roles, + job.configuration['column-roles'][loop.index0], + {class: 'form-control'}) }} - {{ Form.checkbox(('map['~loop.index0~']'),1,map[index]) }} + {{ Form.checkbox(('map['~loop.index0~']'),1, + job.configuration['column-do-mapping'][loop.index0] + + ) }} diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index b69d953026..b378cfe026 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -6,7 +6,6 @@ {% block content %}
    -

    {{ 'import'|_ }}

    From 92449942332b504b047d5103883b0d0149a229c8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 4 Jul 2016 12:37:33 +0200 Subject: [PATCH 33/94] Fix bug that would report wrong file size to browser. --- app/Http/Controllers/AttachmentController.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 94763e9327..903b2602e8 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -18,6 +18,7 @@ use FireflyIII\Http\Requests\AttachmentFormRequest; use FireflyIII\Models\Attachment; use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; use Input; +use Log; use Preferences; use Response; use Session; @@ -93,9 +94,11 @@ class AttachmentController extends Controller if ($disk->exists($file)) { $quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\')); + $content = Crypt::decrypt($disk->get($file)); + Log::debug('Send file to user', ['file' => $quoted, 'size' => strlen($content)]); - return response(Crypt::decrypt($disk->get($file)), 200) + return response($content, 200) ->header('Content-Description', 'File Transfer') ->header('Content-Type', 'application/octet-stream') ->header('Content-Disposition', 'attachment; filename=' . $quoted) @@ -104,7 +107,7 @@ class AttachmentController extends Controller ->header('Expires', '0') ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') ->header('Pragma', 'public') - ->header('Content-Length', $disk->size($file)); + ->header('Content-Length', strlen($content)); } throw new FireflyException('Could not find the indicated attachment. The file is no longer there.'); From f1e8d1cf1e96a8940cb0f4bbdeb87b847c0df110 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 5 Jul 2016 08:57:45 +0200 Subject: [PATCH 34/94] Show bill average. --- app/Http/Controllers/BillController.php | 14 ++++-- app/Repositories/Bill/BillRepository.php | 48 +++++++++++++++++++ .../Bill/BillRepositoryInterface.php | 15 ++++++ resources/lang/en_US/firefly.php | 2 + resources/views/bills/show.twig | 8 ++++ 5 files changed, 83 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index f1c9cb2db4..47ca757b4d 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -11,6 +11,7 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; +use Carbon\Carbon; use FireflyIII\Http\Requests\BillFormRequest; use FireflyIII\Models\Bill; use FireflyIII\Models\TransactionJournal; @@ -190,15 +191,20 @@ class BillController extends Controller */ public function show(BillRepositoryInterface $repository, Bill $bill) { - $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $journals = $repository->getJournals($bill, $page, $pageSize); + /** @var Carbon $date */ + $date = session('start'); + $year = $date->year; + $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); + $pageSize = Preferences::get('transactionPageSize', 50)->data; + $journals = $repository->getJournals($bill, $page, $pageSize); + $yearAverage = $repository->getYearAverage($bill, $date); + $overallAverage = $repository->getOverallAverage($bill); $journals->setPath('/bills/show/' . $bill->id); $bill->nextExpectedMatch = $repository->nextExpectedMatch($bill); $hideBill = true; $subTitle = e($bill->name); - return view('bills.show', compact('journals', 'hideBill', 'bill', 'subTitle')); + return view('bills.show', compact('journals', 'yearAverage', 'overallAverage', 'year', 'hideBill', 'bill', 'subTitle')); } /** diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 7a6c99375d..43aabe96cf 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -557,4 +557,52 @@ class BillRepository implements BillRepositoryInterface return $wordMatch; } + + /** + * @param Bill $bill + * @param Carbon $date + * + * @return string + */ + public function getYearAverage(Bill $bill, Carbon $date): string + { + $journals = $bill->transactionjournals() + ->where('date', '>=', $date->year . '-01-01') + ->where('date', '<=', $date->year . '-12-31') + ->get(); + $sum = '0'; + $count = strval($journals->count()); + /** @var TransactionJournal $journal */ + foreach ($journals as $journal) { + $sum = bcadd($sum, TransactionJournal::amountPositive($journal)); + } + $avg = '0'; + if ($journals->count() > 0) { + $avg = bcdiv($sum, $count); + } + + return $avg; + } + + /** + * @param $bill + * + * @return string + */ + public function getOverallAverage($bill): string + { + $journals = $bill->transactionjournals()->get(); + $sum = '0'; + $count = strval($journals->count()); + /** @var TransactionJournal $journal */ + foreach ($journals as $journal) { + $sum = bcadd($sum, TransactionJournal::amountPositive($journal)); + } + $avg = '0'; + if ($journals->count() > 0) { + $avg = bcdiv($sum, $count); + } + + return $avg; + } } diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index 3305e26d68..494b4d2d7f 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -32,6 +32,21 @@ interface BillRepositoryInterface */ public function destroy(Bill $bill): bool; + /** + * @param Bill $bill + * @param Carbon $date + * + * @return string + */ + public function getYearAverage(Bill $bill, Carbon $date): string; + + /** + * @param $bill + * + * @return string + */ + public function getOverallAverage($bill): string; + /** * Find a bill by ID. * diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 077c75cb33..ecb93831af 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -415,6 +415,8 @@ return [ 'cannot_scan_inactive_bill' => 'Inactive bills cannot be scanned.', 'rescanned_bill' => 'Rescanned everything.', 'bill_date_little_relevance' => 'The only part of this date used by Firefly is the day. It is only useful when your bill arrives at exactly the same date every month. If the payment date of your bills varies, simply use the first of the month.', + 'average_bill_amount_year' => 'Average bill amount (:year)', + 'average_bill_amount_overall' => 'Average bill amount (overall)', // accounts: 'details_for_asset' => 'Details for asset account ":name"', diff --git a/resources/views/bills/show.twig b/resources/views/bills/show.twig index ab5303ab6e..45cf8cddb1 100644 --- a/resources/views/bills/show.twig +++ b/resources/views/bills/show.twig @@ -60,6 +60,14 @@ {% endif %} + + {{ trans('firefly.average_bill_amount_year', {year: year}) }} + {{ '0'|formatAmount }} + + + {{ 'average_bill_amount_overall'|_ }} + {{ '0'|formatAmount }} +
    From f5b3dc36e30407b1d434bdc3298fea1b73d82320 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 5 Jul 2016 09:02:01 +0200 Subject: [PATCH 35/94] Fix view. --- resources/views/bills/show.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/bills/show.twig b/resources/views/bills/show.twig index 45cf8cddb1..85dd232568 100644 --- a/resources/views/bills/show.twig +++ b/resources/views/bills/show.twig @@ -62,11 +62,11 @@ {{ trans('firefly.average_bill_amount_year', {year: year}) }} - {{ '0'|formatAmount }} + {{ yearAverage|formatAmount }} {{ 'average_bill_amount_overall'|_ }} - {{ '0'|formatAmount }} + {{ overallAverage|formatAmount }}
    From c9e46a4dd190f6424289fa44716fa93555c1d225 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jul 2016 22:26:08 +0200 Subject: [PATCH 36/94] Lots of import related code. --- app/Console/Commands/Import.php | 85 +++++++++++++++++++ .../Commands/UpgradeFireflyInstructions.php | 2 +- app/Console/Kernel.php | 2 + app/Import/Converter/AssetAccountIban.php | 48 +++++++++++ app/Import/Converter/BasicConverter.php | 57 +++++++++++++ app/Import/Converter/ConverterInterface.php | 43 ++++++++++ app/Import/Converter/SomeConverter.php | 31 +++++++ app/Import/ImportEntry.php | 41 +++++++++ app/Import/Importer/CsvImporter.php | 68 +++++++++++++++ app/Import/Importer/ImporterInterface.php | 8 ++ app/Import/Logging/CommandHandler.php | 49 +++++++++++ app/Import/Mapper/AssetAccountIbans.php | 56 ++++++++++++ app/Import/Mapper/OpposingAccountIbans.php | 61 +++++++++++++ config/csv.php | 4 +- resources/lang/en_US/csv.php | 9 ++ resources/lang/en_US/firefly.php | 4 + resources/views/import/complete.twig | 11 ++- resources/views/import/csv/configure.twig | 69 ++++++++------- resources/views/import/csv/map.twig | 2 +- 19 files changed, 609 insertions(+), 41 deletions(-) create mode 100644 app/Console/Commands/Import.php create mode 100644 app/Import/Converter/AssetAccountIban.php create mode 100644 app/Import/Converter/BasicConverter.php create mode 100644 app/Import/Converter/ConverterInterface.php create mode 100644 app/Import/Converter/SomeConverter.php create mode 100644 app/Import/ImportEntry.php create mode 100644 app/Import/Logging/CommandHandler.php create mode 100644 app/Import/Mapper/AssetAccountIbans.php create mode 100644 app/Import/Mapper/OpposingAccountIbans.php diff --git a/app/Console/Commands/Import.php b/app/Console/Commands/Import.php new file mode 100644 index 0000000000..8a139083df --- /dev/null +++ b/app/Console/Commands/Import.php @@ -0,0 +1,85 @@ +argument('key'); + $job = ImportJob::whereKey($jobKey)->first(); + if (is_null($job)) { + $this->error('This job does not seem to exist.'); + + return; + } + + if ($job->status != 'settings_complete') { + $this->error('This job is not ready to be imported.'); + + return; + } + + $this->line('Going to import job with key "' . $job->key . '" of type ' . $job->file_type); + $class = config('firefly.import_formats.' . $job->file_type); + /** @var ImporterInterface $importer */ + $importer = app($class); + $importer->setJob($job); + // intercept logging by importer. + $monolog = Log::getMonolog(); + $handler = new CommandHandler($this); + $monolog->pushHandler($handler); + $importer->start(); + + + $this->line('Something something import: ' . $jobKey); + } +} diff --git a/app/Console/Commands/UpgradeFireflyInstructions.php b/app/Console/Commands/UpgradeFireflyInstructions.php index 4535d17a7f..7881f87646 100644 --- a/app/Console/Commands/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/UpgradeFireflyInstructions.php @@ -25,7 +25,7 @@ class UpgradeFireflyInstructions extends Command * * @var string */ - protected $description = 'Command description'; + protected $description = 'Instructions in case of upgrade trouble.'; /** * The name and signature of the console command. * diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index ea6b228eea..5e9147a429 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -11,6 +11,7 @@ declare(strict_types = 1); namespace FireflyIII\Console; +use FireflyIII\Console\Commands\Import; use FireflyIII\Console\Commands\UpgradeFireflyInstructions; use FireflyIII\Console\Commands\VerifyDatabase; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; @@ -51,5 +52,6 @@ class Kernel extends ConsoleKernel = [ UpgradeFireflyInstructions::class, VerifyDatabase::class, + Import::class, ]; } diff --git a/app/Import/Converter/AssetAccountIban.php b/app/Import/Converter/AssetAccountIban.php new file mode 100644 index 0000000000..a0fcd9e40d --- /dev/null +++ b/app/Import/Converter/AssetAccountIban.php @@ -0,0 +1,48 @@ +user]); + + + if (isset($this->mapping[$value])) { + Log::debug('Found account in mapping. Should exist.',['value' => $value]); + $account = $repository->find(intval($value)); + Log::debug('Found account ', ['id' => $account->id]); + + } + + Log::debug('Given map is ', $this->mapping); + + exit; + } +} \ No newline at end of file diff --git a/app/Import/Converter/BasicConverter.php b/app/Import/Converter/BasicConverter.php new file mode 100644 index 0000000000..29e5c70924 --- /dev/null +++ b/app/Import/Converter/BasicConverter.php @@ -0,0 +1,57 @@ +doMap = $doMap; + } + + /** + * @param array $mapping + * + */ + public function setMapping(array $mapping) + { + $this->mapping = $mapping; + } + + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } +} \ No newline at end of file diff --git a/app/Import/Converter/ConverterInterface.php b/app/Import/Converter/ConverterInterface.php new file mode 100644 index 0000000000..c0d9b187fe --- /dev/null +++ b/app/Import/Converter/ConverterInterface.php @@ -0,0 +1,43 @@ +job = $job; } + /** + * Run the actual import + * + * @return bool + */ + public function start(): bool + { + $config = $this->job->configuration; + $content = $this->job->uploadFileContents(); + + // create CSV reader. + $reader = Reader::createFromString($content); + $start = $config['has-headers'] ? 1 : 0; + $results = $reader->fetch(); + foreach ($results as $index => $row) { + if ($index >= $start) { + Log::debug(sprintf('Now going to import row %d.', $index)); + $this->importSingleRow($row); + } + } + + Log::debug('This call should be intercepted somehow.'); + + return true; + } + /** * Store the settings filled in by the user, if applicable. * @@ -387,4 +415,44 @@ class CsvImporter implements ImporterInterface } + + /** + * @param array $row + * + * @return bool + */ + private function importSingleRow(array $row): bool + { + $object = new ImportEntry; + $config = $this->job->configuration; + + foreach ($row as $index => $value) { + // find the role for this column: + $role = $config['column-roles'][$index] ?? '_ignore'; + $doMap = $config['column-do-mapping'][$index] ?? false; + $converterClass = config('csv.import_roles.' . $role . '.converter'); + $mapping = $config['column-mapping-config'][$index] ?? []; + /** @var ConverterInterface $converter */ + $converter = app('FireflyIII\\Import\\Converter\\' . $converterClass); + // set some useful values for the converter: + $converter->setMapping($mapping); + $converter->setDoMap($doMap); + $converter->setUser($this->job->user); + + // run the converter for this value: + $convertedValue = $converter->convert($value); + + // log it. + Log::debug('Value ', ['index' => $index, 'value' => $value, 'role' => $role]); + + // store in import entry: + // $object->fromRawValue($role, $value); + + + } + + exit; + + return true; + } } \ No newline at end of file diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php index 6e332da8fe..66b72a6f08 100644 --- a/app/Import/Importer/ImporterInterface.php +++ b/app/Import/Importer/ImporterInterface.php @@ -23,6 +23,14 @@ use Symfony\Component\HttpFoundation\FileBag; */ interface ImporterInterface { + + /** + * Run the actual import + * + * @return bool + */ + public function start(): bool; + /** * After uploading, and after setJob(), prepare anything that is * necessary for the configure() line. diff --git a/app/Import/Logging/CommandHandler.php b/app/Import/Logging/CommandHandler.php new file mode 100644 index 0000000000..6b4d3106c1 --- /dev/null +++ b/app/Import/Logging/CommandHandler.php @@ -0,0 +1,49 @@ +command = $command; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @param array $record + * + * @return void + */ + protected function write(array $record) + { + $this->command->line((string) $record['formatted']); + } +} \ No newline at end of file diff --git a/app/Import/Mapper/AssetAccountIbans.php b/app/Import/Mapper/AssetAccountIbans.php new file mode 100644 index 0000000000..d3a4c246e6 --- /dev/null +++ b/app/Import/Mapper/AssetAccountIbans.php @@ -0,0 +1,56 @@ +getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + $topList = []; + $list = []; + + /** @var Account $account */ + foreach ($set as $account) { + $iban = $account->iban ?? ''; + if (strlen($iban) > 0) { + $topList[$account->id] = $account->iban . ' (' . $account->name . ')'; + } + if (strlen($iban) == 0) { + $list[$account->id] = $account->name; + } + } + asort($topList); + asort($list); + + $list = $topList + $list; + $list = [0 => trans('csv.do_not_map')] + $list; + + return $list; + + } +} \ No newline at end of file diff --git a/app/Import/Mapper/OpposingAccountIbans.php b/app/Import/Mapper/OpposingAccountIbans.php new file mode 100644 index 0000000000..f788345483 --- /dev/null +++ b/app/Import/Mapper/OpposingAccountIbans.php @@ -0,0 +1,61 @@ +getAccountsByType( + [ + AccountType::DEFAULT, AccountType::ASSET, + AccountType::EXPENSE, AccountType::BENEFICIARY, + AccountType::REVENUE + ]); + $topList = []; + $list = []; + + /** @var Account $account */ + foreach ($set as $account) { + $iban = $account->iban ?? ''; + if (strlen($iban) > 0) { + $topList[$account->id] = $account->iban . ' (' . $account->name . ')'; + } + if (strlen($iban) == 0) { + $list[$account->id] = $account->name; + } + } + asort($topList); + asort($list); + + $list = $topList + $list; + $list = [0 => trans('csv.do_not_map')] + $list; + + + return $list; + } +} \ No newline at end of file diff --git a/config/csv.php b/config/csv.php index 3481ca2769..20fb48a91d 100644 --- a/config/csv.php +++ b/config/csv.php @@ -135,7 +135,7 @@ return [ 'mappable' => true, 'field' => 'asset-account-iban', 'converter' => 'AssetAccountIban', - 'mapper' => 'AssetAccounts', + 'mapper' => 'AssetAccountIbans', ], 'account-number' => [ @@ -160,7 +160,7 @@ return [ 'mappable' => true, 'field' => 'opposing-account-iban', 'converter' => 'OpposingAccountIban', - 'mapper' => 'OpposingAccounts', + 'mapper' => 'OpposingAccountIbans', ], 'opposing-number' => [ 'mappable' => true, diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index 3f2003ff22..ae51cf1f68 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -33,6 +33,15 @@ return [ 'no_example_data' => 'No example data available', 'store_column_roles' => 'Continue import', 'do_not_map' => '(do not map)', + 'map_title' => 'Connect data in your files', + 'map_text' => 'Connect data in your files', + + 'field_value' => 'Field value', + 'field_mapped_to' => 'Mapped to', + 'store_column_mapping' => 'Store mapping', + + // map things. + 'column__ignore' => '(ignore this column)', 'column_account-iban' => 'Asset account (IBAN)', diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index ecb93831af..c7982f10b1 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -759,4 +759,8 @@ return [ 'configure_import' => 'Further configure your import', 'import_finish_configuration' => 'Finish configuration', 'settings_for_import' => 'Settings', + 'import_complete' => 'Import configuration complete!', + 'import_complete_text' => 'Download the config file. You can also run it from the command line.', + 'import_download_config' => 'Download configuration', + 'import_start_import' => 'Start import', ]; diff --git a/resources/views/import/complete.twig b/resources/views/import/complete.twig index 0a7aaaff7b..e7e7a82629 100644 --- a/resources/views/import/complete.twig +++ b/resources/views/import/complete.twig @@ -14,12 +14,15 @@

    {{ 'import_complete_text'|_ }}

    +

    + php artisan firefly:import {{ job.key }} +

    - diff --git a/resources/views/import/csv/configure.twig b/resources/views/import/csv/configure.twig index 0cbab054f3..fd3949e778 100644 --- a/resources/views/import/csv/configure.twig +++ b/resources/views/import/csv/configure.twig @@ -32,44 +32,47 @@

    {{ trans('csv.import_configure_form') }}

    +
    +
    - {{ ExpandedForm.checkbox('has_headers',1,job.configuration['has-headers'],{helpText: trans('csv.header_help')}) }} - {{ ExpandedForm.text('date_format',job.configuration['date-format'],{helpText: trans('csv.date_help', {dateExample: phpdate('Ymd')}) }) }} - {{ ExpandedForm.select('csv_delimiter', data.delimiters, job.configuration['delimiter'], {helpText: trans('csv.delimiter_help') } ) }} - {{ ExpandedForm.select('csv_import_account', data.accounts, 0, {helpText: trans('csv.import_account_help')} ) }} + {{ ExpandedForm.checkbox('has_headers',1,job.configuration['has-headers'],{helpText: trans('csv.header_help')}) }} + {{ ExpandedForm.text('date_format',job.configuration['date-format'],{helpText: trans('csv.date_help', {dateExample: phpdate('Ymd')}) }) }} + {{ ExpandedForm.select('csv_delimiter', data.delimiters, job.configuration['delimiter'], {helpText: trans('csv.delimiter_help') } ) }} + {{ ExpandedForm.select('csv_import_account', data.accounts, 0, {helpText: trans('csv.import_account_help')} ) }} - {% for type, specific in data.specifics %} -
    - - -
    -
    + {% endfor %} + + {% if not data.is_upload_possible %} +
    +
    +   +
    + +
    +
    {{ data.upload_path }}
    +

    + {{ trans('csv.upload_not_writeable') }} +

    +
    +
    + {% endif %}
    - {% endfor %} - - {% if not data.is_upload_possible %} -
    -
    -   -
    - -
    -
    {{ data.upload_path }}
    -

    - {{ trans('csv.upload_not_writeable') }} -

    -
    -
    - {% endif %} - +
    diff --git a/resources/views/import/csv/map.twig b/resources/views/import/csv/map.twig index 7119e382aa..5f21f1a9d0 100644 --- a/resources/views/import/csv/map.twig +++ b/resources/views/import/csv/map.twig @@ -31,7 +31,7 @@
    -

    {{ field.name }}

    +

    {{ trans('csv.column_'~field.name) }}

    From 5130ba7ea457ace5bbd46b31d890fb618d153b09 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Jul 2016 22:37:47 +0200 Subject: [PATCH 37/94] Working IBAN account import thing. --- app/Crud/Account/AccountCrud.php | 18 +++++++++++++++ app/Crud/Account/AccountCrudInterface.php | 7 ++++++ app/Import/Converter/AssetAccountIban.php | 28 ++++++++++++++++++----- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/app/Crud/Account/AccountCrud.php b/app/Crud/Account/AccountCrud.php index c7ffb509df..e9c4046b2f 100644 --- a/app/Crud/Account/AccountCrud.php +++ b/app/Crud/Account/AccountCrud.php @@ -82,6 +82,24 @@ class AccountCrud implements AccountCrudInterface return $account; } + /** + * @param string $iban + * + * @return Account + */ + public function findByIban(string $iban): Account + { + $accounts = $this->user->accounts()->where('iban', '!=', "")->get(); + /** @var Account $account */ + foreach ($accounts as $account) { + if ($account->iban === $iban) { + return $account; + } + } + + return new Account; + } + /** * @param array $accountIds * diff --git a/app/Crud/Account/AccountCrudInterface.php b/app/Crud/Account/AccountCrudInterface.php index a325e3e6d2..8d3ac26634 100644 --- a/app/Crud/Account/AccountCrudInterface.php +++ b/app/Crud/Account/AccountCrudInterface.php @@ -37,6 +37,13 @@ interface AccountCrudInterface */ public function find(int $accountId): Account; + /** + * @param string $iban + * + * @return Account + */ + public function findByIban(string $iban): Account; + /** * @param array $accountIds * diff --git a/app/Import/Converter/AssetAccountIban.php b/app/Import/Converter/AssetAccountIban.php index a0fcd9e40d..f469a8ef92 100644 --- a/app/Import/Converter/AssetAccountIban.php +++ b/app/Import/Converter/AssetAccountIban.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; use FireflyIII\Crud\Account\AccountCrudInterface; +use FireflyIII\Models\Account; use Log; /** @@ -25,8 +26,9 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface /** * @param $value * + * @return Account */ - public function convert($value) + public function convert($value): Account { Log::debug('Going to convert value ' . $value); @@ -35,14 +37,28 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface if (isset($this->mapping[$value])) { - Log::debug('Found account in mapping. Should exist.',['value' => $value]); - $account = $repository->find(intval($value)); - Log::debug('Found account ', ['id' => $account->id]); + Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $account = $repository->find(intval($this->mapping[$value])); + if (!is_null($account->id)) { + Log::debug('Found account by ID', ['id' => $account->id]); + return $account; + } } - Log::debug('Given map is ', $this->mapping); + // not mapped? Still try to find it first: + $account = $repository->findByIban($value); + if (!is_null($account->id)) { + Log::debug('Found account by IBAN', ['id' => $account->id]); - exit; + return $account; + } + + + $account = $repository->store( + ['name' => $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, 'active' => true] + ); + + return $account; } } \ No newline at end of file From 697566fe42d533971331ed86b2d012a92212c177 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Jul 2016 07:58:25 +0200 Subject: [PATCH 38/94] New importers. --- app/Crud/Account/AccountCrud.php | 38 ++++++- app/Crud/Account/AccountCrudInterface.php | 11 +- app/Import/Converter/Amount.php | 63 +++++++++++ app/Import/Converter/AssetAccountIban.php | 10 +- app/Import/Converter/AssetAccountId.php | 34 ++++++ app/Import/Converter/AssetAccountName.php | 34 ++++++ app/Import/Converter/BasicConverter.php | 12 ++- app/Import/Converter/ConverterInterface.php | 6 ++ app/Import/Converter/CurrencyCode.php | 65 ++++++++++++ app/Import/Converter/Date.php | 48 +++++++++ app/Import/Converter/Description.php | 36 +++++++ app/Import/Converter/Ignore.php | 32 ++++++ app/Import/Converter/OpposingAccountIban.php | 106 +++++++++++++++++++ app/Import/Converter/OpposingAccountName.php | 74 +++++++++++++ app/Import/Converter/RabobankDebetCredit.php | 39 +++++++ app/Import/Importer/CsvImporter.php | 1 + config/firefly.php | 13 ++- 17 files changed, 611 insertions(+), 11 deletions(-) create mode 100644 app/Import/Converter/Amount.php create mode 100644 app/Import/Converter/AssetAccountId.php create mode 100644 app/Import/Converter/AssetAccountName.php create mode 100644 app/Import/Converter/CurrencyCode.php create mode 100644 app/Import/Converter/Date.php create mode 100644 app/Import/Converter/Description.php create mode 100644 app/Import/Converter/Ignore.php create mode 100644 app/Import/Converter/OpposingAccountIban.php create mode 100644 app/Import/Converter/OpposingAccountName.php create mode 100644 app/Import/Converter/RabobankDebetCredit.php diff --git a/app/Crud/Account/AccountCrud.php b/app/Crud/Account/AccountCrud.php index e9c4046b2f..11958fba4b 100644 --- a/app/Crud/Account/AccountCrud.php +++ b/app/Crud/Account/AccountCrud.php @@ -84,12 +84,20 @@ class AccountCrud implements AccountCrudInterface /** * @param string $iban + * @param array $types * * @return Account */ - public function findByIban(string $iban): Account + public function findByIban(string $iban, array $types): Account { - $accounts = $this->user->accounts()->where('iban', '!=', "")->get(); + $query = $this->user->accounts()->where('iban', '!=', ""); + + if (count($types) > 0) { + $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); + $query->whereIn('account_types.type', $types); + } + + $accounts = $query->get(); /** @var Account $account */ foreach ($accounts as $account) { if ($account->iban === $iban) { @@ -100,6 +108,32 @@ class AccountCrud implements AccountCrudInterface return new Account; } + /** + * @param string $name + * @param array $types + * + * @return Account + */ + public function findByName(string $name, array $types): Account + { + $query = $this->user->accounts()->where('iban', '!=', ""); + + if (count($types) > 0) { + $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); + $query->whereIn('account_types.type', $types); + } + + $accounts = $query->get(); + /** @var Account $account */ + foreach ($accounts as $account) { + if ($account->name === $name) { + return $account; + } + } + + return new Account; + } + /** * @param array $accountIds * diff --git a/app/Crud/Account/AccountCrudInterface.php b/app/Crud/Account/AccountCrudInterface.php index 8d3ac26634..1b11bdbbb0 100644 --- a/app/Crud/Account/AccountCrudInterface.php +++ b/app/Crud/Account/AccountCrudInterface.php @@ -39,10 +39,19 @@ interface AccountCrudInterface /** * @param string $iban + * @param array $types * * @return Account */ - public function findByIban(string $iban): Account; + public function findByIban(string $iban, array $types): Account; + + /** + * @param string $name + * @param array $types + * + * @return Account + */ + public function findByName(string $name, array $types): Account; /** * @param array $accountIds diff --git a/app/Import/Converter/Amount.php b/app/Import/Converter/Amount.php new file mode 100644 index 0000000000..274f9b5457 --- /dev/null +++ b/app/Import/Converter/Amount.php @@ -0,0 +1,63 @@ + 2 && $value{$decimalPosition} == '.') { + $decimal = '.'; + } + if ($len > 2 && $value{$decimalPosition} == ',') { + $decimal = ','; + } + + // if decimal is dot, replace all comma's and spaces with nothing. then parse as float (round to 4 pos) + if ($decimal === '.') { + $search = [',', ' ']; + $value = str_replace($search, '', $value); + } + if ($decimal === ',') { + $search = ['.', ' ']; + $value = str_replace($search, '', $value); + $value = str_replace(',', '.', $value); + } + if (is_null($decimal)) { + // replace all: + $search = ['.', ' ', ',']; + $value = str_replace($search, '', $value); + } + + + return round(floatval($value), 4); + + } +} \ No newline at end of file diff --git a/app/Import/Converter/AssetAccountIban.php b/app/Import/Converter/AssetAccountIban.php index f469a8ef92..e903312a63 100644 --- a/app/Import/Converter/AssetAccountIban.php +++ b/app/Import/Converter/AssetAccountIban.php @@ -13,6 +13,7 @@ namespace FireflyIII\Import\Converter; use FireflyIII\Crud\Account\AccountCrudInterface; use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; use Log; /** @@ -30,7 +31,12 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface */ public function convert($value): Account { - Log::debug('Going to convert value ' . $value); + $value = trim($value); + Log::debug('Going to convert ', ['value' => $value]); + + if (strlen($value) === 0) { + return new Account; + } /** @var AccountCrudInterface $repository */ $repository = app(AccountCrudInterface::class, [$this->user]); @@ -47,7 +53,7 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface } // not mapped? Still try to find it first: - $account = $repository->findByIban($value); + $account = $repository->findByIban($value, [AccountType::ASSET]); if (!is_null($account->id)) { Log::debug('Found account by IBAN', ['id' => $account->id]); diff --git a/app/Import/Converter/AssetAccountId.php b/app/Import/Converter/AssetAccountId.php new file mode 100644 index 0000000000..b335e8e621 --- /dev/null +++ b/app/Import/Converter/AssetAccountId.php @@ -0,0 +1,34 @@ +config = $config; + } + /** * @param mixed $doMap */ diff --git a/app/Import/Converter/ConverterInterface.php b/app/Import/Converter/ConverterInterface.php index c0d9b187fe..87e8fc5d62 100644 --- a/app/Import/Converter/ConverterInterface.php +++ b/app/Import/Converter/ConverterInterface.php @@ -10,6 +10,7 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; + use FireflyIII\User; /** @@ -25,6 +26,11 @@ interface ConverterInterface */ public function convert($value); + /** + * @param array $config + */ + public function setConfig(array $config); + /** * @param bool $doMap */ diff --git a/app/Import/Converter/CurrencyCode.php b/app/Import/Converter/CurrencyCode.php new file mode 100644 index 0000000000..3ac41ce074 --- /dev/null +++ b/app/Import/Converter/CurrencyCode.php @@ -0,0 +1,65 @@ + $value]); + + /** @var CurrencyRepositoryInterface $repository */ + $repository = app(CurrencyRepositoryInterface::class); + + if (isset($this->mapping[$value])) { + Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $currency = $repository->find(intval($this->mapping[$value])); + if (!is_null($currency->id)) { + Log::debug('Found currency by ID', ['id' => $currency->id]); + + return $currency; + } + } + + // not mapped? Still try to find it first: + $currency = $repository->findByCode($value); + if (!is_null($currency->id)) { + Log::debug('Found currency by code', ['id' => $currency->id]); + + return $currency; + } + $currency = $repository->store( + [ + 'name' => $value, + 'code' => $value, + 'symbol' => $value, + ] + ); + + return $currency; + } +} \ No newline at end of file diff --git a/app/Import/Converter/Date.php b/app/Import/Converter/Date.php new file mode 100644 index 0000000000..c9e9676b6f --- /dev/null +++ b/app/Import/Converter/Date.php @@ -0,0 +1,48 @@ + $value]); + Log::debug('Format: ', ['format' => $this->config['date-format']]); + try { + $date = Carbon::createFromFormat($this->config['date-format'], $value); + } catch (InvalidArgumentException $e) { + Log::critical($e->getMessage()); + Log::critical('Cannot convert this string using the given format.', ['value' => $value, 'format' => $this->config['date-format']]); + throw new FireflyException(sprintf('Cannot convert "%s" to a valid date using format "%s".', $value, $this->config['date-format'])); + } + Log::debug('Converted date', ['converted' => $date->toAtomString()]); + + return $date; + } +} \ No newline at end of file diff --git a/app/Import/Converter/Description.php b/app/Import/Converter/Description.php new file mode 100644 index 0000000000..18b90ee92e --- /dev/null +++ b/app/Import/Converter/Description.php @@ -0,0 +1,36 @@ + $value]); + + if (strlen($value) === 0) { + return new Account; + } + + /** @var AccountCrudInterface $repository */ + $repository = app(AccountCrudInterface::class, [$this->user]); + + + if (isset($this->mapping[$value])) { + Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $account = $repository->find(intval($this->mapping[$value])); + if (!is_null($account->id)) { + Log::debug('Found account by ID', ['id' => $account->id]); + + return $account; + } + } + + // not mapped? Still try to find it first: + $account = $repository->findByIban($value, []); + if (!is_null($account->id)) { + Log::debug('Found account by IBAN', ['id' => $account->id]); + Log::warning( + 'The match between IBAN and account is uncertain because the type of transactions may not have been determined.', + ['id' => $account->id, 'iban' => $value] + ); + + return $account; + } + + $account = $repository->store( + ['name' => $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'import', 'virtualBalance' => 0, 'active' => true, + 'openingBalance' => 0 $value]); + + if (strlen($value) === 0) { + $value = '(empty account name)'; + } + + /** @var AccountCrudInterface $repository */ + $repository = app(AccountCrudInterface::class, [$this->user]); + + + if (isset($this->mapping[$value])) { + Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $account = $repository->find(intval($this->mapping[$value])); + if (!is_null($account->id)) { + Log::debug('Found account by ID', ['id' => $account->id]); + + return $account; + } + } + + // not mapped? Still try to find it first: + $account = $repository->findByName($value, []); + if (!is_null($account->id)) { + Log::debug('Found account by name', ['id' => $account->id]); + Log::warning( + 'The match between name and account is uncertain because the type of transactions may not have been determined.', + ['id' => $account->id, 'name' => $value] + ); + + return $account; + } + + $account = $repository->store( + ['name' => $value, 'iban' => null, 'user' => $this->user->id, 'accountType' => 'import', 'virtualBalance' => 0, 'active' => true, + 'openingBalance' => 0, + ] + ); + + return $account; + } +} \ No newline at end of file diff --git a/app/Import/Converter/RabobankDebetCredit.php b/app/Import/Converter/RabobankDebetCredit.php new file mode 100644 index 0000000000..ca450b5b76 --- /dev/null +++ b/app/Import/Converter/RabobankDebetCredit.php @@ -0,0 +1,39 @@ + $value]); + + if ($value === 'D') { + return -1; + } + + return 1; + } +} \ No newline at end of file diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index ec60721ef8..beea9b5ad6 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -438,6 +438,7 @@ class CsvImporter implements ImporterInterface $converter->setMapping($mapping); $converter->setDoMap($doMap); $converter->setUser($this->job->user); + $converter->setConfig($config); // run the converter for this value: $convertedValue = $converter->convert($value); diff --git a/config/firefly.php b/config/firefly.php index f764212203..a053f95031 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -60,12 +60,15 @@ return [ 'Beneficiary account' => 'fa-shopping-cart', 'revenue' => 'fa-download', 'Revenue account' => 'fa-download', + 'import' => 'fa-download', + 'Import account' => 'fa-download', ], 'accountTypesByIdentifier' => [ 'asset' => ['Default account', 'Asset account'], 'expense' => ['Expense account', 'Beneficiary account'], 'revenue' => ['Revenue account'], + 'import' => ['Import account'], ], 'accountTypeByIdentifier' => [ @@ -74,11 +77,13 @@ return [ 'revenue' => 'Revenue account', 'opening' => 'Initial balance account', 'initial' => 'Initial balance account', + 'import' => 'Import account', ], 'shortNamesByFullName' => [ 'Default account' => 'asset', 'Asset account' => 'asset', + 'Import account' => 'import', 'Expense account' => 'expense', 'Beneficiary account' => 'expense', 'Revenue account' => 'revenue', @@ -136,7 +141,7 @@ return [ 'end_date' => 'FireflyIII\Support\Binder\Date', ], - 'rule-triggers' => [ + 'rule-triggers' => [ 'user_action' => 'FireflyIII\Rules\Triggers\UserAction', 'from_account_starts' => 'FireflyIII\Rules\Triggers\FromAccountStarts', 'from_account_ends' => 'FireflyIII\Rules\Triggers\FromAccountEnds', @@ -155,7 +160,7 @@ return [ 'description_contains' => 'FireflyIII\Rules\Triggers\DescriptionContains', 'description_is' => 'FireflyIII\Rules\Triggers\DescriptionIs', ], - 'rule-actions' => [ + 'rule-actions' => [ 'set_category' => 'FireflyIII\Rules\Actions\SetCategory', 'clear_category' => 'FireflyIII\Rules\Actions\ClearCategory', 'set_budget' => 'FireflyIII\Rules\Actions\SetBudget', @@ -168,7 +173,7 @@ return [ 'prepend_description' => 'FireflyIII\Rules\Actions\PrependDescription', ], // all rule actions that require text input: - 'rule-actions-text' => [ + 'rule-actions-text' => [ 'set_category', 'set_budget', 'add_tag', @@ -177,7 +182,7 @@ return [ 'append_description', 'prepend_description', ], - 'test-triggers' => [ + 'test-triggers' => [ // The maximum number of transactions shown when testing a list of triggers 'limit' => 10, From 3b686b6d1cc821c9e8318d63d43e13f6733f35b1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Jul 2016 08:25:39 +0200 Subject: [PATCH 39/94] New but empty converters. --- app/Import/Converter/AssetAccountName.php | 2 +- app/Import/Converter/AssetAccountNumber.php | 34 +++++++++++++++++++ app/Import/Converter/BillId.php | 34 +++++++++++++++++++ app/Import/Converter/BillName.php | 34 +++++++++++++++++++ app/Import/Converter/BudgetId.php | 34 +++++++++++++++++++ app/Import/Converter/BudgetName.php | 34 +++++++++++++++++++ app/Import/Converter/CategoryId.php | 34 +++++++++++++++++++ app/Import/Converter/CategoryName.php | 34 +++++++++++++++++++ app/Import/Converter/CurrencyId.php | 34 +++++++++++++++++++ app/Import/Converter/CurrencyName.php | 34 +++++++++++++++++++ app/Import/Converter/CurrencySymbol.php | 34 +++++++++++++++++++ app/Import/Converter/INGDebetCredit.php | 34 +++++++++++++++++++ app/Import/Converter/OpposingAccountId.php | 34 +++++++++++++++++++ .../Converter/OpposingAccountNumber.php | 34 +++++++++++++++++++ app/Import/Converter/TagsComma.php | 34 +++++++++++++++++++ app/Import/Converter/TagsSpace.php | 34 +++++++++++++++++++ 16 files changed, 511 insertions(+), 1 deletion(-) create mode 100644 app/Import/Converter/AssetAccountNumber.php create mode 100644 app/Import/Converter/BillId.php create mode 100644 app/Import/Converter/BillName.php create mode 100644 app/Import/Converter/BudgetId.php create mode 100644 app/Import/Converter/BudgetName.php create mode 100644 app/Import/Converter/CategoryId.php create mode 100644 app/Import/Converter/CategoryName.php create mode 100644 app/Import/Converter/CurrencyId.php create mode 100644 app/Import/Converter/CurrencyName.php create mode 100644 app/Import/Converter/CurrencySymbol.php create mode 100644 app/Import/Converter/INGDebetCredit.php create mode 100644 app/Import/Converter/OpposingAccountId.php create mode 100644 app/Import/Converter/OpposingAccountNumber.php create mode 100644 app/Import/Converter/TagsComma.php create mode 100644 app/Import/Converter/TagsSpace.php diff --git a/app/Import/Converter/AssetAccountName.php b/app/Import/Converter/AssetAccountName.php index 785d0ad24f..ba5789cd31 100644 --- a/app/Import/Converter/AssetAccountName.php +++ b/app/Import/Converter/AssetAccountName.php @@ -28,7 +28,7 @@ class AssetAccountName extends BasicConverter implements ConverterInterface */ public function convert($value) { - throw new FireflyException('Importer with name AssetAccountId has not yet been configured.'); + throw new FireflyException('Importer with name AssetAccountName has not yet been configured.'); } } \ No newline at end of file diff --git a/app/Import/Converter/AssetAccountNumber.php b/app/Import/Converter/AssetAccountNumber.php new file mode 100644 index 0000000000..d991c3ff14 --- /dev/null +++ b/app/Import/Converter/AssetAccountNumber.php @@ -0,0 +1,34 @@ + Date: Sun, 17 Jul 2016 08:35:04 +0200 Subject: [PATCH 40/94] No map for tags. --- config/csv.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/csv.php b/config/csv.php index 20fb48a91d..707a34b8c4 100644 --- a/config/csv.php +++ b/config/csv.php @@ -108,13 +108,13 @@ return [ 'mapper' => 'Categories', ], 'tags-comma' => [ - 'mappable' => true, + 'mappable' => false, 'field' => 'tags', 'converter' => 'TagsComma', 'mapper' => 'Tags', ], 'tags-space' => [ - 'mappable' => true, + 'mappable' => false, 'field' => 'tags', 'converter' => 'TagsSpace', 'mapper' => 'Tags', From cb9433f4b91b8ad6d8cecb33c379d420ee0110a8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jul 2016 08:50:22 +0200 Subject: [PATCH 41/94] This should fix #280 --- .../Transaction/MassController.php | 68 +++++++++++++------ resources/lang/en_US/firefly.php | 3 + resources/views/transactions/mass-edit.twig | 17 +++-- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index a8b0ccc636..c402817eaf 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -110,13 +110,40 @@ class MassController extends Controller $crud = app('FireflyIII\Crud\Account\AccountCrudInterface'); $accountList = ExpandedForm::makeSelectList($crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])); + // skip transactions that have multiple destinations + // or multiple sources: + $filtered = new Collection; + $messages = []; + /** + * @var int $index + * @var TransactionJournal $journal + */ + foreach ($journals as $index => $journal) { + $sources = TransactionJournal::sourceAccountList($journal); + $destinations = TransactionJournal::destinationAccountList($journal); + if ($sources->count() > 1) { + $messages[] = trans('firefly.cannot_edit_multiple_source', ['description' => $journal->description, 'id' => $journal->id]); + continue; + } + + if ($destinations->count() > 1) { + $messages[] = trans('firefly.cannot_edit_multiple_dest', ['description' => $journal->description, 'id' => $journal->id]); + continue; + } + $filtered->push($journal); + } + + if (count($messages)) { + Session::flash('info', $messages); + } + // put previous url in session Session::put('transactions.mass-edit.url', URL::previous()); Session::flash('gaEventCategory', 'transactions'); Session::flash('gaEventAction', 'mass-edit'); // set some values to be used in the edit routine: - $journals->each( + $filtered->each( function (TransactionJournal $journal) { $journal->amount = TransactionJournal::amountPositive($journal); $sources = TransactionJournal::sourceAccountList($journal); @@ -133,6 +160,12 @@ class MassController extends Controller } ); + if ($filtered->count() === 0) { + Session::flash('error', trans('firefly.no_edit_multiple_left')); + } + + $journals = $filtered; + return view('transactions.mass-edit', compact('journals', 'subTitle', 'accountList')); } @@ -151,33 +184,26 @@ class MassController extends Controller $journal = $repository->find(intval($journalId)); if ($journal) { // get optional fields: - $what = strtolower(TransactionJournal::transactionTypeStr($journal)); - $sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0; - $destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0; - $expenseAccount = $request->get('expense_account')[$journal->id] ?? ''; - $revenueAccount = $request->get('revenue_account')[$journal->id] ?? ''; - $budgetId = $journal->budgets->first() ? $journal->budgets->first()->id : 0; - $category = $journal->categories->first() ? $journal->categories->first()->name : ''; - $tags = $journal->tags->pluck('tag')->toArray(); + $what = strtolower(TransactionJournal::transactionTypeStr($journal)); - // for a deposit, the 'account_id' is the account the money is deposited on. - // needs a better way of handling. - // more uniform source/destination field names - $accountId = $sourceAccountId; - if ($what == 'deposit') { - $accountId = $destAccountId; - } + $sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0; + $sourceAccountName = $request->get('source_account_name')[$journal->id] ?? ''; + $destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0; + $destAccountName = $request->get('destination_account_name')[$journal->id] ?? ''; + + $budgetId = $journal->budgets->first() ? $journal->budgets->first()->id : 0; + $category = $journal->categories->first() ? $journal->categories->first()->name : ''; + $tags = $journal->tags->pluck('tag')->toArray(); // build data array $data = [ 'id' => $journal->id, 'what' => $what, 'description' => $request->get('description')[$journal->id], - 'account_id' => intval($accountId), - 'account_from_id' => intval($sourceAccountId), - 'account_to_id' => intval($destAccountId), - 'expense_account' => $expenseAccount, - 'revenue_account' => $revenueAccount, + 'source_account_id' => intval($sourceAccountId), + 'source_account_name' => intval($destAccountId), + 'destination_account_id' => $sourceAccountName, + 'destination_account_name' => $destAccountName, 'amount' => round($request->get('amount')[$journal->id], 4), 'user' => Auth::user()->id, 'amount_currency_id_amount' => intval($request->get('amount_currency_id_amount_' . $journal->id)), diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index c7982f10b1..ccffccc358 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -749,6 +749,9 @@ return [ 'split_this_withdrawal' => 'Split this withdrawal', 'split_this_deposit' => 'Split this deposit', 'split_this_transfer' => 'Split this transfer', + 'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.', + 'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.', + 'no_edit_multiple_left' => 'You have selected no valid transactions to edit.', // import 'configuration_file_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you.', diff --git a/resources/views/transactions/mass-edit.twig b/resources/views/transactions/mass-edit.twig index 45fe55359d..ddeb20afa9 100644 --- a/resources/views/transactions/mass-edit.twig +++ b/resources/views/transactions/mass-edit.twig @@ -56,24 +56,24 @@ @@ -83,8 +83,11 @@
    - + {% if journal.transaction_type_type == 'Transfer' or journal.transaction_type_type == 'Withdrawal' %} {{ Form.select('source_account_id['~journal.id~']', accountList, journal.source_account_id, {'class': 'form-control'}) }} {% else %} - - {{ Form.input('text', 'revenue_account['~journal.id~']', journal.source_account_name, {'class': 'form-control', 'placeholder': trans('form.revenue_account')}) }} + + {{ Form.input('text', 'source_account_name['~journal.id~']', journal.source_account_name, {'class': 'form-control', 'placeholder': trans('form.revenue_account')}) }} {% endif %} {% if journal.transaction_type_type == 'Transfer' or journal.transaction_type_type == 'Deposit' %} - + {{ Form.select('destination_account_id['~journal.id~']', accountList, journal.destination_account_id, {'class': 'form-control'}) }} {% else %} - + - {{ Form.input('text', 'expense_account['~journal.id~']', journal.destination_account_name, {'class': 'form-control', 'placeholder': trans('form.expense_account')}) }} + {{ Form.input('text', 'destination_account_name['~journal.id~']', journal.destination_account_name, {'class': 'form-control', 'placeholder': trans('form.expense_account')}) }} {% endif %}
    From 921e2c06f4467e3843644b04fad585a38b0179bf Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jul 2016 15:47:30 +0200 Subject: [PATCH 42/94] Beta warning for import thing. --- resources/lang/en_US/firefly.php | 1 + resources/views/import/index.twig | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index ccffccc358..1861ae752b 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -766,4 +766,5 @@ return [ 'import_complete_text' => 'Download the config file. You can also run it from the command line.', 'import_download_config' => 'Download configuration', 'import_start_import' => 'Start import', + 'import_intro_beta' => 'The import function is currently being developed and will most probably not work.', ]; diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index b378cfe026..0627280b9f 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -14,8 +14,9 @@

    {{ 'import_intro_text'|_ }}

    -

    -   + +

    +  {{ 'import_intro_beta'|_ }}

    Date: Wed, 20 Jul 2016 15:57:42 +0200 Subject: [PATCH 43/94] Fixed import error. --- app/Import/Importer/CsvImporter.php | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index beea9b5ad6..db29c244cb 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -280,12 +280,14 @@ class CsvImporter implements ImporterInterface } } if ($request->get('settings') == 'map') { - foreach ($all['mapping'] as $index => $data) { - $config['column-mapping-config'][$index] = []; - foreach ($data as $value => $mapId) { - $mapId = intval($mapId); - if ($mapId !== 0) { - $config['column-mapping-config'][$index][$value] = intval($mapId); + if (isset($all['mapping'])) { + foreach ($all['mapping'] as $index => $data) { + $config['column-mapping-config'][$index] = []; + foreach ($data as $value => $mapId) { + $mapId = intval($mapId); + if ($mapId !== 0) { + $config['column-mapping-config'][$index][$value] = intval($mapId); + } } } } @@ -302,7 +304,16 @@ class CsvImporter implements ImporterInterface */ private function doColumnMapping(): bool { - return $this->job->configuration['column-mapping-complete'] === false; + $mapArray = $this->job->configuration['column-do-mapping'] ?? []; + $doMap = false; + foreach ($mapArray as $value) { + if ($value === true) { + $doMap = true; + break; + } + } + + return $this->job->configuration['column-mapping-complete'] === false && $doMap; } /** From 87c0f1d86e63c1e7cf56b1a4a62e7334afdcca12 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 23 Jul 2016 21:37:06 +0200 Subject: [PATCH 44/94] More CSV related updates. --- app/Crud/Account/AccountCrud.php | 28 ++ app/Crud/Account/AccountCrudInterface.php | 8 + app/Http/Controllers/ImportController.php | 2 +- app/Import/Converter/AssetAccountIban.php | 3 +- app/Import/Converter/AssetAccountId.php | 38 ++- app/Import/Converter/AssetAccountName.php | 45 ++- app/Import/Converter/AssetAccountNumber.php | 43 ++- app/Import/Converter/BillId.php | 36 ++- app/Import/Converter/BillName.php | 51 +++- app/Import/Converter/OpposingAccountIban.php | 35 +-- app/Import/Importer/CsvImporter.php | 56 +++- .../PreProcessorInterface.php | 28 ++ app/Import/MapperPreProcess/TagsComma.php | 31 ++ app/Import/MapperPreProcess/TagsSpace.php | 30 ++ app/Providers/AccountServiceProvider.php | 2 +- app/Providers/AttachmentServiceProvider.php | 2 +- app/Providers/BillServiceProvider.php | 2 +- app/Providers/BudgetServiceProvider.php | 2 +- app/Providers/CategoryServiceProvider.php | 2 +- app/Providers/CrudServiceProvider.php | 10 +- app/Providers/ExportJobServiceProvider.php | 4 +- app/Providers/JournalServiceProvider.php | 2 +- app/Providers/PiggyBankServiceProvider.php | 2 +- app/Providers/RuleGroupServiceProvider.php | 2 +- app/Providers/RuleServiceProvider.php | 2 +- app/Providers/TagServiceProvider.php | 2 +- app/Repositories/Bill/BillRepository.php | 117 ++++--- .../Bill/BillRepositoryInterface.php | 39 ++- config/app.php | 33 +- config/csv.php | 288 +++++++++++------- resources/views/import/csv/configure.twig | 2 +- 31 files changed, 684 insertions(+), 263 deletions(-) create mode 100644 app/Import/MapperPreProcess/PreProcessorInterface.php create mode 100644 app/Import/MapperPreProcess/TagsComma.php create mode 100644 app/Import/MapperPreProcess/TagsSpace.php diff --git a/app/Crud/Account/AccountCrud.php b/app/Crud/Account/AccountCrud.php index 11958fba4b..682b8bc162 100644 --- a/app/Crud/Account/AccountCrud.php +++ b/app/Crud/Account/AccountCrud.php @@ -74,6 +74,7 @@ class AccountCrud implements AccountCrudInterface */ public function find(int $accountId): Account { + Log::debug('Searching for user ', ['user' => $this->user->id]); $account = $this->user->accounts()->find($accountId); if (is_null($account)) { return new Account; @@ -82,6 +83,33 @@ class AccountCrud implements AccountCrudInterface return $account; } + /** + * @param string $number + * @param array $types + * + * @return Account + */ + public function findByAccountNumber(string $number, array $types): Account + { + $query = $this->user->accounts() + ->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id') + ->where('account_meta.name', 'accountNumber') + ->where('account_meta.data', json_encode($number)); + + if (count($types) > 0) { + $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); + $query->whereIn('account_types.type', $types); + } + + /** @var Collection $accounts */ + $accounts = $query->get(); + if ($accounts->count() > 0) { + return $accounts->first(); + } + + return new Account; + } + /** * @param string $iban * @param array $types diff --git a/app/Crud/Account/AccountCrudInterface.php b/app/Crud/Account/AccountCrudInterface.php index 1b11bdbbb0..41b6956cfa 100644 --- a/app/Crud/Account/AccountCrudInterface.php +++ b/app/Crud/Account/AccountCrudInterface.php @@ -53,6 +53,14 @@ interface AccountCrudInterface */ public function findByName(string $name, array $types): Account; + /** + * @param string $number + * @param array $types + * + * @return Account + */ + public function findByAccountNumber(string $number, array $types): Account; + /** * @param array $accountIds * diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 589d7a1a5a..40a636deaf 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -117,7 +117,7 @@ class ImportController extends Controller /** * This is step 1. Upload a file. * - * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @return View */ public function index() { diff --git a/app/Import/Converter/AssetAccountIban.php b/app/Import/Converter/AssetAccountIban.php index e903312a63..acc1389335 100644 --- a/app/Import/Converter/AssetAccountIban.php +++ b/app/Import/Converter/AssetAccountIban.php @@ -62,7 +62,8 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface $account = $repository->store( - ['name' => $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, 'active' => true] + ['name' => 'Account with IBAN ' . $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, + 'active' => true] ); return $account; diff --git a/app/Import/Converter/AssetAccountId.php b/app/Import/Converter/AssetAccountId.php index b335e8e621..f055f819c7 100644 --- a/app/Import/Converter/AssetAccountId.php +++ b/app/Import/Converter/AssetAccountId.php @@ -11,7 +11,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Crud\Account\AccountCrudInterface; +use FireflyIII\Models\Account; +use Log; /** * Class AssetAccountId @@ -24,11 +26,41 @@ class AssetAccountId extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return Account */ public function convert($value) { - throw new FireflyException('Importer with name AssetAccountId has not yet been configured.'); + $value = intval(trim($value)); + Log::debug('Going to convert using AssetAccountId', ['value' => $value]); + + if ($value === 0) { + return new Account; + } + + /** @var AccountCrudInterface $repository */ + $repository = app(AccountCrudInterface::class, [$this->user]); + + + if (isset($this->mapping[$value])) { + Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $account = $repository->find(intval($this->mapping[$value])); + if (!is_null($account->id)) { + Log::debug('Found account by ID', ['id' => $account->id]); + + return $account; + } + } + + // not mapped? Still try to find it first: + $account = $repository->find($value); + if (!is_null($account->id)) { + Log::debug('Found account by ID ', ['id' => $account->id]); + + return $account; + } + + // should not really happen. If the ID does not match FF, what is FF supposed to do? + return new Account; } } \ No newline at end of file diff --git a/app/Import/Converter/AssetAccountName.php b/app/Import/Converter/AssetAccountName.php index ba5789cd31..e1b10b222a 100644 --- a/app/Import/Converter/AssetAccountName.php +++ b/app/Import/Converter/AssetAccountName.php @@ -11,7 +11,10 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Crud\Account\AccountCrudInterface; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use Log; /** * Class AssetAccountName @@ -24,11 +27,47 @@ class AssetAccountName extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return Account */ public function convert($value) { - throw new FireflyException('Importer with name AssetAccountName has not yet been configured.'); + $value = trim($value); + Log::debug('Going to convert using AssetAccountName', ['value' => $value]); + + if (strlen($value) === 0) { + return new Account; + } + + /** @var AccountCrudInterface $repository */ + $repository = app(AccountCrudInterface::class, [$this->user]); + + + if (isset($this->mapping[$value])) { + Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $account = $repository->find(intval($this->mapping[$value])); + if (!is_null($account->id)) { + Log::debug('Found account by ID', ['id' => $account->id]); + + return $account; + } + } + + // not mapped? Still try to find it first: + $account = $repository->findByName($value, [AccountType::ASSET]); + if (!is_null($account->id)) { + Log::debug('Found account by name', ['id' => $account->id]); + + return $account; + } + + + $account = $repository->store( + ['name' => $value, 'iban' => null, 'openingBalance' => 0, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, + 'active' => true] + ); + + return $account; + } } \ No newline at end of file diff --git a/app/Import/Converter/AssetAccountNumber.php b/app/Import/Converter/AssetAccountNumber.php index d991c3ff14..d2373e23c5 100644 --- a/app/Import/Converter/AssetAccountNumber.php +++ b/app/Import/Converter/AssetAccountNumber.php @@ -11,7 +11,11 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; +use FireflyIII\Crud\Account\AccountCrudInterface; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use Log; /** * Class AssetAccountNumber @@ -24,11 +28,46 @@ class AssetAccountNumber extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return Account */ public function convert($value) { - throw new FireflyException('Importer with name AssetAccountNumber has not yet been configured.'); + $value = trim($value); + Log::debug('Going to convert using AssetAccountName', ['value' => $value]); + + if (strlen($value) === 0) { + return new Account; + } + + /** @var AccountCrudInterface $repository */ + $repository = app(AccountCrudInterface::class, [$this->user]); + + + if (isset($this->mapping[$value])) { + Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $account = $repository->find(intval($this->mapping[$value])); + if (!is_null($account->id)) { + Log::debug('Found account by ID', ['id' => $account->id]); + + return $account; + } + } + + // not mapped? Still try to find it first: + $account = $repository->findByAccountNumber($value, [AccountType::ASSET]); + if (!is_null($account->id)) { + Log::debug('Found account by name', ['id' => $account->id]); + + return $account; + } + + + $account = $repository->store( + ['name' => 'Account with number ' . $value, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id, 'accountType' => 'asset', + 'virtualBalance' => 0, 'active' => true] + ); + + return $account; } } \ No newline at end of file diff --git a/app/Import/Converter/BillId.php b/app/Import/Converter/BillId.php index 76b8c01fd2..00fa42d0ef 100644 --- a/app/Import/Converter/BillId.php +++ b/app/Import/Converter/BillId.php @@ -12,6 +12,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Bill; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use Log; /** * Class BillId @@ -24,11 +27,40 @@ class BillId extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return Bill */ public function convert($value) { - throw new FireflyException('Importer with name BillId has not yet been configured.'); + $value = intval(trim($value)); + Log::debug('Going to convert using BillId', ['value' => $value]); + + if ($value === 0) { + return new Bill; + } + + /** @var BillRepositoryInterface $repository */ + $repository = app(BillRepositoryInterface::class, [$this->user]); + + if (isset($this->mapping[$value])) { + Log::debug('Found bill in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $bill = $repository->find(intval($this->mapping[$value])); + if (!is_null($bill->id)) { + Log::debug('Found bill by ID', ['id' => $bill->id]); + + return $bill; + } + } + + // not mapped? Still try to find it first: + $bill = $repository->find($value); + if (!is_null($bill->id)) { + Log::debug('Found bill by ID ', ['id' => $bill->id]); + + return $bill; + } + + // should not really happen. If the ID does not match FF, what is FF supposed to do? + return new Bill; } } \ No newline at end of file diff --git a/app/Import/Converter/BillName.php b/app/Import/Converter/BillName.php index 888b6c4779..7dd8241e3c 100644 --- a/app/Import/Converter/BillName.php +++ b/app/Import/Converter/BillName.php @@ -12,6 +12,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Bill; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use Log; /** * Class BillName @@ -28,7 +31,53 @@ class BillName extends BasicConverter implements ConverterInterface */ public function convert($value) { - throw new FireflyException('Importer with name BillName has not yet been configured.'); + $value = trim($value); + Log::debug('Going to convert using BillName', ['value' => $value]); + + if (strlen($value) === 0) { + return new Bill; + } + + /** @var BillRepositoryInterface $repository */ + $repository = app(BillRepositoryInterface::class, [$this->user]); + + if (isset($this->mapping[$value])) { + Log::debug('Found bill in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $bill = $repository->find(intval($this->mapping[$value])); + if (!is_null($bill->id)) { + Log::debug('Found bill by ID', ['id' => $bill->id]); + + return $bill; + } + } + + // not mapped? Still try to find it first: + $bill = $repository->findByName($value); + if (!is_null($bill->id)) { + Log::debug('Found bill by name ', ['id' => $bill->id]); + + return $bill; + } + + // create new bill. Use a lot of made up values. + $bill = $repository->store( + [ + 'name' => $value, + 'match' => $value, + 'amount_min' => 1, + 'user_id' => $this->user->id, + 'amount_max' => 10, + 'date' => date('Ymd'), + 'repeat_freq' => 'monthly', + 'skip' => 0, + 'automatch' => 0, + 'active' => 1, + + ] + ); + + return $bill; + } } \ No newline at end of file diff --git a/app/Import/Converter/OpposingAccountIban.php b/app/Import/Converter/OpposingAccountIban.php index a364c597ef..c59f4ffb3c 100644 --- a/app/Import/Converter/OpposingAccountIban.php +++ b/app/Import/Converter/OpposingAccountIban.php @@ -65,40 +65,7 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface $account = $repository->store( ['name' => $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'import', 'virtualBalance' => 0, 'active' => true, - 'openingBalance' => 0 0] ); return $account; diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index db29c244cb..2779fb33cc 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -17,6 +17,7 @@ use FireflyIII\Crud\Account\AccountCrud; use FireflyIII\Import\Converter\ConverterInterface; use FireflyIII\Import\ImportEntry; use FireflyIII\Import\Mapper\MapperInterface; +use FireflyIII\Import\MapperPreProcess\PreProcessorInterface; use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; use Illuminate\Http\Request; @@ -180,16 +181,23 @@ class CsvImporter implements ImporterInterface public function saveImportConfiguration(array $data, FileBag $files): bool { /** @var AccountCrud $repository */ - $repository = app(AccountCrud::class); - $account = $repository->find(intval($data['csv_import_account'])); + $repository = app(AccountCrud::class, [auth()->user()]); + $account = $repository->find(intval($data['csv_import_account'])); + $hasHeaders = isset($data['has_headers']) && intval($data['has_headers']) === 1 ? true : false; $config = $this->job->configuration; $config['has-headers'] = $hasHeaders; $config['date-format'] = $data['date_format']; $config['delimiter'] = $data['csv_delimiter']; + Log::debug('Entered import account.', ['id' => $data['csv_import_account']]); + + if (!is_null($account->id)) { + Log::debug('Found account.', ['id' => $account->id, 'name' => $account->name]); $config['import-account'] = $account->id; + } else { + Log::error('Could not find anything for csv_import_account.', ['id' => $data['csv_import_account']]); } // loop specifics. if (isset($data['specifics']) && is_array($data['specifics'])) { @@ -335,21 +343,28 @@ class CsvImporter implements ImporterInterface foreach ($config['column-do-mapping'] as $index => $mustBeMapped) { if ($mustBeMapped) { - $column = $config['column-roles'][$index] ?? '_ignore'; - $canBeMapped = config('csv.import_roles.' . $column . '.mappable'); + $column = $config['column-roles'][$index] ?? '_ignore'; + $canBeMapped = config('csv.import_roles.' . $column . '.mappable'); + $preProcessMap = config('csv.import_roles.' . $column . '.pre-process-map'); if ($canBeMapped) { $mapperName = '\FireflyIII\Import\Mapper\\' . config('csv.import_roles.' . $column . '.mapper'); /** @var MapperInterface $mapper */ $mapper = new $mapperName; $indexes[] = $index; $data[$index] = [ - 'name' => $column, - 'mapper' => $mapperName, - 'index' => $index, - 'options' => $mapper->getMap(), - 'values' => [], + 'name' => $column, + 'mapper' => $mapperName, + 'index' => $index, + 'options' => $mapper->getMap(), + 'preProcessMap' => null, + 'values' => [], ]; + if ($preProcessMap) { + $data[$index]['preProcessMap'] = '\FireflyIII\Import\MapperPreProcess\\' . + config('csv.import_roles.' . $column . '.pre-process-mapper'); + } } + } } @@ -358,12 +373,29 @@ class CsvImporter implements ImporterInterface $reader = Reader::createFromString($content); $results = $reader->fetch(); - foreach ($results as $row) { + foreach ($results as $rowIndex => $row) { //do something here - foreach ($indexes as $index) { + foreach ($indexes as $index) { // this is simply 1, 2, 3, etc. $value = $row[$index]; if (strlen($value) > 0) { - $data[$index]['values'][] = $row[$index]; + + // we can do some preprocessing here, + // which is exclusively to fix the tags: + if (!is_null($data[$index]['preProcessMap'])) { + /** @var PreProcessorInterface $preProcessor */ + $preProcessor = app($data[$index]['preProcessMap']); + $result = $preProcessor->run($value); + $data[$index]['values'] = array_merge($data[$index]['values'], $result); + + Log::debug($rowIndex . ':' . $index . 'Value before preprocessor', ['value' => $value]); + Log::debug($rowIndex . ':' . $index . 'Value after preprocessor', ['value-new' => $result]); + Log::debug($rowIndex . ':' . $index . 'Value after joining', ['value-complete' => $data[$index]['values']]); + + + continue; + } + + $data[$index]['values'][] = $value; } } } diff --git a/app/Import/MapperPreProcess/PreProcessorInterface.php b/app/Import/MapperPreProcess/PreProcessorInterface.php new file mode 100644 index 0000000000..d577b90cc7 --- /dev/null +++ b/app/Import/MapperPreProcess/PreProcessorInterface.php @@ -0,0 +1,28 @@ +auth->check()) { - return app('FireflyIII\Repositories\Account\AccountRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\Account\AccountRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/AttachmentServiceProvider.php b/app/Providers/AttachmentServiceProvider.php index bee83cf88c..0cd70ecc90 100644 --- a/app/Providers/AttachmentServiceProvider.php +++ b/app/Providers/AttachmentServiceProvider.php @@ -44,7 +44,7 @@ class AttachmentServiceProvider extends ServiceProvider 'FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Attachment\AttachmentRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\Attachment\AttachmentRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/BillServiceProvider.php b/app/Providers/BillServiceProvider.php index 289d712094..20bdd0f6bc 100644 --- a/app/Providers/BillServiceProvider.php +++ b/app/Providers/BillServiceProvider.php @@ -44,7 +44,7 @@ class BillServiceProvider extends ServiceProvider 'FireflyIII\Repositories\Bill\BillRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Bill\BillRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\Bill\BillRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/BudgetServiceProvider.php b/app/Providers/BudgetServiceProvider.php index afd73a846b..aa7f6687f3 100644 --- a/app/Providers/BudgetServiceProvider.php +++ b/app/Providers/BudgetServiceProvider.php @@ -44,7 +44,7 @@ class BudgetServiceProvider extends ServiceProvider 'FireflyIII\Repositories\Budget\BudgetRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Budget\BudgetRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\Budget\BudgetRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/CategoryServiceProvider.php b/app/Providers/CategoryServiceProvider.php index 5adab5c229..fbea960b60 100644 --- a/app/Providers/CategoryServiceProvider.php +++ b/app/Providers/CategoryServiceProvider.php @@ -44,7 +44,7 @@ class CategoryServiceProvider extends ServiceProvider 'FireflyIII\Repositories\Category\CategoryRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Category\CategoryRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\Category\CategoryRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/CrudServiceProvider.php b/app/Providers/CrudServiceProvider.php index 0117cf78a5..cfb30450e3 100644 --- a/app/Providers/CrudServiceProvider.php +++ b/app/Providers/CrudServiceProvider.php @@ -12,8 +12,10 @@ declare(strict_types = 1); namespace FireflyIII\Providers; use FireflyIII\Exceptions\FireflyException; +use Illuminate\Auth\AuthManager; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; +use Log; /** * Class CrudServiceProvider @@ -49,12 +51,14 @@ class CrudServiceProvider extends ServiceProvider $this->app->bind( 'FireflyIII\Crud\Account\AccountCrudInterface', function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Crud\Account\AccountCrud', [$app->auth->user()]); + + if (!isset($arguments[0]) && auth()->check()) { + return app('FireflyIII\Crud\Account\AccountCrud', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); } + Log::debug('AccountCrud constructor, run with default arguments.', $arguments); return app('FireflyIII\Crud\Account\AccountCrud', $arguments); } @@ -67,7 +71,7 @@ class CrudServiceProvider extends ServiceProvider 'FireflyIII\Crud\Split\JournalInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Crud\Split\Journal', [$app->auth->user()]); + return app('FireflyIII\Crud\Split\Journal', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/ExportJobServiceProvider.php b/app/Providers/ExportJobServiceProvider.php index f39e24cd1e..cd908bc969 100644 --- a/app/Providers/ExportJobServiceProvider.php +++ b/app/Providers/ExportJobServiceProvider.php @@ -56,7 +56,7 @@ class ExportJobServiceProvider extends ServiceProvider 'FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\ExportJob\ExportJobRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\ExportJob\ExportJobRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); @@ -73,7 +73,7 @@ class ExportJobServiceProvider extends ServiceProvider 'FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\ImportJob\ImportJobRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\ImportJob\ImportJobRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/JournalServiceProvider.php b/app/Providers/JournalServiceProvider.php index db50c23499..4e82de187e 100644 --- a/app/Providers/JournalServiceProvider.php +++ b/app/Providers/JournalServiceProvider.php @@ -44,7 +44,7 @@ class JournalServiceProvider extends ServiceProvider 'FireflyIII\Repositories\Journal\JournalRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Journal\JournalRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\Journal\JournalRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/PiggyBankServiceProvider.php b/app/Providers/PiggyBankServiceProvider.php index 14aa3353af..d662698511 100644 --- a/app/Providers/PiggyBankServiceProvider.php +++ b/app/Providers/PiggyBankServiceProvider.php @@ -45,7 +45,7 @@ class PiggyBankServiceProvider extends ServiceProvider 'FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\PiggyBank\PiggyBankRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\PiggyBank\PiggyBankRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/RuleGroupServiceProvider.php b/app/Providers/RuleGroupServiceProvider.php index 7536006145..4cff99454f 100644 --- a/app/Providers/RuleGroupServiceProvider.php +++ b/app/Providers/RuleGroupServiceProvider.php @@ -45,7 +45,7 @@ class RuleGroupServiceProvider extends ServiceProvider 'FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\RuleGroup\RuleGroupRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\RuleGroup\RuleGroupRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/RuleServiceProvider.php b/app/Providers/RuleServiceProvider.php index 2a416fcea0..14d3163a53 100644 --- a/app/Providers/RuleServiceProvider.php +++ b/app/Providers/RuleServiceProvider.php @@ -44,7 +44,7 @@ class RuleServiceProvider extends ServiceProvider 'FireflyIII\Repositories\Rule\RuleRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Rule\RuleRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\Rule\RuleRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Providers/TagServiceProvider.php b/app/Providers/TagServiceProvider.php index 51e4a16e45..34ef48591a 100644 --- a/app/Providers/TagServiceProvider.php +++ b/app/Providers/TagServiceProvider.php @@ -44,7 +44,7 @@ class TagServiceProvider extends ServiceProvider 'FireflyIII\Repositories\Tag\TagRepositoryInterface', function (Application $app, array $arguments) { if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Tag\TagRepository', [$app->auth->user()]); + return app('FireflyIII\Repositories\Tag\TagRepository', [auth()->user()]); } if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 43aabe96cf..6ea9f802af 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -72,6 +72,27 @@ class BillRepository implements BillRepositoryInterface return $bill; } + /** + * Find a bill by name. + * + * @param string $name + * + * @return Bill + */ + public function findByName(string $name) : Bill + { + $bills = $this->user->bills()->get(); + + /** @var Bill $bill */ + foreach ($bills as $bill) { + if ($bill->name === $name) { + return $bill; + } + } + + return new Bill; + } + /** * @return Collection */ @@ -293,6 +314,28 @@ class BillRepository implements BillRepositoryInterface return $bill->transactionjournals()->before($end)->after($start)->get(); } + /** + * @param $bill + * + * @return string + */ + public function getOverallAverage($bill): string + { + $journals = $bill->transactionjournals()->get(); + $sum = '0'; + $count = strval($journals->count()); + /** @var TransactionJournal $journal */ + foreach ($journals as $journal) { + $sum = bcadd($sum, TransactionJournal::amountPositive($journal)); + } + $avg = '0'; + if ($journals->count() > 0) { + $avg = bcdiv($sum, $count); + } + + return $avg; + } + /** * @param Bill $bill * @@ -358,6 +401,32 @@ class BillRepository implements BillRepositoryInterface return $validRanges; } + /** + * @param Bill $bill + * @param Carbon $date + * + * @return string + */ + public function getYearAverage(Bill $bill, Carbon $date): string + { + $journals = $bill->transactionjournals() + ->where('date', '>=', $date->year . '-01-01') + ->where('date', '<=', $date->year . '-12-31') + ->get(); + $sum = '0'; + $count = strval($journals->count()); + /** @var TransactionJournal $journal */ + foreach ($journals as $journal) { + $sum = bcadd($sum, TransactionJournal::amountPositive($journal)); + } + $avg = '0'; + if ($journals->count() > 0) { + $avg = bcdiv($sum, $count); + } + + return $avg; + } + /** * @param Bill $bill * @@ -557,52 +626,4 @@ class BillRepository implements BillRepositoryInterface return $wordMatch; } - - /** - * @param Bill $bill - * @param Carbon $date - * - * @return string - */ - public function getYearAverage(Bill $bill, Carbon $date): string - { - $journals = $bill->transactionjournals() - ->where('date', '>=', $date->year . '-01-01') - ->where('date', '<=', $date->year . '-12-31') - ->get(); - $sum = '0'; - $count = strval($journals->count()); - /** @var TransactionJournal $journal */ - foreach ($journals as $journal) { - $sum = bcadd($sum, TransactionJournal::amountPositive($journal)); - } - $avg = '0'; - if ($journals->count() > 0) { - $avg = bcdiv($sum, $count); - } - - return $avg; - } - - /** - * @param $bill - * - * @return string - */ - public function getOverallAverage($bill): string - { - $journals = $bill->transactionjournals()->get(); - $sum = '0'; - $count = strval($journals->count()); - /** @var TransactionJournal $journal */ - foreach ($journals as $journal) { - $sum = bcadd($sum, TransactionJournal::amountPositive($journal)); - } - $avg = '0'; - if ($journals->count() > 0) { - $avg = bcdiv($sum, $count); - } - - return $avg; - } } diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index 494b4d2d7f..ff28c7b521 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -32,21 +32,6 @@ interface BillRepositoryInterface */ public function destroy(Bill $bill): bool; - /** - * @param Bill $bill - * @param Carbon $date - * - * @return string - */ - public function getYearAverage(Bill $bill, Carbon $date): string; - - /** - * @param $bill - * - * @return string - */ - public function getOverallAverage($bill): string; - /** * Find a bill by ID. * @@ -56,6 +41,15 @@ interface BillRepositoryInterface */ public function find(int $billId) : Bill; + /** + * Find a bill by name. + * + * @param string $name + * + * @return Bill + */ + public function findByName(string $name) : Bill; + /** * @return Collection */ @@ -128,6 +122,13 @@ interface BillRepositoryInterface */ public function getJournalsInRange(Bill $bill, Carbon $start, Carbon $end): Collection; + /** + * @param $bill + * + * @return string + */ + public function getOverallAverage($bill): string; + /** * @param Bill $bill * @@ -148,6 +149,14 @@ interface BillRepositoryInterface */ public function getRanges(Bill $bill, Carbon $start, Carbon $end): array; + /** + * @param Bill $bill + * @param Carbon $date + * + * @return string + */ + public function getYearAverage(Bill $bill, Carbon $date): string; + /** * @param Bill $bill * diff --git a/config/app.php b/config/app.php index 79a9e67ed6..7e3766d118 100644 --- a/config/app.php +++ b/config/app.php @@ -152,21 +152,6 @@ return [ Collective\Html\HtmlServiceProvider::class, - /* - * More service providers. - */ - FireflyIII\Providers\CrudServiceProvider::class, - FireflyIII\Providers\AccountServiceProvider::class, - FireflyIII\Providers\AttachmentServiceProvider::class, - FireflyIII\Providers\BillServiceProvider::class, - FireflyIII\Providers\BudgetServiceProvider::class, - FireflyIII\Providers\CategoryServiceProvider::class, - FireflyIII\Providers\ExportJobServiceProvider::class, - FireflyIII\Providers\JournalServiceProvider::class, - FireflyIII\Providers\PiggyBankServiceProvider::class, - FireflyIII\Providers\RuleServiceProvider::class, - FireflyIII\Providers\RuleGroupServiceProvider::class, - FireflyIII\Providers\TagServiceProvider::class, /* @@ -186,6 +171,24 @@ return [ 'TwigBridge\ServiceProvider', 'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider', + + /* + * More service providers. + */ + FireflyIII\Providers\CrudServiceProvider::class, + FireflyIII\Providers\AccountServiceProvider::class, + FireflyIII\Providers\AttachmentServiceProvider::class, + FireflyIII\Providers\BillServiceProvider::class, + FireflyIII\Providers\BudgetServiceProvider::class, + FireflyIII\Providers\CategoryServiceProvider::class, + FireflyIII\Providers\ExportJobServiceProvider::class, + FireflyIII\Providers\JournalServiceProvider::class, + FireflyIII\Providers\PiggyBankServiceProvider::class, + FireflyIII\Providers\RuleServiceProvider::class, + FireflyIII\Providers\RuleGroupServiceProvider::class, + FireflyIII\Providers\TagServiceProvider::class, + + ], /* diff --git a/config/csv.php b/config/csv.php index 707a34b8c4..915b8f724b 100644 --- a/config/csv.php +++ b/config/csv.php @@ -14,179 +14,247 @@ return [ /* * Configuration for possible column roles. + * + * The key is the short name for the column role. There are five values, which mean this: + * + * 'mappable' + * Whether or not the value in the CSV column can be linked to an existing value in your + * Firefly database. For example: account names can be linked to existing account names you have already + * so double entries cannot occur. This process is called "mapping". You have to make each unique value in your + * CSV file to an existing entry in your database. For example, map all account names in your CSV file to existing + * accounts. If you have an entry that does not exist in your database, you can set Firefly to ignore it, and it will + * create it. + * + * 'pre-process-map' + * In the case of tags, there are multiple values in one csv column (for example: "expense groceries snack" in one column). + * This means the content of the column must be "pre processed" aka split in parts so the importer can work with the data. + * + * 'pre-process-mapper' + * This is the class that will actually do the pre-processing. + * + * 'field' + * I don't believe this value is used any more, but I am not sure. + * + * 'converter' + * The converter is a class in app/Import/Converter that converts the given value into an object Firefly understands. + * The CategoryName converter can convert a category name into an actual category. This converter will take a mapping + * into account: if you mapped "Groceries" to category "Groceries" the converter will simply return "Groceries" instead of + * trying to make a new category also named Groceries. + * + * 'mapper' + * When you map data (see "mappable") you need a list of stuff you can map to. If you say a certain column is mappable + * and the column contains "category names", the mapper will be "Category" and it will give you a list of possible categories. + * This way the importer always presents you with a valid list of things to map to. + * + * + * */ 'import_roles' => [ '_ignore' => [ - 'mappable' => false, - 'field' => 'ignored', - 'converter' => 'Ignore', + 'mappable' => false, + 'pre-process-map' => false, + 'field' => 'ignored', + 'converter' => 'Ignore', + 'mapper' => null, + ], 'bill-id' => [ - 'mappable' => false, - 'field' => 'bill', - 'converter' => 'BillId', - 'mapper' => 'Bills', + 'mappable' => false, + 'pre-process-map' => false, + 'field' => 'bill', + 'converter' => 'BillId', + 'mapper' => 'Bills', ], 'bill-name' => [ - 'mappable' => true, - 'field' => 'bill', - 'converter' => 'BillName', - 'mapper' => 'Bills', + 'mappable' => true, + 'pre-process-map' => false, + 'field' => 'bill', + 'converter' => 'BillName', + 'mapper' => 'Bills', ], 'currency-id' => [ - 'mappable' => true, - 'field' => 'currency', - 'converter' => 'CurrencyId', - 'mapper' => 'TransactionCurrencies', + 'mappable' => true, + 'pre-process-map' => false, + 'field' => 'currency', + 'converter' => 'CurrencyId', + 'mapper' => 'TransactionCurrencies', ], 'currency-name' => [ - 'mappable' => true, - 'converter' => 'CurrencyName', - 'field' => 'currency', - 'mapper' => 'TransactionCurrencies', + 'mappable' => true, + 'pre-process-map' => false, + 'converter' => 'CurrencyName', + 'field' => 'currency', + 'mapper' => 'TransactionCurrencies', ], 'currency-code' => [ - 'mappable' => true, - 'converter' => 'CurrencyCode', - 'field' => 'currency', - 'mapper' => 'TransactionCurrencies', + 'mappable' => true, + 'pre-process-map' => false, + 'converter' => 'CurrencyCode', + 'field' => 'currency', + 'mapper' => 'TransactionCurrencies', ], 'currency-symbol' => [ - 'mappable' => true, - 'converter' => 'CurrencySymbol', - 'field' => 'currency', - 'mapper' => 'TransactionCurrencies', + 'mappable' => true, + 'pre-process-map' => false, + 'converter' => 'CurrencySymbol', + 'field' => 'currency', + 'mapper' => 'TransactionCurrencies', ], 'description' => [ - 'mappable' => false, - 'converter' => 'Description', - 'field' => 'description', + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'Description', + 'field' => 'description', ], 'date-transaction' => [ - 'mappable' => false, - 'converter' => 'Date', - 'field' => 'date', + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'Date', + 'field' => 'date', ], 'date-rent' => [ - 'mappable' => false, - 'converter' => 'Date', - 'field' => 'date-rent', + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'Date', + 'field' => 'date-rent', ], 'budget-id' => [ - 'mappable' => true, - 'converter' => 'BudgetId', - 'field' => 'budget', - 'mapper' => 'Budgets', + 'mappable' => true, + 'pre-process-map' => false, + 'converter' => 'BudgetId', + 'field' => 'budget', + 'mapper' => 'Budgets', ], 'budget-name' => [ - 'mappable' => true, - 'converter' => 'BudgetName', - 'field' => 'budget', - 'mapper' => 'Budgets', + 'mappable' => true, + 'pre-process-map' => false, + 'converter' => 'BudgetName', + 'field' => 'budget', + 'mapper' => 'Budgets', ], 'rabo-debet-credit' => [ - 'mappable' => false, - 'converter' => 'RabobankDebetCredit', - 'field' => 'amount-modifier', + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'RabobankDebetCredit', + 'field' => 'amount-modifier', ], 'ing-debet-credit' => [ - 'mappable' => false, - 'converter' => 'INGDebetCredit', - 'field' => 'amount-modifier', + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'INGDebetCredit', + 'field' => 'amount-modifier', ], 'category-id' => [ - 'mappable' => true, - 'converter' => 'CategoryId', - 'field' => 'category', - 'mapper' => 'Categories', + 'mappable' => true, + 'pre-process-map' => false, + 'converter' => 'CategoryId', + 'field' => 'category', + 'mapper' => 'Categories', ], 'category-name' => [ - 'mappable' => true, - 'converter' => 'CategoryName', - 'field' => 'category', - 'mapper' => 'Categories', + 'mappable' => true, + 'pre-process-map' => false, + 'converter' => 'CategoryName', + 'field' => 'category', + 'mapper' => 'Categories', ], 'tags-comma' => [ - 'mappable' => false, - 'field' => 'tags', - 'converter' => 'TagsComma', - 'mapper' => 'Tags', + 'mappable' => true, + 'pre-process-map' => true, + 'pre-process-mapper' => 'TagsComma', + 'field' => 'tags', + 'converter' => 'TagsComma', + 'mapper' => 'Tags', ], 'tags-space' => [ - 'mappable' => false, - 'field' => 'tags', - 'converter' => 'TagsSpace', - 'mapper' => 'Tags', + 'mappable' => true, + 'pre-process-map' => true, + 'pre-process-mapper' => 'TagsSpace', + 'field' => 'tags', + 'converter' => 'TagsSpace', + 'mapper' => 'Tags', ], 'account-id' => [ - 'mappable' => true, - 'field' => 'asset-account-id', - 'converter' => 'AssetAccountId', - 'mapper' => 'AssetAccounts', + 'mappable' => true, + 'pre-process-map' => false, + 'field' => 'asset-account-id', + 'converter' => 'AssetAccountId', + 'mapper' => 'AssetAccounts', ], 'account-name' => [ - 'mappable' => true, - 'field' => 'asset-account-name', - 'converter' => 'AssetAccountName', - 'mapper' => 'AssetAccounts', + 'mappable' => true, + 'pre-process-map' => false, + 'field' => 'asset-account-name', + 'converter' => 'AssetAccountName', + 'mapper' => 'AssetAccounts', ], 'account-iban' => [ - 'mappable' => true, - 'field' => 'asset-account-iban', - 'converter' => 'AssetAccountIban', - 'mapper' => 'AssetAccountIbans', + 'mappable' => true, + 'pre-process-map' => false, + 'field' => 'asset-account-iban', + 'converter' => 'AssetAccountIban', + 'mapper' => 'AssetAccountIbans', ], 'account-number' => [ - 'mappable' => true, - 'field' => 'asset-account-number', - 'converter' => 'AssetAccountNumber', - 'mapper' => 'AssetAccounts', + 'mappable' => true, + 'pre-process-map' => false, + 'field' => 'asset-account-number', + 'converter' => 'AssetAccountNumber', + 'mapper' => 'AssetAccounts', ], 'opposing-id' => [ - 'mappable' => true, - 'field' => 'opposing-account-id', - 'converter' => 'OpposingAccountId', - 'mapper' => 'OpposingAccounts', + 'mappable' => true, + 'pre-process-map' => false, + 'field' => 'opposing-account-id', + 'converter' => 'OpposingAccountId', + 'mapper' => 'OpposingAccounts', ], 'opposing-name' => [ - 'mappable' => true, - 'field' => 'opposing-account-name', - 'converter' => 'OpposingAccountName', - 'mapper' => 'OpposingAccounts', + 'mappable' => true, + 'pre-process-map' => false, + 'field' => 'opposing-account-name', + 'converter' => 'OpposingAccountName', + 'mapper' => 'OpposingAccounts', ], 'opposing-iban' => [ - 'mappable' => true, - 'field' => 'opposing-account-iban', - 'converter' => 'OpposingAccountIban', - 'mapper' => 'OpposingAccountIbans', + 'mappable' => true, + 'pre-process-map' => false, + 'field' => 'opposing-account-iban', + 'converter' => 'OpposingAccountIban', + 'mapper' => 'OpposingAccountIbans', ], 'opposing-number' => [ - 'mappable' => true, - 'field' => 'opposing-account-number', - 'converter' => 'OpposingAccountNumber', - 'mapper' => 'OpposingAccounts', + 'mappable' => true, + 'pre-process-map' => false, + 'field' => 'opposing-account-number', + 'converter' => 'OpposingAccountNumber', + 'mapper' => 'OpposingAccounts', ], 'amount' => [ - 'mappable' => false, - 'converter' => 'Amount', - 'field' => 'amount', + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'Amount', + 'field' => 'amount', ], 'sepa-ct-id' => [ - 'mappable' => false, - 'converter' => 'Description', - 'field' => 'description', + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'Description', + 'field' => 'description', ], 'sepa-ct-op' => [ - 'mappable' => false, - 'converter' => 'Description', - 'field' => 'description', + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'Description', + 'field' => 'description', ], 'sepa-db' => [ - 'mappable' => false, - 'converter' => 'Description', - 'field' => 'description', + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'Description', + 'field' => 'description', ], ], diff --git a/resources/views/import/csv/configure.twig b/resources/views/import/csv/configure.twig index fd3949e778..7dccd35ede 100644 --- a/resources/views/import/csv/configure.twig +++ b/resources/views/import/csv/configure.twig @@ -38,7 +38,7 @@ {{ ExpandedForm.checkbox('has_headers',1,job.configuration['has-headers'],{helpText: trans('csv.header_help')}) }} {{ ExpandedForm.text('date_format',job.configuration['date-format'],{helpText: trans('csv.date_help', {dateExample: phpdate('Ymd')}) }) }} {{ ExpandedForm.select('csv_delimiter', data.delimiters, job.configuration['delimiter'], {helpText: trans('csv.delimiter_help') } ) }} - {{ ExpandedForm.select('csv_import_account', data.accounts, 0, {helpText: trans('csv.import_account_help')} ) }} + {{ ExpandedForm.select('csv_import_account', data.accounts, job.configuration['import-account'], {helpText: trans('csv.import_account_help')} ) }} {% for type, specific in data.specifics %}
    From 1392275b8109d63139bcf761cf050a19e2198c72 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 24 Jul 2016 18:47:55 +0200 Subject: [PATCH 45/94] Lots of new code for importer and some preferences. --- app/Http/Controllers/Admin/UserController.php | 112 ++++ app/Http/breadcrumbs.php | 17 + app/Http/routes.php | 4 + .../{AssetAccountId.php => AccountId.php} | 6 +- app/Import/Converter/BasicConverter.php | 10 + app/Import/Converter/BudgetId.php | 36 +- app/Import/Converter/BudgetName.php | 43 +- app/Import/Converter/CategoryId.php | 36 +- app/Import/Converter/CategoryName.php | 43 +- app/Import/Converter/ConverterInterface.php | 5 + app/Import/Converter/CurrencyId.php | 36 +- app/Import/Converter/CurrencyName.php | 45 +- app/Import/Converter/CurrencySymbol.php | 45 +- app/Import/Converter/INGDebetCredit.php | 13 +- app/Import/Converter/OpposingAccountId.php | 34 -- .../Converter/OpposingAccountNumber.php | 42 +- app/Import/Converter/RabobankDebetCredit.php | 4 + app/Import/Converter/TagsComma.php | 56 +- app/Import/Converter/TagsSpace.php | 58 +- app/Import/ImportEntry.php | 32 +- app/Import/Importer/CsvImporter.php | 2 + app/Models/Configuration.php | 62 ++ app/Models/Preference.php | 1 + app/Providers/FireflyServiceProvider.php | 7 + app/Repositories/Budget/BudgetRepository.php | 22 +- .../Budget/BudgetRepositoryInterface.php | 31 +- .../Category/CategoryRepository.php | 20 + .../Category/CategoryRepositoryInterface.php | 27 +- app/Repositories/Tag/TagRepository.php | 33 ++ .../Tag/TagRepositoryInterface.php | 14 + app/Support/Facades/FireflyConfig.php | 33 ++ app/Support/FireflyConfig.php | 91 +++ composer.lock | 554 ++++++++++++------ config/app.php | 91 ++- config/csv.php | 6 +- ...016_06_16_000000_create_support_tables.php | 22 +- resources/lang/en_US/firefly.php | 12 + resources/lang/en_US/form.php | 3 + resources/lang/en_US/list.php | 102 ++-- resources/views/admin/index.twig | 1 + resources/views/admin/users/domains.twig | 120 ++++ 41 files changed, 1562 insertions(+), 369 deletions(-) rename app/Import/Converter/{AssetAccountId.php => AccountId.php} (93%) delete mode 100644 app/Import/Converter/OpposingAccountId.php create mode 100644 app/Models/Configuration.php create mode 100644 app/Support/Facades/FireflyConfig.php create mode 100644 app/Support/FireflyConfig.php create mode 100644 resources/views/admin/users/domains.twig diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 996fc4c991..168e674f2b 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -14,8 +14,11 @@ namespace FireflyIII\Http\Controllers\Admin; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\User\UserRepositoryInterface; +use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\User; +use Illuminate\Http\Request; use Preferences; +use Session; /** * Class UserController @@ -24,8 +27,29 @@ use Preferences; */ class UserController extends Controller { + + /** + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function domains() + { + + $title = strval(trans('firefly.administration')); + $mainTitleIcon = 'fa-hand-spock-o'; + $subTitle = strval(trans('firefly.blocked_domains')); + $subTitleIcon = 'fa-users'; + $domains = FireflyConfig::get('blocked-domains', [])->data; + + // known domains + $knownDomains = $this->getKnownDomains(); + + return view('admin.users.domains', compact('title', 'mainTitleIcon', 'knownDomains', 'subTitle', 'subTitleIcon', 'domains')); + } + /** * @param UserRepositoryInterface $repository + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function index(UserRepositoryInterface $repository) { @@ -61,4 +85,92 @@ class UserController extends Controller } + /** + * @param Request $request + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function manual(Request $request) + { + if (strlen($request->get('domain')) === 0) { + Session::flash('error', trans('firefly.no_domain_filled_in')); + + return redirect(route('admin.users.domains')); + } + + $domain = $request->get('domain'); + $blocked = FireflyConfig::get('blocked-domains', [])->data; + + if (in_array($domain, $blocked)) { + Session::flash('error', trans('firefly.domain_already_blocked', ['domain' => $domain])); + + return redirect(route('admin.users.domains')); + } + $blocked[] = $domain; + FireflyConfig::set('blocked-domains', $blocked); + + Session::flash('success', trans('firefly.domain_is_now_blocked', ['domain' => $domain])); + return redirect(route('admin.users.domains')); + } + + /** + * @param string $domain + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function toggleDomain(string $domain) + { + $blocked = FireflyConfig::get('blocked-domains', [])->data; + + if (in_array($domain, $blocked)) { + $key = array_search($domain, $blocked); + unset($blocked[$key]); + sort($blocked); + + FireflyConfig::set('blocked-domains', $blocked); + Session::flash('message', trans('firefly.domain_now_unblocked', ['domain' => $domain])); + + + return redirect(route('admin.users.domains')); + + } + + $blocked[] = $domain; + + FireflyConfig::set('blocked-domains', $blocked); + Session::flash('message', trans('firefly.domain_now_blocked', ['domain' => $domain])); + + return redirect(route('admin.users.domains')); + } + + /** + * @return array + */ + private function getKnownDomains(): array + { + $users = User::get(); + $set = []; + $filtered = []; + /** @var User $user */ + foreach ($users as $user) { + $email = $user->email; + $parts = explode('@', $email); + $domain = $parts[1]; + $set[] = $domain; + } + $set = array_unique($set); + // filter for already banned domains: + $blocked = FireflyConfig::get('blocked-domains', [])->data; + + foreach ($set as $domain) { + // in the block array? ignore it. + if (!in_array($domain, $blocked)) { + $filtered[] = $domain; + } + } + asort($filtered); + + return $filtered; + } + } diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index f1925bc906..c1dcda94cd 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -96,6 +96,23 @@ Breadcrumbs::register( } ); +/** + * ADMIN + */ +Breadcrumbs::register( + 'admin.index', function (BreadCrumbGenerator $breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push(trans('firefly.administration'), route('admin.index')); +} +); + +Breadcrumbs::register( + 'admin.users', function (BreadCrumbGenerator $breadcrumbs) { + $breadcrumbs->parent('admin.index'); + $breadcrumbs->push(trans('firefly.list_all_users'), route('admin.users')); +} +); + /** * ATTACHMENTS */ diff --git a/app/Http/routes.php b/app/Http/routes.php index b2496d42cd..4d1f4b7617 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -424,6 +424,10 @@ Route::group( // user manager Route::get('/admin/users', ['uses' => 'Admin\UserController@index', 'as' => 'admin.users']); + Route::get('/admin/users/domains', ['uses' => 'Admin\UserController@domains', 'as' => 'admin.users.domains']); + Route::get('/admin/users/domains/toggle/{domain}', ['uses' => 'Admin\UserController@toggleDomain', 'as' => 'admin.users.domains.block-toggle']); + + Route::post('/admin/users/domains/manual', ['uses' => 'Admin\UserController@manual', 'as' => 'admin.users.domains.manual']); } ); diff --git a/app/Import/Converter/AssetAccountId.php b/app/Import/Converter/AccountId.php similarity index 93% rename from app/Import/Converter/AssetAccountId.php rename to app/Import/Converter/AccountId.php index f055f819c7..5ba5e19d61 100644 --- a/app/Import/Converter/AssetAccountId.php +++ b/app/Import/Converter/AccountId.php @@ -1,6 +1,6 @@ certainty; + } + /** * @param array $config */ diff --git a/app/Import/Converter/BudgetId.php b/app/Import/Converter/BudgetId.php index 652c27dcc6..71178f3a67 100644 --- a/app/Import/Converter/BudgetId.php +++ b/app/Import/Converter/BudgetId.php @@ -12,6 +12,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Budget; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use Log; /** * Class BudgetId @@ -24,11 +27,40 @@ class BudgetId extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return Budget */ public function convert($value) { - throw new FireflyException('Importer with name BudgetId has not yet been configured.'); + $value = intval(trim($value)); + Log::debug('Going to convert using BudgetId', ['value' => $value]); + + if ($value === 0) { + return new Budget; + } + + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepositoryInterface::class, [$this->user]); + + if (isset($this->mapping[$value])) { + Log::debug('Found budget in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $budget = $repository->find(intval($this->mapping[$value])); + if (!is_null($budget->id)) { + Log::debug('Found budget by ID', ['id' => $budget->id]); + + return $budget; + } + } + + // not mapped? Still try to find it first: + $budget = $repository->find($value); + if (!is_null($budget->id)) { + Log::debug('Found budget by ID ', ['id' => $budget->id]); + + return $budget; + } + + // should not really happen. If the ID does not match FF, what is FF supposed to do? + return new Budget; } } \ No newline at end of file diff --git a/app/Import/Converter/BudgetName.php b/app/Import/Converter/BudgetName.php index 6db955bc12..8aeab92dbf 100644 --- a/app/Import/Converter/BudgetName.php +++ b/app/Import/Converter/BudgetName.php @@ -12,6 +12,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Budget; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use Log; /** * Class BudgetName @@ -24,11 +27,47 @@ class BudgetName extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return Budget */ public function convert($value) { - throw new FireflyException('Importer with name BudgetName has not yet been configured.'); + $value = trim($value); + Log::debug('Going to convert using BudgetName', ['value' => $value]); + + if (strlen($value) === 0) { + return new Budget; + } + + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepositoryInterface::class, [$this->user]); + + if (isset($this->mapping[$value])) { + Log::debug('Found budget in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $budget = $repository->find(intval($this->mapping[$value])); + if (!is_null($budget->id)) { + Log::debug('Found budget by ID', ['id' => $budget->id]); + + return $budget; + } + } + + // not mapped? Still try to find it first: + $budget = $repository->findByName($value); + if (!is_null($budget->id)) { + Log::debug('Found budget by name ', ['id' => $budget->id]); + + return $budget; + } + + // create new budget. Use a lot of made up values. + $budget = $repository->store( + [ + 'name' => $value, + 'user_id' => $this->user->id, + ] + ); + + return $budget; } } \ No newline at end of file diff --git a/app/Import/Converter/CategoryId.php b/app/Import/Converter/CategoryId.php index b177835914..4704a81f43 100644 --- a/app/Import/Converter/CategoryId.php +++ b/app/Import/Converter/CategoryId.php @@ -12,6 +12,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Category; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use Log; /** * Class CategoryId @@ -24,11 +27,40 @@ class CategoryId extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return Category */ public function convert($value) { - throw new FireflyException('Importer with name CategoryId has not yet been configured.'); + $value = intval(trim($value)); + Log::debug('Going to convert using CategoryId', ['value' => $value]); + + if ($value === 0) { + return new Category; + } + + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class, [$this->user]); + + if (isset($this->mapping[$value])) { + Log::debug('Found category in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $category = $repository->find(intval($this->mapping[$value])); + if (!is_null($category->id)) { + Log::debug('Found category by ID', ['id' => $category->id]); + + return $category; + } + } + + // not mapped? Still try to find it first: + $category = $repository->find($value); + if (!is_null($category->id)) { + Log::debug('Found category by ID ', ['id' => $category->id]); + + return $category; + } + + // should not really happen. If the ID does not match FF, what is FF supposed to do? + return new Category; } } \ No newline at end of file diff --git a/app/Import/Converter/CategoryName.php b/app/Import/Converter/CategoryName.php index f87a2bd7bc..bfcefeb75b 100644 --- a/app/Import/Converter/CategoryName.php +++ b/app/Import/Converter/CategoryName.php @@ -12,6 +12,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Category; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use Log; /** * Class CategoryName @@ -24,11 +27,47 @@ class CategoryName extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return Category */ public function convert($value) { - throw new FireflyException('Importer with name CategoryName has not yet been configured.'); + $value = trim($value); + Log::debug('Going to convert using CategoryName', ['value' => $value]); + + if (strlen($value) === 0) { + return new Category; + } + + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class, [$this->user]); + + if (isset($this->mapping[$value])) { + Log::debug('Found category in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $category = $repository->find(intval($this->mapping[$value])); + if (!is_null($category->id)) { + Log::debug('Found category by ID', ['id' => $category->id]); + + return $category; + } + } + + // not mapped? Still try to find it first: + $category = $repository->findByName($value); + if (!is_null($category->id)) { + Log::debug('Found category by name ', ['id' => $category->id]); + + return $category; + } + + // create new category. Use a lot of made up values. + $category = $repository->store( + [ + 'name' => $value, + 'user_id' => $this->user->id, + ] + ); + + return $category; } } \ No newline at end of file diff --git a/app/Import/Converter/ConverterInterface.php b/app/Import/Converter/ConverterInterface.php index 87e8fc5d62..ac127f1275 100644 --- a/app/Import/Converter/ConverterInterface.php +++ b/app/Import/Converter/ConverterInterface.php @@ -31,6 +31,11 @@ interface ConverterInterface */ public function setConfig(array $config); + /** + * @return int + */ + public function getCertainty(): int; + /** * @param bool $doMap */ diff --git a/app/Import/Converter/CurrencyId.php b/app/Import/Converter/CurrencyId.php index dbf5cac5e8..1bc4314ac5 100644 --- a/app/Import/Converter/CurrencyId.php +++ b/app/Import/Converter/CurrencyId.php @@ -12,6 +12,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use Log; /** * Class CurrencyId @@ -24,11 +27,40 @@ class CurrencyId extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return TransactionCurrency */ public function convert($value) { - throw new FireflyException('Importer with name CurrencyId has not yet been configured.'); + $value = intval(trim($value)); + Log::debug('Going to convert using CurrencyId', ['value' => $value]); + + if ($value === 0) { + return new TransactionCurrency; + } + + /** @var CurrencyRepositoryInterface $repository */ + $repository = app(CurrencyRepositoryInterface::class, [$this->user]); + + if (isset($this->mapping[$value])) { + Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $currency = $repository->find(intval($this->mapping[$value])); + if (!is_null($currency->id)) { + Log::debug('Found currency by ID', ['id' => $currency->id]); + + return $currency; + } + } + + // not mapped? Still try to find it first: + $currency = $repository->find($value); + if (!is_null($currency->id)) { + Log::debug('Found currency by ID ', ['id' => $currency->id]); + + return $currency; + } + + // should not really happen. If the ID does not match FF, what is FF supposed to do? + return new TransactionCurrency; } } \ No newline at end of file diff --git a/app/Import/Converter/CurrencyName.php b/app/Import/Converter/CurrencyName.php index 70a03fbe0a..f28194d018 100644 --- a/app/Import/Converter/CurrencyName.php +++ b/app/Import/Converter/CurrencyName.php @@ -11,7 +11,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use Log; /** * Class CurrencyName @@ -24,11 +26,48 @@ class CurrencyName extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return TransactionCurrency */ public function convert($value) { - throw new FireflyException('Importer with name CurrencyName has not yet been configured.'); + $value = trim($value); + Log::debug('Going to convert using CurrencyName', ['value' => $value]); + + if ($value === 0) { + return new TransactionCurrency; + } + + /** @var CurrencyRepositoryInterface $repository */ + $repository = app(CurrencyRepositoryInterface::class, [$this->user]); + + if (isset($this->mapping[$value])) { + Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $currency = $repository->find(intval($this->mapping[$value])); + if (!is_null($currency->id)) { + Log::debug('Found currency by ID', ['id' => $currency->id]); + + return $currency; + } + } + + // not mapped? Still try to find it first: + $currency = $repository->findByName($value); + if (!is_null($currency->id)) { + Log::debug('Found currency by name ', ['id' => $currency->id]); + + return $currency; + } + + // create new currency + $currency = $repository->store( + [ + 'name' => $value, + 'code' => strtoupper(substr($value, 0, 3)), + 'symbol' => strtoupper(substr($value, 0, 1)), + ] + ); + + return $currency; } } \ No newline at end of file diff --git a/app/Import/Converter/CurrencySymbol.php b/app/Import/Converter/CurrencySymbol.php index f468ecc6e8..2f829d26d0 100644 --- a/app/Import/Converter/CurrencySymbol.php +++ b/app/Import/Converter/CurrencySymbol.php @@ -11,7 +11,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use Log; /** * Class CurrencySymbol @@ -24,11 +26,48 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return TransactionCurrency */ public function convert($value) { - throw new FireflyException('Importer with name CurrencySymbol has not yet been configured.'); + $value = trim($value); + Log::debug('Going to convert using CurrencySymbol', ['value' => $value]); + + if ($value === 0) { + return new TransactionCurrency; + } + + /** @var CurrencyRepositoryInterface $repository */ + $repository = app(CurrencyRepositoryInterface::class, [$this->user]); + + if (isset($this->mapping[$value])) { + Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $currency = $repository->find(intval($this->mapping[$value])); + if (!is_null($currency->id)) { + Log::debug('Found currency by ID', ['id' => $currency->id]); + + return $currency; + } + } + + // not mapped? Still try to find it first: + $currency = $repository->findBySymbol($value); + if (!is_null($currency->id)) { + Log::debug('Found currency by symbol ', ['id' => $currency->id]); + + return $currency; + } + + // create new currency + $currency = $repository->store( + [ + 'name' => 'Currency ' . $value, + 'code' => $value, + 'symbol' => $value, + ] + ); + + return $currency; } } \ No newline at end of file diff --git a/app/Import/Converter/INGDebetCredit.php b/app/Import/Converter/INGDebetCredit.php index 658127fd2f..cede9dba3a 100644 --- a/app/Import/Converter/INGDebetCredit.php +++ b/app/Import/Converter/INGDebetCredit.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; use FireflyIII\Exceptions\FireflyException; +use Log; /** * Class INGDebetCredit @@ -24,11 +25,19 @@ class INGDebetCredit extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return int */ public function convert($value) { - throw new FireflyException('Importer with name INGDebetCredit has not yet been configured.'); + Log::debug('Going to convert ', ['value' => $value]); + + if ($value === 'Af') { + Log::debug('Return -1'); + return -1; + } + + Log::debug('Return 1'); + return 1; } } \ No newline at end of file diff --git a/app/Import/Converter/OpposingAccountId.php b/app/Import/Converter/OpposingAccountId.php deleted file mode 100644 index c71af34127..0000000000 --- a/app/Import/Converter/OpposingAccountId.php +++ /dev/null @@ -1,34 +0,0 @@ - $value]); + + if (strlen($value) === 0) { + return new Account; + } + + /** @var AccountCrudInterface $repository */ + $repository = app(AccountCrudInterface::class, [$this->user]); + + + if (isset($this->mapping[$value])) { + Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); + $account = $repository->find(intval($this->mapping[$value])); + if (!is_null($account->id)) { + Log::debug('Found account by ID', ['id' => $account->id]); + + return $account; + } + } + + // not mapped? Still try to find it first: + $account = $repository->findByAccountNumber($value, []); + if (!is_null($account->id)) { + Log::debug('Found account by name', ['id' => $account->id]); + + return $account; + } + + + $account = $repository->store( + ['name' => 'Account with number ' . $value, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id, 'accountType' => 'asset', + 'virtualBalance' => 0, 'active' => true] + ); + + return $account; } } \ No newline at end of file diff --git a/app/Import/Converter/RabobankDebetCredit.php b/app/Import/Converter/RabobankDebetCredit.php index ca450b5b76..01feb80c59 100644 --- a/app/Import/Converter/RabobankDebetCredit.php +++ b/app/Import/Converter/RabobankDebetCredit.php @@ -31,9 +31,13 @@ class RabobankDebetCredit extends BasicConverter implements ConverterInterface Log::debug('Going to convert ', ['value' => $value]); if ($value === 'D') { + Log::debug('Return -1'); + return -1; } + Log::debug('Return 1'); + return 1; } } \ No newline at end of file diff --git a/app/Import/Converter/TagsComma.php b/app/Import/Converter/TagsComma.php index 78064397a4..7b02e7972b 100644 --- a/app/Import/Converter/TagsComma.php +++ b/app/Import/Converter/TagsComma.php @@ -11,7 +11,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use Illuminate\Support\Collection; +use Log; /** * Class TagsComma @@ -24,11 +26,59 @@ class TagsComma extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return Collection */ public function convert($value) { - throw new FireflyException('Importer with name TagsComma has not yet been configured.'); + $value = trim($value); + Log::debug('Going to convert using TagsComma', ['value' => $value]); + if (strlen($value) === 0) { + return new Collection; + } + $parts = array_unique(explode(',', $value)); + $set = new Collection; + Log::debug('Exploded parts.', $parts); + + /** @var TagRepositoryInterface $repository */ + $repository = app(TagRepositoryInterface::class, [$this->user]); + + + /** @var string $part */ + foreach ($parts as $part) { + if (isset($this->mapping[$part])) { + Log::debug('Found tag in mapping. Should exist.', ['value' => $part, 'map' => $this->mapping[$part]]); + $tag = $repository->find(intval($this->mapping[$part])); + if (!is_null($tag->id)) { + Log::debug('Found tag by ID', ['id' => $tag->id]); + + $set->push($tag); + continue; + } + } + // not mapped? Still try to find it first: + $tag = $repository->findByTag($part); + if (!is_null($tag->id)) { + Log::debug('Found tag by name ', ['id' => $tag->id]); + + $set->push($tag); + continue; + } + // create new tag + $tag = $repository->store( + [ + 'tag' => $part, + 'date' => null, + 'description' => $part, + 'latitude' => null, + 'longitude' => null, + 'zoomLevel' => null, + 'tagMode' => 'nothing', + ] + ); + Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); + $set->push($tag); + } + return $set; } } \ No newline at end of file diff --git a/app/Import/Converter/TagsSpace.php b/app/Import/Converter/TagsSpace.php index b522281e9a..211e85c0d3 100644 --- a/app/Import/Converter/TagsSpace.php +++ b/app/Import/Converter/TagsSpace.php @@ -11,7 +11,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use Illuminate\Support\Collection; +use Log; /** * Class TagsSpace @@ -24,11 +26,61 @@ class TagsSpace extends BasicConverter implements ConverterInterface /** * @param $value * - * @throws FireflyException + * @return Collection */ public function convert($value) { - throw new FireflyException('Importer with name TagsSpace has not yet been configured.'); + $value = trim($value); + Log::debug('Going to convert using TagsSpace', ['value' => $value]); + + if (strlen($value) === 0) { + return new Collection; + } + $parts = array_unique(explode(' ', $value)); + $set = new Collection; + Log::debug('Exploded parts.', $parts); + + /** @var TagRepositoryInterface $repository */ + $repository = app(TagRepositoryInterface::class, [$this->user]); + + + /** @var string $part */ + foreach ($parts as $part) { + if (isset($this->mapping[$part])) { + Log::debug('Found tag in mapping. Should exist.', ['value' => $part, 'map' => $this->mapping[$part]]); + $tag = $repository->find(intval($this->mapping[$part])); + if (!is_null($tag->id)) { + Log::debug('Found tag by ID', ['id' => $tag->id]); + + $set->push($tag); + continue; + } + } + // not mapped? Still try to find it first: + $tag = $repository->findByTag($part); + if (!is_null($tag->id)) { + Log::debug('Found tag by name ', ['id' => $tag->id]); + + $set->push($tag); + continue; + } + // create new tag + $tag = $repository->store( + [ + 'tag' => $part, + 'date' => null, + 'description' => $part, + 'latitude' => null, + 'longitude' => null, + 'zoomLevel' => null, + 'tagMode' => 'nothing', + ] + ); + Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); + $set->push($tag); + } + + return $set; } } \ No newline at end of file diff --git a/app/Import/ImportEntry.php b/app/Import/ImportEntry.php index c69ab325e9..d23310646e 100644 --- a/app/Import/ImportEntry.php +++ b/app/Import/ImportEntry.php @@ -12,7 +12,7 @@ declare(strict_types = 1); namespace FireflyIII\Import; use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Account; +use Log; /** * Class ImportEntry @@ -21,21 +21,39 @@ use FireflyIII\Models\Account; */ class ImportEntry { - /** @var Account */ - public $assetAccount; + public $amount; + /** - * @param $role - * @param $value + * @param string $role + * @param string $value + * @param int $certainty + * @param $convertedValue * * @throws FireflyException */ - public function fromRawValue($role, $value) + public function importValue(string $role, string $value, int $certainty, $convertedValue) { switch ($role) { default: - throw new FireflyException('Cannot handle role of type "' . $role . '".'); + Log::error('Import entry cannot handle object.', ['role' => $role]); + throw new FireflyException('Import entry cannot handle object of type "' . $role . '".'); + break; + case 'amount': + $this->setAmount($convertedValue); + return; + case 'account-id': + + break; } } + + /** + * @param float $amount + */ + private function setAmount(float $amount) + { + $this->amount = $amount; + } } \ No newline at end of file diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 2779fb33cc..731129bdc5 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -485,11 +485,13 @@ class CsvImporter implements ImporterInterface // run the converter for this value: $convertedValue = $converter->convert($value); + $certainty = $converter->getCertainty(); // log it. Log::debug('Value ', ['index' => $index, 'value' => $value, 'role' => $role]); // store in import entry: + $object->importValue($role, $value, $certainty, $convertedValue); // $object->fromRawValue($role, $value); diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php new file mode 100644 index 0000000000..93de3d0602 --- /dev/null +++ b/app/Models/Configuration.php @@ -0,0 +1,62 @@ +attributes['data'] = json_encode($value); + } + + + +} diff --git a/app/Models/Preference.php b/app/Models/Preference.php index cbb7b0512a..b19d4458d7 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -47,6 +47,7 @@ class Preference extends Model * @param $value * * @return mixed + * @throws FireflyException */ public function getDataAttribute($value) { diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 6ea3d33f3e..8380895404 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -13,6 +13,7 @@ namespace FireflyIII\Providers; use FireflyIII\Support\Amount; use FireflyIII\Support\ExpandedForm; +use FireflyIII\Support\FireflyConfig; use FireflyIII\Support\Navigation; use FireflyIII\Support\Preferences; use FireflyIII\Support\Steam; @@ -62,6 +63,12 @@ class FireflyServiceProvider extends ServiceProvider return new Preferences; } ); + + $this->app->bind( + 'fireflyconfig', function () { + return new FireflyConfig; + } + ); $this->app->bind( 'navigation', function () { return new Navigation; diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index c12d3a3fb4..af1994ebce 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -23,7 +23,6 @@ use FireflyIII\User; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; -use Log; /** * Class BudgetRepository @@ -86,6 +85,26 @@ class BudgetRepository implements BudgetRepositoryInterface return $budget; } + /** + * Find a budget. + * + * @param string $name + * + * @return Budget + */ + public function findByName(string $name): Budget + { + $budgets = $this->user->budgets()->get(); + /** @var Budget $budget */ + foreach ($budgets as $budget) { + if ($budget->name === $name) { + return $budget; + } + } + + return new Budget; + } + /** * This method returns the oldest journal or transaction date known to this budget. * Will cache result. @@ -499,5 +518,4 @@ class BudgetRepository implements BudgetRepositoryInterface return $limit; } - } diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index 31bf8d4ed2..ae5a07f3fc 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -24,6 +24,11 @@ use Illuminate\Support\Collection; interface BudgetRepositoryInterface { + /** + * @return bool + */ + public function cleanupBudgets(): bool; + /** * @param Budget $budget * @@ -40,6 +45,15 @@ interface BudgetRepositoryInterface */ public function find(int $budgetId): Budget; + /** + * Find a budget. + * + * @param string $name + * + * @return Budget + */ + public function findByName(string $name): Budget; + /** * This method returns the oldest journal or transaction date known to this budget. * Will cache result. @@ -92,15 +106,6 @@ interface BudgetRepositoryInterface */ public function journalsInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): Collection; - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): string; - /** * @param Collection $budgets * @param Collection $accounts @@ -112,9 +117,13 @@ interface BudgetRepositoryInterface public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) : string; /** - * @return bool + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string */ - public function cleanupBudgets(): bool; + public function spentInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): string; /** * @param array $data diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 4287eb429c..9cd4024928 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -101,6 +101,25 @@ class CategoryRepository implements CategoryRepositoryInterface return $category; } + /** + * Find a category + * + * @param string $name + * + * @return Category + */ + public function findByName(string $name) : Category + { + $categories = $this->user->categories()->get(); + foreach ($categories as $category) { + if ($category->name === $name) { + return $category; + } + } + + return new Category; + } + /** * @param Category $category * @param Collection $accounts @@ -554,4 +573,5 @@ class CategoryRepository implements CategoryRepositoryInterface return $sum; } + } diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 3df5717ab9..cb6d757e32 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -59,6 +59,15 @@ interface CategoryRepositoryInterface */ public function find(int $categoryId) : Category; + /** + * Find a category + * + * @param string $name + * + * @return Category + */ + public function findByName(string $name) : Category; + /** * @param Category $category * @param Collection $accounts @@ -94,15 +103,6 @@ interface CategoryRepositoryInterface */ public function journalsInPeriod(Collection $categories, Collection $accounts, array $types, Carbon $start, Carbon $end): Collection; - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end) : string; - /** * @param Collection $accounts * @param array $types @@ -133,6 +133,15 @@ interface CategoryRepositoryInterface */ public function spentInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end) : string; + /** * @param array $data * diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index b2ae35a528..d83f9ce17f 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -82,6 +82,39 @@ class TagRepository implements TagRepositoryInterface return true; } + /** + * @param int $tagId + * + * @return Tag + */ + public function find(int $tagId) : Tag + { + $tag = $this->user->tags()->find($tagId); + if (is_null($tag)) { + $tag = new Tag; + } + + return $tag; + } + + /** + * @param string $tag + * + * @return Tag + */ + public function findByTag(string $tag) : Tag + { + $tags = $this->user->tags()->get(); + /** @var Tag $tag */ + foreach ($tags as $tag) { + if ($tag->tag === $tag) { + return $tag; + } + } + + return new Tag; + } + /** * @return Collection */ diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index 4f9304c662..fad9b301c8 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -42,6 +42,20 @@ interface TagRepositoryInterface */ public function destroy(Tag $tag): bool; + /** + * @param string $tag + * + * @return Tag + */ + public function findByTag(string $tag) : Tag; + + /** + * @param int $tagId + * + * @return Tag + */ + public function find(int $tagId) : Tag; + /** * This method returns all the user's tags. * diff --git a/app/Support/Facades/FireflyConfig.php b/app/Support/Facades/FireflyConfig.php new file mode 100644 index 0000000000..0b1b47cff8 --- /dev/null +++ b/app/Support/Facades/FireflyConfig.php @@ -0,0 +1,33 @@ +id . $name; + if (Cache::has($fullName)) { + Cache::forget($fullName); + } + Preference::where('user_id', Auth::user()->id)->where('name', $name)->delete(); + + return true; + } + + /** + * @param $name + * @param null $default + * + * @return Configuration|null + */ + public function get($name, $default = null) + { + $fullName = 'ff-config-' . $name; + if (Cache::has($fullName)) { + return Cache::get($fullName); + } + + $config = Configuration::where('name', $name)->first(['id', 'name', 'data']); + + if ($config) { + Cache::forever($fullName, $config); + + return $config; + } + // no preference found and default is null: + if (is_null($default)) { + // return NULL + return null; + } + + return $this->set($name, $default); + + } + + /** + * @param $name + * @param string $value + * + * @return Configuration + */ + public function set($name, $value): Configuration + { + $item = $this->get($name, $value); + $item->data = $value; + $item->save(); + + Cache::forget('ff-config-' . $name); + + return $item; + + } + +} diff --git a/composer.lock b/composer.lock index 8bcb7affc5..6168be6600 100644 --- a/composer.lock +++ b/composer.lock @@ -855,16 +855,16 @@ }, { "name": "laravel/framework", - "version": "v5.2.32", + "version": "5.2.41", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "f688217113f70b01d0e127da9035195415812bef" + "reference": "29ba2e310cfeb42ab6545bcd81ff4c2ec1f6b5c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/f688217113f70b01d0e127da9035195415812bef", - "reference": "f688217113f70b01d0e127da9035195415812bef", + "url": "https://api.github.com/repos/laravel/framework/zipball/29ba2e310cfeb42ab6545bcd81ff4c2ec1f6b5c2", + "reference": "29ba2e310cfeb42ab6545bcd81ff4c2ec1f6b5c2", "shasum": "" }, "require": { @@ -921,7 +921,8 @@ "illuminate/support": "self.version", "illuminate/translation": "self.version", "illuminate/validation": "self.version", - "illuminate/view": "self.version" + "illuminate/view": "self.version", + "tightenco/collect": "self.version" }, "require-dev": { "aws/aws-sdk-php": "~3.0", @@ -980,7 +981,7 @@ "framework", "laravel" ], - "time": "2016-05-17 13:24:40" + "time": "2016-07-20 13:13:06" }, { "name": "laravelcollective/html", @@ -1038,16 +1039,16 @@ }, { "name": "league/commonmark", - "version": "0.13.3", + "version": "0.14.0", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "35816f39eb2498484fbb7b1495633a976ee1a8de" + "reference": "b73c0b7288bd0e6f9f56bd0b20d0657214b91838" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/35816f39eb2498484fbb7b1495633a976ee1a8de", - "reference": "35816f39eb2498484fbb7b1495633a976ee1a8de", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/b73c0b7288bd0e6f9f56bd0b20d0657214b91838", + "reference": "b73c0b7288bd0e6f9f56bd0b20d0657214b91838", "shasum": "" }, "require": { @@ -1076,7 +1077,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.14-dev" + "dev-master": "0.15-dev" } }, "autoload": { @@ -1103,7 +1104,7 @@ "markdown", "parser" ], - "time": "2016-05-21 18:41:30" + "time": "2016-07-02 18:48:39" }, { "name": "league/csv", @@ -1164,16 +1165,16 @@ }, { "name": "league/flysystem", - "version": "1.0.22", + "version": "1.0.25", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "bd73a91703969a2d20ab4bfbf971d6c2cbe36612" + "reference": "a76afa4035931be0c78ca8efc6abf3902362f437" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/bd73a91703969a2d20ab4bfbf971d6c2cbe36612", - "reference": "bd73a91703969a2d20ab4bfbf971d6c2cbe36612", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a76afa4035931be0c78ca8efc6abf3902362f437", + "reference": "a76afa4035931be0c78ca8efc6abf3902362f437", "shasum": "" }, "require": { @@ -1186,7 +1187,7 @@ "ext-fileinfo": "*", "mockery/mockery": "~0.9", "phpspec/phpspec": "^2.2", - "phpunit/phpunit": "~4.8 || ~5.0" + "phpunit/phpunit": "~4.8" }, "suggest": { "ext-fileinfo": "Required for MimeType", @@ -1243,20 +1244,20 @@ "sftp", "storage" ], - "time": "2016-04-28 06:53:12" + "time": "2016-07-18 12:22:57" }, { "name": "monolog/monolog", - "version": "1.19.0", + "version": "1.20.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf" + "reference": "55841909e2bcde01b5318c35f2b74f8ecc86e037" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5f56ed5212dc509c8dc8caeba2715732abb32dbf", - "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/55841909e2bcde01b5318c35f2b74f8ecc86e037", + "reference": "55841909e2bcde01b5318c35f2b74f8ecc86e037", "shasum": "" }, "require": { @@ -1275,8 +1276,8 @@ "php-console/php-console": "^3.1.3", "phpunit/phpunit": "~4.5", "phpunit/phpunit-mock-objects": "2.3.0", - "raven/raven": "^0.13", "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", "swiftmailer/swiftmailer": "~5.3" }, "suggest": { @@ -1288,9 +1289,9 @@ "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", "php-console/php-console": "Allow sending log messages to Google Chrome", - "raven/raven": "Allow sending log messages to a Sentry server", "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" }, "type": "library", "extra": { @@ -1321,7 +1322,7 @@ "logging", "psr-3" ], - "time": "2016-04-12 18:29:35" + "time": "2016-07-02 14:02:10" }, { "name": "mtdowling/cron-expression", @@ -1843,23 +1844,23 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v5.4.2", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "d8db871a54619458a805229a057ea2af33c753e8" + "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d8db871a54619458a805229a057ea2af33c753e8", - "reference": "d8db871a54619458a805229a057ea2af33c753e8", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/4cc92842069c2bbc1f28daaaf1d2576ec4dfe153", + "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "mockery/mockery": "~0.9.1,<0.9.4" + "mockery/mockery": "~0.9.1" }, "type": "library", "extra": { @@ -1892,20 +1893,20 @@ "mail", "mailer" ], - "time": "2016-05-01 08:45:47" + "time": "2016-07-08 11:51:25" }, { "name": "symfony/console", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "34a214710e0714b6efcf40ba3cd1e31373a97820" + "reference": "a7abb7153f6d1da47f87ec50274844e246b09d9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/34a214710e0714b6efcf40ba3cd1e31373a97820", - "reference": "34a214710e0714b6efcf40ba3cd1e31373a97820", + "url": "https://api.github.com/repos/symfony/console/zipball/a7abb7153f6d1da47f87ec50274844e246b09d9f", + "reference": "a7abb7153f6d1da47f87ec50274844e246b09d9f", "shasum": "" }, "require": { @@ -1952,20 +1953,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2016-04-28 09:48:42" + "time": "2016-06-29 07:02:21" }, { "name": "symfony/debug", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "a06d10888a45afd97534506afb058ec38d9ba35b" + "reference": "c54bc3539c3b87e86799533801e8ae0e971d78c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/a06d10888a45afd97534506afb058ec38d9ba35b", - "reference": "a06d10888a45afd97534506afb058ec38d9ba35b", + "url": "https://api.github.com/repos/symfony/debug/zipball/c54bc3539c3b87e86799533801e8ae0e971d78c2", + "reference": "c54bc3539c3b87e86799533801e8ae0e971d78c2", "shasum": "" }, "require": { @@ -2009,20 +2010,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2016-03-30 10:41:14" + "time": "2016-06-29 05:40:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.0.6", + "version": "v3.1.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "807dde98589f9b2b00624dca326740380d78dbbc" + "reference": "7f9839ede2070f53e7e2f0849b9bd14748c434c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/807dde98589f9b2b00624dca326740380d78dbbc", - "reference": "807dde98589f9b2b00624dca326740380d78dbbc", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7f9839ede2070f53e7e2f0849b9bd14748c434c5", + "reference": "7f9839ede2070f53e7e2f0849b9bd14748c434c5", "shasum": "" }, "require": { @@ -2042,7 +2043,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2069,20 +2070,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2016-05-05 06:56:13" + "time": "2016-06-29 05:41:56" }, { "name": "symfony/finder", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "c54e407b35bc098916704e9fd090da21da4c4f52" + "reference": "3eb4e64c6145ef8b92adefb618a74ebdde9e3fe9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/c54e407b35bc098916704e9fd090da21da4c4f52", - "reference": "c54e407b35bc098916704e9fd090da21da4c4f52", + "url": "https://api.github.com/repos/symfony/finder/zipball/3eb4e64c6145ef8b92adefb618a74ebdde9e3fe9", + "reference": "3eb4e64c6145ef8b92adefb618a74ebdde9e3fe9", "shasum": "" }, "require": { @@ -2118,20 +2119,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2016-03-10 11:13:05" + "time": "2016-06-29 05:40:00" }, { "name": "symfony/http-foundation", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "18b24bc32d2495ae79d76e777368786a6536fe31" + "reference": "1341139f906d295baa4f4abd55293d07e25a065a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/18b24bc32d2495ae79d76e777368786a6536fe31", - "reference": "18b24bc32d2495ae79d76e777368786a6536fe31", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1341139f906d295baa4f4abd55293d07e25a065a", + "reference": "1341139f906d295baa4f4abd55293d07e25a065a", "shasum": "" }, "require": { @@ -2171,20 +2172,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2016-04-12 18:09:53" + "time": "2016-06-29 07:02:21" }, { "name": "symfony/http-kernel", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "6a5010978edf0a9646342232531e53bfc7abbcd3" + "reference": "177b63b2d50b63fa6d82ea41359ed9928cc7a1fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6a5010978edf0a9646342232531e53bfc7abbcd3", - "reference": "6a5010978edf0a9646342232531e53bfc7abbcd3", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/177b63b2d50b63fa6d82ea41359ed9928cc7a1fb", + "reference": "177b63b2d50b63fa6d82ea41359ed9928cc7a1fb", "shasum": "" }, "require": { @@ -2192,7 +2193,7 @@ "psr/log": "~1.0", "symfony/debug": "~2.8|~3.0", "symfony/event-dispatcher": "~2.8|~3.0", - "symfony/http-foundation": "~2.8|~3.0" + "symfony/http-foundation": "~2.8.8|~3.0.8|~3.1.2|~3.2" }, "conflict": { "symfony/config": "<2.8" @@ -2253,7 +2254,7 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2016-05-09 22:13:13" + "time": "2016-06-30 16:30:17" }, { "name": "symfony/polyfill-mbstring", @@ -2424,16 +2425,16 @@ }, { "name": "symfony/process", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "53f9407c0bb1c5a79127db8f7bfe12f0f6f3dcdb" + "reference": "d7cde1f9d94d87060204f863779389b61c382eeb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/53f9407c0bb1c5a79127db8f7bfe12f0f6f3dcdb", - "reference": "53f9407c0bb1c5a79127db8f7bfe12f0f6f3dcdb", + "url": "https://api.github.com/repos/symfony/process/zipball/d7cde1f9d94d87060204f863779389b61c382eeb", + "reference": "d7cde1f9d94d87060204f863779389b61c382eeb", "shasum": "" }, "require": { @@ -2469,20 +2470,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2016-04-14 15:30:28" + "time": "2016-06-29 05:40:00" }, { "name": "symfony/routing", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "a6cd168310066176599442aa21f5da86c3f8e0b3" + "reference": "9038984bd9c05ab07280121e9e10f61a7231457b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/a6cd168310066176599442aa21f5da86c3f8e0b3", - "reference": "a6cd168310066176599442aa21f5da86c3f8e0b3", + "url": "https://api.github.com/repos/symfony/routing/zipball/9038984bd9c05ab07280121e9e10f61a7231457b", + "reference": "9038984bd9c05ab07280121e9e10f61a7231457b", "shasum": "" }, "require": { @@ -2544,20 +2545,20 @@ "uri", "url" ], - "time": "2016-05-03 12:23:49" + "time": "2016-06-29 05:40:00" }, { "name": "symfony/translation", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "f7a07af51ea067745a521dab1e3152044a2fb1f2" + "reference": "6bf844e1ee3c820c012386c10427a5c67bbefec8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/f7a07af51ea067745a521dab1e3152044a2fb1f2", - "reference": "f7a07af51ea067745a521dab1e3152044a2fb1f2", + "url": "https://api.github.com/repos/symfony/translation/zipball/6bf844e1ee3c820c012386c10427a5c67bbefec8", + "reference": "6bf844e1ee3c820c012386c10427a5c67bbefec8", "shasum": "" }, "require": { @@ -2608,20 +2609,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2016-03-25 01:41:20" + "time": "2016-06-29 05:40:00" }, { "name": "symfony/var-dumper", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "0e918c269093ba4c77fca14e9424fa74ed16f1a6" + "reference": "2f046e9a9d571f22cc8b26783564876713b06579" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0e918c269093ba4c77fca14e9424fa74ed16f1a6", - "reference": "0e918c269093ba4c77fca14e9424fa74ed16f1a6", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2f046e9a9d571f22cc8b26783564876713b06579", + "reference": "2f046e9a9d571f22cc8b26783564876713b06579", "shasum": "" }, "require": { @@ -2671,20 +2672,20 @@ "debug", "dump" ], - "time": "2016-04-25 11:17:47" + "time": "2016-06-29 05:40:00" }, { "name": "twig/twig", - "version": "v1.24.0", + "version": "v1.24.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8" + "reference": "3566d311a92aae4deec6e48682dc5a4528c4a512" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8", - "reference": "3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/3566d311a92aae4deec6e48682dc5a4528c4a512", + "reference": "3566d311a92aae4deec6e48682dc5a4528c4a512", "shasum": "" }, "require": { @@ -2732,20 +2733,20 @@ "keywords": [ "templating" ], - "time": "2016-01-25 21:22:18" + "time": "2016-05-30 09:11:59" }, { "name": "vlucas/phpdotenv", - "version": "v2.2.1", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "63f37b9395e8041cd4313129c08ece896d06ca8e" + "reference": "9ca5644c536654e9509b9d257f53c58630eb2a6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/63f37b9395e8041cd4313129c08ece896d06ca8e", - "reference": "63f37b9395e8041cd4313129c08ece896d06ca8e", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/9ca5644c536654e9509b9d257f53c58630eb2a6a", + "reference": "9ca5644c536654e9509b9d257f53c58630eb2a6a", "shasum": "" }, "require": { @@ -2757,7 +2758,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" } }, "autoload": { @@ -2782,7 +2783,7 @@ "env", "environment" ], - "time": "2016-04-15 10:48:49" + "time": "2016-06-14 14:14:52" }, { "name": "watson/validating", @@ -2897,28 +2898,31 @@ }, { "name": "barryvdh/laravel-ide-helper", - "version": "v2.1.4", + "version": "v2.2.1", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "f1ebd847aac9a4545325d35108cafc285fe1605f" + "reference": "28af7cd19ca41cc0c63dd1de2b46c2b84d31c463" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/f1ebd847aac9a4545325d35108cafc285fe1605f", - "reference": "f1ebd847aac9a4545325d35108cafc285fe1605f", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/28af7cd19ca41cc0c63dd1de2b46c2b84d31c463", + "reference": "28af7cd19ca41cc0c63dd1de2b46c2b84d31c463", "shasum": "" }, "require": { - "illuminate/console": "5.0.x|5.1.x|5.2.x", - "illuminate/filesystem": "5.0.x|5.1.x|5.2.x", - "illuminate/support": "5.0.x|5.1.x|5.2.x", + "barryvdh/reflection-docblock": "^2.0.4", + "illuminate/console": "^5.0,<5.4", + "illuminate/filesystem": "^5.0,<5.4", + "illuminate/support": "^5.0,<5.4", "php": ">=5.4.0", - "phpdocumentor/reflection-docblock": "^2.0.4", - "symfony/class-loader": "~2.3|~3.0" + "symfony/class-loader": "^2.3|^3.0" }, "require-dev": { - "doctrine/dbal": "~2.3" + "doctrine/dbal": "~2.3", + "phpunit/phpunit": "4.*", + "scrutinizer/ocular": "~1.1", + "squizlabs/php_codesniffer": "~2.3" }, "suggest": { "doctrine/dbal": "Load information from the database about models for phpdocs (~2.3)" @@ -2926,7 +2930,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.2-dev" } }, "autoload": { @@ -2956,7 +2960,56 @@ "phpstorm", "sublime" ], - "time": "2016-03-03 08:45:00" + "time": "2016-07-04 11:52:48" + }, + { + "name": "barryvdh/reflection-docblock", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/ReflectionDocBlock.git", + "reference": "3dcbd98b5d9384a5357266efba8fd29884458e5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/3dcbd98b5d9384a5357266efba8fd29884458e5c", + "reference": "3dcbd98b5d9384a5357266efba8fd29884458e5c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0,<4.5" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Barryvdh": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2016-06-13 19:28:20" }, { "name": "doctrine/instantiator", @@ -3066,12 +3119,12 @@ "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "8bfb4013724c1f62dc267af0e998207ac3fdc226" + "reference": "b7a5e18824117d8b65942e9aa77425d9b7dd7ff8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8bfb4013724c1f62dc267af0e998207ac3fdc226", - "reference": "8bfb4013724c1f62dc267af0e998207ac3fdc226", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b7a5e18824117d8b65942e9aa77425d9b7dd7ff8", + "reference": "b7a5e18824117d8b65942e9aa77425d9b7dd7ff8", "shasum": "" }, "require": { @@ -3106,7 +3159,7 @@ "keywords": [ "test" ], - "time": "2016-04-21 19:47:43" + "time": "2016-07-22 14:03:17" }, { "name": "maximebf/debugbar", @@ -3175,12 +3228,12 @@ "source": { "type": "git", "url": "https://github.com/padraic/mockery.git", - "reference": "ad31ff997d983e0d5d60ac80cfcedcbb4e6c4461" + "reference": "ee06e7b564ea4dc9b90605d894c2626f87df334d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/padraic/mockery/zipball/ad31ff997d983e0d5d60ac80cfcedcbb4e6c4461", - "reference": "ad31ff997d983e0d5d60ac80cfcedcbb4e6c4461", + "url": "https://api.github.com/repos/padraic/mockery/zipball/ee06e7b564ea4dc9b90605d894c2626f87df334d", + "reference": "ee06e7b564ea4dc9b90605d894c2626f87df334d", "shasum": "" }, "require": { @@ -3232,41 +3285,90 @@ "test double", "testing" ], - "time": "2016-05-03 10:17:25" + "time": "2016-07-06 09:05:19" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "2.0.4", + "name": "phpdocumentor/reflection-common", + "version": "1.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", - "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "dflydev/markdown": "~1.0", - "erusev/parsedown": "~1.0" + "phpunit/phpunit": "^4.6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "psr-0": { - "phpDocumentor": [ + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2015-12-27 11:43:31" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "9270140b940ff02e58ec577c237274e92cd40cdd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd", + "reference": "9270140b940ff02e58ec577c237274e92cd40cdd", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.2.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ "src/" ] } @@ -3278,39 +3380,87 @@ "authors": [ { "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" + "email": "me@mikevanriel.com" } ], - "time": "2015-02-03 12:10:50" + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2016-06-10 09:48:41" }, { - "name": "phpspec/prophecy", - "version": "v1.6.0", + "name": "phpdocumentor/type-resolver", + "version": "0.2", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972" + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972", - "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443", + "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2016-06-10 07:14:17" + }, + { + "name": "phpspec/prophecy", + "version": "v1.6.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "58a8137754bc24b25740d4281399a4a3596058e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", + "reference": "58a8137754bc24b25740d4281399a4a3596058e0", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "~2.0", - "sebastian/comparator": "~1.1", - "sebastian/recursion-context": "~1.0" + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1", + "sebastian/recursion-context": "^1.0" }, "require-dev": { - "phpspec/phpspec": "~2.0" + "phpspec/phpspec": "^2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.5.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -3343,7 +3493,7 @@ "spy", "stub" ], - "time": "2016-02-15 07:46:21" + "time": "2016-06-07 08:13:47" }, { "name": "phpunit/php-code-coverage", @@ -3590,16 +3740,16 @@ }, { "name": "phpunit/phpunit", - "version": "4.8.26", + "version": "4.8.27", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74" + "reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc1d8cd5b5de11625979125c5639347896ac2c74", - "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c062dddcb68e44b563f66ee319ddae2b5a322a90", + "reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90", "shasum": "" }, "require": { @@ -3658,7 +3808,7 @@ "testing", "xunit" ], - "time": "2016-05-17 03:09:28" + "time": "2016-07-21 06:48:14" }, { "name": "phpunit/phpunit-mock-objects", @@ -3884,16 +4034,16 @@ }, { "name": "sebastian/exporter", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e" + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", "shasum": "" }, "require": { @@ -3901,12 +4051,13 @@ "sebastian/recursion-context": "~1.0" }, "require-dev": { + "ext-mbstring": "*", "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { @@ -3946,7 +4097,7 @@ "export", "exporter" ], - "time": "2015-06-21 07:55:53" + "time": "2016-06-17 09:04:28" }, { "name": "sebastian/global-state", @@ -4089,16 +4240,16 @@ }, { "name": "symfony/class-loader", - "version": "v3.0.6", + "version": "v3.1.2", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", - "reference": "cbb7e6a9c0213a0cffa5d9065ee8214ca4e83877" + "reference": "0d0ac77c336eb73f35bebdf3e1f3695ac741bbc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/cbb7e6a9c0213a0cffa5d9065ee8214ca4e83877", - "reference": "cbb7e6a9c0213a0cffa5d9065ee8214ca4e83877", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/0d0ac77c336eb73f35bebdf3e1f3695ac741bbc9", + "reference": "0d0ac77c336eb73f35bebdf3e1f3695ac741bbc9", "shasum": "" }, "require": { @@ -4114,7 +4265,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -4141,20 +4292,20 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2016-03-30 10:41:14" + "time": "2016-06-29 05:41:56" }, { "name": "symfony/css-selector", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "65e764f404685f2dc20c057e889b3ad04b2e2db0" + "reference": "b8999c1f33c224b2b66b38253f5e3a838d0d0115" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/65e764f404685f2dc20c057e889b3ad04b2e2db0", - "reference": "65e764f404685f2dc20c057e889b3ad04b2e2db0", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/b8999c1f33c224b2b66b38253f5e3a838d0d0115", + "reference": "b8999c1f33c224b2b66b38253f5e3a838d0d0115", "shasum": "" }, "require": { @@ -4194,20 +4345,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2016-03-04 07:55:57" + "time": "2016-06-29 05:40:00" }, { "name": "symfony/dom-crawler", - "version": "v3.0.6", + "version": "v3.0.8", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "49b588841225b205700e5122fa01911cabada857" + "reference": "62769e3409006b937bb333b29da8df9a8b262975" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/49b588841225b205700e5122fa01911cabada857", - "reference": "49b588841225b205700e5122fa01911cabada857", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/62769e3409006b937bb333b29da8df9a8b262975", + "reference": "62769e3409006b937bb333b29da8df9a8b262975", "shasum": "" }, "require": { @@ -4250,20 +4401,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2016-04-12 18:09:53" + "time": "2016-06-29 05:40:00" }, { "name": "symfony/yaml", - "version": "v3.0.6", + "version": "v3.1.2", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "0047c8366744a16de7516622c5b7355336afae96" + "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/0047c8366744a16de7516622c5b7355336afae96", - "reference": "0047c8366744a16de7516622c5b7355336afae96", + "url": "https://api.github.com/repos/symfony/yaml/zipball/2884c26ce4c1d61aebf423a8b912950fe7c764de", + "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de", "shasum": "" }, "require": { @@ -4272,7 +4423,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -4299,7 +4450,56 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-03-04 07:55:57" + "time": "2016-06-29 05:41:56" + }, + { + "name": "webmozart/assert", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", + "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2015-08-24 13:29:44" } ], "aliases": [], diff --git a/config/app.php b/config/app.php index 7e3766d118..8b47da3aeb 100644 --- a/config/app.php +++ b/config/app.php @@ -152,8 +152,6 @@ return [ Collective\Html\HtmlServiceProvider::class, - - /* * Application Service Providers... */ @@ -165,8 +163,8 @@ return [ // own stuff: -// Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, -// Barryvdh\Debugbar\ServiceProvider::class, + Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, + Barryvdh\Debugbar\ServiceProvider::class, 'DaveJamesMiller\Breadcrumbs\ServiceProvider', 'TwigBridge\ServiceProvider', 'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider', @@ -204,48 +202,49 @@ return [ 'aliases' => [ - 'App' => Illuminate\Support\Facades\App::class, - 'Artisan' => Illuminate\Support\Facades\Artisan::class, - 'Auth' => Illuminate\Support\Facades\Auth::class, - 'Blade' => Illuminate\Support\Facades\Blade::class, - 'Cache' => Illuminate\Support\Facades\Cache::class, - 'Config' => Illuminate\Support\Facades\Config::class, - 'Cookie' => Illuminate\Support\Facades\Cookie::class, - 'Crypt' => Illuminate\Support\Facades\Crypt::class, - 'DB' => Illuminate\Support\Facades\DB::class, - 'Eloquent' => Illuminate\Database\Eloquent\Model::class, - 'Event' => Illuminate\Support\Facades\Event::class, - 'File' => Illuminate\Support\Facades\File::class, - 'Gate' => Illuminate\Support\Facades\Gate::class, - 'Hash' => Illuminate\Support\Facades\Hash::class, - 'Lang' => Illuminate\Support\Facades\Lang::class, - 'Log' => Illuminate\Support\Facades\Log::class, - 'Mail' => Illuminate\Support\Facades\Mail::class, - 'Password' => Illuminate\Support\Facades\Password::class, - 'Queue' => Illuminate\Support\Facades\Queue::class, - 'Redirect' => Illuminate\Support\Facades\Redirect::class, - 'Redis' => Illuminate\Support\Facades\Redis::class, - 'Request' => Illuminate\Support\Facades\Request::class, - 'Response' => Illuminate\Support\Facades\Response::class, - 'Route' => Illuminate\Support\Facades\Route::class, - 'Schema' => Illuminate\Support\Facades\Schema::class, - 'Session' => Illuminate\Support\Facades\Session::class, - 'Storage' => Illuminate\Support\Facades\Storage::class, - 'URL' => Illuminate\Support\Facades\URL::class, - 'Validator' => Illuminate\Support\Facades\Validator::class, - 'View' => Illuminate\Support\Facades\View::class, - 'Twig' => 'TwigBridge\Facade\Twig', - 'Form' => Collective\Html\FormFacade::class, - 'Html' => Collective\Html\HtmlFacade::class, - 'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade', - 'Preferences' => 'FireflyIII\Support\Facades\Preferences', - 'Navigation' => 'FireflyIII\Support\Facades\Navigation', - 'Amount' => 'FireflyIII\Support\Facades\Amount', - 'Steam' => 'FireflyIII\Support\Facades\Steam', - 'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm', - 'Entrust' => 'Zizaco\Entrust\EntrustFacade', - 'Input' => 'Illuminate\Support\Facades\Input', - 'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade', + 'App' => Illuminate\Support\Facades\App::class, + 'Artisan' => Illuminate\Support\Facades\Artisan::class, + 'Auth' => Illuminate\Support\Facades\Auth::class, + 'Blade' => Illuminate\Support\Facades\Blade::class, + 'Cache' => Illuminate\Support\Facades\Cache::class, + 'Config' => Illuminate\Support\Facades\Config::class, + 'Cookie' => Illuminate\Support\Facades\Cookie::class, + 'Crypt' => Illuminate\Support\Facades\Crypt::class, + 'DB' => Illuminate\Support\Facades\DB::class, + 'Eloquent' => Illuminate\Database\Eloquent\Model::class, + 'Event' => Illuminate\Support\Facades\Event::class, + 'File' => Illuminate\Support\Facades\File::class, + 'Gate' => Illuminate\Support\Facades\Gate::class, + 'Hash' => Illuminate\Support\Facades\Hash::class, + 'Lang' => Illuminate\Support\Facades\Lang::class, + 'Log' => Illuminate\Support\Facades\Log::class, + 'Mail' => Illuminate\Support\Facades\Mail::class, + 'Password' => Illuminate\Support\Facades\Password::class, + 'Queue' => Illuminate\Support\Facades\Queue::class, + 'Redirect' => Illuminate\Support\Facades\Redirect::class, + 'Redis' => Illuminate\Support\Facades\Redis::class, + 'Request' => Illuminate\Support\Facades\Request::class, + 'Response' => Illuminate\Support\Facades\Response::class, + 'Route' => Illuminate\Support\Facades\Route::class, + 'Schema' => Illuminate\Support\Facades\Schema::class, + 'Session' => Illuminate\Support\Facades\Session::class, + 'Storage' => Illuminate\Support\Facades\Storage::class, + 'URL' => Illuminate\Support\Facades\URL::class, + 'Validator' => Illuminate\Support\Facades\Validator::class, + 'View' => Illuminate\Support\Facades\View::class, + 'Twig' => 'TwigBridge\Facade\Twig', + 'Form' => Collective\Html\FormFacade::class, + 'Html' => Collective\Html\HtmlFacade::class, + 'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade', + 'Preferences' => 'FireflyIII\Support\Facades\Preferences', + 'FireflyConfig' => 'FireflyIII\Support\Facades\FireflyConfig', + 'Navigation' => 'FireflyIII\Support\Facades\Navigation', + 'Amount' => 'FireflyIII\Support\Facades\Amount', + 'Steam' => 'FireflyIII\Support\Facades\Steam', + 'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm', + 'Entrust' => 'Zizaco\Entrust\EntrustFacade', + 'Input' => 'Illuminate\Support\Facades\Input', + 'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade', ], diff --git a/config/csv.php b/config/csv.php index 915b8f724b..f5bab29465 100644 --- a/config/csv.php +++ b/config/csv.php @@ -60,7 +60,7 @@ return [ ], 'bill-id' => [ - 'mappable' => false, + 'mappable' => true, 'pre-process-map' => false, 'field' => 'bill', 'converter' => 'BillId', @@ -179,7 +179,7 @@ return [ 'mappable' => true, 'pre-process-map' => false, 'field' => 'asset-account-id', - 'converter' => 'AssetAccountId', + 'converter' => 'AccountId', 'mapper' => 'AssetAccounts', ], 'account-name' => [ @@ -208,7 +208,7 @@ return [ 'mappable' => true, 'pre-process-map' => false, 'field' => 'opposing-account-id', - 'converter' => 'OpposingAccountId', + 'converter' => 'AccountId', 'mapper' => 'OpposingAccounts', ], 'opposing-name' => [ diff --git a/database/migrations/2016_06_16_000000_create_support_tables.php b/database/migrations/2016_06_16_000000_create_support_tables.php index 2e3fd98741..a379310090 100644 --- a/database/migrations/2016_06_16_000000_create_support_tables.php +++ b/database/migrations/2016_06_16_000000_create_support_tables.php @@ -25,6 +25,7 @@ class CreateSupportTables extends Migration Schema::drop('permissions'); Schema::drop('roles'); Schema::drop('sessions'); + Schema::drop('configuration'); } @@ -79,6 +80,8 @@ class CreateSupportTables extends Migration */ $this->createSessionsTable(); + $this->createConfigurationTable(); + } /** @@ -123,6 +126,23 @@ class CreateSupportTables extends Migration } } + private function createConfigurationTable() + { + if (!Schema::hasTable('configuration')) { + Schema::create( + 'configuration', function (Blueprint $table) { + + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->string('name', 50); + $table->text('data'); + $table->unique(['name']); + } + ); + } + } + /** * */ @@ -176,7 +196,7 @@ class CreateSupportTables extends Migration 'permission_role', function (Blueprint $table) { $table->integer('permission_id')->unsigned(); $table->integer('role_id')->unsigned(); - + $table->foreign('permission_id')->references('id')->on('permissions')->onUpdate('cascade')->onDelete('cascade'); $table->foreign('role_id')->references('id')->on('roles')->onUpdate('cascade')->onDelete('cascade'); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 1861ae752b..ee4fee636f 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -718,6 +718,18 @@ return [ 'user_administration' => 'User administration', 'list_all_users' => 'All users', 'all_users' => 'All users', + 'all_blocked_domains' => 'All blocked domains', + 'blocked_domains' => 'Blocked domains', + 'no_domains_banned' => 'No domains blocked', + 'all_user_domains' => 'All user email address domains', + 'all_domains_is_filtered' => 'This list does not include already blocked domains.', + 'domain_now_blocked' => 'Domain :domain is now blocked', + 'domain_now_unblocked' => 'Domain :domain is now unblocked', + 'manual_block_domain' => 'Block a domain by hand', + 'block_domain' => 'Block domain', + 'no_domain_filled_in' => 'No domain filled in', + 'domain_already_blocked' => 'Domain :domain is already blocked', + 'domain_is_now_blocked' => 'Domain :domain is now blocked', // split a transaction: 'transaction_meta_data' => 'Transaction meta-data', diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 0caec98f1c..fa1f42a199 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -131,6 +131,9 @@ return [ 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.', 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.', + // admin + 'domain' => 'Domain', + // import 'import_file' => 'Import file', 'configuration_file' => 'Configuration file', diff --git a/resources/lang/en_US/list.php b/resources/lang/en_US/list.php index 8a69fced9e..e66b2afcda 100644 --- a/resources/lang/en_US/list.php +++ b/resources/lang/en_US/list.php @@ -8,54 +8,56 @@ */ return [ - 'buttons' => 'Buttons', - 'icon' => 'Icon', - 'create_date' => 'Created at', - 'update_date' => 'Updated at', - 'balance_before' => 'Balance before', - 'balance_after' => 'Balance after', - 'name' => 'Name', - 'role' => 'Role', - 'currentBalance' => 'Current balance', - 'active' => 'Is active?', - 'lastActivity' => 'Last activity', - 'balanceDiff' => 'Balance difference between :start and :end', - 'matchedOn' => 'Matched on', - 'matchesOn' => 'Matched on', - 'account_type' => 'Account type', - 'new_balance' => 'New balance', - 'account' => 'Account', - 'matchingAmount' => 'Amount', - 'lastMatch' => 'Last match', - 'split_number' => 'Split #', - 'destination' => 'Destination', - 'expectedMatch' => 'Expected match', - 'automatch' => 'Auto match?', - 'repeat_freq' => 'Repeats', - 'description' => 'Description', - 'amount' => 'Amount', - 'date' => 'Date', - 'interest_date' => 'Interest date', - 'book_date' => 'Book date', - 'process_date' => 'Processing date', - 'from' => 'From', - 'piggy_bank' => 'Piggy bank', - 'to' => 'To', - 'budget' => 'Budget', - 'category' => 'Category', - 'bill' => 'Bill', - 'withdrawal' => 'Withdrawal', - 'deposit' => 'Deposit', - 'transfer' => 'Transfer', - 'type' => 'Type', - 'completed' => 'Completed', - 'iban' => 'IBAN', - 'paid_current_period' => 'Paid this period', - 'email' => 'Email', - 'registered_at' => 'Registered at', - 'is_activated' => 'Is activated', - 'is_blocked' => 'Is blocked', - 'is_admin' => 'Is admin', - 'has_two_factor' => 'Has 2FA', - 'blocked_code' => 'Block code', + 'buttons' => 'Buttons', + 'icon' => 'Icon', + 'create_date' => 'Created at', + 'update_date' => 'Updated at', + 'balance_before' => 'Balance before', + 'balance_after' => 'Balance after', + 'name' => 'Name', + 'role' => 'Role', + 'currentBalance' => 'Current balance', + 'active' => 'Is active?', + 'lastActivity' => 'Last activity', + 'balanceDiff' => 'Balance difference between :start and :end', + 'matchedOn' => 'Matched on', + 'matchesOn' => 'Matched on', + 'account_type' => 'Account type', + 'new_balance' => 'New balance', + 'account' => 'Account', + 'matchingAmount' => 'Amount', + 'lastMatch' => 'Last match', + 'split_number' => 'Split #', + 'destination' => 'Destination', + 'expectedMatch' => 'Expected match', + 'automatch' => 'Auto match?', + 'repeat_freq' => 'Repeats', + 'description' => 'Description', + 'amount' => 'Amount', + 'date' => 'Date', + 'interest_date' => 'Interest date', + 'book_date' => 'Book date', + 'process_date' => 'Processing date', + 'from' => 'From', + 'piggy_bank' => 'Piggy bank', + 'to' => 'To', + 'budget' => 'Budget', + 'category' => 'Category', + 'bill' => 'Bill', + 'withdrawal' => 'Withdrawal', + 'deposit' => 'Deposit', + 'transfer' => 'Transfer', + 'type' => 'Type', + 'completed' => 'Completed', + 'iban' => 'IBAN', + 'paid_current_period' => 'Paid this period', + 'email' => 'Email', + 'registered_at' => 'Registered at', + 'is_activated' => 'Is activated', + 'is_blocked' => 'Is blocked', + 'is_admin' => 'Is admin', + 'has_two_factor' => 'Has 2FA', + 'blocked_code' => 'Block code', + 'domain' => 'Domain', + 'registration_attempts' => 'Registration attempts', ]; diff --git a/resources/views/admin/index.twig b/resources/views/admin/index.twig index 55c45db3d5..ea8a12b066 100644 --- a/resources/views/admin/index.twig +++ b/resources/views/admin/index.twig @@ -13,6 +13,7 @@ diff --git a/resources/views/admin/users/domains.twig b/resources/views/admin/users/domains.twig new file mode 100644 index 0000000000..2da0fe9ad2 --- /dev/null +++ b/resources/views/admin/users/domains.twig @@ -0,0 +1,120 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists }} +{% endblock %} +{% block content %} +
    +
    +
    +
    +

    {{ 'all_blocked_domains'|_ }}

    +
    +
    + {% if domains|length > 0 %} + + + + + + + + + + {% for domain in domains %} + + + + + + {% endfor %} + +
     {{ trans('list.domain') }}{{ trans('list.is_blocked') }}
    + unblock + {{ domain }} + +
    + {% else %} +

    + {{ 'no_domains_banned'|_ }} +

    + {% endif %} +
    +
    +
    +
    + + + +
    +
    +
    +
    +

    {{ 'all_user_domains'|_ }}

    +
    +
    + {% if knownDomains|length > 0 %} +

    + {{ 'all_domains_is_filtered'|_ }} + +

    + + + + + + + + + {% for domain in knownDomains %} + + + + + + {% endfor %} + +
     {{ trans('list.domain') }}
    block{{ domain }}
    + {% else %} +

    + {{ 'no_domains_banned'|_ }} +

    + {% endif %} +
    +
    +
    +
    + + +
    +
    +
    +
    +

    {{ 'manual_block_domain'|_ }}

    +
    +
    + +
    +
    + + + + + + {{ ExpandedForm.text('domain') }} + + + + + +
    +
    +
    +
    +
    +
    + + +{% endblock %} From c287bc139c64c3a7e303286430fcf29c258ee13b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 24 Jul 2016 18:51:39 +0200 Subject: [PATCH 46/94] Check new list. --- .env.example | 2 -- app/Http/Controllers/Auth/AuthController.php | 12 ++---------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/.env.example b/.env.example index df8630b39b..0d3284ab7e 100755 --- a/.env.example +++ b/.env.example @@ -42,5 +42,3 @@ SHOW_DEMO_WARNING=false ANALYTICS_ID= RUNCLEANUP=true SITE_OWNER=mail@example.com - -BLOCKED_DOMAINS= \ No newline at end of file diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 7cec83dee4..366a7e5258 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -15,6 +15,7 @@ use Auth; use FireflyIII\Events\UserRegistration; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\User; use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers; use Illuminate\Foundation\Auth\ThrottlesLogins; @@ -177,16 +178,7 @@ class AuthController extends Controller */ protected function getBlockedDomains() { - $set = explode(',', env('BLOCKED_DOMAINS', '')); - $domains = []; - foreach ($set as $entry) { - $domain = trim($entry); - if (strlen($domain) > 0) { - $domains[] = $domain; - } - } - - return $domains; + return FireflyConfig::get('blocked-domains')->data; } /** From 684f6e0b5c98ea5eb54e199c0a86f97ba67e95c6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 24 Jul 2016 19:07:47 +0200 Subject: [PATCH 47/94] Don't need these. --- config/app.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/app.php b/config/app.php index 8b47da3aeb..bef9fdc3bd 100644 --- a/config/app.php +++ b/config/app.php @@ -163,8 +163,8 @@ return [ // own stuff: - Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, - Barryvdh\Debugbar\ServiceProvider::class, +// Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, +// Barryvdh\Debugbar\ServiceProvider::class, 'DaveJamesMiller\Breadcrumbs\ServiceProvider', 'TwigBridge\ServiceProvider', 'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider', From 90865a52846041fd7130f28cb37acb2d1ba7ce1e Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 24 Jul 2016 19:24:02 +0200 Subject: [PATCH 48/94] Fix recursive. --- app/Support/FireflyConfig.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/Support/FireflyConfig.php b/app/Support/FireflyConfig.php index 4368df4cc4..649f49a48f 100644 --- a/app/Support/FireflyConfig.php +++ b/app/Support/FireflyConfig.php @@ -15,6 +15,7 @@ use Auth; use Cache; use FireflyIII\Models\Configuration; use FireflyIII\Models\Preference; +use Log; /** * Class FireflyConfig @@ -48,8 +49,11 @@ class FireflyConfig */ public function get($name, $default = null) { + Log::debug('Now in FFConfig::get()', ['name' => $name]); $fullName = 'ff-config-' . $name; if (Cache::has($fullName)) { + Log::debug('Return cache.'); + return Cache::get($fullName); } @@ -57,15 +61,20 @@ class FireflyConfig if ($config) { Cache::forever($fullName, $config); + Log::debug('Return found one.'); return $config; } // no preference found and default is null: if (is_null($default)) { // return NULL + Log::debug('Return null.'); + return null; } + Log::debug('Return this->set().'); + return $this->set($name, $default); } @@ -78,7 +87,10 @@ class FireflyConfig */ public function set($name, $value): Configuration { - $item = $this->get($name, $value); + // + + $item = new Configuration; + $item->name = $name; $item->data = $value; $item->save(); From ce191fa6d3885f0ed8d488723f84c3ff165320dc Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 26 Jul 2016 20:26:46 +0200 Subject: [PATCH 49/94] Fix split deposit. --- app/Crud/Split/Journal.php | 6 +- app/Http/Requests/SplitJournalFormRequest.php | 12 +- resources/lang/en_US/list.php | 1 + resources/views/split/journals/create.twig | 311 +++++++++--------- resources/views/split/journals/edit.twig | 5 +- 5 files changed, 168 insertions(+), 167 deletions(-) diff --git a/app/Crud/Split/Journal.php b/app/Crud/Split/Journal.php index 8930331d56..cbb4e34c35 100644 --- a/app/Crud/Split/Journal.php +++ b/app/Crud/Split/Journal.php @@ -105,9 +105,6 @@ class Journal implements JournalInterface */ public function updateJournal(TransactionJournal $journal, array $data): TransactionJournal { - echo '
    ';
    -        print_r($data);
    -
             $journal->description             = $data['journal_description'];
             $journal->transaction_currency_id = $data['journal_currency_id'];
             $journal->date                    = $data['date'];
    @@ -167,7 +164,8 @@ class Journal implements JournalInterface
         {
             $destinationAccount = Account::where('user_id', $this->user->id)->where('id', $data['destination_account_id'])->first(['accounts.*']);
     
    -        if (strlen($data['source_account_name']) > 0) {
    +
    +        if (isset($data['source_account_name']) && strlen($data['source_account_name']) > 0) {
                 $sourceType    = AccountType::where('type', 'Revenue account')->first();
                 $sourceAccount = Account::firstOrCreateEncrypted(
                     ['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name'], 'active' => 1]
    diff --git a/app/Http/Requests/SplitJournalFormRequest.php b/app/Http/Requests/SplitJournalFormRequest.php
    index 5213fb182e..67ff3b438b 100644
    --- a/app/Http/Requests/SplitJournalFormRequest.php
    +++ b/app/Http/Requests/SplitJournalFormRequest.php
    @@ -59,14 +59,10 @@ class SplitJournalFormRequest extends Request
                     'amount'                   => round($this->get('amount')[$index], 2),
                     'budget_id'                => $this->get('budget_id')[$index] ? intval($this->get('budget_id')[$index]) : 0,
                     'category'                 => $this->get('category')[$index] ?? '',
    -                'source_account_id'        => intval($this->get('journal_source_account_id')),
    -                'source_account_name'      => $this->get('journal_source_account_name'),
    -                'piggy_bank_id'            => isset($this->get('piggy_bank_id')[$index])
    -                    ? intval($this->get('piggy_bank_id')[$index])
    -                    : 0,
    -                'destination_account_id'   => isset($this->get('destination_account_id')[$index])
    -                    ? intval($this->get('destination_account_id')[$index])
    -                    : intval($this->get('journal_destination_account_id')),
    +                'source_account_id'        => isset($this->get('source_account_id')[$index]) ? intval($this->get('source_account_id')[$index]) : intval($this->get('journal_source_account_id')),
    +                'source_account_name'      => $this->get('source_account_name')[$index] ?? '',
    +                'piggy_bank_id'            => isset($this->get('piggy_bank_id')[$index]) ? intval($this->get('piggy_bank_id')[$index]) : 0,
    +                'destination_account_id'   => isset($this->get('destination_account_id')[$index]) ? intval($this->get('destination_account_id')[$index]) : intval($this->get('journal_destination_account_id')),
                     'destination_account_name' => $this->get('destination_account_name')[$index] ?? '',
                 ];
                 $data['transactions'][] = $transaction;
    diff --git a/resources/lang/en_US/list.php b/resources/lang/en_US/list.php
    index e66b2afcda..743b67adfa 100644
    --- a/resources/lang/en_US/list.php
    +++ b/resources/lang/en_US/list.php
    @@ -29,6 +29,7 @@ return [
         'lastMatch'             => 'Last match',
         'split_number'          => 'Split #',
         'destination'           => 'Destination',
    +    'source'                => 'Source',
         'expectedMatch'         => 'Expected match',
         'automatch'             => 'Auto match?',
         'repeat_freq'           => 'Repeats',
    diff --git a/resources/views/split/journals/create.twig b/resources/views/split/journals/create.twig
    index 90acdc41f4..cb995a1865 100644
    --- a/resources/views/split/journals/create.twig
    +++ b/resources/views/split/journals/create.twig
    @@ -7,193 +7,196 @@
         
    - - - + + + - {% if errors.all()|length > 0 %} + {% if errors.all()|length > 0 %} +
    +
    +
    +
    +

    {{ 'errors'|_ }}

    +
    +
    +
      + {% for key, err in errors.all() %} +
    • {{ err }}
    • + {% endfor %} +
    +
    +
    +
    +
    + {% endif %}
    -
    -
    +
    +
    -

    {{ 'errors'|_ }}

    +

    {{ 'transaction_meta_data'|_ }}

    -
      - {% for key, err in errors.all() %} -
    • {{ err }}
    • - {% endfor %} -
    + {{ ExpandedForm.text('journal_description', journal.description) }} + {{ ExpandedForm.select('journal_currency_id', currencies, journal.transaction_currency_id) }} + {{ ExpandedForm.staticText('journal_amount', preFilled.journal_amount|formatAmount ) }} + + + {% if preFilled.what == 'withdrawal' or preFilled.what == 'transfer' %} + {{ ExpandedForm.select('journal_source_account_id', assetAccounts, preFilled.journal_source_account_id) }} + {% endif %} + + {% if preFilled.what == 'deposit' %} + {{ ExpandedForm.text('journal_source_account_name', preFilled.journal_source_account_name) }} + {% endif %} + + {% if preFilled.what == 'transfer' %} + {{ ExpandedForm.select('journal_destination_account_id', assetAccounts, preFilled.journal_destination_account_id) }} + {% endif %}
    -
    - {% endif %} -
    -
    -
    -
    -

    {{ 'transaction_meta_data'|_ }}

    -
    -
    - {{ ExpandedForm.text('journal_description', journal.description) }} - {{ ExpandedForm.select('journal_currency_id', currencies, journal.transaction_currency_id) }} - {{ ExpandedForm.staticText('journal_amount', preFilled.journal_amount|formatAmount ) }} - - - {% if preFilled.what == 'withdrawal' or preFilled.what == 'transfer' %} - {{ ExpandedForm.select('journal_source_account_id', assetAccounts, preFilled.journal_source_account_id) }} - {% endif %} - - {% if preFilled.what == 'deposit' %} - {{ ExpandedForm.text('journal_source_account_name', preFilled.journal_source_account_name) }} - {% endif %} - - {% if preFilled.what == 'transfer' %} - {{ ExpandedForm.select('journal_destination_account_id', assetAccounts, preFilled.journal_destination_account_id) }} - {% endif %} +
    +
    +
    +

    {{ 'transaction_dates'|_ }}

    +
    +
    + {{ ExpandedForm.date('date', journal.date) }} + + {{ ExpandedForm.date('interest_date', journal.interest_date) }} + + {{ ExpandedForm.date('book_date', journal.book_date) }} + + {{ ExpandedForm.date('process_date', journal.process_date) }} +
    +
    -
    -
    -
    -

    {{ 'transaction_dates'|_ }}

    -
    -
    - {{ ExpandedForm.date('date', journal.date) }} - - {{ ExpandedForm.date('interest_date', journal.interest_date) }} - - {{ ExpandedForm.date('book_date', journal.book_date) }} - - {{ ExpandedForm.date('process_date', journal.process_date) }} -
    -
    - -
    -
    -
    -
    -
    -
    -

    {{ 'splits'|_ }}

    -
    -
    - - - - - - {% if preFilled.what == 'withdrawal' or preFilled.what == 'deposit' %} - - {% endif %} - - {% if preFilled.what == 'withdrawal' %} - - {% endif %} - - {% if preFilled.what == 'transfer' %} - - {% endif %} - - - - {% for index, descr in preFilled.description %} - - - - - +
    +
    +
    +
    +

    {{ 'splits'|_ }}

    +
    +
    +
    {{ trans('list.split_number') }}{{ trans('list.description') }}{{ trans('list.destination') }}{{ trans('list.amount') }}{{ trans('list.budget') }}{{ trans('list.category') }}{{ trans('list.piggy_bank') }}
    #{{ loop.index }} - -
    + + + + {% if preFilled.what == 'withdrawal' %} - + {% endif %} - - {% if preFilled.what == 'deposit' %} - + {% endif %} - - - + {% if preFilled.what == 'withdrawal' %} - + {% endif %} - + {% if preFilled.what == 'transfer' %} - + {% endif %} - {% endfor %} - -
    {{ trans('list.split_number') }}{{ trans('list.description') }} - - {{ trans('list.destination') }} - {{ Form.select('destination_account_id[]', assetAccounts, preFilled.destination_account_id[index], {class: 'form-control'}) }} - {{ trans('list.source') }} - - {{ trans('list.amount') }} - - {{ trans('list.budget') }} - - {{ trans('list.category') }} - - {{ Form.select('piggy_bank_id[]',piggyBanks, preFilled.piggy_bank_id[index], {class: 'form-control'}) }} - {{ trans('list.piggy_bank') }}
    -

    -
    - {{ 'add_another_split'|_ }} -

    - + {% if preFilled.what == 'withdrawal' %} + + + + {% endif %} + + + {% if preFilled.what == 'deposit' %} + + {{ Form.select('destination_account_id[]', assetAccounts, preFilled.destination_account_id[index], {class: 'form-control'}) }} + + {% endif %} + + + + + + {% if preFilled.what == 'withdrawal' %} + + + + {% endif %} + + + + {% if preFilled.what == 'transfer' %} + + + {{ Form.select('piggy_bank_id[]',piggyBanks, preFilled.piggy_bank_id[index], {class: 'form-control'}) }} + + {% endif %} + + {% endfor %} + + +

    +
    + {{ 'add_another_split'|_ }} +

    + +
    -
    -
    -
    +
    +
    -
    -
    -

    {{ 'optionalFields'|_ }}

    +
    +
    +

    {{ 'optionalFields'|_ }}

    +
    +
    + + {{ ExpandedForm.file('attachments[]', {'multiple': 'multiple','helpText': trans('firefly.upload_max_file_size', {'size': uploadSize|filesize}) }) }} +
    -
    - - {{ ExpandedForm.file('attachments[]', {'multiple': 'multiple','helpText': trans('firefly.upload_max_file_size', {'size': uploadSize|filesize}) }) }} +
    +
    + +
    +
    +

    {{ 'options'|_ }}

    +
    +
    + {{ ExpandedForm.optionsList('create','split-transaction') }} +
    +
    -
    - -
    -
    -

    {{ 'options'|_ }}

    -
    -
    - {{ ExpandedForm.optionsList('create','split-transaction') }} -
    - -
    -
    -
    {% endblock %} diff --git a/resources/views/split/journals/edit.twig b/resources/views/split/journals/edit.twig index 44deb1c9e1..e7fc9e7105 100644 --- a/resources/views/split/journals/edit.twig +++ b/resources/views/split/journals/edit.twig @@ -94,9 +94,12 @@ {{ trans('list.description') }} - {% if preFilled.what == 'withdrawal' or preFilled.what == 'deposit' %} + {% if preFilled.what == 'withdrawal' %} {{ trans('list.destination') }} {% endif %} + {% if preFilled.what == 'deposit' %} + {{ trans('list.source') }} + {% endif %} {{ trans('list.amount') }} From e434de72a3c55f1cc3710e2edf790c1f88abb1a9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 26 Jul 2016 20:40:46 +0200 Subject: [PATCH 50/94] Expand fields. --- public/js/ff/split/journal/from-store.js | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/public/js/ff/split/journal/from-store.js b/public/js/ff/split/journal/from-store.js index 4cbb57d3d7..5c8b0ca708 100644 --- a/public/js/ff/split/journal/from-store.js +++ b/public/js/ff/split/journal/from-store.js @@ -8,9 +8,28 @@ /* globals globalSum */ +var destAccounts = {}; +var srcAccounts = {}; +var categories = {}; $(function () { "use strict"; $('.btn-do-split').click(cloneRow); + + $.getJSON('json/expense-accounts').done(function (data) { + destAccounts = data; + console.log('destAccounts length is now ' + destAccounts.length); + }); + + $.getJSON('json/revenue-accounts').done(function (data) { + srcAccounts = data; + console.log('srcAccounts length is now ' + srcAccounts.length); + }); + + $.getJSON('json/categories').done(function (data) { + categories = data; + console.log('categories length is now ' + categories.length); + }); + $('input[name="amount[]"]').on('input', calculateSum) }); @@ -21,6 +40,19 @@ function cloneRow() { source.removeClass('initial-row'); source.find('.count').text('#' + count); source.find('input[name="amount[]"]').val("").on('input', calculateSum); + if (destAccounts.length > 0) { + console.log('Will be able to extend dest-accounts.'); + source.find('input[name="destination_account_name[]"]').typeahead({source: destAccounts}); + } + + if (destAccounts.length > 0) { + console.log('Will be able to extend src-accounts.'); + source.find('input[name="source_account_name[]"]').typeahead({source: srcAccounts}); + } + if(categories.length > 0) { + console.log('Will be able to extend categories.'); + source.find('input[name="category[]"]').typeahead({source: categories}); + } $('.split-table tbody').append(source); From 7707c81b2dabda66ed2ffb2c433291aeafac6edd Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 29 Jul 2016 21:29:46 +0200 Subject: [PATCH 51/94] Each CSV converter can set the certainty of their conversion. --- app/Import/Converter/AccountId.php | 8 ++++++++ app/Import/Converter/BasicConverter.php | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/app/Import/Converter/AccountId.php b/app/Import/Converter/AccountId.php index 5ba5e19d61..221f5e4c84 100644 --- a/app/Import/Converter/AccountId.php +++ b/app/Import/Converter/AccountId.php @@ -34,6 +34,8 @@ class AccountId extends BasicConverter implements ConverterInterface Log::debug('Going to convert using AssetAccountId', ['value' => $value]); if ($value === 0) { + $this->setCertainty(0); + return new Account; } @@ -47,6 +49,8 @@ class AccountId extends BasicConverter implements ConverterInterface if (!is_null($account->id)) { Log::debug('Found account by ID', ['id' => $account->id]); + $this->setCertainty(100); + return $account; } } @@ -54,11 +58,15 @@ class AccountId extends BasicConverter implements ConverterInterface // not mapped? Still try to find it first: $account = $repository->find($value); if (!is_null($account->id)) { + $this->setCertainty(90); + Log::debug('Found account by ID ', ['id' => $account->id]); return $account; } + $this->setCertainty(0); + // should not really happen. If the ID does not match FF, what is FF supposed to do? return new Account; diff --git a/app/Import/Converter/BasicConverter.php b/app/Import/Converter/BasicConverter.php index f160d0e4fa..d93590c81c 100644 --- a/app/Import/Converter/BasicConverter.php +++ b/app/Import/Converter/BasicConverter.php @@ -40,6 +40,14 @@ class BasicConverter return $this->certainty; } + /** + * @param int $certainty + */ + protected function setCertainty(int $certainty) + { + $this->certainty = $certainty; + } + /** * @param array $config */ From 3682467ae3c028bb8aaa2bc6a5b096de512a3f6b Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 29 Jul 2016 21:40:58 +0200 Subject: [PATCH 52/94] Each CSV converter can set the certainty of their conversion. --- app/Import/Converter/Amount.php | 2 + app/Import/Converter/AssetAccountIban.php | 5 + app/Import/Converter/AssetAccountName.php | 4 +- app/Import/Converter/AssetAccountNumber.php | 3 +- app/Import/Converter/BillId.php | 8 +- app/Import/Converter/BillName.php | 6 +- app/Import/Converter/BudgetId.php | 5 +- app/Import/Converter/BudgetName.php | 6 +- app/Import/Converter/CategoryId.php | 6 +- app/Import/Converter/CategoryName.php | 6 +- app/Import/Converter/CurrencyCode.php | 5 +- app/Import/Converter/CurrencyId.php | 7 +- app/Import/Converter/CurrencyName.php | 6 +- app/Import/Converter/CurrencySymbol.php | 6 +- app/Import/Converter/Date.php | 2 +- app/Import/Converter/Description.php | 1 + app/Import/Converter/INGDebetCredit.php | 2 + app/Import/Converter/OpposingAccountIban.php | 5 +- app/Import/Converter/OpposingAccountName.php | 5 +- .../Converter/OpposingAccountNumber.php | 8 +- app/Import/Converter/RabobankDebetCredit.php | 2 + app/Import/Converter/TagsComma.php | 3 + app/Import/Converter/TagsSpace.php | 2 + public/result.html | 932 ++++++++++++++++++ 24 files changed, 1009 insertions(+), 28 deletions(-) create mode 100644 public/result.html diff --git a/app/Import/Converter/Amount.php b/app/Import/Converter/Amount.php index 274f9b5457..3b8db1f12f 100644 --- a/app/Import/Converter/Amount.php +++ b/app/Import/Converter/Amount.php @@ -56,6 +56,8 @@ class Amount extends BasicConverter implements ConverterInterface $value = str_replace($search, '', $value); } + $this->setCertainty(90); + return round(floatval($value), 4); diff --git a/app/Import/Converter/AssetAccountIban.php b/app/Import/Converter/AssetAccountIban.php index acc1389335..3cbe47bc91 100644 --- a/app/Import/Converter/AssetAccountIban.php +++ b/app/Import/Converter/AssetAccountIban.php @@ -35,6 +35,8 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface Log::debug('Going to convert ', ['value' => $value]); if (strlen($value) === 0) { + $this->setCertainty(0); + return new Account; } @@ -46,6 +48,7 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); $account = $repository->find(intval($this->mapping[$value])); if (!is_null($account->id)) { + $this->setCertainty(100); Log::debug('Found account by ID', ['id' => $account->id]); return $account; @@ -56,6 +59,7 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface $account = $repository->findByIban($value, [AccountType::ASSET]); if (!is_null($account->id)) { Log::debug('Found account by IBAN', ['id' => $account->id]); + $this->setCertainty(50); return $account; } @@ -65,6 +69,7 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface ['name' => 'Account with IBAN ' . $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, 'active' => true] ); + $this->setCertainty(100); return $account; } diff --git a/app/Import/Converter/AssetAccountName.php b/app/Import/Converter/AssetAccountName.php index e1b10b222a..5b309c7702 100644 --- a/app/Import/Converter/AssetAccountName.php +++ b/app/Import/Converter/AssetAccountName.php @@ -35,6 +35,7 @@ class AssetAccountName extends BasicConverter implements ConverterInterface Log::debug('Going to convert using AssetAccountName', ['value' => $value]); if (strlen($value) === 0) { + $this->setCertainty(0); return new Account; } @@ -47,7 +48,7 @@ class AssetAccountName extends BasicConverter implements ConverterInterface $account = $repository->find(intval($this->mapping[$value])); if (!is_null($account->id)) { Log::debug('Found account by ID', ['id' => $account->id]); - + $this->setCertainty(100); return $account; } } @@ -65,6 +66,7 @@ class AssetAccountName extends BasicConverter implements ConverterInterface ['name' => $value, 'iban' => null, 'openingBalance' => 0, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, 'active' => true] ); + $this->setCertainty(100); return $account; diff --git a/app/Import/Converter/AssetAccountNumber.php b/app/Import/Converter/AssetAccountNumber.php index d2373e23c5..aacef8ae7a 100644 --- a/app/Import/Converter/AssetAccountNumber.php +++ b/app/Import/Converter/AssetAccountNumber.php @@ -57,7 +57,7 @@ class AssetAccountNumber extends BasicConverter implements ConverterInterface $account = $repository->findByAccountNumber($value, [AccountType::ASSET]); if (!is_null($account->id)) { Log::debug('Found account by name', ['id' => $account->id]); - + $this->setCertainty(50); return $account; } @@ -66,6 +66,7 @@ class AssetAccountNumber extends BasicConverter implements ConverterInterface ['name' => 'Account with number ' . $value, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, 'active' => true] ); + $this->setCertainty(100); return $account; diff --git a/app/Import/Converter/BillId.php b/app/Import/Converter/BillId.php index 00fa42d0ef..3d6a4cc67e 100644 --- a/app/Import/Converter/BillId.php +++ b/app/Import/Converter/BillId.php @@ -11,7 +11,6 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Bill; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use Log; @@ -35,6 +34,8 @@ class BillId extends BasicConverter implements ConverterInterface Log::debug('Going to convert using BillId', ['value' => $value]); if ($value === 0) { + $this->setCertainty(0); + return new Bill; } @@ -46,6 +47,7 @@ class BillId extends BasicConverter implements ConverterInterface $bill = $repository->find(intval($this->mapping[$value])); if (!is_null($bill->id)) { Log::debug('Found bill by ID', ['id' => $bill->id]); + $this->setCertainty(100); return $bill; } @@ -55,11 +57,15 @@ class BillId extends BasicConverter implements ConverterInterface $bill = $repository->find($value); if (!is_null($bill->id)) { Log::debug('Found bill by ID ', ['id' => $bill->id]); + $this->setCertainty(100); return $bill; } // should not really happen. If the ID does not match FF, what is FF supposed to do? + + $this->setCertainty(0); + return new Bill; } diff --git a/app/Import/Converter/BillName.php b/app/Import/Converter/BillName.php index 7dd8241e3c..6cb443c367 100644 --- a/app/Import/Converter/BillName.php +++ b/app/Import/Converter/BillName.php @@ -35,6 +35,7 @@ class BillName extends BasicConverter implements ConverterInterface Log::debug('Going to convert using BillName', ['value' => $value]); if (strlen($value) === 0) { + $this->setCertainty(0); return new Bill; } @@ -46,7 +47,7 @@ class BillName extends BasicConverter implements ConverterInterface $bill = $repository->find(intval($this->mapping[$value])); if (!is_null($bill->id)) { Log::debug('Found bill by ID', ['id' => $bill->id]); - + $this->setCertainty(100); return $bill; } } @@ -55,7 +56,7 @@ class BillName extends BasicConverter implements ConverterInterface $bill = $repository->findByName($value); if (!is_null($bill->id)) { Log::debug('Found bill by name ', ['id' => $bill->id]); - + $this->setCertainty(100); return $bill; } @@ -75,6 +76,7 @@ class BillName extends BasicConverter implements ConverterInterface ] ); + $this->setCertainty(100); return $bill; diff --git a/app/Import/Converter/BudgetId.php b/app/Import/Converter/BudgetId.php index 71178f3a67..6e50fb0e20 100644 --- a/app/Import/Converter/BudgetId.php +++ b/app/Import/Converter/BudgetId.php @@ -35,6 +35,7 @@ class BudgetId extends BasicConverter implements ConverterInterface Log::debug('Going to convert using BudgetId', ['value' => $value]); if ($value === 0) { + $this->setCertainty(0); return new Budget; } @@ -46,6 +47,7 @@ class BudgetId extends BasicConverter implements ConverterInterface $budget = $repository->find(intval($this->mapping[$value])); if (!is_null($budget->id)) { Log::debug('Found budget by ID', ['id' => $budget->id]); + $this->setCertainty(100); return $budget; } @@ -55,11 +57,12 @@ class BudgetId extends BasicConverter implements ConverterInterface $budget = $repository->find($value); if (!is_null($budget->id)) { Log::debug('Found budget by ID ', ['id' => $budget->id]); - + $this->setCertainty(100); return $budget; } // should not really happen. If the ID does not match FF, what is FF supposed to do? + $this->setCertainty(0); return new Budget; } diff --git a/app/Import/Converter/BudgetName.php b/app/Import/Converter/BudgetName.php index 8aeab92dbf..59bcaf8bbb 100644 --- a/app/Import/Converter/BudgetName.php +++ b/app/Import/Converter/BudgetName.php @@ -35,6 +35,7 @@ class BudgetName extends BasicConverter implements ConverterInterface Log::debug('Going to convert using BudgetName', ['value' => $value]); if (strlen($value) === 0) { + $this->setCertainty(0); return new Budget; } @@ -46,7 +47,7 @@ class BudgetName extends BasicConverter implements ConverterInterface $budget = $repository->find(intval($this->mapping[$value])); if (!is_null($budget->id)) { Log::debug('Found budget by ID', ['id' => $budget->id]); - + $this->setCertainty(100); return $budget; } } @@ -55,7 +56,7 @@ class BudgetName extends BasicConverter implements ConverterInterface $budget = $repository->findByName($value); if (!is_null($budget->id)) { Log::debug('Found budget by name ', ['id' => $budget->id]); - + $this->setCertainty(100); return $budget; } @@ -66,6 +67,7 @@ class BudgetName extends BasicConverter implements ConverterInterface 'user_id' => $this->user->id, ] ); + $this->setCertainty(100); return $budget; diff --git a/app/Import/Converter/CategoryId.php b/app/Import/Converter/CategoryId.php index 4704a81f43..03fefa2cd5 100644 --- a/app/Import/Converter/CategoryId.php +++ b/app/Import/Converter/CategoryId.php @@ -35,6 +35,7 @@ class CategoryId extends BasicConverter implements ConverterInterface Log::debug('Going to convert using CategoryId', ['value' => $value]); if ($value === 0) { + $this->setCertainty(0); return new Category; } @@ -46,7 +47,7 @@ class CategoryId extends BasicConverter implements ConverterInterface $category = $repository->find(intval($this->mapping[$value])); if (!is_null($category->id)) { Log::debug('Found category by ID', ['id' => $category->id]); - + $this->setCertainty(100); return $category; } } @@ -55,11 +56,12 @@ class CategoryId extends BasicConverter implements ConverterInterface $category = $repository->find($value); if (!is_null($category->id)) { Log::debug('Found category by ID ', ['id' => $category->id]); - + $this->setCertainty(100); return $category; } // should not really happen. If the ID does not match FF, what is FF supposed to do? + $this->setCertainty(0); return new Category; } diff --git a/app/Import/Converter/CategoryName.php b/app/Import/Converter/CategoryName.php index bfcefeb75b..ac4f5ce720 100644 --- a/app/Import/Converter/CategoryName.php +++ b/app/Import/Converter/CategoryName.php @@ -35,6 +35,7 @@ class CategoryName extends BasicConverter implements ConverterInterface Log::debug('Going to convert using CategoryName', ['value' => $value]); if (strlen($value) === 0) { + $this->setCertainty(0); return new Category; } @@ -46,7 +47,7 @@ class CategoryName extends BasicConverter implements ConverterInterface $category = $repository->find(intval($this->mapping[$value])); if (!is_null($category->id)) { Log::debug('Found category by ID', ['id' => $category->id]); - + $this->setCertainty(100); return $category; } } @@ -55,7 +56,7 @@ class CategoryName extends BasicConverter implements ConverterInterface $category = $repository->findByName($value); if (!is_null($category->id)) { Log::debug('Found category by name ', ['id' => $category->id]); - + $this->setCertainty(100); return $category; } @@ -66,6 +67,7 @@ class CategoryName extends BasicConverter implements ConverterInterface 'user_id' => $this->user->id, ] ); + $this->setCertainty(100); return $category; diff --git a/app/Import/Converter/CurrencyCode.php b/app/Import/Converter/CurrencyCode.php index 3ac41ce074..9a448eacf5 100644 --- a/app/Import/Converter/CurrencyCode.php +++ b/app/Import/Converter/CurrencyCode.php @@ -40,7 +40,7 @@ class CurrencyCode extends BasicConverter implements ConverterInterface $currency = $repository->find(intval($this->mapping[$value])); if (!is_null($currency->id)) { Log::debug('Found currency by ID', ['id' => $currency->id]); - + $this->setCertainty(100); return $currency; } } @@ -49,7 +49,7 @@ class CurrencyCode extends BasicConverter implements ConverterInterface $currency = $repository->findByCode($value); if (!is_null($currency->id)) { Log::debug('Found currency by code', ['id' => $currency->id]); - + $this->setCertainty(100); return $currency; } $currency = $repository->store( @@ -59,6 +59,7 @@ class CurrencyCode extends BasicConverter implements ConverterInterface 'symbol' => $value, ] ); + $this->setCertainty(100); return $currency; } diff --git a/app/Import/Converter/CurrencyId.php b/app/Import/Converter/CurrencyId.php index 1bc4314ac5..08b8b43775 100644 --- a/app/Import/Converter/CurrencyId.php +++ b/app/Import/Converter/CurrencyId.php @@ -35,6 +35,7 @@ class CurrencyId extends BasicConverter implements ConverterInterface Log::debug('Going to convert using CurrencyId', ['value' => $value]); if ($value === 0) { + $this->setCertainty(0); return new TransactionCurrency; } @@ -46,7 +47,7 @@ class CurrencyId extends BasicConverter implements ConverterInterface $currency = $repository->find(intval($this->mapping[$value])); if (!is_null($currency->id)) { Log::debug('Found currency by ID', ['id' => $currency->id]); - + $this->setCertainty(100); return $currency; } } @@ -55,10 +56,10 @@ class CurrencyId extends BasicConverter implements ConverterInterface $currency = $repository->find($value); if (!is_null($currency->id)) { Log::debug('Found currency by ID ', ['id' => $currency->id]); - + $this->setCertainty(100); return $currency; } - + $this->setCertainty(0); // should not really happen. If the ID does not match FF, what is FF supposed to do? return new TransactionCurrency; diff --git a/app/Import/Converter/CurrencyName.php b/app/Import/Converter/CurrencyName.php index f28194d018..f91c2aa377 100644 --- a/app/Import/Converter/CurrencyName.php +++ b/app/Import/Converter/CurrencyName.php @@ -34,6 +34,7 @@ class CurrencyName extends BasicConverter implements ConverterInterface Log::debug('Going to convert using CurrencyName', ['value' => $value]); if ($value === 0) { + $this->setCertainty(0); return new TransactionCurrency; } @@ -45,7 +46,7 @@ class CurrencyName extends BasicConverter implements ConverterInterface $currency = $repository->find(intval($this->mapping[$value])); if (!is_null($currency->id)) { Log::debug('Found currency by ID', ['id' => $currency->id]); - + $this->setCertainty(100); return $currency; } } @@ -54,7 +55,7 @@ class CurrencyName extends BasicConverter implements ConverterInterface $currency = $repository->findByName($value); if (!is_null($currency->id)) { Log::debug('Found currency by name ', ['id' => $currency->id]); - + $this->setCertainty(100); return $currency; } @@ -66,6 +67,7 @@ class CurrencyName extends BasicConverter implements ConverterInterface 'symbol' => strtoupper(substr($value, 0, 1)), ] ); + $this->setCertainty(100); return $currency; diff --git a/app/Import/Converter/CurrencySymbol.php b/app/Import/Converter/CurrencySymbol.php index 2f829d26d0..00977f3349 100644 --- a/app/Import/Converter/CurrencySymbol.php +++ b/app/Import/Converter/CurrencySymbol.php @@ -34,6 +34,7 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface Log::debug('Going to convert using CurrencySymbol', ['value' => $value]); if ($value === 0) { + $this->setCertainty(0); return new TransactionCurrency; } @@ -45,7 +46,7 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface $currency = $repository->find(intval($this->mapping[$value])); if (!is_null($currency->id)) { Log::debug('Found currency by ID', ['id' => $currency->id]); - + $this->setCertainty(100); return $currency; } } @@ -54,7 +55,7 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface $currency = $repository->findBySymbol($value); if (!is_null($currency->id)) { Log::debug('Found currency by symbol ', ['id' => $currency->id]); - + $this->setCertainty(100); return $currency; } @@ -66,6 +67,7 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface 'symbol' => $value, ] ); + $this->setCertainty(100); return $currency; diff --git a/app/Import/Converter/Date.php b/app/Import/Converter/Date.php index c9e9676b6f..d9619631a2 100644 --- a/app/Import/Converter/Date.php +++ b/app/Import/Converter/Date.php @@ -42,7 +42,7 @@ class Date extends BasicConverter implements ConverterInterface throw new FireflyException(sprintf('Cannot convert "%s" to a valid date using format "%s".', $value, $this->config['date-format'])); } Log::debug('Converted date', ['converted' => $date->toAtomString()]); - + $this->setCertainty(100); return $date; } } \ No newline at end of file diff --git a/app/Import/Converter/Description.php b/app/Import/Converter/Description.php index 18b90ee92e..a8ce132873 100644 --- a/app/Import/Converter/Description.php +++ b/app/Import/Converter/Description.php @@ -29,6 +29,7 @@ class Description extends BasicConverter implements ConverterInterface // this should replace all control characters // but leave utf8 intact: $value = preg_replace('/[\x00-\x1F\x80-\x9F]/u', '', $value); + $this->setCertainty(100); return strval($value); diff --git a/app/Import/Converter/INGDebetCredit.php b/app/Import/Converter/INGDebetCredit.php index cede9dba3a..a6c4da9d69 100644 --- a/app/Import/Converter/INGDebetCredit.php +++ b/app/Import/Converter/INGDebetCredit.php @@ -33,9 +33,11 @@ class INGDebetCredit extends BasicConverter implements ConverterInterface if ($value === 'Af') { Log::debug('Return -1'); + $this->setCertainty(100); return -1; } + $this->setCertainty(100); Log::debug('Return 1'); return 1; diff --git a/app/Import/Converter/OpposingAccountIban.php b/app/Import/Converter/OpposingAccountIban.php index c59f4ffb3c..674134c43b 100644 --- a/app/Import/Converter/OpposingAccountIban.php +++ b/app/Import/Converter/OpposingAccountIban.php @@ -34,6 +34,7 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface Log::debug('Going to convert ', ['value' => $value]); if (strlen($value) === 0) { + $this->setCertainty(0); return new Account; } @@ -46,7 +47,7 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface $account = $repository->find(intval($this->mapping[$value])); if (!is_null($account->id)) { Log::debug('Found account by ID', ['id' => $account->id]); - + $this->setCertainty(100); return $account; } } @@ -59,6 +60,7 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface 'The match between IBAN and account is uncertain because the type of transactions may not have been determined.', ['id' => $account->id, 'iban' => $value] ); + $this->setCertainty(50); return $account; } @@ -67,6 +69,7 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface ['name' => $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'import', 'virtualBalance' => 0, 'active' => true, 'openingBalance' => 0] ); + $this->setCertainty(100); return $account; } diff --git a/app/Import/Converter/OpposingAccountName.php b/app/Import/Converter/OpposingAccountName.php index a5bb8138f2..9d8b4d8623 100644 --- a/app/Import/Converter/OpposingAccountName.php +++ b/app/Import/Converter/OpposingAccountName.php @@ -46,7 +46,7 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface $account = $repository->find(intval($this->mapping[$value])); if (!is_null($account->id)) { Log::debug('Found account by ID', ['id' => $account->id]); - + $this->setCertainty(100); return $account; } } @@ -59,7 +59,7 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface 'The match between name and account is uncertain because the type of transactions may not have been determined.', ['id' => $account->id, 'name' => $value] ); - + $this->setCertainty(50); return $account; } @@ -68,6 +68,7 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface 'openingBalance' => 0, ] ); + $this->setCertainty(100); return $account; } diff --git a/app/Import/Converter/OpposingAccountNumber.php b/app/Import/Converter/OpposingAccountNumber.php index 545baa22c1..e47e596a38 100644 --- a/app/Import/Converter/OpposingAccountNumber.php +++ b/app/Import/Converter/OpposingAccountNumber.php @@ -35,6 +35,7 @@ class OpposingAccountNumber extends BasicConverter implements ConverterInterface Log::debug('Going to convert using OpposingAccountNumber', ['value' => $value]); if (strlen($value) === 0) { + $this->setCertainty(0); return new Account; } @@ -47,7 +48,7 @@ class OpposingAccountNumber extends BasicConverter implements ConverterInterface $account = $repository->find(intval($this->mapping[$value])); if (!is_null($account->id)) { Log::debug('Found account by ID', ['id' => $account->id]); - + $this->setCertainty(100); return $account; } } @@ -55,8 +56,8 @@ class OpposingAccountNumber extends BasicConverter implements ConverterInterface // not mapped? Still try to find it first: $account = $repository->findByAccountNumber($value, []); if (!is_null($account->id)) { - Log::debug('Found account by name', ['id' => $account->id]); - + Log::debug('Found account by number', ['id' => $account->id]); + $this->setCertainty(50); return $account; } @@ -65,6 +66,7 @@ class OpposingAccountNumber extends BasicConverter implements ConverterInterface ['name' => 'Account with number ' . $value, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, 'active' => true] ); + $this->setCertainty(100); return $account; diff --git a/app/Import/Converter/RabobankDebetCredit.php b/app/Import/Converter/RabobankDebetCredit.php index 01feb80c59..3908b3a062 100644 --- a/app/Import/Converter/RabobankDebetCredit.php +++ b/app/Import/Converter/RabobankDebetCredit.php @@ -32,11 +32,13 @@ class RabobankDebetCredit extends BasicConverter implements ConverterInterface if ($value === 'D') { Log::debug('Return -1'); + $this->setCertainty(100); return -1; } Log::debug('Return 1'); + $this->setCertainty(100); return 1; } diff --git a/app/Import/Converter/TagsComma.php b/app/Import/Converter/TagsComma.php index 7b02e7972b..63c8e3e85c 100644 --- a/app/Import/Converter/TagsComma.php +++ b/app/Import/Converter/TagsComma.php @@ -34,6 +34,7 @@ class TagsComma extends BasicConverter implements ConverterInterface Log::debug('Going to convert using TagsComma', ['value' => $value]); if (strlen($value) === 0) { + $this->setCertainty(0); return new Collection; } $parts = array_unique(explode(',', $value)); @@ -79,6 +80,8 @@ class TagsComma extends BasicConverter implements ConverterInterface Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); $set->push($tag); } + $this->setCertainty(100); + return $set; } } \ No newline at end of file diff --git a/app/Import/Converter/TagsSpace.php b/app/Import/Converter/TagsSpace.php index 211e85c0d3..c5c5f9a505 100644 --- a/app/Import/Converter/TagsSpace.php +++ b/app/Import/Converter/TagsSpace.php @@ -34,6 +34,7 @@ class TagsSpace extends BasicConverter implements ConverterInterface Log::debug('Going to convert using TagsSpace', ['value' => $value]); if (strlen($value) === 0) { + $this->setCertainty(0); return new Collection; } $parts = array_unique(explode(' ', $value)); @@ -79,6 +80,7 @@ class TagsSpace extends BasicConverter implements ConverterInterface Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); $set->push($tag); } + $this->setCertainty(100); return $set; diff --git a/public/result.html b/public/result.html new file mode 100644 index 0000000000..d28b8ce89d --- /dev/null +++ b/public/result.html @@ -0,0 +1,932 @@ +PHPMD +

    PHPMD report

    Problems found

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    #FileLineProblem
    1/sites/firefly-iii/app/Crud/Account/AccountCrud.php192The method update() has an NPath complexity of 210. The configured NPath complexity threshold is 200.
    2/sites/firefly-iii/app/Crud/Account/AccountCrud.php192The method update() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    3/sites/firefly-iii/app/Crud/Account/AccountCrud.php192The method update() has an NPath complexity of 210. The configured NPath complexity threshold is 128.
    4/sites/firefly-iii/app/Crud/Account/AccountCrud.php192The method update() has 42 lines of code. Current threshold is set to 40. Avoid really long methods.
    5/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php41The method fix() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    6/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php119The method parseSepaDescription() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5.
    7/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php119The method parseSepaDescription() has 49 lines of code. Current threshold is set to 40. Avoid really long methods.
    8/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php174The method parseTRTPDescription() has a Cyclomatic Complexity of 10. The configured cyclomatic complexity threshold is 10.
    9/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php174The method parseTRTPDescription() has a Cyclomatic Complexity of 10. The configured cyclomatic complexity threshold is 5.
    10/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php174The method parseTRTPDescription() has 51 lines of code. Current threshold is set to 40. Avoid really long methods.
    11/sites/firefly-iii/app/Helpers/Report/BalanceReportHelper.php34The class BalanceReportHelper has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13.
    12/sites/firefly-iii/app/Helpers/Report/BalanceReportHelper.php268The method removeUnusedBudgets() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    13/sites/firefly-iii/app/Helpers/Report/BudgetReportHelper.php50The method getBudgetReport() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    14/sites/firefly-iii/app/Helpers/Report/BudgetReportHelper.php50The method getBudgetReport() has 57 lines of code. Current threshold is set to 40. Avoid really long methods.
    15/sites/firefly-iii/app/Http/Controllers/Admin/UserController.php30The method index() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    16/sites/firefly-iii/app/Http/Controllers/AttachmentController.php33The class AttachmentController has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13.
    17/sites/firefly-iii/app/Http/Controllers/Auth/AuthController.php36The class AuthController has a coupling between objects value of 15. Consider to reduce the number of dependencies under 13.
    18/sites/firefly-iii/app/Http/Controllers/Auth/AuthController.php64The method login() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    19/sites/firefly-iii/app/Http/Controllers/Auth/PasswordController.php60The method sendResetLinkEmail() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    20/sites/firefly-iii/app/Http/Controllers/Auth/TwoFactorController.php75Avoid unused parameters such as '$request'.
    21/sites/firefly-iii/app/Http/Controllers/CategoryController.php34The class CategoryController has 11 public methods. Consider refactoring CategoryController to keep number of public methods under 10.
    22/sites/firefly-iii/app/Http/Controllers/CategoryController.php34The class CategoryController has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13.
    23/sites/firefly-iii/app/Http/Controllers/CategoryController.php164The method show() has 61 lines of code. Current threshold is set to 40. Avoid really long methods.
    24/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php32The class BudgetController has a coupling between objects value of 13. Consider to reduce the number of dependencies under 13.
    25/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php56The method budget() has 40 lines of code. Current threshold is set to 40. Avoid really long methods.
    26/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php144The method frontpage() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10.
    27/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php144The method frontpage() has an NPath complexity of 8036. The configured NPath complexity threshold is 200.
    28/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php144The method frontpage() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 5.
    29/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php144The method frontpage() has an NPath complexity of 8036. The configured NPath complexity threshold is 128.
    30/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php144The method frontpage() has 66 lines of code. Current threshold is set to 40. Avoid really long methods.
    31/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php222The method multiYear() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    32/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php222The method multiYear() has 60 lines of code. Current threshold is set to 40. Avoid really long methods.
    33/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php222The method multiYear has 5 parameters. Consider to reduce parameter number under 5.
    34/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php292The method period() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    35/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php292The method period() has 47 lines of code. Current threshold is set to 40. Avoid really long methods.
    36/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php292The method period has 5 parameters. Consider to reduce parameter number under 5.
    37/sites/firefly-iii/app/Http/Controllers/Chart/CategoryController.php158The method multiYear() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    38/sites/firefly-iii/app/Http/Controllers/Chart/CategoryController.php158The method multiYear() has 63 lines of code. Current threshold is set to 40. Avoid really long methods.
    39/sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php101The method yearInOut() has 43 lines of code. Current threshold is set to 40. Avoid really long methods.
    40/sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php101The method yearInOut has 5 parameters. Consider to reduce parameter number under 5.
    41/sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php154The method yearInOutSummarized() has 42 lines of code. Current threshold is set to 40. Avoid really long methods.
    42/sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php154The method yearInOutSummarized has 5 parameters. Consider to reduce parameter number under 5.
    43/sites/firefly-iii/app/Http/Controllers/Controller.php30The class Controller has 21 children. Consider to rebalance this class hierarchy to keep number of children under 15.
    44/sites/firefly-iii/app/Http/Controllers/CurrencyController.php31The class CurrencyController has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13.
    45/sites/firefly-iii/app/Http/Controllers/RuleController.php36The class RuleController has 13 public methods. Consider refactoring RuleController to keep number of public methods under 10.
    46/sites/firefly-iii/app/Http/Controllers/RuleController.php36The class RuleController has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13.
    47/sites/firefly-iii/app/Http/Controllers/RuleController.php326Avoid using short method names like RuleController::up(). The configured minimum method name length is 3.
    48/sites/firefly-iii/app/Http/Controllers/TagController.php41The class TagController has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13.
    49/sites/firefly-iii/app/Http/Controllers/TagController.php181The method index() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    50/sites/firefly-iii/app/Http/Controllers/TagController.php181The method index() has an NPath complexity of 131. The configured NPath complexity threshold is 128.
    51/sites/firefly-iii/app/Http/Middleware/AuthenticateTwoFactor.php36The method handle() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    52/sites/firefly-iii/app/Http/Middleware/IsConfirmed.php36The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    53/sites/firefly-iii/app/Http/Middleware/IsNotConfirmed.php36The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    54/sites/firefly-iii/app/Http/Middleware/Range.php61The method handle() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    55/sites/firefly-iii/app/Http/Middleware/RedirectIfTwoFactorAuthenticated.php35The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    56/sites/firefly-iii/app/Http/Requests/Request.php21The class Request has 18 children. Consider to rebalance this class hierarchy to keep number of children under 15.
    57/sites/firefly-iii/app/Jobs/MailError.php67The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    58/sites/firefly-iii/app/Models/Account.php91The method firstOrCreateEncrypted() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    59/sites/firefly-iii/app/Models/TransactionJournal.php94The class TransactionJournal has 23 public methods. Consider refactoring TransactionJournal to keep number of public methods under 10.
    60/sites/firefly-iii/app/Providers/AccountServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    61/sites/firefly-iii/app/Providers/AttachmentServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    62/sites/firefly-iii/app/Providers/BillServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    63/sites/firefly-iii/app/Providers/BudgetServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    64/sites/firefly-iii/app/Providers/CategoryServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    65/sites/firefly-iii/app/Providers/CrudServiceProvider.php46The method registerAccount() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    66/sites/firefly-iii/app/Providers/CrudServiceProvider.php64The method registerJournal() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    67/sites/firefly-iii/app/Providers/ExportJobServiceProvider.php32The method boot() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    68/sites/firefly-iii/app/Providers/FireflyServiceProvider.php36The class FireflyServiceProvider has a coupling between objects value of 15. Consider to reduce the number of dependencies under 13.
    69/sites/firefly-iii/app/Providers/FireflyServiceProvider.php58The method register() has 53 lines of code. Current threshold is set to 40. Avoid really long methods.
    70/sites/firefly-iii/app/Providers/JournalServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    71/sites/firefly-iii/app/Providers/PiggyBankServiceProvider.php42The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    72/sites/firefly-iii/app/Providers/RuleGroupServiceProvider.php42The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    73/sites/firefly-iii/app/Providers/RuleServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    74/sites/firefly-iii/app/Providers/TagServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    75/sites/firefly-iii/app/Repositories/Account/AccountRepository.php35The class AccountRepository has 14 public methods. Consider refactoring AccountRepository to keep number of public methods under 10.
    76/sites/firefly-iii/app/Repositories/Account/AccountRepository.php41Avoid unused private fields such as '$validFields'.
    77/sites/firefly-iii/app/Repositories/Account/AccountRepository.php240The method getPiggyBankAccounts() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    78/sites/firefly-iii/app/Repositories/Account/AccountRepository.php282The method getSavingsAccounts() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    79/sites/firefly-iii/app/Repositories/Account/AccountRepository.php282The method getSavingsAccounts() has 44 lines of code. Current threshold is set to 40. Avoid really long methods.
    80/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php32The class BudgetRepository has 11 public methods. Consider refactoring BudgetRepository to keep number of public methods under 10.
    81/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php84The method firstUseDate() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    82/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php184The method journalsInPeriod() has 60 lines of code. Current threshold is set to 40. Avoid really long methods.
    83/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php252The method journalsInPeriodWithoutBudget() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    84/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php252The method journalsInPeriodWithoutBudget() has 49 lines of code. Current threshold is set to 40. Avoid really long methods.
    85/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php310The method spentInPeriod() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    86/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php310The method spentInPeriod() has 51 lines of code. Current threshold is set to 40. Avoid really long methods.
    87/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php446The method updateLimitAmount() has 43 lines of code. Current threshold is set to 40. Avoid really long methods.
    88/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php446The method updateLimitAmount has 5 parameters. Consider to reduce parameter number under 5.
    89/sites/firefly-iii/app/Repositories/Budget/BudgetRepositoryInterface.php138The method updateLimitAmount has 5 parameters. Consider to reduce parameter number under 5.
    90/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php28The class CategoryRepository has 13 public methods. Consider refactoring CategoryRepository to keep number of public methods under 10.
    91/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php28The class CategoryRepository has an overall complexity of 52 which is very high. The configured complexity threshold is 50.
    92/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php110The method firstUseDate() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5.
    93/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php110The method firstUseDate() has 42 lines of code. Current threshold is set to 40. Avoid really long methods.
    94/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php132Avoid excessively long variable names like $firstTransactionQuery. Keep variable name length under 20.
    95/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php225The method journalsInPeriod() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    96/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php225The method journalsInPeriod() has an NPath complexity of 128. The configured NPath complexity threshold is 128.
    97/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php225The method journalsInPeriod() has 54 lines of code. Current threshold is set to 40. Avoid really long methods.
    98/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php225The method journalsInPeriod has 5 parameters. Consider to reduce parameter number under 5.
    99/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php288The method journalsInPeriodWithoutCategory() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    100/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php288The method journalsInPeriodWithoutCategory() has 53 lines of code. Current threshold is set to 40. Avoid really long methods.
    101/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php348The method lastUseDate() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5.
    102/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php348The method lastUseDate() has 42 lines of code. Current threshold is set to 40. Avoid really long methods.
    103/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php464The method sumInPeriod() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    104/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php464The method sumInPeriod() has 58 lines of code. Current threshold is set to 40. Avoid really long methods.
    105/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php464The method sumInPeriod has 5 parameters. Consider to reduce parameter number under 5.
    106/sites/firefly-iii/app/Repositories/Category/CategoryRepositoryInterface.php95The method journalsInPeriod has 5 parameters. Consider to reduce parameter number under 5.
    107/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php39The class JournalRepository has an overall complexity of 51 which is very high. The configured complexity threshold is 50.
    108/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php39The class JournalRepository has a coupling between objects value of 19. Consider to reduce the number of dependencies under 13.
    109/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php227The method getTransactions() has 57 lines of code. Current threshold is set to 40. Avoid really long methods.
    110/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php290The method store() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    111/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php290The method store() has 64 lines of code. Current threshold is set to 40. Avoid really long methods.
    112/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php392The method update() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    113/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php392The method update() has 55 lines of code. Current threshold is set to 40. Avoid really long methods.
    114/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php482The method storeAccounts() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    115/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php581The method updateTags() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    116/sites/firefly-iii/app/Repositories/Rule/RuleRepository.php26The class RuleRepository has 12 public methods. Consider refactoring RuleRepository to keep number of public methods under 10.
    117/sites/firefly-iii/app/Repositories/Tag/TagRepository.php49The method connect() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    118/sites/firefly-iii/app/Repositories/Tag/TagRepository.php150The method connectAdvancePayment() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    119/sites/firefly-iii/app/Repositories/Tag/TagRepository.php195The method connectBalancingAct() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    120/sites/firefly-iii/app/Repositories/Tag/TagRepository.php235The method matchAll() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    121/sites/firefly-iii/app/Rules/Processor.php179The method triggered() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    122/sites/firefly-iii/app/Rules/TransactionMatcher.php57The method findMatchingTransactions() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    123/sites/firefly-iii/app/Rules/TransactionMatcher.php57The method findMatchingTransactions() has 49 lines of code. Current threshold is set to 40. Avoid really long methods.
    124/sites/firefly-iii/app/Support/Binder/Date.php36The method routeBinder() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    125/sites/firefly-iii/app/Support/CacheProperties.php95The method md5() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    126/sites/firefly-iii/app/Support/ExpandedForm.php29The class ExpandedForm has 18 public methods. Consider refactoring ExpandedForm to keep number of public methods under 10.
    127/sites/firefly-iii/app/Support/ExpandedForm.php211The method makeSelectList() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    128/sites/firefly-iii/app/Support/ExpandedForm.php236The method makeSelectListWithEmpty() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    129/sites/firefly-iii/app/Support/ExpandedForm.php442The method fillFieldValue() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    130/sites/firefly-iii/app/Support/Models/TagSupport.php31The method tagAllowAdvance() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    131/sites/firefly-iii/app/Support/Navigation.php73The method endOfPeriod() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    132/sites/firefly-iii/app/Support/Navigation.php73The method endOfPeriod() has 55 lines of code. Current threshold is set to 40. Avoid really long methods.
    133/sites/firefly-iii/app/Support/Navigation.php213The method startOfPeriod() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    134/sites/firefly-iii/app/Support/Navigation.php213The method startOfPeriod() has 42 lines of code. Current threshold is set to 40. Avoid really long methods.
    135/sites/firefly-iii/app/Support/Navigation.php264The method subtractPeriod() has 52 lines of code. Current threshold is set to 40. Avoid really long methods.
    136/sites/firefly-iii/app/Support/Twig/Journal.php37The method formatAccountPerspective() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    137/sites/firefly-iii/app/Support/Twig/Journal.php37The method formatAccountPerspective() has 46 lines of code. Current threshold is set to 40. Avoid really long methods.
    138/sites/firefly-iii/app/Support/Twig/Journal.php87The method formatBudgetPerspective() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    139/sites/firefly-iii/app/Support/Twig/Journal.php239The method journalBudgets() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    140/sites/firefly-iii/app/Support/Twig/Journal.php276The method journalCategories() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    141/sites/firefly-iii/app/User.php58The class User has 16 public methods. Consider refactoring User to keep number of public methods under 10.
    142/sites/firefly-iii/database/migrations/2016_02_04_144117_changes_for_v380.php29Avoid using short method names like ChangesForV380::up(). The configured minimum method name length is 3.
    143/sites/firefly-iii/database/migrations/2016_02_04_144117_changes_for_v380.php29The method up() has 41 lines of code. Current threshold is set to 40. Avoid really long methods.
    144/sites/firefly-iii/database/migrations/2016_02_24_172426_create_jobs_table.php18Avoid using short method names like CreateJobsTable::up(). The configured minimum method name length is 3.
    145/sites/firefly-iii/database/migrations/2016_04_08_181054_changes_for_v383.php29Avoid using short method names like ChangesForV383::up(). The configured minimum method name length is 3.
    146/sites/firefly-iii/database/migrations/2016_04_25_093451_changes_for_v390.php59Avoid using short method names like ChangesForV390::up(). The configured minimum method name length is 3.
    147/sites/firefly-iii/database/migrations/2016_04_25_093451_changes_for_v390.php59The method up() has 75 lines of code. Current threshold is set to 40. Avoid really long methods.

    Processing errors

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FileProblem
    /sites/firefly-iii/app/Console/Commands/UpgradeFireflyInstructions.phpUnexpected token: ??, line: 56, col: 38, file: /sites/firefly-iii/app/Console/Commands/UpgradeFireflyInstructions.php.
    /sites/firefly-iii/app/Helpers/Collection/Balance.phpUnexpected token: ??, line: 51, col: 37, file: /sites/firefly-iii/app/Helpers/Collection/Balance.php.
    /sites/firefly-iii/app/Helpers/Collection/BalanceLine.phpUnexpected token: ??, line: 82, col: 30, file: /sites/firefly-iii/app/Helpers/Collection/BalanceLine.php.
    /sites/firefly-iii/app/Helpers/Collection/BillLine.phpUnexpected token: ??, line: 45, col: 30, file: /sites/firefly-iii/app/Helpers/Collection/BillLine.php.
    /sites/firefly-iii/app/Helpers/Collection/BudgetLine.phpUnexpected token: ??, line: 43, col: 30, file: /sites/firefly-iii/app/Helpers/Collection/BudgetLine.php.
    /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountIban.phpUnexpected token: DEFAULT, line: 59, col: 55, file: /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountIban.php.
    /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountName.phpUnexpected token: DEFAULT, line: 39, col: 55, file: /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountName.php.
    /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountNumber.phpUnexpected token: ??, line: 40, col: 31, file: /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountNumber.php.
    /sites/firefly-iii/app/Helpers/Csv/Converter/Description.phpUnexpected token: ??, line: 27, col: 51, file: /sites/firefly-iii/app/Helpers/Csv/Converter/Description.php.
    /sites/firefly-iii/app/Helpers/Csv/Data.phpUnexpected token: ??, line: 70, col: 38, file: /sites/firefly-iii/app/Helpers/Csv/Data.php.
    /sites/firefly-iii/app/Helpers/Csv/Importer.phpUnexpected token: ??, line: 185, col: 54, file: /sites/firefly-iii/app/Helpers/Csv/Importer.php.
    /sites/firefly-iii/app/Helpers/Csv/Mapper/AssetAccount.phpUnexpected token: ??, line: 41, col: 36, file: /sites/firefly-iii/app/Helpers/Csv/Mapper/AssetAccount.php.
    /sites/firefly-iii/app/Helpers/Csv/PostProcessing/Amount.phpUnexpected token: ??, line: 30, col: 55, file: /sites/firefly-iii/app/Helpers/Csv/PostProcessing/Amount.php.
    /sites/firefly-iii/app/Helpers/Csv/PostProcessing/AssetAccount.phpUnexpected token: ??, line: 74, col: 62, file: /sites/firefly-iii/app/Helpers/Csv/PostProcessing/AssetAccount.php.
    /sites/firefly-iii/app/Helpers/Csv/PostProcessing/Description.phpUnexpected token: ??, line: 29, col: 65, file: /sites/firefly-iii/app/Helpers/Csv/PostProcessing/Description.php.
    /sites/firefly-iii/app/Helpers/Report/ReportHelper.phpUnexpected token: ??, line: 273, col: 59, file: /sites/firefly-iii/app/Helpers/Report/ReportHelper.php.
    /sites/firefly-iii/app/Http/Controllers/AccountController.phpUnexpected token: ??, line: 169, col: 23, file: /sites/firefly-iii/app/Http/Controllers/AccountController.php.
    /sites/firefly-iii/app/Http/Controllers/BudgetController.phpUnexpected token: DEFAULT, line: 187, col: 69, file: /sites/firefly-iii/app/Http/Controllers/BudgetController.php.
    /sites/firefly-iii/app/Http/Controllers/Chart/AccountController.phpUnexpected token: ??, line: 80, col: 60, file: /sites/firefly-iii/app/Http/Controllers/Chart/AccountController.php.
    /sites/firefly-iii/app/Http/Controllers/CsvController.phpUnexpected token: DEFAULT, line: 202, col: 109, file: /sites/firefly-iii/app/Http/Controllers/CsvController.php.
    /sites/firefly-iii/app/Http/Controllers/ExportController.phpUnexpected token: DEFAULT, line: 107, col: 65, file: /sites/firefly-iii/app/Http/Controllers/ExportController.php.
    /sites/firefly-iii/app/Http/Controllers/HomeController.phpUnexpected token: DEFAULT, line: 127, col: 73, file: /sites/firefly-iii/app/Http/Controllers/HomeController.php.
    /sites/firefly-iii/app/Http/Controllers/JsonController.phpUnexpected token: DEFAULT, line: 116, col: 60, file: /sites/firefly-iii/app/Http/Controllers/JsonController.php.
    /sites/firefly-iii/app/Http/Controllers/PiggyBankController.phpUnexpected token: DEFAULT, line: 78, col: 93, file: /sites/firefly-iii/app/Http/Controllers/PiggyBankController.php.
    /sites/firefly-iii/app/Http/Controllers/Popup/ReportController.phpUnexpected token: ??, line: 246, col: 59, file: /sites/firefly-iii/app/Http/Controllers/Popup/ReportController.php.
    /sites/firefly-iii/app/Http/Controllers/PreferencesController.phpUnexpected token: DEFAULT, line: 77, col: 71, file: /sites/firefly-iii/app/Http/Controllers/PreferencesController.php.
    /sites/firefly-iii/app/Http/Controllers/ReportController.phpUnexpected token: DEFAULT, line: 83, col: 60, file: /sites/firefly-iii/app/Http/Controllers/ReportController.php.
    /sites/firefly-iii/app/Http/Controllers/RuleGroupController.phpUnexpected token: DEFAULT, line: 189, col: 67, file: /sites/firefly-iii/app/Http/Controllers/RuleGroupController.php.
    /sites/firefly-iii/app/Http/Controllers/Transaction/MassController.phpUnexpected token: DEFAULT, line: 111, col: 92, file: /sites/firefly-iii/app/Http/Controllers/Transaction/MassController.php.
    /sites/firefly-iii/app/Http/Controllers/Transaction/SplitController.phpUnexpected token: ??, line: 68, col: 67, file: /sites/firefly-iii/app/Http/Controllers/Transaction/SplitController.php.
    /sites/firefly-iii/app/Http/Controllers/TransactionController.phpUnexpected token: ??, line: 95, col: 64, file: /sites/firefly-iii/app/Http/Controllers/TransactionController.php.
    /sites/firefly-iii/app/Http/Requests/JournalFormRequest.phpUnexpected token: ??, line: 43, col: 36, file: /sites/firefly-iii/app/Http/Requests/JournalFormRequest.php.
    /sites/firefly-iii/app/Http/Requests/SplitJournalFormRequest.phpUnexpected token: ??, line: 40, col: 68, file: /sites/firefly-iii/app/Http/Requests/SplitJournalFormRequest.php.
    /sites/firefly-iii/app/Http/Requests/TagFormRequest.phpUnexpected token: ??, line: 49, col: 36, file: /sites/firefly-iii/app/Http/Requests/TagFormRequest.php.
    /sites/firefly-iii/app/Http/breadcrumbs.phpUnexpected token: ??, line: 594, col: 56, file: /sites/firefly-iii/app/Http/breadcrumbs.php.
    /sites/firefly-iii/app/Models/AccountType.phpUnexpected token: DEFAULT, line: 35, col: 11, file: /sites/firefly-iii/app/Models/AccountType.php.
    /sites/firefly-iii/app/Models/Tag.phpUnexpected token: ??, line: 81, col: 57, file: /sites/firefly-iii/app/Models/Tag.php.
    /sites/firefly-iii/app/Repositories/Bill/BillRepository.phpUnexpected token: ??, line: 212, col: 48, file: /sites/firefly-iii/app/Repositories/Bill/BillRepository.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountExactly.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountExactly.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountLess.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountLess.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountMore.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountMore.php.
    /sites/firefly-iii/app/Support/Amount.phpUnexpected token: ??, line: 87, col: 61, file: /sites/firefly-iii/app/Support/Amount.php.
    /sites/firefly-iii/app/Support/Migration/TestData.phpUnexpected token: ??, line: 267, col: 77, file: /sites/firefly-iii/app/Support/Migration/TestData.php.
    /sites/firefly-iii/app/Support/Models/TransactionJournalSupport.phpUnexpected token: ??, line: 286, col: 52, file: /sites/firefly-iii/app/Support/Models/TransactionJournalSupport.php.
    /sites/firefly-iii/app/Support/Twig/General.phpUnexpected token: ??, line: 112, col: 44, file: /sites/firefly-iii/app/Support/Twig/General.php.
    /sites/firefly-iii/app/Validation/FireflyValidator.phpUnexpected token: ??, line: 84, col: 33, file: /sites/firefly-iii/app/Validation/FireflyValidator.php.
    \ No newline at end of file From 1e724712e0deea42ef46ca6e252bac5fb26b2cd8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 29 Jul 2016 21:42:12 +0200 Subject: [PATCH 53/94] Bug fix in converter --- app/Import/Converter/CurrencyName.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Import/Converter/CurrencyName.php b/app/Import/Converter/CurrencyName.php index f91c2aa377..7babdef1c4 100644 --- a/app/Import/Converter/CurrencyName.php +++ b/app/Import/Converter/CurrencyName.php @@ -33,7 +33,7 @@ class CurrencyName extends BasicConverter implements ConverterInterface $value = trim($value); Log::debug('Going to convert using CurrencyName', ['value' => $value]); - if ($value === 0) { + if (strlen($value) === 0) { $this->setCertainty(0); return new TransactionCurrency; } From 541d9ebdd9cbe5ecdc9c66dfd0093f49c2092be3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 30 Jul 2016 16:29:04 +0200 Subject: [PATCH 54/94] Optimised some code. --- app/Crud/Account/AccountCrud.php | 152 ++- app/Helpers/Report/BalanceReportHelper.php | 3 +- .../Controllers/Chart/BudgetController.php | 151 ++- app/Import/Converter/AccountId.php | 13 +- app/Import/Converter/CurrencySymbol.php | 2 +- app/Import/ImportEntry.php | 11 +- public/result.html | 1195 +++++++++-------- 7 files changed, 862 insertions(+), 665 deletions(-) diff --git a/app/Crud/Account/AccountCrud.php b/app/Crud/Account/AccountCrud.php index 682b8bc162..f81bbb3263 100644 --- a/app/Crud/Account/AccountCrud.php +++ b/app/Crud/Account/AccountCrud.php @@ -11,6 +11,7 @@ declare(strict_types = 1); namespace FireflyIII\Crud\Account; +use Carbon\Carbon; use DB; use FireflyIII\Models\Account; use FireflyIII\Models\AccountMeta; @@ -233,21 +234,8 @@ class AccountCrud implements AccountCrudInterface $this->storeMetadata($newAccount, $data); } - // continue with the opposing account: if ($data['openingBalance'] != 0) { - $opposingData = [ - 'user' => $data['user'], - 'accountType' => 'initial', - 'virtualBalance' => 0, - 'name' => $data['name'] . ' initial balance', - 'active' => false, - 'iban' => '', - ]; - $opposing = $this->storeAccount($opposingData); - if (!is_null($opposing) && !is_null($newAccount)) { - $this->storeInitialBalance($newAccount, $opposing, $data); - } - + $this->storeInitialBalance($newAccount, $data); } return $newAccount; @@ -282,35 +270,7 @@ class AccountCrud implements AccountCrudInterface $account->save(); $this->updateMetadata($account, $data); - $openingBalance = $this->openingBalanceTransaction($account); - if ($data['openingBalance'] != 0) { - if (!is_null($openingBalance->id)) { - $this->updateInitialBalance($account, $openingBalance, $data); - - return $account; - } - - $type = $data['openingBalance'] < 0 ? 'expense' : 'revenue'; - $opposingData = [ - 'user' => $data['user'], - 'accountType' => $type, - 'name' => $data['name'] . ' initial balance', - 'active' => false, - 'iban' => '', - 'virtualBalance' => 0, - ]; - $opposing = $this->storeAccount($opposingData); - if (!is_null($opposing)) { - $this->storeInitialBalance($account, $opposing, $data); - } - - return $account; - - } - - if ($openingBalance) { // opening balance is zero, should we delete it? - $openingBalance->delete(); // delete existing opening balance. - } + $this->updateInitialBalance($account, $data); return $account; } @@ -359,19 +319,21 @@ class AccountCrud implements AccountCrudInterface /** * @param Account $account - * @param Account $opposing * @param array $data * * @return TransactionJournal */ - protected function storeInitialBalance(Account $account, Account $opposing, array $data): TransactionJournal + protected function storeInitialBalance(Account $account, array $data): TransactionJournal { + $amount = $data['openingBalance']; + $user = $data['user']; + $name = $data['name']; + $opposing = $this->storeOpposingAccount($amount, $user, $name); $transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); $journal = TransactionJournal::create( [ 'user_id' => $data['user'], 'transaction_type_id' => $transactionType->id, - 'bill_id' => null, 'transaction_currency_id' => $data['openingBalanceCurrency'], 'description' => 'Initial balance for "' . $account->name . '"', 'completed' => true, @@ -382,24 +344,22 @@ class AccountCrud implements AccountCrudInterface $firstAccount = $account; $secondAccount = $opposing; - $firstAmount = $data['openingBalance']; - $secondAmount = $data['openingBalance'] * -1; + $firstAmount = $amount; + $secondAmount = $amount * -1; if ($data['openingBalance'] < 0) { $firstAccount = $opposing; $secondAccount = $account; - $firstAmount = $data['openingBalance'] * -1; - $secondAmount = $data['openingBalance']; + $firstAmount = $amount * -1; + $secondAmount = $amount; } $one = new Transaction(['account_id' => $firstAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $firstAmount]); $one->save();// first transaction: from - $two = new Transaction(['account_id' => $secondAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $secondAmount]); $two->save(); // second transaction: to return $journal; - } /** @@ -425,30 +385,32 @@ class AccountCrud implements AccountCrudInterface } /** - * @param Account $account - * @param TransactionJournal $journal - * @param array $data + * @param Account $account + * @param array $data * - * @return TransactionJournal + * @return bool */ - protected function updateInitialBalance(Account $account, TransactionJournal $journal, array $data): TransactionJournal + protected function updateInitialBalance(Account $account, array $data): bool { - $journal->date = $data['openingBalanceDate']; - $journal->save(); + $openingBalance = $this->openingBalanceTransaction($account); + if ($data['openingBalance'] != 0) { + if (!is_null($openingBalance->id)) { + $date = $data['openingBalanceDate']; + $amount = $data['openingBalance']; - /** @var Transaction $transaction */ - foreach ($journal->transactions()->get() as $transaction) { - if ($account->id == $transaction->account_id) { - $transaction->amount = $data['openingBalance']; - $transaction->save(); - } - if ($account->id != $transaction->account_id) { - $transaction->amount = $data['openingBalance'] * -1; - $transaction->save(); + return $this->updateJournal($account, $openingBalance, $date, $amount); } + + $this->storeInitialBalance($account, $data); + + return true; + } + // else, delete it: + if ($openingBalance) { // opening balance is zero, should we delete it? + $openingBalance->delete(); // delete existing opening balance. } - return $journal; + return true; } /** @@ -501,4 +463,56 @@ class AccountCrud implements AccountCrudInterface return $journal; } + + /** + * @param float $amount + * @param int $user + * @param string $name + * + * @return Account + */ + private function storeOpposingAccount(float $amount, int $user, string $name):Account + { + $type = $amount < 0 ? 'expense' : 'revenue'; + $opposingData = [ + 'user' => $user, + 'accountType' => $type, + 'name' => $name . ' initial balance', + 'active' => false, + 'iban' => '', + 'virtualBalance' => 0, + ]; + + return $this->storeAccount($opposingData); + } + + /** + * @param Account $account + * @param TransactionJournal $journal + * @param Carbon $date + * @param float $amount + * + * @return bool + */ + private function updateJournal(Account $account, TransactionJournal $journal, Carbon $date, float $amount): bool + { + // update date: + $journal->date = $date; + $journal->save(); + // update transactions: + /** @var Transaction $transaction */ + foreach ($journal->transactions()->get() as $transaction) { + if ($account->id == $transaction->account_id) { + $transaction->amount = $amount; + $transaction->save(); + } + if ($account->id != $transaction->account_id) { + $transaction->amount = $amount * -1; + $transaction->save(); + } + } + + return true; + + } } \ No newline at end of file diff --git a/app/Helpers/Report/BalanceReportHelper.php b/app/Helpers/Report/BalanceReportHelper.php index 7f936119d8..b9d37064e5 100644 --- a/app/Helpers/Report/BalanceReportHelper.php +++ b/app/Helpers/Report/BalanceReportHelper.php @@ -58,8 +58,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface */ public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts): Balance { - $balance = new Balance; - // build a balance header: + $balance = new Balance; $header = new BalanceHeader; $limitRepetitions = $this->budgetRepository->getAllBudgetLimitRepetitions($start, $end); foreach ($accounts as $account) { diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 60cc1931e6..b6a80a0b7f 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -32,9 +32,12 @@ use Response; class BudgetController extends Controller { - /** @var \FireflyIII\Generator\Chart\Budget\BudgetChartGeneratorInterface */ + /** @var BudgetChartGeneratorInterface */ protected $generator; + /** @var BudgetRepositoryInterface */ + protected $repository; + /** * */ @@ -43,6 +46,8 @@ class BudgetController extends Controller parent::__construct(); // create chart generator: $this->generator = app(BudgetChartGeneratorInterface::class); + + $this->repository = app(BudgetRepositoryInterface::class); } /** @@ -136,11 +141,9 @@ class BudgetController extends Controller /** * Shows a budget list with spent/left/overspent. * - * @param BudgetRepositoryInterface $repository - * * @return \Symfony\Component\HttpFoundation\Response */ - public function frontpage(BudgetRepositoryInterface $repository) + public function frontpage() { $start = session('start', Carbon::now()->startOfMonth()); $end = session('end', Carbon::now()->endOfMonth()); @@ -153,54 +156,26 @@ class BudgetController extends Controller if ($cache->has()) { return Response::json($cache->get()); } - $budgets = $repository->getActiveBudgets(); - $repetitions = $repository->getAllBudgetLimitRepetitions($start, $end); + $budgets = $this->repository->getActiveBudgets(); + $repetitions = $this->repository->getAllBudgetLimitRepetitions($start, $end); $allEntries = new Collection; - $format = strval(trans('config.month_and_day')); /** @var Budget $budget */ foreach ($budgets as $budget) { // get relevant repetitions: - $name = $budget->name; - $reps = $repetitions->filter( - function (LimitRepetition $repetition) use ($budget, $start, $end) { - if ($repetition->startdate < $end && $repetition->enddate > $start && $repetition->budget_id === $budget->id) { - return $repetition; - } - } - ); + $reps = $this->filterRepetitions($repetitions, $budget, $start, $end); + if ($reps->count() === 0) { - $amount = '0'; - $left = '0'; - $spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end); - $overspent = '0'; - $allEntries->push([$name, $left, $spent, $overspent, $amount, $spent]); - } - /** @var LimitRepetition $repetition */ - foreach ($reps as $repetition) { - $expenses = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate); - if ($reps->count() > 1) { - $name = $budget->name . ' ' . trans( - 'firefly.between_dates', - ['start' => $repetition->startdate->formatLocalized($format), 'end' => $repetition->enddate->formatLocalized($format)] - ); - } - $amount = $repetition->amount; - $left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses); - $spent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcmul($amount, '-1') : $expenses; - $overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0'; - $allEntries->push([$name, $left, $spent, $overspent, $amount, $spent]); + $collection = $this->spentInPeriodSingle($budget, $start, $end); + $allEntries = $allEntries->merge($collection); + continue; } + $collection = $this->spentInPeriodMulti($budget, $reps); + $allEntries = $allEntries->merge($collection); } - - $list = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end); - $sum = '0'; - /** @var TransactionJournal $entry */ - foreach ($list as $entry) { - $sum = bcadd(TransactionJournal::amount($entry), $sum); - } - $allEntries->push([trans('firefly.no_budget'), '0', '0', $sum, '0', '0']); + $entry = $this->spentInPeriodWithout($start, $end); + $allEntries->push($entry); $data = $this->generator->frontpage($allEntries); $cache->store($data); @@ -335,4 +310,94 @@ class BudgetController extends Controller return Response::json($data); } + + /** + * @param Collection $repetitions + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function filterRepetitions(Collection $repetitions, Budget $budget, Carbon $start, Carbon $end): Collection + { + + return $repetitions->filter( + function (LimitRepetition $repetition) use ($budget, $start, $end) { + if ($repetition->startdate < $end && $repetition->enddate > $start && $repetition->budget_id === $budget->id) { + return $repetition; + } + } + ); + } + + /** + * @param Budget $budget + * @param Collection $repetitions + * + * @return Collection + */ + private function spentInPeriodMulti(Budget $budget, Collection $repetitions): Collection + { + $format = strval(trans('config.month_and_day')); + $collection = new Collection; + $name = $budget->name; + /** @var LimitRepetition $repetition */ + foreach ($repetitions as $repetition) { + $expenses = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate); + + if ($repetitions->count() > 1) { + $name = $budget->name . ' ' . trans( + 'firefly.between_dates', + ['start' => $repetition->startdate->formatLocalized($format), 'end' => $repetition->enddate->formatLocalized($format)] + ); + } + $amount = $repetition->amount; + $left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses); + $spent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcmul($amount, '-1') : $expenses; + $overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0'; + $array = [$name, $left, $spent, $overspent, $amount, $spent]; + $collection->push($array); + } + + return $collection; + } + + /** + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function spentInPeriodSingle(Budget $budget, Carbon $start, Carbon $end): Collection + { + $collection = new Collection; + $amount = '0'; + $left = '0'; + $spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end); + $overspent = '0'; + $array = [$budget->name, $left, $spent, $overspent, $amount, $spent]; + $collection->push($array); + + return $collection; + } + + /** + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + private function spentInPeriodWithout(Carbon $start, Carbon $end):array + { + $list = $this->repository->journalsInPeriodWithoutBudget(new Collection, $start, $end); + $sum = '0'; + /** @var TransactionJournal $entry */ + foreach ($list as $entry) { + $sum = bcadd(TransactionJournal::amount($entry), $sum); + } + + return [trans('firefly.no_budget'), '0', '0', $sum, '0', '0']; + } } diff --git a/app/Import/Converter/AccountId.php b/app/Import/Converter/AccountId.php index 221f5e4c84..7fd0b56cf2 100644 --- a/app/Import/Converter/AccountId.php +++ b/app/Import/Converter/AccountId.php @@ -32,17 +32,13 @@ class AccountId extends BasicConverter implements ConverterInterface { $value = intval(trim($value)); Log::debug('Going to convert using AssetAccountId', ['value' => $value]); - if ($value === 0) { $this->setCertainty(0); return new Account; } - /** @var AccountCrudInterface $repository */ $repository = app(AccountCrudInterface::class, [$this->user]); - - if (isset($this->mapping[$value])) { Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); $account = $repository->find(intval($this->mapping[$value])); @@ -54,20 +50,15 @@ class AccountId extends BasicConverter implements ConverterInterface return $account; } } - - // not mapped? Still try to find it first: - $account = $repository->find($value); + $account = $repository->find($value);// not mapped? Still try to find it first: if (!is_null($account->id)) { $this->setCertainty(90); - Log::debug('Found account by ID ', ['id' => $account->id]); return $account; } + $this->setCertainty(0); // should not really happen. If the ID does not match FF, what is FF supposed to do? - $this->setCertainty(0); - - // should not really happen. If the ID does not match FF, what is FF supposed to do? return new Account; } diff --git a/app/Import/Converter/CurrencySymbol.php b/app/Import/Converter/CurrencySymbol.php index 00977f3349..83b86dded2 100644 --- a/app/Import/Converter/CurrencySymbol.php +++ b/app/Import/Converter/CurrencySymbol.php @@ -33,7 +33,7 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface $value = trim($value); Log::debug('Going to convert using CurrencySymbol', ['value' => $value]); - if ($value === 0) { + if (strlen($value) === 0) { $this->setCertainty(0); return new TransactionCurrency; } diff --git a/app/Import/ImportEntry.php b/app/Import/ImportEntry.php index d23310646e..1cfc657669 100644 --- a/app/Import/ImportEntry.php +++ b/app/Import/ImportEntry.php @@ -24,6 +24,8 @@ class ImportEntry public $amount; + + /** * @param string $role * @param string $value @@ -34,6 +36,8 @@ class ImportEntry */ public function importValue(string $role, string $value, int $certainty, $convertedValue) { + Log::debug('Going to import', ['role' => $role, 'value' => $value, 'certainty' => $certainty]); + switch ($role) { default: Log::error('Import entry cannot handle object.', ['role' => $role]); @@ -41,11 +45,12 @@ class ImportEntry break; case 'amount': + /* + * Easy enough. + */ $this->setAmount($convertedValue); - return; - case 'account-id': - break; + return; } } diff --git a/public/result.html b/public/result.html index d28b8ce89d..db161648c9 100644 --- a/public/result.html +++ b/public/result.html @@ -4,929 +4,1052 @@ 1 /sites/firefly-iii/app/Crud/Account/AccountCrud.php -192 -The method update() has an NPath complexity of 210. The configured NPath complexity threshold is 200. +32 +The class AccountCrud has an overall complexity of 52 which is very high. The configured complexity threshold is 50. 2 -/sites/firefly-iii/app/Crud/Account/AccountCrud.php -192 -The method update() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. +/sites/firefly-iii/app/Handlers/Events/UpdateJournalConnection.php +33 +The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. 3 -/sites/firefly-iii/app/Crud/Account/AccountCrud.php -192 -The method update() has an NPath complexity of 210. The configured NPath complexity threshold is 128. - - -4 -/sites/firefly-iii/app/Crud/Account/AccountCrud.php -192 -The method update() has 42 lines of code. Current threshold is set to 40. Avoid really long methods. - - -5 -/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php -41 -The method fix() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - - -6 -/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php -119 -The method parseSepaDescription() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5. - - -7 -/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php -119 -The method parseSepaDescription() has 49 lines of code. Current threshold is set to 40. Avoid really long methods. - - -8 -/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php -174 -The method parseTRTPDescription() has a Cyclomatic Complexity of 10. The configured cyclomatic complexity threshold is 10. - - -9 -/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php -174 -The method parseTRTPDescription() has a Cyclomatic Complexity of 10. The configured cyclomatic complexity threshold is 5. - - -10 -/sites/firefly-iii/app/Helpers/Csv/Specifix/AbnAmroDescription.php -174 -The method parseTRTPDescription() has 51 lines of code. Current threshold is set to 40. Avoid really long methods. - - -11 /sites/firefly-iii/app/Helpers/Report/BalanceReportHelper.php 34 The class BalanceReportHelper has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13. -12 +4 /sites/firefly-iii/app/Helpers/Report/BalanceReportHelper.php -268 +267 The method removeUnusedBudgets() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -13 +5 /sites/firefly-iii/app/Helpers/Report/BudgetReportHelper.php -50 +51 +The method budgetYearOverview() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. + + +6 +/sites/firefly-iii/app/Helpers/Report/BudgetReportHelper.php +51 +The method budgetYearOverview() has 59 lines of code. Current threshold is set to 40. Avoid really long methods. + + +7 +/sites/firefly-iii/app/Helpers/Report/BudgetReportHelper.php +118 The method getBudgetReport() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -14 +8 /sites/firefly-iii/app/Helpers/Report/BudgetReportHelper.php -50 +118 The method getBudgetReport() has 57 lines of code. Current threshold is set to 40. Avoid really long methods. -15 +9 /sites/firefly-iii/app/Http/Controllers/Admin/UserController.php -30 +54 The method index() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -16 +10 /sites/firefly-iii/app/Http/Controllers/AttachmentController.php -33 -The class AttachmentController has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13. +34 +The class AttachmentController has a coupling between objects value of 17. Consider to reduce the number of dependencies under 13. -17 +11 /sites/firefly-iii/app/Http/Controllers/Auth/AuthController.php -36 -The class AuthController has a coupling between objects value of 15. Consider to reduce the number of dependencies under 13. +37 +The class AuthController has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13. -18 +12 /sites/firefly-iii/app/Http/Controllers/Auth/AuthController.php -64 +65 The method login() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. -19 +13 /sites/firefly-iii/app/Http/Controllers/Auth/PasswordController.php 60 The method sendResetLinkEmail() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. -20 +14 /sites/firefly-iii/app/Http/Controllers/Auth/TwoFactorController.php 75 Avoid unused parameters such as '$request'. -21 -/sites/firefly-iii/app/Http/Controllers/CategoryController.php -34 -The class CategoryController has 11 public methods. Consider refactoring CategoryController to keep number of public methods under 10. - - -22 -/sites/firefly-iii/app/Http/Controllers/CategoryController.php -34 -The class CategoryController has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13. - - -23 -/sites/firefly-iii/app/Http/Controllers/CategoryController.php -164 -The method show() has 61 lines of code. Current threshold is set to 40. Avoid really long methods. - - -24 +15 /sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php 32 The class BudgetController has a coupling between objects value of 13. Consider to reduce the number of dependencies under 13. - -25 -/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -56 -The method budget() has 40 lines of code. Current threshold is set to 40. Avoid really long methods. - -26 +16 /sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -144 -The method frontpage() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10. - - -27 -/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -144 -The method frontpage() has an NPath complexity of 8036. The configured NPath complexity threshold is 200. - - -28 -/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -144 -The method frontpage() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 5. - - -29 -/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -144 -The method frontpage() has an NPath complexity of 8036. The configured NPath complexity threshold is 128. - - -30 -/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -144 -The method frontpage() has 66 lines of code. Current threshold is set to 40. Avoid really long methods. - - -31 -/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -222 +196 The method multiYear() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5. - -32 + +17 /sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -222 +196 The method multiYear() has 60 lines of code. Current threshold is set to 40. Avoid really long methods. - -33 + +18 /sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -222 +196 The method multiYear has 5 parameters. Consider to reduce parameter number under 5. - -34 + +19 /sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -292 +266 The method period() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -35 + +20 /sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -292 +266 The method period() has 47 lines of code. Current threshold is set to 40. Avoid really long methods. - -36 + +21 /sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php -292 +266 The method period has 5 parameters. Consider to reduce parameter number under 5. + +22 +/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php +340 +The method spentInPeriodMulti() has an NPath complexity of 251. The configured NPath complexity threshold is 200. + -37 -/sites/firefly-iii/app/Http/Controllers/Chart/CategoryController.php -158 -The method multiYear() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. +23 +/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php +340 +The method spentInPeriodMulti() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. -38 -/sites/firefly-iii/app/Http/Controllers/Chart/CategoryController.php -158 -The method multiYear() has 63 lines of code. Current threshold is set to 40. Avoid really long methods. +24 +/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php +340 +The method spentInPeriodMulti() has an NPath complexity of 251. The configured NPath complexity threshold is 128. -39 +25 /sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php 101 The method yearInOut() has 43 lines of code. Current threshold is set to 40. Avoid really long methods. -40 +26 /sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php 101 The method yearInOut has 5 parameters. Consider to reduce parameter number under 5. -41 +27 /sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php 154 The method yearInOutSummarized() has 42 lines of code. Current threshold is set to 40. Avoid really long methods. -42 +28 /sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php 154 The method yearInOutSummarized has 5 parameters. Consider to reduce parameter number under 5. -43 +29 /sites/firefly-iii/app/Http/Controllers/Controller.php 30 -The class Controller has 21 children. Consider to rebalance this class hierarchy to keep number of children under 15. +The class Controller has 20 children. Consider to rebalance this class hierarchy to keep number of children under 15. -44 +30 /sites/firefly-iii/app/Http/Controllers/CurrencyController.php 31 The class CurrencyController has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13. -45 +31 +/sites/firefly-iii/app/Http/Controllers/ImportController.php +50 +Avoid unused local variables such as '$importer'. + + +32 +/sites/firefly-iii/app/Http/Controllers/ImportController.php +243 +The method upload() has 44 lines of code. Current threshold is set to 40. Avoid really long methods. + + +33 +/sites/firefly-iii/app/Http/Controllers/ImportController.php +294 +The method jobInCorrectStep() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. + + +34 /sites/firefly-iii/app/Http/Controllers/RuleController.php 36 The class RuleController has 13 public methods. Consider refactoring RuleController to keep number of public methods under 10. - -46 + +35 /sites/firefly-iii/app/Http/Controllers/RuleController.php 36 The class RuleController has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13. - -47 + +36 /sites/firefly-iii/app/Http/Controllers/RuleController.php 326 Avoid using short method names like RuleController::up(). The configured minimum method name length is 3. - -48 + +37 /sites/firefly-iii/app/Http/Controllers/TagController.php 41 The class TagController has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13. - -49 + +38 /sites/firefly-iii/app/Http/Controllers/TagController.php 181 The method index() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. - -50 + +39 /sites/firefly-iii/app/Http/Controllers/TagController.php 181 The method index() has an NPath complexity of 131. The configured NPath complexity threshold is 128. - -51 + +40 /sites/firefly-iii/app/Http/Middleware/AuthenticateTwoFactor.php 36 The method handle() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. - -52 + +41 /sites/firefly-iii/app/Http/Middleware/IsConfirmed.php 36 The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -53 + +42 /sites/firefly-iii/app/Http/Middleware/IsNotConfirmed.php 36 The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -54 + +43 /sites/firefly-iii/app/Http/Middleware/Range.php -61 +62 The method handle() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. + +44 +/sites/firefly-iii/app/Http/Middleware/Range.php +95 +The method datePicker() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. + -55 +45 +/sites/firefly-iii/app/Http/Middleware/Range.php +95 +The method datePicker() has 48 lines of code. Current threshold is set to 40. Avoid really long methods. + + +46 /sites/firefly-iii/app/Http/Middleware/RedirectIfTwoFactorAuthenticated.php 35 The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -56 + +47 /sites/firefly-iii/app/Http/Requests/Request.php 21 -The class Request has 18 children. Consider to rebalance this class hierarchy to keep number of children under 15. +The class Request has 19 children. Consider to rebalance this class hierarchy to keep number of children under 15. + + +48 +/sites/firefly-iii/app/Import/Converter/AccountId.php +31 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +49 +/sites/firefly-iii/app/Import/Converter/Amount.php +30 +The method convert() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5. + + +50 +/sites/firefly-iii/app/Import/Converter/AssetAccountIban.php +32 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +51 +/sites/firefly-iii/app/Import/Converter/AssetAccountIban.php +32 +The method convert() has 44 lines of code. Current threshold is set to 40. Avoid really long methods. + + +52 +/sites/firefly-iii/app/Import/Converter/AssetAccountName.php +32 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +53 +/sites/firefly-iii/app/Import/Converter/AssetAccountName.php +32 +The method convert() has 43 lines of code. Current threshold is set to 40. Avoid really long methods. + + +54 +/sites/firefly-iii/app/Import/Converter/AssetAccountNumber.php +33 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +55 +/sites/firefly-iii/app/Import/Converter/AssetAccountNumber.php +33 +The method convert() has 41 lines of code. Current threshold is set to 40. Avoid really long methods. + + +56 +/sites/firefly-iii/app/Import/Converter/BasicConverter.php +22 +The class BasicConverter has 26 children. Consider to rebalance this class hierarchy to keep number of children under 15. 57 +/sites/firefly-iii/app/Import/Converter/BillId.php +31 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +58 +/sites/firefly-iii/app/Import/Converter/BillId.php +31 +The method convert() has 41 lines of code. Current threshold is set to 40. Avoid really long methods. + + +59 +/sites/firefly-iii/app/Import/Converter/BillName.php +32 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +60 +/sites/firefly-iii/app/Import/Converter/BillName.php +32 +The method convert() has 53 lines of code. Current threshold is set to 40. Avoid really long methods. + + +61 +/sites/firefly-iii/app/Import/Converter/BudgetId.php +32 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +62 +/sites/firefly-iii/app/Import/Converter/BudgetName.php +32 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +63 +/sites/firefly-iii/app/Import/Converter/BudgetName.php +32 +The method convert() has 43 lines of code. Current threshold is set to 40. Avoid really long methods. + + +64 +/sites/firefly-iii/app/Import/Converter/CategoryId.php +32 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +65 +/sites/firefly-iii/app/Import/Converter/CategoryName.php +32 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +66 +/sites/firefly-iii/app/Import/Converter/CategoryName.php +32 +The method convert() has 43 lines of code. Current threshold is set to 40. Avoid really long methods. + + +67 +/sites/firefly-iii/app/Import/Converter/CurrencyId.php +32 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +68 +/sites/firefly-iii/app/Import/Converter/CurrencyName.php +31 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +69 +/sites/firefly-iii/app/Import/Converter/CurrencyName.php +31 +The method convert() has 44 lines of code. Current threshold is set to 40. Avoid really long methods. + + +70 +/sites/firefly-iii/app/Import/Converter/CurrencySymbol.php +31 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +71 +/sites/firefly-iii/app/Import/Converter/CurrencySymbol.php +31 +The method convert() has 44 lines of code. Current threshold is set to 40. Avoid really long methods. + + +72 +/sites/firefly-iii/app/Import/Converter/OpposingAccountIban.php +31 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +73 +/sites/firefly-iii/app/Import/Converter/OpposingAccountIban.php +31 +The method convert() has 45 lines of code. Current threshold is set to 40. Avoid really long methods. + + +74 +/sites/firefly-iii/app/Import/Converter/OpposingAccountName.php +31 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +75 +/sites/firefly-iii/app/Import/Converter/OpposingAccountName.php +31 +The method convert() has 44 lines of code. Current threshold is set to 40. Avoid really long methods. + + +76 +/sites/firefly-iii/app/Import/Converter/OpposingAccountNumber.php +32 +The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +77 +/sites/firefly-iii/app/Import/Converter/OpposingAccountNumber.php +32 +The method convert() has 42 lines of code. Current threshold is set to 40. Avoid really long methods. + + +78 +/sites/firefly-iii/app/Import/Converter/TagsComma.php +31 +The method convert() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. + + +79 +/sites/firefly-iii/app/Import/Converter/TagsComma.php +31 +The method convert() has 56 lines of code. Current threshold is set to 40. Avoid really long methods. + + +80 +/sites/firefly-iii/app/Import/Converter/TagsSpace.php +31 +The method convert() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. + + +81 +/sites/firefly-iii/app/Import/Converter/TagsSpace.php +31 +The method convert() has 57 lines of code. Current threshold is set to 40. Avoid really long methods. + + +82 /sites/firefly-iii/app/Jobs/MailError.php 67 The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -58 + +83 /sites/firefly-iii/app/Models/Account.php -91 +93 The method firstOrCreateEncrypted() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + +84 +/sites/firefly-iii/app/Models/Account.php +186 +The method getIbanAttribute() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + -59 +85 /sites/firefly-iii/app/Models/TransactionJournal.php 94 The class TransactionJournal has 23 public methods. Consider refactoring TransactionJournal to keep number of public methods under 10. -60 +86 /sites/firefly-iii/app/Providers/AccountServiceProvider.php 41 The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -61 +87 /sites/firefly-iii/app/Providers/AttachmentServiceProvider.php 41 The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -62 +88 /sites/firefly-iii/app/Providers/BillServiceProvider.php 41 The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -63 +89 /sites/firefly-iii/app/Providers/BudgetServiceProvider.php 41 The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -64 +90 /sites/firefly-iii/app/Providers/CategoryServiceProvider.php 41 The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -65 +91 /sites/firefly-iii/app/Providers/CrudServiceProvider.php -46 +48 The method registerAccount() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -66 +92 /sites/firefly-iii/app/Providers/CrudServiceProvider.php -64 +68 The method registerJournal() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -67 +93 /sites/firefly-iii/app/Providers/ExportJobServiceProvider.php -32 -The method boot() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. +52 +The method exportJob() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -68 -/sites/firefly-iii/app/Providers/FireflyServiceProvider.php -36 -The class FireflyServiceProvider has a coupling between objects value of 15. Consider to reduce the number of dependencies under 13. +94 +/sites/firefly-iii/app/Providers/ExportJobServiceProvider.php +70 +The method importJob() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. -69 +95 /sites/firefly-iii/app/Providers/FireflyServiceProvider.php -58 -The method register() has 53 lines of code. Current threshold is set to 40. Avoid really long methods. +37 +The class FireflyServiceProvider has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13. -70 +96 +/sites/firefly-iii/app/Providers/FireflyServiceProvider.php +59 +The method register() has 58 lines of code. Current threshold is set to 40. Avoid really long methods. + + +97 /sites/firefly-iii/app/Providers/JournalServiceProvider.php 41 The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -71 + +98 /sites/firefly-iii/app/Providers/PiggyBankServiceProvider.php 42 The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -72 + +99 /sites/firefly-iii/app/Providers/RuleGroupServiceProvider.php 42 The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -73 + +100 /sites/firefly-iii/app/Providers/RuleServiceProvider.php 41 The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -74 + +101 /sites/firefly-iii/app/Providers/TagServiceProvider.php 41 The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -75 + +102 /sites/firefly-iii/app/Repositories/Account/AccountRepository.php 35 The class AccountRepository has 14 public methods. Consider refactoring AccountRepository to keep number of public methods under 10. - -76 + +103 /sites/firefly-iii/app/Repositories/Account/AccountRepository.php 41 Avoid unused private fields such as '$validFields'. - -77 -/sites/firefly-iii/app/Repositories/Account/AccountRepository.php -240 -The method getPiggyBankAccounts() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - - -78 -/sites/firefly-iii/app/Repositories/Account/AccountRepository.php -282 -The method getSavingsAccounts() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. - - -79 -/sites/firefly-iii/app/Repositories/Account/AccountRepository.php -282 -The method getSavingsAccounts() has 44 lines of code. Current threshold is set to 40. Avoid really long methods. - - -80 -/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php -32 -The class BudgetRepository has 11 public methods. Consider refactoring BudgetRepository to keep number of public methods under 10. - - -81 -/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php -84 -The method firstUseDate() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - - -82 -/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php -184 -The method journalsInPeriod() has 60 lines of code. Current threshold is set to 40. Avoid really long methods. - - -83 -/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php -252 -The method journalsInPeriodWithoutBudget() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - - -84 -/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php -252 -The method journalsInPeriodWithoutBudget() has 49 lines of code. Current threshold is set to 40. Avoid really long methods. - - -85 -/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php -310 -The method spentInPeriod() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. - - -86 -/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php -310 -The method spentInPeriod() has 51 lines of code. Current threshold is set to 40. Avoid really long methods. - - -87 -/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php -446 -The method updateLimitAmount() has 43 lines of code. Current threshold is set to 40. Avoid really long methods. - - -88 -/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php -446 -The method updateLimitAmount has 5 parameters. Consider to reduce parameter number under 5. - - -89 -/sites/firefly-iii/app/Repositories/Budget/BudgetRepositoryInterface.php -138 -The method updateLimitAmount has 5 parameters. Consider to reduce parameter number under 5. - - -90 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -28 -The class CategoryRepository has 13 public methods. Consider refactoring CategoryRepository to keep number of public methods under 10. - - -91 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -28 -The class CategoryRepository has an overall complexity of 52 which is very high. The configured complexity threshold is 50. - - -92 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -110 -The method firstUseDate() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5. - - -93 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -110 -The method firstUseDate() has 42 lines of code. Current threshold is set to 40. Avoid really long methods. - - -94 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -132 -Avoid excessively long variable names like $firstTransactionQuery. Keep variable name length under 20. - - -95 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -225 -The method journalsInPeriod() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5. - - -96 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -225 -The method journalsInPeriod() has an NPath complexity of 128. The configured NPath complexity threshold is 128. - - -97 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -225 -The method journalsInPeriod() has 54 lines of code. Current threshold is set to 40. Avoid really long methods. - - -98 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -225 -The method journalsInPeriod has 5 parameters. Consider to reduce parameter number under 5. - - -99 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -288 -The method journalsInPeriodWithoutCategory() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - - -100 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -288 -The method journalsInPeriodWithoutCategory() has 53 lines of code. Current threshold is set to 40. Avoid really long methods. - - -101 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -348 -The method lastUseDate() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5. - - -102 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -348 -The method lastUseDate() has 42 lines of code. Current threshold is set to 40. Avoid really long methods. - - -103 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -464 -The method sumInPeriod() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. - 104 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -464 -The method sumInPeriod() has 58 lines of code. Current threshold is set to 40. Avoid really long methods. +/sites/firefly-iii/app/Repositories/Account/AccountRepository.php +247 +The method getPiggyBankAccounts() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. 105 -/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php -464 -The method sumInPeriod has 5 parameters. Consider to reduce parameter number under 5. +/sites/firefly-iii/app/Repositories/Account/AccountRepository.php +289 +The method getSavingsAccounts() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. 106 -/sites/firefly-iii/app/Repositories/Category/CategoryRepositoryInterface.php -95 -The method journalsInPeriod has 5 parameters. Consider to reduce parameter number under 5. +/sites/firefly-iii/app/Repositories/Account/AccountRepository.php +289 +The method getSavingsAccounts() has 44 lines of code. Current threshold is set to 40. Avoid really long methods. 107 -/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php -39 -The class JournalRepository has an overall complexity of 51 which is very high. The configured complexity threshold is 50. +/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php +32 +The class BudgetRepository has 13 public methods. Consider refactoring BudgetRepository to keep number of public methods under 10. 108 +/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php +116 +The method firstUseDate() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +109 +/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php +216 +The method journalsInPeriod() has 60 lines of code. Current threshold is set to 40. Avoid really long methods. + + +110 +/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php +284 +The method journalsInPeriodWithoutBudget() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +111 +/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php +284 +The method journalsInPeriodWithoutBudget() has 49 lines of code. Current threshold is set to 40. Avoid really long methods. + + +112 +/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php +342 +The method spentInPeriod() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. + + +113 +/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php +342 +The method spentInPeriod() has 51 lines of code. Current threshold is set to 40. Avoid really long methods. + + +114 +/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php +478 +The method updateLimitAmount() has 43 lines of code. Current threshold is set to 40. Avoid really long methods. + + +115 +/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php +478 +The method updateLimitAmount has 5 parameters. Consider to reduce parameter number under 5. + + +116 +/sites/firefly-iii/app/Repositories/Budget/BudgetRepositoryInterface.php +152 +The method updateLimitAmount has 5 parameters. Consider to reduce parameter number under 5. + + +117 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +28 +The class CategoryRepository has 14 public methods. Consider refactoring CategoryRepository to keep number of public methods under 10. + + +118 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +28 +The class CategoryRepository has an overall complexity of 55 which is very high. The configured complexity threshold is 50. + + +119 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +129 +The method firstUseDate() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5. + + +120 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +129 +The method firstUseDate() has 42 lines of code. Current threshold is set to 40. Avoid really long methods. + + +121 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +151 +Avoid excessively long variable names like $firstTransactionQuery. Keep variable name length under 20. + + +122 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +244 +The method journalsInPeriod() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5. + + +123 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +244 +The method journalsInPeriod() has an NPath complexity of 128. The configured NPath complexity threshold is 128. + + +124 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +244 +The method journalsInPeriod() has 54 lines of code. Current threshold is set to 40. Avoid really long methods. + + +125 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +244 +The method journalsInPeriod has 5 parameters. Consider to reduce parameter number under 5. + + +126 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +307 +The method journalsInPeriodWithoutCategory() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. + + +127 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +307 +The method journalsInPeriodWithoutCategory() has 52 lines of code. Current threshold is set to 40. Avoid really long methods. + + +128 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +366 +The method lastUseDate() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5. + + +129 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +366 +The method lastUseDate() has 42 lines of code. Current threshold is set to 40. Avoid really long methods. + + +130 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +482 +The method sumInPeriod() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. + + +131 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +482 +The method sumInPeriod() has 58 lines of code. Current threshold is set to 40. Avoid really long methods. + + +132 +/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php +482 +The method sumInPeriod has 5 parameters. Consider to reduce parameter number under 5. + + +133 +/sites/firefly-iii/app/Repositories/Category/CategoryRepositoryInterface.php +104 +The method journalsInPeriod has 5 parameters. Consider to reduce parameter number under 5. + + +134 +/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php +39 +The class JournalRepository has an overall complexity of 50 which is very high. The configured complexity threshold is 50. + + +135 /sites/firefly-iii/app/Repositories/Journal/JournalRepository.php 39 The class JournalRepository has a coupling between objects value of 19. Consider to reduce the number of dependencies under 13. - -109 + +136 /sites/firefly-iii/app/Repositories/Journal/JournalRepository.php -227 +222 The method getTransactions() has 57 lines of code. Current threshold is set to 40. Avoid really long methods. - -110 + +137 /sites/firefly-iii/app/Repositories/Journal/JournalRepository.php -290 +285 The method store() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -111 + +138 /sites/firefly-iii/app/Repositories/Journal/JournalRepository.php -290 +285 The method store() has 64 lines of code. Current threshold is set to 40. Avoid really long methods. - -112 + +139 /sites/firefly-iii/app/Repositories/Journal/JournalRepository.php -392 +387 The method update() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5. - -113 + +140 /sites/firefly-iii/app/Repositories/Journal/JournalRepository.php -392 +387 The method update() has 55 lines of code. Current threshold is set to 40. Avoid really long methods. - -114 + +141 /sites/firefly-iii/app/Repositories/Journal/JournalRepository.php -482 +477 The method storeAccounts() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. - -115 + +142 /sites/firefly-iii/app/Repositories/Journal/JournalRepository.php -581 +576 The method updateTags() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. - -116 + +143 /sites/firefly-iii/app/Repositories/Rule/RuleRepository.php 26 The class RuleRepository has 12 public methods. Consider refactoring RuleRepository to keep number of public methods under 10. - -117 + +144 /sites/firefly-iii/app/Repositories/Tag/TagRepository.php 49 The method connect() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -118 + +145 /sites/firefly-iii/app/Repositories/Tag/TagRepository.php -150 +183 The method connectAdvancePayment() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5. - -119 + +146 /sites/firefly-iii/app/Repositories/Tag/TagRepository.php -195 +228 The method connectBalancingAct() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -120 + +147 /sites/firefly-iii/app/Repositories/Tag/TagRepository.php -235 +268 The method matchAll() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. - -121 + +148 /sites/firefly-iii/app/Rules/Processor.php 179 The method triggered() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -122 + +149 /sites/firefly-iii/app/Rules/TransactionMatcher.php 57 The method findMatchingTransactions() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -123 + +150 /sites/firefly-iii/app/Rules/TransactionMatcher.php 57 The method findMatchingTransactions() has 49 lines of code. Current threshold is set to 40. Avoid really long methods. - -124 + +151 /sites/firefly-iii/app/Support/Binder/Date.php 36 The method routeBinder() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5. - -125 + +152 /sites/firefly-iii/app/Support/CacheProperties.php 95 The method md5() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. - -126 + +153 /sites/firefly-iii/app/Support/ExpandedForm.php 29 The class ExpandedForm has 18 public methods. Consider refactoring ExpandedForm to keep number of public methods under 10. - -127 + +154 /sites/firefly-iii/app/Support/ExpandedForm.php 211 The method makeSelectList() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -128 + +155 /sites/firefly-iii/app/Support/ExpandedForm.php 236 The method makeSelectListWithEmpty() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -129 + +156 /sites/firefly-iii/app/Support/ExpandedForm.php 442 The method fillFieldValue() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. - -130 + +157 /sites/firefly-iii/app/Support/Models/TagSupport.php 31 The method tagAllowAdvance() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5. - -131 + +158 /sites/firefly-iii/app/Support/Navigation.php 73 The method endOfPeriod() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. - -132 + +159 /sites/firefly-iii/app/Support/Navigation.php 73 The method endOfPeriod() has 55 lines of code. Current threshold is set to 40. Avoid really long methods. - -133 + +160 /sites/firefly-iii/app/Support/Navigation.php 213 The method startOfPeriod() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. - -134 + +161 /sites/firefly-iii/app/Support/Navigation.php 213 The method startOfPeriod() has 42 lines of code. Current threshold is set to 40. Avoid really long methods. - -135 + +162 /sites/firefly-iii/app/Support/Navigation.php 264 The method subtractPeriod() has 52 lines of code. Current threshold is set to 40. Avoid really long methods. - -136 + +163 /sites/firefly-iii/app/Support/Twig/Journal.php 37 The method formatAccountPerspective() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5. - -137 + +164 /sites/firefly-iii/app/Support/Twig/Journal.php 37 The method formatAccountPerspective() has 46 lines of code. Current threshold is set to 40. Avoid really long methods. - -138 + +165 /sites/firefly-iii/app/Support/Twig/Journal.php 87 The method formatBudgetPerspective() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5. - -139 + +166 /sites/firefly-iii/app/Support/Twig/Journal.php 239 The method journalBudgets() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -140 + +167 /sites/firefly-iii/app/Support/Twig/Journal.php 276 The method journalCategories() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5. - -141 + +168 /sites/firefly-iii/app/User.php -58 -The class User has 16 public methods. Consider refactoring User to keep number of public methods under 10. - - -142 -/sites/firefly-iii/database/migrations/2016_02_04_144117_changes_for_v380.php -29 -Avoid using short method names like ChangesForV380::up(). The configured minimum method name length is 3. - - -143 -/sites/firefly-iii/database/migrations/2016_02_04_144117_changes_for_v380.php -29 -The method up() has 41 lines of code. Current threshold is set to 40. Avoid really long methods. - - -144 -/sites/firefly-iii/database/migrations/2016_02_24_172426_create_jobs_table.php -18 -Avoid using short method names like CreateJobsTable::up(). The configured minimum method name length is 3. - - -145 -/sites/firefly-iii/database/migrations/2016_04_08_181054_changes_for_v383.php -29 -Avoid using short method names like ChangesForV383::up(). The configured minimum method name length is 3. - - -146 -/sites/firefly-iii/database/migrations/2016_04_25_093451_changes_for_v390.php -59 -Avoid using short method names like ChangesForV390::up(). The configured minimum method name length is 3. - - -147 -/sites/firefly-iii/database/migrations/2016_04_25_093451_changes_for_v390.php -59 -The method up() has 75 lines of code. Current threshold is set to 40. Avoid really long methods. +61 +The class User has 17 public methods. Consider refactoring User to keep number of public methods under 10.

    Processing errors

    - - - - - - - - - - - - - + + + + + - + - + - - + + - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + +
    FileProblem
    /sites/firefly-iii/app/Console/Commands/UpgradeFireflyInstructions.phpUnexpected token: ??, line: 56, col: 38, file: /sites/firefly-iii/app/Console/Commands/UpgradeFireflyInstructions.php.
    /sites/firefly-iii/app/Helpers/Collection/Balance.phpUnexpected token: ??, line: 51, col: 37, file: /sites/firefly-iii/app/Helpers/Collection/Balance.php.
    /sites/firefly-iii/app/Helpers/Collection/BalanceLine.phpUnexpected token: ??, line: 82, col: 30, file: /sites/firefly-iii/app/Helpers/Collection/BalanceLine.php.
    /sites/firefly-iii/app/Helpers/Collection/BillLine.phpUnexpected token: ??, line: 45, col: 30, file: /sites/firefly-iii/app/Helpers/Collection/BillLine.php.
    /sites/firefly-iii/app/Helpers/Collection/BudgetLine.phpUnexpected token: ??, line: 43, col: 30, file: /sites/firefly-iii/app/Helpers/Collection/BudgetLine.php.
    /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountIban.phpUnexpected token: DEFAULT, line: 59, col: 55, file: /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountIban.php.
    /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountName.phpUnexpected token: DEFAULT, line: 39, col: 55, file: /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountName.php.
    /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountNumber.phpUnexpected token: ??, line: 40, col: 31, file: /sites/firefly-iii/app/Helpers/Csv/Converter/AssetAccountNumber.php.
    /sites/firefly-iii/app/Helpers/Csv/Converter/Description.phpUnexpected token: ??, line: 27, col: 51, file: /sites/firefly-iii/app/Helpers/Csv/Converter/Description.php.
    /sites/firefly-iii/app/Helpers/Csv/Data.phpUnexpected token: ??, line: 70, col: 38, file: /sites/firefly-iii/app/Helpers/Csv/Data.php.
    /sites/firefly-iii/app/Helpers/Csv/Importer.phpUnexpected token: ??, line: 185, col: 54, file: /sites/firefly-iii/app/Helpers/Csv/Importer.php.
    /sites/firefly-iii/app/Helpers/Csv/Mapper/AssetAccount.phpUnexpected token: ??, line: 41, col: 36, file: /sites/firefly-iii/app/Helpers/Csv/Mapper/AssetAccount.php.
    /sites/firefly-iii/app/Helpers/Csv/PostProcessing/Amount.phpUnexpected token: ??, line: 30, col: 55, file: /sites/firefly-iii/app/Helpers/Csv/PostProcessing/Amount.php.
    /sites/firefly-iii/app/Helpers/Csv/PostProcessing/AssetAccount.phpUnexpected token: ??, line: 74, col: 62, file: /sites/firefly-iii/app/Helpers/Csv/PostProcessing/AssetAccount.php.
    /sites/firefly-iii/app/Helpers/Csv/PostProcessing/Description.phpUnexpected token: ??, line: 29, col: 65, file: /sites/firefly-iii/app/Helpers/Csv/PostProcessing/Description.php.
    /sites/firefly-iii/app/Helpers/Report/ReportHelper.phpUnexpected token: ??, line: 273, col: 59, file: /sites/firefly-iii/app/Helpers/Report/ReportHelper.php.
    /sites/firefly-iii/app/Http/Controllers/AccountController.phpUnexpected token: ??, line: 169, col: 23, file: /sites/firefly-iii/app/Http/Controllers/AccountController.php.
    /sites/firefly-iii/app/Http/Controllers/BudgetController.phpUnexpected token: DEFAULT, line: 187, col: 69, file: /sites/firefly-iii/app/Http/Controllers/BudgetController.php.
    /sites/firefly-iii/app/Helpers/Csv/Importer.phpUnexpected token: ??, line: 193, col: 54, file: /sites/firefly-iii/app/Helpers/Csv/Importer.php.
    /sites/firefly-iii/app/Helpers/Report/ReportHelper.phpUnexpected token: ??, line: 273, col: 59, file: /sites/firefly-iii/app/Helpers/Report/ReportHelper.php.
    /sites/firefly-iii/app/Http/Controllers/AccountController.phpUnexpected token: ??, line: 169, col: 23, file: /sites/firefly-iii/app/Http/Controllers/AccountController.php.
    /sites/firefly-iii/app/Http/Controllers/BudgetController.phpUnexpected token: DEFAULT, line: 189, col: 69, file: /sites/firefly-iii/app/Http/Controllers/BudgetController.php.
    /sites/firefly-iii/app/Http/Controllers/CategoryController.phpUnexpected token: DEFAULT, line: 211, col: 70, file: /sites/firefly-iii/app/Http/Controllers/CategoryController.php.
    /sites/firefly-iii/app/Http/Controllers/Chart/AccountController.phpUnexpected token: ??, line: 80, col: 60, file: /sites/firefly-iii/app/Http/Controllers/Chart/AccountController.php.
    /sites/firefly-iii/app/Http/Controllers/CsvController.phpUnexpected token: DEFAULT, line: 202, col: 109, file: /sites/firefly-iii/app/Http/Controllers/CsvController.php.
    /sites/firefly-iii/app/Http/Controllers/Chart/CategoryController.phpUnexpected token: DEFAULT, line: 68, col: 70, file: /sites/firefly-iii/app/Http/Controllers/Chart/CategoryController.php.
    /sites/firefly-iii/app/Http/Controllers/ExportController.phpUnexpected token: DEFAULT, line: 107, col: 65, file: /sites/firefly-iii/app/Http/Controllers/ExportController.php.
    /sites/firefly-iii/app/Http/Controllers/HomeController.phpUnexpected token: DEFAULT, line: 127, col: 73, file: /sites/firefly-iii/app/Http/Controllers/HomeController.php.
    /sites/firefly-iii/app/Http/Controllers/HomeController.phpUnexpected token: DEFAULT, line: 134, col: 73, file: /sites/firefly-iii/app/Http/Controllers/HomeController.php.
    /sites/firefly-iii/app/Http/Controllers/JsonController.phpUnexpected token: DEFAULT, line: 116, col: 60, file: /sites/firefly-iii/app/Http/Controllers/JsonController.php.
    /sites/firefly-iii/app/Http/Controllers/PiggyBankController.phpUnexpected token: DEFAULT, line: 78, col: 93, file: /sites/firefly-iii/app/Http/Controllers/PiggyBankController.php.
    /sites/firefly-iii/app/Http/Controllers/Popup/ReportController.phpUnexpected token: ??, line: 246, col: 59, file: /sites/firefly-iii/app/Http/Controllers/Popup/ReportController.php.
    /sites/firefly-iii/app/Http/Controllers/PiggyBankController.phpUnexpected token: DEFAULT, line: 98, col: 93, file: /sites/firefly-iii/app/Http/Controllers/PiggyBankController.php.
    /sites/firefly-iii/app/Http/Controllers/Popup/ReportController.phpUnexpected token: ??, line: 47, col: 51, file: /sites/firefly-iii/app/Http/Controllers/Popup/ReportController.php.
    /sites/firefly-iii/app/Http/Controllers/PreferencesController.phpUnexpected token: DEFAULT, line: 77, col: 71, file: /sites/firefly-iii/app/Http/Controllers/PreferencesController.php.
    /sites/firefly-iii/app/Http/Controllers/ReportController.phpUnexpected token: DEFAULT, line: 83, col: 60, file: /sites/firefly-iii/app/Http/Controllers/ReportController.php.
    /sites/firefly-iii/app/Http/Controllers/RuleGroupController.phpUnexpected token: DEFAULT, line: 189, col: 67, file: /sites/firefly-iii/app/Http/Controllers/RuleGroupController.php.
    /sites/firefly-iii/app/Http/Controllers/Transaction/MassController.phpUnexpected token: DEFAULT, line: 111, col: 92, file: /sites/firefly-iii/app/Http/Controllers/Transaction/MassController.php.
    /sites/firefly-iii/app/Http/Controllers/Transaction/SplitController.phpUnexpected token: ??, line: 68, col: 67, file: /sites/firefly-iii/app/Http/Controllers/Transaction/SplitController.php.
    /sites/firefly-iii/app/Http/Controllers/Transaction/SplitController.phpUnexpected token: ??, line: 72, col: 67, file: /sites/firefly-iii/app/Http/Controllers/Transaction/SplitController.php.
    /sites/firefly-iii/app/Http/Controllers/TransactionController.phpUnexpected token: ??, line: 95, col: 64, file: /sites/firefly-iii/app/Http/Controllers/TransactionController.php.
    /sites/firefly-iii/app/Http/Requests/JournalFormRequest.phpUnexpected token: ??, line: 43, col: 36, file: /sites/firefly-iii/app/Http/Requests/JournalFormRequest.php.
    /sites/firefly-iii/app/Http/Requests/SplitJournalFormRequest.phpUnexpected token: ??, line: 40, col: 68, file: /sites/firefly-iii/app/Http/Requests/SplitJournalFormRequest.php.
    /sites/firefly-iii/app/Http/Requests/TagFormRequest.phpUnexpected token: ??, line: 49, col: 36, file: /sites/firefly-iii/app/Http/Requests/TagFormRequest.php.
    /sites/firefly-iii/app/Http/breadcrumbs.phpUnexpected token: ??, line: 594, col: 56, file: /sites/firefly-iii/app/Http/breadcrumbs.php.
    /sites/firefly-iii/app/Models/AccountType.phpUnexpected token: DEFAULT, line: 35, col: 11, file: /sites/firefly-iii/app/Models/AccountType.php.
    /sites/firefly-iii/app/Models/Tag.phpUnexpected token: ??, line: 81, col: 57, file: /sites/firefly-iii/app/Models/Tag.php.
    /sites/firefly-iii/app/Repositories/Bill/BillRepository.phpUnexpected token: ??, line: 212, col: 48, file: /sites/firefly-iii/app/Repositories/Bill/BillRepository.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountExactly.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountExactly.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountLess.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountLess.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountMore.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountMore.php.
    /sites/firefly-iii/app/Support/Amount.phpUnexpected token: ??, line: 87, col: 61, file: /sites/firefly-iii/app/Support/Amount.php.
    /sites/firefly-iii/app/Support/Migration/TestData.phpUnexpected token: ??, line: 267, col: 77, file: /sites/firefly-iii/app/Support/Migration/TestData.php.
    /sites/firefly-iii/app/Support/Models/TransactionJournalSupport.phpUnexpected token: ??, line: 286, col: 52, file: /sites/firefly-iii/app/Support/Models/TransactionJournalSupport.php.
    /sites/firefly-iii/app/Support/Twig/General.phpUnexpected token: ??, line: 112, col: 44, file: /sites/firefly-iii/app/Support/Twig/General.php.
    /sites/firefly-iii/app/Validation/FireflyValidator.phpUnexpected token: ??, line: 84, col: 33, file: /sites/firefly-iii/app/Validation/FireflyValidator.php.
    /sites/firefly-iii/app/Http/breadcrumbs.phpUnexpected token: ??, line: 573, col: 56, file: /sites/firefly-iii/app/Http/breadcrumbs.php.
    /sites/firefly-iii/app/Import/Importer/CsvImporter.phpUnexpected token: DEFAULT, line: 82, col: 62, file: /sites/firefly-iii/app/Import/Importer/CsvImporter.php.
    /sites/firefly-iii/app/Import/Mapper/AssetAccountIbans.phpUnexpected token: DEFAULT, line: 33, col: 59, file: /sites/firefly-iii/app/Import/Mapper/AssetAccountIbans.php.
    /sites/firefly-iii/app/Import/Mapper/AssetAccounts.phpUnexpected token: DEFAULT, line: 33, col: 56, file: /sites/firefly-iii/app/Import/Mapper/AssetAccounts.php.
    /sites/firefly-iii/app/Import/Mapper/OpposingAccountIbans.phpUnexpected token: DEFAULT, line: 35, col: 30, file: /sites/firefly-iii/app/Import/Mapper/OpposingAccountIbans.php.
    /sites/firefly-iii/app/Import/Mapper/OpposingAccounts.phpUnexpected token: DEFAULT, line: 35, col: 30, file: /sites/firefly-iii/app/Import/Mapper/OpposingAccounts.php.
    /sites/firefly-iii/app/Models/AccountType.phpUnexpected token: DEFAULT, line: 33, col: 11, file: /sites/firefly-iii/app/Models/AccountType.php.
    /sites/firefly-iii/app/Models/Tag.phpUnexpected token: ??, line: 81, col: 57, file: /sites/firefly-iii/app/Models/Tag.php.
    /sites/firefly-iii/app/Repositories/Bill/BillRepository.phpUnexpected token: ??, line: 233, col: 48, file: /sites/firefly-iii/app/Repositories/Bill/BillRepository.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountExactly.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountExactly.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountLess.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountLess.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountMore.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountMore.php.
    /sites/firefly-iii/app/Support/Amount.phpUnexpected token: ??, line: 87, col: 61, file: /sites/firefly-iii/app/Support/Amount.php.
    /sites/firefly-iii/app/Support/Migration/TestData.phpUnexpected token: ??, line: 267, col: 77, file: /sites/firefly-iii/app/Support/Migration/TestData.php.
    /sites/firefly-iii/app/Support/Models/TransactionJournalSupport.phpUnexpected token: ??, line: 286, col: 52, file: /sites/firefly-iii/app/Support/Models/TransactionJournalSupport.php.
    /sites/firefly-iii/app/Support/Twig/General.phpUnexpected token: ??, line: 90, col: 58, file: /sites/firefly-iii/app/Support/Twig/General.php.
    /sites/firefly-iii/app/Validation/FireflyValidator.phpUnexpected token: ??, line: 82, col: 33, file: /sites/firefly-iii/app/Validation/FireflyValidator.php.
    \ No newline at end of file From e41c89bd5906cf9ffe07eb1ccc00c56c995647d1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 30 Jul 2016 16:29:17 +0200 Subject: [PATCH 55/94] Does not belong. --- public/result.html | 1055 -------------------------------------------- 1 file changed, 1055 deletions(-) delete mode 100644 public/result.html diff --git a/public/result.html b/public/result.html deleted file mode 100644 index db161648c9..0000000000 --- a/public/result.html +++ /dev/null @@ -1,1055 +0,0 @@ -PHPMD -

    PHPMD report

    Problems found

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    #FileLineProblem
    1/sites/firefly-iii/app/Crud/Account/AccountCrud.php32The class AccountCrud has an overall complexity of 52 which is very high. The configured complexity threshold is 50.
    2/sites/firefly-iii/app/Handlers/Events/UpdateJournalConnection.php33The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    3/sites/firefly-iii/app/Helpers/Report/BalanceReportHelper.php34The class BalanceReportHelper has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13.
    4/sites/firefly-iii/app/Helpers/Report/BalanceReportHelper.php267The method removeUnusedBudgets() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    5/sites/firefly-iii/app/Helpers/Report/BudgetReportHelper.php51The method budgetYearOverview() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    6/sites/firefly-iii/app/Helpers/Report/BudgetReportHelper.php51The method budgetYearOverview() has 59 lines of code. Current threshold is set to 40. Avoid really long methods.
    7/sites/firefly-iii/app/Helpers/Report/BudgetReportHelper.php118The method getBudgetReport() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    8/sites/firefly-iii/app/Helpers/Report/BudgetReportHelper.php118The method getBudgetReport() has 57 lines of code. Current threshold is set to 40. Avoid really long methods.
    9/sites/firefly-iii/app/Http/Controllers/Admin/UserController.php54The method index() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    10/sites/firefly-iii/app/Http/Controllers/AttachmentController.php34The class AttachmentController has a coupling between objects value of 17. Consider to reduce the number of dependencies under 13.
    11/sites/firefly-iii/app/Http/Controllers/Auth/AuthController.php37The class AuthController has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13.
    12/sites/firefly-iii/app/Http/Controllers/Auth/AuthController.php65The method login() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    13/sites/firefly-iii/app/Http/Controllers/Auth/PasswordController.php60The method sendResetLinkEmail() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    14/sites/firefly-iii/app/Http/Controllers/Auth/TwoFactorController.php75Avoid unused parameters such as '$request'.
    15/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php32The class BudgetController has a coupling between objects value of 13. Consider to reduce the number of dependencies under 13.
    16/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php196The method multiYear() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    17/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php196The method multiYear() has 60 lines of code. Current threshold is set to 40. Avoid really long methods.
    18/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php196The method multiYear has 5 parameters. Consider to reduce parameter number under 5.
    19/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php266The method period() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    20/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php266The method period() has 47 lines of code. Current threshold is set to 40. Avoid really long methods.
    21/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php266The method period has 5 parameters. Consider to reduce parameter number under 5.
    22/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php340The method spentInPeriodMulti() has an NPath complexity of 251. The configured NPath complexity threshold is 200.
    23/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php340The method spentInPeriodMulti() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    24/sites/firefly-iii/app/Http/Controllers/Chart/BudgetController.php340The method spentInPeriodMulti() has an NPath complexity of 251. The configured NPath complexity threshold is 128.
    25/sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php101The method yearInOut() has 43 lines of code. Current threshold is set to 40. Avoid really long methods.
    26/sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php101The method yearInOut has 5 parameters. Consider to reduce parameter number under 5.
    27/sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php154The method yearInOutSummarized() has 42 lines of code. Current threshold is set to 40. Avoid really long methods.
    28/sites/firefly-iii/app/Http/Controllers/Chart/ReportController.php154The method yearInOutSummarized has 5 parameters. Consider to reduce parameter number under 5.
    29/sites/firefly-iii/app/Http/Controllers/Controller.php30The class Controller has 20 children. Consider to rebalance this class hierarchy to keep number of children under 15.
    30/sites/firefly-iii/app/Http/Controllers/CurrencyController.php31The class CurrencyController has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13.
    31/sites/firefly-iii/app/Http/Controllers/ImportController.php50Avoid unused local variables such as '$importer'.
    32/sites/firefly-iii/app/Http/Controllers/ImportController.php243The method upload() has 44 lines of code. Current threshold is set to 40. Avoid really long methods.
    33/sites/firefly-iii/app/Http/Controllers/ImportController.php294The method jobInCorrectStep() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    34/sites/firefly-iii/app/Http/Controllers/RuleController.php36The class RuleController has 13 public methods. Consider refactoring RuleController to keep number of public methods under 10.
    35/sites/firefly-iii/app/Http/Controllers/RuleController.php36The class RuleController has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13.
    36/sites/firefly-iii/app/Http/Controllers/RuleController.php326Avoid using short method names like RuleController::up(). The configured minimum method name length is 3.
    37/sites/firefly-iii/app/Http/Controllers/TagController.php41The class TagController has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13.
    38/sites/firefly-iii/app/Http/Controllers/TagController.php181The method index() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    39/sites/firefly-iii/app/Http/Controllers/TagController.php181The method index() has an NPath complexity of 131. The configured NPath complexity threshold is 128.
    40/sites/firefly-iii/app/Http/Middleware/AuthenticateTwoFactor.php36The method handle() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    41/sites/firefly-iii/app/Http/Middleware/IsConfirmed.php36The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    42/sites/firefly-iii/app/Http/Middleware/IsNotConfirmed.php36The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    43/sites/firefly-iii/app/Http/Middleware/Range.php62The method handle() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    44/sites/firefly-iii/app/Http/Middleware/Range.php95The method datePicker() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    45/sites/firefly-iii/app/Http/Middleware/Range.php95The method datePicker() has 48 lines of code. Current threshold is set to 40. Avoid really long methods.
    46/sites/firefly-iii/app/Http/Middleware/RedirectIfTwoFactorAuthenticated.php35The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    47/sites/firefly-iii/app/Http/Requests/Request.php21The class Request has 19 children. Consider to rebalance this class hierarchy to keep number of children under 15.
    48/sites/firefly-iii/app/Import/Converter/AccountId.php31The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    49/sites/firefly-iii/app/Import/Converter/Amount.php30The method convert() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    50/sites/firefly-iii/app/Import/Converter/AssetAccountIban.php32The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    51/sites/firefly-iii/app/Import/Converter/AssetAccountIban.php32The method convert() has 44 lines of code. Current threshold is set to 40. Avoid really long methods.
    52/sites/firefly-iii/app/Import/Converter/AssetAccountName.php32The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    53/sites/firefly-iii/app/Import/Converter/AssetAccountName.php32The method convert() has 43 lines of code. Current threshold is set to 40. Avoid really long methods.
    54/sites/firefly-iii/app/Import/Converter/AssetAccountNumber.php33The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    55/sites/firefly-iii/app/Import/Converter/AssetAccountNumber.php33The method convert() has 41 lines of code. Current threshold is set to 40. Avoid really long methods.
    56/sites/firefly-iii/app/Import/Converter/BasicConverter.php22The class BasicConverter has 26 children. Consider to rebalance this class hierarchy to keep number of children under 15.
    57/sites/firefly-iii/app/Import/Converter/BillId.php31The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    58/sites/firefly-iii/app/Import/Converter/BillId.php31The method convert() has 41 lines of code. Current threshold is set to 40. Avoid really long methods.
    59/sites/firefly-iii/app/Import/Converter/BillName.php32The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    60/sites/firefly-iii/app/Import/Converter/BillName.php32The method convert() has 53 lines of code. Current threshold is set to 40. Avoid really long methods.
    61/sites/firefly-iii/app/Import/Converter/BudgetId.php32The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    62/sites/firefly-iii/app/Import/Converter/BudgetName.php32The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    63/sites/firefly-iii/app/Import/Converter/BudgetName.php32The method convert() has 43 lines of code. Current threshold is set to 40. Avoid really long methods.
    64/sites/firefly-iii/app/Import/Converter/CategoryId.php32The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    65/sites/firefly-iii/app/Import/Converter/CategoryName.php32The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    66/sites/firefly-iii/app/Import/Converter/CategoryName.php32The method convert() has 43 lines of code. Current threshold is set to 40. Avoid really long methods.
    67/sites/firefly-iii/app/Import/Converter/CurrencyId.php32The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    68/sites/firefly-iii/app/Import/Converter/CurrencyName.php31The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    69/sites/firefly-iii/app/Import/Converter/CurrencyName.php31The method convert() has 44 lines of code. Current threshold is set to 40. Avoid really long methods.
    70/sites/firefly-iii/app/Import/Converter/CurrencySymbol.php31The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    71/sites/firefly-iii/app/Import/Converter/CurrencySymbol.php31The method convert() has 44 lines of code. Current threshold is set to 40. Avoid really long methods.
    72/sites/firefly-iii/app/Import/Converter/OpposingAccountIban.php31The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    73/sites/firefly-iii/app/Import/Converter/OpposingAccountIban.php31The method convert() has 45 lines of code. Current threshold is set to 40. Avoid really long methods.
    74/sites/firefly-iii/app/Import/Converter/OpposingAccountName.php31The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    75/sites/firefly-iii/app/Import/Converter/OpposingAccountName.php31The method convert() has 44 lines of code. Current threshold is set to 40. Avoid really long methods.
    76/sites/firefly-iii/app/Import/Converter/OpposingAccountNumber.php32The method convert() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    77/sites/firefly-iii/app/Import/Converter/OpposingAccountNumber.php32The method convert() has 42 lines of code. Current threshold is set to 40. Avoid really long methods.
    78/sites/firefly-iii/app/Import/Converter/TagsComma.php31The method convert() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    79/sites/firefly-iii/app/Import/Converter/TagsComma.php31The method convert() has 56 lines of code. Current threshold is set to 40. Avoid really long methods.
    80/sites/firefly-iii/app/Import/Converter/TagsSpace.php31The method convert() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    81/sites/firefly-iii/app/Import/Converter/TagsSpace.php31The method convert() has 57 lines of code. Current threshold is set to 40. Avoid really long methods.
    82/sites/firefly-iii/app/Jobs/MailError.php67The method handle() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    83/sites/firefly-iii/app/Models/Account.php93The method firstOrCreateEncrypted() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    84/sites/firefly-iii/app/Models/Account.php186The method getIbanAttribute() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    85/sites/firefly-iii/app/Models/TransactionJournal.php94The class TransactionJournal has 23 public methods. Consider refactoring TransactionJournal to keep number of public methods under 10.
    86/sites/firefly-iii/app/Providers/AccountServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    87/sites/firefly-iii/app/Providers/AttachmentServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    88/sites/firefly-iii/app/Providers/BillServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    89/sites/firefly-iii/app/Providers/BudgetServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    90/sites/firefly-iii/app/Providers/CategoryServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    91/sites/firefly-iii/app/Providers/CrudServiceProvider.php48The method registerAccount() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    92/sites/firefly-iii/app/Providers/CrudServiceProvider.php68The method registerJournal() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    93/sites/firefly-iii/app/Providers/ExportJobServiceProvider.php52The method exportJob() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    94/sites/firefly-iii/app/Providers/ExportJobServiceProvider.php70The method importJob() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    95/sites/firefly-iii/app/Providers/FireflyServiceProvider.php37The class FireflyServiceProvider has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13.
    96/sites/firefly-iii/app/Providers/FireflyServiceProvider.php59The method register() has 58 lines of code. Current threshold is set to 40. Avoid really long methods.
    97/sites/firefly-iii/app/Providers/JournalServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    98/sites/firefly-iii/app/Providers/PiggyBankServiceProvider.php42The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    99/sites/firefly-iii/app/Providers/RuleGroupServiceProvider.php42The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    100/sites/firefly-iii/app/Providers/RuleServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    101/sites/firefly-iii/app/Providers/TagServiceProvider.php41The method register() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    102/sites/firefly-iii/app/Repositories/Account/AccountRepository.php35The class AccountRepository has 14 public methods. Consider refactoring AccountRepository to keep number of public methods under 10.
    103/sites/firefly-iii/app/Repositories/Account/AccountRepository.php41Avoid unused private fields such as '$validFields'.
    104/sites/firefly-iii/app/Repositories/Account/AccountRepository.php247The method getPiggyBankAccounts() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    105/sites/firefly-iii/app/Repositories/Account/AccountRepository.php289The method getSavingsAccounts() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    106/sites/firefly-iii/app/Repositories/Account/AccountRepository.php289The method getSavingsAccounts() has 44 lines of code. Current threshold is set to 40. Avoid really long methods.
    107/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php32The class BudgetRepository has 13 public methods. Consider refactoring BudgetRepository to keep number of public methods under 10.
    108/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php116The method firstUseDate() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    109/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php216The method journalsInPeriod() has 60 lines of code. Current threshold is set to 40. Avoid really long methods.
    110/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php284The method journalsInPeriodWithoutBudget() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    111/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php284The method journalsInPeriodWithoutBudget() has 49 lines of code. Current threshold is set to 40. Avoid really long methods.
    112/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php342The method spentInPeriod() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    113/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php342The method spentInPeriod() has 51 lines of code. Current threshold is set to 40. Avoid really long methods.
    114/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php478The method updateLimitAmount() has 43 lines of code. Current threshold is set to 40. Avoid really long methods.
    115/sites/firefly-iii/app/Repositories/Budget/BudgetRepository.php478The method updateLimitAmount has 5 parameters. Consider to reduce parameter number under 5.
    116/sites/firefly-iii/app/Repositories/Budget/BudgetRepositoryInterface.php152The method updateLimitAmount has 5 parameters. Consider to reduce parameter number under 5.
    117/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php28The class CategoryRepository has 14 public methods. Consider refactoring CategoryRepository to keep number of public methods under 10.
    118/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php28The class CategoryRepository has an overall complexity of 55 which is very high. The configured complexity threshold is 50.
    119/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php129The method firstUseDate() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5.
    120/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php129The method firstUseDate() has 42 lines of code. Current threshold is set to 40. Avoid really long methods.
    121/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php151Avoid excessively long variable names like $firstTransactionQuery. Keep variable name length under 20.
    122/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php244The method journalsInPeriod() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    123/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php244The method journalsInPeriod() has an NPath complexity of 128. The configured NPath complexity threshold is 128.
    124/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php244The method journalsInPeriod() has 54 lines of code. Current threshold is set to 40. Avoid really long methods.
    125/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php244The method journalsInPeriod has 5 parameters. Consider to reduce parameter number under 5.
    126/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php307The method journalsInPeriodWithoutCategory() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    127/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php307The method journalsInPeriodWithoutCategory() has 52 lines of code. Current threshold is set to 40. Avoid really long methods.
    128/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php366The method lastUseDate() has a Cyclomatic Complexity of 9. The configured cyclomatic complexity threshold is 5.
    129/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php366The method lastUseDate() has 42 lines of code. Current threshold is set to 40. Avoid really long methods.
    130/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php482The method sumInPeriod() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    131/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php482The method sumInPeriod() has 58 lines of code. Current threshold is set to 40. Avoid really long methods.
    132/sites/firefly-iii/app/Repositories/Category/CategoryRepository.php482The method sumInPeriod has 5 parameters. Consider to reduce parameter number under 5.
    133/sites/firefly-iii/app/Repositories/Category/CategoryRepositoryInterface.php104The method journalsInPeriod has 5 parameters. Consider to reduce parameter number under 5.
    134/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php39The class JournalRepository has an overall complexity of 50 which is very high. The configured complexity threshold is 50.
    135/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php39The class JournalRepository has a coupling between objects value of 19. Consider to reduce the number of dependencies under 13.
    136/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php222The method getTransactions() has 57 lines of code. Current threshold is set to 40. Avoid really long methods.
    137/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php285The method store() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    138/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php285The method store() has 64 lines of code. Current threshold is set to 40. Avoid really long methods.
    139/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php387The method update() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    140/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php387The method update() has 55 lines of code. Current threshold is set to 40. Avoid really long methods.
    141/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php477The method storeAccounts() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    142/sites/firefly-iii/app/Repositories/Journal/JournalRepository.php576The method updateTags() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    143/sites/firefly-iii/app/Repositories/Rule/RuleRepository.php26The class RuleRepository has 12 public methods. Consider refactoring RuleRepository to keep number of public methods under 10.
    144/sites/firefly-iii/app/Repositories/Tag/TagRepository.php49The method connect() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    145/sites/firefly-iii/app/Repositories/Tag/TagRepository.php183The method connectAdvancePayment() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    146/sites/firefly-iii/app/Repositories/Tag/TagRepository.php228The method connectBalancingAct() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    147/sites/firefly-iii/app/Repositories/Tag/TagRepository.php268The method matchAll() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    148/sites/firefly-iii/app/Rules/Processor.php179The method triggered() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    149/sites/firefly-iii/app/Rules/TransactionMatcher.php57The method findMatchingTransactions() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    150/sites/firefly-iii/app/Rules/TransactionMatcher.php57The method findMatchingTransactions() has 49 lines of code. Current threshold is set to 40. Avoid really long methods.
    151/sites/firefly-iii/app/Support/Binder/Date.php36The method routeBinder() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    152/sites/firefly-iii/app/Support/CacheProperties.php95The method md5() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    153/sites/firefly-iii/app/Support/ExpandedForm.php29The class ExpandedForm has 18 public methods. Consider refactoring ExpandedForm to keep number of public methods under 10.
    154/sites/firefly-iii/app/Support/ExpandedForm.php211The method makeSelectList() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    155/sites/firefly-iii/app/Support/ExpandedForm.php236The method makeSelectListWithEmpty() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    156/sites/firefly-iii/app/Support/ExpandedForm.php442The method fillFieldValue() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    157/sites/firefly-iii/app/Support/Models/TagSupport.php31The method tagAllowAdvance() has a Cyclomatic Complexity of 8. The configured cyclomatic complexity threshold is 5.
    158/sites/firefly-iii/app/Support/Navigation.php73The method endOfPeriod() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    159/sites/firefly-iii/app/Support/Navigation.php73The method endOfPeriod() has 55 lines of code. Current threshold is set to 40. Avoid really long methods.
    160/sites/firefly-iii/app/Support/Navigation.php213The method startOfPeriod() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    161/sites/firefly-iii/app/Support/Navigation.php213The method startOfPeriod() has 42 lines of code. Current threshold is set to 40. Avoid really long methods.
    162/sites/firefly-iii/app/Support/Navigation.php264The method subtractPeriod() has 52 lines of code. Current threshold is set to 40. Avoid really long methods.
    163/sites/firefly-iii/app/Support/Twig/Journal.php37The method formatAccountPerspective() has a Cyclomatic Complexity of 7. The configured cyclomatic complexity threshold is 5.
    164/sites/firefly-iii/app/Support/Twig/Journal.php37The method formatAccountPerspective() has 46 lines of code. Current threshold is set to 40. Avoid really long methods.
    165/sites/firefly-iii/app/Support/Twig/Journal.php87The method formatBudgetPerspective() has a Cyclomatic Complexity of 6. The configured cyclomatic complexity threshold is 5.
    166/sites/firefly-iii/app/Support/Twig/Journal.php239The method journalBudgets() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    167/sites/firefly-iii/app/Support/Twig/Journal.php276The method journalCategories() has a Cyclomatic Complexity of 5. The configured cyclomatic complexity threshold is 5.
    168/sites/firefly-iii/app/User.php61The class User has 17 public methods. Consider refactoring User to keep number of public methods under 10.

    Processing errors

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FileProblem
    /sites/firefly-iii/app/Console/Commands/UpgradeFireflyInstructions.phpUnexpected token: ??, line: 56, col: 38, file: /sites/firefly-iii/app/Console/Commands/UpgradeFireflyInstructions.php.
    /sites/firefly-iii/app/Helpers/Collection/Balance.phpUnexpected token: ??, line: 51, col: 37, file: /sites/firefly-iii/app/Helpers/Collection/Balance.php.
    /sites/firefly-iii/app/Helpers/Collection/BalanceLine.phpUnexpected token: ??, line: 82, col: 30, file: /sites/firefly-iii/app/Helpers/Collection/BalanceLine.php.
    /sites/firefly-iii/app/Helpers/Collection/BillLine.phpUnexpected token: ??, line: 45, col: 30, file: /sites/firefly-iii/app/Helpers/Collection/BillLine.php.
    /sites/firefly-iii/app/Helpers/Collection/BudgetLine.phpUnexpected token: ??, line: 43, col: 30, file: /sites/firefly-iii/app/Helpers/Collection/BudgetLine.php.
    /sites/firefly-iii/app/Helpers/Csv/Importer.phpUnexpected token: ??, line: 193, col: 54, file: /sites/firefly-iii/app/Helpers/Csv/Importer.php.
    /sites/firefly-iii/app/Helpers/Report/ReportHelper.phpUnexpected token: ??, line: 273, col: 59, file: /sites/firefly-iii/app/Helpers/Report/ReportHelper.php.
    /sites/firefly-iii/app/Http/Controllers/AccountController.phpUnexpected token: ??, line: 169, col: 23, file: /sites/firefly-iii/app/Http/Controllers/AccountController.php.
    /sites/firefly-iii/app/Http/Controllers/BudgetController.phpUnexpected token: DEFAULT, line: 189, col: 69, file: /sites/firefly-iii/app/Http/Controllers/BudgetController.php.
    /sites/firefly-iii/app/Http/Controllers/CategoryController.phpUnexpected token: DEFAULT, line: 211, col: 70, file: /sites/firefly-iii/app/Http/Controllers/CategoryController.php.
    /sites/firefly-iii/app/Http/Controllers/Chart/AccountController.phpUnexpected token: ??, line: 80, col: 60, file: /sites/firefly-iii/app/Http/Controllers/Chart/AccountController.php.
    /sites/firefly-iii/app/Http/Controllers/Chart/CategoryController.phpUnexpected token: DEFAULT, line: 68, col: 70, file: /sites/firefly-iii/app/Http/Controllers/Chart/CategoryController.php.
    /sites/firefly-iii/app/Http/Controllers/ExportController.phpUnexpected token: DEFAULT, line: 107, col: 65, file: /sites/firefly-iii/app/Http/Controllers/ExportController.php.
    /sites/firefly-iii/app/Http/Controllers/HomeController.phpUnexpected token: DEFAULT, line: 134, col: 73, file: /sites/firefly-iii/app/Http/Controllers/HomeController.php.
    /sites/firefly-iii/app/Http/Controllers/JsonController.phpUnexpected token: DEFAULT, line: 116, col: 60, file: /sites/firefly-iii/app/Http/Controllers/JsonController.php.
    /sites/firefly-iii/app/Http/Controllers/PiggyBankController.phpUnexpected token: DEFAULT, line: 98, col: 93, file: /sites/firefly-iii/app/Http/Controllers/PiggyBankController.php.
    /sites/firefly-iii/app/Http/Controllers/Popup/ReportController.phpUnexpected token: ??, line: 47, col: 51, file: /sites/firefly-iii/app/Http/Controllers/Popup/ReportController.php.
    /sites/firefly-iii/app/Http/Controllers/PreferencesController.phpUnexpected token: DEFAULT, line: 77, col: 71, file: /sites/firefly-iii/app/Http/Controllers/PreferencesController.php.
    /sites/firefly-iii/app/Http/Controllers/ReportController.phpUnexpected token: DEFAULT, line: 83, col: 60, file: /sites/firefly-iii/app/Http/Controllers/ReportController.php.
    /sites/firefly-iii/app/Http/Controllers/RuleGroupController.phpUnexpected token: DEFAULT, line: 189, col: 67, file: /sites/firefly-iii/app/Http/Controllers/RuleGroupController.php.
    /sites/firefly-iii/app/Http/Controllers/Transaction/MassController.phpUnexpected token: DEFAULT, line: 111, col: 92, file: /sites/firefly-iii/app/Http/Controllers/Transaction/MassController.php.
    /sites/firefly-iii/app/Http/Controllers/Transaction/SplitController.phpUnexpected token: ??, line: 72, col: 67, file: /sites/firefly-iii/app/Http/Controllers/Transaction/SplitController.php.
    /sites/firefly-iii/app/Http/Controllers/TransactionController.phpUnexpected token: ??, line: 95, col: 64, file: /sites/firefly-iii/app/Http/Controllers/TransactionController.php.
    /sites/firefly-iii/app/Http/Requests/JournalFormRequest.phpUnexpected token: ??, line: 43, col: 36, file: /sites/firefly-iii/app/Http/Requests/JournalFormRequest.php.
    /sites/firefly-iii/app/Http/Requests/SplitJournalFormRequest.phpUnexpected token: ??, line: 40, col: 68, file: /sites/firefly-iii/app/Http/Requests/SplitJournalFormRequest.php.
    /sites/firefly-iii/app/Http/Requests/TagFormRequest.phpUnexpected token: ??, line: 49, col: 36, file: /sites/firefly-iii/app/Http/Requests/TagFormRequest.php.
    /sites/firefly-iii/app/Http/breadcrumbs.phpUnexpected token: ??, line: 573, col: 56, file: /sites/firefly-iii/app/Http/breadcrumbs.php.
    /sites/firefly-iii/app/Import/Importer/CsvImporter.phpUnexpected token: DEFAULT, line: 82, col: 62, file: /sites/firefly-iii/app/Import/Importer/CsvImporter.php.
    /sites/firefly-iii/app/Import/Mapper/AssetAccountIbans.phpUnexpected token: DEFAULT, line: 33, col: 59, file: /sites/firefly-iii/app/Import/Mapper/AssetAccountIbans.php.
    /sites/firefly-iii/app/Import/Mapper/AssetAccounts.phpUnexpected token: DEFAULT, line: 33, col: 56, file: /sites/firefly-iii/app/Import/Mapper/AssetAccounts.php.
    /sites/firefly-iii/app/Import/Mapper/OpposingAccountIbans.phpUnexpected token: DEFAULT, line: 35, col: 30, file: /sites/firefly-iii/app/Import/Mapper/OpposingAccountIbans.php.
    /sites/firefly-iii/app/Import/Mapper/OpposingAccounts.phpUnexpected token: DEFAULT, line: 35, col: 30, file: /sites/firefly-iii/app/Import/Mapper/OpposingAccounts.php.
    /sites/firefly-iii/app/Models/AccountType.phpUnexpected token: DEFAULT, line: 33, col: 11, file: /sites/firefly-iii/app/Models/AccountType.php.
    /sites/firefly-iii/app/Models/Tag.phpUnexpected token: ??, line: 81, col: 57, file: /sites/firefly-iii/app/Models/Tag.php.
    /sites/firefly-iii/app/Repositories/Bill/BillRepository.phpUnexpected token: ??, line: 233, col: 48, file: /sites/firefly-iii/app/Repositories/Bill/BillRepository.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountExactly.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountExactly.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountLess.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountLess.php.
    /sites/firefly-iii/app/Rules/Triggers/AmountMore.phpUnexpected token: ??, line: 57, col: 49, file: /sites/firefly-iii/app/Rules/Triggers/AmountMore.php.
    /sites/firefly-iii/app/Support/Amount.phpUnexpected token: ??, line: 87, col: 61, file: /sites/firefly-iii/app/Support/Amount.php.
    /sites/firefly-iii/app/Support/Migration/TestData.phpUnexpected token: ??, line: 267, col: 77, file: /sites/firefly-iii/app/Support/Migration/TestData.php.
    /sites/firefly-iii/app/Support/Models/TransactionJournalSupport.phpUnexpected token: ??, line: 286, col: 52, file: /sites/firefly-iii/app/Support/Models/TransactionJournalSupport.php.
    /sites/firefly-iii/app/Support/Twig/General.phpUnexpected token: ??, line: 90, col: 58, file: /sites/firefly-iii/app/Support/Twig/General.php.
    /sites/firefly-iii/app/Validation/FireflyValidator.phpUnexpected token: ??, line: 82, col: 33, file: /sites/firefly-iii/app/Validation/FireflyValidator.php.
    \ No newline at end of file From 0de72b691462678f546563dcff79f1afc4ca116f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 30 Jul 2016 16:29:49 +0200 Subject: [PATCH 56/94] Ignore result.html --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 736f45d8ce..76b63316ba 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .env _development .env.local +result.html From d26bbf3cdceaecb4bef613ecfe92a126bd91cc7c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 30 Jul 2016 19:09:58 +0200 Subject: [PATCH 57/94] Handling import values. This is a dead end but it seemed a good idea. --- app/Import/ImportEntry.php | 134 +++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/app/Import/ImportEntry.php b/app/Import/ImportEntry.php index 1cfc657669..8d5efc9c05 100644 --- a/app/Import/ImportEntry.php +++ b/app/Import/ImportEntry.php @@ -12,6 +12,10 @@ declare(strict_types = 1); namespace FireflyIII\Import; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Account; +use FireflyIII\Models\Bill; +use FireflyIII\Models\Budget; +use FireflyIII\Models\Category; use Log; /** @@ -21,10 +25,31 @@ use Log; */ class ImportEntry { + /** @var float */ public $amount; + /** @var Account */ + public $assetAccount; + /** @var int */ + public $assetAccountCertainty = 0; + /** @var Bill */ + public $bill; + /** @var int */ + public $billCertainty; + /** @var Budget */ + public $budget; + /** @var int */ + public $budgetCertainty; + /** @var Account */ + public $opposingAccount; + /** @var int */ + public $opposingAccountCertainty = 0; + /** @var Category */ + public $category; + /** @var int */ + public $categoryCertainty; /** * @param string $role @@ -51,6 +76,28 @@ class ImportEntry $this->setAmount($convertedValue); return; + case 'account-id': + case 'account-iban': + case 'account-name': + $this->setAssetAccount($convertedValue, $certainty); + break; + case 'opposing-number': + $this->setOpposingAccount($convertedValue, $certainty); + break; + case 'bill-id': + case 'bill-name': + $this->setBill($convertedValue, $certainty); + break; + case 'budget-id': + case 'budget-name': + $this->setObject('budget', 'budgetCertainty', $convertedValue, $certainty); + //$this->setBudget($convertedValue, $certainty); + break; + case 'category-id': + case 'category-name': + $this->setObject('category', 'categoryCertainty', $convertedValue, $certainty); + break; + } } @@ -61,4 +108,91 @@ class ImportEntry { $this->amount = $amount; } + + /** + * @param Account $account + * @param int $certainty + */ + private function setAssetAccount(Account $account, int $certainty) + { + if ($certainty > $this->assetAccountCertainty && !is_null($account->id)) { + Log::debug(sprintf('ImportEntry: Asset Account ID is now %d with certainty %d', $account->id, $certainty)); + $this->assetAccount = $account; + $this->assetAccountCertainty = $certainty; + + return; + } + Log::error(sprintf('Will not set asset account based on certainty %d (current certainty is %d) or NULL id.', $certainty, $this->assetAccountCertainty)); + } + + /** + * @param Bill $bill + * @param int $certainty + */ + private function setBill(Bill $bill, int $certainty) + { + if ($certainty > $this->billCertainty && !is_null($bill->id)) { + Log::debug(sprintf('ImportEntry: Bill-ID is now %d with certainty %d', $bill->id, $certainty)); + $this->bill = $bill; + $this->billCertainty = $certainty; + + return; + } + Log::error(sprintf('Will not set bill based on certainty %d (current certainty is %d) or NULL id.', $certainty, $this->billCertainty)); + } + + /** + * @param Budget $budget + * @param int $certainty + */ + private function setBudget(Budget $budget, int $certainty) + { + if ($certainty > $this->budgetCertainty && !is_null($budget->id)) { + Log::debug(sprintf('ImportEntry: Budget-ID is now %d with certainty %d', $budget->id, $certainty)); + $this->budget = $budget; + $this->budgetCertainty = $certainty; + + return; + } + Log::error(sprintf('Will not set budget based on certainty %d (current certainty is %d) or NULL id.', $certainty, $this->budgetCertainty)); + } + + /** + * @param string $field + * @param string $cert + * @param $object + * @param int $certainty + */ + private function setObject(string $field, string $cert, $object, int $certainty) + { + if ($certainty > $this->$cert && !is_null($object->id)) { + Log::debug(sprintf('ImportEntry: %s ID is now %d with certainty %d', $field, $object->id, $certainty)); + $this->$field = $object; + $this->$cert = $certainty; + + return; + } + Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->$cert)); + + } + + /** + * @param Account $account + * @param int $certainty + */ + private function setOpposingAccount(Account $account, int $certainty) + { + if ($certainty > $this->opposingAccountCertainty && !is_null($account->id)) { + Log::debug(sprintf('ImportEntry: Opposing Account ID is now %d with certainty %d', $account->id, $certainty)); + $this->assetAccount = $account; + $this->assetAccountCertainty = $certainty; + + return; + } + Log::error( + sprintf('Will not set opposing account based on certainty %d (current certainty is %d) or NULL id.', $certainty, $this->assetAccountCertainty) + ); + } + + } \ No newline at end of file From c8f5a6b7adb5be0d06efa3ae8b303e5c842187ba Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 30 Jul 2016 20:04:24 +0200 Subject: [PATCH 58/94] Ignore deleted columns. --- app/Repositories/Account/AccountRepository.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index ef95442a73..d072c706bc 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -135,6 +135,8 @@ class AccountRepository implements AccountRepositoryInterface $query->whereIn('destination.account_id', $accountIds); $query->whereNotIn('source.account_id', $accountIds); + $query->whereNull('destination.deleted_at'); + $query->whereNull('source.deleted_at'); } // remove group by From 1ba88f182b999320a26fa21a9d094028262579d2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 31 Jul 2016 17:17:45 +0200 Subject: [PATCH 59/94] Various CSV bugs and a config bug fixed. --- app/Import/ImportEntry.php | 233 ++++++++++++++++++---------------- app/Support/FireflyConfig.php | 24 +++- config/csv.php | 16 ++- resources/lang/en_US/csv.php | 4 +- 4 files changed, 159 insertions(+), 118 deletions(-) diff --git a/app/Import/ImportEntry.php b/app/Import/ImportEntry.php index 8d5efc9c05..bdae2d8398 100644 --- a/app/Import/ImportEntry.php +++ b/app/Import/ImportEntry.php @@ -11,11 +11,8 @@ declare(strict_types = 1); namespace FireflyIII\Import; +use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Account; -use FireflyIII\Models\Bill; -use FireflyIII\Models\Budget; -use FireflyIII\Models\Category; use Log; /** @@ -25,31 +22,33 @@ use Log; */ class ImportEntry { - /** @var float */ - public $amount; + /** @var array */ + public $certain = []; + /** @var array */ + public $fields = []; - /** @var Account */ - public $assetAccount; + /** @var array */ + private $validFields + = ['amount', + 'date-transaction', + 'date-interest', + 'date-book', + 'description', + 'date-process', + 'currency', 'asset-account', 'opposing-account', 'bill', 'budget', 'category']; - /** @var int */ - public $assetAccountCertainty = 0; - /** @var Bill */ - public $bill; - /** @var int */ - public $billCertainty; - /** @var Budget */ - public $budget; - /** @var int */ - public $budgetCertainty; - /** @var Account */ - public $opposingAccount; - /** @var int */ - public $opposingAccountCertainty = 0; + /** + * ImportEntry constructor. + */ + public function __construct() + { - /** @var Category */ - public $category; - /** @var int */ - public $categoryCertainty; + /** @var string $value */ + foreach ($this->validFields as $value) { + $this->fields[$value] = null; + $this->certain[$value] = 0; + } + } /** * @param string $role @@ -73,126 +72,144 @@ class ImportEntry /* * Easy enough. */ - $this->setAmount($convertedValue); + $this->setFloat('amount', $convertedValue, $certainty); return; case 'account-id': case 'account-iban': case 'account-name': - $this->setAssetAccount($convertedValue, $certainty); + $this->setObject('asset-account', $convertedValue, $certainty); break; case 'opposing-number': - $this->setOpposingAccount($convertedValue, $certainty); + case 'opposing-iban': + case 'opposing-id': + case 'opposing-number': + case 'opposing-name': + $this->setObject('opposing-account', $convertedValue, $certainty); break; case 'bill-id': case 'bill-name': - $this->setBill($convertedValue, $certainty); + $this->setObject('bill', $convertedValue, $certainty); break; case 'budget-id': case 'budget-name': - $this->setObject('budget', 'budgetCertainty', $convertedValue, $certainty); - //$this->setBudget($convertedValue, $certainty); + $this->setObject('budget', $convertedValue, $certainty); break; case 'category-id': case 'category-name': - $this->setObject('category', 'categoryCertainty', $convertedValue, $certainty); + $this->setObject('category', $convertedValue, $certainty); + break; + case 'currency-code': + case 'currency-id': + case 'currency-name': + case 'currency-symbol': + $this->setObject('currency', $convertedValue, $certainty); + break; + case 'date-transaction': + $this->setDate('date-transaction', $convertedValue, $certainty); break; + case 'date-interest': + $this->setDate('date-interest', $convertedValue, $certainty); + break; + case 'date-book': + $this->setDate('date-book', $convertedValue, $certainty); + break; + case 'date-process': + $this->setDate('date-process', $convertedValue, $certainty); + break; + case'description': + $this->setAppendableString('description', $convertedValue); + break; + case '_ignore': + break; + case 'ing-debet-credit': + case 'rabo-debet-credit': + $this->manipulateFloat('amount', 'multiply', $convertedValue); + break; } } - /** - * @param float $amount - */ - private function setAmount(float $amount) - { - $this->amount = $amount; - } - - /** - * @param Account $account - * @param int $certainty - */ - private function setAssetAccount(Account $account, int $certainty) - { - if ($certainty > $this->assetAccountCertainty && !is_null($account->id)) { - Log::debug(sprintf('ImportEntry: Asset Account ID is now %d with certainty %d', $account->id, $certainty)); - $this->assetAccount = $account; - $this->assetAccountCertainty = $certainty; - - return; - } - Log::error(sprintf('Will not set asset account based on certainty %d (current certainty is %d) or NULL id.', $certainty, $this->assetAccountCertainty)); - } - - /** - * @param Bill $bill - * @param int $certainty - */ - private function setBill(Bill $bill, int $certainty) - { - if ($certainty > $this->billCertainty && !is_null($bill->id)) { - Log::debug(sprintf('ImportEntry: Bill-ID is now %d with certainty %d', $bill->id, $certainty)); - $this->bill = $bill; - $this->billCertainty = $certainty; - - return; - } - Log::error(sprintf('Will not set bill based on certainty %d (current certainty is %d) or NULL id.', $certainty, $this->billCertainty)); - } - - /** - * @param Budget $budget - * @param int $certainty - */ - private function setBudget(Budget $budget, int $certainty) - { - if ($certainty > $this->budgetCertainty && !is_null($budget->id)) { - Log::debug(sprintf('ImportEntry: Budget-ID is now %d with certainty %d', $budget->id, $certainty)); - $this->budget = $budget; - $this->budgetCertainty = $certainty; - - return; - } - Log::error(sprintf('Will not set budget based on certainty %d (current certainty is %d) or NULL id.', $certainty, $this->budgetCertainty)); - } - /** * @param string $field - * @param string $cert - * @param $object - * @param int $certainty + * @param string $action + * @param $convertedValue + * + * @throws FireflyException */ - private function setObject(string $field, string $cert, $object, int $certainty) + private function manipulateFloat(string $field, string $action, $convertedValue) { - if ($certainty > $this->$cert && !is_null($object->id)) { - Log::debug(sprintf('ImportEntry: %s ID is now %d with certainty %d', $field, $object->id, $certainty)); - $this->$field = $object; - $this->$cert = $certainty; + switch ($action) { + default: + Log::error('Cannot handle manipulateFloat', ['field' => $field, 'action' => $action]); + throw new FireflyException('Cannot manipulateFloat with action ' . $action); + case'multiply': + $this->fields[$field] = $this->fields[$field] * $convertedValue; + break; + } + } + + /** + * @param string $field + * @param string $value + */ + private function setAppendableString(string $field, string $value) + { + $value = trim($value); + $this->fields[$field] .= ' ' . $value; + } + + /** + * @param string $field + * @param Carbon $date + * @param int $certainty + */ + private function setDate(string $field, Carbon $date, int $certainty) + { + if ($certainty > $this->certain[$field] && !is_null($date)) { + Log::debug(sprintf('ImportEntry: %s is now %s with certainty %d', $field, $date->format('Y-m-d'), $certainty)); + $this->fields[$field] = $date; + $this->certain[$field] = $certainty; return; } - Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->$cert)); + Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field])); } /** - * @param Account $account - * @param int $certainty + * @param string $field + * @param float $value + * @param int $certainty */ - private function setOpposingAccount(Account $account, int $certainty) + private function setFloat(string $field, float $value, int $certainty) { - if ($certainty > $this->opposingAccountCertainty && !is_null($account->id)) { - Log::debug(sprintf('ImportEntry: Opposing Account ID is now %d with certainty %d', $account->id, $certainty)); - $this->assetAccount = $account; - $this->assetAccountCertainty = $certainty; + if ($certainty > $this->certain[$field]) { + Log::debug(sprintf('ImportEntry: %s is now %f with certainty %d', $field, $value, $certainty)); + $this->fields[$field] = $value; + $this->certain[$field] = $certainty; return; } - Log::error( - sprintf('Will not set opposing account based on certainty %d (current certainty is %d) or NULL id.', $certainty, $this->assetAccountCertainty) - ); + Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d).', $field, $certainty, $this->certain[$field])); } + /** + * @param string $field + * @param $object + * @param int $certainty + */ + private function setObject(string $field, $object, int $certainty) + { + if ($certainty > $this->certain[$field] && !is_null($object->id)) { + Log::debug(sprintf('ImportEntry: %s ID is now %d with certainty %d', $field, $object->id, $certainty)); + $this->fields[$field] = $object; + $this->certain[$field] = $certainty; + + return; + } + Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field])); + + } } \ No newline at end of file diff --git a/app/Support/FireflyConfig.php b/app/Support/FireflyConfig.php index 649f49a48f..1c515445d1 100644 --- a/app/Support/FireflyConfig.php +++ b/app/Support/FireflyConfig.php @@ -87,16 +87,26 @@ class FireflyConfig */ public function set($name, $value): Configuration { - // + Log::debug('Set new value for ', ['name' => $name]); + $config = Configuration::whereName($name)->first(); + if (is_null($config)) { + Log::debug('Does not exist yet ', ['name' => $name]); + $item = new Configuration; + $item->name = $name; + $item->data = $value; + $item->save(); - $item = new Configuration; - $item->name = $name; - $item->data = $value; - $item->save(); + Cache::forget('ff-config-' . $name); - Cache::forget('ff-config-' . $name); + return $item; + } else { + Log::debug('Exists already ', ['name' => $name]); + $config->data = $value; + $config->save(); + Cache::forget('ff-config-' . $name); - return $item; + return $config; + } } diff --git a/config/csv.php b/config/csv.php index f5bab29465..5b5ac530d5 100644 --- a/config/csv.php +++ b/config/csv.php @@ -113,11 +113,23 @@ return [ 'converter' => 'Date', 'field' => 'date', ], - 'date-rent' => [ + 'date-interest' => [ 'mappable' => false, 'pre-process-map' => false, 'converter' => 'Date', - 'field' => 'date-rent', + 'field' => 'date-interest', + ], + 'date-book' => [ + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'Date', + 'field' => 'date-book', + ], + 'date-process' => [ + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'Date', + 'field' => 'date-process', ], 'budget-id' => [ 'mappable' => true, diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index ae51cf1f68..dc9b75e148 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -59,7 +59,9 @@ return [ 'column_currency-id' => 'Currency ID (matching Firefly)', 'column_currency-name' => 'Currency name (matching Firefly)', 'column_currency-symbol' => 'Currency symbol (matching Firefly)', - 'column_date-rent' => 'Rent calculation date', + 'column_date-interest' => 'Interest calculation date', + 'column_date-book' => 'Transaction booking date', + 'column_date-process' => 'Transaction process date', 'column_date-transaction' => 'Date', 'column_description' => 'Description', 'column_opposing-iban' => 'Opposing account (IBAN)', From 7e3f9048fe78e057c2e191f90a560d9b08e2b594 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 1 Aug 2016 20:14:23 +0200 Subject: [PATCH 60/94] Fixed some fields in split thing. --- resources/views/split/journals/create.twig | 31 +++++++++++++++++++--- resources/views/split/journals/edit.twig | 5 +--- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/resources/views/split/journals/create.twig b/resources/views/split/journals/create.twig index cb995a1865..076d790df1 100644 --- a/resources/views/split/journals/create.twig +++ b/resources/views/split/journals/create.twig @@ -11,6 +11,17 @@ + + {% if errors.all()|length > 0 %}
    @@ -40,14 +51,17 @@ {{ ExpandedForm.select('journal_currency_id', currencies, journal.transaction_currency_id) }} {{ ExpandedForm.staticText('journal_amount', preFilled.journal_amount|formatAmount ) }} + {% if preFilled.what == 'withdrawal' or preFilled.what == 'transfer' %} {{ ExpandedForm.select('journal_source_account_id', assetAccounts, preFilled.journal_source_account_id) }} {% endif %} - + + {% if preFilled.what == 'deposit' %} - {{ ExpandedForm.text('journal_source_account_name', preFilled.journal_source_account_name) }} + {{ ExpandedForm.select('journal_destination_account_id', assetAccounts, preFilled.journal_destination_account_id) }} {% endif %} + {% if preFilled.what == 'transfer' %} {{ ExpandedForm.select('journal_destination_account_id', assetAccounts, preFilled.journal_destination_account_id) }} @@ -85,17 +99,26 @@ {{ trans('list.split_number') }} {{ trans('list.description') }} + + + {% if preFilled.what == 'withdrawal' %} {{ trans('list.destination') }} {% endif %} + {% if preFilled.what == 'deposit' %} {{ trans('list.source') }} {% endif %} + + {{ trans('list.amount') }} + + {% if preFilled.what == 'withdrawal' %} {{ trans('list.budget') }} {% endif %} {{ trans('list.category') }} + {% if preFilled.what == 'transfer' %} {{ trans('list.piggy_bank') }} {% endif %} @@ -117,10 +140,10 @@ {% endif %} - + {% if preFilled.what == 'deposit' %} - {{ Form.select('destination_account_id[]', assetAccounts, preFilled.destination_account_id[index], {class: 'form-control'}) }} + {% endif %} diff --git a/resources/views/split/journals/edit.twig b/resources/views/split/journals/edit.twig index e7fc9e7105..2ec26d99ea 100644 --- a/resources/views/split/journals/edit.twig +++ b/resources/views/split/journals/edit.twig @@ -51,10 +51,6 @@ {{ ExpandedForm.select('journal_destination_account_id', assetAccounts, preFilled.journal_destination_account_id) }} {% endif %} - - {% if preFilled.what == 'deposit' %} - {% endif %} - {% if preFilled.what == 'transfer' %} {{ ExpandedForm.select('journal_destination_account_id', assetAccounts, preFilled.journal_destination_account_id) }} @@ -97,6 +93,7 @@ {% if preFilled.what == 'withdrawal' %} {{ trans('list.destination') }} {% endif %} + {% if preFilled.what == 'deposit' %} {{ trans('list.source') }} {% endif %} From 0db9852769157a0d1a8b4e6e47025ad857d2cc80 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 2 Aug 2016 19:12:18 +0200 Subject: [PATCH 61/94] This should fix a bug in split expenses. --- app/Http/Controllers/TransactionController.php | 4 +++- app/Repositories/Account/AccountRepository.php | 14 +++++++++++--- app/Repositories/Journal/JournalRepository.php | 2 ++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 9243717407..054801ddc5 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -197,7 +197,7 @@ class TransactionController extends Controller */ public function index(Request $request, JournalRepositoryInterface $repository, string $what) { - $pageSize = Preferences::get('transactionPageSize', 50)->data; + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); $types = config('firefly.transactionTypesByWhat.' . $what); $subTitle = trans('firefly.title_' . $what); @@ -274,6 +274,8 @@ class TransactionController extends Controller // store the journal only, flash the rest. if ($doSplit) { $journal = $repository->storeJournal($journalData); + $journal->completed = false; + $journal->save(); // store attachments: $att->saveAttachmentsForModel($journal); diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index d072c706bc..7a2b650532 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -568,6 +568,7 @@ class AccountRepository implements AccountRepositoryInterface $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0); } ); + $query->leftJoin( 'transactions as destination', function (JoinClause $join) { $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0); @@ -575,15 +576,22 @@ class AccountRepository implements AccountRepositoryInterface ); $query->whereIn('source.account_id', $accountIds); $query->whereNotIn('destination.account_id', $accountIds); + $query->whereNull('source.deleted_at'); + $query->whereNull('destination.deleted_at'); + $query->distinct(); } // remove group by $query->getQuery()->getQuery()->groups = null; + $ids = $query->get(['transaction_journals.id'])->pluck('id')->toArray(); - // that should do it: - $sum = strval($query->sum('source.amount')); + $sum = $this->user->transactions() + ->whereIn('transaction_journal_id', $ids) + ->where('amount', '<', '0') + ->whereNull('transactions.deleted_at') + ->sum('amount'); - return $sum; + return strval($sum); } } diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index a42e0cffcd..0ea29d5deb 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -144,6 +144,7 @@ class JournalRepository implements JournalRepositoryInterface { $offset = ($page - 1) * $pageSize; $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); + $query->where('transaction_journals.completed', 1); if (count($types) > 0) { $query->transactionTypes($types); } @@ -166,6 +167,7 @@ class JournalRepository implements JournalRepositoryInterface public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection { $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); + $query->where('transaction_journals.completed', 1); $query->before($end); $query->after($start); From d870e0f42ebe5311260744f6c53c3bd2ec9b6833 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 2 Aug 2016 19:42:41 +0200 Subject: [PATCH 62/94] Fix split query. --- app/Repositories/Budget/BudgetRepository.php | 35 +++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index af1994ebce..8498fa037d 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -354,6 +354,9 @@ class BudgetRepository implements BudgetRepositoryInterface $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0); } ); + $query->whereNull('source.deleted_at'); + $query->whereNull('destination.deleted_at'); + $query->where('transaction_journals.completed', 1); if ($end >= $start) { $query->before($end)->after($start); @@ -362,16 +365,27 @@ class BudgetRepository implements BudgetRepositoryInterface $accountIds = $accounts->pluck('id')->toArray(); $set = join(', ', $accountIds); $query->whereRaw('(source.account_id in (' . $set . ') XOR destination.account_id in (' . $set . '))'); + } if ($budgets->count() > 0) { $budgetIds = $budgets->pluck('id')->toArray(); $query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); $query->whereIn('budget_transaction_journal.budget_id', $budgetIds); + } // that should do it: - $first = strval($query->sum('source.amount')); - + $ids = $query->distinct()->get(['transaction_journals.id'])->pluck('id')->toArray(); + $first = '0'; + if (count($ids) > 0) { + $first = strval( + $this->user->transactions() + ->whereIn('transaction_journal_id', $ids) + ->where('amount', '<', '0') + ->whereNull('transactions.deleted_at') + ->sum('amount') + ); + } // then collection transactions (harder) $query = $this->user->transactions() ->where('transactions.amount', '<', 0) @@ -419,7 +433,10 @@ class BudgetRepository implements BudgetRepositoryInterface ->whereNull('budget_transaction_journal.id') ->whereNull('budget_transaction.id') ->before($end) - ->after($start); + ->after($start) + ->whereNull('source.deleted_at') + ->whereNull('destination.deleted_at') + ->where('transaction_journals.completed', 1); if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); @@ -427,7 +444,17 @@ class BudgetRepository implements BudgetRepositoryInterface $set = join(', ', $accountIds); $query->whereRaw('(source.account_id in (' . $set . ') XOR destination.account_id in (' . $set . '))'); } - $sum = strval($query->sum('source.amount')); + $ids = $query->get(['transaction_journals.id'])->pluck('id')->toArray(); + $sum = '0'; + if (count($ids) > 0) { + $sum = strval( + $this->user->transactions() + ->whereIn('transaction_journal_id', $ids) + ->where('amount', '<', '0') + ->whereNull('transactions.deleted_at') + ->sum('amount') + ); + } return $sum; } From 1186e95c51772d0d7923baa444e70cea2ac0cac0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 3 Aug 2016 20:57:01 +0200 Subject: [PATCH 63/94] Admin view will show some IP addresses. Signed-off-by: James Cole --- app/Events/UserIsConfirmed.php | 40 ++++++++++++ app/Handlers/Events/UserSaveIpAddress.php | 62 +++++++++++++++++++ .../Auth/ConfirmationController.php | 10 ++- app/Providers/EventServiceProvider.php | 4 ++ config/twigbridge.php | 1 + resources/lang/en_US/list.php | 2 + resources/views/admin/users/index.twig | 8 +++ 7 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 app/Events/UserIsConfirmed.php create mode 100644 app/Handlers/Events/UserSaveIpAddress.php diff --git a/app/Events/UserIsConfirmed.php b/app/Events/UserIsConfirmed.php new file mode 100644 index 0000000000..8313d835a0 --- /dev/null +++ b/app/Events/UserIsConfirmed.php @@ -0,0 +1,40 @@ +user = $user; + $this->ipAddress = $ipAddress; + } +} diff --git a/app/Handlers/Events/UserSaveIpAddress.php b/app/Handlers/Events/UserSaveIpAddress.php new file mode 100644 index 0000000000..5b34ebc6da --- /dev/null +++ b/app/Handlers/Events/UserSaveIpAddress.php @@ -0,0 +1,62 @@ +user, 'registration_ip_address', $event->ipAddress); + + return true; + } + + /** + * Handle the event. + * + * @param UserIsConfirmed $event + * + * @return bool + */ + public function saveFromConfirmation(UserIsConfirmed $event): bool + { + Preferences::setForUser($event->user, 'confirmation_ip_address', $event->ipAddress); + + return true; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/ConfirmationController.php b/app/Http/Controllers/Auth/ConfirmationController.php index ef0a045fd3..ea45b5efb8 100644 --- a/app/Http/Controllers/Auth/ConfirmationController.php +++ b/app/Http/Controllers/Auth/ConfirmationController.php @@ -13,6 +13,7 @@ namespace FireflyIII\Http\Controllers\Auth; use Auth; use FireflyIII\Events\ResendConfirmation; +use FireflyIII\Events\UserIsConfirmed; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use Illuminate\Http\Request; @@ -36,12 +37,13 @@ class ConfirmationController extends Controller } /** - * @param string $code + * @param Request $request + * @param string $code * * @return mixed * @throws FireflyException */ - public function doConfirmation(string $code) + public function doConfirmation(Request $request, string $code) { // check user_confirmed_last_mail @@ -51,6 +53,10 @@ class ConfirmationController extends Controller $maxDiff = config('firefly.confirmation_age'); if ($database === $code && ($now - $time <= $maxDiff)) { + + // trigger user registration event: + event(new UserIsConfirmed(Auth::user(), $request->ip())); + Preferences::setForUser(Auth::user(), 'user_confirmed', true); Preferences::setForUser(Auth::user(), 'user_confirmed_confirmed', time()); Session::flash('success', strval(trans('firefly.account_is_confirmed'))); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index dc6e71565e..2758e7f0b6 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -62,6 +62,10 @@ class EventServiceProvider extends ServiceProvider 'FireflyIII\Handlers\Events\SendRegistrationMail', 'FireflyIII\Handlers\Events\AttachUserRole', 'FireflyIII\Handlers\Events\UserConfirmation@sendConfirmation', + 'FireflyIII\Handlers\Events\UserSaveIpAddress@saveFromRegistration', + ], + 'FireflyIII\Events\UserIsConfirmed' => [ + 'FireflyIII\Handlers\Events\UserSaveIpAddress@saveFromConfirmation', ], 'FireflyIII\Events\ResendConfirmation' => [ 'FireflyIII\Handlers\Events\UserConfirmation@resendConfirmation', diff --git a/config/twigbridge.php b/config/twigbridge.php index 2064bbf643..586f7f047e 100644 --- a/config/twigbridge.php +++ b/config/twigbridge.php @@ -142,6 +142,7 @@ return [ 'Route', 'Auth', 'Lang', + 'Preferences', 'URL', 'Config', 'ExpandedForm' => [ diff --git a/resources/lang/en_US/list.php b/resources/lang/en_US/list.php index 743b67adfa..193740c717 100644 --- a/resources/lang/en_US/list.php +++ b/resources/lang/en_US/list.php @@ -58,6 +58,8 @@ return [ 'is_blocked' => 'Is blocked', 'is_admin' => 'Is admin', 'has_two_factor' => 'Has 2FA', + 'confirmed_from' => 'Confirmed from', + 'registered_from' => 'Registered from', 'blocked_code' => 'Block code', 'domain' => 'Domain', 'registration_attempts' => 'Registration attempts', diff --git a/resources/views/admin/users/index.twig b/resources/views/admin/users/index.twig index 05bd5375f7..15cf943bcb 100644 --- a/resources/views/admin/users/index.twig +++ b/resources/views/admin/users/index.twig @@ -17,6 +17,8 @@   {{ trans('list.email') }} {{ trans('list.registered_at') }} + {{ trans('list.registered_from') }} + {{ trans('list.confirmed_from') }} {{ trans('list.is_admin') }} {{ trans('list.has_two_factor') }} {{ trans('list.is_activated') }} @@ -34,6 +36,12 @@ {{ user.created_at.formatLocalized(monthAndDayFormat) }} {{ user.created_at.format('H:i') }} + + {{ Preferences.getForUser(user,"registration_ip_address").data }} + + + {{ Preferences.getForUser(user,"confirmation_ip_address").data }} + {% if user.isAdmin %} From 38800d61b0f1202ed81dde04fa25e5c5dc43203e Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 4 Aug 2016 06:07:53 +0200 Subject: [PATCH 64/94] New user related code. --- app/Events/UserIsDeleted.php | 40 ++++++ .../Controllers/Admin/DomainController.php | 136 ++++++++++++++++++ app/Http/Controllers/Admin/UserController.php | 104 +------------- app/Http/Controllers/ProfileController.php | 5 + app/Http/routes.php | 8 +- resources/views/admin/users/index.twig | 6 +- 6 files changed, 194 insertions(+), 105 deletions(-) create mode 100644 app/Events/UserIsDeleted.php create mode 100644 app/Http/Controllers/Admin/DomainController.php diff --git a/app/Events/UserIsDeleted.php b/app/Events/UserIsDeleted.php new file mode 100644 index 0000000000..34459decec --- /dev/null +++ b/app/Events/UserIsDeleted.php @@ -0,0 +1,40 @@ +user = $user; + $this->ipAddress = $ipAddress; + } +} diff --git a/app/Http/Controllers/Admin/DomainController.php b/app/Http/Controllers/Admin/DomainController.php new file mode 100644 index 0000000000..163e7e90b8 --- /dev/null +++ b/app/Http/Controllers/Admin/DomainController.php @@ -0,0 +1,136 @@ +data; + + // known domains + $knownDomains = $this->getKnownDomains(); + + return view('admin.users.domains', compact('title', 'mainTitleIcon', 'knownDomains', 'subTitle', 'subTitleIcon', 'domains')); + } + + + /** + * @param Request $request + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function manual(Request $request) + { + if (strlen($request->get('domain')) === 0) { + Session::flash('error', trans('firefly.no_domain_filled_in')); + + return redirect(route('admin.users.domains')); + } + + $domain = $request->get('domain'); + $blocked = FireflyConfig::get('blocked-domains', [])->data; + + if (in_array($domain, $blocked)) { + Session::flash('error', trans('firefly.domain_already_blocked', ['domain' => $domain])); + + return redirect(route('admin.users.domains')); + } + $blocked[] = $domain; + FireflyConfig::set('blocked-domains', $blocked); + + Session::flash('success', trans('firefly.domain_is_now_blocked', ['domain' => $domain])); + + return redirect(route('admin.users.domains')); + } + + /** + * @param string $domain + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function toggleDomain(string $domain) + { + $blocked = FireflyConfig::get('blocked-domains', [])->data; + + if (in_array($domain, $blocked)) { + $key = array_search($domain, $blocked); + unset($blocked[$key]); + sort($blocked); + + FireflyConfig::set('blocked-domains', $blocked); + Session::flash('message', trans('firefly.domain_now_unblocked', ['domain' => $domain])); + + + return redirect(route('admin.users.domains')); + + } + + $blocked[] = $domain; + + FireflyConfig::set('blocked-domains', $blocked); + Session::flash('message', trans('firefly.domain_now_blocked', ['domain' => $domain])); + + return redirect(route('admin.users.domains')); + } + + /** + * @return array + */ + private function getKnownDomains(): array + { + $users = User::get(); + $set = []; + $filtered = []; + /** @var User $user */ + foreach ($users as $user) { + $email = $user->email; + $parts = explode('@', $email); + $domain = $parts[1]; + $set[] = $domain; + } + $set = array_unique($set); + // filter for already banned domains: + $blocked = FireflyConfig::get('blocked-domains', [])->data; + + foreach ($set as $domain) { + // in the block array? ignore it. + if (!in_array($domain, $blocked)) { + $filtered[] = $domain; + } + } + asort($filtered); + + return $filtered; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 168e674f2b..08d4663e06 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -14,11 +14,8 @@ namespace FireflyIII\Http\Controllers\Admin; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\User\UserRepositoryInterface; -use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\User; -use Illuminate\Http\Request; use Preferences; -use Session; /** * Class UserController @@ -28,22 +25,14 @@ use Session; class UserController extends Controller { + /** - * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @param User $user */ - public function domains() + public function edit(User $user) { - $title = strval(trans('firefly.administration')); - $mainTitleIcon = 'fa-hand-spock-o'; - $subTitle = strval(trans('firefly.blocked_domains')); - $subTitleIcon = 'fa-users'; - $domains = FireflyConfig::get('blocked-domains', [])->data; - // known domains - $knownDomains = $this->getKnownDomains(); - - return view('admin.users.domains', compact('title', 'mainTitleIcon', 'knownDomains', 'subTitle', 'subTitleIcon', 'domains')); } /** @@ -85,92 +74,5 @@ class UserController extends Controller } - /** - * @param Request $request - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector - */ - public function manual(Request $request) - { - if (strlen($request->get('domain')) === 0) { - Session::flash('error', trans('firefly.no_domain_filled_in')); - - return redirect(route('admin.users.domains')); - } - - $domain = $request->get('domain'); - $blocked = FireflyConfig::get('blocked-domains', [])->data; - - if (in_array($domain, $blocked)) { - Session::flash('error', trans('firefly.domain_already_blocked', ['domain' => $domain])); - - return redirect(route('admin.users.domains')); - } - $blocked[] = $domain; - FireflyConfig::set('blocked-domains', $blocked); - - Session::flash('success', trans('firefly.domain_is_now_blocked', ['domain' => $domain])); - return redirect(route('admin.users.domains')); - } - - /** - * @param string $domain - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector - */ - public function toggleDomain(string $domain) - { - $blocked = FireflyConfig::get('blocked-domains', [])->data; - - if (in_array($domain, $blocked)) { - $key = array_search($domain, $blocked); - unset($blocked[$key]); - sort($blocked); - - FireflyConfig::set('blocked-domains', $blocked); - Session::flash('message', trans('firefly.domain_now_unblocked', ['domain' => $domain])); - - - return redirect(route('admin.users.domains')); - - } - - $blocked[] = $domain; - - FireflyConfig::set('blocked-domains', $blocked); - Session::flash('message', trans('firefly.domain_now_blocked', ['domain' => $domain])); - - return redirect(route('admin.users.domains')); - } - - /** - * @return array - */ - private function getKnownDomains(): array - { - $users = User::get(); - $set = []; - $filtered = []; - /** @var User $user */ - foreach ($users as $user) { - $email = $user->email; - $parts = explode('@', $email); - $domain = $parts[1]; - $set[] = $domain; - } - $set = array_unique($set); - // filter for already banned domains: - $blocked = FireflyConfig::get('blocked-domains', [])->data; - - foreach ($set as $domain) { - // in the block array? ignore it. - if (!in_array($domain, $blocked)) { - $filtered[] = $domain; - } - } - asort($filtered); - - return $filtered; - } } diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 95cf5fad7d..1fac4e2d2d 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use Auth; +use FireflyIII\Events\UserIsDeleted; use FireflyIII\Http\Requests\DeleteAccountFormRequest; use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\User; @@ -106,6 +107,10 @@ class ProfileController extends Controller return redirect(route('profile.delete-account')); } + // respond to deletion: + event(new UserIsDeleted(Auth::user(), $request->ip())); + + // DELETE! $email = Auth::user()->email; Auth::user()->delete(); diff --git a/app/Http/routes.php b/app/Http/routes.php index 4d1f4b7617..955af396a3 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -424,10 +424,12 @@ Route::group( // user manager Route::get('/admin/users', ['uses' => 'Admin\UserController@index', 'as' => 'admin.users']); - Route::get('/admin/users/domains', ['uses' => 'Admin\UserController@domains', 'as' => 'admin.users.domains']); - Route::get('/admin/users/domains/toggle/{domain}', ['uses' => 'Admin\UserController@toggleDomain', 'as' => 'admin.users.domains.block-toggle']); + Route::get('/admin/users/edit/{user}', ['uses' => 'Admin\UserController@edit', 'as' => 'admin.users.edit']); - Route::post('/admin/users/domains/manual', ['uses' => 'Admin\UserController@manual', 'as' => 'admin.users.domains.manual']); + // user domains: + Route::get('/admin/domains', ['uses' => 'Admin\DomainController@domains', 'as' => 'admin.users.domains']); + Route::get('/admin/domains/toggle/{domain}', ['uses' => 'Admin\DomainController@toggleDomain', 'as' => 'admin.users.domains.block-toggle']); + Route::post('/admin/domains/manual', ['uses' => 'Admin\DomainController@manual', 'as' => 'admin.users.domains.manual']); } ); diff --git a/resources/views/admin/users/index.twig b/resources/views/admin/users/index.twig index 15cf943bcb..9d17bee41b 100644 --- a/resources/views/admin/users/index.twig +++ b/resources/views/admin/users/index.twig @@ -29,7 +29,11 @@ {% for user in users %} - ~ + +
    + +
    + #{{ user.id }} {{ user.email }} From 5f5469a7d43db44b4da34767f27902cd8e3ac267 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 4 Aug 2016 06:10:30 +0200 Subject: [PATCH 65/94] Moved some files around. --- app/Http/Controllers/Admin/DomainController.php | 2 +- app/Http/Controllers/ProfileController.php | 2 ++ .../views/admin/{users/domains.twig => domains/index.twig} | 0 3 files changed, 3 insertions(+), 1 deletion(-) rename resources/views/admin/{users/domains.twig => domains/index.twig} (100%) diff --git a/app/Http/Controllers/Admin/DomainController.php b/app/Http/Controllers/Admin/DomainController.php index 163e7e90b8..ce52f103ff 100644 --- a/app/Http/Controllers/Admin/DomainController.php +++ b/app/Http/Controllers/Admin/DomainController.php @@ -41,7 +41,7 @@ class DomainController extends Controller // known domains $knownDomains = $this->getKnownDomains(); - return view('admin.users.domains', compact('title', 'mainTitleIcon', 'knownDomains', 'subTitle', 'subTitleIcon', 'domains')); + return view('admin.domains.index', compact('title', 'mainTitleIcon', 'knownDomains', 'subTitle', 'subTitleIcon', 'domains')); } diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 1fac4e2d2d..b5ebd9e3ee 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -110,6 +110,8 @@ class ProfileController extends Controller // respond to deletion: event(new UserIsDeleted(Auth::user(), $request->ip())); + // store some stuff for the future: + // DELETE! $email = Auth::user()->email; diff --git a/resources/views/admin/users/domains.twig b/resources/views/admin/domains/index.twig similarity index 100% rename from resources/views/admin/users/domains.twig rename to resources/views/admin/domains/index.twig From 09d63b584dfe85c1c73a7e2adf52445816d8b07e Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 4 Aug 2016 06:14:08 +0200 Subject: [PATCH 66/94] Save old data; bread crumbs --- app/Http/Controllers/ProfileController.php | 13 +++++++++++-- app/Http/breadcrumbs.php | 7 +++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index b5ebd9e3ee..1febd0a0ab 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -17,6 +17,7 @@ use FireflyIII\Http\Requests\DeleteAccountFormRequest; use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\User; use Hash; +use Preferences; use Session; /** @@ -111,7 +112,8 @@ class ProfileController extends Controller event(new UserIsDeleted(Auth::user(), $request->ip())); // store some stuff for the future: - + $registration = Preferences::get('registration_ip_address')->data; + $confirmation = Preferences::get('confirmation_ip_address')->data; // DELETE! $email = Auth::user()->email; @@ -121,7 +123,7 @@ class ProfileController extends Controller Session::flash('gaEventAction', 'delete-account'); // create a new user with the same email address so re-registration is blocked. - User::create( + $newUser = User::create( [ 'email' => $email, 'password' => 'deleted', @@ -129,6 +131,13 @@ class ProfileController extends Controller 'blocked_code' => 'deleted', ] ); + if (strlen($registration) > 0) { + Preferences::setForUser($newUser, 'registration_ip_address', $registration); + + } + if (strlen($confirmation) > 0) { + Preferences::setForUser($newUser, 'confirmation_ip_address', $confirmation); + } return redirect(route('index')); } diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index c1dcda94cd..e977635219 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -113,6 +113,13 @@ Breadcrumbs::register( } ); +Breadcrumbs::register( + 'admin.users.domains', function (BreadCrumbGenerator $breadcrumbs) { + $breadcrumbs->parent('admin.index'); + $breadcrumbs->push(trans('firefly.blocked_domains'), route('admin.users.domains')); +} +); + /** * ATTACHMENTS */ From bcfe2c6410f5d122949cab7717d0f02236e6d8bb Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 5 Aug 2016 18:52:16 +0200 Subject: [PATCH 67/94] Fixes #284 Signed-off-by: James Cole --- app/Support/Steam.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Support/Steam.php b/app/Support/Steam.php index d532a43cce..9dda968cb1 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -220,7 +220,7 @@ class Steam return intval($bytes); } - return $string; + return intval($string); } From 47376f1f354cb7dcbe55b0e501b74b58b19775db Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 5 Aug 2016 19:29:44 +0200 Subject: [PATCH 68/94] Fix a problem mentioned in issue #283 Signed-off-by: James Cole --- app/Http/Controllers/Auth/AuthController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 366a7e5258..11ec64cfbf 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -178,7 +178,7 @@ class AuthController extends Controller */ protected function getBlockedDomains() { - return FireflyConfig::get('blocked-domains')->data; + return FireflyConfig::get('blocked-domains', [])->data; } /** From f065f3a0b867080ee6854a7af7d79acd2b4d0d97 Mon Sep 17 00:00:00 2001 From: Niek van der Kooy Date: Fri, 5 Aug 2016 20:53:09 +0200 Subject: [PATCH 69/94] Make phpBytes case insensitive, since php.ini can contain both capitals and small letters --- app/Support/Steam.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 9dda968cb1..3b2a140dc4 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -206,16 +206,16 @@ class Steam { $string = strtolower($string); - if (!(strpos($string, 'k') === false)) { + if (!(stripos($string, 'k') === false)) { // has a K in it, remove the K and multiply by 1024. - $bytes = bcmul(rtrim($string, 'k'), '1024'); + $bytes = bcmul(rtrim($string, 'kK'), '1024'); return intval($bytes); } - if (!(strpos($string, 'm') === false)) { + if (!(stripos($string, 'm') === false)) { // has a M in it, remove the M and multiply by 1048576. - $bytes = bcmul(rtrim($string, 'm'), '1048576'); + $bytes = bcmul(rtrim($string, 'mM'), '1048576'); return intval($bytes); } From 51570a5d08ea484f0d873817755054c50897bb74 Mon Sep 17 00:00:00 2001 From: Niek van der Kooy Date: Fri, 5 Aug 2016 20:54:59 +0200 Subject: [PATCH 70/94] Allow phpBytes to work with configs where gigabyte file sizes are allowed --- app/Support/Steam.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 3b2a140dc4..5158d2ab03 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -220,6 +220,13 @@ class Steam return intval($bytes); } + if (!(stripos($string, 'g') === false)) { + // has a G in it, remove the G and multiply by (1024)^3. + $bytes = bcmul(rtrim($string, 'gG'), '1073741824'); + + return intval($bytes); + } + return intval($string); From 70356009849dc2105ce6fec54b687683c8cefe28 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 5 Aug 2016 21:50:49 +0200 Subject: [PATCH 71/94] Add some logging for #283 --- app/Models/Preference.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Models/Preference.php b/app/Models/Preference.php index b19d4458d7..15ddef16e6 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -15,7 +15,7 @@ use Crypt; use FireflyIII\Exceptions\FireflyException; use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Database\Eloquent\Model; - +use Log; /** * FireflyIII\Models\Preference * @@ -54,6 +54,7 @@ class Preference extends Model try { $data = Crypt::decrypt($value); } catch (DecryptException $e) { + Log::error('Could not decrypt preference.', ['id' => $this->id,'name' => $this->name,'data' => $this->data]); throw new FireflyException('Could not decrypt preference #' . $this->id . '.'); } From 5826fec519aa33496718a55cbb518a26855de3f5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 6 Aug 2016 06:21:25 +0200 Subject: [PATCH 72/94] Some new import stuff. --- app/Console/Commands/Import.php | 3 +- app/Http/Controllers/ImportController.php | 17 +- app/Import/ImportEntry.php | 161 ++++++- app/Import/ImportResult.php | 168 +++++++ app/Import/Importer/CsvImporter.php | 482 -------------------- app/Import/Importer/ImporterInterface.php | 74 --- app/Import/Setup/CsvSetup.php | 528 ++++++++++++++++++++++ app/Import/Setup/SetupInterface.php | 92 ++++ 8 files changed, 956 insertions(+), 569 deletions(-) create mode 100644 app/Import/ImportResult.php create mode 100644 app/Import/Setup/CsvSetup.php create mode 100644 app/Import/Setup/SetupInterface.php diff --git a/app/Console/Commands/Import.php b/app/Console/Commands/Import.php index 8a139083df..33be3c8e06 100644 --- a/app/Console/Commands/Import.php +++ b/app/Console/Commands/Import.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace FireflyIII\Console\Commands; use FireflyIII\Import\Importer\ImporterInterface; +use FireflyIII\Import\Setup\SetupInterface; use FireflyIII\Import\Logging\CommandHandler; use FireflyIII\Models\ImportJob; use Illuminate\Console\Command; @@ -70,6 +71,7 @@ class Import extends Command $this->line('Going to import job with key "' . $job->key . '" of type ' . $job->file_type); $class = config('firefly.import_formats.' . $job->file_type); + /** @var ImporterInterface $importer */ $importer = app($class); $importer->setJob($job); @@ -79,7 +81,6 @@ class Import extends Command $monolog->pushHandler($handler); $importer->start(); - $this->line('Something something import: ' . $jobKey); } } diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 40a636deaf..03a39c2f49 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -1,4 +1,11 @@ file_type; - /** @var ImporterInterface $importer */ - $importer = app('FireflyIII\Import\Importer\\' . ucfirst($type) . 'Importer'); + /** @var SetupInterface $importer */ + $importer = app('FireflyIII\Import\Setup\\' . ucfirst($type) . 'Setup'); $importer->setJob($job); return $importer; diff --git a/app/Import/ImportEntry.php b/app/Import/ImportEntry.php index bdae2d8398..3e75f643c0 100644 --- a/app/Import/ImportEntry.php +++ b/app/Import/ImportEntry.php @@ -13,6 +13,13 @@ namespace FireflyIII\Import; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Account; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\User; +use Illuminate\Support\Collection; use Log; /** @@ -27,6 +34,12 @@ class ImportEntry /** @var array */ public $fields = []; + /** @var Account */ + public $defaultImportAccount; + + /** @var User */ + public $user; + /** @var array */ private $validFields = ['amount', @@ -35,14 +48,14 @@ class ImportEntry 'date-book', 'description', 'date-process', - 'currency', 'asset-account', 'opposing-account', 'bill', 'budget', 'category']; + 'currency', 'asset-account', 'opposing-account', 'bill', 'budget', 'category', 'tags']; /** * ImportEntry constructor. */ public function __construct() { - + $this->defaultImportAccount = new Account; /** @var string $value */ foreach ($this->validFields as $value) { $this->fields[$value] = null; @@ -50,6 +63,21 @@ class ImportEntry } } + /** + * @return ImportResult + */ + public function import(): ImportResult + { + + $validation = $this->validate(); + + if ($validation->valid()) { + return $this->doImport(); + } + + return $validation; + } + /** * @param string $role * @param string $value @@ -81,10 +109,9 @@ class ImportEntry $this->setObject('asset-account', $convertedValue, $certainty); break; case 'opposing-number': - case 'opposing-iban': - case 'opposing-id': - case 'opposing-number': - case 'opposing-name': + case 'opposing-iban': + case 'opposing-id': + case 'opposing-name': $this->setObject('opposing-account', $convertedValue, $certainty); break; case 'bill-id': @@ -124,12 +151,102 @@ class ImportEntry case '_ignore': break; case 'ing-debet-credit': - case 'rabo-debet-credit': + case 'rabo-debet-credit': $this->manipulateFloat('amount', 'multiply', $convertedValue); break; + case 'tags-comma': + case 'tags-space': + $this->appendCollection('tags', $convertedValue); + } } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + + /** + * @param string $field + * @param Collection $convertedValue + */ + private function appendCollection(string $field, Collection $convertedValue) + { + if (is_null($this->fields[$field])) { + $this->fields[$field] = new Collection; + } + $this->fields[$field] = $this->fields[$field]->merge($convertedValue); + } + + + /** + * @return ImportResult + */ + private function doImport(): ImportResult + { + $result = new ImportResult; + + // here we go! + $journal = new TransactionJournal; + $journal->user()->associate($this->user); + $journal->transactionType()->associate($this->getTransactionType()); + $journal->transactionCurrency()->associate($this->getTransactionCurrency()); + $journal->description = $this->fields['description'] ?? '(empty transaction description)'; + $journal->date = $this->fields['date-transaction'] ?? new Carbon; + $journal->interest_date = $this->fields['date-interest']; + $journal->process_date = $this->fields['date-process']; + $journal->book_date = $this->fields['date-book']; + $journal->completed = 0; + + + + + } + + /** + * @return TransactionCurrency + */ + private function getTransactionCurrency(): TransactionCurrency + { + if (!is_null($this->fields['currency'])) { + return $this->fields['currency']; + } + /** @var CurrencyRepositoryInterface $repository */ + $repository = app(CurrencyRepositoryInterface::class); + + return $repository->findByCode(env('DEFAULT_CURRENCY', 'EUR')); + } + + /** + * @return TransactionType + */ + private function getTransactionType(): TransactionType + { + + + /* + * source: import/asset/expense/revenue/null + * destination: import/asset/expense/revenue/null + * + * */ + + // source and opposing are asset = transfer + // source = asset and dest = import and amount = neg = withdrawal + // source = asset and dest = expense and amount = neg = withdrawal + // source = asset and dest = revenue and amount = pos = deposit + // source = asset and dest = import and amount = pos = deposit + + // source = import + + // source = expense + // + + // source = revenue + } + /** * @param string $field * @param string $action @@ -212,4 +329,34 @@ class ImportEntry } + /** + * Validate the content of the import entry so far. We only need a few things. + * + * @return ImportResult + */ + private function validate(): ImportResult + { + $result = new ImportResult; + $result->validated(); + if ($this->fields['amount'] == 0) { + // false, amount must be above or below zero. + $result->failed(); + $result->appendError('No valid amount found.'); + } + if (is_null($this->fields['date-transaction'])) { + $result->appendWarning('No valid date found.'); + } + if (is_null($this->fields['description']) || (!is_null($this->fields['description']) && strlen($this->fields['description']) == 0)) { + $result->appendWarning('No valid description found.'); + } + if (is_null($this->fields['asset-account'])) { + $result->appendWarning('No valid asset account found. Will use default account.'); + } + if (is_null($this->fields['opposing-account'])) { + $result->appendWarning('No valid asset opposing found. Will use default.'); + } + + return $result; + } + } \ No newline at end of file diff --git a/app/Import/ImportResult.php b/app/Import/ImportResult.php new file mode 100644 index 0000000000..e7be183e85 --- /dev/null +++ b/app/Import/ImportResult.php @@ -0,0 +1,168 @@ +errors = new Collection; + $this->warnings = new Collection; + $this->messages = new Collection; + } + + /** + * @param string $error + * + * @return $this + */ + public function appendError(string $error) + { + $this->errors->push($error); + + return $this; + } + + /** + * @param string $message + * + * @return $this + */ + public function appendMessage(string $message) + { + $this->messages->push($message); + + return $this; + } + + /** + * @param string $title + * + * @return $this + */ + public function appendTitle(string $title) + { + $this->title .= $title; + + return $this; + } + + /** + * @param string $warning + * + * @return $this + */ + public function appendWarning(string $warning) + { + $this->warnings->push($warning); + + return $this; + } + + /** + * @return $this + */ + public function failed() + { + $this->status = self::IMPORT_FAILED; + + return $this; + } + + /** + * @param Collection $errors + */ + public function setErrors(Collection $errors) + { + $this->errors = $errors; + } + + /** + * @param Collection $messages + */ + public function setMessages(Collection $messages) + { + $this->messages = $messages; + } + + /** + * @param string $title + * + * @return $this + */ + public function setTitle(string $title) + { + $this->title = $title; + + return $this; + } + + /** + * @param Collection $warnings + */ + public function setWarnings(Collection $warnings) + { + $this->warnings = $warnings; + } + + /** + * @return $this + */ + public function success() + { + $this->status = self::IMPORT_SUCCESS; + + return $this; + } + + /** + * @return bool + */ + public function valid(): bool + { + return $this->status === self::IMPORT_VALID; + } + + /** + * + */ + public function validated() + { + $this->status = self::IMPORT_VALID; + } + + +} \ No newline at end of file diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 731129bdc5..5722cab569 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -11,20 +11,6 @@ declare(strict_types = 1); namespace FireflyIII\Import\Importer; - -use ExpandedForm; -use FireflyIII\Crud\Account\AccountCrud; -use FireflyIII\Import\Converter\ConverterInterface; -use FireflyIII\Import\ImportEntry; -use FireflyIII\Import\Mapper\MapperInterface; -use FireflyIII\Import\MapperPreProcess\PreProcessorInterface; -use FireflyIII\Models\AccountType; -use FireflyIII\Models\ImportJob; -use Illuminate\Http\Request; -use League\Csv\Reader; -use Log; -use Symfony\Component\HttpFoundation\FileBag; - /** * Class CsvImporter * @@ -32,473 +18,5 @@ use Symfony\Component\HttpFoundation\FileBag; */ class CsvImporter implements ImporterInterface { - const EXAMPLE_ROWS = 5; - /** @var ImportJob */ - public $job; - /** - * Create initial (empty) configuration array. - * - * - * - * @return bool - */ - public function configure(): bool - { - if (is_null($this->job->configuration) || (is_array($this->job->configuration) && count($this->job->configuration) === 0)) { - Log::debug('No config detected, will create empty one.'); - - $config = [ - 'has-headers' => false, // assume - 'date-format' => 'Ymd', // assume - 'delimiter' => ',', // assume - 'import-account' => 0, // none, - 'specifics' => [], // none - 'column-count' => 0, // unknown - 'column-roles' => [], // unknown - 'column-do-mapping' => [], // not yet set which columns must be mapped - 'column-roles-complete' => false, // not yet configured roles for columns - 'column-mapping-config' => [], // no mapping made yet. - 'column-mapping-complete' => false, // so mapping is not complete. - ]; - $this->job->configuration = $config; - $this->job->save(); - - return true; - } - - // need to do nothing, for now. - Log::debug('Detected config in upload, will use that one. ', $this->job->configuration); - - return true; - } - - /** - * @return array - */ - public function getConfigurationData(): array - { - $crud = app('FireflyIII\Crud\Account\AccountCrudInterface'); - $accounts = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); - $delimiters = [ - ',' => trans('form.csv_comma'), - ';' => trans('form.csv_semicolon'), - 'tab' => trans('form.csv_tab'), - ]; - - $specifics = []; - - // collect specifics. - foreach (config('csv.import_specifics') as $name => $className) { - $specifics[$name] = [ - 'name' => $className::getName(), - 'description' => $className::getDescription(), - ]; - } - - $data = [ - 'accounts' => ExpandedForm::makeSelectList($accounts), - 'specifix' => [], - 'delimiters' => $delimiters, - 'upload_path' => storage_path('upload'), - 'is_upload_possible' => is_writable(storage_path('upload')), - 'specifics' => $specifics, - ]; - - return $data; - } - - /** - * This method returns the data required for the view that will let the user add settings to the import job. - * - * @return array - */ - public function getDataForSettings(): array - { - - if ($this->doColumnRoles()) { - $data = $this->getDataForColumnRoles(); - - return $data; - } - - if ($this->doColumnMapping()) { - $data = $this->getDataForColumnMapping(); - - return $data; - } - - echo 'no settings to do.'; - exit; - - } - - /** - * This method returns the name of the view that will be shown to the user to further configure - * the import job. - * - * @return string - */ - public function getViewForSettings(): string - { - if ($this->doColumnRoles()) { - return 'import.csv.roles'; - } - - if ($this->doColumnMapping()) { - return 'import.csv.map'; - } - - echo 'no view for settings'; - exit; - } - - /** - * This method returns whether or not the user must configure this import - * job further. - * - * @return bool - */ - public function requireUserSettings(): bool - { - Log::debug('doColumnMapping is ' . ($this->doColumnMapping() ? 'true' : 'false')); - Log::debug('doColumnRoles is ' . ($this->doColumnRoles() ? 'true' : 'false')); - if ($this->doColumnMapping() || $this->doColumnRoles()) { - Log::debug('Return true'); - - return true; - } - Log::debug('Return false'); - - return false; - } - - /** - * @param array $data - * - * @return bool - */ - public function saveImportConfiguration(array $data, FileBag $files): bool - { - /** @var AccountCrud $repository */ - $repository = app(AccountCrud::class, [auth()->user()]); - $account = $repository->find(intval($data['csv_import_account'])); - - $hasHeaders = isset($data['has_headers']) && intval($data['has_headers']) === 1 ? true : false; - $config = $this->job->configuration; - $config['has-headers'] = $hasHeaders; - $config['date-format'] = $data['date_format']; - $config['delimiter'] = $data['csv_delimiter']; - - Log::debug('Entered import account.', ['id' => $data['csv_import_account']]); - - - if (!is_null($account->id)) { - Log::debug('Found account.', ['id' => $account->id, 'name' => $account->name]); - $config['import-account'] = $account->id; - } else { - Log::error('Could not find anything for csv_import_account.', ['id' => $data['csv_import_account']]); - } - // loop specifics. - if (isset($data['specifics']) && is_array($data['specifics'])) { - foreach ($data['specifics'] as $name => $enabled) { - $config['specifics'][$name] = 1; - } - } - $this->job->configuration = $config; - $this->job->save(); - - return true; - - - } - - /** - * @param ImportJob $job - */ - public function setJob(ImportJob $job) - { - $this->job = $job; - } - - /** - * Run the actual import - * - * @return bool - */ - public function start(): bool - { - $config = $this->job->configuration; - $content = $this->job->uploadFileContents(); - - // create CSV reader. - $reader = Reader::createFromString($content); - $start = $config['has-headers'] ? 1 : 0; - $results = $reader->fetch(); - foreach ($results as $index => $row) { - if ($index >= $start) { - Log::debug(sprintf('Now going to import row %d.', $index)); - $this->importSingleRow($row); - } - } - - Log::debug('This call should be intercepted somehow.'); - - return true; - } - - /** - * Store the settings filled in by the user, if applicable. - * - * @param Request $request - * - */ - public function storeSettings(Request $request) - { - $config = $this->job->configuration; - $all = $request->all(); - if ($request->get('settings') == 'roles') { - $count = $config['column-count']; - - $roleSet = 0; // how many roles have been defined - $mapSet = 0; // how many columns must be mapped - for ($i = 0; $i < $count; $i++) { - $selectedRole = $all['role'][$i] ?? '_ignore'; - $doMapping = isset($all['map'][$i]) && $all['map'][$i] == '1' ? true : false; - if ($selectedRole == '_ignore' && $doMapping === true) { - $doMapping = false; // cannot map ignored columns. - } - if ($selectedRole != '_ignore') { - $roleSet++; - } - if ($doMapping === true) { - $mapSet++; - } - $config['column-roles'][$i] = $selectedRole; - $config['column-do-mapping'][$i] = $doMapping; - } - if ($roleSet > 0) { - $config['column-roles-complete'] = true; - $this->job->configuration = $config; - $this->job->save(); - } - if ($mapSet === 0) { - // skip setting of map: - $config['column-mapping-complete'] = true; - } - } - if ($request->get('settings') == 'map') { - if (isset($all['mapping'])) { - foreach ($all['mapping'] as $index => $data) { - $config['column-mapping-config'][$index] = []; - foreach ($data as $value => $mapId) { - $mapId = intval($mapId); - if ($mapId !== 0) { - $config['column-mapping-config'][$index][$value] = intval($mapId); - } - } - } - } - - // set thing to be completed. - $config['column-mapping-complete'] = true; - $this->job->configuration = $config; - $this->job->save(); - } - } - - /** - * @return bool - */ - private function doColumnMapping(): bool - { - $mapArray = $this->job->configuration['column-do-mapping'] ?? []; - $doMap = false; - foreach ($mapArray as $value) { - if ($value === true) { - $doMap = true; - break; - } - } - - return $this->job->configuration['column-mapping-complete'] === false && $doMap; - } - - /** - * @return bool - */ - private function doColumnRoles(): bool - { - return $this->job->configuration['column-roles-complete'] === false; - } - - /** - * @return array - */ - private function getDataForColumnMapping(): array - { - $config = $this->job->configuration; - $data = []; - $indexes = []; - - foreach ($config['column-do-mapping'] as $index => $mustBeMapped) { - if ($mustBeMapped) { - $column = $config['column-roles'][$index] ?? '_ignore'; - $canBeMapped = config('csv.import_roles.' . $column . '.mappable'); - $preProcessMap = config('csv.import_roles.' . $column . '.pre-process-map'); - if ($canBeMapped) { - $mapperName = '\FireflyIII\Import\Mapper\\' . config('csv.import_roles.' . $column . '.mapper'); - /** @var MapperInterface $mapper */ - $mapper = new $mapperName; - $indexes[] = $index; - $data[$index] = [ - 'name' => $column, - 'mapper' => $mapperName, - 'index' => $index, - 'options' => $mapper->getMap(), - 'preProcessMap' => null, - 'values' => [], - ]; - if ($preProcessMap) { - $data[$index]['preProcessMap'] = '\FireflyIII\Import\MapperPreProcess\\' . - config('csv.import_roles.' . $column . '.pre-process-mapper'); - } - } - - } - } - - // in order to actually map we also need all possible values from the CSV file. - $content = $this->job->uploadFileContents(); - $reader = Reader::createFromString($content); - $results = $reader->fetch(); - - foreach ($results as $rowIndex => $row) { - //do something here - foreach ($indexes as $index) { // this is simply 1, 2, 3, etc. - $value = $row[$index]; - if (strlen($value) > 0) { - - // we can do some preprocessing here, - // which is exclusively to fix the tags: - if (!is_null($data[$index]['preProcessMap'])) { - /** @var PreProcessorInterface $preProcessor */ - $preProcessor = app($data[$index]['preProcessMap']); - $result = $preProcessor->run($value); - $data[$index]['values'] = array_merge($data[$index]['values'], $result); - - Log::debug($rowIndex . ':' . $index . 'Value before preprocessor', ['value' => $value]); - Log::debug($rowIndex . ':' . $index . 'Value after preprocessor', ['value-new' => $result]); - Log::debug($rowIndex . ':' . $index . 'Value after joining', ['value-complete' => $data[$index]['values']]); - - - continue; - } - - $data[$index]['values'][] = $value; - } - } - } - foreach ($data as $index => $entry) { - $data[$index]['values'] = array_unique($data[$index]['values']); - } - - return $data; - } - - /** - * @return array - */ - private function getDataForColumnRoles():array - { - $config = $this->job->configuration; - $data = [ - 'columns' => [], - 'columnCount' => 0, - ]; - - // show user column role configuration. - $content = $this->job->uploadFileContents(); - - // create CSV reader. - $reader = Reader::createFromString($content); - $start = $config['has-headers'] ? 1 : 0; - $end = $start + self::EXAMPLE_ROWS; // first X rows - - // collect example data in $data['columns'] - while ($start < $end) { - $row = $reader->fetchOne($start); - foreach ($row as $index => $value) { - $value = trim($value); - if (strlen($value) > 0) { - $data['columns'][$index][] = $value; - } - } - $start++; - $data['columnCount'] = count($row); - } - - // make unique example data - foreach ($data['columns'] as $index => $values) { - $data['columns'][$index] = array_unique($values); - } - - $data['set_roles'] = []; - // collect possible column roles: - $data['available_roles'] = []; - foreach (array_keys(config('csv.import_roles')) as $role) { - $data['available_roles'][$role] = trans('csv.column_' . $role); - } - - $config['column-count'] = $data['columnCount']; - $this->job->configuration = $config; - $this->job->save(); - - return $data; - - - } - - /** - * @param array $row - * - * @return bool - */ - private function importSingleRow(array $row): bool - { - $object = new ImportEntry; - $config = $this->job->configuration; - - foreach ($row as $index => $value) { - // find the role for this column: - $role = $config['column-roles'][$index] ?? '_ignore'; - $doMap = $config['column-do-mapping'][$index] ?? false; - $converterClass = config('csv.import_roles.' . $role . '.converter'); - $mapping = $config['column-mapping-config'][$index] ?? []; - /** @var ConverterInterface $converter */ - $converter = app('FireflyIII\\Import\\Converter\\' . $converterClass); - // set some useful values for the converter: - $converter->setMapping($mapping); - $converter->setDoMap($doMap); - $converter->setUser($this->job->user); - $converter->setConfig($config); - - // run the converter for this value: - $convertedValue = $converter->convert($value); - $certainty = $converter->getCertainty(); - - // log it. - Log::debug('Value ', ['index' => $index, 'value' => $value, 'role' => $role]); - - // store in import entry: - $object->importValue($role, $value, $certainty, $convertedValue); - // $object->fromRawValue($role, $value); - - - } - - exit; - - return true; - } } \ No newline at end of file diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php index 66b72a6f08..f5a4883a9d 100644 --- a/app/Import/Importer/ImporterInterface.php +++ b/app/Import/Importer/ImporterInterface.php @@ -11,82 +11,8 @@ declare(strict_types = 1); namespace FireflyIII\Import\Importer; -use FireflyIII\Import\Role\Map; -use FireflyIII\Models\ImportJob; -use Illuminate\Http\Request; -use Symfony\Component\HttpFoundation\FileBag; -/** - * Interface ImporterInterface - * - * @package FireflyIII\Import\Importer - */ interface ImporterInterface { - /** - * Run the actual import - * - * @return bool - */ - public function start(): bool; - - /** - * After uploading, and after setJob(), prepare anything that is - * necessary for the configure() line. - * - * @return bool - */ - public function configure(): bool; - - /** - * Returns any data necessary to do the configuration. - * - * @return array - */ - public function getConfigurationData(): array; - - /** - * This method returns the data required for the view that will let the user add settings to the import job. - * - * @return array - */ - public function getDataForSettings(): array; - - /** - * Store the settings filled in by the user, if applicable. - * - * @param Request $request - * - */ - public function storeSettings(Request $request); - - /** - * This method returns the name of the view that will be shown to the user to further configure - * the import job. - * - * @return string - */ - public function getViewForSettings(): string; - - /** - * This method returns whether or not the user must configure this import - * job further. - * - * @return bool - */ - public function requireUserSettings(): bool; - - /** - * @param array $data - * - * @return bool - */ - public function saveImportConfiguration(array $data, FileBag $files): bool; - - /** - * @param ImportJob $job - * - */ - public function setJob(ImportJob $job); } \ No newline at end of file diff --git a/app/Import/Setup/CsvSetup.php b/app/Import/Setup/CsvSetup.php new file mode 100644 index 0000000000..4df6f69f6f --- /dev/null +++ b/app/Import/Setup/CsvSetup.php @@ -0,0 +1,528 @@ +defaultImportAccount = new Account; + } + + /** + * Create initial (empty) configuration array. + * + * + * + * @return bool + */ + public function configure(): bool + { + if (is_null($this->job->configuration) || (is_array($this->job->configuration) && count($this->job->configuration) === 0)) { + Log::debug('No config detected, will create empty one.'); + + $config = [ + 'has-headers' => false, // assume + 'date-format' => 'Ymd', // assume + 'delimiter' => ',', // assume + 'import-account' => 0, // none, + 'specifics' => [], // none + 'column-count' => 0, // unknown + 'column-roles' => [], // unknown + 'column-do-mapping' => [], // not yet set which columns must be mapped + 'column-roles-complete' => false, // not yet configured roles for columns + 'column-mapping-config' => [], // no mapping made yet. + 'column-mapping-complete' => false, // so mapping is not complete. + ]; + $this->job->configuration = $config; + $this->job->save(); + + return true; + } + + // need to do nothing, for now. + Log::debug('Detected config in upload, will use that one. ', $this->job->configuration); + + return true; + } + + /** + * @return array + */ + public function getConfigurationData(): array + { + $crud = app('FireflyIII\Crud\Account\AccountCrudInterface'); + $accounts = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + $delimiters = [ + ',' => trans('form.csv_comma'), + ';' => trans('form.csv_semicolon'), + 'tab' => trans('form.csv_tab'), + ]; + + $specifics = []; + + // collect specifics. + foreach (config('csv.import_specifics') as $name => $className) { + $specifics[$name] = [ + 'name' => $className::getName(), + 'description' => $className::getDescription(), + ]; + } + + $data = [ + 'accounts' => ExpandedForm::makeSelectList($accounts), + 'specifix' => [], + 'delimiters' => $delimiters, + 'upload_path' => storage_path('upload'), + 'is_upload_possible' => is_writable(storage_path('upload')), + 'specifics' => $specifics, + ]; + + return $data; + } + + /** + * This method returns the data required for the view that will let the user add settings to the import job. + * + * @return array + */ + public function getDataForSettings(): array + { + + if ($this->doColumnRoles()) { + $data = $this->getDataForColumnRoles(); + + return $data; + } + + if ($this->doColumnMapping()) { + $data = $this->getDataForColumnMapping(); + + return $data; + } + + echo 'no settings to do.'; + exit; + + } + + /** + * This method returns the name of the view that will be shown to the user to further configure + * the import job. + * + * @return string + */ + public function getViewForSettings(): string + { + if ($this->doColumnRoles()) { + return 'import.csv.roles'; + } + + if ($this->doColumnMapping()) { + return 'import.csv.map'; + } + + echo 'no view for settings'; + exit; + } + + /** + * This method returns whether or not the user must configure this import + * job further. + * + * @return bool + */ + public function requireUserSettings(): bool + { + Log::debug('doColumnMapping is ' . ($this->doColumnMapping() ? 'true' : 'false')); + Log::debug('doColumnRoles is ' . ($this->doColumnRoles() ? 'true' : 'false')); + if ($this->doColumnMapping() || $this->doColumnRoles()) { + Log::debug('Return true'); + + return true; + } + Log::debug('Return false'); + + return false; + } + + /** + * @param array $data + * + * @return bool + */ + public function saveImportConfiguration(array $data, FileBag $files): bool + { + /** @var AccountCrud $repository */ + $repository = app(AccountCrud::class, [auth()->user()]); + $account = $repository->find(intval($data['csv_import_account'])); + + $hasHeaders = isset($data['has_headers']) && intval($data['has_headers']) === 1 ? true : false; + $config = $this->job->configuration; + $config['has-headers'] = $hasHeaders; + $config['date-format'] = $data['date_format']; + $config['delimiter'] = $data['csv_delimiter']; + + Log::debug('Entered import account.', ['id' => $data['csv_import_account']]); + + + if (!is_null($account->id)) { + Log::debug('Found account.', ['id' => $account->id, 'name' => $account->name]); + $config['import-account'] = $account->id; + } else { + Log::error('Could not find anything for csv_import_account.', ['id' => $data['csv_import_account']]); + } + // loop specifics. + if (isset($data['specifics']) && is_array($data['specifics'])) { + foreach ($data['specifics'] as $name => $enabled) { + $config['specifics'][$name] = 1; + } + } + $this->job->configuration = $config; + $this->job->save(); + + return true; + + + } + + /** + * @param ImportJob $job + */ + public function setJob(ImportJob $job) + { + $this->job = $job; + } + + /** + * Run the actual import + * + * @return bool + */ + public function start(): bool + { + $config = $this->job->configuration; + $content = $this->job->uploadFileContents(); + + if ($config['import-account'] != 0) { + $repository = app(AccountCrud::class, [auth()->user()]); + $this->defaultImportAccount = $repository->find($config['csv_import_account']); + } + + + // create CSV reader. + $reader = Reader::createFromString($content); + $start = $config['has-headers'] ? 1 : 0; + $results = $reader->fetch(); + foreach ($results as $index => $row) { + if ($index >= $start) { + Log::debug(sprintf('Now going to import row %d.', $index)); + $this->importSingleRow($row); + } + } + + Log::debug('This call should be intercepted somehow.'); + + return true; + } + + /** + * Store the settings filled in by the user, if applicable. + * + * @param Request $request + * + */ + public function storeSettings(Request $request) + { + $config = $this->job->configuration; + $all = $request->all(); + if ($request->get('settings') == 'roles') { + $count = $config['column-count']; + + $roleSet = 0; // how many roles have been defined + $mapSet = 0; // how many columns must be mapped + for ($i = 0; $i < $count; $i++) { + $selectedRole = $all['role'][$i] ?? '_ignore'; + $doMapping = isset($all['map'][$i]) && $all['map'][$i] == '1' ? true : false; + if ($selectedRole == '_ignore' && $doMapping === true) { + $doMapping = false; // cannot map ignored columns. + } + if ($selectedRole != '_ignore') { + $roleSet++; + } + if ($doMapping === true) { + $mapSet++; + } + $config['column-roles'][$i] = $selectedRole; + $config['column-do-mapping'][$i] = $doMapping; + } + if ($roleSet > 0) { + $config['column-roles-complete'] = true; + $this->job->configuration = $config; + $this->job->save(); + } + if ($mapSet === 0) { + // skip setting of map: + $config['column-mapping-complete'] = true; + } + } + if ($request->get('settings') == 'map') { + if (isset($all['mapping'])) { + foreach ($all['mapping'] as $index => $data) { + $config['column-mapping-config'][$index] = []; + foreach ($data as $value => $mapId) { + $mapId = intval($mapId); + if ($mapId !== 0) { + $config['column-mapping-config'][$index][$value] = intval($mapId); + } + } + } + } + + // set thing to be completed. + $config['column-mapping-complete'] = true; + $this->job->configuration = $config; + $this->job->save(); + } + } + + /** + * @return bool + */ + private function doColumnMapping(): bool + { + $mapArray = $this->job->configuration['column-do-mapping'] ?? []; + $doMap = false; + foreach ($mapArray as $value) { + if ($value === true) { + $doMap = true; + break; + } + } + + return $this->job->configuration['column-mapping-complete'] === false && $doMap; + } + + /** + * @return bool + */ + private function doColumnRoles(): bool + { + return $this->job->configuration['column-roles-complete'] === false; + } + + /** + * @return array + */ + private function getDataForColumnMapping(): array + { + $config = $this->job->configuration; + $data = []; + $indexes = []; + + foreach ($config['column-do-mapping'] as $index => $mustBeMapped) { + if ($mustBeMapped) { + $column = $config['column-roles'][$index] ?? '_ignore'; + $canBeMapped = config('csv.import_roles.' . $column . '.mappable'); + $preProcessMap = config('csv.import_roles.' . $column . '.pre-process-map'); + if ($canBeMapped) { + $mapperName = '\FireflyIII\Import\Mapper\\' . config('csv.import_roles.' . $column . '.mapper'); + /** @var MapperInterface $mapper */ + $mapper = new $mapperName; + $indexes[] = $index; + $data[$index] = [ + 'name' => $column, + 'mapper' => $mapperName, + 'index' => $index, + 'options' => $mapper->getMap(), + 'preProcessMap' => null, + 'values' => [], + ]; + if ($preProcessMap) { + $data[$index]['preProcessMap'] = '\FireflyIII\Import\MapperPreProcess\\' . + config('csv.import_roles.' . $column . '.pre-process-mapper'); + } + } + + } + } + + // in order to actually map we also need all possible values from the CSV file. + $content = $this->job->uploadFileContents(); + $reader = Reader::createFromString($content); + $results = $reader->fetch(); + + foreach ($results as $rowIndex => $row) { + //do something here + foreach ($indexes as $index) { // this is simply 1, 2, 3, etc. + $value = $row[$index]; + if (strlen($value) > 0) { + + // we can do some preprocessing here, + // which is exclusively to fix the tags: + if (!is_null($data[$index]['preProcessMap'])) { + /** @var PreProcessorInterface $preProcessor */ + $preProcessor = app($data[$index]['preProcessMap']); + $result = $preProcessor->run($value); + $data[$index]['values'] = array_merge($data[$index]['values'], $result); + + Log::debug($rowIndex . ':' . $index . 'Value before preprocessor', ['value' => $value]); + Log::debug($rowIndex . ':' . $index . 'Value after preprocessor', ['value-new' => $result]); + Log::debug($rowIndex . ':' . $index . 'Value after joining', ['value-complete' => $data[$index]['values']]); + + + continue; + } + + $data[$index]['values'][] = $value; + } + } + } + foreach ($data as $index => $entry) { + $data[$index]['values'] = array_unique($data[$index]['values']); + } + + return $data; + } + + /** + * @return array + */ + private function getDataForColumnRoles():array + { + $config = $this->job->configuration; + $data = [ + 'columns' => [], + 'columnCount' => 0, + ]; + + // show user column role configuration. + $content = $this->job->uploadFileContents(); + + // create CSV reader. + $reader = Reader::createFromString($content); + $start = $config['has-headers'] ? 1 : 0; + $end = $start + self::EXAMPLE_ROWS; // first X rows + + // collect example data in $data['columns'] + while ($start < $end) { + $row = $reader->fetchOne($start); + foreach ($row as $index => $value) { + $value = trim($value); + if (strlen($value) > 0) { + $data['columns'][$index][] = $value; + } + } + $start++; + $data['columnCount'] = count($row); + } + + // make unique example data + foreach ($data['columns'] as $index => $values) { + $data['columns'][$index] = array_unique($values); + } + + $data['set_roles'] = []; + // collect possible column roles: + $data['available_roles'] = []; + foreach (array_keys(config('csv.import_roles')) as $role) { + $data['available_roles'][$role] = trans('csv.column_' . $role); + } + + $config['column-count'] = $data['columnCount']; + $this->job->configuration = $config; + $this->job->save(); + + return $data; + + + } + + /** + * @param array $row + * + * @return ImportResult + */ + private function importSingleRow(array $row): ImportResult + { + $object = new ImportEntry; + $object->setUser($this->job->user); + $config = $this->job->configuration; + $result = new ImportResult; + + foreach ($row as $index => $value) { + // find the role for this column: + $role = $config['column-roles'][$index] ?? '_ignore'; + $doMap = $config['column-do-mapping'][$index] ?? false; + $converterClass = config('csv.import_roles.' . $role . '.converter'); + $mapping = $config['column-mapping-config'][$index] ?? []; + /** @var ConverterInterface $converter */ + $converter = app('FireflyIII\\Import\\Converter\\' . $converterClass); + // set some useful values for the converter: + $converter->setMapping($mapping); + $converter->setDoMap($doMap); + $converter->setUser($this->job->user); + $converter->setConfig($config); + + // run the converter for this value: + $convertedValue = $converter->convert($value); + $certainty = $converter->getCertainty(); + + + // log it. + Log::debug('Value ', ['index' => $index, 'value' => $value, 'role' => $role]); + + // store in import entry: + $object->importValue($role, $value, $certainty, $convertedValue); + // $object->fromRawValue($role, $value); + } + + $result = $object->import(); + if ($result->failed()) { + Log::error('Import of row has failed.', $result->errors->toArray()); + } + + exit; + + return true; + } +} \ No newline at end of file diff --git a/app/Import/Setup/SetupInterface.php b/app/Import/Setup/SetupInterface.php new file mode 100644 index 0000000000..66b72a6f08 --- /dev/null +++ b/app/Import/Setup/SetupInterface.php @@ -0,0 +1,92 @@ + Date: Sat, 6 Aug 2016 06:21:46 +0200 Subject: [PATCH 73/94] Configuration for import routine. --- app/Import/Setup/CsvSetup.php | 8 +++---- app/Import/Setup/SetupInterface.php | 10 ++++---- .../Account/AccountRepository.php | 11 +++++++-- config/csv.php | 23 ++++++++++++------- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/app/Import/Setup/CsvSetup.php b/app/Import/Setup/CsvSetup.php index 4df6f69f6f..967b9deeee 100644 --- a/app/Import/Setup/CsvSetup.php +++ b/app/Import/Setup/CsvSetup.php @@ -1,6 +1,6 @@ getQuery()->getQuery()->groups = null; + $ids = $query->get(['transaction_journals.id'])->pluck('id')->toArray(); + + // that should do it: - $sum = strval($query->sum('destination.amount')); + $sum = $this->user->transactions() + ->whereIn('transaction_journal_id', $ids) + ->where('amount', '>', '0') + ->whereNull('transactions.deleted_at') + ->sum('amount'); - return $sum; + return strval($sum); } diff --git a/config/csv.php b/config/csv.php index 5b5ac530d5..d4561c8a1a 100644 --- a/config/csv.php +++ b/config/csv.php @@ -50,7 +50,7 @@ return [ * */ 'import_roles' => [ - '_ignore' => [ + '_ignore' => [ 'mappable' => false, 'pre-process-map' => false, 'field' => 'ignored', @@ -59,41 +59,48 @@ return [ ], - 'bill-id' => [ + 'bill-id' => [ 'mappable' => true, 'pre-process-map' => false, 'field' => 'bill', 'converter' => 'BillId', 'mapper' => 'Bills', ], - 'bill-name' => [ + 'bill-name' => [ 'mappable' => true, 'pre-process-map' => false, 'field' => 'bill', 'converter' => 'BillName', 'mapper' => 'Bills', ], - 'currency-id' => [ + 'currency-id' => [ 'mappable' => true, 'pre-process-map' => false, 'field' => 'currency', 'converter' => 'CurrencyId', 'mapper' => 'TransactionCurrencies', ], - 'currency-name' => [ + 'currency-name' => [ 'mappable' => true, 'pre-process-map' => false, 'converter' => 'CurrencyName', 'field' => 'currency', 'mapper' => 'TransactionCurrencies', ], - 'currency-code' => [ + 'currency-code' => [ 'mappable' => true, 'pre-process-map' => false, 'converter' => 'CurrencyCode', 'field' => 'currency', 'mapper' => 'TransactionCurrencies', ], + 'external-id' => [ + 'mappable' => false, + 'pre-process-map' => false, + 'converter' => 'ExternalId', + 'field' => 'external-id', + ], + 'currency-symbol' => [ 'mappable' => true, 'pre-process-map' => false, @@ -113,7 +120,7 @@ return [ 'converter' => 'Date', 'field' => 'date', ], - 'date-interest' => [ + 'date-interest' => [ 'mappable' => false, 'pre-process-map' => false, 'converter' => 'Date', @@ -125,7 +132,7 @@ return [ 'converter' => 'Date', 'field' => 'date-book', ], - 'date-process' => [ + 'date-process' => [ 'mappable' => false, 'pre-process-map' => false, 'converter' => 'Date', From d4510440b83e2e23447578e35a63261474948e1f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 6 Aug 2016 06:28:21 +0200 Subject: [PATCH 74/94] Split the importer and the setup routine. --- app/Import/Importer/CsvImporter.php | 103 ++++++++++++++++++++++ app/Import/Importer/ImporterInterface.php | 18 +++- app/Import/Setup/CsvSetup.php | 84 ------------------ app/Import/Setup/SetupInterface.php | 7 -- 4 files changed, 120 insertions(+), 92 deletions(-) diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 5722cab569..85ee7359dc 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -11,6 +11,15 @@ declare(strict_types = 1); namespace FireflyIII\Import\Importer; +use FireflyIII\Crud\Account\AccountCrud; +use FireflyIII\Import\Converter\ConverterInterface; +use FireflyIII\Import\ImportEntry; +use FireflyIII\Import\ImportResult; +use FireflyIII\Models\Account; +use FireflyIII\Models\ImportJob; +use League\Csv\Reader; +use Log; + /** * Class CsvImporter * @@ -18,5 +27,99 @@ namespace FireflyIII\Import\Importer; */ class CsvImporter implements ImporterInterface { + /** @var ImportJob */ + public $job; + /** @var Account */ + public $defaultImportAccount; + + /** + * @param ImportJob $job + */ + public function setJob(ImportJob $job) + { + $this->job = $job; + } + + /** + * Run the actual import + * + * @return bool + */ + public function start(): bool + { + $config = $this->job->configuration; + $content = $this->job->uploadFileContents(); + + if ($config['import-account'] != 0) { + $repository = app(AccountCrud::class, [$this->job->user]); + $this->defaultImportAccount = $repository->find($config['import-account']); + } + + + // create CSV reader. + $reader = Reader::createFromString($content); + $start = $config['has-headers'] ? 1 : 0; + $results = $reader->fetch(); + foreach ($results as $index => $row) { + if ($index >= $start) { + Log::debug(sprintf('Now going to import row %d.', $index)); + $this->importSingleRow($row); + } + } + + Log::debug('This call should be intercepted somehow.'); + + return true; + } + + + /** + * @param array $row + * + * @return ImportResult + */ + private function importSingleRow(array $row): ImportResult + { + $object = new ImportEntry; + $object->setUser($this->job->user); + $config = $this->job->configuration; + $result = new ImportResult; + + foreach ($row as $index => $value) { + // find the role for this column: + $role = $config['column-roles'][$index] ?? '_ignore'; + $doMap = $config['column-do-mapping'][$index] ?? false; + $converterClass = config('csv.import_roles.' . $role . '.converter'); + $mapping = $config['column-mapping-config'][$index] ?? []; + /** @var ConverterInterface $converter */ + $converter = app('FireflyIII\\Import\\Converter\\' . $converterClass); + // set some useful values for the converter: + $converter->setMapping($mapping); + $converter->setDoMap($doMap); + $converter->setUser($this->job->user); + $converter->setConfig($config); + + // run the converter for this value: + $convertedValue = $converter->convert($value); + $certainty = $converter->getCertainty(); + + + // log it. + Log::debug('Value ', ['index' => $index, 'value' => $value, 'role' => $role]); + + // store in import entry: + $object->importValue($role, $value, $certainty, $convertedValue); + // $object->fromRawValue($role, $value); + } + + $result = $object->import(); + if ($result->failed()) { + Log::error('Import of row has failed.', $result->errors->toArray()); + } + + exit; + + return true; + } } \ No newline at end of file diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php index f5a4883a9d..308d495040 100644 --- a/app/Import/Importer/ImporterInterface.php +++ b/app/Import/Importer/ImporterInterface.php @@ -10,9 +10,25 @@ declare(strict_types = 1); namespace FireflyIII\Import\Importer; +use FireflyIII\Models\ImportJob; - +/** + * Interface ImporterInterface + * + * @package FireflyIII\Import\Importer + */ interface ImporterInterface { + /** + * Run the actual import + * + * @return bool + */ + public function start(): bool; + /** + * @param ImportJob $job + * + */ + public function setJob(ImportJob $job); } \ No newline at end of file diff --git a/app/Import/Setup/CsvSetup.php b/app/Import/Setup/CsvSetup.php index 967b9deeee..53d445037f 100644 --- a/app/Import/Setup/CsvSetup.php +++ b/app/Import/Setup/CsvSetup.php @@ -14,9 +14,6 @@ namespace FireflyIII\Import\Setup; use ExpandedForm; use FireflyIII\Crud\Account\AccountCrud; -use FireflyIII\Import\Converter\ConverterInterface; -use FireflyIII\Import\ImportEntry; -use FireflyIII\Import\ImportResult; use FireflyIII\Import\Mapper\MapperInterface; use FireflyIII\Import\MapperPreProcess\PreProcessorInterface; use FireflyIII\Models\Account; @@ -233,38 +230,6 @@ class CsvSetup implements SetupInterface $this->job = $job; } - /** - * Run the actual import - * - * @return bool - */ - public function start(): bool - { - $config = $this->job->configuration; - $content = $this->job->uploadFileContents(); - - if ($config['import-account'] != 0) { - $repository = app(AccountCrud::class, [auth()->user()]); - $this->defaultImportAccount = $repository->find($config['csv_import_account']); - } - - - // create CSV reader. - $reader = Reader::createFromString($content); - $start = $config['has-headers'] ? 1 : 0; - $results = $reader->fetch(); - foreach ($results as $index => $row) { - if ($index >= $start) { - Log::debug(sprintf('Now going to import row %d.', $index)); - $this->importSingleRow($row); - } - } - - Log::debug('This call should be intercepted somehow.'); - - return true; - } - /** * Store the settings filled in by the user, if applicable. * @@ -476,53 +441,4 @@ class CsvSetup implements SetupInterface } - - /** - * @param array $row - * - * @return ImportResult - */ - private function importSingleRow(array $row): ImportResult - { - $object = new ImportEntry; - $object->setUser($this->job->user); - $config = $this->job->configuration; - $result = new ImportResult; - - foreach ($row as $index => $value) { - // find the role for this column: - $role = $config['column-roles'][$index] ?? '_ignore'; - $doMap = $config['column-do-mapping'][$index] ?? false; - $converterClass = config('csv.import_roles.' . $role . '.converter'); - $mapping = $config['column-mapping-config'][$index] ?? []; - /** @var ConverterInterface $converter */ - $converter = app('FireflyIII\\Import\\Converter\\' . $converterClass); - // set some useful values for the converter: - $converter->setMapping($mapping); - $converter->setDoMap($doMap); - $converter->setUser($this->job->user); - $converter->setConfig($config); - - // run the converter for this value: - $convertedValue = $converter->convert($value); - $certainty = $converter->getCertainty(); - - - // log it. - Log::debug('Value ', ['index' => $index, 'value' => $value, 'role' => $role]); - - // store in import entry: - $object->importValue($role, $value, $certainty, $convertedValue); - // $object->fromRawValue($role, $value); - } - - $result = $object->import(); - if ($result->failed()) { - Log::error('Import of row has failed.', $result->errors->toArray()); - } - - exit; - - return true; - } } \ No newline at end of file diff --git a/app/Import/Setup/SetupInterface.php b/app/Import/Setup/SetupInterface.php index 497c8e4769..9055c5f98e 100644 --- a/app/Import/Setup/SetupInterface.php +++ b/app/Import/Setup/SetupInterface.php @@ -24,13 +24,6 @@ use Symfony\Component\HttpFoundation\FileBag; interface SetupInterface { - /** - * Run the actual import - * - * @return bool - */ - public function start(): bool; - /** * After uploading, and after setJob(), prepare anything that is * necessary for the configure() line. From c9bd72337d00062328078291a358d86a37e32dc6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 6 Aug 2016 09:31:32 +0200 Subject: [PATCH 75/94] Some notes on the import process. --- app/Import/ImportEntry.php | 102 +--------------------------- app/Import/Importer/CsvImporter.php | 14 ++-- app/Import/notes.txt | 46 +++++++++++++ 3 files changed, 54 insertions(+), 108 deletions(-) create mode 100644 app/Import/notes.txt diff --git a/app/Import/ImportEntry.php b/app/Import/ImportEntry.php index 3e75f643c0..05b3d76a10 100644 --- a/app/Import/ImportEntry.php +++ b/app/Import/ImportEntry.php @@ -34,9 +34,6 @@ class ImportEntry /** @var array */ public $fields = []; - /** @var Account */ - public $defaultImportAccount; - /** @var User */ public $user; @@ -181,72 +178,6 @@ class ImportEntry $this->fields[$field] = $this->fields[$field]->merge($convertedValue); } - - /** - * @return ImportResult - */ - private function doImport(): ImportResult - { - $result = new ImportResult; - - // here we go! - $journal = new TransactionJournal; - $journal->user()->associate($this->user); - $journal->transactionType()->associate($this->getTransactionType()); - $journal->transactionCurrency()->associate($this->getTransactionCurrency()); - $journal->description = $this->fields['description'] ?? '(empty transaction description)'; - $journal->date = $this->fields['date-transaction'] ?? new Carbon; - $journal->interest_date = $this->fields['date-interest']; - $journal->process_date = $this->fields['date-process']; - $journal->book_date = $this->fields['date-book']; - $journal->completed = 0; - - - - - } - - /** - * @return TransactionCurrency - */ - private function getTransactionCurrency(): TransactionCurrency - { - if (!is_null($this->fields['currency'])) { - return $this->fields['currency']; - } - /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class); - - return $repository->findByCode(env('DEFAULT_CURRENCY', 'EUR')); - } - - /** - * @return TransactionType - */ - private function getTransactionType(): TransactionType - { - - - /* - * source: import/asset/expense/revenue/null - * destination: import/asset/expense/revenue/null - * - * */ - - // source and opposing are asset = transfer - // source = asset and dest = import and amount = neg = withdrawal - // source = asset and dest = expense and amount = neg = withdrawal - // source = asset and dest = revenue and amount = pos = deposit - // source = asset and dest = import and amount = pos = deposit - - // source = import - - // source = expense - // - - // source = revenue - } - /** * @param string $field * @param string $action @@ -328,35 +259,4 @@ class ImportEntry Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field])); } - - /** - * Validate the content of the import entry so far. We only need a few things. - * - * @return ImportResult - */ - private function validate(): ImportResult - { - $result = new ImportResult; - $result->validated(); - if ($this->fields['amount'] == 0) { - // false, amount must be above or below zero. - $result->failed(); - $result->appendError('No valid amount found.'); - } - if (is_null($this->fields['date-transaction'])) { - $result->appendWarning('No valid date found.'); - } - if (is_null($this->fields['description']) || (!is_null($this->fields['description']) && strlen($this->fields['description']) == 0)) { - $result->appendWarning('No valid description found.'); - } - if (is_null($this->fields['asset-account'])) { - $result->appendWarning('No valid asset account found. Will use default account.'); - } - if (is_null($this->fields['opposing-account'])) { - $result->appendWarning('No valid asset opposing found. Will use default.'); - } - - return $result; - } - -} \ No newline at end of file +} diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 85ee7359dc..e989d603a6 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -56,7 +56,6 @@ class CsvImporter implements ImporterInterface $this->defaultImportAccount = $repository->find($config['import-account']); } - // create CSV reader. $reader = Reader::createFromString($content); $start = $config['has-headers'] ? 1 : 0; @@ -64,7 +63,7 @@ class CsvImporter implements ImporterInterface foreach ($results as $index => $row) { if ($index >= $start) { Log::debug(sprintf('Now going to import row %d.', $index)); - $this->importSingleRow($row); + $this->importSingleRow($index, $row); } } @@ -75,16 +74,19 @@ class CsvImporter implements ImporterInterface /** + * @param int $index * @param array $row * * @return ImportResult */ - private function importSingleRow(array $row): ImportResult + private function importSingleRow(int $index, array $row): ImportResult { + // create import object: $object = new ImportEntry; + + // set some vars: $object->setUser($this->job->user); $config = $this->job->configuration; - $result = new ImportResult; foreach ($row as $index => $value) { // find the role for this column: @@ -104,18 +106,16 @@ class CsvImporter implements ImporterInterface $convertedValue = $converter->convert($value); $certainty = $converter->getCertainty(); - // log it. Log::debug('Value ', ['index' => $index, 'value' => $value, 'role' => $role]); // store in import entry: $object->importValue($role, $value, $certainty, $convertedValue); - // $object->fromRawValue($role, $value); } $result = $object->import(); if ($result->failed()) { - Log::error('Import of row has failed.', $result->errors->toArray()); + Log::error(sprintf('Import of row %d has failed.', $index), $result->errors->toArray()); } exit; diff --git a/app/Import/notes.txt b/app/Import/notes.txt new file mode 100644 index 0000000000..045c762e4a --- /dev/null +++ b/app/Import/notes.txt @@ -0,0 +1,46 @@ +The import routine is as follows: + +1. Upload and setup: + +User uploads a file with entries. The Setup/SetupInterface gives the user +the opportunity (in any number of steps) to do the necessary configuration. +This could also be skipped of course. An ImportJob object is created with a +basic and empty configuration. + +Helper classes are as follows, greatly modelled to the CSV importer: + +- The Mapper classes give back lists of Firefly objects. You can show them to the +user in order to help you convert text values to their Firefly counterparts. +For example, the user maps "Gcrsr" to category Groceries. +- The Converter classes exist to help convert text values to their Firely counterparts. +Feed "AB12ABCD897829" to the AssetAccountIban Converter and you should end up with a new +or found asset account. The previously built mapping is used to narrow it down. Submit an empty +mapping if this one is not relevant. + +The mapping and possibly other configuration options are stored in a newly created +ImportJob object, stored in the database. This import job holds a reference to the uploaded file +(placed encrypted in /storage/uploads) and the status of the import. + +2. Actual import + +Using either the command line or the web interface the user can tell Firefly to start the import. + +The ImporterInterface runs and creates an ImportEntry for each line, blob or whatever distinction it +wants. + +For each line, the ImporterInterface should run each field through the selected Converter in order +to convert the text values to their Firefly counterparts. Again, this is modelled to the CSV importer +and may need an update for MT940. + +In any case, this newly minted set of ImportEntries (it cannot be saved or stored atm, +this has to be done in one go) is then fed to the ImportValidator which will reject entries +(almost never) and corrects fields if necessary. + +- Adds a default description if there isn't one present. +- Adds the date (today) if no date is present. +- Determins the type of transaction (withdrawal, deposit, transfer). +- Determins the types of accounts involved (asset, expense, revenue). +- Determins the currency of the transaction. + +This set of corrected ImportEntries is then fed to the ImportStorage class which will generate +TransactionJournals, Transactions and other related objects. \ No newline at end of file From 200366f5be099bb7948c6384ff769bc315fa52e1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 10 Aug 2016 18:49:16 +0200 Subject: [PATCH 76/94] Extended the validator and created more code to handle exceptions. Signed-off-by: James Cole --- app/Console/Commands/Import.php | 18 +- app/Crud/Account/AccountCrud.php | 18 + app/Crud/Account/AccountCrudInterface.php | 25 +- app/Import/Converter/AssetAccountNumber.php | 17 +- app/Import/Converter/ExternalId.php | 37 ++ .../Converter/OpposingAccountNumber.php | 17 +- app/Import/ImportEntry.php | 12 +- app/Import/ImportValidator.php | 343 ++++++++++++++++++ app/Import/Importer/CsvImporter.php | 83 ++--- app/Import/Importer/ImporterInterface.php | 5 +- app/Import/Logging/CommandHandler.php | 2 +- app/Import/notes.txt | 3 +- resources/lang/en_US/csv.php | 1 + 13 files changed, 510 insertions(+), 71 deletions(-) create mode 100644 app/Import/Converter/ExternalId.php create mode 100644 app/Import/ImportValidator.php diff --git a/app/Console/Commands/Import.php b/app/Console/Commands/Import.php index 33be3c8e06..483eb179d9 100644 --- a/app/Console/Commands/Import.php +++ b/app/Console/Commands/Import.php @@ -11,8 +11,9 @@ declare(strict_types = 1); namespace FireflyIII\Console\Commands; +use FireflyIII\Crud\Account\AccountCrud; use FireflyIII\Import\Importer\ImporterInterface; -use FireflyIII\Import\Setup\SetupInterface; +use FireflyIII\Import\ImportValidator; use FireflyIII\Import\Logging\CommandHandler; use FireflyIII\Models\ImportJob; use Illuminate\Console\Command; @@ -79,7 +80,20 @@ class Import extends Command $monolog = Log::getMonolog(); $handler = new CommandHandler($this); $monolog->pushHandler($handler); - $importer->start(); + + // create import entries + $collection = $importer->createImportEntries(); + + // validate / clean collection: + $validator = new ImportValidator($collection); + $validator->setUser($job->user); + if ($job->configuration['import-account'] != 0) { + $repository = app(AccountCrud::class, [$job->user]); + $validator->setDefaultImportAccount($repository->find($job->configuration['import-account'])); + } + + $validator->clean(); + $this->line('Something something import: ' . $jobKey); } diff --git a/app/Crud/Account/AccountCrud.php b/app/Crud/Account/AccountCrud.php index f81bbb3263..075374efe5 100644 --- a/app/Crud/Account/AccountCrud.php +++ b/app/Crud/Account/AccountCrud.php @@ -275,6 +275,24 @@ class AccountCrud implements AccountCrudInterface return $account; } + /** + * @param Account $account + * @param string $type + * + * @return Account + */ + public function updateAccountType(Account $account, string $type): Account + { + $type = AccountType::whereType($type)->first(); + if (!is_null($type)) { + $account->account_type_id = $type->id; + $account->save(); + } + + return $account; + + } + /** * @param array $data * diff --git a/app/Crud/Account/AccountCrudInterface.php b/app/Crud/Account/AccountCrudInterface.php index 41b6956cfa..95e794418d 100644 --- a/app/Crud/Account/AccountCrudInterface.php +++ b/app/Crud/Account/AccountCrudInterface.php @@ -37,6 +37,14 @@ interface AccountCrudInterface */ public function find(int $accountId): Account; + /** + * @param string $number + * @param array $types + * + * @return Account + */ + public function findByAccountNumber(string $number, array $types): Account; + /** * @param string $iban * @param array $types @@ -53,14 +61,6 @@ interface AccountCrudInterface */ public function findByName(string $name, array $types): Account; - /** - * @param string $number - * @param array $types - * - * @return Account - */ - public function findByAccountNumber(string $number, array $types): Account; - /** * @param array $accountIds * @@ -91,7 +91,6 @@ interface AccountCrudInterface */ public function storeMeta(Account $account, string $name, $value): AccountMeta; - /** * @param Account $account * @param array $data @@ -99,4 +98,12 @@ interface AccountCrudInterface * @return Account */ public function update(Account $account, array $data): Account; + + /** + * @param Account $account + * @param string $type + * + * @return Account + */ + public function updateAccountType(Account $account, string $type): Account; } \ No newline at end of file diff --git a/app/Import/Converter/AssetAccountNumber.php b/app/Import/Converter/AssetAccountNumber.php index aacef8ae7a..cc478ca94a 100644 --- a/app/Import/Converter/AssetAccountNumber.php +++ b/app/Import/Converter/AssetAccountNumber.php @@ -12,7 +12,6 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; use FireflyIII\Crud\Account\AccountCrudInterface; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use Log; @@ -58,13 +57,25 @@ class AssetAccountNumber extends BasicConverter implements ConverterInterface if (!is_null($account->id)) { Log::debug('Found account by name', ['id' => $account->id]); $this->setCertainty(50); + + return $account; + } + + // try to find by the name we would give it: + $accountName = 'Asset account with number ' . e($value); + $account = $repository->findByName($accountName, [AccountType::ASSET]); + if (!is_null($account->id)) { + Log::debug('Found account by name', ['id' => $account->id]); + $this->setCertainty(50); + return $account; } $account = $repository->store( - ['name' => 'Account with number ' . $value, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id, 'accountType' => 'asset', - 'virtualBalance' => 0, 'active' => true] + ['name' => $accountName, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id, + 'accountType' => 'asset', + 'virtualBalance' => 0, 'accountNumber' => $value, 'active' => true] ); $this->setCertainty(100); diff --git a/app/Import/Converter/ExternalId.php b/app/Import/Converter/ExternalId.php new file mode 100644 index 0000000000..272dc5b7d7 --- /dev/null +++ b/app/Import/Converter/ExternalId.php @@ -0,0 +1,37 @@ +setCertainty(100); + + return strval(trim($value)); + + } +} \ No newline at end of file diff --git a/app/Import/Converter/OpposingAccountNumber.php b/app/Import/Converter/OpposingAccountNumber.php index e47e596a38..cb97af5cf0 100644 --- a/app/Import/Converter/OpposingAccountNumber.php +++ b/app/Import/Converter/OpposingAccountNumber.php @@ -14,6 +14,7 @@ namespace FireflyIII\Import\Converter; use FireflyIII\Crud\Account\AccountCrudInterface; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; use Log; /** @@ -61,13 +62,23 @@ class OpposingAccountNumber extends BasicConverter implements ConverterInterface return $account; } + // try to find by the name we would give it: + $accountName = 'Import account with number ' . e($value); + $account = $repository->findByName($accountName, [AccountType::IMPORT]); + if (!is_null($account->id)) { + Log::debug('Found account by name', ['id' => $account->id]); + $this->setCertainty(50); + + return $account; + } + $account = $repository->store( - ['name' => 'Account with number ' . $value, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id, 'accountType' => 'asset', - 'virtualBalance' => 0, 'active' => true] + ['name' => $accountName, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id, + 'accountType' => 'import', + 'virtualBalance' => 0, 'accountNumber' => $value, 'active' => true] ); $this->setCertainty(100); - return $account; } diff --git a/app/Import/ImportEntry.php b/app/Import/ImportEntry.php index 05b3d76a10..5d30621124 100644 --- a/app/Import/ImportEntry.php +++ b/app/Import/ImportEntry.php @@ -14,10 +14,6 @@ namespace FireflyIII\Import; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; -use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Models\TransactionType; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\User; use Illuminate\Support\Collection; use Log; @@ -33,10 +29,10 @@ class ImportEntry public $certain = []; /** @var array */ public $fields = []; - /** @var User */ public $user; - + /** @var bool */ + public $valid = true; /** @var array */ private $validFields = ['amount', @@ -45,6 +41,7 @@ class ImportEntry 'date-book', 'description', 'date-process', + 'transaction-type', 'currency', 'asset-account', 'opposing-account', 'bill', 'budget', 'category', 'tags']; /** @@ -154,6 +151,9 @@ class ImportEntry case 'tags-comma': case 'tags-space': $this->appendCollection('tags', $convertedValue); + case 'external-id': + // ignore for now. + break; } } diff --git a/app/Import/ImportValidator.php b/app/Import/ImportValidator.php new file mode 100644 index 0000000000..761f421b13 --- /dev/null +++ b/app/Import/ImportValidator.php @@ -0,0 +1,343 @@ +entries = $entries; + } + + /** + * Clean collection by filling in all the blanks. + */ + public function clean() + { + /** @var ImportEntry $entry */ + foreach ($this->entries as $entry) { + /* + * X Adds the date (today) if no date is present. + * X Determins the types of accounts involved (asset, expense, revenue). + * X Determins the type of transaction (withdrawal, deposit, transfer). + * - Determins the currency of the transaction. + * X Adds a default description if there isn't one present. + */ + $this->checkAmount($entry); + $this->setDate($entry); + $this->setAssetAccount($entry); + $this->setOpposingAccount($entry); + $this->cleanDescription($entry); + $this->setTransactionType($entry); + $this->setTransactionCurrency($entry); + } + + + /** @var ImportEntry $entry */ + foreach ($this->entries as $entry) { + Log::debug('Description: ' . $entry->fields['description']); + } + + } + + /** + * @param Account $defaultImportAccount + */ + public function setDefaultImportAccount(Account $defaultImportAccount) + { + $this->defaultImportAccount = $defaultImportAccount; + } + + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + + /** + * @param ImportEntry $entry + */ + private function checkAmount(ImportEntry $entry) + { + if ($entry->fields['amount'] == 0) { + $entry->valid = false; + Log::error('Amount of transaction is zero, cannot handle.'); + } + Log::debug('Amount is OK.'); + } + + /** + * @param ImportEntry $entry + */ + private function cleanDescription(ImportEntry $entry) + { + if (!isset($entry->fields['description'])) { + Log::debug('Set empty transaction description because field was not set.'); + $entry->fields['description'] = '(empty transaction description)'; + + return; + } + if (is_null($entry->fields['description'])) { + Log::debug('Set empty transaction description because field was null.'); + $entry->fields['description'] = '(empty transaction description)'; + + return; + } + + if (strlen($entry->fields['description']) == 0) { + Log::debug('Set empty transaction description because field was empty.'); + $entry->fields['description'] = '(empty transaction description)'; + + return; + } + Log::debug('Transaction description is OK.'); + $entry->fields['description'] = trim($entry->fields['description']); + } + + /** + * @param Account $account + * @param string $type + * + * @return Account + */ + private function convertAccount(Account $account, string $type): Account + { + $accountType = $account->accountType->type; + if ($accountType === $type) { + return $account; + } + // find it first by new type: + /** @var AccountCrudInterface $repository */ + $repository = app(AccountCrudInterface::class, [$this->user]); + $result = $repository->findByName($account->name, [$type]); + if (is_null($result->id)) { + // can convert account: + $result = $repository->updateAccountType($account, $type); + } + + return $result; + + + } + + /** + * @return Account + */ + private function fallbackExpenseAccount(): Account + { + + /** @var AccountCrudInterface $repository */ + $repository = app(AccountCrudInterface::class, [$this->user]); + $name = 'Unknown expense account'; + $result = $repository->findByName($name, [AccountType::EXPENSE]); + if (is_null($result->id)) { + $result = $repository->store( + ['name' => $name, 'iban' => null, 'openingBalance' => 0, 'user' => $this->user->id, 'accountType' => 'expense', 'virtualBalance' => 0, + 'active' => true] + ); + } + + return $result; + } + + /** + * @return Account + */ + private function fallbackRevenueAccount(): Account + { + + /** @var AccountCrudInterface $repository */ + $repository = app(AccountCrudInterface::class, [$this->user]); + $name = 'Unknown revenue account'; + $result = $repository->findByName($name, [AccountType::REVENUE]); + if (is_null($result->id)) { + $result = $repository->store( + ['name' => $name, 'iban' => null, 'openingBalance' => 0, 'user' => $this->user->id, 'accountType' => 'revenue', 'virtualBalance' => 0, + 'active' => true] + ); + } + + return $result; + } + + /** + * @param ImportEntry $entry + */ + private function setAssetAccount(ImportEntry $entry) + { + if (is_null($entry->fields['asset-account'])) { + if (!is_null($this->defaultImportAccount)) { + Log::debug('Set asset account from default asset account'); + $entry->fields['asset-account'] = $this->defaultImportAccount; + + return; + } + // default import is null? should not happen. Entry cannot be imported. + // set error message and block. + $entry->valid = false; + Log::error('Cannot import entry. Asset account is NULL and import account is also NULL.'); + } + Log::debug('Asset account is OK.'); + } + + + /** + * @param ImportEntry $entry + */ + private function setDate(ImportEntry $entry) + { + if (is_null($entry->fields['date-transaction'])) { + // empty date field? find alternative. + $alternatives = ['date-book', 'date-interest', 'date-process']; + foreach ($alternatives as $alternative) { + if (!is_null($entry->fields[$alternative])) { + Log::debug(sprintf('Copied date-transaction from %s.', $alternative)); + $entry->fields['date-transaction'] = clone $entry->fields[$alternative]; + + return; + } + } + // date is still null at this point + Log::debug('Set date-transaction to today.'); + $entry->fields['date-transaction'] = new Carbon; + + return; + } + Log::debug('Date-transaction is OK'); + + + } + + /** + * @param ImportEntry $entry + */ + private function setOpposingAccount(ImportEntry $entry) + { + // empty opposing account. Create one based on amount. + if (is_null($entry->fields['opposing-account'])) { + + if ($entry->fields['amount'] < 0) { + // create or find general opposing expense account. + Log::debug('Created fallback expense account'); + $entry->fields['opposing-account'] = $this->fallbackExpenseAccount(); + + return; + } + Log::debug('Created fallback revenue account'); + $entry->fields['opposing-account'] = $this->fallbackRevenueAccount(); + + return; + } + + // opposing is of type "import". Convert to correct type (by amount): + $type = $entry->fields['opposing-account']->accountType->type; + if ($type == AccountType::IMPORT && $entry->fields['amount'] < 0) { + $account = $this->convertAccount($entry->fields['opposing-account'], AccountType::EXPENSE); + $entry->fields['opposing-account'] = $account; + Log::debug('Converted import account to expense account'); + + return; + } + if ($type == AccountType::IMPORT && $entry->fields['amount'] > 0) { + $account = $this->convertAccount($entry->fields['opposing-account'], AccountType::REVENUE); + $entry->fields['opposing-account'] = $account; + Log::debug('Converted import account to revenue account'); + + return; + } + // amount < 0, but opposing is revenue + if ($type == AccountType::REVENUE && $entry->fields['amount'] < 0) { + $account = $this->convertAccount($entry->fields['opposing-account'], AccountType::EXPENSE); + $entry->fields['opposing-account'] = $account; + Log::debug('Converted revenue account to expense account'); + + return; + } + + // amount > 0, but opposing is expense + if ($type == AccountType::EXPENSE && $entry->fields['amount'] < 0) { + $account = $this->convertAccount($entry->fields['opposing-account'], AccountType::REVENUE); + $entry->fields['opposing-account'] = $account; + Log::debug('Converted expense account to revenue account'); + + return; + } + // account type is OK + Log::debug('Opposing account is OK.'); + + } + + /** + * @param ImportEntry $entry + */ + private function setTransactionCurrency(ImportEntry $entry) + { + if (is_null($entry->fields['currency'])) { + /** @var CurrencyRepositoryInterface $repository */ + $repository = app(CurrencyRepositoryInterface::class); + $entry->fields['currency'] = $repository->findByCode(env('DEFAULT_CURRENCY', 'EUR')); + Log::debug('Set currency to EUR'); + return; + } + Log::debug('Currency is OK'); + } + + /** + * @param ImportEntry $entry + */ + private function setTransactionType(ImportEntry $entry) + { + $type = $entry->fields['opposing-account']->accountType->type; + switch ($type) { + case AccountType::EXPENSE: + $entry->fields['transaction-type'] = TransactionType::WITHDRAWAL; + break; + case AccountType::REVENUE: + $entry->fields['transaction-type'] = TransactionType::DEPOSIT; + break; + case AccountType::ASSET: + $entry->fields['transaction-type'] = TransactionType::TRANSFER; + break; + } + } + + +} \ No newline at end of file diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index e989d603a6..9f74eab0ec 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -14,9 +14,9 @@ namespace FireflyIII\Import\Importer; use FireflyIII\Crud\Account\AccountCrud; use FireflyIII\Import\Converter\ConverterInterface; use FireflyIII\Import\ImportEntry; -use FireflyIII\Import\ImportResult; use FireflyIII\Models\Account; use FireflyIII\Models\ImportJob; +use Illuminate\Support\Collection; use League\Csv\Reader; use Log; @@ -30,8 +30,33 @@ class CsvImporter implements ImporterInterface /** @var ImportJob */ public $job; - /** @var Account */ - public $defaultImportAccount; + /** + * Run the actual import + * + * @return Collection + */ + public function createImportEntries(): Collection + { + $config = $this->job->configuration; + $content = $this->job->uploadFileContents(); + + // create CSV reader. + $reader = Reader::createFromString($content); + $start = $config['has-headers'] ? 1 : 0; + $results = $reader->fetch(); + $collection = new Collection; + foreach ($results as $index => $row) { + if ($index >= $start) { + Log::debug(sprintf('Now going to import row %d.', $index)); + $importEntry = $this->importSingleRow($index, $row); + $collection->push($importEntry); + } + } + Log::debug(sprintf('Collection contains %d entries', $collection->count())); + Log::debug('This call should be intercepted somehow.'); + + return $collection; + } /** * @param ImportJob $job @@ -41,45 +66,13 @@ class CsvImporter implements ImporterInterface $this->job = $job; } - /** - * Run the actual import - * - * @return bool - */ - public function start(): bool - { - $config = $this->job->configuration; - $content = $this->job->uploadFileContents(); - - if ($config['import-account'] != 0) { - $repository = app(AccountCrud::class, [$this->job->user]); - $this->defaultImportAccount = $repository->find($config['import-account']); - } - - // create CSV reader. - $reader = Reader::createFromString($content); - $start = $config['has-headers'] ? 1 : 0; - $results = $reader->fetch(); - foreach ($results as $index => $row) { - if ($index >= $start) { - Log::debug(sprintf('Now going to import row %d.', $index)); - $this->importSingleRow($index, $row); - } - } - - Log::debug('This call should be intercepted somehow.'); - - return true; - } - - /** * @param int $index * @param array $row * - * @return ImportResult + * @return ImportEntry */ - private function importSingleRow(int $index, array $row): ImportResult + private function importSingleRow(int $index, array $row): ImportEntry { // create import object: $object = new ImportEntry; @@ -113,13 +106,15 @@ class CsvImporter implements ImporterInterface $object->importValue($role, $value, $certainty, $convertedValue); } - $result = $object->import(); - if ($result->failed()) { - Log::error(sprintf('Import of row %d has failed.', $index), $result->errors->toArray()); - } + return $object; - exit; - - return true; + // $result = $object->import(); + // if ($result->failed()) { + // Log::error(sprintf('Import of row %d has failed.', $index), $result->errors->toArray()); + // } + // + // exit; + // + // return true; } } \ No newline at end of file diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php index 308d495040..08fbb591fe 100644 --- a/app/Import/Importer/ImporterInterface.php +++ b/app/Import/Importer/ImporterInterface.php @@ -11,6 +11,7 @@ declare(strict_types = 1); namespace FireflyIII\Import\Importer; use FireflyIII\Models\ImportJob; +use Illuminate\Support\Collection; /** * Interface ImporterInterface @@ -22,9 +23,9 @@ interface ImporterInterface /** * Run the actual import * - * @return bool + * @return Collection */ - public function start(): bool; + public function createImportEntries(): Collection; /** * @param ImportJob $job diff --git a/app/Import/Logging/CommandHandler.php b/app/Import/Logging/CommandHandler.php index 6b4d3106c1..d4f2021948 100644 --- a/app/Import/Logging/CommandHandler.php +++ b/app/Import/Logging/CommandHandler.php @@ -44,6 +44,6 @@ class CommandHandler extends AbstractProcessingHandler */ protected function write(array $record) { - $this->command->line((string) $record['formatted']); + $this->command->line((string) trim($record['formatted'])); } } \ No newline at end of file diff --git a/app/Import/notes.txt b/app/Import/notes.txt index 045c762e4a..603e185f84 100644 --- a/app/Import/notes.txt +++ b/app/Import/notes.txt @@ -36,11 +36,12 @@ In any case, this newly minted set of ImportEntries (it cannot be saved or store this has to be done in one go) is then fed to the ImportValidator which will reject entries (almost never) and corrects fields if necessary. -- Adds a default description if there isn't one present. + - Adds the date (today) if no date is present. - Determins the type of transaction (withdrawal, deposit, transfer). - Determins the types of accounts involved (asset, expense, revenue). - Determins the currency of the transaction. +- Adds a default description if there isn't one present. This set of corrected ImportEntries is then fed to the ImportStorage class which will generate TransactionJournals, Transactions and other related objects. \ No newline at end of file diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index dc9b75e148..231c3c54fc 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -66,6 +66,7 @@ return [ 'column_description' => 'Description', 'column_opposing-iban' => 'Opposing account (IBAN)', 'column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'column_external-id' => 'External ID', 'column_opposing-name' => 'Opposing account (name)', 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', 'column_ing-debet-credit' => 'ING specific debet/credit indicator', From efe9933721a6c0fdb3d6574659962c65724aee11 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 11 Aug 2016 08:00:02 +0200 Subject: [PATCH 77/94] Import storage routine is creating the first transaction journals. Signed-off-by: James Cole --- .gitignore | 1 + app/Console/Commands/Import.php | 10 +- app/Import/ImportStorage.php | 158 ++++++++++++++++++++++++++++ app/Import/ImportValidator.php | 16 +-- app/Import/Importer/CsvImporter.php | 1 + app/Import/Setup/CsvSetup.php | 10 +- 6 files changed, 181 insertions(+), 15 deletions(-) create mode 100644 app/Import/ImportStorage.php diff --git a/.gitignore b/.gitignore index 76b63316ba..3285ef1ed2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ _development .env.local result.html +test-import.sh diff --git a/app/Console/Commands/Import.php b/app/Console/Commands/Import.php index 483eb179d9..bd02d71384 100644 --- a/app/Console/Commands/Import.php +++ b/app/Console/Commands/Import.php @@ -13,6 +13,7 @@ namespace FireflyIII\Console\Commands; use FireflyIII\Crud\Account\AccountCrud; use FireflyIII\Import\Importer\ImporterInterface; +use FireflyIII\Import\ImportStorage; use FireflyIII\Import\ImportValidator; use FireflyIII\Import\Logging\CommandHandler; use FireflyIII\Models\ImportJob; @@ -92,7 +93,14 @@ class Import extends Command $validator->setDefaultImportAccount($repository->find($job->configuration['import-account'])); } - $validator->clean(); + $cleaned = $validator->clean(); + + // then import collection: + $storage = new ImportStorage($collection); + $storage->setUser($job->user); + + // and run store routine: + $storage->store(); $this->line('Something something import: ' . $jobKey); diff --git a/app/Import/ImportStorage.php b/app/Import/ImportStorage.php new file mode 100644 index 0000000000..178c6bdb25 --- /dev/null +++ b/app/Import/ImportStorage.php @@ -0,0 +1,158 @@ +entries = $entries; + + } + + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + + /** + * + */ + public function store() + { + foreach ($this->entries as $entry) { + $this->storeSingle($entry); + } + + } + + /** + * @param float $amount + * + * @return string + */ + private function makePositive(float $amount): string + { + $amount = strval($amount); + if (bccomp($amount, '0', 4) === -1) { // left is larger than right + $amount = bcmul($amount, '-1'); + } + + return $amount; + } + + /** + * @param ImportEntry $entry + */ + private function storeSingle(ImportEntry $entry) + { + Log::debug('Going to store entry!'); + $billId = is_null($entry->fields['bill']) ? null : $entry->fields['bill']->id; + $journalData = [ + 'user_id' => $entry->user->id, + 'transaction_type_id' => $entry->fields['transaction-type']->id, + 'bill_id' => $billId, + 'transaction_currency_id' => $entry->fields['currency']->id, + 'description' => $entry->fields['description'], + 'date' => $entry->fields['date-transaction'], + 'interest_date' => $entry->fields['date-interest'], + 'book_date' => $entry->fields['date-book'], + 'process_date' => $entry->fields['date-process'], + 'completed' => 0, + ]; + /** @var TransactionJournal $journal */ + $journal = TransactionJournal::create($journalData); + $amount = $this->makePositive($entry->fields['amount']); + + Log::debug('Created journal', ['id' => $journal->id]); + + // then create transactions. Single ones, unfortunately. + switch ($entry->fields['transaction-type']->type) { + default: + throw new FireflyException('ImportStorage cannot handle ' . $entry->fields['transaction-type']->type); + case TransactionType::WITHDRAWAL: + $source = $entry->fields['asset-account']; + $destination = $entry->fields['opposing-account']; + // make amount positive, if it is not. + break; + case TransactionType::DEPOSIT: + $source = $entry->fields['opposing-account']; + $destination = $entry->fields['asset-account']; + break; + case TransactionType::TRANSFER: + // depends on amount: + if ($entry->fields['amount'] < 0) { + $source = $entry->fields['asset-account']; + $destination = $entry->fields['opposing-account']; + break; + } + $destination = $entry->fields['asset-account']; + $source = $entry->fields['opposing-account']; + break; + } + + // create new transactions. This is something that needs a rewrite for multiple/split transactions. + $sourceData = [ + 'account_id' => $source->id, + 'transaction_journal_id' => $journal->id, + 'description' => $journalData['description'], + 'amount' => bcmul($amount, '-1'), + ]; + + $destinationData = [ + 'account_id' => $destination->id, + 'transaction_journal_id' => $journal->id, + 'description' => $journalData['description'], + 'amount' => $amount, + ]; + + $one = Transaction::create($sourceData); + $two = Transaction::create($destinationData); + Log::debug('Created transactions', ['source' => $one->id,'destination' => $two->id]); + + $journal->completed = 1; + $journal->save(); + + // now attach budget and so on. + + + + } +} \ No newline at end of file diff --git a/app/Import/ImportValidator.php b/app/Import/ImportValidator.php index 761f421b13..e53a503bac 100644 --- a/app/Import/ImportValidator.php +++ b/app/Import/ImportValidator.php @@ -48,7 +48,7 @@ class ImportValidator /** * Clean collection by filling in all the blanks. */ - public function clean() + public function clean(): Collection { /** @var ImportEntry $entry */ foreach ($this->entries as $entry) { @@ -67,13 +67,7 @@ class ImportValidator $this->setTransactionType($entry); $this->setTransactionCurrency($entry); } - - - /** @var ImportEntry $entry */ - foreach ($this->entries as $entry) { - Log::debug('Description: ' . $entry->fields['description']); - } - + return $this->entries; } /** @@ -328,13 +322,13 @@ class ImportValidator $type = $entry->fields['opposing-account']->accountType->type; switch ($type) { case AccountType::EXPENSE: - $entry->fields['transaction-type'] = TransactionType::WITHDRAWAL; + $entry->fields['transaction-type'] = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); break; case AccountType::REVENUE: - $entry->fields['transaction-type'] = TransactionType::DEPOSIT; + $entry->fields['transaction-type'] = TransactionType::whereType(TransactionType::DEPOSIT)->first(); break; case AccountType::ASSET: - $entry->fields['transaction-type'] = TransactionType::TRANSFER; + $entry->fields['transaction-type'] = TransactionType::whereType(TransactionType::TRANSFER)->first(); break; } } diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 9f74eab0ec..8abaab2024 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -42,6 +42,7 @@ class CsvImporter implements ImporterInterface // create CSV reader. $reader = Reader::createFromString($content); + $reader->setDelimiter($config['delimiter']); $start = $config['has-headers'] ? 1 : 0; $results = $reader->fetch(); $collection = new Collection; diff --git a/app/Import/Setup/CsvSetup.php b/app/Import/Setup/CsvSetup.php index 53d445037f..5dc009b90e 100644 --- a/app/Import/Setup/CsvSetup.php +++ b/app/Import/Setup/CsvSetup.php @@ -191,7 +191,8 @@ class CsvSetup implements SetupInterface { /** @var AccountCrud $repository */ $repository = app(AccountCrud::class, [auth()->user()]); - $account = $repository->find(intval($data['csv_import_account'])); + $importId = $data['csv_import_account'] ?? 0; + $account = $repository->find(intval($importId)); $hasHeaders = isset($data['has_headers']) && intval($data['has_headers']) === 1 ? true : false; $config = $this->job->configuration; @@ -199,14 +200,14 @@ class CsvSetup implements SetupInterface $config['date-format'] = $data['date_format']; $config['delimiter'] = $data['csv_delimiter']; - Log::debug('Entered import account.', ['id' => $data['csv_import_account']]); + Log::debug('Entered import account.', ['id' => $importId]); if (!is_null($account->id)) { Log::debug('Found account.', ['id' => $account->id, 'name' => $account->name]); $config['import-account'] = $account->id; } else { - Log::error('Could not find anything for csv_import_account.', ['id' => $data['csv_import_account']]); + Log::error('Could not find anything for csv_import_account.', ['id' => $importId]); } // loop specifics. if (isset($data['specifics']) && is_array($data['specifics'])) { @@ -353,7 +354,9 @@ class CsvSetup implements SetupInterface // in order to actually map we also need all possible values from the CSV file. $content = $this->job->uploadFileContents(); + /** @var Reader $reader */ $reader = Reader::createFromString($content); + $reader->setDelimiter($config['delimiter']); $results = $reader->fetch(); foreach ($results as $rowIndex => $row) { @@ -405,6 +408,7 @@ class CsvSetup implements SetupInterface // create CSV reader. $reader = Reader::createFromString($content); + $reader->setDelimiter($config['delimiter']); $start = $config['has-headers'] ? 1 : 0; $end = $start + self::EXAMPLE_ROWS; // first X rows From 186b704509deaa17257ddd6956f579db2bb37d67 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 11 Aug 2016 10:21:32 +0200 Subject: [PATCH 78/94] Lots of new code to test the import routine. Signed-off-by: James Cole --- app/Console/Commands/EncryptFile.php | 69 ++ app/Console/Kernel.php | 2 + app/Crud/Account/AccountCrud.php | 17 +- app/Http/Controllers/Auth/AuthController.php | 1 + app/Http/Controllers/HomeController.php | 1 + app/Http/Controllers/NewUserController.php | 1 + app/Import/Converter/AssetAccountName.php | 6 +- app/Import/Converter/BillName.php | 2 +- app/Import/Converter/BudgetName.php | 2 +- app/Import/Converter/CategoryName.php | 2 +- app/Import/Converter/OpposingAccountName.php | 4 +- app/Import/ImportStorage.php | 13 +- app/Import/ImportValidator.php | 116 +- app/Import/Importer/CsvImporter.php | 1 + app/Models/Account.php | 4 +- app/Providers/CrudServiceProvider.php | 2 +- app/Repositories/Bill/BillRepository.php | 2 +- app/Repositories/Budget/BudgetRepository.php | 2 +- .../Category/CategoryRepository.php | 2 +- .../ExportJob/ExportJobRepository.php | 2 +- .../ImportJob/ImportJobRepository.php | 2 +- app/Support/Migration/TestData.php | 37 +- storage/database/.gitignore | 3 +- storage/database/seed.import-test.json | 72 ++ storage/database/seed.local.json | 3 +- storage/database/seed.split.json | 3 +- storage/database/seed.testing.json | 1011 +++++++++++++++++ 27 files changed, 1315 insertions(+), 67 deletions(-) create mode 100644 app/Console/Commands/EncryptFile.php create mode 100644 storage/database/seed.import-test.json create mode 100644 storage/database/seed.testing.json diff --git a/app/Console/Commands/EncryptFile.php b/app/Console/Commands/EncryptFile.php new file mode 100644 index 0000000000..3d68deacf5 --- /dev/null +++ b/app/Console/Commands/EncryptFile.php @@ -0,0 +1,69 @@ +argument('file'); + if (!file_exists($file)) { + $this->error(sprintf('File "%s" does not seem to exist.', $file)); + + return; + } + $content = file_get_contents($file); + $content = Crypt::encrypt($content); + $newName = $this->argument('key') . '.upload'; + + $path = storage_path('upload') . '/' . $newName; + file_put_contents($path, $content); + $this->line(sprintf('Encrypted "%s" and put it in "%s"', $file, $path)); + + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 5e9147a429..25b79b71eb 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -11,6 +11,7 @@ declare(strict_types = 1); namespace FireflyIII\Console; +use FireflyIII\Console\Commands\EncryptFile; use FireflyIII\Console\Commands\Import; use FireflyIII\Console\Commands\UpgradeFireflyInstructions; use FireflyIII\Console\Commands\VerifyDatabase; @@ -53,5 +54,6 @@ class Kernel extends ConsoleKernel UpgradeFireflyInstructions::class, VerifyDatabase::class, Import::class, + EncryptFile::class, ]; } diff --git a/app/Crud/Account/AccountCrud.php b/app/Crud/Account/AccountCrud.php index 075374efe5..787ced7bbf 100644 --- a/app/Crud/Account/AccountCrud.php +++ b/app/Crud/Account/AccountCrud.php @@ -103,7 +103,7 @@ class AccountCrud implements AccountCrudInterface } /** @var Collection $accounts */ - $accounts = $query->get(); + $accounts = $query->get(['accounts.*']); if ($accounts->count() > 0) { return $accounts->first(); } @@ -126,7 +126,7 @@ class AccountCrud implements AccountCrudInterface $query->whereIn('account_types.type', $types); } - $accounts = $query->get(); + $accounts = $query->get(['accounts.*']); /** @var Account $account */ foreach ($accounts as $account) { if ($account->iban === $iban) { @@ -145,20 +145,23 @@ class AccountCrud implements AccountCrudInterface */ public function findByName(string $name, array $types): Account { - $query = $this->user->accounts()->where('iban', '!=', ""); + $query = $this->user->accounts(); + Log::debug('Now in findByName()', ['name' => $name, 'types' => $types]); if (count($types) > 0) { $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); $query->whereIn('account_types.type', $types); } - $accounts = $query->get(); + $accounts = $query->get(['accounts.*']); + Log::debug(sprintf('Total set count is %d ', $accounts->count())); /** @var Account $account */ foreach ($accounts as $account) { if ($account->name === $name) { - return $account; + Log::debug('Account name is an exact match. ', ['db' => $account->name, 'source' => $name,'id' => $account->id]); } } + Log::warning('Found nothing in findByName()', ['name' => $name, 'types' => $types]); return new Account; } @@ -285,11 +288,11 @@ class AccountCrud implements AccountCrudInterface { $type = AccountType::whereType($type)->first(); if (!is_null($type)) { - $account->account_type_id = $type->id; + $account->accountType()->associate($type); $account->save(); } - return $account; + return $this->find($account->id); } diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 11ec64cfbf..e66c2561e9 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -64,6 +64,7 @@ class AuthController extends Controller */ public function login(Request $request) { + $this->validate($request, [$this->loginUsername() => 'required', 'password' => 'required',]); $throttles = $this->isUsingThrottlesLoginsTrait(); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 48ad6319a0..c70d974824 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -119,6 +119,7 @@ class HomeController extends Controller */ public function index(ARI $repository, AccountCrudInterface $crud) { + $types = config('firefly.accountTypesByIdentifier.asset'); $count = $repository->countAccounts($types); diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 3c7dcd4102..81c2e05dda 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -42,6 +42,7 @@ class NewUserController extends Controller */ public function index(ARI $repository) { + View::share('title', 'Welcome to Firefly!'); View::share('mainTitleIcon', 'fa-fire'); diff --git a/app/Import/Converter/AssetAccountName.php b/app/Import/Converter/AssetAccountName.php index 5b309c7702..af293656be 100644 --- a/app/Import/Converter/AssetAccountName.php +++ b/app/Import/Converter/AssetAccountName.php @@ -36,6 +36,7 @@ class AssetAccountName extends BasicConverter implements ConverterInterface if (strlen($value) === 0) { $this->setCertainty(0); + return new Account; } @@ -49,6 +50,7 @@ class AssetAccountName extends BasicConverter implements ConverterInterface if (!is_null($account->id)) { Log::debug('Found account by ID', ['id' => $account->id]); $this->setCertainty(100); + return $account; } } @@ -56,7 +58,7 @@ class AssetAccountName extends BasicConverter implements ConverterInterface // not mapped? Still try to find it first: $account = $repository->findByName($value, [AccountType::ASSET]); if (!is_null($account->id)) { - Log::debug('Found account by name', ['id' => $account->id]); + Log::debug('Found asset account by name', ['value' => $value, 'id' => $account->id]); return $account; } @@ -68,6 +70,8 @@ class AssetAccountName extends BasicConverter implements ConverterInterface ); $this->setCertainty(100); + Log::debug('Created new asset account ', ['name' => $account->name, 'id' => $account->id]); + return $account; diff --git a/app/Import/Converter/BillName.php b/app/Import/Converter/BillName.php index 6cb443c367..9d06e13105 100644 --- a/app/Import/Converter/BillName.php +++ b/app/Import/Converter/BillName.php @@ -66,7 +66,7 @@ class BillName extends BasicConverter implements ConverterInterface 'name' => $value, 'match' => $value, 'amount_min' => 1, - 'user_id' => $this->user->id, + 'user' => $this->user->id, 'amount_max' => 10, 'date' => date('Ymd'), 'repeat_freq' => 'monthly', diff --git a/app/Import/Converter/BudgetName.php b/app/Import/Converter/BudgetName.php index 59bcaf8bbb..bee601b682 100644 --- a/app/Import/Converter/BudgetName.php +++ b/app/Import/Converter/BudgetName.php @@ -64,7 +64,7 @@ class BudgetName extends BasicConverter implements ConverterInterface $budget = $repository->store( [ 'name' => $value, - 'user_id' => $this->user->id, + 'user' => $this->user->id, ] ); $this->setCertainty(100); diff --git a/app/Import/Converter/CategoryName.php b/app/Import/Converter/CategoryName.php index ac4f5ce720..8d09d271b8 100644 --- a/app/Import/Converter/CategoryName.php +++ b/app/Import/Converter/CategoryName.php @@ -64,7 +64,7 @@ class CategoryName extends BasicConverter implements ConverterInterface $category = $repository->store( [ 'name' => $value, - 'user_id' => $this->user->id, + 'user' => $this->user->id, ] ); $this->setCertainty(100); diff --git a/app/Import/Converter/OpposingAccountName.php b/app/Import/Converter/OpposingAccountName.php index 9d8b4d8623..b45a28cdb5 100644 --- a/app/Import/Converter/OpposingAccountName.php +++ b/app/Import/Converter/OpposingAccountName.php @@ -54,7 +54,7 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface // not mapped? Still try to find it first: $account = $repository->findByName($value, []); if (!is_null($account->id)) { - Log::debug('Found account by name', ['id' => $account->id]); + Log::debug('Found opposing account by name', ['id' => $account->id]); Log::warning( 'The match between name and account is uncertain because the type of transactions may not have been determined.', ['id' => $account->id, 'name' => $value] @@ -70,6 +70,8 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface ); $this->setCertainty(100); + Log::debug('Created new opposing account ', ['name' => $account->name, 'id' => $account->id]); + return $account; } } \ No newline at end of file diff --git a/app/Import/ImportStorage.php b/app/Import/ImportStorage.php index 178c6bdb25..a5c72a2ffe 100644 --- a/app/Import/ImportStorage.php +++ b/app/Import/ImportStorage.php @@ -58,6 +58,7 @@ class ImportStorage public function store() { foreach ($this->entries as $entry) { + Log::debug('--- import store start ---'); $this->storeSingle($entry); } @@ -80,9 +81,17 @@ class ImportStorage /** * @param ImportEntry $entry + * + * @throws FireflyException */ private function storeSingle(ImportEntry $entry) { + if ($entry->valid === false) { + Log::error('Cannot import entry, because valid=false'); + + return; + } + Log::debug('Going to store entry!'); $billId = is_null($entry->fields['bill']) ? null : $entry->fields['bill']->id; $journalData = [ @@ -145,7 +154,8 @@ class ImportStorage $one = Transaction::create($sourceData); $two = Transaction::create($destinationData); - Log::debug('Created transactions', ['source' => $one->id,'destination' => $two->id]); + Log::debug('Created transaction 1', ['id' => $one->id, 'account' => $one->account_id,'account_name' => $source->name]); + Log::debug('Created transaction 2', ['id' => $two->id, 'account' => $two->account_id,'account_name' => $destination->name]); $journal->completed = 1; $journal->save(); @@ -153,6 +163,5 @@ class ImportStorage // now attach budget and so on. - } } \ No newline at end of file diff --git a/app/Import/ImportValidator.php b/app/Import/ImportValidator.php index e53a503bac..527f407a8a 100644 --- a/app/Import/ImportValidator.php +++ b/app/Import/ImportValidator.php @@ -50,8 +50,10 @@ class ImportValidator */ public function clean(): Collection { + $newCollection = new Collection; /** @var ImportEntry $entry */ foreach ($this->entries as $entry) { + Log::debug('--- import validator start ---'); /* * X Adds the date (today) if no date is present. * X Determins the types of accounts involved (asset, expense, revenue). @@ -59,15 +61,20 @@ class ImportValidator * - Determins the currency of the transaction. * X Adds a default description if there isn't one present. */ - $this->checkAmount($entry); - $this->setDate($entry); - $this->setAssetAccount($entry); - $this->setOpposingAccount($entry); - $this->cleanDescription($entry); - $this->setTransactionType($entry); - $this->setTransactionCurrency($entry); + $entry = $this->checkAmount($entry); + $entry = $this->setDate($entry); + $entry = $this->setAssetAccount($entry); + Log::debug(sprintf('Opposing account is of type %s', $entry->fields['opposing-account']->accountType->type)); + $entry = $this->setOpposingAccount($entry); + Log::debug(sprintf('Opposing account is of type %s', $entry->fields['opposing-account']->accountType->type)); + $entry = $this->cleanDescription($entry); + $entry = $this->setTransactionType($entry); + $entry = $this->setTransactionCurrency($entry); + + $newCollection->push($entry); } - return $this->entries; + + return $newCollection; } /** @@ -88,42 +95,52 @@ class ImportValidator /** * @param ImportEntry $entry + * + * @return ImportEntry */ - private function checkAmount(ImportEntry $entry) + private function checkAmount(ImportEntry $entry): ImportEntry { if ($entry->fields['amount'] == 0) { $entry->valid = false; Log::error('Amount of transaction is zero, cannot handle.'); + + return $entry; } Log::debug('Amount is OK.'); + + return $entry; } /** * @param ImportEntry $entry + * + * @return ImportEntry */ - private function cleanDescription(ImportEntry $entry) + private function cleanDescription(ImportEntry $entry): ImportEntry { if (!isset($entry->fields['description'])) { Log::debug('Set empty transaction description because field was not set.'); $entry->fields['description'] = '(empty transaction description)'; - return; + return $entry; } if (is_null($entry->fields['description'])) { Log::debug('Set empty transaction description because field was null.'); $entry->fields['description'] = '(empty transaction description)'; - return; + return $entry; } if (strlen($entry->fields['description']) == 0) { Log::debug('Set empty transaction description because field was empty.'); $entry->fields['description'] = '(empty transaction description)'; - return; + return $entry; } Log::debug('Transaction description is OK.'); $entry->fields['description'] = trim($entry->fields['description']); + + return $entry; } /** @@ -136,6 +153,8 @@ class ImportValidator { $accountType = $account->accountType->type; if ($accountType === $type) { + Log::debug(sprintf('Account %s already of type %s', $account->name, $type)); + return $account; } // find it first by new type: @@ -144,7 +163,9 @@ class ImportValidator $result = $repository->findByName($account->name, [$type]); if (is_null($result->id)) { // can convert account: + Log::debug(sprintf('No account named %s of type %s, will convert.', $account->name, $type)); $result = $repository->updateAccountType($account, $type); + } return $result; @@ -194,29 +215,36 @@ class ImportValidator /** * @param ImportEntry $entry + * + * @return ImportEntry */ - private function setAssetAccount(ImportEntry $entry) + private function setAssetAccount(ImportEntry $entry): ImportEntry { if (is_null($entry->fields['asset-account'])) { if (!is_null($this->defaultImportAccount)) { Log::debug('Set asset account from default asset account'); $entry->fields['asset-account'] = $this->defaultImportAccount; - return; + return $entry; } // default import is null? should not happen. Entry cannot be imported. // set error message and block. $entry->valid = false; Log::error('Cannot import entry. Asset account is NULL and import account is also NULL.'); + } - Log::debug('Asset account is OK.'); + Log::debug('Asset account is OK.', ['id' => $entry->fields['asset-account']->id, 'name' => $entry->fields['asset-account']->name]); + + return $entry; } /** * @param ImportEntry $entry + * + * @return ImportEntry */ - private function setDate(ImportEntry $entry) + private function setDate(ImportEntry $entry): ImportEntry { if (is_null($entry->fields['date-transaction'])) { // empty date field? find alternative. @@ -226,24 +254,26 @@ class ImportValidator Log::debug(sprintf('Copied date-transaction from %s.', $alternative)); $entry->fields['date-transaction'] = clone $entry->fields[$alternative]; - return; + return $entry; } } // date is still null at this point Log::debug('Set date-transaction to today.'); $entry->fields['date-transaction'] = new Carbon; - return; + return $entry; } Log::debug('Date-transaction is OK'); - + return $entry; } /** * @param ImportEntry $entry + * + * @return ImportEntry */ - private function setOpposingAccount(ImportEntry $entry) + private function setOpposingAccount(ImportEntry $entry): ImportEntry { // empty opposing account. Create one based on amount. if (is_null($entry->fields['opposing-account'])) { @@ -253,12 +283,12 @@ class ImportValidator Log::debug('Created fallback expense account'); $entry->fields['opposing-account'] = $this->fallbackExpenseAccount(); - return; + return $entry; } Log::debug('Created fallback revenue account'); $entry->fields['opposing-account'] = $this->fallbackRevenueAccount(); - return; + return $entry; } // opposing is of type "import". Convert to correct type (by amount): @@ -268,14 +298,14 @@ class ImportValidator $entry->fields['opposing-account'] = $account; Log::debug('Converted import account to expense account'); - return; + return $entry; } if ($type == AccountType::IMPORT && $entry->fields['amount'] > 0) { $account = $this->convertAccount($entry->fields['opposing-account'], AccountType::REVENUE); $entry->fields['opposing-account'] = $account; Log::debug('Converted import account to revenue account'); - return; + return $entry; } // amount < 0, but opposing is revenue if ($type == AccountType::REVENUE && $entry->fields['amount'] < 0) { @@ -283,7 +313,7 @@ class ImportValidator $entry->fields['opposing-account'] = $account; Log::debug('Converted revenue account to expense account'); - return; + return $entry; } // amount > 0, but opposing is expense @@ -292,45 +322,65 @@ class ImportValidator $entry->fields['opposing-account'] = $account; Log::debug('Converted expense account to revenue account'); - return; + return $entry; } // account type is OK Log::debug('Opposing account is OK.'); + return $entry; + } /** * @param ImportEntry $entry + * + * @return ImportEntry */ - private function setTransactionCurrency(ImportEntry $entry) + private function setTransactionCurrency(ImportEntry $entry): ImportEntry { if (is_null($entry->fields['currency'])) { /** @var CurrencyRepositoryInterface $repository */ $repository = app(CurrencyRepositoryInterface::class); $entry->fields['currency'] = $repository->findByCode(env('DEFAULT_CURRENCY', 'EUR')); Log::debug('Set currency to EUR'); - return; + + return $entry; } Log::debug('Currency is OK'); + + return $entry; } /** * @param ImportEntry $entry + * + * @return ImportEntry */ - private function setTransactionType(ImportEntry $entry) + private function setTransactionType(ImportEntry $entry): ImportEntry { + Log::debug(sprintf('Opposing account is of type %s', $entry->fields['opposing-account']->accountType->type)); $type = $entry->fields['opposing-account']->accountType->type; switch ($type) { case AccountType::EXPENSE: $entry->fields['transaction-type'] = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); - break; + Log::debug('Transaction type is now withdrawal.'); + + return $entry; case AccountType::REVENUE: $entry->fields['transaction-type'] = TransactionType::whereType(TransactionType::DEPOSIT)->first(); - break; + Log::debug('Transaction type is now deposit.'); + + return $entry; case AccountType::ASSET: $entry->fields['transaction-type'] = TransactionType::whereType(TransactionType::TRANSFER)->first(); - break; + Log::debug('Transaction type is now transfer.'); + + return $entry; } + Log::error(sprintf('Opposing account is of type %s, cannot handle this.', $type)); + $entry->valid = false; + + return $entry; } diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 8abaab2024..f2e630d5c7 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -48,6 +48,7 @@ class CsvImporter implements ImporterInterface $collection = new Collection; foreach ($results as $index => $row) { if ($index >= $start) { + Log::debug('----- import entry build start --'); Log::debug(sprintf('Now going to import row %d.', $index)); $importEntry = $this->importSingleRow($index, $row); $collection->push($importEntry); diff --git a/app/Models/Account.php b/app/Models/Account.php index fdb7b3c2e9..2c8313c0b2 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -301,8 +301,8 @@ class Account extends Model */ public function setNameAttribute($value) { - $this->attributes['name'] = Crypt::encrypt($value); - $this->attributes['encrypted'] = true; + $this->attributes['name'] = $value; + $this->attributes['encrypted'] = false; } /** diff --git a/app/Providers/CrudServiceProvider.php b/app/Providers/CrudServiceProvider.php index cfb30450e3..815236b605 100644 --- a/app/Providers/CrudServiceProvider.php +++ b/app/Providers/CrudServiceProvider.php @@ -58,7 +58,7 @@ class CrudServiceProvider extends ServiceProvider if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); } - Log::debug('AccountCrud constructor, run with default arguments.', $arguments); + // Log::debug('AccountCrud constructor, run with default arguments.', $arguments); return app('FireflyIII\Crud\Account\AccountCrud', $arguments); } diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 6ea9f802af..51e0d4301a 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -81,7 +81,7 @@ class BillRepository implements BillRepositoryInterface */ public function findByName(string $name) : Bill { - $bills = $this->user->bills()->get(); + $bills = $this->user->bills()->get(['bills.*']); /** @var Bill $bill */ foreach ($bills as $bill) { diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 8498fa037d..88bb56912a 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -94,7 +94,7 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function findByName(string $name): Budget { - $budgets = $this->user->budgets()->get(); + $budgets = $this->user->budgets()->get(['budgets.*']); /** @var Budget $budget */ foreach ($budgets as $budget) { if ($budget->name === $name) { diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 9cd4024928..e0a662b355 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -110,7 +110,7 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function findByName(string $name) : Category { - $categories = $this->user->categories()->get(); + $categories = $this->user->categories()->get(['categories.*']); foreach ($categories as $category) { if ($category->name === $name) { return $category; diff --git a/app/Repositories/ExportJob/ExportJobRepository.php b/app/Repositories/ExportJob/ExportJobRepository.php index fe4f54f4f3..009aadbf6e 100644 --- a/app/Repositories/ExportJob/ExportJobRepository.php +++ b/app/Repositories/ExportJob/ExportJobRepository.php @@ -99,7 +99,7 @@ class ExportJobRepository implements ExportJobRepositoryInterface */ public function findByKey(string $key): ExportJob { - $result = $this->user->exportJobs()->where('key', $key)->first(); + $result = $this->user->exportJobs()->where('key', $key)->first(['export_jobs.*']); if (is_null($result)) { return new ExportJob; } diff --git a/app/Repositories/ImportJob/ImportJobRepository.php b/app/Repositories/ImportJob/ImportJobRepository.php index 4e281d83c6..15c02b5e60 100644 --- a/app/Repositories/ImportJob/ImportJobRepository.php +++ b/app/Repositories/ImportJob/ImportJobRepository.php @@ -71,7 +71,7 @@ class ImportJobRepository implements ImportJobRepositoryInterface */ public function findByKey(string $key): ImportJob { - $result = $this->user->importJobs()->where('key', $key)->first(); + $result = $this->user->importJobs()->where('key', $key)->first(['import_jobs.*']); if (is_null($result)) { return new ImportJob; } diff --git a/app/Support/Migration/TestData.php b/app/Support/Migration/TestData.php index 4b72873c21..d3e59f9cdd 100644 --- a/app/Support/Migration/TestData.php +++ b/app/Support/Migration/TestData.php @@ -244,6 +244,26 @@ class TestData DB::table('categories')->insert($insert); } + /** + * + */ + private function createImportJobs() + { + $insert = []; + foreach ($this->data['import-jobs'] as $job) { + $insert[] = [ + 'created_at' => $this->time, + 'updated_at' => $this->time, + 'user_id' => $job['user_id'], + 'file_type' => $job['file_type'], + 'key' => $job['key'], + 'status' => $job['status'], + 'configuration' => json_encode($job['configuration']), + ]; + } + DB::table('import_jobs')->insert($insert); + } + /** * */ @@ -640,14 +660,14 @@ class TestData foreach ($this->data['piggy-banks'] as $piggyBank) { $piggyId = DB::table('piggy_banks')->insertGetId( [ - 'created_at' => $this->time, - 'updated_at' => $this->time, - 'account_id' => $piggyBank['account_id'], - 'name' => Crypt::encrypt($piggyBank['name']), - 'targetamount' => $piggyBank['targetamount'], - 'startdate' => $piggyBank['startdate'], - 'order' => $piggyBank['order'], - 'encrypted' => 1, + 'created_at' => $this->time, + 'updated_at' => $this->time, + 'account_id' => $piggyBank['account_id'], + 'name' => Crypt::encrypt($piggyBank['name']), + 'targetamount' => $piggyBank['targetamount'], + 'startdate' => $piggyBank['startdate'], + 'order' => $piggyBank['order'], + 'encrypted' => 1, ] ); if (isset($piggyBank['currentamount'])) { @@ -808,6 +828,7 @@ class TestData $this->createMultiWithdrawals(); $this->createMultiDeposits(); $this->createMultiTransfers(); + $this->createImportJobs(); } } diff --git a/storage/database/.gitignore b/storage/database/.gitignore index 79a5312b13..2f8355d95c 100644 --- a/storage/database/.gitignore +++ b/storage/database/.gitignore @@ -1,4 +1,3 @@ * !.gitignore -!seed.local.json -!seed.split.json \ No newline at end of file +!seed.*.json \ No newline at end of file diff --git a/storage/database/seed.import-test.json b/storage/database/seed.import-test.json new file mode 100644 index 0000000000..47cde9a977 --- /dev/null +++ b/storage/database/seed.import-test.json @@ -0,0 +1,72 @@ +{ + "users": [ + { + "email": "thegrumpydictator@gmail.com", + "password": "james" + } + ], + "roles": [ + { + "user_id": 1, + "role": 1 + } + ], + "accounts": [], + "account-meta": [], + "bills": [], + "budgets": [], + "budget-limits": [], + "monthly-limits": [], + "categories": [], + "piggy-banks": [], + "piggy-events": [], + "rule-groups": [], + "rules": [], + "rule-triggers": [], + "rule-actions": [], + "tags": [], + "monthly-deposits": [], + "monthly-transfers": [], + "monthly-withdrawals": [], + "attachments": [], + "multi-withdrawals": [], + "multi-deposits": [], + "multi-transfers": [], + "import-jobs": [ + { + "user_id": 1, + "key": "testImport", + "file_type": "csv", + "status": "settings_complete", + "configuration": { + "has-headers": false, + "date-format": "Ymd", + "delimiter": ",", + "import-account": 0, + "specifics": [], + "column-count": 7, + "column-roles": [ + "account-name", + "opposing-name", + "amount", + "date-transaction", + "category-name", + "budget-name", + "description" + ], + "column-do-mapping": [ + false, + false, + false, + false, + false, + false, + false + ], + "column-roles-complete": false, + "column-mapping-config": [], + "column-mapping-complete": false + } + } + ] +} \ No newline at end of file diff --git a/storage/database/seed.local.json b/storage/database/seed.local.json index 0a22dcf358..463b1119ff 100644 --- a/storage/database/seed.local.json +++ b/storage/database/seed.local.json @@ -1006,5 +1006,6 @@ 9 ] } - ] + ], + "import-jobs": [] } \ No newline at end of file diff --git a/storage/database/seed.split.json b/storage/database/seed.split.json index aa276339e7..2a60692705 100644 --- a/storage/database/seed.split.json +++ b/storage/database/seed.split.json @@ -297,5 +297,6 @@ 3 ] } - ] + ], + "import-jobs": [] } \ No newline at end of file diff --git a/storage/database/seed.testing.json b/storage/database/seed.testing.json new file mode 100644 index 0000000000..5dbaaaf5e9 --- /dev/null +++ b/storage/database/seed.testing.json @@ -0,0 +1,1011 @@ +{ + "users": [ + { + "email": "thegrumpydictator@gmail.com", + "password": "james" + }, + { + "email": "thegrumpydictator+empty@gmail.com", + "password": "james" + }, + { + "email": "thegrumpydictator+deleteme@gmail.com", + "password": "james" + } + ], + "roles": [ + { + "user_id": 1, + "role": 1 + } + ], + "accounts": [ + { + "user_id": 1, + "account_type_id": 3, + "name": "Checking Account", + "iban": "NL11XOLA6707795988" + }, + { + "user_id": 1, + "account_type_id": 3, + "name": "Alternate Checking Account", + "iban": "NL40UKBK3619908726" + }, + { + "user_id": 1, + "account_type_id": 3, + "name": "Savings Account", + "iban": "NL96DZCO4665940223" + }, + { + "user_id": 1, + "account_type_id": 3, + "name": "Shared Checking Account", + "iban": "NL81RCQZ7160379858" + }, + { + "user_id": 1, + "account_type_id": 3, + "name": "Emergency Savings Account", + "iban": "NL38SRMN4325934708" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Adobe" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Google" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Vitens" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Albert Heijn" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "PLUS" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Bakker" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Belastingdienst" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "bol.com" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Cafe Central" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "conrad.nl" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Coolblue" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Shell" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "SixtyFive" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "EightyFour" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Fiftyone" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "DUO" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Etos" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "FEBO" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Greenchoice" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Halfords" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "XS4All" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "iCentre" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Jumper" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "Land lord" + }, + { + "user_id": 1, + "account_type_id": 5, + "name": "Job" + }, + { + "user_id": 1, + "account_type_id": 5, + "name": "Belastingdienst" + }, + { + "user_id": 1, + "account_type_id": 5, + "name": "Bank" + }, + { + "user_id": 1, + "account_type_id": 5, + "name": "KPN" + }, + { + "user_id": 1, + "account_type_id": 5, + "name": "Google" + }, + { + "user_id": 1, + "account_type_id": 5, + "name": "Work SixtyFive" + }, + { + "user_id": 1, + "account_type_id": 5, + "name": "Work EightyFour" + }, + { + "user_id": 1, + "account_type_id": 5, + "name": "Work Fiftyone" + }, + { + "user_id": 1, + "account_type_id": 6, + "name": "Opposing for Savings Account" + }, + { + "user_id": 1, + "account_type_id": 6, + "name": "Opposing for Emergency Savings Account" + } + ], + "account-meta": [ + { + "account_id": 1, + "name": "accountRole", + "data": "\"defaultAsset\"" + }, + { + "account_id": 2, + "name": "accountRole", + "data": "\"defaultAsset\"" + }, + { + "account_id": 3, + "name": "accountRole", + "data": "\"savingAsset\"" + }, + { + "account_id": 4, + "name": "accountRole", + "data": "\"sharedAsset\"" + } + ], + "bills": [ + { + "name": "Rent", + "match": "rent,land,lord", + "amount_min": 795, + "amount_max": 805, + "user_id": 1, + "date": "2015-01-01", + "active": 1, + "automatch": 1, + "repeat_freq": "monthly", + "skip": 0 + }, + { + "name": "Health insurance", + "match": "insurer,insurance,health", + "amount_min": 120, + "amount_max": 140, + "user_id": 1, + "date": "2015-01-01", + "active": 1, + "automatch": 1, + "repeat_freq": "monthly", + "skip": 0 + } + ], + "budgets": [ + { + "name": "Groceries", + "user_id": 1 + }, + { + "name": "Bills", + "user_id": 1 + }, + { + "name": "Car", + "user_id": 1 + }, + { + "name": "One Empty Budget", + "user_id": 1 + }, + { + "name": "Another Empty Budget", + "user_id": 1 + }, + { + "name": "Going out", + "user_id": 1 + }, + { + "name": "Multi budget A", + "user_id": 1 + }, + { + "name": "Multi budget B", + "user_id": 1 + }, + { + "name": "Multi budget C", + "user_id": 1 + } + ], + "budget-limits": [ + { + "budget_id": 1, + "startdate": "2016-04-01", + "amount_min": 100, + "amount_max": 200, + "repeat_freq": "daily" + }, + { + "budget_id": 1, + "startdate": "2016-05-01", + "amount_min": 100, + "amount_max": 200, + "repeat_freq": "weekly" + }, + { + "budget_id": 1, + "startdate": "2016-06-01", + "amount_min": 100, + "amount_max": 200, + "repeat_freq": "monthly" + }, + { + "budget_id": 1, + "startdate": "2016-07-01", + "amount_min": 100, + "amount_max": 200, + "repeat_freq": "quarterly" + }, + { + "budget_id": 1, + "startdate": "2016-08-01", + "amount_min": 100, + "amount_max": 200, + "repeat_freq": "half-year" + }, + { + "budget_id": 1, + "startdate": "2016-09-01", + "amount_min": 100, + "amount_max": 200, + "repeat_freq": "yearly" + } + ], + "monthly-limits": [ + { + "budget_id": 1, + "amount_min": 200, + "amount_max": 200 + }, + { + "budget_id": 2, + "amount_min": 1000, + "amount_max": 1000 + }, + { + "budget_id": 3, + "amount_min": 200, + "amount_max": 200 + }, + { + "budget_id": 6, + "amount_min": 100, + "amount_max": 100 + } + ], + "categories": [ + { + "name": "Daily groceries", + "user_id": 1 + }, + { + "name": "Car", + "user_id": 1 + }, + { + "name": "Reimbursements", + "user_id": 1 + }, + { + "name": "Salary", + "user_id": 1 + }, + { + "name": "Bills", + "user_id": 1 + }, + { + "name": "Going out", + "user_id": 1 + }, + { + "name": "Multi category A", + "user_id": 1 + }, + { + "name": "Multi category B", + "user_id": 1 + }, + { + "name": "Multi category C", + "user_id": 1 + } + ], + "piggy-banks": [ + { + "account_id": 3, + "name": "New camera", + "targetamount": 1000, + "startdate": "2015-04-01", + "reminder_skip": 0, + "remind_me": 0, + "order": 1, + "currentamount": 735 + }, + { + "account_id": 3, + "name": "New phone", + "targetamount": 600, + "startdate": "2015-04-01", + "reminder_skip": 0, + "remind_me": 0, + "order": 2, + "currentamount": 333 + }, + { + "account_id": 3, + "name": "New couch", + "targetamount": 500, + "startdate": "2015-04-01", + "reminder_skip": 0, + "remind_me": 0, + "order": 3, + "currentamount": 120 + } + ], + "piggy-events": [ + { + "piggy_bank_id": 1, + "date": "2015-05-01", + "amount": 245 + }, + { + "piggy_bank_id": 1, + "date": "2015-06-02", + "amount": 245 + }, + { + "piggy_bank_id": 1, + "date": "2015-07-03", + "amount": 245 + }, + { + "piggy_bank_id": 2, + "date": "2015-08-04", + "amount": 111 + }, + { + "piggy_bank_id": 2, + "date": "2015-09-05", + "amount": 111 + }, + { + "piggy_bank_id": 2, + "date": "2015-10-06", + "amount": 111 + }, + { + "piggy_bank_id": 3, + "date": "2015-11-07", + "amount": 40 + }, + { + "piggy_bank_id": 3, + "date": "2015-12-08", + "amount": 40 + }, + { + "piggy_bank_id": 3, + "date": "2016-01-09", + "amount": 40 + } + ], + "rule-groups": [ + { + "user_id": 1, + "order": 1, + "title": "Default rules", + "description": "All your rules not in a particular group." + } + ], + "rules": [ + { + "user_id": 1, + "rule_group_id": 1, + "order": 1, + "active": 1, + "stop_processing": 0, + "title": "Your first default rule", + "description": "This rule is an example. You can safely delete it." + } + ], + "rule-triggers": [ + { + "rule_id": 1, + "order": 1, + "active": 1, + "stop_processing": 0, + "trigger_type": "user_action", + "trigger_value": "store-journal" + }, + { + "rule_id": 1, + "order": 2, + "active": 1, + "stop_processing": 0, + "trigger_type": "description_is", + "trigger_value": "The Man Who Sold the World" + }, + { + "rule_id": 1, + "order": 3, + "active": 1, + "stop_processing": 0, + "trigger_type": "from_account_is", + "trigger_value": "David Bowie" + } + ], + "rule-actions": [ + { + "rule_id": 1, + "order": 1, + "active": 1, + "stop_processing": 0, + "action_type": "prepend_description", + "action_value": "Bought the world from " + }, + { + "rule_id": 1, + "order": 2, + "active": 1, + "stop_processing": 0, + "action_type": "set_category", + "action_value": "Large expenses" + } + ], + "tags": [ + { + "user_id": 1, + "tag": "TagJanuary", + "tagMode": "nothing", + "date": "2015-01-01" + }, + { + "user_id": 1, + "tag": "TagFebruary", + "tagMode": "nothing", + "date": "2015-02-02" + }, + { + "user_id": 1, + "tag": "TagMarch", + "tagMode": "nothing", + "date": "2015-03-03" + }, + { + "user_id": 1, + "tag": "TagApril", + "tagMode": "nothing", + "date": "2015-04-04" + }, + { + "user_id": 1, + "tag": "TagMay", + "tagMode": "nothing", + "date": "2015-05-05" + }, + { + "user_id": 1, + "tag": "TagJune", + "tagMode": "nothing", + "date": "2015-06-06" + }, + { + "user_id": 1, + "tag": "TagJuly", + "tagMode": "nothing", + "date": "2015-07-07" + }, + { + "user_id": 1, + "tag": "TagAugust", + "tagMode": "nothing", + "date": "2015-08-08" + }, + { + "user_id": 1, + "tag": "TagSeptember", + "tagMode": "nothing", + "date": "2015-09-09" + }, + { + "user_id": 1, + "tag": "TagOctober", + "tagMode": "nothing", + "date": "2015-10-10" + }, + { + "user_id": 1, + "tag": "TagNovember", + "tagMode": "nothing", + "date": "2015-11-11" + }, + { + "user_id": 1, + "tag": "TagDecember", + "tagMode": "nothing", + "date": "2015-12-12" + } + ], + "monthly-deposits": [ + { + "user_id": 1, + "day-of-month": 24, + "description": "Salary in :month", + "source_id": 30, + "destination_id": 1, + "min_amount": 1500, + "max_amount": 1700, + "category_id": 4 + } + ], + "monthly-transfers": [ + { + "user_id": 1, + "day-of-month": 28, + "description": "Saving money for :month", + "source_id": 1, + "destination_id": 3, + "min_amount": 150, + "max_amount": 150 + } + ], + "monthly-withdrawals": [ + { + "user_id": 1, + "day-of-month": "02", + "description": "Rent for :month", + "source_id": 1, + "destination_id": 29, + "min_amount": 800, + "max_amount": 800, + "category_id": 5, + "budget_id": 2 + }, + { + "user_id": 1, + "day-of-month": "04", + "description": "Water bill :month", + "source_id": 1, + "destination_id": 8, + "min_amount": 8, + "max_amount": 12, + "category_id": 5, + "budget_id": 2 + }, + { + "user_id": 1, + "day-of-month": "06", + "description": "TV bill :month", + "source_id": 1, + "destination_id": 26, + "min_amount": 50, + "max_amount": 60, + "category_id": 5, + "budget_id": 2 + }, + { + "user_id": 1, + "day-of-month": "08", + "description": "Power bill :month", + "source_id": 1, + "destination_id": 24, + "min_amount": 75, + "max_amount": 90, + "category_id": 5, + "budget_id": 2 + }, + { + "user_id": 1, + "day-of-month": "07", + "description": "Bought gas", + "source_id": 1, + "destination_id": 17, + "min_amount": 40, + "max_amount": 50, + "category_id": 2, + "budget_id": 3 + }, + { + "user_id": 1, + "day-of-month": 17, + "description": "Filled the car up again", + "source_id": 1, + "destination_id": 17, + "min_amount": 40, + "max_amount": 50, + "category_id": 2, + "budget_id": 3 + }, + { + "user_id": 1, + "day-of-month": 27, + "description": "Needed gas again", + "source_id": 1, + "destination_id": 17, + "min_amount": 45, + "max_amount": 55, + "category_id": 2, + "budget_id": 3 + }, + { + "user_id": 1, + "day-of-month": "02", + "description": "Groceries", + "source_id": 1, + "destination_id": 9, + "min_amount": 15, + "max_amount": 25, + "category_id": 1, + "budget_id": 1 + }, + { + "user_id": 1, + "day-of-month": "06", + "description": "Groceries", + "source_id": 1, + "destination_id": 10, + "min_amount": 15, + "max_amount": 25, + "category_id": 1, + "budget_id": 1 + }, + { + "user_id": 1, + "day-of-month": "08", + "description": "Groceries", + "source_id": 1, + "destination_id": 11, + "min_amount": 15, + "max_amount": 25, + "category_id": 1, + "budget_id": 1 + }, + { + "user_id": 1, + "day-of-month": 11, + "description": "Groceries", + "source_id": 1, + "destination_id": 9, + "min_amount": 15, + "max_amount": 25, + "category_id": 1, + "budget_id": 1 + }, + { + "user_id": 1, + "day-of-month": 15, + "description": "Groceries", + "source_id": 1, + "destination_id": 10, + "min_amount": 15, + "max_amount": 25, + "category_id": 1, + "budget_id": 1 + }, + { + "user_id": 1, + "day-of-month": 19, + "description": "Groceries", + "source_id": 1, + "destination_id": 11, + "min_amount": 15, + "max_amount": 25, + "category_id": 1, + "budget_id": 1 + }, + { + "user_id": 1, + "day-of-month": 23, + "description": "Groceries", + "source_id": 1, + "destination_id": 9, + "min_amount": 15, + "max_amount": 25, + "category_id": 1, + "budget_id": 1 + }, + { + "user_id": 1, + "day-of-month": 26, + "description": "Groceries", + "source_id": 1, + "destination_id": 10, + "min_amount": 15, + "max_amount": 25, + "category_id": 1, + "budget_id": 1 + }, + { + "user_id": 1, + "day-of-month": 13, + "description": "Going out for drinks", + "source_id": 1, + "destination_id": 14, + "min_amount": 15, + "max_amount": 36, + "category_id": 6, + "budget_id": 6 + }, + { + "user_id": 1, + "day-of-month": 26, + "description": "Going out for drinks again", + "source_id": 1, + "destination_id": 14, + "min_amount": 15, + "max_amount": 36, + "category_id": 6, + "budget_id": 6 + } + ], + "attachments": [ + { + "attachable_id": 1, + "attachable_type": "FireflyIII\\Models\\TransactionJournal", + "user_id": 1, + "content": "This is attachment number one.", + "filename": "empty-file.txt", + "title": "Empty file", + "description": "This file is empty", + "notes": "Some notes", + "mime": "text\/plain", + "uploaded": 1 + }, + { + "attachable_id": 2, + "attachable_type": "FireflyIII\\Models\\TransactionJournal", + "user_id": 1, + "content": "This is attachment number two.", + "filename": "empty-file2.txt", + "title": "Empty file", + "description": "This file is empty", + "notes": "Some notes", + "mime": "text\/plain", + "uploaded": 1 + } + ], + "multi-withdrawals": [ + { + "user_id": 1, + "date": "2016-03-12", + "description": "Even multi-withdrawal (50, 50)", + "destination_ids": [ + 18, + 19 + ], + "source_id": 1, + "amounts": [ + 50, + 50 + ], + "category_ids": [ + 7, + 8, + 9 + ], + "budget_ids": [ + 7, + 8, + 9 + ] + }, + { + "user_id": 1, + "date": "2016-05-12", + "description": "Uneven multi-withdrawal (15,34,51)", + "destination_ids": [ + 18, + 19, + 20 + ], + "source_id": 1, + "amounts": [ + 14, + 35, + 51 + ], + "category_ids": [ + 7, + 8, + 9 + ], + "budget_ids": [ + 7, + 8, + 9 + ] + } + ], + "multi-deposits": [ + { + "user_id": 1, + "date": "2016-03-02", + "description": "Even multi-deposit (50, 50)", + "source_ids": [ + 35, + 36 + ], + "destination_id": 1, + "amounts": [ + 50, + 50 + ], + "category_ids": [ + 7, + 8, + 9 + ] + }, + { + "user_id": 1, + "date": "2016-05-02", + "description": "Uneven multi-deposit (15,34,51)", + "source_ids": [ + 35, + 36, + 37 + ], + "destination_id": 1, + "amounts": [ + 14, + 35, + 51 + ], + "category_ids": [ + 7, + 8, + 9 + ] + } + ], + "multi-transfers": [ + { + "user_id": 1, + "date": "2016-03-02", + "description": "Even multi-transfer (50, 50)", + "source_ids": [ + 4, + 4 + ], + "destination_ids": [ + 5, + 5 + ], + "amounts": [ + 50, + 50 + ], + "category_ids": [ + 7, + 8 + ] + }, + { + "user_id": 1, + "date": "2016-05-02", + "description": "Uneven multi-transfer (15,34,51)", + "source_ids": [ + 4, + 4, + 4 + ], + "destination_ids": [ + 5, + 5, + 5 + ], + "amounts": [ + 14, + 35, + 51 + ], + "category_ids": [ + 7, + 8, + 9 + ] + } + ], + "import-jobs": [] +} \ No newline at end of file From 0aaf9a6fdaccf77bf555b8c3938c91c1af296aba Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 11 Aug 2016 18:44:11 +0200 Subject: [PATCH 79/94] Extend rule set for import. --- .gitignore | 1 + app/Console/Commands/Import.php | 2 + app/Crud/Account/AccountCrud.php | 12 +- app/Import/Converter/AssetAccountIban.php | 12 +- app/Import/Converter/AssetAccountName.php | 8 + app/Import/Converter/AssetAccountNumber.php | 10 +- app/Import/Converter/BillId.php | 1 + app/Import/Converter/BillName.php | 7 + app/Import/Converter/BudgetId.php | 3 + app/Import/Converter/CategoryId.php | 3 + app/Import/Converter/CurrencyId.php | 3 + app/Import/Converter/Date.php | 7 +- app/Import/Converter/OpposingAccountIban.php | 2 +- app/Import/Converter/OpposingAccountName.php | 9 +- app/Import/ImportEntry.php | 6 +- app/Import/ImportStorage.php | 41 +++-- app/Import/ImportValidator.php | 18 ++- app/Import/Importer/CsvImporter.php | 5 +- app/Import/Logging/CommandHandler.php | 38 ++++- app/Models/Account.php | 3 +- app/Models/Bill.php | 6 +- app/Models/Budget.php | 4 +- app/Models/Category.php | 4 +- app/Models/Tag.php | 4 + app/Models/TransactionCurrency.php | 22 +-- .../Category/CategoryRepository.php | 6 + app/Support/Migration/TestData.php | 24 ++- database/seeds/TestDataSeeder.php | 9 +- storage/database/seed.import-test.json | 142 ++++++++++++++++-- storage/database/seed.local.json | 3 +- storage/database/seed.split.json | 3 +- storage/database/seed.testing.json | 3 +- 32 files changed, 349 insertions(+), 72 deletions(-) diff --git a/.gitignore b/.gitignore index 3285ef1ed2..485afe93a2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ _development .env.local result.html test-import.sh +test-import-report.txt diff --git a/app/Console/Commands/Import.php b/app/Console/Commands/Import.php index bd02d71384..7f92e063ab 100644 --- a/app/Console/Commands/Import.php +++ b/app/Console/Commands/Import.php @@ -19,6 +19,7 @@ use FireflyIII\Import\Logging\CommandHandler; use FireflyIII\Models\ImportJob; use Illuminate\Console\Command; use Log; +use Monolog\Handler\StreamHandler; /** * Class Import @@ -80,6 +81,7 @@ class Import extends Command // intercept logging by importer. $monolog = Log::getMonolog(); $handler = new CommandHandler($this); + $monolog->pushHandler($handler); // create import entries diff --git a/app/Crud/Account/AccountCrud.php b/app/Crud/Account/AccountCrud.php index 787ced7bbf..552d7e0f5f 100644 --- a/app/Crud/Account/AccountCrud.php +++ b/app/Crud/Account/AccountCrud.php @@ -158,10 +158,12 @@ class AccountCrud implements AccountCrudInterface /** @var Account $account */ foreach ($accounts as $account) { if ($account->name === $name) { - Log::debug('Account name is an exact match. ', ['db' => $account->name, 'source' => $name,'id' => $account->id]); + Log::debug('Account name is an exact match. ', ['db' => $account->name, 'source' => $name, 'id' => $account->id]); + + return $account; } } - Log::warning('Found nothing in findByName()', ['name' => $name, 'types' => $types]); + Log::debug('Found nothing in findByName()', ['name' => $name, 'types' => $types]); return new Account; } @@ -233,7 +235,7 @@ class AccountCrud implements AccountCrudInterface public function store(array $data): Account { $newAccount = $this->storeAccount($data); - if (!is_null($newAccount)) { + if (!is_null($newAccount->id)) { $this->storeMetadata($newAccount, $data); } @@ -327,8 +329,8 @@ class AccountCrud implements AccountCrudInterface ]; $existingAccount = Account::firstOrNullEncrypted($searchData); if (!$existingAccount) { - Log::error('Account create error: ' . $newAccount->getErrors()->toJson()); - abort(500); + Log::error('Account create error', $newAccount->getErrors()->toArray()); + return new Account; } $newAccount = $existingAccount; diff --git a/app/Import/Converter/AssetAccountIban.php b/app/Import/Converter/AssetAccountIban.php index 3cbe47bc91..a22022db3b 100644 --- a/app/Import/Converter/AssetAccountIban.php +++ b/app/Import/Converter/AssetAccountIban.php @@ -66,9 +66,17 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface $account = $repository->store( - ['name' => 'Account with IBAN ' . $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, - 'active' => true] + ['name' => 'Asset account with IBAN ' . $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, + 'active' => true, 'openingBalance' => 0] ); + + if (is_null($account->id)) { + $this->setCertainty(0); + Log::info('Could not store new asset account by IBAN', $account->getErrors()->toArray()); + + return new Account; + } + $this->setCertainty(100); return $account; diff --git a/app/Import/Converter/AssetAccountName.php b/app/Import/Converter/AssetAccountName.php index af293656be..a8e7ef27a6 100644 --- a/app/Import/Converter/AssetAccountName.php +++ b/app/Import/Converter/AssetAccountName.php @@ -68,6 +68,14 @@ class AssetAccountName extends BasicConverter implements ConverterInterface ['name' => $value, 'iban' => null, 'openingBalance' => 0, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, 'active' => true] ); + + if (is_null($account->id)) { + $this->setCertainty(0); + Log::info('Could not store new asset account by name', $account->getErrors()->toArray()); + + return new Account; + } + $this->setCertainty(100); Log::debug('Created new asset account ', ['name' => $account->name, 'id' => $account->id]); diff --git a/app/Import/Converter/AssetAccountNumber.php b/app/Import/Converter/AssetAccountNumber.php index cc478ca94a..241ebf0a3a 100644 --- a/app/Import/Converter/AssetAccountNumber.php +++ b/app/Import/Converter/AssetAccountNumber.php @@ -32,7 +32,7 @@ class AssetAccountNumber extends BasicConverter implements ConverterInterface public function convert($value) { $value = trim($value); - Log::debug('Going to convert using AssetAccountName', ['value' => $value]); + Log::debug('Going to convert using AssetAccountNumber', ['value' => $value]); if (strlen($value) === 0) { return new Account; @@ -77,6 +77,14 @@ class AssetAccountNumber extends BasicConverter implements ConverterInterface 'accountType' => 'asset', 'virtualBalance' => 0, 'accountNumber' => $value, 'active' => true] ); + + if (is_null($account->id)) { + $this->setCertainty(0); + Log::notice('Could not store new asset account by account number', $account->getErrors()->toArray()); + + return new Account; + } + $this->setCertainty(100); return $account; diff --git a/app/Import/Converter/BillId.php b/app/Import/Converter/BillId.php index 3d6a4cc67e..74ccedc513 100644 --- a/app/Import/Converter/BillId.php +++ b/app/Import/Converter/BillId.php @@ -63,6 +63,7 @@ class BillId extends BasicConverter implements ConverterInterface } // should not really happen. If the ID does not match FF, what is FF supposed to do? + Log::info(sprintf('Could not find bill with ID %d. Will return NULL', $value)); $this->setCertainty(0); diff --git a/app/Import/Converter/BillName.php b/app/Import/Converter/BillName.php index 9d06e13105..a159ab4a3a 100644 --- a/app/Import/Converter/BillName.php +++ b/app/Import/Converter/BillName.php @@ -76,6 +76,13 @@ class BillName extends BasicConverter implements ConverterInterface ] ); + if (is_null($bill->id)) { + $this->setCertainty(0); + Log::info('Could not store new bill by name', $bill->getErrors()->toArray()); + + return new Bill; + } + $this->setCertainty(100); return $bill; diff --git a/app/Import/Converter/BudgetId.php b/app/Import/Converter/BudgetId.php index 6e50fb0e20..7d18d58de4 100644 --- a/app/Import/Converter/BudgetId.php +++ b/app/Import/Converter/BudgetId.php @@ -63,6 +63,9 @@ class BudgetId extends BasicConverter implements ConverterInterface // should not really happen. If the ID does not match FF, what is FF supposed to do? $this->setCertainty(0); + + Log::info(sprintf('Could not find budget with ID %d. Will return NULL', $value)); + return new Budget; } diff --git a/app/Import/Converter/CategoryId.php b/app/Import/Converter/CategoryId.php index 03fefa2cd5..1a2d3fc45c 100644 --- a/app/Import/Converter/CategoryId.php +++ b/app/Import/Converter/CategoryId.php @@ -62,6 +62,9 @@ class CategoryId extends BasicConverter implements ConverterInterface // should not really happen. If the ID does not match FF, what is FF supposed to do? $this->setCertainty(0); + + Log::info(sprintf('Could not find category with ID %d. Will return NULL', $value)); + return new Category; } diff --git a/app/Import/Converter/CurrencyId.php b/app/Import/Converter/CurrencyId.php index 08b8b43775..2dd402dafe 100644 --- a/app/Import/Converter/CurrencyId.php +++ b/app/Import/Converter/CurrencyId.php @@ -61,6 +61,9 @@ class CurrencyId extends BasicConverter implements ConverterInterface } $this->setCertainty(0); // should not really happen. If the ID does not match FF, what is FF supposed to do? + + Log::info(sprintf('Could not find category with ID %d. Will return NULL', $value)); + return new TransactionCurrency; } diff --git a/app/Import/Converter/Date.php b/app/Import/Converter/Date.php index d9619631a2..134d141e10 100644 --- a/app/Import/Converter/Date.php +++ b/app/Import/Converter/Date.php @@ -37,9 +37,10 @@ class Date extends BasicConverter implements ConverterInterface try { $date = Carbon::createFromFormat($this->config['date-format'], $value); } catch (InvalidArgumentException $e) { - Log::critical($e->getMessage()); - Log::critical('Cannot convert this string using the given format.', ['value' => $value, 'format' => $this->config['date-format']]); - throw new FireflyException(sprintf('Cannot convert "%s" to a valid date using format "%s".', $value, $this->config['date-format'])); + Log::notice($e->getMessage()); + Log::notice('Cannot convert this string using the given format.', ['value' => $value, 'format' => $this->config['date-format']]); + $this->setCertainty(0); + return new Carbon; } Log::debug('Converted date', ['converted' => $date->toAtomString()]); $this->setCertainty(100); diff --git a/app/Import/Converter/OpposingAccountIban.php b/app/Import/Converter/OpposingAccountIban.php index 674134c43b..27487b4542 100644 --- a/app/Import/Converter/OpposingAccountIban.php +++ b/app/Import/Converter/OpposingAccountIban.php @@ -56,7 +56,7 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface $account = $repository->findByIban($value, []); if (!is_null($account->id)) { Log::debug('Found account by IBAN', ['id' => $account->id]); - Log::warning( + Log::notice( 'The match between IBAN and account is uncertain because the type of transactions may not have been determined.', ['id' => $account->id, 'iban' => $value] ); diff --git a/app/Import/Converter/OpposingAccountName.php b/app/Import/Converter/OpposingAccountName.php index b45a28cdb5..bf872bec82 100644 --- a/app/Import/Converter/OpposingAccountName.php +++ b/app/Import/Converter/OpposingAccountName.php @@ -47,6 +47,7 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface if (!is_null($account->id)) { Log::debug('Found account by ID', ['id' => $account->id]); $this->setCertainty(100); + return $account; } } @@ -55,11 +56,12 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface $account = $repository->findByName($value, []); if (!is_null($account->id)) { Log::debug('Found opposing account by name', ['id' => $account->id]); - Log::warning( + Log::info( 'The match between name and account is uncertain because the type of transactions may not have been determined.', ['id' => $account->id, 'name' => $value] ); $this->setCertainty(50); + return $account; } @@ -68,6 +70,11 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface 'openingBalance' => 0, ] ); + if (is_null($account->id)) { + $this->setCertainty(0); + + return new Account; + } $this->setCertainty(100); Log::debug('Created new opposing account ', ['name' => $account->name, 'id' => $account->id]); diff --git a/app/Import/ImportEntry.php b/app/Import/ImportEntry.php index 5d30621124..29a49effae 100644 --- a/app/Import/ImportEntry.php +++ b/app/Import/ImportEntry.php @@ -221,7 +221,7 @@ class ImportEntry return; } - Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field])); + Log::info(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field])); } @@ -239,7 +239,7 @@ class ImportEntry return; } - Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d).', $field, $certainty, $this->certain[$field])); + Log::info(sprintf('Will not set %s based on certainty %d (current certainty is %d).', $field, $certainty, $this->certain[$field])); } /** @@ -256,7 +256,7 @@ class ImportEntry return; } - Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field])); + Log::info(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field])); } } diff --git a/app/Import/ImportStorage.php b/app/Import/ImportStorage.php index a5c72a2ffe..0f2b45414b 100644 --- a/app/Import/ImportStorage.php +++ b/app/Import/ImportStorage.php @@ -57,9 +57,9 @@ class ImportStorage */ public function store() { - foreach ($this->entries as $entry) { - Log::debug('--- import store start ---'); - $this->storeSingle($entry); + foreach ($this->entries as $index => $entry) { + Log::debug(sprintf('--- import store start for row %d ---', $index)); + $this->storeSingle($index, $entry); } } @@ -80,19 +80,20 @@ class ImportStorage } /** + * @param int $index * @param ImportEntry $entry * * @throws FireflyException */ - private function storeSingle(ImportEntry $entry) + private function storeSingle(int $index, ImportEntry $entry) { if ($entry->valid === false) { - Log::error('Cannot import entry, because valid=false'); + Log::error(sprintf('Cannot import row %d, because valid=false', $index)); return; } - Log::debug('Going to store entry!'); + Log::debug(sprintf('Going to store row %d', $index)); $billId = is_null($entry->fields['bill']) ? null : $entry->fields['bill']->id; $journalData = [ 'user_id' => $entry->user->id, @@ -108,7 +109,12 @@ class ImportStorage ]; /** @var TransactionJournal $journal */ $journal = TransactionJournal::create($journalData); - $amount = $this->makePositive($entry->fields['amount']); + + foreach ($journal->getErrors()->all() as $err) { + Log::error($err); + } + + $amount = $this->makePositive($entry->fields['amount']); Log::debug('Created journal', ['id' => $journal->id]); @@ -154,13 +160,30 @@ class ImportStorage $one = Transaction::create($sourceData); $two = Transaction::create($destinationData); - Log::debug('Created transaction 1', ['id' => $one->id, 'account' => $one->account_id,'account_name' => $source->name]); - Log::debug('Created transaction 2', ['id' => $two->id, 'account' => $two->account_id,'account_name' => $destination->name]); + Log::debug('Created transaction 1', ['id' => $one->id, 'account' => $one->account_id, 'account_name' => $source->name]); + Log::debug('Created transaction 2', ['id' => $two->id, 'account' => $two->account_id, 'account_name' => $destination->name]); $journal->completed = 1; $journal->save(); // now attach budget and so on. + if (!is_null($entry->fields['budget']) && !is_null($entry->fields['budget']->id)) { + $journal->budgets()->save($entry->fields['budget']); + Log::debug('Attached budget', ['id' => $entry->fields['budget']->id, 'name' => $entry->fields['budget']->name]); + $journal->save(); + } + + if (!is_null($entry->fields['category']) && !is_null($entry->fields['category']->id)) { + $journal->categories()->save($entry->fields['category']); + Log::debug('Attached category', ['id' => $entry->fields['category']->id, 'name' => $entry->fields['category']->name]); + $journal->save(); + } + + if (!is_null($entry->fields['bill']) && !is_null($entry->fields['bill']->id)) { + $journal->bill()->associate($entry->fields['bill']); + Log::debug('Attached bill', ['id' => $entry->fields['bill']->id, 'name' => $entry->fields['bill']->name]); + $journal->save(); + } } diff --git a/app/Import/ImportValidator.php b/app/Import/ImportValidator.php index 527f407a8a..596252a10c 100644 --- a/app/Import/ImportValidator.php +++ b/app/Import/ImportValidator.php @@ -52,8 +52,8 @@ class ImportValidator { $newCollection = new Collection; /** @var ImportEntry $entry */ - foreach ($this->entries as $entry) { - Log::debug('--- import validator start ---'); + foreach ($this->entries as $index => $entry) { + Log::debug(sprintf('--- import validator start for row %d ---', $index)); /* * X Adds the date (today) if no date is present. * X Determins the types of accounts involved (asset, expense, revenue). @@ -64,14 +64,12 @@ class ImportValidator $entry = $this->checkAmount($entry); $entry = $this->setDate($entry); $entry = $this->setAssetAccount($entry); - Log::debug(sprintf('Opposing account is of type %s', $entry->fields['opposing-account']->accountType->type)); $entry = $this->setOpposingAccount($entry); - Log::debug(sprintf('Opposing account is of type %s', $entry->fields['opposing-account']->accountType->type)); $entry = $this->cleanDescription($entry); $entry = $this->setTransactionType($entry); $entry = $this->setTransactionCurrency($entry); - $newCollection->push($entry); + $newCollection->put($index, $entry); } return $newCollection; @@ -118,6 +116,7 @@ class ImportValidator */ private function cleanDescription(ImportEntry $entry): ImportEntry { + if (!isset($entry->fields['description'])) { Log::debug('Set empty transaction description because field was not set.'); $entry->fields['description'] = '(empty transaction description)'; @@ -130,6 +129,7 @@ class ImportValidator return $entry; } + $entry->fields['description'] = trim($entry->fields['description']); if (strlen($entry->fields['description']) == 0) { Log::debug('Set empty transaction description because field was empty.'); @@ -137,8 +137,7 @@ class ImportValidator return $entry; } - Log::debug('Transaction description is OK.'); - $entry->fields['description'] = trim($entry->fields['description']); + Log::debug('Transaction description is OK.', ['description' => $entry->fields['description']]); return $entry; } @@ -246,7 +245,7 @@ class ImportValidator */ private function setDate(ImportEntry $entry): ImportEntry { - if (is_null($entry->fields['date-transaction'])) { + if (is_null($entry->fields['date-transaction']) || $entry->certain['date-transaction'] == 0) { // empty date field? find alternative. $alternatives = ['date-book', 'date-interest', 'date-process']; foreach ($alternatives as $alternative) { @@ -263,6 +262,9 @@ class ImportValidator return $entry; } + + // confidence is zero? + Log::debug('Date-transaction is OK'); return $entry; diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index f2e630d5c7..38ec2d272c 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -51,11 +51,10 @@ class CsvImporter implements ImporterInterface Log::debug('----- import entry build start --'); Log::debug(sprintf('Now going to import row %d.', $index)); $importEntry = $this->importSingleRow($index, $row); - $collection->push($importEntry); + $collection->put($index, $importEntry); } } - Log::debug(sprintf('Collection contains %d entries', $collection->count())); - Log::debug('This call should be intercepted somehow.'); + Log::debug(sprintf('Import collection contains %d entries', $collection->count())); return $collection; } diff --git a/app/Import/Logging/CommandHandler.php b/app/Import/Logging/CommandHandler.php index d4f2021948..eeab715e22 100644 --- a/app/Import/Logging/CommandHandler.php +++ b/app/Import/Logging/CommandHandler.php @@ -13,6 +13,7 @@ namespace FireflyIII\Import\Logging; use Illuminate\Console\Command; use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Logger; /** * Class CommandHandler @@ -32,7 +33,9 @@ class CommandHandler extends AbstractProcessingHandler */ public function __construct(Command $command) { + parent::__construct(); $this->command = $command; + $this->changeLevel(env('LOG_LEVEL', 'debug')); } /** @@ -44,6 +47,39 @@ class CommandHandler extends AbstractProcessingHandler */ protected function write(array $record) { - $this->command->line((string) trim($record['formatted'])); + $this->command->line((string)trim($record['formatted'])); + } + + /** + * @param string $level + */ + private function changeLevel(string $level) + { + switch ($level) { + case 'debug': + $this->setLevel(Logger::DEBUG); + break; + case 'info': + $this->setLevel(Logger::INFO); + break; + case 'notice': + $this->setLevel(Logger::NOTICE); + break; + case 'warning': + $this->setLevel(Logger::WARNING); + break; + case 'error': + $this->setLevel(Logger::ERROR); + break; + case 'critical': + $this->setLevel(Logger::CRITICAL); + break; + case 'alert': + $this->setLevel(Logger::ALERT); + break; + case 'emergency': + $this->setLevel(Logger::EMERGENCY); + break; + } } } \ No newline at end of file diff --git a/app/Models/Account.php b/app/Models/Account.php index 2c8313c0b2..c06e5f3d51 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -79,8 +79,9 @@ class Account extends Model = [ 'user_id' => 'required|exists:users,id', 'account_type_id' => 'required|exists:account_types,id', - 'name' => 'required', + 'name' => 'required|between:1,200', 'active' => 'required|boolean', + 'iban' => 'between:1,50|iban', ]; /** @var bool */ private $joinedAccountTypes; diff --git a/app/Models/Bill.php b/app/Models/Bill.php index c3bec163d7..a833cd7c3f 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Watson\Validating\ValidatingTrait; /** * FireflyIII\Models\Bill @@ -58,17 +59,20 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereNameEncrypted($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereMatchEncrypted($value) * @mixin \Eloquent - * @property string $deleted_at + * @property string $deleted_at * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereDeletedAt($value) */ class Bill extends Model { + use ValidatingTrait; + protected $dates = ['created_at', 'updated_at', 'date']; protected $fillable = ['name', 'match', 'amount_min', 'match_encrypted', 'name_encrypted', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip', 'automatch', 'active',]; protected $hidden = ['amount_min_encrypted', 'amount_max_encrypted', 'name_encrypted', 'match_encrypted']; + protected $rules = ['name' => 'required|between:1,200',]; /** * @param Bill $value diff --git a/app/Models/Budget.php b/app/Models/Budget.php index d9ca98a14b..d33cdad51f 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Watson\Validating\ValidatingTrait; /** * FireflyIII\Models\Budget @@ -51,11 +52,12 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class Budget extends Model { - use SoftDeletes; + use SoftDeletes, ValidatingTrait; protected $dates = ['created_at', 'updated_at', 'deleted_at', 'startdate', 'enddate']; protected $fillable = ['user_id', 'name', 'active']; protected $hidden = ['encrypted']; + protected $rules = ['name' => 'required|between:1,200',]; /** * @param array $fields diff --git a/app/Models/Category.php b/app/Models/Category.php index db3a8172f4..d88d627ecb 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Watson\Validating\ValidatingTrait; /** * FireflyIII\Models\Category @@ -46,11 +47,12 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class Category extends Model { - use SoftDeletes; + use SoftDeletes, ValidatingTrait; protected $dates = ['created_at', 'updated_at', 'deleted_at']; protected $fillable = ['user_id', 'name']; protected $hidden = ['encrypted']; + protected $rules = ['name' => 'required|between:1,200',]; /** * @param array $fields diff --git a/app/Models/Tag.php b/app/Models/Tag.php index bfa9e17dee..2c5ddab13c 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -15,6 +15,7 @@ use Auth; use Crypt; use FireflyIII\Support\Models\TagSupport; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Watson\Validating\ValidatingTrait; /** * FireflyIII\Models\Tag @@ -52,6 +53,9 @@ class Tag extends TagSupport { protected $dates = ['created_at', 'updated_at', 'date']; protected $fillable = ['user_id', 'tag', 'date', 'description', 'longitude', 'latitude', 'zoomLevel', 'tagMode']; + protected $rules = ['tag' => 'required|between:1,200',]; + + use ValidatingTrait; /** * @param array $fields diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index 69d33955b6..84208e2a22 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -15,6 +15,7 @@ use Auth; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Watson\Validating\ValidatingTrait; /** * FireflyIII\Models\TransactionCurrency @@ -38,19 +39,12 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class TransactionCurrency extends Model { - use SoftDeletes; + use SoftDeletes, ValidatingTrait; - protected $fillable = ['name', 'code', 'symbol']; protected $dates = ['created_at', 'updated_at', 'deleted_at']; - - /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function transactionJournals() - { - return $this->hasMany('FireflyIII\Models\TransactionJournal'); - } + protected $fillable = ['name', 'code', 'symbol']; + protected $rules = ['name' => 'required|between:1,200', 'code' => 'required|between:3,3', 'symbol' => 'required|between:1,12']; /** * @param TransactionCurrency $currency @@ -64,4 +58,12 @@ class TransactionCurrency extends Model } throw new NotFoundHttpException; } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function transactionJournals() + { + return $this->hasMany('FireflyIII\Models\TransactionJournal'); + } } diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index e0a662b355..380578e174 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -444,6 +444,12 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function store(array $data): Category { + // TODO use validation, not this. + if (strlen($data['name']) > 200 || strlen($data['name']) === 0) { + + } + + $newCategory = Category::firstOrCreateEncrypted( [ 'user_id' => $data['user'], diff --git a/app/Support/Migration/TestData.php b/app/Support/Migration/TestData.php index d3e59f9cdd..5c87f63003 100644 --- a/app/Support/Migration/TestData.php +++ b/app/Support/Migration/TestData.php @@ -72,9 +72,9 @@ class TestData 'updated_at' => $this->time, 'user_id' => $account['user_id'], 'account_type_id' => $account['account_type_id'], - 'name' => Crypt::encrypt($account['name']), + 'name' => $account['name'], 'active' => 1, - 'encrypted' => 1, + 'encrypted' => 0, 'virtual_balance' => 0, 'iban' => isset($account['iban']) ? Crypt::encrypt($account['iban']) : null, ]; @@ -244,6 +244,25 @@ class TestData DB::table('categories')->insert($insert); } + /** + * + */ + private function createCurrencies() + { + $insert = []; + foreach ($this->data['currencies'] as $job) { + $insert[] = [ + 'created_at' => $this->time, + 'updated_at' => $this->time, + 'deleted_at' => null, + 'code' => $job['code'], + 'name' => $job['name'], + 'symbol' => $job['symbol'], + ]; + } + DB::table('transaction_currencies')->insert($insert); + } + /** * */ @@ -829,6 +848,7 @@ class TestData $this->createMultiDeposits(); $this->createMultiTransfers(); $this->createImportJobs(); + $this->createCurrencies(); } } diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index d4faa0e4f3..a0ccd5eee9 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -38,10 +38,15 @@ class TestDataSeeder extends Seeder if ($disk->exists($fileName)) { Log::debug('Now seeding ' . $fileName); $file = json_decode($disk->get($fileName), true); - // run the file: - TestData::run($file); + if (is_array($file)) { + // run the file: + TestData::run($file); + return; + } + Log::error('No valid data found (' . $fileName . ') for environment ' . $env); return; + } Log::info('No seed file (' . $fileName . ') for environment ' . $env); } diff --git a/storage/database/seed.import-test.json b/storage/database/seed.import-test.json index 47cde9a977..bbdd6fe38c 100644 --- a/storage/database/seed.import-test.json +++ b/storage/database/seed.import-test.json @@ -11,20 +11,78 @@ "role": 1 } ], - "accounts": [], - "account-meta": [], - "bills": [], - "budgets": [], + "accounts": [ + { + "user_id": 1, + "account_type_id": 3, + "name": "ExistingAssetAccount", + "iban": "NL62EXFK3945306779" + }, + { + "user_id": 1, + "account_type_id": 4, + "name": "ExistingOpposingAccount", + "iban": "NL79BGWN6303364632" + } + ], + "account-meta": [ + { + "account_id": 1, + "name": "accountNumber", + "data": "\"3945306779\"" + }, + { + "account_id": 2, + "name": "accountNumber", + "data": "\"6303364632\"" + } + ], + "bills": [ + { + "name": "ExistingBill", + "match": "ExistingBill", + "amount_min": 100, + "amount_max": 200, + "user_id": 1, + "date": "2015-01-01", + "active": 1, + "automatch": 1, + "repeat_freq": "monthly", + "skip": 0 + } + ], + "budgets": [ + { + "name": "ExistingBudget", + "user_id": 1 + } + ], "budget-limits": [], "monthly-limits": [], - "categories": [], + "categories": [ + { + "name": "ExistingCategory", + "user_id": 1 + } + ], "piggy-banks": [], "piggy-events": [], "rule-groups": [], "rules": [], "rule-triggers": [], "rule-actions": [], - "tags": [], + "tags": [ + { + "user_id": 1, + "tag": "ExistingTag", + "tagMode": "nothing" + }, + { + "user_id": 1, + "tag": "AnotherExistingTag", + "tagMode": "nothing" + } + ], "monthly-deposits": [], "monthly-transfers": [], "monthly-withdrawals": [], @@ -43,18 +101,67 @@ "date-format": "Ymd", "delimiter": ",", "import-account": 0, - "specifics": [], - "column-count": 7, + "specifics": { + "RabobankDescription": 1, + "AbnAmroDescription": 1 + }, + "column-count": 30, "column-roles": [ - "account-name", - "opposing-name", "amount", - "date-transaction", - "category-name", + "account-id", + "account-iban", + "account-name", + "opposing-number", + "bill-id", + "bill-name", + "budget-id", "budget-name", - "description" + "category-id", + "category-name", + "currency-code", + "currency-id", + "currency-symbol", + "currency-name", + "date-transaction", + "description", + "_ignore", + "ing-debet-credit", + "opposing-iban", + "opposing-id", + "opposing-name", + "opposing-number", + "rabo-debet-credit", + "tags-comma", + "tags-space", + "date-interest", + "date-book", + "date-process", + "external-id" ], "column-do-mapping": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, false, false, false, @@ -64,9 +171,16 @@ false ], "column-roles-complete": false, - "column-mapping-config": [], + "column-mapping-config": {}, "column-mapping-complete": false } } + ], + "currencies": [ + { + "name": "ExistingCurrency", + "symbol": "#", + "code": "EXI" + } ] } \ No newline at end of file diff --git a/storage/database/seed.local.json b/storage/database/seed.local.json index 463b1119ff..bd1d17d784 100644 --- a/storage/database/seed.local.json +++ b/storage/database/seed.local.json @@ -1007,5 +1007,6 @@ ] } ], - "import-jobs": [] + "import-jobs": [], + "currencies": [] } \ No newline at end of file diff --git a/storage/database/seed.split.json b/storage/database/seed.split.json index 2a60692705..38fa070988 100644 --- a/storage/database/seed.split.json +++ b/storage/database/seed.split.json @@ -298,5 +298,6 @@ ] } ], - "import-jobs": [] + "import-jobs": [], + "currencies": [] } \ No newline at end of file diff --git a/storage/database/seed.testing.json b/storage/database/seed.testing.json index 5dbaaaf5e9..847832ee07 100644 --- a/storage/database/seed.testing.json +++ b/storage/database/seed.testing.json @@ -1007,5 +1007,6 @@ ] } ], - "import-jobs": [] + "import-jobs": [], + "currencies": [] } \ No newline at end of file From 2111873bcf6b99459df676df83afb6c616f74fe7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 11 Aug 2016 19:01:23 +0200 Subject: [PATCH 80/94] Fixed a bug in tag creation. --- app/Import/Converter/TagsComma.php | 32 ++++++++++++++------------ app/Import/Converter/TagsSpace.php | 32 ++++++++++++++------------ app/Repositories/Tag/TagRepository.php | 6 ++--- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/app/Import/Converter/TagsComma.php b/app/Import/Converter/TagsComma.php index 63c8e3e85c..4ae1b10646 100644 --- a/app/Import/Converter/TagsComma.php +++ b/app/Import/Converter/TagsComma.php @@ -35,6 +35,7 @@ class TagsComma extends BasicConverter implements ConverterInterface if (strlen($value) === 0) { $this->setCertainty(0); + return new Collection; } $parts = array_unique(explode(',', $value)); @@ -63,22 +64,23 @@ class TagsComma extends BasicConverter implements ConverterInterface Log::debug('Found tag by name ', ['id' => $tag->id]); $set->push($tag); - continue; } - // create new tag - $tag = $repository->store( - [ - 'tag' => $part, - 'date' => null, - 'description' => $part, - 'latitude' => null, - 'longitude' => null, - 'zoomLevel' => null, - 'tagMode' => 'nothing', - ] - ); - Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); - $set->push($tag); + if (is_null($tag->id)) { + // create new tag + $tag = $repository->store( + [ + 'tag' => $part, + 'date' => null, + 'description' => $part, + 'latitude' => null, + 'longitude' => null, + 'zoomLevel' => null, + 'tagMode' => 'nothing', + ] + ); + Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); + $set->push($tag); + } } $this->setCertainty(100); diff --git a/app/Import/Converter/TagsSpace.php b/app/Import/Converter/TagsSpace.php index c5c5f9a505..6f61d192c6 100644 --- a/app/Import/Converter/TagsSpace.php +++ b/app/Import/Converter/TagsSpace.php @@ -35,6 +35,7 @@ class TagsSpace extends BasicConverter implements ConverterInterface if (strlen($value) === 0) { $this->setCertainty(0); + return new Collection; } $parts = array_unique(explode(' ', $value)); @@ -63,22 +64,23 @@ class TagsSpace extends BasicConverter implements ConverterInterface Log::debug('Found tag by name ', ['id' => $tag->id]); $set->push($tag); - continue; } - // create new tag - $tag = $repository->store( - [ - 'tag' => $part, - 'date' => null, - 'description' => $part, - 'latitude' => null, - 'longitude' => null, - 'zoomLevel' => null, - 'tagMode' => 'nothing', - ] - ); - Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); - $set->push($tag); + if (is_null($tag->id)) { + // create new tag + $tag = $repository->store( + [ + 'tag' => $part, + 'date' => null, + 'description' => $part, + 'latitude' => null, + 'longitude' => null, + 'zoomLevel' => null, + 'tagMode' => 'nothing', + ] + ); + Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); + $set->push($tag); + } } $this->setCertainty(100); diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index d83f9ce17f..a794d3f6f7 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -106,9 +106,9 @@ class TagRepository implements TagRepositoryInterface { $tags = $this->user->tags()->get(); /** @var Tag $tag */ - foreach ($tags as $tag) { - if ($tag->tag === $tag) { - return $tag; + foreach ($tags as $databaseTag) { + if ($databaseTag->tag === $tag) { + return $databaseTag; } } From 28962007c1c345d7f7c0fe00859590e711d8d24a Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 09:27:09 +0200 Subject: [PATCH 81/94] More code for new importer --- app/Import/Converter/CurrencyCode.php | 2 +- app/Import/Converter/Date.php | 2 +- app/Import/Converter/INGDebetCredit.php | 2 +- app/Import/Converter/OpposingAccountIban.php | 2 +- app/Import/Converter/OpposingAccountName.php | 5 +- app/Import/ImportEntry.php | 33 +++++++-- app/Import/Importer/CsvImporter.php | 59 ++++++++------- app/Import/Specifics/AbnAmroDescription.php | 10 +++ app/Import/Specifics/RabobankDescription.php | 34 ++++++++- app/Import/Specifics/SpecificInterface.php | 7 ++ storage/database/seed.import-test.json | 76 ++++++++------------ 11 files changed, 146 insertions(+), 86 deletions(-) diff --git a/app/Import/Converter/CurrencyCode.php b/app/Import/Converter/CurrencyCode.php index 9a448eacf5..3e78e80d80 100644 --- a/app/Import/Converter/CurrencyCode.php +++ b/app/Import/Converter/CurrencyCode.php @@ -30,7 +30,7 @@ class CurrencyCode extends BasicConverter implements ConverterInterface */ public function convert($value): TransactionCurrency { - Log::debug('Going to convert ', ['value' => $value]); + Log::debug('Going to convert currency code', ['value' => $value]); /** @var CurrencyRepositoryInterface $repository */ $repository = app(CurrencyRepositoryInterface::class); diff --git a/app/Import/Converter/Date.php b/app/Import/Converter/Date.php index 134d141e10..e5ccc28e57 100644 --- a/app/Import/Converter/Date.php +++ b/app/Import/Converter/Date.php @@ -32,7 +32,7 @@ class Date extends BasicConverter implements ConverterInterface */ public function convert($value): Carbon { - Log::debug('Going to convert ', ['value' => $value]); + Log::debug('Going to convert date', ['value' => $value]); Log::debug('Format: ', ['format' => $this->config['date-format']]); try { $date = Carbon::createFromFormat($this->config['date-format'], $value); diff --git a/app/Import/Converter/INGDebetCredit.php b/app/Import/Converter/INGDebetCredit.php index a6c4da9d69..378fad1130 100644 --- a/app/Import/Converter/INGDebetCredit.php +++ b/app/Import/Converter/INGDebetCredit.php @@ -29,7 +29,7 @@ class INGDebetCredit extends BasicConverter implements ConverterInterface */ public function convert($value) { - Log::debug('Going to convert ', ['value' => $value]); + Log::debug('Going to convert ing debet credit', ['value' => $value]); if ($value === 'Af') { Log::debug('Return -1'); diff --git a/app/Import/Converter/OpposingAccountIban.php b/app/Import/Converter/OpposingAccountIban.php index 27487b4542..bf227412da 100644 --- a/app/Import/Converter/OpposingAccountIban.php +++ b/app/Import/Converter/OpposingAccountIban.php @@ -31,7 +31,7 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface public function convert($value): Account { $value = trim($value); - Log::debug('Going to convert ', ['value' => $value]); + Log::debug('Going to convert opposing IBAN', ['value' => $value]); if (strlen($value) === 0) { $this->setCertainty(0); diff --git a/app/Import/Converter/OpposingAccountName.php b/app/Import/Converter/OpposingAccountName.php index bf872bec82..973a8b4da5 100644 --- a/app/Import/Converter/OpposingAccountName.php +++ b/app/Import/Converter/OpposingAccountName.php @@ -31,10 +31,11 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface public function convert($value): Account { $value = trim($value); - Log::debug('Going to convert ', ['value' => $value]); + Log::debug('Going to convert opposing account name', ['value' => $value]); if (strlen($value) === 0) { - $value = '(empty account name)'; + $this->setCertainty(0); + return new Account; } /** @var AccountCrudInterface $repository */ diff --git a/app/Import/ImportEntry.php b/app/Import/ImportEntry.php index 29a49effae..b982e806e0 100644 --- a/app/Import/ImportEntry.php +++ b/app/Import/ImportEntry.php @@ -27,12 +27,18 @@ class ImportEntry { /** @var array */ public $certain = []; + /** @var string */ + public $externalID; /** @var array */ public $fields = []; /** @var User */ public $user; /** @var bool */ public $valid = true; + + /** @var int */ + private $amountMultiplier = 0; + /** @var array */ private $validFields = ['amount', @@ -74,16 +80,13 @@ class ImportEntry /** * @param string $role - * @param string $value * @param int $certainty * @param $convertedValue * * @throws FireflyException */ - public function importValue(string $role, string $value, int $certainty, $convertedValue) + public function importValue(string $role, int $certainty, $convertedValue) { - Log::debug('Going to import', ['role' => $role, 'value' => $value, 'certainty' => $certainty]); - switch ($role) { default: Log::error('Import entry cannot handle object.', ['role' => $role]); @@ -96,6 +99,8 @@ class ImportEntry */ $this->setFloat('amount', $convertedValue, $certainty); + $this->applyMultiplier('amount'); // if present. + return; case 'account-id': case 'account-iban': @@ -139,6 +144,9 @@ class ImportEntry case 'date-process': $this->setDate('date-process', $convertedValue, $certainty); break; + case 'sepa-ct-id': + case 'sepa-db': + case 'sepa-ct-op': case'description': $this->setAppendableString('description', $convertedValue); break; @@ -147,12 +155,13 @@ class ImportEntry case 'ing-debet-credit': case 'rabo-debet-credit': $this->manipulateFloat('amount', 'multiply', $convertedValue); + $this->applyMultiplier('amount'); // if present. break; case 'tags-comma': case 'tags-space': $this->appendCollection('tags', $convertedValue); case 'external-id': - // ignore for now. + $this->externalID = $convertedValue; break; } @@ -178,6 +187,16 @@ class ImportEntry $this->fields[$field] = $this->fields[$field]->merge($convertedValue); } + /** + * @param string $field + */ + private function applyMultiplier(string $field) + { + if ($this->fields[$field] != 0 && $this->amountMultiplier != 0) { + $this->fields[$field] = $this->fields[$field] * $this->amountMultiplier; + } + } + /** * @param string $field * @param string $action @@ -191,8 +210,8 @@ class ImportEntry default: Log::error('Cannot handle manipulateFloat', ['field' => $field, 'action' => $action]); throw new FireflyException('Cannot manipulateFloat with action ' . $action); - case'multiply': - $this->fields[$field] = $this->fields[$field] * $convertedValue; + case 'multiply': + $this->amountMultiplier = $convertedValue; break; } } diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 38ec2d272c..ff9845dd5c 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -11,10 +11,9 @@ declare(strict_types = 1); namespace FireflyIII\Import\Importer; -use FireflyIII\Crud\Account\AccountCrud; use FireflyIII\Import\Converter\ConverterInterface; use FireflyIII\Import\ImportEntry; -use FireflyIII\Models\Account; +use FireflyIII\Import\Specifics\SpecificInterface; use FireflyIII\Models\ImportJob; use Illuminate\Support\Collection; use League\Csv\Reader; @@ -27,9 +26,19 @@ use Log; */ class CsvImporter implements ImporterInterface { + /** @var Collection */ + public $collection; /** @var ImportJob */ public $job; + /** + * CsvImporter constructor. + */ + public function __construct() + { + $this->collection = new Collection; + } + /** * Run the actual import * @@ -41,22 +50,21 @@ class CsvImporter implements ImporterInterface $content = $this->job->uploadFileContents(); // create CSV reader. - $reader = Reader::createFromString($content); + $reader = Reader::createFromString($content); $reader->setDelimiter($config['delimiter']); - $start = $config['has-headers'] ? 1 : 0; - $results = $reader->fetch(); - $collection = new Collection; + $start = $config['has-headers'] ? 1 : 0; + $results = $reader->fetch(); foreach ($results as $index => $row) { if ($index >= $start) { Log::debug('----- import entry build start --'); Log::debug(sprintf('Now going to import row %d.', $index)); $importEntry = $this->importSingleRow($index, $row); - $collection->put($index, $importEntry); + $this->collection->put($index, $importEntry); } } - Log::debug(sprintf('Import collection contains %d entries', $collection->count())); + Log::debug(sprintf('Import collection contains %d entries', $this->collection->count())); - return $collection; + return $this->collection; } /** @@ -75,19 +83,28 @@ class CsvImporter implements ImporterInterface */ private function importSingleRow(int $index, array $row): ImportEntry { - // create import object: + // create import object. This is where each entry ends up. $object = new ImportEntry; // set some vars: $object->setUser($this->job->user); $config = $this->job->configuration; - foreach ($row as $index => $value) { + // and this is the point where the specifix go to work. + foreach ($config['specifics'] as $name => $enabled) { + /** @var SpecificInterface $specific */ + $specific = app('FireflyIII\Import\Specifics\\' . $name); + + // it returns the row, possibly modified: + $row = $specific->run($row); + } + + foreach ($row as $rowIndex => $value) { // find the role for this column: - $role = $config['column-roles'][$index] ?? '_ignore'; - $doMap = $config['column-do-mapping'][$index] ?? false; + $role = $config['column-roles'][$rowIndex] ?? '_ignore'; + $doMap = $config['column-do-mapping'][$rowIndex] ?? false; $converterClass = config('csv.import_roles.' . $role . '.converter'); - $mapping = $config['column-mapping-config'][$index] ?? []; + $mapping = $config['column-mapping-config'][$rowIndex] ?? []; /** @var ConverterInterface $converter */ $converter = app('FireflyIII\\Import\\Converter\\' . $converterClass); // set some useful values for the converter: @@ -101,21 +118,15 @@ class CsvImporter implements ImporterInterface $certainty = $converter->getCertainty(); // log it. - Log::debug('Value ', ['index' => $index, 'value' => $value, 'role' => $role]); + Log::debug('Value ', ['index' => $rowIndex, 'value' => $value, 'role' => $role]); // store in import entry: - $object->importValue($role, $value, $certainty, $convertedValue); + Log::debug('Going to import', ['role' => $role, 'value' => $value, 'certainty' => $certainty]); + $object->importValue($role, $certainty, $convertedValue); } + return $object; - // $result = $object->import(); - // if ($result->failed()) { - // Log::error(sprintf('Import of row %d has failed.', $index), $result->errors->toArray()); - // } - // - // exit; - // - // return true; } } \ No newline at end of file diff --git a/app/Import/Specifics/AbnAmroDescription.php b/app/Import/Specifics/AbnAmroDescription.php index a3d3f058fa..f6787e11f5 100644 --- a/app/Import/Specifics/AbnAmroDescription.php +++ b/app/Import/Specifics/AbnAmroDescription.php @@ -34,4 +34,14 @@ class AbnAmroDescription implements SpecificInterface { return 'Fixes possible problems with ABN Amro descriptions.'; } + + /** + * @param array $row + * + * @return array + */ + public function run(array $row): array + { + return $row; + } } \ No newline at end of file diff --git a/app/Import/Specifics/RabobankDescription.php b/app/Import/Specifics/RabobankDescription.php index 09fe209d47..7372ba3dd0 100644 --- a/app/Import/Specifics/RabobankDescription.php +++ b/app/Import/Specifics/RabobankDescription.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace FireflyIII\Import\Specifics; +use Log; + /** * Class RabobankDescription * @@ -18,6 +20,14 @@ namespace FireflyIII\Import\Specifics; */ class RabobankDescription implements SpecificInterface { + /** + * @return string + */ + static public function getDescription(): string + { + return 'Fixes possible problems with Rabobank descriptions.'; + } + /** * @return string */ @@ -27,10 +37,28 @@ class RabobankDescription implements SpecificInterface } /** - * @return string + * @param array $row + * + * @return array */ - static public function getDescription(): string + public function run(array $row): array { - return 'Fixes possible problems with Rabobank descriptions.'; + $oppositeAccount = trim($row[5]); + $oppositeName = trim($row[6]); + $alternateName = trim($row[10]); + if (strlen($oppositeAccount) < 1 && strlen($oppositeName) < 1) { + Log::debug( + sprintf( + 'Rabobank specific: Opposite account and opposite name are' . + ' both empty. Will use "%s" (from description) instead', $alternateName + ) + ); + $row[6] = $alternateName; + $row[10] = ''; + } else { + Log::debug('Rabobank specific: either opposite account or name are filled.'); + } + + return $row; } } \ No newline at end of file diff --git a/app/Import/Specifics/SpecificInterface.php b/app/Import/Specifics/SpecificInterface.php index 52015ab01e..847e930af9 100644 --- a/app/Import/Specifics/SpecificInterface.php +++ b/app/Import/Specifics/SpecificInterface.php @@ -28,4 +28,11 @@ interface SpecificInterface */ static public function getDescription(): string; + /** + * @param array $row + * + * @return array + */ + public function run(array $row): array; + } \ No newline at end of file diff --git a/storage/database/seed.import-test.json b/storage/database/seed.import-test.json index bbdd6fe38c..20ea6708ec 100644 --- a/storage/database/seed.import-test.json +++ b/storage/database/seed.import-test.json @@ -100,63 +100,40 @@ "has-headers": false, "date-format": "Ymd", "delimiter": ",", - "import-account": 0, + "import-account": 1, "specifics": { - "RabobankDescription": 1, - "AbnAmroDescription": 1 + "RabobankDescription": 1 }, - "column-count": 30, + "column-count": 19, "column-roles": [ - "amount", - "account-id", "account-iban", - "account-name", - "opposing-number", - "bill-id", - "bill-name", - "budget-id", - "budget-name", - "category-id", - "category-name", "currency-code", - "currency-id", - "currency-symbol", - "currency-name", - "date-transaction", + "date-interest", + "rabo-debet-credit", + "amount", + "opposing-iban", + "opposing-name", + "date-book", "description", "_ignore", - "ing-debet-credit", - "opposing-iban", - "opposing-id", - "opposing-name", - "opposing-number", - "rabo-debet-credit", - "tags-comma", - "tags-space", - "date-interest", - "date-book", - "date-process", - "external-id" + "description", + "description", + "description", + "description", + "description", + "description", + "sepa-ct-id", + "sepa-ct-op", + "sepa-db" ], "column-do-mapping": [ + true, + true, false, false, false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, + true, + true, false, false, false, @@ -171,7 +148,14 @@ false ], "column-roles-complete": false, - "column-mapping-config": {}, + "column-mapping-config": { + "0": [], + "1": { + "EUR": 1 + }, + "5": [], + "6": [] + }, "column-mapping-complete": false } } From 4d7fa11172ba68508e8de546029e924f76da60eb Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 09:35:09 +0200 Subject: [PATCH 82/94] Upgrade instructions for 3.10 --- config/upgrade.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/upgrade.php b/config/upgrade.php index 27059b92db..d12d96b1c3 100644 --- a/config/upgrade.php +++ b/config/upgrade.php @@ -27,5 +27,6 @@ return [ 'Please follow the instructions on the following page: https://github.com/JC5/firefly-iii/wiki/Upgrade-to-3.7.0', '3.8.0' => 'This version of Firefly III requires PHP 7.0.', '3.8.1' => 'This version of Firefly III requires PHP 7.0.', + '3.10' => 'Please find the full upgrade instructions here: https://github.com/JC5/firefly-iii/wiki/Upgrade-to-3.10' ], ]; From 5e310776b4e530544814e3414ec98c1fff099574 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 09:50:54 +0200 Subject: [PATCH 83/94] New change log --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3ddc1bd4d..6bc2c8ef1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,44 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - No unreleased changes yet. +## [3.10] - 2015-05-25 +### Added +- New charts in year report +- Can add / remove money from piggy bank on mobile device. +- Bill overview shows some useful things. +- Firefly will track registration / activation IP addresses. + + +### Changed +- Rewrote the import routine. +- The date picker now supports more ranges and periods. +- Rewrote all migrations. #272 + + +### Deprecated +- Initial release. + +### Removed +- Initial release. + +### Fixed +- Issue #264 +- Issue #265 +- Fixed amount calculation problems, #266, thanks @xzaz +- Issue #271 +- Issue #278, #273, thanks @StevenReitsma and @rubella +- Bug in attachment download routine would report the wrong size to the user's browser. +- Various NULL errors fixed. +- Various strict typing errors fixed. +- Fixed pagination problems, #276, thanks @xzaz +- Fixed a bug where an expense would be assigned to a piggy bank if you created a transfer first. +- Bulk update problems, #280, thanks @stickgrinder +- Fixed various problems with amount reporting of split transactions. + +### Security +- Initial release. + + [3.9.1] ### Fixed - Fixed a bug where removing money from a piggy bank would not work. See issue #265 and #269 From 76649cb7de9da8eb5f562fd3d16fd053186eb09e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 10:07:53 +0200 Subject: [PATCH 84/94] Extended views and language. --- app/Http/Controllers/ImportController.php | 4 ++-- resources/lang/en_US/csv.php | 8 ++++---- resources/lang/en_US/firefly.php | 16 +++++++--------- resources/views/import/complete.twig | 2 +- resources/views/import/csv/roles.twig | 2 +- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 03a39c2f49..d216aa5eb1 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -37,7 +37,7 @@ class ImportController extends Controller { parent::__construct(); View::share('mainTitleIcon', 'fa-archive'); - View::share('title', trans('firefly.import_data')); + View::share('title', trans('firefly.import_data_full')); } /** @@ -215,7 +215,7 @@ class ImportController extends Controller } Log::debug('Continue in settings()'); $importer = $this->makeImporter($job); - $subTitle = trans('firefy.settings_for_import'); + $subTitle = trans('firefly.settings_for_import'); $subTitleIcon = 'fa-wrench'; // now show settings screen to user. diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index 231c3c54fc..858026dad6 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -12,7 +12,7 @@ declare(strict_types = 1); return [ 'import_configure_title' => 'Configure your import', - 'import_configure_intro' => 'There are some options for your CSV import.', + 'import_configure_intro' => 'There are some options for your CSV import. Please indicate if your CSV file contains headers on the first column, and what the date format of your date-fields is. That might require some experimentation. The field delimiter is usually a ",", but could also be a ";". Check this carefully.', 'import_configure_form' => 'Form', 'header_help' => 'Check this if the first row of your CSV file are the column titles', 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', @@ -23,7 +23,7 @@ return [ // roles 'column_roles_title' => 'Define column roles', - 'column_roles_text' => 'Each column contains some data. What data?', + 'column_roles_text' => '

    Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

    Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

    ', 'column_roles_table' => 'Table', 'column_name' => 'Name of column', 'column_example' => 'Column example data', @@ -33,8 +33,8 @@ return [ 'no_example_data' => 'No example data available', 'store_column_roles' => 'Continue import', 'do_not_map' => '(do not map)', - 'map_title' => 'Connect data in your files', - 'map_text' => 'Connect data in your files', + 'map_title' => 'Connect import data to Firefly III data', + 'map_text' => 'In the following tables, the left value shows you information found in your uploaded CSV file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', 'field_value' => 'Field value', 'field_mapped_to' => 'Mapped to', diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index ee4fee636f..08238377e7 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -326,13 +326,6 @@ return [ 'title_transfer' => 'Transfers', 'title_transfers' => 'Transfers', - // import routine - 'import_data' => 'Import data', - 'import' => 'Import', - 'import_intro_text' => 'Some intro here', - 'import_file_help' => 'Select your file', - - // create new stuff: 'create_new_withdrawal' => 'Create new withdrawal', 'create_new_deposit' => 'Create new deposit', @@ -775,8 +768,13 @@ return [ 'import_finish_configuration' => 'Finish configuration', 'settings_for_import' => 'Settings', 'import_complete' => 'Import configuration complete!', - 'import_complete_text' => 'Download the config file. You can also run it from the command line.', + 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you need to execute the following command in your console. Unfortunately, a web-based import is not yet possible.', 'import_download_config' => 'Download configuration', 'import_start_import' => 'Start import', - 'import_intro_beta' => 'The import function is currently being developed and will most probably not work.', + 'import_intro_beta' => 'The import function of Firefly III is in beta. Many users of Firefly III have tried many different files. Although each individual compontent of this import routine works (really), the combination might break. If your file cannot be imported by Firefly, please read this wiki page so I can fix the problem you have run into.', + 'import_data' => 'Import data', + 'import_data_full' => 'Import data into Firefly III', + 'import' => 'Import', + 'import_intro_text' => 'Welcome to the Firefly III data import routine. At the moment, this routine can help you import files into Firefly. To do so, you must download or export transactions from other systems or software, and upload them here. The next steps will let you help Firefly III determin what the content is of your file, and how to handle it. Please select a file, and read all instructions carefully.', + 'import_file_help' => 'Select your file', ]; diff --git a/resources/views/import/complete.twig b/resources/views/import/complete.twig index e7e7a82629..2f9d96cb26 100644 --- a/resources/views/import/complete.twig +++ b/resources/views/import/complete.twig @@ -22,7 +22,7 @@ {{ 'import_download_config'|_ }}
    diff --git a/resources/views/import/csv/roles.twig b/resources/views/import/csv/roles.twig index e73f172e43..2886c62f25 100644 --- a/resources/views/import/csv/roles.twig +++ b/resources/views/import/csv/roles.twig @@ -13,7 +13,7 @@

    {{ trans('csv.column_roles_title') }}

    -

    {{ trans('csv.column_roles_text') }}

    +

    {{ trans('csv.column_roles_text')|raw }}

    From b9e1e013378cfdf69bb4c77e39f08852860592c4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 10:34:45 +0200 Subject: [PATCH 85/94] New translations. --- resources/lang/fr_FR/breadcrumbs.php | 10 +- resources/lang/fr_FR/config.php | 4 +- resources/lang/fr_FR/csv.php | 80 ++ resources/lang/fr_FR/firefly.php | 1222 ++++++++++++-------------- resources/lang/fr_FR/form.php | 203 +++-- resources/lang/fr_FR/help.php | 131 ++- resources/lang/fr_FR/list.php | 105 +-- resources/lang/fr_FR/passwords.php | 12 +- resources/lang/fr_FR/validation.php | 28 +- resources/lang/nl_NL/csv.php | 80 ++ resources/lang/nl_NL/firefly.php | 180 ++-- resources/lang/nl_NL/form.php | 23 +- resources/lang/nl_NL/help.php | 5 - resources/lang/nl_NL/list.php | 105 +-- resources/lang/pt_BR/csv.php | 80 ++ resources/lang/pt_BR/firefly.php | 206 ++--- resources/lang/pt_BR/form.php | 47 +- resources/lang/pt_BR/help.php | 5 - resources/lang/pt_BR/list.php | 105 +-- resources/lang/pt_BR/validation.php | 2 +- resources/lang/zh-HK/breadcrumbs.php | 46 + resources/lang/zh-HK/config.php | 21 + resources/lang/zh-HK/csv.php | 80 ++ resources/lang/zh-HK/firefly.php | 780 ++++++++++++++++ resources/lang/zh-HK/form.php | 149 ++++ resources/lang/zh-HK/help.php | 85 ++ resources/lang/zh-HK/list.php | 66 ++ resources/lang/zh-HK/pagination.php | 13 + resources/lang/zh-HK/passwords.php | 17 + resources/lang/zh-HK/validation.php | 80 ++ resources/lang/zh-TW/breadcrumbs.php | 46 + resources/lang/zh-TW/config.php | 21 + resources/lang/zh-TW/csv.php | 80 ++ resources/lang/zh-TW/firefly.php | 780 ++++++++++++++++ resources/lang/zh-TW/form.php | 149 ++++ resources/lang/zh-TW/help.php | 85 ++ resources/lang/zh-TW/list.php | 66 ++ resources/lang/zh-TW/pagination.php | 13 + resources/lang/zh-TW/passwords.php | 17 + resources/lang/zh-TW/validation.php | 80 ++ 40 files changed, 4037 insertions(+), 1270 deletions(-) create mode 100644 resources/lang/fr_FR/csv.php create mode 100644 resources/lang/nl_NL/csv.php create mode 100644 resources/lang/pt_BR/csv.php create mode 100644 resources/lang/zh-HK/breadcrumbs.php create mode 100644 resources/lang/zh-HK/config.php create mode 100644 resources/lang/zh-HK/csv.php create mode 100644 resources/lang/zh-HK/firefly.php create mode 100644 resources/lang/zh-HK/form.php create mode 100644 resources/lang/zh-HK/help.php create mode 100644 resources/lang/zh-HK/list.php create mode 100644 resources/lang/zh-HK/pagination.php create mode 100644 resources/lang/zh-HK/passwords.php create mode 100644 resources/lang/zh-HK/validation.php create mode 100644 resources/lang/zh-TW/breadcrumbs.php create mode 100644 resources/lang/zh-TW/config.php create mode 100644 resources/lang/zh-TW/csv.php create mode 100644 resources/lang/zh-TW/firefly.php create mode 100644 resources/lang/zh-TW/form.php create mode 100644 resources/lang/zh-TW/help.php create mode 100644 resources/lang/zh-TW/list.php create mode 100644 resources/lang/zh-TW/pagination.php create mode 100644 resources/lang/zh-TW/passwords.php create mode 100644 resources/lang/zh-TW/validation.php diff --git a/resources/lang/fr_FR/breadcrumbs.php b/resources/lang/fr_FR/breadcrumbs.php index d5fe95ea0e..e20f075864 100644 --- a/resources/lang/fr_FR/breadcrumbs.php +++ b/resources/lang/fr_FR/breadcrumbs.php @@ -10,13 +10,13 @@ return [ 'home' => 'Accueil', - 'cash_accounts' => 'Cash accounts', + 'cash_accounts' => 'Comptes de trésorerie', 'edit_account' => 'Editer le compte : ":name"', 'edit_currency' => 'Editer la devise : ";name"', 'delete_currency' => 'Supprimer la devise ":name"', - 'newPiggyBank' => 'Create a new piggy bank', - 'edit_piggyBank' => 'Edit piggy bank ":name"', - 'preferences' => 'Preferences', + 'newPiggyBank' => 'Créer une nouvelle tirelire', + 'edit_piggyBank' => 'Modifier la tirelire ":name"', + 'preferences' => 'Préférences', 'profile' => 'Profil', 'changePassword' => 'Modifier le mot de passe', 'bills' => 'Factures', @@ -35,7 +35,7 @@ return [ 'transfer_list' => 'Transferts', 'transfers_list' => 'Transferts', 'create_withdrawal' => 'Creer un nouveau retrait', - 'create_deposit' => 'Create new deposit', + 'create_deposit' => 'Créer un nouveau dépôt', 'create_transfer' => 'Creer un nouveau transfert', 'edit_journal' => 'Editer la transaction ":description"', 'delete_journal' => 'Supprimer la transaction ":description"', diff --git a/resources/lang/fr_FR/config.php b/resources/lang/fr_FR/config.php index f7faeec18e..3a7bf091bf 100644 --- a/resources/lang/fr_FR/config.php +++ b/resources/lang/fr_FR/config.php @@ -11,9 +11,9 @@ return [ 'locale' => 'fr, French, fr_FR, fr_FR.utf8', 'month' => '%B %Y', 'month_and_day' => '%e %B %Y', - 'date_time' => '%B %e, %Y, @ %T', + 'date_time' => '%B %e %Y @ %T', 'specific_day' => '%e %B %Y', - 'week_in_year' => 'Week %W, %Y', + 'week_in_year' => '%W %Y', 'quarter_of_year' => '%B %Y', 'year' => '%Y', 'half_year' => '%B %Y', diff --git a/resources/lang/fr_FR/csv.php b/resources/lang/fr_FR/csv.php new file mode 100644 index 0000000000..858026dad6 --- /dev/null +++ b/resources/lang/fr_FR/csv.php @@ -0,0 +1,80 @@ + 'Configure your import', + 'import_configure_intro' => 'There are some options for your CSV import. Please indicate if your CSV file contains headers on the first column, and what the date format of your date-fields is. That might require some experimentation. The field delimiter is usually a ",", but could also be a ";". Check this carefully.', + 'import_configure_form' => 'Form', + 'header_help' => 'Check this if the first row of your CSV file are the column titles', + 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', + 'import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', + + // roles + 'column_roles_title' => 'Define column roles', + 'column_roles_text' => '

    Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

    Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

    ', + 'column_roles_table' => 'Table', + 'column_name' => 'Name of column', + 'column_example' => 'Column example data', + 'column_role' => 'Column data meaning', + 'do_map_value' => 'Map these values', + 'column' => 'Column', + 'no_example_data' => 'No example data available', + 'store_column_roles' => 'Continue import', + 'do_not_map' => '(do not map)', + 'map_title' => 'Connect import data to Firefly III data', + 'map_text' => 'In the following tables, the left value shows you information found in your uploaded CSV file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', + + 'field_value' => 'Field value', + 'field_mapped_to' => 'Mapped to', + 'store_column_mapping' => 'Store mapping', + + // map things. + + + 'column__ignore' => '(ignore this column)', + 'column_account-iban' => 'Asset account (IBAN)', + 'column_account-id' => 'Asset account ID (matching Firefly)', + 'column_account-name' => 'Asset account (name)', + 'column_amount' => 'Amount', + 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', + 'column_bill-id' => 'Bill ID (matching Firefly)', + 'column_bill-name' => 'Bill name', + 'column_budget-id' => 'Budget ID (matching Firefly)', + 'column_budget-name' => 'Budget name', + 'column_category-id' => 'Category ID (matching Firefly)', + 'column_category-name' => 'Category name', + 'column_currency-code' => 'Currency code (ISO 4217)', + 'column_currency-id' => 'Currency ID (matching Firefly)', + 'column_currency-name' => 'Currency name (matching Firefly)', + 'column_currency-symbol' => 'Currency symbol (matching Firefly)', + 'column_date-interest' => 'Interest calculation date', + 'column_date-book' => 'Transaction booking date', + 'column_date-process' => 'Transaction process date', + 'column_date-transaction' => 'Date', + 'column_description' => 'Description', + 'column_opposing-iban' => 'Opposing account (IBAN)', + 'column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'column_external-id' => 'External ID', + 'column_opposing-name' => 'Opposing account (name)', + 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'column_ing-debet-credit' => 'ING specific debet/credit indicator', + 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'column_sepa-db' => 'SEPA Direct Debet', + 'column_tags-comma' => 'Tags (comma separated)', + 'column_tags-space' => 'Tags (space separated)', + 'column_account-number' => 'Asset account (account number)', + 'column_opposing-number' => 'Opposing account (account number)', +]; \ No newline at end of file diff --git a/resources/lang/fr_FR/firefly.php b/resources/lang/fr_FR/firefly.php index 3a24a0601a..fee7cf1760 100644 --- a/resources/lang/fr_FR/firefly.php +++ b/resources/lang/fr_FR/firefly.php @@ -10,235 +10,235 @@ return [ // general stuff: 'language_incomplete' => 'Cette langue n\'est pas encore complètement traduite', - 'test' => 'Vous avez choisi Anglais', + 'test' => 'Vous avez choisi l\'anglais.', 'close' => 'Fermer', - 'pleaseHold' => 'Veuillew patienter...', + 'pleaseHold' => 'Veuillez patienter...', 'actions' => 'Actions', - 'edit' => 'Editer', + 'edit' => 'Modifier', 'delete' => 'Supprimer', - 'welcomeBack' => 'What\'s playing?', + 'welcomeBack' => 'Que se passe-t-il ?', 'everything' => 'Tout', - 'customRange' => 'Plage personnalisée', + 'customRange' => 'Etendue personnalisée', 'apply' => 'Appliquer', 'cancel' => 'Annuler', 'from' => 'Depuis', - 'to' => 'To', + 'to' => 'A', 'total_sum' => 'Montant total ', 'period_sum' => 'Somme pour la période', 'showEverything' => 'Tout Afficher', 'never' => 'Jamais', 'search_results_for' => 'Résultats de recherche pour ":query"', 'bounced_error' => 'Le message envoyé à :email a été rejeté, donc pas d\'accès pour vous.', - 'deleted_error' => 'These credentials do not match our records.', - 'general_blocked_error' => 'Your account has been disabled, so you cannot login.', - 'expired_error' => 'Your account has expired, and can no longer be used.', - 'unbalanced_error' => 'Your transactions are unbalanced. This means a withdrawal, deposit or transfer was not stored properly. Please check your accounts and transactions for errors (unbalanced amount :amount).', + 'deleted_error' => 'Ces informations d\'identification ne sont pas présentes dans nos données.', + 'general_blocked_error' => 'Votre compte a été désactivé, vous ne pouvez plus vous connecter.', + 'expired_error' => 'Votre compte a expiré et ne peut plus être utilisé.', + 'unbalanced_error' => 'Vos transactions sont mal équilibrées. Cela signifie qu\'un retrait, un dépôt ou un transfert n’a pas été enregistré correctement. Vérifier vos comptes et opérations pour trouver des erreurs (montant mal équilibré: montant).', 'removed_amount' => 'Supprimé :amount', 'added_amount' => 'Ajouté :amount', - 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', + 'asset_account_role_help' => 'Toutes options supplémentaires résultant de votre choix peut être réglée plus tard.', 'Opening balance' => 'Solde initial', - 'create_new_stuff' => 'Create new stuff', + 'create_new_stuff' => 'Créer de nouvelles choses', 'new_withdrawal' => 'Nouveau retrait', 'new_deposit' => 'Nouveau dépôt', 'new_transfer' => 'Nouveau transfert', - 'new_asset_account' => 'New asset account', - 'new_expense_account' => 'New expense account', + 'new_asset_account' => 'Nouveau compte d’actif', + 'new_expense_account' => 'Nouveau compte de dépenses', 'new_revenue_account' => 'Nouveau compte de recettes', 'new_budget' => 'Nouveau budget', 'new_bill' => 'Nouvelle facture', - 'block_account_logout' => 'You have been logged out. Blocked accounts cannot use this site. Did you register with a valid email address?', - 'flash_success' => 'Success!', + 'block_account_logout' => 'Vous avez été déconnecté. Les comptes bloqués ne peuvent pas utiliser ce site. Vous êtes vous enregistrés avec une adresse email valide ?', + 'flash_success' => 'Terminé avec succès !', 'flash_info' => 'Message', - 'flash_warning' => 'Warning!', - 'flash_error' => 'Error!', - 'flash_info_multiple' => 'There is one message|There are :count messages', - 'flash_error_multiple' => 'There is one error|There are :count errors', - 'net_worth' => 'Net worth', - 'route_has_no_help' => 'There is no help for this route, or there is no help available in your language.', - 'two_factor_welcome' => 'Hello, :user!', - 'two_factor_enter_code' => 'To continue, please enter your two factor authentication code. Your application can generate it for you.', - 'two_factor_code_here' => 'Enter code here', - 'two_factor_title' => 'Two factor authentication', - 'authenticate' => 'Authenticate', - 'two_factor_forgot_title' => 'Lost two factor authentication', - 'two_factor_forgot' => 'I forgot my two-factor thing.', - 'two_factor_lost_header' => 'Lost your two factor authentication?', - 'two_factor_lost_intro' => 'Unfortunately, this is not something you can reset from the web interface. You have two choices.', - 'two_factor_lost_fix_self' => 'If you run your own instance of Firefly III, check the logs in storage/logs for instructions.', - 'two_factor_lost_fix_owner' => 'Otherwise, email the site owner, :site_owner and ask them to reset your two factor authentication.', - 'warning_much_data' => ':days days of data may take a while to load.', - 'registered' => 'You have registered successfully!', - 'search' => 'Search', - 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', - 'source_accounts' => 'Source account(s)', - 'destination_accounts' => 'Destination account(s)', + 'flash_warning' => 'Attention !', + 'flash_error' => 'Erreur !', + 'flash_info_multiple' => 'Il y a un message| Il y a :count messages', + 'flash_error_multiple' => 'Il y a une erreur| Il y a :count errors', + 'net_worth' => 'Valeur nette', + 'route_has_no_help' => 'Il n\'y a pas d\'aide pour cette page, ou il n\'y a pas de texte d\'aide en français.', + 'two_factor_welcome' => 'Bonjour, :user !', + 'two_factor_enter_code' => 'Pour continuer, veuillez entrer votre code d’authentification à deux facteurs. Votre application peut la générer pour vous.', + 'two_factor_code_here' => 'Entrez votre code ici', + 'two_factor_title' => 'Authentification à deux facteurs', + 'authenticate' => 'S\'authentifier', + 'two_factor_forgot_title' => 'Perte de l’authentification à deux facteurs', + 'two_factor_forgot' => 'J’ai oublié mon code d\'identification à deux facteurs.', + 'two_factor_lost_header' => 'Perdu votre authentification à deux facteurs ?', + 'two_factor_lost_intro' => 'Malheureusement, ce n’est pas quelque chose que vous pouvez réinitialiser depuis l’interface web. Vous avez deux choix.', + 'two_factor_lost_fix_self' => 'Si vous exécutez votre propre instance de Firefly III, vérifiez les logs dans storage/logs pour obtenir des instructions.', + 'two_factor_lost_fix_owner' => 'Dans le cas contraire, contactez le propriétaire du site par courriel : site_owner et demandez leur de réinitialiser votre authentification à deux facteurs.', + 'warning_much_data' => ':days de données peuvent prendre un certain temps à charger.', + 'registered' => 'Vous avez été enregistré avec succès !', + 'search' => 'Rechercher', + 'no_budget_pointer' => 'Vous semblez n’avoir encore aucun budget. Vous devez en créer sur la page des budgets. Les budgets peuvent vous aider à garder une trace des dépenses.', + 'source_accounts' => 'Compte(s) source', + 'destination_accounts' => 'Compte(s) de destination', // repeat frequencies: - 'repeat_freq_monthly' => 'monthly', - 'weekly' => 'Weekly', - 'quarterly' => 'Quarterly', - 'half-year' => 'Every six months', - 'yearly' => 'Yearly', + 'repeat_freq_monthly' => 'mensuel', + 'weekly' => 'hebdomadaire', + 'quarterly' => 'trimestriel', + 'half-year' => 'semestriel', + 'yearly' => 'annuel', // account confirmation: - 'confirm_account_header' => 'Please confirm your account', - 'confirm_account_intro' => 'An email has been sent to the address you used during your registration. Please check it out for further instructions. If you did not get this message, you can have Firefly send it again.', - 'confirm_account_resend_email' => 'Send me the confirmation message I need to activate my account.', - 'account_is_confirmed' => 'Your account has been confirmed!', - 'invalid_activation_code' => 'It seems the code you are using is not valid, or has expired.', - 'confirm_account_is_resent_header' => 'The confirmation has been resent', - 'confirm_account_is_resent_text' => 'The confirmation message has been resent. If you still did not receive the confirmation message, please contact the site owner at :owner or check the log files to see what went wrong.', - 'confirm_account_is_resent_go_home' => 'Go to the index page of Firefly', - 'confirm_account_not_resent_header' => 'Something went wrong :(', - 'confirm_account_not_resent_intro' => 'The confirmation message has been not resent. If you still did not receive the confirmation message, please contact the site owner at :owner instead. Possibly, you have tried to resend the activation message too often. You can have Firefly III try to resend the confirmation message every hour.', - 'confirm_account_not_resent_go_home' => 'Go to the index page of Firefly', + 'confirm_account_header' => 'Merci de vérifier votre compte', + 'confirm_account_intro' => 'Un courriel a été envoyé à l’adresse utilisée lors de votre inscription. Merci de vous y référer pour obtenir des instructions supplémentaires. Si vous n’avez pas reçu ce message, Firefly peut vous l\'envoyer de nouveau.', + 'confirm_account_resend_email' => 'M’envoyer le message de confirmation nécessaire pour activer mon compte.', + 'account_is_confirmed' => 'Votre compte a été validé !', + 'invalid_activation_code' => 'Il semble que le code que vous utilisez n’est pas valide ou a expiré.', + 'confirm_account_is_resent_header' => 'La confirmation a été renvoyée', + 'confirm_account_is_resent_text' => 'Le message de confirmation a été renvoyé. Si vous n’avez toujours pas reçu le message de confirmation, veuillez prendre contact avec le propriétaire du site à :owner ou vérifiez les logs pour voir ce qui s’est mal passé.', + 'confirm_account_is_resent_go_home' => 'Aller à la page d’accueil de Firefly', + 'confirm_account_not_resent_header' => 'Quelque chose s\'est mal passé :(', + 'confirm_account_not_resent_intro' => 'Le message de confirmation n\'a pas été renvoyé. Si vous continuez à ne pas recevoir le message de confirmation, veuillez communiquer avec le propriétaire du site au : propriétaire. Vous pourriez avoir essayé de vous renvoyer le message d’activation trop souvent. Vous pouvez demander à Firefly III de vous renvoyer le message de confirmation toutes les heures.', + 'confirm_account_not_resent_go_home' => 'Aller à la page d’accueil de Firefly', // export data: - 'import_and_export' => 'Import and export', - 'export_data' => 'Export data', - 'export_data_intro' => 'For backup purposes, when migrating to another system or when migrating to another Firefly III installation.', - 'export_format' => 'Export format', - 'export_format_csv' => 'Comma separated values (CSV file)', - 'export_format_mt940' => 'MT940 compatible format', - 'export_included_accounts' => 'Export transactions from these accounts', - 'include_config_help' => 'For easy re-import into Firefly III', - 'include_old_uploads_help' => 'Firefly III does not throw away the original CSV files you have imported in the past. You can include them in your export.', - 'do_export' => 'Export', - 'export_status_never_started' => 'The export has not started yet', - 'export_status_make_exporter' => 'Creating exporter thing...', - 'export_status_collecting_journals' => 'Collecting your transactions...', - 'export_status_collected_journals' => 'Collected your transactions!', - 'export_status_converting_to_export_format' => 'Converting your transactions...', - 'export_status_converted_to_export_format' => 'Converted your transactions!', - 'export_status_creating_journal_file' => 'Creating the export file...', - 'export_status_created_journal_file' => 'Created the export file!', - 'export_status_collecting_attachments' => 'Collecting all your attachments...', - 'export_status_collected_attachments' => 'Collected all your attachments!', - 'export_status_collecting_old_uploads' => 'Collecting all your previous uploads...', - 'export_status_collected_old_uploads' => 'Collected all your previous uploads!', - 'export_status_creating_config_file' => 'Creating a configuration file...', - 'export_status_created_config_file' => 'Created a configuration file!', - 'export_status_creating_zip_file' => 'Creating a zip file...', - 'export_status_created_zip_file' => 'Created a zip file!', - 'export_status_finished' => 'Export has succesfully finished! Yay!', - 'export_data_please_wait' => 'Please wait...', - 'attachment_explanation' => 'The file called \':attachment_name\' (#:attachment_id) was originally uploaded to :type \':description\' (#:journal_id) dated :date for the amount of :amount.', + 'import_and_export' => 'Importer et Exporter', + 'export_data' => 'Exporter les données', + 'export_data_intro' => 'À des fins de sauvegarde, lors de la migration vers un autre système ou lors de la migration sur une autre installation de Firefly III.', + 'export_format' => 'Format d\'export', + 'export_format_csv' => 'Valeurs séparées par des virgules (fichier CSV)', + 'export_format_mt940' => 'Format compatible MT940', + 'export_included_accounts' => 'Exporter les opérations depuis ces comptes', + 'include_config_help' => 'Pour facilement ré-importer dans Firefly III', + 'include_old_uploads_help' => 'Firefly III ne détruit pas les fichier CSV originaux que vous avez déjà importé dans le passé. Vous pouvez les inclure dans votre exportation.', + 'do_export' => 'Exporter', + 'export_status_never_started' => 'L’exportation n’a pas encore commencé', + 'export_status_make_exporter' => 'Créer un export...', + 'export_status_collecting_journals' => 'Collecte de vos opérations...', + 'export_status_collected_journals' => 'Vos opérations sont collectées!', + 'export_status_converting_to_export_format' => 'Conversion de vos opérations...', + 'export_status_converted_to_export_format' => 'Vos opérations sont converties!', + 'export_status_creating_journal_file' => 'Création du fichier d\'export...', + 'export_status_created_journal_file' => 'Fichier d\'export créé!', + 'export_status_collecting_attachments' => 'Collecte toutes vos pièces jointes...', + 'export_status_collected_attachments' => 'Toutes vos pièces jointes sont collectées !', + 'export_status_collecting_old_uploads' => 'Tous vos précédent upload sont en cours de collecte...', + 'export_status_collected_old_uploads' => 'Tous vos précédent upload sont collectés !', + 'export_status_creating_config_file' => 'Création d’un fichier de configuration...', + 'export_status_created_config_file' => 'Fichier de configuration créé !', + 'export_status_creating_zip_file' => 'Création d’un fichier zip...', + 'export_status_created_zip_file' => 'Fichier zip créé!', + 'export_status_finished' => 'L\'export s\'est terminé avec succès ! Yay !', + 'export_data_please_wait' => 'Veuillez patienter...', + 'attachment_explanation' => 'Le fichier appelé \':attachment_name\' (#:attachment_id) était initialement envoyé à :type \': description\' (#:journal_id) en date du :date pour un montant de :amount.', // rules - 'rules' => 'Rules', - 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.', - 'rule_name' => 'Name of rule', - 'rule_triggers' => 'Rule triggers when', + 'rules' => 'Règles', + 'rules_explanation' => 'Ici vous pouvez gérer les règles. Les règles sont déclenchées lorsqu’une opération est créée ou mise à jour. Ensuite, si l\'opération a certaines propriétés (appelées "déclencheurs" ) Firefly exécutera des "actions". Combiné, vous pouvez faire réagir Firefly d’une certaine façon aux nouvelles opérations.', + 'rule_name' => 'Nom de la règle', + 'rule_triggers' => 'La règle se déclenchera lorsque', 'rule_actions' => 'Rule will', - 'new_rule' => 'New rule', - 'new_rule_group' => 'New rule group', - 'rule_priority_up' => 'Give rule more priority', - 'rule_priority_down' => 'Give rule less priority', - 'make_new_rule_group' => 'Make new rule group', - 'store_new_rule_group' => 'Store new rule group', - 'created_new_rule_group' => 'New rule group ":title" stored!', - 'updated_rule_group' => 'Successfully updated rule group ":title".', - 'edit_rule_group' => 'Edit rule group ":title"', - 'delete_rule_group' => 'Delete rule group ":title"', - 'deleted_rule_group' => 'Deleted rule group ":title"', - 'update_rule_group' => 'Update rule group', - 'no_rules_in_group' => 'There are no rules in this group', - 'move_rule_group_up' => 'Move rule group up', - 'move_rule_group_down' => 'Move rule group down', - 'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:', - 'make_new_rule' => 'Make new rule in rule group ":title"', - 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', - 'rule_help_active' => 'Inactive rules will never fire.', - 'stored_new_rule' => 'Stored new rule with title ":title"', - 'deleted_rule' => 'Deleted rule with title ":title"', - 'store_new_rule' => 'Store new rule', - 'updated_rule' => 'Updated rule with title ":title"', - 'default_rule_group_name' => 'Default rules', - 'default_rule_group_description' => 'All your rules not in a particular group.', - 'default_rule_name' => 'Your first default rule', - 'default_rule_description' => 'This rule is an example. You can safely delete it.', - 'default_rule_trigger_description' => 'The Man Who Sold the World', + 'new_rule' => 'Nouvelle règle', + 'new_rule_group' => 'Nouveau groupe de règles', + 'rule_priority_up' => 'Donner à la règle plus de priorité', + 'rule_priority_down' => 'Donner à la règle moins de priorité', + 'make_new_rule_group' => 'Créer un nouveau groupe de règles', + 'store_new_rule_group' => 'Créer un nouveau groupe de règles', + 'created_new_rule_group' => 'Le nouveau groupe de règles ": titre" est créé !', + 'updated_rule_group' => 'Groupe de règles ":title" mise à jour avec succès.', + 'edit_rule_group' => 'Modifier le groupe de règles ":title"', + 'delete_rule_group' => 'Supprimer le groupe de règles ":title"', + 'deleted_rule_group' => 'Groupe de règles ":title" supprimée', + 'update_rule_group' => 'Mettre à jour le groupe de règles', + 'no_rules_in_group' => 'Il n’y a pas de règles dans ce groupe', + 'move_rule_group_up' => 'Monter le groupe de règles', + 'move_rule_group_down' => 'Descendre le groupe de règles', + 'save_rules_by_moving' => 'Enregistrer ces règles en les déplaçant vers un autre groupe de règles :', + 'make_new_rule' => 'Créer une nouvelle règle dans le groupe de règles ":title"', + 'rule_help_stop_processing' => 'Lorsque vous cochez cette case, les règles suivantes de ce groupe ne seront pas exécutées.', + 'rule_help_active' => 'Les règles inactives ne se déclencheront jamais.', + 'stored_new_rule' => 'Nouvelle règle créée avec le titre ":title"', + 'deleted_rule' => 'Règle supprimée avec le titre ":title"', + 'store_new_rule' => 'Créer une nouvelle règle', + 'updated_rule' => 'Nouvelle règle enregistrée avec le titre ":title"', + 'default_rule_group_name' => 'Règles par défaut', + 'default_rule_group_description' => 'Toutes vos règles n\'étant pas dans un groupe particulier.', + 'default_rule_name' => 'Votre première règle par défaut', + 'default_rule_description' => 'Cette règle est un exemple. Vous pouvez la supprimer en toute sécurité.', + 'default_rule_trigger_description' => 'L’homme qui a vendu le monde', 'default_rule_trigger_from_account' => 'David Bowie', 'default_rule_action_prepend' => 'Bought the world from ', - 'default_rule_action_set_category' => 'Large expenses', - 'trigger' => 'Trigger', - 'trigger_value' => 'Trigger on value', - 'stop_processing_other_triggers' => 'Stop processing other triggers', - 'add_rule_trigger' => 'Add new trigger', + 'default_rule_action_set_category' => 'Grandes dépenses', + 'trigger' => 'Déclencheur', + 'trigger_value' => 'Déclenchement sur valeur', + 'stop_processing_other_triggers' => 'Arrêter le traitement d’autres déclencheurs', + 'add_rule_trigger' => 'Ajouter un nouveau déclencheur', 'action' => 'Action', - 'action_value' => 'Action value', - 'stop_executing_other_actions' => 'Stop executing other actions', - 'add_rule_action' => 'Add new action', - 'edit_rule' => 'Edit rule ":title"', - 'delete_rule' => 'Delete rule ":title"', - 'update_rule' => 'Update rule', - 'test_rule_triggers' => 'See matching transactions', - 'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions', - 'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.', - 'warning_no_valid_triggers' => 'No valid triggers provided.', - 'execute_on_existing_transactions' => 'Execute for existing transactions', - 'execute_on_existing_transactions_intro' => 'When a rule or group has been changed or added, you can execute it for existing transactions', - 'execute_on_existing_transactions_short' => 'Existing transactions', + 'action_value' => 'Valeur de l’action', + 'stop_executing_other_actions' => 'Arrêter l’exécution des autres actions', + 'add_rule_action' => 'Ajouter une nouvelle action', + 'edit_rule' => 'Modifier la règle ": titre"', + 'delete_rule' => 'Supprimer la règle ":titre"', + 'update_rule' => 'Mettre à jour la règle', + 'test_rule_triggers' => 'Voir les opérations correspondantes', + 'warning_transaction_subset' => 'Pour des raisons de performances cette liste est limitée à :max_num_transactions et peut n\'afficher qu\'une partie des opérations correspondantes', + 'warning_no_matching_transactions' => 'Aucunes opérations correspondantes trouvées. Veuillez noter que pour des raisons de performances, seule les dernières :num_transactions opérations ont été vérifiées.', + 'warning_no_valid_triggers' => 'Aucun déclencheurs valide fourni.', + 'execute_on_existing_transactions' => 'Exécuter des opérations existantes', + 'execute_on_existing_transactions_intro' => 'Lorsqu’une règle ou un groupe a été modifié ou ajouté, vous pouvez l’exécuter pour des opérations existantes', + 'execute_on_existing_transactions_short' => 'Opérations existantes', 'executed_group_on_existing_transactions' => 'Executed group ":title" for existing transactions', 'execute_group_on_existing_transactions' => 'Execute group ":title" for existing transactions', - 'include_transactions_from_accounts' => 'Include transactions from these accounts', - 'execute' => 'Execute', + 'include_transactions_from_accounts' => 'Iclure les opérations depuis ces comptes', + 'execute' => 'Executer', // actions and triggers - 'rule_trigger_user_action' => 'User action is ":trigger_value"', - 'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"', - 'rule_trigger_from_account_ends' => 'Source account ends with ":trigger_value"', - 'rule_trigger_from_account_is' => 'Source account is ":trigger_value"', - 'rule_trigger_from_account_contains' => 'Source account contains ":trigger_value"', - 'rule_trigger_to_account_starts' => 'Destination account starts with ":trigger_value"', - 'rule_trigger_to_account_ends' => 'Destination account ends with ":trigger_value"', - 'rule_trigger_to_account_is' => 'Destination account is ":trigger_value"', - 'rule_trigger_to_account_contains' => 'Destination account contains ":trigger_value"', - 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"', - 'rule_trigger_amount_less' => 'Amount is less than :trigger_value', - 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', - 'rule_trigger_amount_more' => 'Amount is more than :trigger_value', - 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"', - 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"', - 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', - 'rule_trigger_description_is' => 'Description is ":trigger_value"', - 'rule_trigger_from_account_starts_choice' => 'Source account starts with..', - 'rule_trigger_from_account_ends_choice' => 'Source account ends with..', - 'rule_trigger_from_account_is_choice' => 'Source account is..', - 'rule_trigger_from_account_contains_choice' => 'Source account contains..', - 'rule_trigger_to_account_starts_choice' => 'Destination account starts with..', - 'rule_trigger_to_account_ends_choice' => 'Destination account ends with..', - 'rule_trigger_to_account_is_choice' => 'Destination account is..', - 'rule_trigger_to_account_contains_choice' => 'Destination account contains..', - 'rule_trigger_transaction_type_choice' => 'Transaction is of type..', - 'rule_trigger_amount_less_choice' => 'Amount is less than..', - 'rule_trigger_amount_exactly_choice' => 'Amount is..', - 'rule_trigger_amount_more_choice' => 'Amount is more than..', - 'rule_trigger_description_starts_choice' => 'Description starts with..', - 'rule_trigger_description_ends_choice' => 'Description ends with..', - 'rule_trigger_description_contains_choice' => 'Description contains..', - 'rule_trigger_description_is_choice' => 'Description is..', - 'rule_trigger_store_journal' => 'When a journal is created', - 'rule_trigger_update_journal' => 'When a journal is updated', - 'rule_action_set_category' => 'Set category to ":action_value"', - 'rule_action_clear_category' => 'Clear category', - 'rule_action_set_budget' => 'Set budget to ":action_value"', - 'rule_action_clear_budget' => 'Clear budget', - 'rule_action_add_tag' => 'Add tag ":action_value"', - 'rule_action_remove_tag' => 'Remove tag ":action_value"', - 'rule_action_remove_all_tags' => 'Remove all tags', - 'rule_action_set_description' => 'Set description to ":action_value"', - 'rule_action_append_description' => 'Append description with ":action_value"', - 'rule_action_prepend_description' => 'Prepend description with ":action_value"', - 'rule_action_set_category_choice' => 'Set category to..', - 'rule_action_clear_category_choice' => 'Clear any category', - 'rule_action_set_budget_choice' => 'Set budget to..', - 'rule_action_clear_budget_choice' => 'Clear any budget', - 'rule_action_add_tag_choice' => 'Add tag..', - 'rule_action_remove_tag_choice' => 'Remove tag..', - 'rule_action_remove_all_tags_choice' => 'Remove all tags', - 'rule_action_set_description_choice' => 'Set description to..', - 'rule_action_append_description_choice' => 'Append description with..', - 'rule_action_prepend_description_choice' => 'Prepend description with..', + 'rule_trigger_user_action' => 'L\'action de l’utilisateur est ": trigger_value"', + 'rule_trigger_from_account_starts' => 'Le compte source commence par ":trigger_value"', + 'rule_trigger_from_account_ends' => 'Le compte source se termine par ":trigger_value"', + 'rule_trigger_from_account_is' => 'Le compte source est ":trigger_value"', + 'rule_trigger_from_account_contains' => 'Le compte source contient ":trigger_value"', + 'rule_trigger_to_account_starts' => 'Le compte de destination commence par ":trigger_value"', + 'rule_trigger_to_account_ends' => 'Le compte de destination se termine par ":trigger_value"', + 'rule_trigger_to_account_is' => 'Le compte de destination est ":trigger_value"', + 'rule_trigger_to_account_contains' => 'Le compte de destination contient ":trigger_value"', + 'rule_trigger_transaction_type' => 'L\'opération est du type ":trigger_value"', + 'rule_trigger_amount_less' => 'Le montant est inférieur à :trigger_value', + 'rule_trigger_amount_exactly' => 'Le montant est :trigger_value', + 'rule_trigger_amount_more' => 'Le montant est supérieur à :trigger_value', + 'rule_trigger_description_starts' => 'La description commence par ":trigger_value"', + 'rule_trigger_description_ends' => 'La description se termine par ":trigger_value"', + 'rule_trigger_description_contains' => 'La description contient ":trigger_value"', + 'rule_trigger_description_is' => 'La description est ":trigger_value"', + 'rule_trigger_from_account_starts_choice' => 'Le compte source commence par..', + 'rule_trigger_from_account_ends_choice' => 'Le compte source se termine par..', + 'rule_trigger_from_account_is_choice' => 'Le compte source est..', + 'rule_trigger_from_account_contains_choice' => 'Le compte source contient..', + 'rule_trigger_to_account_starts_choice' => 'Le compte de destination commence par..', + 'rule_trigger_to_account_ends_choice' => 'Compte de destination se terminant par..', + 'rule_trigger_to_account_is_choice' => 'Le compte de destination est..', + 'rule_trigger_to_account_contains_choice' => 'Le compte de destination est..', + 'rule_trigger_transaction_type_choice' => 'L\'opération est du type..', + 'rule_trigger_amount_less_choice' => 'Le montant est inférieur à..', + 'rule_trigger_amount_exactly_choice' => 'Le montant est..', + 'rule_trigger_amount_more_choice' => 'Le montant est supérieur à..', + 'rule_trigger_description_starts_choice' => 'Le description commence par..', + 'rule_trigger_description_ends_choice' => 'La description se termine par..', + 'rule_trigger_description_contains_choice' => 'La description contient..', + 'rule_trigger_description_is_choice' => 'La description est..', + 'rule_trigger_store_journal' => 'Lorsqu’un journal est créé', + 'rule_trigger_update_journal' => 'Lorsqu’un journal est mis à jour', + 'rule_action_set_category' => 'Définir la catégorie à ":action_value"', + 'rule_action_clear_category' => 'Supprimer la catégorie', + 'rule_action_set_budget' => 'Définir le budget à ":action_value"', + 'rule_action_clear_budget' => 'Supprimer le budget', + 'rule_action_add_tag' => 'Ajouter un tag ":action_value"', + 'rule_action_remove_tag' => 'Supprimer un tag ":action_value"', + 'rule_action_remove_all_tags' => 'Supprimer tous les tags', + 'rule_action_set_description' => 'Définir la description à ":action_value"', + 'rule_action_append_description' => 'Ajouter à la description ":action_value"', + 'rule_action_prepend_description' => 'Préfixer la description avec ":action_value"', + 'rule_action_set_category_choice' => 'Définir la catégorie à..', + 'rule_action_clear_category_choice' => 'Effacer les catégories', + 'rule_action_set_budget_choice' => 'Définir le budget à..', + 'rule_action_clear_budget_choice' => 'Effacer les budgets', + 'rule_action_add_tag_choice' => 'Ajouter un tag..', + 'rule_action_remove_tag_choice' => 'Supprimer le tag..', + 'rule_action_remove_all_tags_choice' => 'Supprimer tous les tags', + 'rule_action_set_description_choice' => 'Définir la description à..', + 'rule_action_append_description_choice' => 'Suffixer la description avec..', + 'rule_action_prepend_description_choice' => 'Préfixer la description avec..', // tags 'store_new_tag' => 'Créer un nouveau tag', @@ -248,493 +248,400 @@ return [ 'location' => 'Emplacement', // preferences - 'pref_home_screen_accounts' => 'Home screen accounts', - 'pref_home_screen_accounts_help' => 'Which accounts should be displayed on the home page?', + 'pref_home_screen_accounts' => 'Comptes de l’écran d’accueil', + 'pref_home_screen_accounts_help' => 'Quel compte devrait être affiché sur l\'écran d’accueil?', 'pref_budget_settings' => 'Paramètres de budget', - 'pref_budget_settings_help' => 'What\'s the maximum amount of money a budget envelope may contain?', - 'pref_view_range' => 'View range', - 'pref_view_range_help' => 'Some charts are automatically grouped in periods. What period would you prefer?', - 'pref_1D' => 'One day', - 'pref_1W' => 'One week', - 'pref_1M' => 'One month', - 'pref_3M' => 'Three months (quarter)', - 'pref_6M' => 'Six months', - 'pref_1Y' => 'One year', - 'pref_languages' => 'Languages', - 'pref_languages_help' => 'Firefly III supports several languages. Which one do you prefer?', - 'pref_custom_fiscal_year' => 'Fiscal year settings', - 'pref_custom_fiscal_year_label' => 'Enabled', - 'pref_custom_fiscal_year_help' => 'In countries that use a financial year other than January 1 to December 31, you can switch this on and specify start / end days of the fiscal year', - 'pref_fiscal_year_start_label' => 'Fiscal year start date', - 'pref_two_factor_auth' => '2-step verification', - 'pref_two_factor_auth_help' => 'When you enable 2-step verification (also known as two-factor authentication), you add an extra layer of security to your account. You sign in with something you know (your password) and something you have (a verification code). Verification codes are generated by an application on your phone, such as Authy or Google Authenticator.', - 'pref_enable_two_factor_auth' => 'Enable 2-step verification', - 'pref_two_factor_auth_disabled' => '2-step verification code removed and disabled', - 'pref_two_factor_auth_remove_it' => 'Don\'t forget to remove the account from your authentication app!', - 'pref_two_factor_auth_code' => 'Verify code', - 'pref_two_factor_auth_code_help' => 'Scan the QR code with an application on your phone such as Authy or Google Authenticator and enter the generated code.', - 'pref_two_factor_auth_reset_code' => 'Reset verification code', - 'pref_two_factor_auth_remove_code' => 'Remove verification code', - 'pref_two_factor_auth_remove_will_disable' => '(this will also disable two-factor authentication)', - 'pref_save_settings' => 'Save settings', - 'saved_preferences' => 'Preferences saved!', - 'transaction_page_size_title' => 'Page size', + 'pref_budget_settings_help' => 'Quel est le montant maximum d’argent qu\'une enveloppe budgétaire peut contenir ?', + 'pref_view_range' => 'Voir l\'étendue', + 'pref_view_range_help' => 'Certains graphiques sont automatiquement groupés par périodes. Quelle période préférez-vous ?', + 'pref_1D' => 'Un jour', + 'pref_1W' => 'Une semaine', + 'pref_1M' => 'Un mois', + 'pref_3M' => 'Trois mois (trimestre)', + 'pref_6M' => 'Six mois', + 'pref_1Y' => 'Un an', + 'pref_languages' => 'Langues', + 'pref_languages_help' => 'Firefly III prend en charge plusieurs langues. Laquelle préférez-vous ?', + 'pref_custom_fiscal_year' => 'Paramètres fiscaux de l\'année', + 'pref_custom_fiscal_year_label' => 'Activé', + 'pref_custom_fiscal_year_help' => 'Dans les pays qui utilisent une année financière autre que du 1er janvier au 31 décembre, vous pouvez le changer en spécifiant le jour de début et de fin de l\'année fiscale', + 'pref_fiscal_year_start_label' => 'Date du début de l\'année fiscale', + 'pref_two_factor_auth' => 'Validation en 2 étapes', + 'pref_two_factor_auth_help' => 'Lorsque vous activez la validation en 2 étapes (également connu sous le nom de deux facteurs d’authentification), vous ajoutez une couche de sécurité supplémentaire à votre compte. Vous vous connecter avec quelque chose que vous connaissez (votre mot de passe) et quelque chose que vous avez (un code de vérification). Codes de vérification sont générés par une application sur votre téléphone, telles que Authy ou Google Authenticator.', + 'pref_enable_two_factor_auth' => 'Activez la validation en 2 étapes', + 'pref_two_factor_auth_disabled' => 'Le code de vérification en deux tapes a été enlevé et désactivé', + 'pref_two_factor_auth_remove_it' => 'N’oubliez pas de supprimer ce compte de votre application d’authentification !', + 'pref_two_factor_auth_code' => 'Vérifier le code', + 'pref_two_factor_auth_code_help' => 'Scanner le code QR avec une application sur votre téléphone comme Authy ou Google Authenticator et entrez le code généré.', + 'pref_two_factor_auth_reset_code' => 'Réinitialiser le code de vérification', + 'pref_two_factor_auth_remove_code' => 'Supprimez le code de vérification', + 'pref_two_factor_auth_remove_will_disable' => '(cela désactivera également l\'authentification à deux facteurs)', + 'pref_save_settings' => 'Enregistrer les paramètres', + 'saved_preferences' => 'Préférences enregistrées!', + 'transaction_page_size_title' => 'Taille de la page', 'transaction_page_size_help' => 'Any list of transactions shows at most this many transactions', - 'transaction_page_size_label' => 'Page size', + 'transaction_page_size_label' => 'Taille de la page', 'budget_maximum' => 'Budget maximum', - 'between_dates' => '(:start and :end)', + 'between_dates' => '(:start et :end)', // profile: - 'change_your_password' => 'Change your password', - 'delete_account' => 'Delete account', - 'current_password' => 'Current password', - 'new_password' => 'New password', - 'new_password_again' => 'New password (again)', - 'delete_your_account' => 'Delete your account', - 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', - 'delete_your_account_password' => 'Enter your password to continue.', - 'password' => 'Password', - 'are_you_sure' => 'Are you sure? You cannot undo this.', - 'delete_account_button' => 'DELETE your account', - 'invalid_current_password' => 'Invalid current password!', - 'password_changed' => 'Password changed!', - 'should_change' => 'The idea is to change your password.', - 'invalid_password' => 'Invalid password!', + 'change_your_password' => 'Modifier votre mot de passe', + 'delete_account' => 'Supprimer le compte', + 'current_password' => 'Mot de passe actuel', + 'new_password' => 'Nouveau mot de passe ', + 'new_password_again' => 'Nouveau mot de passe (encore)', + 'delete_your_account' => 'Supprimer votre compte', + 'delete_your_account_help' => 'La suppression de votre compte supprimera également les comptes, les opérations, tout ce que vous pourriez avoir enregistré dans Firefly III. Tout sera SUPPRIME.', + 'delete_your_account_password' => 'Entrez votre mot de passe pour continuer.', + 'password' => 'Mot de passe', + 'are_you_sure' => 'Es-tu sûr ? Vous ne pourrez pas annuler cette action.', + 'delete_account_button' => 'SUPPRIMER votre compte', + 'invalid_current_password' => 'Mot de passe actuel non valide!', + 'password_changed' => 'Mot de passe modifié!', + 'should_change' => 'L’idée est de changer votre mot de passe.', + 'invalid_password' => 'Mot de passe incorrect!', // attachments - 'nr_of_attachments' => 'One attachment|:count attachments', - 'attachments' => 'Attachments', - 'edit_attachment' => 'Edit attachment ":name"', - 'update_attachment' => 'Update attachment', - 'delete_attachment' => 'Delete attachment ":name"', - 'attachment_deleted' => 'Deleted attachment ":name"', - 'attachment_updated' => 'Updated attachment ":name"', - 'upload_max_file_size' => 'Maximum file size: :size', + 'nr_of_attachments' => 'Une pièce jointe|:count pièces jointes', + 'attachments' => 'Pièces jointes', + 'edit_attachment' => 'Modifier la pièce jointe ":name"', + 'update_attachment' => 'Mettre à jour la pièce jointe', + 'delete_attachment' => 'Supprimer la pièce jointe ":name"', + 'attachment_deleted' => 'Pièce jointe ":name" supprimée', + 'attachment_updated' => 'Pièce jointe ":name" mise à jour', + 'upload_max_file_size' => 'Taille maximum du fichier : :size', // tour: - 'prev' => 'Prev', - 'next' => 'Next', + 'prev' => 'Précédent', + 'next' => 'Suivant', 'end-tour' => 'End tour', - 'pause' => 'Pause', + 'pause' => 'Suspendre', // transaction index - 'title_expenses' => 'Expenses', - 'title_withdrawal' => 'Expenses', - 'title_revenue' => 'Revenue / income', - 'title_deposit' => 'Revenue / income', + 'title_expenses' => 'Dépenses', + 'title_withdrawal' => 'Dépenses', + 'title_revenue' => 'Recette / revenu', + 'title_deposit' => 'Recette / revenu', 'title_transfer' => 'Transferts', 'title_transfers' => 'Transferts', - - // csv import: - 'csv_import' => 'Import CSV file', - 'csv' => 'CSV', - 'csv_index_title' => 'Upload and import a CSV file', - 'csv_define_column_roles' => 'Define column roles', - 'csv_map_values' => 'Map found values to existing values', - 'csv_download_config' => 'Download CSV configuration file.', - 'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by the folks at Atlassian. Simply upload your CSV file and follow the instructions. If you would like to learn more, please click on the button at the top of this page.', - 'csv_index_beta_warning' => 'This tool is very much in beta. Please proceed with caution', - 'csv_header_help' => 'Check this box when your CSV file\'s first row consists of column names, not actual data', - 'csv_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', - 'csv_csv_file_help' => 'Select the CSV file here. You can only upload one file at a time', - 'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', - 'csv_upload_button' => 'Start importing CSV', - 'csv_column_roles_title' => 'Define column roles', - 'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. The next step will show you what this button does.', - 'csv_column_roles_table' => 'Column roles', - 'csv_column' => 'CSV column', - 'csv_column_name' => 'CSV column name', - 'csv_column_example' => 'Column example data', - 'csv_column_role' => 'Column contains?', - 'csv_do_map_value' => 'Map value?', - 'csv_continue' => 'Continue to the next step', - 'csv_go_back' => 'Go back to the previous step', - 'csv_map_title' => 'Map found values to existing values', - 'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other things won\'t be created twice.', - 'csv_field_value' => 'Field value from CSV', - 'csv_field_mapped_to' => 'Must be mapped to...', - 'csv_do_not_map' => 'Do not map this value', - 'csv_download_config_title' => 'Download CSV configuration', - 'csv_download_config_text' => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.', - 'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all over again. But, if the import succeeds, it will be easier to upload similar CSV files.', - 'csv_do_download_config' => 'Download configuration file.', - 'csv_empty_description' => '(empty description)', - 'csv_upload_form' => 'CSV upload form', - 'csv_index_unsupported_warning' => 'The CSV importer is yet incapable of doing the following:', - 'csv_unsupported_map' => 'The importer cannot map the column ":columnRole" to existing values in the database.', - 'csv_unsupported_value' => 'The importer does not know how to handle values in columns marked as ":columnRole".', - 'csv_cannot_store_value' => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.', - 'csv_process_title' => 'CSV import finished!', - 'csv_process_text' => 'The CSV importer has finished and has processed :rows rows', - 'csv_row' => 'Row', - 'csv_import_with_errors' => 'There was one error.|There were :errors errors.', - 'csv_error_see_logs' => 'Check the log files to see details.', - 'csv_process_new_entries' => 'Firefly has created :imported new transaction(s).', - 'csv_start_over' => 'Import again', - 'csv_to_index' => 'Back home', - 'csv_upload_not_writeable' => 'Cannot write to the path mentioned here. Cannot upload', - 'csv_column__ignore' => '(ignore this column)', - 'csv_column_account-iban' => 'Asset account (IBAN)', - 'csv_column_account-id' => 'Asset account ID (matching Firefly)', - 'csv_column_account-name' => 'Asset account (name)', - 'csv_column_amount' => 'Amount', - 'csv_column_amount-comma-separated' => 'Amount (comma as decimal separator)', - 'csv_column_bill-id' => 'Bill ID (matching Firefly)', - 'csv_column_bill-name' => 'Bill name', - 'csv_column_budget-id' => 'Budget ID (matching Firefly)', - 'csv_column_budget-name' => 'Budget name', - 'csv_column_category-id' => 'Category ID (matching Firefly)', - 'csv_column_category-name' => 'Category name', - 'csv_column_currency-code' => 'Currency code (ISO 4217)', - 'csv_column_currency-id' => 'Currency ID (matching Firefly)', - 'csv_column_currency-name' => 'Currency name (matching Firefly)', - 'csv_column_currency-symbol' => 'Currency symbol (matching Firefly)', - 'csv_column_date-rent' => 'Rent calculation date', - 'csv_column_date-transaction' => 'Date', - 'csv_column_description' => 'Description', - 'csv_column_opposing-iban' => 'Opposing account (IBAN)', - 'csv_column_opposing-id' => 'Opposing account ID (matching Firefly)', - 'csv_column_opposing-name' => 'Opposing account (name)', - 'csv_column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', - 'csv_column_ing-debet-credit' => 'ING specific debet/credit indicator', - 'csv_column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', - 'csv_column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', - 'csv_column_sepa-db' => 'SEPA Direct Debet', - 'csv_column_tags-comma' => 'Tags (comma separated)', - 'csv_column_tags-space' => 'Tags (space separated)', - 'csv_column_account-number' => 'Asset account (account number)', - 'csv_column_opposing-number' => 'Opposing account (account number)', - 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', - 'csv_specifix_AbnAmroDescription' => 'Select this when you\'re importing ABN AMRO CSV export files.', - 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', - 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', - 'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?', - 'could_not_recover' => 'Could not continue from the previous step. Your progress has been lost :(. The log files will tell you what happened.', - 'must_select_roles' => 'You must select some roles for your file content, or the process cannot continue.', - 'invalid_mapping' => 'You have submitted an invalid mapping. The process cannot continue.', - 'no_file_uploaded' => 'It seems you did not upload a file.', - - // create new stuff: 'create_new_withdrawal' => 'Creer un nouveau retrait', - 'create_new_deposit' => 'Create new deposit', + 'create_new_deposit' => 'Créer un nouveau dépôt', 'create_new_transfer' => 'Creer un nouveau transfert', - 'create_new_asset' => 'Create new asset account', - 'create_new_expense' => 'Create new expense account', - 'create_new_revenue' => 'Create new revenue account', - 'create_new_piggy_bank' => 'Create new piggy bank', - 'create_new_bill' => 'Create new bill', + 'create_new_asset' => 'Créer le nouveau compte d’actif', + 'create_new_expense' => 'Créer nouveau compte de dépenses', + 'create_new_revenue' => 'Créer nouveau compte de recettes', + 'create_new_piggy_bank' => 'Créer une nouvelle tirelire', + 'create_new_bill' => 'Créer une nouvelle facture', // currencies: - 'create_currency' => 'Create a new currency', - 'edit_currency' => 'Edit currency ":name"', - 'store_currency' => 'Store new currency', - 'update_currency' => 'Update currency', + 'create_currency' => 'Créer une nouvelle devise', + 'edit_currency' => 'Modifier la devise ":name"', + 'store_currency' => 'Créer une nouvelle devise', + 'update_currency' => 'Mise à jour de la balance', 'new_default_currency' => ':name is now the default currency.', 'cannot_delete_currency' => 'Cannot delete :name because there are still transactions attached to it!', - 'deleted_currency' => 'Currency :name deleted', - 'created_currency' => 'Currency :name created', - 'updated_currency' => 'Currency :name updated', - 'ask_site_owner' => 'Please ask :owner to add, remove or edit currencies.', - 'currencies_intro' => 'Firefly III supports various currencies which you can set and enable here.', - 'make_default_currency' => 'make default', - 'default_currency' => 'default', + 'deleted_currency' => 'Devise ":name" supprimée', + 'created_currency' => 'Devise ":name" créée', + 'updated_currency' => 'Devise ":name" mise à jour', + 'ask_site_owner' => 'Merci de demander à :owner pour ajouter, modifier ou supprimer des devises.', + 'currencies_intro' => 'Firefly III prend en charge diverses monnaies que vous pouvez configurer et activer ici.', + 'make_default_currency' => 'définir par défaut', + 'default_currency' => 'par défaut', // new user: - 'submit' => 'Submit', - 'getting_started' => 'Getting started', - 'to_get_started' => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:', - 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:', - 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.', - 'stored_new_account_new_user' => 'Yay! Your new account has been stored.', - 'stored_new_accounts_new_user' => 'Yay! Your new accounts have been stored.', + 'submit' => 'Soumettre', + 'getting_started' => 'Mise en route', + 'to_get_started' => 'Pour débuter avec Firefly, veuillez entrer le nom de votre banque actuelle et le solde de votre compte courant :', + 'savings_balance_text' => 'Si vous avez un compte d’épargne, entrer le solde actuel de votre compte d’épargne :', + 'cc_balance_text' => 'Si vous avez une carte de crédit, entrez la plafond de votre carte.', + 'stored_new_account_new_user' => 'Super ! Votre nouveau compte à été créé.', + 'stored_new_accounts_new_user' => 'Super ! Vos nouveaux comptes ont été créé.', // forms: - 'mandatoryFields' => 'Mandatory fields', - 'optionalFields' => 'Optional fields', + 'mandatoryFields' => 'Champs obligatoires', + 'optionalFields' => 'Champs optionnels', 'options' => 'Options', - 'something' => 'Something!', + 'something' => 'Quelque chose !', // budgets: - 'create_new_budget' => 'Create a new budget', - 'store_new_budget' => 'Store new budget', - 'stored_new_budget' => 'Stored new budget ":name"', - 'availableIn' => 'Available in :date', + 'create_new_budget' => 'Créer un nouveau budget', + 'store_new_budget' => 'Créer un nouveau budget', + 'stored_new_budget' => 'Nouveau budget ":name" créé', + 'availableIn' => 'Disponible depuis', 'available_between' => 'Available between :start and :end', - 'transactionsWithoutBudget' => 'Expenses without budget', - 'transactionsWithoutBudgetDate' => 'Expenses without budget in :date', - 'transactions_no_budget' => 'Expenses without budget between :start and :end', - 'spent_between' => 'Spent between :start and :end', - 'createBudget' => 'New budget', - 'inactiveBudgets' => 'Inactive budgets', - 'without_budget_between' => 'Transactions without a budget between :start and :end', - 'budget_in_month' => ':name in :month', - 'delete_budget' => 'Delete budget ":name"', - 'deleted_budget' => 'Deleted budget ":name"', - 'edit_budget' => 'Edit budget ":name"', - 'updated_budget' => 'Updated budget ":name"', - 'update_amount' => 'Update amount', - 'update_budget' => 'Update budget', - 'update_budget_amount_range' => 'Update (expected) available amount between :start and :end', + 'transactionsWithoutBudget' => 'Dépenses non budgétisées', + 'transactionsWithoutBudgetDate' => 'Dépenses non budgétisées en date du :date', + 'transactions_no_budget' => 'Dépenses non budgetisées entre le :start et le :end', + 'spent_between' => 'Dépensé entre le :start et le :end', + 'createBudget' => 'Nouveau budget', + 'inactiveBudgets' => 'Budgets inactifs', + 'without_budget_between' => 'Opérations non budgetisées entre le :start et le :end', + 'budget_in_month' => ':name en :month', + 'delete_budget' => 'Supprimer le budget ":name"', + 'deleted_budget' => 'Budget ":name" supprimé', + 'edit_budget' => 'Modifier le budget ":name"', + 'updated_budget' => 'Mettre à jour le budget ":name"', + 'update_amount' => 'Mettre à jour le montant', + 'update_budget' => 'Mettre à jour le budget', + 'update_budget_amount_range' => 'Mettre à jour le montant disponible (prévu) entre le :start et le :end', // bills: 'matching_on' => 'Matching on', - 'between_amounts' => 'between :low and :high.', - 'repeats' => 'Repeats', - 'connected_journals' => 'Connected transactions', - 'auto_match_on' => 'Automatically matched by Firefly', - 'auto_match_off' => 'Not automatically matched by Firefly', - 'next_expected_match' => 'Next expected match', - 'delete_bill' => 'Delete bill ":name"', - 'deleted_bill' => 'Deleted bill ":name"', - 'edit_bill' => 'Edit bill ":name"', - 'more' => 'More', - 'rescan_old' => 'Rescan old transactions', - 'update_bill' => 'Update bill', - 'updated_bill' => 'Updated bill ":name"', - 'store_new_bill' => 'Store new bill', - 'stored_new_bill' => 'Stored new bill ":name"', - 'cannot_scan_inactive_bill' => 'Inactive bills cannot be scanned.', - 'rescanned_bill' => 'Rescanned everything.', - 'bill_date_little_relevance' => 'The only part of this date used by Firefly is the day. It is only useful when your bill arrives at exactly the same date every month. If the payment date of your bills varies, simply use the first of the month.', + 'between_amounts' => 'entre :low et :high.', + 'repeats' => 'Répétitions', + 'connected_journals' => 'Opérations liées', + 'auto_match_on' => 'Automatiquement mis en correspondance par Firefly', + 'auto_match_off' => 'Pas mis automatiquement en correspondance par Firefly', + 'next_expected_match' => 'Prochain montant attendu', + 'delete_bill' => 'Supprimer la facture ":name"', + 'deleted_bill' => 'Facture ":name" supprimée', + 'edit_bill' => 'Modifier la facture : ":name"', + 'more' => 'Plus', + 'rescan_old' => 'Réanalyser les anciennes opérations', + 'update_bill' => 'Mettre à jour la facture', + 'updated_bill' => 'Facture ":name" mise à jour', + 'store_new_bill' => 'Créer une nouvelle facture', + 'stored_new_bill' => 'Nouvelle facture ":name" créée', + 'cannot_scan_inactive_bill' => 'Les factures inactives ne peuvent pas être analysées.', + 'rescanned_bill' => 'Réanalyser tout.', + 'bill_date_little_relevance' => 'La seule partie de cette date utilisée par Firefly est le jour. Il n’est utile que lorsque votre facture arrive à exactement la même date chaque mois. Si la date de paiement de vos factures varie, il suffit d’utiliser le premier jour du mois.', + 'average_bill_amount_year' => 'Average bill amount (:year)', + 'average_bill_amount_overall' => 'Average bill amount (overall)', // accounts: - 'details_for_asset' => 'Details for asset account ":name"', - 'details_for_expense' => 'Details for expense account ":name"', - 'details_for_revenue' => 'Details for revenue account ":name"', - 'details_for_cash' => 'Details for cash account ":name"', - 'store_new_asset_account' => 'Store new asset account', - 'store_new_expense_account' => 'Store new expense account', - 'store_new_revenue_account' => 'Store new revenue account', - 'edit_asset_account' => 'Edit asset account ":name"', - 'edit_expense_account' => 'Edit expense account ":name"', - 'edit_revenue_account' => 'Edit revenue account ":name"', - 'delete_asset_account' => 'Delete asset account ":name"', - 'delete_expense_account' => 'Delete expense account ":name"', - 'delete_revenue_account' => 'Delete revenue account ":name"', - 'asset_deleted' => 'Successfully deleted asset account ":name"', - 'expense_deleted' => 'Successfully deleted expense account ":name"', - 'revenue_deleted' => 'Successfully deleted revenue account ":name"', - 'update_asset_account' => 'Update asset account', - 'update_expense_account' => 'Update expense account', - 'update_revenue_account' => 'Update revenue account', - 'make_new_asset_account' => 'Create a new asset account', - 'make_new_expense_account' => 'Create a new expense account', - 'make_new_revenue_account' => 'Create a new revenue account', - 'asset_accounts' => 'Asset accounts', - 'expense_accounts' => 'Expense accounts', - 'revenue_accounts' => 'Revenue accounts', - 'cash_accounts' => 'Cash accounts', - 'Cash account' => 'Cash account', + 'details_for_asset' => 'Détails pour le compte d’actif ":name"', + 'details_for_expense' => 'Détail du compte de dépenses ":name"', + 'details_for_revenue' => 'Détails du comptes de recettes ":name"', + 'details_for_cash' => 'Détails pour le compte de trésorerie ":name"', + 'store_new_asset_account' => 'Créer un nouveau compte d’actif', + 'store_new_expense_account' => 'Créer un nouveau compte de dépenses', + 'store_new_revenue_account' => 'Créer un compte de recettes', + 'edit_asset_account' => 'Modifier le compte d’actif ":name"', + 'edit_expense_account' => 'Modifier le compte de dépenses ";name"', + 'edit_revenue_account' => 'Modifier le compte de recettes ":name"', + 'delete_asset_account' => 'Supprimer le compte d’actif ":name"', + 'delete_expense_account' => 'Supprimer le compte de dépenses ":name"', + 'delete_revenue_account' => 'Supprimer le compte de recettes ":name"', + 'asset_deleted' => 'Compte d’actif ":name" correctement supprimé', + 'expense_deleted' => 'Compte de dépenses ":name" correctement supprimé', + 'revenue_deleted' => 'Compte de recettes ":name" correctement supprimé', + 'update_asset_account' => 'Mettre à jour le compte d’actif', + 'update_expense_account' => 'Mettre à jour le compte de dépenses', + 'update_revenue_account' => 'Mettre à jour le compte de recettes', + 'make_new_asset_account' => 'Créer un nouveau compte d’actif', + 'make_new_expense_account' => 'Créer un nouveau compte de dépenses', + 'make_new_revenue_account' => 'Créer un nouveau compte de recettes', + 'asset_accounts' => 'Comptes d’actif', + 'expense_accounts' => 'Comptes de dépenses', + 'revenue_accounts' => 'Comptes de recettes', + 'cash_accounts' => 'Comptes de trésorerie', + 'Cash account' => 'Compte de trésorerie', 'accountExtraHelp_asset' => '', 'accountExtraHelp_expense' => '', 'accountExtraHelp_revenue' => '', - 'account_type' => 'Account type', - 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:', - 'stored_new_account' => 'New account ":name" stored!', - 'updated_account' => 'Updated account ":name"', - 'credit_card_options' => 'Credit card options', + 'account_type' => 'Type de compte', + 'save_transactions_by_moving' => 'Enregistrer ces opération(s) en les déplaçant vers un autre compte :', + 'stored_new_account' => 'Nouveau compte ":name" créé !', + 'updated_account' => 'Nom du compte ":name"', + 'credit_card_options' => 'Cartes de crédit', // categories: - 'new_category' => 'New category', - 'create_new_category' => 'Create a new category', - 'without_category' => 'Without a category', - 'update_category' => 'Update category', - 'updated_category' => 'Updated category ":name"', - 'categories' => 'Categories', - 'edit_category' => 'Edit category ":name"', - 'no_category' => '(no category)', - 'category' => 'Category', - 'delete_category' => 'Delete category ":name"', - 'deleted_category' => 'Deleted category ":name"', - 'store_category' => 'Store new category', + 'new_category' => 'Nouvelle catégorie', + 'create_new_category' => 'Créer une nouvelle catégorie', + 'without_category' => 'Sans catégorie', + 'update_category' => 'Modifier la catégorie', + 'updated_category' => 'Catégorie ":name" mise à jour', + 'categories' => 'Catégories', + 'edit_category' => 'Modifier la catégorie ":name"', + 'no_category' => '(aucune catégorie)', + 'category' => 'Catégorie', + 'delete_category' => 'Supprimer la catégorie ":name"', + 'deleted_category' => 'Catégorie ":name" supprimée', + 'store_category' => 'Créer une nouvelle catgorie', 'stored_category' => 'Stored new category ":name"', - 'without_category_between' => 'Without category between :start and :end', + 'without_category_between' => 'Sans catégorie entre :start et :end', // transactions: - 'update_withdrawal' => 'Update withdrawal', - 'update_deposit' => 'Update deposit', - 'update_transfer' => 'Update transfer', - 'updated_withdrawal' => 'Updated withdrawal ":description"', - 'updated_deposit' => 'Updated deposit ":description"', - 'updated_transfer' => 'Updated transfer ":description"', - 'delete_withdrawal' => 'Delete withdrawal ":description"', - 'delete_deposit' => 'Delete deposit ":description"', - 'delete_transfer' => 'Delete transfer ":description"', - 'deleted_withdrawal' => 'Successfully deleted withdrawal ":description"', - 'deleted_deposit' => 'Successfully deleted deposit ":description"', - 'deleted_transfer' => 'Successfully deleted transfer ":description"', - 'stored_journal' => 'Successfully created new transaction ":description"', - 'select_transactions' => 'Select transactions', + 'update_withdrawal' => 'Mettre à jour un retrait', + 'update_deposit' => 'Mettre à jour un dépôt', + 'update_transfer' => 'Mettre à jour un transfert', + 'updated_withdrawal' => 'Retrait ":description" mis à jour', + 'updated_deposit' => 'Dépôt ":description" mis à jour', + 'updated_transfer' => 'Transfert ":description" mis à jour', + 'delete_withdrawal' => 'Supprimer le retrait ":description"', + 'delete_deposit' => 'Supprimer le dépôt ":description"', + 'delete_transfer' => 'Supprimer le transfert ":description"', + 'deleted_withdrawal' => 'Retrait ":name" correctement supprimé', + 'deleted_deposit' => 'Dépot ":name" correctement supprimé', + 'deleted_transfer' => 'Opération ":name" correctement supprimée', + 'stored_journal' => 'Opération créée avec succès ":description"', + 'select_transactions' => 'Sélectionner des opérations', 'stop_selection' => 'Stop selecting transactions', - 'edit_selected' => 'Edit selected', - 'delete_selected' => 'Delete selected', + 'edit_selected' => 'Modifier la sélection', + 'delete_selected' => 'Supprimer la sélection', 'mass_delete_journals' => 'Delete a number of transactions', - 'mass_edit_journals' => 'Edit a number of transactions', - 'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.', + 'mass_edit_journals' => 'Modifier un certain nombre d’opérations', + 'cannot_edit_other_fields' => 'Vous ne peut pas modifier en masse d\'autres champs que ceux ici, car il n’y a pas de place pour tous les montrer. S’il vous plaît suivez le lien et modifiez les par un par un, si vous devez modifier ces champs.', 'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious.', - 'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).', - 'mass_edited_transactions_success' => 'Updated :amount transaction(s)', + 'mass_deleted_transactions_success' => 'Montant des opérations supprimées : :amount.', + 'mass_edited_transactions_success' => 'Montant des opérations mises à jour : :amount', // new user: - 'welcome' => 'Welcome to Firefly!', - 'createNewAsset' => 'Create a new asset account to get started. ' . - 'This will allow you to create transactions and start your financial management', - 'createNewAssetButton' => 'Create new asset account', + 'welcome' => 'Bienvenue sur Firefly !', + 'createNewAsset' => 'Créer un nouveau compte d’actif pour commencer. ' . + 'Cela vous permettra de créer des opérations et de commencer votre gestion financière', + 'createNewAssetButton' => 'Créer un nouveau compte d’actif', // home page: - 'yourAccounts' => 'Your accounts', - 'budgetsAndSpending' => 'Budgets and spending', - 'savings' => 'Savings', - 'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel', - 'createPiggyToContinue' => 'Create piggy banks to fill this panel.', - 'newWithdrawal' => 'New expense', - 'newDeposit' => 'New deposit', - 'newTransfer' => 'New transfer', - 'moneyIn' => 'Money in', - 'moneyOut' => 'Money out', - 'billsToPay' => 'Bills to pay', - 'billsPaid' => 'Bills paid', - 'viewDetails' => 'View details', - 'divided' => 'divided', - 'toDivide' => 'left to divide', + 'yourAccounts' => 'Vos comptes', + 'budgetsAndSpending' => 'Budgets et dépenses', + 'savings' => 'Épargne', + 'markAsSavingsToContinue' => 'Marquez votre compte d’actif comme "Compte d\'épargne" pour remplir ce panneau', + 'createPiggyToContinue' => 'Créer des tirelires pour remplir ce panneau.', + 'newWithdrawal' => 'Nouvelle dépense', + 'newDeposit' => 'Nouveau dépôt', + 'newTransfer' => 'Nouveau transfert', + 'moneyIn' => 'Argent entrant', + 'moneyOut' => 'Argent sortant', + 'billsToPay' => 'Factures à payer', + 'billsPaid' => 'Factures payées', + 'viewDetails' => 'Voir les détails', + 'divided' => 'divisé', + 'toDivide' => 'Restant à dépenser', // menu and titles, should be recycled as often as possible: - 'toggleNavigation' => 'Toggle navigation', - 'currency' => 'Currency', - 'preferences' => 'Preferences', - 'logout' => 'Logout', - 'searchPlaceholder' => 'Search...', - 'dashboard' => 'Dashboard', - 'currencies' => 'Currencies', - 'accounts' => 'Accounts', - 'Asset account' => 'Asset account', - 'Default account' => 'Asset account', - 'Expense account' => 'Expense account', - 'Revenue account' => 'Revenue account', - 'Initial balance account' => 'Initial balance account', + 'toggleNavigation' => 'Basculer la navigation', + 'currency' => 'Devise', + 'preferences' => 'Préférences', + 'logout' => 'Se déconnecter', + 'searchPlaceholder' => 'Rechercher...', + 'dashboard' => 'Tableau de Bord', + 'currencies' => 'Devises', + 'accounts' => 'Comptes', + 'Asset account' => 'Compte d’actif', + 'Default account' => 'Compte d’actif', + 'Expense account' => 'Compte de dépenses', + 'Revenue account' => 'Compte de recettes', + 'Initial balance account' => 'Balance initiale', 'budgets' => 'Budgets', 'tags' => 'Tags', - 'reports' => 'Reports', - 'transactions' => 'Transactions', - 'expenses' => 'Expenses', - 'income' => 'Revenue / income', + 'reports' => 'Rapports', + 'transactions' => 'Opérations', + 'expenses' => 'Dépenses', + 'income' => 'Recette / revenu', 'transfers' => 'Transferts', - 'moneyManagement' => 'Money management', - 'piggyBanks' => 'Piggy banks', - 'bills' => 'Bills', - 'createNew' => 'Create new', - 'withdrawal' => 'Withdrawal', - 'deposit' => 'Deposit', - 'account' => 'Account', - 'transfer' => 'Transfer', - 'Withdrawal' => 'Withdrawal', - 'Deposit' => 'Deposit', - 'Transfer' => 'Transfer', - 'bill' => 'Bill', - 'yes' => 'Yes', - 'no' => 'No', - 'amount' => 'Amount', - 'newBalance' => 'New balance', - 'overview' => 'Overview', - 'saveOnAccount' => 'Save on account', - 'unknown' => 'Unknown', - 'daily' => 'Daily', - 'weekly' => 'Weekly', - 'monthly' => 'Monthly', - 'quarterly' => 'Quarterly', - 'half-year' => 'Every six months', - 'yearly' => 'Yearly', - 'profile' => 'Profile', + 'moneyManagement' => 'Gérer les comptes', + 'piggyBanks' => 'Tirelires', + 'bills' => 'Factures', + 'createNew' => 'Créer un nouveau', + 'withdrawal' => 'Retrait', + 'deposit' => 'Dépôt', + 'account' => 'Compte', + 'transfer' => 'Transfert', + 'Withdrawal' => 'Retrait', + 'Deposit' => 'Dépôt', + 'Transfer' => 'Transfert', + 'bill' => 'Facture', + 'yes' => 'Oui', + 'no' => 'Non', + 'amount' => 'Montant', + 'newBalance' => 'Nouveau solde', + 'overview' => 'Vue globale', + 'saveOnAccount' => 'Sauvegarder le compte', + 'unknown' => 'Inconnu', + 'daily' => 'Journalier', + 'monthly' => 'Mensuel', + 'profile' => 'Profil', + 'errors' => 'Errors', // reports: - 'report_default' => 'Default financial report for :start until :end', - 'report_audit' => 'Transaction history overview for :start until :end', - 'quick_link_reports' => 'Quick links', - 'quick_link_default_report' => 'Default financial report', - 'quick_link_audit_report' => 'Transaction history overview', - 'report_this_month_quick' => 'Current month, all accounts', - 'report_this_year_quick' => 'Current year, all accounts', - 'report_this_fiscal_year_quick' => 'Current fiscal year, all accounts', + 'report_default' => 'Rapport financier par défaut du :start au :end', + 'report_audit' => 'Historique des transactions du :start au :end', + 'quick_link_reports' => 'Liens rapides', + 'quick_link_default_report' => 'Rapport financier par défaut', + 'quick_link_audit_report' => 'Historique des transactions', + 'report_this_month_quick' => 'Mois en cours, tous les comptes', + 'report_this_year_quick' => 'Année en cours, tous les comptes', + 'report_this_fiscal_year_quick' => 'Année fiscale en cours, tous les comptes', 'report_all_time_quick' => 'All-time, all accounts', 'reports_can_bookmark' => 'Remember that reports can be bookmarked.', - 'incomeVsExpenses' => 'Income vs. expenses', - 'accountBalances' => 'Account balances', - 'balanceStartOfYear' => 'Balance at start of year', - 'balanceEndOfYear' => 'Balance at end of year', - 'balanceStartOfMonth' => 'Balance at start of month', - 'balanceEndOfMonth' => 'Balance at end of month', + 'incomeVsExpenses' => 'Revenus vs dépenses', + 'accountBalances' => 'Solde du compte', + 'balanceStartOfYear' => 'Solde au début de l\'année', + 'balanceEndOfYear' => 'Solde à la fin de l\'année', + 'balanceStartOfMonth' => 'Solde au début du mois', + 'balanceEndOfMonth' => 'Solde à la fin du mois', 'balanceStart' => 'Balance at start of period', 'balanceEnd' => 'Balance at end of period', - 'reportsOwnAccounts' => 'Reports for your own accounts', - 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', + 'reportsOwnAccounts' => 'Rapport pour vos propres comptes', + 'reportsOwnAccountsAndShared' => 'Rapport pour vos comptes et ceux partagés', 'splitByAccount' => 'Split by account', 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', 'coveredWithTags' => 'Covered with tags', 'leftUnbalanced' => 'Left unbalanced', 'expectedBalance' => 'Expected balance', 'outsideOfBudgets' => 'Outside of budgets', - 'leftInBudget' => 'Left in budget', - 'sumOfSums' => 'Sum of sums', - 'noCategory' => '(no category)', + 'leftInBudget' => 'Budget restant', + 'sumOfSums' => 'Montant des sommes', + 'noCategory' => '(aucune catégorie)', 'notCharged' => 'Not charged (yet)', - 'inactive' => 'Inactive', - 'active' => 'Active', - 'difference' => 'Difference', + 'inactive' => 'Désactivé', + 'active' => 'Actif', + 'difference' => 'Différence', 'in' => 'In', 'out' => 'Out', 'topX' => 'top :number', - 'showTheRest' => 'Show everything', - 'hideTheRest' => 'Show only the top :number', - 'sum_of_year' => 'Sum of year', - 'sum_of_years' => 'Sum of years', - 'average_of_year' => 'Average of year', + 'showTheRest' => 'Tout Afficher', + 'hideTheRest' => 'Afficher uniquement le top :number', + 'sum_of_year' => 'Total sur l’année', + 'sum_of_years' => 'Total des années', + 'average_of_year' => 'Moyenne sur l\'année', 'average_of_years' => 'Average of years', - 'categories_earned_in_year' => 'Categories (by earnings)', - 'categories_spent_in_year' => 'Categories (by spendings)', - 'report_type' => 'Report type', - 'report_type_default' => 'Default financial report', - 'report_type_audit' => 'Transaction history overview (audit)', - 'report_type_meta-history' => 'Categories, budgets and bills overview', + 'categories_earned_in_year' => 'Catégories (selon le revenu)', + 'categories_spent_in_year' => 'Catégories (par dépenses)', + 'report_type' => 'Type de rapport', + 'report_type_default' => 'Rapport financier par défaut', + 'report_type_audit' => 'Historique des transactions', + 'report_type_meta-history' => 'Vue d’ensemble des budgets, des catégories et des factures', 'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.', 'report_included_accounts' => 'Included accounts', - 'report_date_range' => 'Date range', - 'report_include_help' => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.', - 'report_preset_ranges' => 'Pre-set ranges', - 'shared' => 'Shared', - 'fiscal_year' => 'Fiscal year', + 'report_date_range' => 'Intervalle de dates', + 'report_include_help' => 'Dans tous les cas, les transferts vers des comptes partagés comptes comme des dépenses, et les transferts depuis les comptes partagés comme un revenu.', + 'report_preset_ranges' => 'Pré-configurer les étendues', + 'shared' => 'Partagé', + 'fiscal_year' => 'Année fiscale', 'income_entry' => 'Income from account ":name" between :start and :end', - 'expense_entry' => 'Expenses to account ":name" between :start and :end', - 'category_entry' => 'Expenses in category ":name" between :start and :end', - 'budget_spent_amount' => 'Expenses in budget ":budget" between :start and :end', - 'balance_amount' => 'Expenses in budget ":budget" paid from account ":account" between :start and :end', + 'expense_entry' => 'Dépenses du compte ":name" entre le :start et le :end', + 'category_entry' => 'Dépenses dans la catégorie ":name" entre le :start et le :end', + 'budget_spent_amount' => 'Dépenses dans le budget ":budget" entre le :start et le :end', + 'balance_amount' => 'Dépenses dans le budget ":budget" payé depuis le compte ":account" entre le :start et le :end', 'no_audit_activity' => 'No activity was recorded on account :account_name between :start and :end.', 'audit_end_balance' => 'Account balance of :account_name at the end of :end was: :balance', // charts: 'chart' => 'Chart', - 'dayOfMonth' => 'Day of the month', - 'month' => 'Month', + 'dayOfMonth' => 'Jour du mois', + 'month' => 'Mois', 'budget' => 'Budget', - 'spent' => 'Spent', - 'earned' => 'Earned', + 'spent' => 'Dépensé', + 'earned' => 'Gagné', 'overspent' => 'Overspent', - 'left' => 'Left', - 'no_budget' => '(no budget)', - 'maxAmount' => 'Maximum amount', - 'minAmount' => 'Minumum amount', + 'left' => 'Gauche', + 'no_budget' => '(pas de budget)', + 'maxAmount' => 'Montant maximum', + 'minAmount' => 'Montant minimum', 'billEntry' => 'Current bill entry', - 'name' => 'Name', + 'name' => 'Nom', 'date' => 'Date', - 'paid' => 'Paid', - 'unpaid' => 'Unpaid', - 'day' => 'Day', - 'budgeted' => 'Budgeted', - 'period' => 'Period', + 'paid' => 'Payé', + 'unpaid' => 'Impayé', + 'day' => 'Jour', + 'budgeted' => 'Budgétisé', + 'period' => 'Période', 'balance' => 'Balance', 'summary' => 'Summary', 'sum' => 'Sum', @@ -742,30 +649,31 @@ return [ 'balanceFor' => 'Balance for :name', // piggy banks: - 'piggy_bank' => 'Piggy bank', - 'new_piggy_bank' => 'Create new piggy bank', - 'store_piggy_bank' => 'Store new piggy bank', - 'stored_piggy_bank' => 'Store new piggy bank ":name"', - 'account_status' => 'Account status', + 'add_money_to_piggy' => 'Add money to piggy bank ":name"', + 'piggy_bank' => 'Tirelire', + 'new_piggy_bank' => 'Créer une nouvelle tirelire', + 'store_piggy_bank' => 'Créer une nouvelle tirelire', + 'stored_piggy_bank' => 'Créer une nouvelle tirelire ":name"', + 'account_status' => 'Statut du compte', 'left_for_piggy_banks' => 'Left for piggy banks', - 'sum_of_piggy_banks' => 'Sum of piggy banks', + 'sum_of_piggy_banks' => 'Somme des tirelires', 'saved_so_far' => 'Saved so far', 'left_to_save' => 'Left to save', 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', 'add' => 'Add', 'remove' => 'Remove', - 'max_amount_add' => 'The maximum amount you can add is', - 'max_amount_remove' => 'The maximum amount you can remove is', + 'max_amount_add' => 'Le montant maximum que vous pouvez ajouter est', + 'max_amount_remove' => 'Le montant maximum que vous pouvez supprimer est', 'update_piggy_button' => 'Update piggy bank', 'update_piggy_title' => 'Update piggy bank ":name"', 'updated_piggy_bank' => 'Updated piggy bank ":name"', - 'details' => 'Details', - 'events' => 'Events', - 'target_amount' => 'Target amount', - 'start_date' => 'Start date', - 'target_date' => 'Target date', - 'no_target_date' => 'No target date', + 'details' => 'Détails', + 'events' => 'Evènements', + 'target_amount' => 'Montant cible', + 'start_date' => 'Date de début', + 'target_date' => 'Date cible', + 'no_target_date' => 'Aucune date butoir', 'todo' => 'to do', 'table' => 'Table', 'piggy_bank_not_exists' => 'Piggy bank no longer exists.', @@ -773,66 +681,100 @@ return [ 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date', 'delete_piggy_bank' => 'Delete piggy bank ":name"', 'cannot_add_amount_piggy' => 'Could not add :amount to ":name".', - 'deleted_piggy_bank' => 'Deleted piggy bank ":name"', + 'deleted_piggy_bank' => 'Tirelire ":name" supprimée', 'added_amount_to_piggy' => 'Added :amount to ":name"', 'removed_amount_from_piggy' => 'Removed :amount from ":name"', 'cannot_remove_amount_piggy' => 'Could not remove :amount from ":name".', // tags 'regular_tag' => 'Just a regular tag.', - 'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.', - 'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.', + 'balancing_act' => 'Un tag prend au maximum deux opérations : une dépense et un transfert. Ils s\'équilibreront mutuellement.', + 'advance_payment' => 'Un tag accepte une dépense et un nombre quelconque de dépôts visant à rembourser la dépense originale.', 'delete_tag' => 'Supprimer le tag ":tag"', - 'deleted_tag' => 'Deleted tag ":tag"', - 'new_tag' => 'Make new tag', - 'edit_tag' => 'Editer le tag ":tag"', + 'deleted_tag' => 'Tag ":tag" supprimé', + 'new_tag' => 'Créer un nouveau tag', + 'edit_tag' => 'Modifier le tag ":tag"', 'updated_tag' => 'Updated tag ":tag"', 'created_tag' => 'Tag ":tag" has been created!', 'no_year' => 'No year set', 'no_month' => 'No month set', - 'tag_title_nothing' => 'Default tags', + 'tag_title_nothing' => 'Tags par défaut', 'tag_title_balancingAct' => 'Balancing act tags', - 'tag_title_advancePayment' => 'Advance payment tags', - 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like expensive, bill or for-party. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called Christmas dinner with friends and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.', - 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.', + 'tag_title_advancePayment' => 'Tags de paiement anticipé', + 'tags_introduction' => 'Les tags sont généralement des mots au singulier, conçus pour grouper rapidement des éléments en utilisant des mots comme cher, factures ou pour-fêtes. Dans Firefly III, les tags peuvent avoir plus de propriétés comme une date, la description et l’emplacement. Cela vous permet de réunir des opérations de façon plus significative. Par exemple, vous pourriez faire un tag appelé Dîner de Noël avec des amis et ajouter des informations sur le restaurant. Ces tags sont au singulier, vous devez seulement les utiliser pour une occasion unique, peut-être avec plusieurs opérations.', + 'tags_group' => 'Les tags groupent des opérations ensemble, ce qui permet de stocker des remboursements (dans le cas où vous avancer de l\'argent aux autres) et d\'autres types "d\'équilibres" où résumer les dépenses (les remboursements de votre nouvelle TV) ou quand les dépenses et les dépôts s\'annulent les uns les autres (achat de quelque chose avec de l\'argent mis de coté). C\'est comme vous souhaitez. Utiliser les tags à l\'ancienne est toujours possible.', 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', // administration 'administration' => 'Administration', - 'user_administration' => 'User administration', - 'list_all_users' => 'All users', - 'all_users' => 'All users', + 'user_administration' => 'Gestion des utilisateurs', + 'list_all_users' => 'Tous les utilisateurs', + 'all_users' => 'Tous les utilisateurs', + 'all_blocked_domains' => 'All blocked domains', + 'blocked_domains' => 'Blocked domains', + 'no_domains_banned' => 'No domains blocked', + 'all_user_domains' => 'All user email address domains', + 'all_domains_is_filtered' => 'This list does not include already blocked domains.', + 'domain_now_blocked' => 'Domain :domain is now blocked', + 'domain_now_unblocked' => 'Domain :domain is now unblocked', + 'manual_block_domain' => 'Block a domain by hand', + 'block_domain' => 'Block domain', + 'no_domain_filled_in' => 'No domain filled in', + 'domain_already_blocked' => 'Domain :domain is already blocked', + 'domain_is_now_blocked' => 'Domain :domain is now blocked', // split a transaction: 'transaction_meta_data' => 'Transaction meta-data', - 'transaction_dates' => 'Transaction dates', + 'transaction_dates' => 'Date de l\'opération', 'splits' => 'Splits', 'split_title_withdrawal' => 'Split your new withdrawal', 'split_intro_one_withdrawal' => 'Firefly supports the "splitting" of a withdrawal.', - 'split_intro_two_withdrawal' => 'It means that the amount of money you\'ve spent is divided between several destination expense accounts, budgets or categories.', + 'split_intro_two_withdrawal' => 'Cela signifie que le montant d’argent que vous avez dépensé est réparti entre plusieurs budgets, catégories ou comptes de revenus.', 'split_intro_three_withdrawal' => 'For example: you could split your :total groceries so you pay :split_one from your "daily groceries" budget and :split_two from your "cigarettes" budget.', 'split_table_intro_withdrawal' => 'Split your withdrawal in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', 'store_splitted_withdrawal' => 'Store splitted withdrawal', 'update_splitted_withdrawal' => 'Update splitted withdrawal', + 'split_title_deposit' => 'Split your new deposit', + 'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.', + 'split_intro_two_deposit' => 'Cela signifie que le montant d’argent que vous avez gagné est réparti entre plusieurs catégories ou comptes de revenus source.', + 'split_intro_three_deposit' => 'Par exemple : vous pouvez fractionner votre salaire de :total donc vous obtenez :split_one comme votre salaire de base et :split_two comme un remboursement des dépenses faites.', + 'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_deposit' => 'Store splitted deposit', + 'split_title_transfer' => 'Split your new transfer', + 'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.', + 'split_intro_two_transfer' => 'Cela signifie que le montant d’argent que vous déplacez est divisé entre plusieurs catégories ou tirelires.', + 'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.', + 'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_transfer' => 'Store splitted transfer', + 'add_another_split' => 'Add another split', + 'split-transactions' => 'Split transactions', + 'split-new-transaction' => 'Split a new transaction', + 'do_split' => 'Do a split', + 'split_this_withdrawal' => 'Split this withdrawal', + 'split_this_deposit' => 'Split this deposit', + 'split_this_transfer' => 'Split this transfer', + 'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.', + 'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.', + 'no_edit_multiple_left' => 'You have selected no valid transactions to edit.', - 'split_title_deposit' => 'Split your new deposit', - 'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.', - 'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.', - 'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.', - 'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', - 'store_splitted_deposit' => 'Store splitted deposit', - - 'split_title_transfer' => 'Split your new transfer', - 'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.', - 'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.', - 'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.', - 'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', - 'store_splitted_transfer' => 'Store splitted transfer', - - 'add_another_split' => 'Add another split', - 'split-transactions' => 'Split transactions', - 'split-new-transaction' => 'Split a new transaction', - - + // import + 'configuration_file_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you.', + 'import_data_index' => 'Index', + 'import_file_type_csv' => 'CSV (comma separated values)', + 'import_file_type_help' => 'Select the type of file you will upload', + 'import_start' => 'Start the import', + 'configure_import' => 'Further configure your import', + 'import_finish_configuration' => 'Finish configuration', + 'settings_for_import' => 'Settings', + 'import_complete' => 'Import configuration complete!', + 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you need to execute the following command in your console. Unfortunately, a web-based import is not yet possible.', + 'import_download_config' => 'Download configuration', + 'import_start_import' => 'Start import', + 'import_intro_beta' => 'The import function of Firefly III is in beta. Many users of Firefly III have tried many different files. Although each individual compontent of this import routine works (really), the combination might break. If your file cannot be imported by Firefly, please read this wiki page so I can fix the problem you have run into.', + 'import_data' => 'Import data', + 'import_data_full' => 'Import data into Firefly III', + 'import' => 'Import', + 'import_intro_text' => 'Welcome to the Firefly III data import routine. At the moment, this routine can help you import files into Firefly. To do so, you must download or export transactions from other systems or software, and upload them here. The next steps will let you help Firefly III determin what the content is of your file, and how to handle it. Please select a file, and read all instructions carefully.', + 'import_file_help' => 'Select your file', ]; diff --git a/resources/lang/fr_FR/form.php b/resources/lang/fr_FR/form.php index 6babd72883..ed8b0d0163 100644 --- a/resources/lang/fr_FR/form.php +++ b/resources/lang/fr_FR/form.php @@ -14,127 +14,136 @@ return [ 'bank_balance' => 'Solde', 'savings_balance' => 'Solde de l\'épargne', 'credit_card_limit' => 'Limite de carte de crédit', - 'automatch' => 'Match automatically', - 'skip' => 'Skip', + 'automatch' => 'Correspondre automatiquement', + 'skip' => 'Ignorer', 'name' => 'Nom', 'active' => 'Actif', 'amount_min' => 'Montant minimum', 'amount_max' => 'Montant maximum', - 'match' => 'Matches on', - 'repeat_freq' => 'Repeats', - 'journal_currency_id' => 'Currency', - 'journal_amount' => 'Amount', - 'journal_asset_source_account' => 'Asset account (source)', - 'journal_source_account_name' => 'Revenue account (source)', - 'journal_source_account_id' => 'Asset account (source)', + 'match' => 'Correspondre à', + 'repeat_freq' => 'Répétitions', + 'journal_currency_id' => 'Devise', + 'journal_amount' => 'Montant', + 'journal_asset_source_account' => 'Compte d’actif (source)', + 'journal_source_account_name' => 'Compte de recettes (source)', + 'journal_source_account_id' => 'Compte d’actif (source)', 'account_from_id' => 'Compte d\'origine', 'account_to_id' => 'Compte de destination', - 'journal_destination_account_id' => 'Asset account (destination)', - 'asset_destination_account' => 'Asset account (destination)', - 'asset_source_account' => 'Asset account (source)', + 'journal_destination_account_id' => 'Compte d’actif (destination)', + 'asset_destination_account' => 'Compte d’actif (destination)', + 'asset_source_account' => 'Compte d’actif (source)', 'journal_description' => 'Description', - 'split_journal' => 'Split this transaction', - 'split_journal_explanation' => 'Split this transaction in multiple parts', - 'currency' => 'Currency', - 'account_id' => 'Asset account', + 'split_journal' => 'Ventiler cette opération', + 'split_journal_explanation' => 'Diviser cette opération en plusieurs parties', + 'currency' => 'Devise', + 'account_id' => 'Compte d’actif', 'budget_id' => 'Budget', 'openingBalance' => 'Solde initial', - 'tagMode' => 'Tag mode', - 'tagPosition' => 'Tag location', + 'tagMode' => 'Mode tag', + 'tagPosition' => 'Emplacement du tag', 'virtualBalance' => 'Solde virtuel', - 'longitude_latitude' => 'Location', - 'targetamount' => 'Target amount', - 'accountRole' => 'Account role', - 'openingBalanceDate' => 'Opening balance date', + 'longitude_latitude' => 'Emplacement', + 'targetamount' => 'Montant cible', + 'accountRole' => 'Rôle du compte', + 'openingBalanceDate' => 'Date du solde initial', 'ccType' => 'Credit card payment plan', 'ccMonthlyPaymentDate' => 'Credit card monthly payment date', 'piggy_bank_id' => 'Tirelire', 'returnHere' => 'Return here', - 'returnHereExplanation' => 'After storing, return here to create another one.', - 'returnHereUpdateExplanation' => 'After updating, return here.', + 'returnHereExplanation' => 'Après enregistrement, revenir ici pour en créer un nouveau.', + 'returnHereUpdateExplanation' => 'Après mise à jour, revenir ici.', 'description' => 'Description', - 'expense_account' => 'Expense account', - 'revenue_account' => 'Revenue account', - 'amount' => 'Amount', + 'expense_account' => 'Compte de dépenses', + 'revenue_account' => 'Compte de recettes', + 'amount' => 'Montant', 'date' => 'Date', 'interest_date' => 'Interest date', 'book_date' => 'Book date', 'process_date' => 'Processing date', - 'category' => 'Category', + 'category' => 'Catégorie', 'tags' => 'Tags', - 'deletePermanently' => 'Delete permanently', - 'cancel' => 'Cancel', - 'targetdate' => 'Target date', + 'deletePermanently' => 'Supprimer définitivement', + 'cancel' => 'Annuler', + 'targetdate' => 'Date cible', 'tag' => 'Tag', - 'under' => 'Under', - 'symbol' => 'Symbol', + 'under' => 'En dessous de', + 'symbol' => 'Symbole', 'code' => 'Code', 'iban' => 'IBAN', - 'accountNumber' => 'Account number', - 'csv' => 'CSV file', - 'has_headers' => 'Headers', - 'date_format' => 'Date format', - 'csv_config' => 'CSV import configuration', + 'accountNumber' => 'N° de compte', + 'has_headers' => 'Entêtes ', + 'date_format' => 'Format de la date', 'specifix' => 'Bank- or file specific fixes', - 'csv_import_account' => 'Default import account', - 'csv_delimiter' => 'CSV field delimiter', - 'attachments[]' => 'Attachments', - 'store_new_withdrawal' => 'Store new withdrawal', - 'store_new_deposit' => 'Store new deposit', - 'store_new_transfer' => 'Store new transfer', - 'add_new_withdrawal' => 'Add a new withdrawal', - 'add_new_deposit' => 'Add a new deposit', - 'add_new_transfer' => 'Add a new transfer', - 'noPiggybank' => '(no piggy bank)', - 'title' => 'Title', + 'attachments[]' => 'Pièces jointes', + 'store_new_withdrawal' => 'Enregistrer un nouveau retrait', + 'store_new_deposit' => 'Enregistrer un nouveau dépôt', + 'store_new_transfer' => 'Enregistrer un nouveau transfert', + 'add_new_withdrawal' => 'Ajouter un nouveau retrait', + 'add_new_deposit' => 'Ajouter un nouveau dépôt', + 'add_new_transfer' => 'Ajouter un nouveau transfert', + 'noPiggybank' => '(aucun tirelire)', + 'title' => 'Titre', 'notes' => 'Notes', - 'filename' => 'File name', - 'mime' => 'Mime type', - 'size' => 'Size', - 'trigger' => 'Trigger', - 'stop_processing' => 'Stop processing', - 'start_date' => 'Start of range', - 'end_date' => 'End of range', - 'export_start_range' => 'Start of export range', - 'export_end_range' => 'End of export range', - 'export_format' => 'File format', - 'include_attachments' => 'Include uploaded attachments', - 'include_config' => 'Include configuration file', - 'include_old_uploads' => 'Include imported data', - 'accounts' => 'Export transactions from these accounts', - 'csv_comma' => 'A comma (,)', - 'csv_semicolon' => 'A semicolon (;)', - 'csv_tab' => 'A tab (invisible)', - 'delete_account' => 'Delete account ":name"', + 'filename' => 'Nom du fichier', + 'mime' => 'Type Mime', + 'size' => 'Taille', + 'trigger' => 'Déclencheur', + 'stop_processing' => 'Arrêter le traitement', + 'start_date' => 'Début de l\'étendue', + 'end_date' => 'Fin de l\'étendue', + 'export_start_range' => 'Début de l’étendue d’exportation', + 'export_end_range' => 'Fin de l’étendue d\'exportation', + 'export_format' => 'Format de fichier', + 'include_attachments' => 'Inclure des pièces jointes téléchargées', + 'include_config' => 'Inclure le fichier de configuration', + 'include_old_uploads' => 'Inclure les données importées', + 'accounts' => 'Exporter les opérations depuis ces comptes', + 'delete_account' => 'Supprimer le compte ":name"', 'delete_bill' => 'Supprimer la facture ":name"', - 'delete_budget' => 'Delete budget ":name"', - 'delete_category' => 'Delete category ":name"', - 'delete_currency' => 'Delete currency ":name"', - 'delete_journal' => 'Delete transaction with description ":description"', - 'delete_attachment' => 'Delete attachment ":name"', - 'delete_rule' => 'Delete rule ":title"', - 'delete_rule_group' => 'Delete rule group ":title"', - 'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?', - 'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?', - 'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?', - 'rule_areYouSure' => 'Are you sure you want to delete the rule titled ":title"?', - 'ruleGroup_areYouSure' => 'Are you sure you want to delete the rule group titled ":title"?', - 'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?', - 'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?', - 'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?', - 'piggyBank_areYouSure' => 'Are you sure you want to delete the piggy bank named ":name"?', - 'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?', - 'mass_journal_are_you_sure' => 'Are you sure you want to delete these transactions?', - 'tag_areYouSure' => 'Are you sure you want to delete the tag ":tag"?', - 'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.', - 'mass_make_selection' => 'You can still prevent items from being deleted by removing the checkbox.', - 'delete_all_permanently' => 'Delete selected permanently', - 'update_all_journals' => 'Update these transactions', - 'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.', - 'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.', - 'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.', - 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.', - 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.', - 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.', - 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.', + 'delete_budget' => 'Supprimer le budget ":name"', + 'delete_category' => 'Supprimer la catégorie ":name"', + 'delete_currency' => 'Supprimer la devise ":name"', + 'delete_journal' => 'Supprimer l\'opération ayant comme description ":description"', + 'delete_attachment' => 'Supprimer la pièce jointe ":name"', + 'delete_rule' => 'Supprimer la règle ":title"', + 'delete_rule_group' => 'Supprimer le groupe de filtres ":title"', + 'attachment_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la pièce jointe nommée ":name" ?', + 'account_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le compte nommé ": ame" ?', + 'bill_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la facture nommée ":name" ?', + 'rule_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la règle intitulée ":title" ?', + 'ruleGroup_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le groupe de règles intitulé ":title" ?', + 'budget_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le budget nommé ":name" ?', + 'category_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la catégorie nommée ":name" ?', + 'currency_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la devise nommée ":name" ?', + 'piggyBank_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la tirelire nommée ":name" ?', + 'journal_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la description de l\'opération ":description" ?', + 'mass_journal_are_you_sure' => 'Êtes-vous sûr de que vouloir supprimer ces opérations ?', + 'tag_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le tag ":tag" ?', + 'permDeleteWarning' => 'Supprimer quelque chose dans Firefly est permanent et ne peut pas être annulé.', + 'mass_make_selection' => 'Vous pouvez toujours empêcher des éléments d’être supprimés en décochant la case à cocher.', + 'delete_all_permanently' => 'Supprimer la selection définitivement', + 'update_all_journals' => 'Mettre à jour ces opérations', + 'also_delete_transactions' => 'La seule opération liée à ce compte sera aussi supprimée.|Les :count opérations liées à ce compte seront aussi supprimées.', + 'also_delete_rules' => 'La seule règle liée à ce groupe de règles sera aussi supprimée.|Les :count règles liées à ce groupe de règles seront aussi supprimées.', + 'also_delete_piggyBanks' => 'La seule tirelire liée à ce compte sera aussi supprimée.|Les :count tirelires liées à ce compte seront aussi supprimées.', + 'bill_keep_transactions' => 'La seule opération liée à cette facture ne sera pas supprimée.|Les :count opérations liées à cette facture ne seront pas supprimées.', + 'budget_keep_transactions' => 'La seule opération liée à ce budget ne sera pas supprimée.|Les :count opérations liées à ce budget ne seront pas supprimées.', + 'category_keep_transactions' => 'La seule opération liée à cette catégorie ne sera pas supprimée.|Les :count opérations liées à cette catégorie ne seront pas supprimées.', + 'tag_keep_transactions' => 'La seule opération liée à ce tag ne sera pas supprimée.|Les :count opérations liées à ce tag ne seront pas supprimées.', + + // admin + 'domain' => 'Domain', + + // import + 'import_file' => 'Import file', + 'configuration_file' => 'Configuration file', + 'import_file_type' => 'Import file type', + 'csv_comma' => 'Une virgule (,)', + 'csv_semicolon' => 'Un point-virgule (;)', + 'csv_tab' => 'Un onglet (invisible)', + 'csv_delimiter' => 'Délimiteur de champ CSV', + 'csv_import_account' => 'Compte d’importation par défaut', + 'csv_config' => 'Configuration d\'importation CSV', + + ]; diff --git a/resources/lang/fr_FR/help.php b/resources/lang/fr_FR/help.php index 6cee914b70..77facb6918 100644 --- a/resources/lang/fr_FR/help.php +++ b/resources/lang/fr_FR/help.php @@ -11,80 +11,75 @@ return [ // tour! 'main-content-title' => 'Bienvenue sur Firefly III', - 'main-content-text' => 'Do yourself a favor and follow this short guide to make sure you know your way around.', - 'sidebar-toggle-title' => 'Sidebar to create stuff', - 'sidebar-toggle-text' => 'Hidden under the plus icon are all the buttons to create new stuff. Accounts, transactions, everything!', - 'account-menu-title' => 'All your accounts', - 'account-menu-text' => 'Here you can find all the accounts you\'ve made.', + 'main-content-text' => 'Rendez-vous service et suivez ce petit guide. Vous saurez exactement comment tout fonctionne.', + 'sidebar-toggle-title' => 'Barre latérale pour créer quelque chose', + 'sidebar-toggle-text' => 'Sous l\'icone plus sont cachés tous les boutons permettant de créer quelque chose. Comptes, opérations, tous!', + 'account-menu-title' => 'Tous vos comptes', + 'account-menu-text' => 'Vous trouverez ici tous les comptes que vous avez fait.', 'budget-menu-title' => 'Budgets', - 'budget-menu-text' => 'Use this page to organise your finances and limit spending.', - 'report-menu-title' => 'Rapport', - 'report-menu-text' => 'Check this out when you want a solid overview of your finances.', - 'transaction-menu-title' => 'Transactions', + 'budget-menu-text' => 'Utilisez cette page pour organiser vos finances et limiter les dépenses.', + 'report-menu-title' => 'Rapports', + 'report-menu-text' => 'Cochez cette case si vous voulez un aperçu complet de vos finances.', + 'transaction-menu-title' => 'Opérations', 'transaction-menu-text' => 'Toutes les transactions que vous avez créé peuvent être trouvées ici.', 'option-menu-title' => 'Options', 'option-menu-text' => 'C\'est assez explicite.', 'main-content-end-title' => 'Fin !', 'main-content-end-text' => 'N\'oubliez pas que chaque page a un petit point d\'interrogation en haut à droite. Cliquez dessus pour obtenir de l\'aide concernant la page actuelle.', 'index' => 'index', - 'home' => 'home', - 'accounts-index' => 'accounts.index', - 'accounts-create' => 'accounts.create', - 'accounts-edit' => 'accounts.edit', - 'accounts-delete' => 'accounts.delete', - 'accounts-show' => 'accounts.show', - 'attachments-edit' => 'attachments.edit', - 'attachments-delete' => 'attachments.delete', - 'attachments-show' => 'attachments.show', - 'attachments-preview' => 'attachments.preview', - 'bills-index' => 'bills.index', - 'bills-create' => 'bills.create', - 'bills-edit' => 'bills.edit', - 'bills-delete' => 'bills.delete', - 'bills-show' => 'bills.show', - 'budgets-index' => 'budgets.index', - 'budgets-create' => 'budgets.create', - 'budgets-edit' => 'budgets.edit', - 'budgets-delete' => 'budgets.delete', - 'budgets-show' => 'budgets.show', - 'budgets-noBudget' => 'budgets.noBudget', - 'categories-index' => 'categories.index', - 'categories-create' => 'categories.create', - 'categories-edit' => 'categories.edit', - 'categories-delete' => 'categories.delete', - 'categories-show' => 'categories.show', - 'categories-show-date' => 'categories.show.date', - 'categories-noCategory' => 'categories.noCategory', - 'csv-index' => 'csv.index', - 'csv-column-roles' => 'csv.column-roles', - 'csv-map' => 'csv.map', - 'csv-download-config-page' => 'csv.download-config-page', - 'csv-process' => 'csv.process', - 'currency-index' => 'currency.index', - 'currency-create' => 'currency.create', - 'currency-edit' => 'currency.edit', - 'currency-delete' => 'currency.delete', - 'new-user-index' => 'new-user.index', - 'piggy-banks-index' => 'piggy-banks.index', - 'piggy-banks-create' => 'piggy-banks.create', - 'piggy-banks-edit' => 'piggy-banks.edit', - 'piggy-banks-delete' => 'piggy-banks.delete', - 'piggy-banks-show' => 'piggy-banks.show', - 'preferences' => 'preferences', + 'home' => 'accueil', + 'accounts-index' => 'comptes', + 'accounts-create' => 'créer un compte', + 'accounts-edit' => 'éditer un compte', + 'accounts-delete' => 'supprimer un compte', + 'accounts-show' => 'visualiser un compte', + 'attachments-edit' => 'modifier la pièce jointe', + 'attachments-delete' => 'supprimer la pièce jointe', + 'attachments-show' => 'visualiser la pièce jointe', + 'attachments-preview' => 'prévisualiser les pièces jointes', + 'bills-index' => 'factures', + 'bills-create' => 'créer une facture', + 'bills-edit' => 'éditer une facture', + 'bills-delete' => 'supprimer une facture', + 'bills-show' => 'visualiser une facture', + 'budgets-index' => 'budgets', + 'budgets-create' => 'créer un budget', + 'budgets-edit' => 'éditer un budget', + 'budgets-delete' => 'supprimer un budget', + 'budgets-show' => 'visualiser un budget', + 'budgets-noBudget' => 'opérations sans budgets', + 'categories-index' => 'catégories', + 'categories-create' => 'créer des catégories', + 'categories-edit' => 'éditer les catégories', + 'categories-delete' => 'supprimer des catégories', + 'categories-show' => 'visualiser une catégorie', + 'categories-show-date' => 'voir la catégorie', + 'categories-noCategory' => 'opérations sans catégories', + 'currency-index' => 'devises', + 'currency-create' => 'créer une devise', + 'currency-edit' => 'éditer une devise', + 'currency-delete' => 'supprimer une devise', + 'new-user-index' => 'nouvel utilisateur', + 'piggy-banks-index' => 'tirelires', + 'piggy-banks-create' => 'créer une tirelire', + 'piggy-banks-edit' => 'éditer une tirelire', + 'piggy-banks-delete' => 'supprimer une tirelire', + 'piggy-banks-show' => 'visualiser une tire-lire', + 'preferences' => 'préférences', 'profile' => 'profile', - 'profile-change-password' => 'profile.change-password', - 'profile-delete-account' => 'profile.delete-account', - 'reports-index' => 'reports.index', - 'reports-report' => 'reports.report', - 'search' => 'search', - 'tags-index' => 'tags.index', - 'tags-create' => 'tags.create', - 'tags-show' => 'tags.show', - 'tags-edit' => 'tags.edit', - 'tags-delete' => 'tags.delete', - 'transactions-index' => 'transactions.index', - 'transactions-create' => 'transactions.create', - 'transactions-edit' => 'transactions.edit', - 'transactions-delete' => 'transactions.delete', - 'transactions-show' => 'transactions.show', + 'profile-change-password' => 'changez votre mot de passe', + 'profile-delete-account' => 'supprimer votre compte', + 'reports-index' => 'rapports', + 'reports-report' => 'rapports', + 'search' => 'recherche', + 'tags-index' => 'tags', + 'tags-create' => 'créer un tag', + 'tags-show' => 'visualiser un tag', + 'tags-edit' => 'éditer un tag', + 'tags-delete' => 'supprimer un tag', + 'transactions-index' => 'opérations', + 'transactions-create' => 'créer une opération', + 'transactions-edit' => 'éditer une opération', + 'transactions-delete' => 'supprimer une opération', + 'transactions-show' => 'visualiser une opération', ]; diff --git a/resources/lang/fr_FR/list.php b/resources/lang/fr_FR/list.php index 97815e936d..f68f3a1291 100644 --- a/resources/lang/fr_FR/list.php +++ b/resources/lang/fr_FR/list.php @@ -8,54 +8,59 @@ */ return [ - 'buttons' => 'Buttons', - 'icon' => 'Icon', - 'create_date' => 'Created at', - 'update_date' => 'Updated at', - 'balance_before' => 'Balance before', - 'balance_after' => 'Balance after', - 'name' => 'Nom', - 'role' => 'Rôle', - 'currentBalance' => 'Solde courant', - 'active' => 'Actif ?', - 'lastActivity' => 'Activité récente', - 'balanceDiff' => 'Difference solde entre :start et :end', - 'matchedOn' => 'Matched on', - 'matchesOn' => 'Matched on', - 'account_type' => 'Account type', - 'new_balance' => 'New balance', - 'account' => 'Account', - 'matchingAmount' => 'Montant', - 'lastMatch' => 'Last match', - 'split_number' => 'Split #', - 'destination' => 'Destination', - 'expectedMatch' => 'Expected match', - 'automatch' => 'Auto match?', - 'repeat_freq' => 'Repeats', - 'description' => 'Description', - 'amount' => 'Amount', - 'date' => 'Date', - 'interest_date' => 'Interest date', - 'book_date' => 'Book date', - 'process_date' => 'Processing date', - 'from' => 'From', - 'piggy_bank' => 'Piggy bank', - 'to' => 'To', - 'budget' => 'Budget', - 'category' => 'Category', - 'bill' => 'Bill', - 'withdrawal' => 'Withdrawal', - 'deposit' => 'Deposit', - 'transfer' => 'Transfer', - 'type' => 'Type', - 'completed' => 'Completed', - 'iban' => 'IBAN', - 'paid_current_period' => 'Paid this period', - 'email' => 'Email', - 'registered_at' => 'Registered at', - 'is_activated' => 'Is activated', - 'is_blocked' => 'Is blocked', - 'is_admin' => 'Is admin', - 'has_two_factor' => 'Has 2FA', - 'blocked_code' => 'Block code', + 'buttons' => 'Boutons', + 'icon' => 'Icône', + 'create_date' => 'Créé le', + 'update_date' => 'Mis à jour le', + 'balance_before' => 'Solde avant', + 'balance_after' => 'Solde après', + 'name' => 'Nom', + 'role' => 'Rôle', + 'currentBalance' => 'Solde courant', + 'active' => 'Actif ?', + 'lastActivity' => 'Activité récente', + 'balanceDiff' => 'Difference solde entre :start et :end', + 'matchedOn' => 'Correspond à', + 'matchesOn' => 'Correspond à', + 'account_type' => 'Type de compte', + 'new_balance' => 'Nouveau solde', + 'account' => 'Compte', + 'matchingAmount' => 'Montant', + 'lastMatch' => 'Dernière correspondance', + 'split_number' => 'Segmenter en', + 'destination' => 'Destination', + 'source' => 'Source', + 'expectedMatch' => 'Correspondance attendue', + 'automatch' => 'Correspondance automatique ?', + 'repeat_freq' => 'Répétitions', + 'description' => 'Description', + 'amount' => 'Montant', + 'date' => 'Date', + 'interest_date' => 'Date des intérêts', + 'book_date' => 'Book date', + 'process_date' => 'Date de traitement', + 'from' => 'Depuis', + 'piggy_bank' => 'Tirelire', + 'to' => 'À', + 'budget' => 'Budget', + 'category' => 'Catégorie', + 'bill' => 'Facture', + 'withdrawal' => 'Retrait', + 'deposit' => 'Dépôt', + 'transfer' => 'Transfert', + 'type' => 'Type', + 'completed' => 'Terminé', + 'iban' => 'IBAN', + 'paid_current_period' => 'Payé cette période', + 'email' => 'E-mail', + 'registered_at' => 'Enregistré le', + 'is_activated' => 'Est activé', + 'is_blocked' => 'Est bloqué', + 'is_admin' => 'Est admin', + 'has_two_factor' => 'A 2FA', + 'confirmed_from' => 'Confirmed from', + 'registered_from' => 'Registered from', + 'blocked_code' => 'Code de blocage', + 'domain' => 'Domain', + 'registration_attempts' => 'Registration attempts', ]; diff --git a/resources/lang/fr_FR/passwords.php b/resources/lang/fr_FR/passwords.php index 6c382f830f..6d64931122 100644 --- a/resources/lang/fr_FR/passwords.php +++ b/resources/lang/fr_FR/passwords.php @@ -8,10 +8,10 @@ */ return [ - 'password' => 'Passwords must be at least six characters and match the confirmation.', - 'user' => 'We can\'t find a user with that e-mail address.', - 'token' => 'This password reset token is invalid.', - 'sent' => 'We have e-mailed your password reset link!', - 'reset' => 'Your password has been reset!', - 'blocked' => 'Nice try though.', + 'password' => 'Les mots de passe doivent contenir au moins six caractères et correspondre à la confirmation.', + 'user' => 'Nous ne pouvons pas trouver un utilisateur avec cette adresse e-mail.', + 'token' => 'Le jeton de réinitialisation de mot de passe est invalide.', + 'sent' => 'Nous vous avons envoyé par e-mail un lien de réinitialisation de votre mot de passe !', + 'reset' => 'Votre mot de passe a été réinitialisé !', + 'blocked' => 'Bien essayé cependant.', ]; diff --git a/resources/lang/fr_FR/validation.php b/resources/lang/fr_FR/validation.php index 49fc9c9e96..4d34c2bdba 100644 --- a/resources/lang/fr_FR/validation.php +++ b/resources/lang/fr_FR/validation.php @@ -8,16 +8,16 @@ */ return [ - 'iban' => 'This is not a valid IBAN.', - 'unique_account_number_for_user' => 'It looks like this account number is already in use.', - 'rule_trigger_value' => 'This value is invalid for the selected trigger.', - 'rule_action_value' => 'This value is invalid for the selected action.', - 'invalid_domain' => 'Due to security constraints, you cannot register from this domain.', - 'file_already_attached' => 'Uploaded file ":name" is already attached to this object.', - 'file_attached' => 'Succesfully uploaded file ":name".', - 'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.', - 'file_too_large' => 'File ":name" is too large.', - 'belongs_to_user' => 'The value of :attribute is unknown', + 'iban' => 'Il ne s\'agit pas d\'un IBAN valide.', + 'unique_account_number_for_user' => 'Il semble que ce numéro de compte est déjà utilisé.', + 'rule_trigger_value' => 'Cette valeur n’est pas valide pour le déclencheur sélectionné.', + 'rule_action_value' => 'Cette valeur n’est pas valide pour l’action sélectionnée.', + 'invalid_domain' => 'Compte tenu des contraintes de sécurité, vous ne peut pas vous enregistrer depuis ce domaine.', + 'file_already_attached' => 'Le fichier téléchargé ":name" est déjà attaché à cet objet.', + 'file_attached' => 'Envoi du fichier ":name" avec succès.', + 'file_invalid_mime' => 'Le fichier ":name" est du type ":mime" ce qui n\'est pas accepté pour un nouvel envoi.', + 'file_too_large' => 'Le fichier ":name" est trop grand.', + 'belongs_to_user' => 'La valeur de :attribute est inconnue', 'accepted' => 'Le champ :attribute doit être accepté.', 'active_url' => 'Le champ :attribute n\'est pas une URL valide.', 'after' => 'Le champ :attribute doit être une date postérieure au :date.', @@ -25,10 +25,10 @@ return [ 'alpha_dash' => 'Le champ :attribute doit seulement contenir des lettres, des chiffres et des tirets.', 'alpha_num' => 'Le champ :attribute doit seulement contenir des chiffres et des lettres.', 'array' => 'Le champ :attribute doit être un tableau.', - 'unique_for_user' => 'There already is an entry with this :attribute.', + 'unique_for_user' => 'Il existe déjà une entrée avec ceci :attribute.', 'before' => 'Le champ :attribute doit être une date antérieure au :date.', - 'unique_object_for_user' => 'This name is already in use', - 'unique_account_for_user' => 'This account name is already in use', + 'unique_object_for_user' => 'Ce nom est déjà utilisé', + 'unique_account_for_user' => 'Ce nom de compte est déjà utilisé', 'between.numeric' => 'La valeur de :attribute doit être comprise entre :min et :max.', 'between.file' => 'Le fichier :attribute doit avoir une taille entre :min et :max kilo-octets.', 'between.string' => 'Le texte :attribute doit avoir entre :min et :max caractères.', @@ -76,5 +76,5 @@ return [ 'string' => 'Le champ :attribute doit être une chaîne de caractères.', 'url' => 'Le format de l\'URL de :attribute n\'est pas valide.', 'timezone' => 'Le champ :attribute doit être un fuseau horaire valide.', - '2fa_code' => 'The :attribute field is invalid.', + '2fa_code' => 'Le champ :attribute est invalide.', ]; diff --git a/resources/lang/nl_NL/csv.php b/resources/lang/nl_NL/csv.php new file mode 100644 index 0000000000..991dc2fa08 --- /dev/null +++ b/resources/lang/nl_NL/csv.php @@ -0,0 +1,80 @@ + 'Import configureren', + 'import_configure_intro' => 'Hier zie je enkele opties voor jouw CSV bestand. Geef aan of je CSV bestand kolomtitels bevat, en hoe het datumveld is opgebouwd. Hier moet je wellicht wat experimenteren. Het scheidingsteken is meestal een ",", maar dat kan ook een ";" zijn. Controleer dit zorgvuldig.', + 'import_configure_form' => 'Formulier', + 'header_help' => 'Vink hier als de eerste rij kolomtitels bevat', + 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', + 'import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', + + // roles + 'column_roles_title' => 'Bepaal de inhoud van elke kolom', + 'column_roles_text' => '

    Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

    Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

    ', + 'column_roles_table' => 'Tabel', + 'column_name' => 'Kolomnaam', + 'column_example' => 'Voorbeeldgegevens', + 'column_role' => 'Column data meaning', + 'do_map_value' => 'Map these values', + 'column' => 'Column', + 'no_example_data' => 'No example data available', + 'store_column_roles' => 'Continue import', + 'do_not_map' => '(do not map)', + 'map_title' => 'Connect import data to Firefly III data', + 'map_text' => 'In the following tables, the left value shows you information found in your uploaded CSV file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', + + 'field_value' => 'Field value', + 'field_mapped_to' => 'Mapped to', + 'store_column_mapping' => 'Store mapping', + + // map things. + + + 'column__ignore' => '(negeer deze kolom)', + 'column_account-iban' => 'Betaalrekening (IBAN)', + 'column_account-id' => 'Betaalrekening (ID gelijk aan Firefly)', + 'column_account-name' => 'Betaalrekeningnaam', + 'column_amount' => 'Bedrag', + 'column_amount-comma-separated' => 'Bedrag (komma as decimaalscheidingsteken)', + 'column_bill-id' => 'Contract (ID gelijk aan Firefly)', + 'column_bill-name' => 'Contractnaam', + 'column_budget-id' => 'Budget (ID gelijk aan Firefly)', + 'column_budget-name' => 'Budgetnaam', + 'column_category-id' => 'Categorie (ID gelijk aan Firefly)', + 'column_category-name' => 'Categorienaam', + 'column_currency-code' => 'Valutacode (ISO 4217)', + 'column_currency-id' => 'Valuta (ID gelijk aan Firefly)', + 'column_currency-name' => 'Valutanaam', + 'column_currency-symbol' => 'Valutasymbool', + 'column_date-interest' => 'Datum (renteberekening)', + 'column_date-book' => 'Datum (boeking)', + 'column_date-process' => 'Datum (verwerking)', + 'column_date-transaction' => 'Datum', + 'column_description' => 'Omschrijving', + 'column_opposing-iban' => 'Tegenrekening (IBAN)', + 'column_opposing-id' => 'Tegenrekening (ID gelijk aan Firefly)', + 'column_external-id' => 'Externe ID', + 'column_opposing-name' => 'Tegenrekeningnaam', + 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'column_ing-debet-credit' => 'ING specific debet/credit indicator', + 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'column_sepa-db' => 'SEPA Direct Debet', + 'column_tags-comma' => 'Tags (comma separated)', + 'column_tags-space' => 'Tags (space separated)', + 'column_account-number' => 'Asset account (account number)', + 'column_opposing-number' => 'Opposing account (account number)', +]; \ No newline at end of file diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 159eedee49..8926b5f0f1 100644 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -75,10 +75,10 @@ return [ // repeat frequencies: 'repeat_freq_monthly' => 'maandelijks', - 'weekly' => 'Wekelijks', - 'quarterly' => 'Elk kwartaal', - 'half-year' => 'Elk half jaar', - 'yearly' => 'Jaarlijks', + 'weekly' => 'wekelijks', + 'quarterly' => 'elk kwartaal', + 'half-year' => 'elk half jaar', + 'yearly' => 'elk jaar', // account confirmation: 'confirm_account_header' => 'Bevestig je account', 'confirm_account_intro' => 'TIjdens het registreren heb je een mailtje gehad. Kijk daar in voor instructies. Als je het mailtje niet hebt gehad, kan Firefly je een nieuwe sturen.', @@ -326,98 +326,6 @@ return [ 'title_transfer' => 'Overboekingen', 'title_transfers' => 'Overboekingen', - - // csv import: - 'csv_import' => 'Importeer CSV-bestand', - 'csv' => 'CSV', - 'csv_index_title' => 'Upload en importeer een kommagescheiden tekstbestand', - 'csv_define_column_roles' => 'Bepaal kolominhoud', - 'csv_map_values' => 'Leg relaties met kolomwaardes', - 'csv_download_config' => 'Download CSV configuratiebestand.', - 'csv_index_text' => 'Met deze (en de komende) pagina\'s kan je kommagescheiden tekstbestanden importeren. Deze tool is gebaseerd op de prachtige tool van Atlassian. Om te beginnen selecteer je jouw tekstbestand bij "CSV-bestand". Als je hulp nodig hebt, klik dan op het -icoontje rechtsboven.', - 'csv_index_beta_warning' => 'Deze tool is nog erg experimenteel. Wees dus voorzichtig.', - 'csv_header_help' => 'Zet hier een vinkje als de eerste rij van het CSV-bestand de namen van de kolommen bevat', - 'csv_date_help' => 'Het gebruikte datumformaat in jouw bestand. Gebruik het formaat zoals deze pagina het uitlegt (Engels). Het standaardformaat kan omgaan met data zoals deze: :dateExample.', - 'csv_csv_file_help' => 'Voer hier je kommagescheiden tekstbestand in. Je kan er maar één tegelijkertijd invoeren.', - 'csv_csv_config_file_help' => 'Voer hier je configuratiebestand in. Als je deze niet hebt, geen zorgen. Latere stappen leggen dit uit.', - 'csv_upload_button' => 'Begin de import', - 'csv_column_roles_title' => 'Bepaal de inhoud van elke kolom', - 'csv_column_roles_text' => 'Firefly kan niet automatisch ontdekken wat elke kolom betekent. Je moet het zelf aangeven. Gebruik de voorbeeldgegevens als je het ook niet zeker weet. Klik op het vraagteken-icoontje (rechtsboven) om te ontdekken wat elke kolomsoort precies is. Als de kolominhoud een directe relatie heeft met gegevens die al in Firefly staan, gebruik dan het vinkje. Tijdens de volgende stap komt Firefly hier dan op terug.', - 'csv_column_roles_table' => 'Kolominhoud', - 'csv_column' => 'CSV-kolom', - 'csv_column_name' => 'CSV-kolomnaam', - 'csv_column_example' => 'Voorbeeldgegevens', - 'csv_column_role' => 'Kolom bevat?', - 'csv_do_map_value' => 'Directe relatie?', - 'csv_continue' => 'Naar de volgende stap', - 'csv_go_back' => 'Terug naar de vorige stap', - 'csv_map_title' => 'Leg relaties met kolomwaardes', - 'csv_map_text' => 'Sommige kolommen bevatten waardes die misschien al in Firefly bestaan. Selecteer hier de juiste combinaties zodat het importeren netjes aansluit bij je huidige gegevens.', - 'csv_field_value' => 'Veldwaarde', - 'csv_field_mapped_to' => 'Is gelijk aan', - 'csv_do_not_map' => 'Geen relatie', - 'csv_download_config_title' => 'Download importconfiguratie', - 'csv_download_config_text' => 'Alles wat je nu hebt zitten instellen kan je downloaden als configuratiebestand voor de volgende keer. Klik op de knop om dit te doen.', - 'csv_more_information_text' => 'Ook als het importeren fout gaat is dit bestand handig. Na het importeren krijg je nogmaals de gelegenheid dit bestand te downloaden.', - 'csv_do_download_config' => 'Download het configuratiebestand', - 'csv_empty_description' => '(geen omschrijving)', - 'csv_upload_form' => 'CSV upload formulier', - 'csv_index_unsupported_warning' => 'Het volgende wordt nog niet ondersteund:', - 'csv_unsupported_map' => 'De tool kan de kolom ":columnRole" niet koppelen aan bestaande gegevens in de database.', - 'csv_unsupported_value' => 'Firefly kan niet omgaan met kolommen gemarkeerd als ":columnRole".', - 'csv_cannot_store_value' => 'Firefly heeft geen ruimte gereserveerd voor kolommen gemarkeert als ":columnRole" en kan ze helaas niet verwerken.', - 'csv_process_title' => 'Het importeren is klaar', - 'csv_process_text' => ':rows rijen zijn verwerkt.', - 'csv_row' => 'Rij', - 'csv_import_with_errors' => 'Er ging één ding fout.|Er gingen :errors dingen fout.', - 'csv_error_see_logs' => 'De logboeken bevatten mogelijk meer details.', - 'csv_process_new_entries' => 'Firefly heeft :imported nieuwe transactie(s) gemaakt.', - 'csv_start_over' => 'Begin opnieuw', - 'csv_to_index' => 'Naar de index', - 'csv_upload_not_writeable' => 'Kan niet naar onderstaand pad schrijven. Kan dus niet uploaden.', - 'csv_column__ignore' => '(negeer deze kolom)', - 'csv_column_account-iban' => 'Betaalrekening (IBAN)', - 'csv_column_account-id' => 'Betaalrekening (ID gelijk aan Firefly)', - 'csv_column_account-name' => 'Betaalrekeningnaam', - 'csv_column_amount' => 'Bedrag', - 'csv_column_amount-comma-separated' => 'Bedrag (komma as decimaalscheidingsteken)', - 'csv_column_bill-id' => 'Contract (ID gelijk aan Firefly)', - 'csv_column_bill-name' => 'Contractnaam', - 'csv_column_budget-id' => 'Budget (ID gelijk aan Firefly)', - 'csv_column_budget-name' => 'Budgetnaam', - 'csv_column_category-id' => 'Categorie (ID gelijk aan Firefly)', - 'csv_column_category-name' => 'Categorienaam', - 'csv_column_currency-code' => 'Valutacode (ISO 4217)', - 'csv_column_currency-id' => 'Valuta (ID gelijk aan Firefly)', - 'csv_column_currency-name' => 'Valutanaam', - 'csv_column_currency-symbol' => 'Valuta', - 'csv_column_date-rent' => 'Datum (renteberekening)', - 'csv_column_date-transaction' => 'Datum (transactie)', - 'csv_column_description' => 'Omschrijving', - 'csv_column_opposing-iban' => 'Tegenrekening (IBAN)', - 'csv_column_opposing-id' => 'Tegenrekening (ID gelijk aan Firefly)', - 'csv_column_opposing-name' => 'Tegenrekeningnaam', - 'csv_column_rabo-debet-credit' => 'Rabobankspecifiek bij/af indicator', - 'csv_column_ing-debet-credit' => 'ING-specifieke bij/af indicator', - 'csv_column_sepa-ct-id' => 'SEPA transactienummer', - 'csv_column_sepa-ct-op' => 'SEPA tegenrekeningnummer', - 'csv_column_sepa-db' => 'SEPA "direct debet"-nummer', - 'csv_column_tags-comma' => 'Tags (kommagescheiden)', - 'csv_column_tags-space' => 'Tags (spatiegescheiden)', - 'csv_column_account-number' => 'Betaalrekening (rekeningnummer)', - 'csv_column_opposing-number' => 'Tegenrekening (rekeningnummer)', - 'csv_specifix_RabobankDescription' => 'Vink dit aan als je Rabobank CSV-bestanden importeert.', - 'csv_specifix_AbnAmroDescription' => 'Vink dit aan als je ABN AMRO CSV-bestanden importeert.', - 'csv_specifix_Dummy' => 'Dit vinkje doet niks (dummy).', - 'csv_import_account_help' => 'Als jouw CSV bestand geen referenties bevat naar jouw rekening(en), geef dan hier aan om welke rekening het gaat.', - 'csv_delimiter_help' => 'Kies het veldscheidingsteken dat in het invoerbestand is gebruikt. Bij twijfel is de komma de veiligste optie.', - 'csv_date_parse_error' => 'Firefly kan van ":value" geen datum maken, gegeven het formaat ":format". Weet je zeker dat je CSV goed is?', - 'could_not_recover' => 'Helaas, kan niet doorgaan vanaf de vorige stap. Je voortgang is verloren gegaan :(. De logbestanden bevatten meer informatie.', - 'must_select_roles' => 'Je moet enkele rollen selecteren voor de kolommen in je bestand, anders kan je niet verder.', - 'invalid_mapping' => 'Je hebt verkeerde gegevens ingevoerd. Je kan helaas niet verder.', - 'no_file_uploaded' => 'Het lijkt er op dat je niets hebt geüpload.', - - // create new stuff: 'create_new_withdrawal' => 'Nieuwe uitgave', 'create_new_deposit' => 'Nieuwe inkomsten', @@ -500,6 +408,8 @@ return [ 'cannot_scan_inactive_bill' => 'Inactieve contracten kunnen niet worden gescand.', 'rescanned_bill' => 'Alles is opnieuw gescand.', 'bill_date_little_relevance' => 'Firefly gebruikt alleen de dag van dit datumveld. Dit veld heeft alleen zin als je rekening ook echt op die dag van de maand komt. Zo niet, vul dan gewoon de 1e van de maand in.', + 'average_bill_amount_year' => 'Gemiddeld contractbedrag (:year)', + 'average_bill_amount_overall' => 'Gemiddeld contractbedrag (gehele periode)', // accounts: 'details_for_asset' => 'Overzicht voor betaalrekening ":name"', @@ -644,12 +554,9 @@ return [ 'saveOnAccount' => 'Sparen op rekening', 'unknown' => 'Onbekend', 'daily' => 'Dagelijks', - 'weekly' => 'Wekelijks', 'monthly' => 'Maandelijks', - 'quarterly' => 'Elk kwartaal', - 'half-year' => 'Elk half jaar', - 'yearly' => 'Jaarlijks', 'profile' => 'Profiel', + 'errors' => 'Fouten', // reports: 'report_default' => 'Standaard financieel rapport (:start tot :end)', @@ -742,6 +649,7 @@ return [ 'balanceFor' => 'Saldo op :name', // piggy banks: + 'add_money_to_piggy' => 'Stop geld in spaarpotje ":name"', 'piggy_bank' => 'Spaarpotje', 'new_piggy_bank' => 'Nieuw spaarpotje', 'store_piggy_bank' => 'Sla spaarpotje op', @@ -803,6 +711,18 @@ return [ 'user_administration' => 'Gebruikersadministratie', 'list_all_users' => 'Alle gebruikers', 'all_users' => 'Alle gebruikers', + 'all_blocked_domains' => 'Alle geblokkeerde domeinen', + 'blocked_domains' => 'Geblokkeerde domeinen', + 'no_domains_banned' => 'Geen domeinen geblokkeerd', + 'all_user_domains' => 'Alle domeinen van gebruikers', + 'all_domains_is_filtered' => 'Deze lijst bevat geen domeinen die al geblokkeerd zijn.', + 'domain_now_blocked' => 'Domein :domein is geblokkeerd', + 'domain_now_unblocked' => 'Domein :domain is niet meer geblokkeerd', + 'manual_block_domain' => 'Blokkeer een domein handmatig', + 'block_domain' => 'Blokkeer domein', + 'no_domain_filled_in' => 'Geen domein opgegeven', + 'domain_already_blocked' => 'Domein :domain is al geblokkeerd', + 'domain_is_now_blocked' => 'Domein :domain is nu geblokkeerd', // split a transaction: 'transaction_meta_data' => 'Transactie meta-data', @@ -815,24 +735,46 @@ return [ 'split_table_intro_withdrawal' => 'Split je uitgave in zoveel stukken als je maar wilt. Standaard is je uitgave niet gesplitst; er is maar één "split". Voeg hieronder zoveel splits toe als je wilt. Denk er aan dat je niet afwijkt van het totaalbedrag. Als je dat wel doet zal Firefly je waarschuwen maar niet corrigeren.', 'store_splitted_withdrawal' => 'Sla gesplitste uitgave op', 'update_splitted_withdrawal' => 'Gesplitste uitgave updaten', + 'split_title_deposit' => 'Splits je nieuwe inkomsten', + 'split_intro_one_deposit' => 'Firefly kan inkomsten "splitsen".', + 'split_intro_two_deposit' => 'Dat betekent dat de inkomsten die je krijgt wordt verdeeld over verschillende doelrekeningen of categorieën.', + 'split_intro_three_deposit' => 'Je kan bijvoorbeeld je salaris van :total verdelen zodat :split_one wordt opgeslagen als je basissalaris, en :split_two als declaratieteruggave.', + 'split_table_intro_deposit' => 'Split je inkomsten in zoveel stukken als je maar wilt. Standaard zijn je inkomsten niet gesplitst; er is maar één "split". Voeg hieronder zoveel splits toe als je wilt. Denk er aan dat je niet afwijkt van het totaalbedrag. Als je dat wel doet zal Firefly je waarschuwen maar niet corrigeren.', + 'store_splitted_deposit' => 'Sla gesplitse inkomsten op', + 'split_title_transfer' => 'Splits je nieuwe overschrijving', + 'split_intro_one_transfer' => 'Firefly kan overschrijvingen "splitsen".', + 'split_intro_two_transfer' => 'Dat betekent dat de uitgave die je maakt wordt verdeeld over verschillende categorieën of spaarpotjes.', + 'split_intro_three_transfer' => 'Je kan bijvoorbeeld je overschrijving van :total verdelen zodat :split_one in het ene spaarpotje terecht komt, en :split_two in het andere spaarpotje.', + 'split_table_intro_transfer' => 'Split je overschrijving in zoveel stukken als je maar wilt. Standaard is je overschrijving niet gesplitst; er is maar één "split". Voeg hieronder zoveel splits toe als je wilt. Denk er aan dat je niet afwijkt van het totaalbedrag. Als je dat wel doet zal Firefly je waarschuwen maar niet corrigeren.', + 'store_splitted_transfer' => 'Sla gesplitste overschrijving op', + 'add_another_split' => 'Voeg een split toe', + 'split-transactions' => 'Split transacties', + 'split-new-transaction' => 'Split een nieuwe transactie', + 'do_split' => 'Splits', + 'split_this_withdrawal' => 'Splits deze uitgave', + 'split_this_deposit' => 'Splits deze inkomsten', + 'split_this_transfer' => 'Splits deze overboeking', + 'cannot_edit_multiple_source' => 'Je kan transactie #:id met omschrijving ":description" niet splitsen, want deze bevat meerdere bronrekeningen.', + 'cannot_edit_multiple_dest' => 'Je kan transactie #:id met omschrijving ":description" niet splitsen, want deze bevat meerdere doelrekeningen.', + 'no_edit_multiple_left' => 'Je hebt geen geldige transacties geselecteerd.', - 'split_title_deposit' => 'Splits je nieuwe inkomsten', - 'split_intro_one_deposit' => 'Firefly kan inkomsten "splitsen".', - 'split_intro_two_deposit' => 'Dat betekent dat de inkomsten die je krijgt wordt verdeeld over verschillende doelrekeningen of categorieën.', - 'split_intro_three_deposit' => 'Je kan bijvoorbeeld je salaris van :total verdelen zodat :split_one wordt opgeslagen als je basissalaris, en :split_two als declaratieteruggave.', - 'split_table_intro_deposit' => 'Split je inkomsten in zoveel stukken als je maar wilt. Standaard zijn je inkomsten niet gesplitst; er is maar één "split". Voeg hieronder zoveel splits toe als je wilt. Denk er aan dat je niet afwijkt van het totaalbedrag. Als je dat wel doet zal Firefly je waarschuwen maar niet corrigeren.', - 'store_splitted_deposit' => 'Sla gesplitse inkomsten op', - - 'split_title_transfer' => 'Splits je nieuwe overschrijving', - 'split_intro_one_transfer' => 'Firefly kan overschrijvingen "splitsen".', - 'split_intro_two_transfer' => 'Dat betekent dat de uitgave die je maakt wordt verdeeld over verschillende categorieën of spaarpotjes.', - 'split_intro_three_transfer' => 'Je kan bijvoorbeeld je overschrijving van :total verdelen zodat :split_one in het ene spaarpotje terecht komt, en :split_two in het andere spaarpotje.', - 'split_table_intro_transfer' => 'Split je overschrijving in zoveel stukken als je maar wilt. Standaard is je overschrijving niet gesplitst; er is maar één "split". Voeg hieronder zoveel splits toe als je wilt. Denk er aan dat je niet afwijkt van het totaalbedrag. Als je dat wel doet zal Firefly je waarschuwen maar niet corrigeren.', - 'store_splitted_transfer' => 'Sla gesplitste overschrijving op', - - 'add_another_split' => 'Voeg een split toe', - 'split-transactions' => 'Split transacties', - 'split-new-transaction' => 'Split een nieuwe transactie', - - + // import + 'configuration_file_help' => 'Als je eerder gegevens in Firefly III hebt geïmporteerd, heb je een configuratiebestand, dat voor jou de instellingen zal doen.', + 'import_data_index' => 'Index', + 'import_file_type_csv' => 'CSV (kommagescheiden waardes)', + 'import_file_type_help' => 'Selecteer het type bestand dat je zal uploaden', + 'import_start' => 'Start importeren', + 'configure_import' => 'Verder configureren van je import', + 'import_finish_configuration' => 'De configuratie voltooien', + 'settings_for_import' => 'Instellingen', + 'import_complete' => 'Configureren van import is klaar!', + 'import_complete_text' => 'De import is klaar om te beginnen. Alle de configuratie die je moest doen is gedaan. Download alsjeblieft het configuratiebestand. Als de import mislukt hoef je straks niet overnieuw te beginnen. Voor het daadwerkelijk draaien van de import-routine, voer je het onderstaande commando uit in je browser. Via de site zelf is dit helaas nog niet mogelijk.', + 'import_download_config' => 'Download importconfiguratie', + 'import_start_import' => 'Import starten', + 'import_intro_beta' => 'De importfunctie van Firefly III is in bèta. Er zijn al veel bestanden geprobeerd, en elk individueel component zou moeten werken. Gecombineerd echter, kan er wat stuk gaan. Als jouw bestand problemen geeft, lees dan deze wikipagina zodat ik eventuele bugs kan fixen.', + 'import_data' => 'Importeer data', + 'import_data_full' => 'Gegevens importeren in Firefly III', + 'import' => 'Import', + 'import_intro_text' => 'Welkom bij de import-routine van Firefly III. Deze pagina\'s helpen je data in Firefly III te importeren. Om dat te doen, download of exporteer je transacties uit andere systemen, en upload ze hier. In de volgende stappen help je Firefly met het bepalen wat de inhoud is van de bestanden die je upload, en hoe er mee om te gaan. Selecteer een bestand, en lees alle instructies zorgvuldig.', + 'import_file_help' => 'Selecteer je bestand', ]; diff --git a/resources/lang/nl_NL/form.php b/resources/lang/nl_NL/form.php index be8e10d516..b07a736e57 100644 --- a/resources/lang/nl_NL/form.php +++ b/resources/lang/nl_NL/form.php @@ -71,13 +71,9 @@ return [ 'code' => 'Code', 'iban' => 'IBAN', 'accountNumber' => 'Rekeningnummer', - 'csv' => 'CSV-bestand', 'has_headers' => 'Kolomnamen op de eerste rij?', 'date_format' => 'Datumformaat', - 'csv_config' => 'Configuratiebestand', 'specifix' => 'Bank- or of bestandsspecifieke opties', - 'csv_import_account' => 'Standaard rekening voor importeren', - 'csv_delimiter' => 'CSV scheidingsteken', 'attachments[]' => 'Bijlagen', 'store_new_withdrawal' => 'Nieuwe uitgave opslaan', 'store_new_deposit' => 'Nieuwe inkomsten opslaan', @@ -102,9 +98,6 @@ return [ 'include_config' => 'Sla ook een configuratiebestand ook', 'include_old_uploads' => 'Sla ook geïmporteerde bestanden op', 'accounts' => 'Exporteer boekingen van deze rekeningen', - 'csv_comma' => 'Een komma (,)', - 'csv_semicolon' => 'Een puntkomma (;)', - 'csv_tab' => 'Een tab (onzichtbaar)', 'delete_account' => 'Verwijder rekening ":name"', 'delete_bill' => 'Verwijder contract ":name"', 'delete_budget' => 'Verwijder budget ":name"', @@ -137,4 +130,20 @@ return [ 'budget_keep_transactions' => 'De transactie verbonden aan dit budget blijft bewaard.|De :count transacties verbonden aan dit budget blijven bewaard.', 'category_keep_transactions' => 'De transactie verbonden aan deze categorie blijft bewaard.|De :count transacties verbonden aan deze categorie blijven bewaard.', 'tag_keep_transactions' => 'De transactie verbonden aan deze tag blijft bewaard.|De :count transacties verbonden aan deze tag blijven bewaard.', + + // admin + 'domain' => 'Domain', + + // import + 'import_file' => 'Import file', + 'configuration_file' => 'Configuration file', + 'import_file_type' => 'Import file type', + 'csv_comma' => 'Een komma (,)', + 'csv_semicolon' => 'Een puntkomma (;)', + 'csv_tab' => 'Een tab (onzichtbaar)', + 'csv_delimiter' => 'CSV scheidingsteken', + 'csv_import_account' => 'Standaard rekening voor importeren', + 'csv_config' => 'Configuratiebestand', + + ]; diff --git a/resources/lang/nl_NL/help.php b/resources/lang/nl_NL/help.php index 24ff6fbdac..76184e52fe 100644 --- a/resources/lang/nl_NL/help.php +++ b/resources/lang/nl_NL/help.php @@ -55,11 +55,6 @@ return [ 'categories-show' => 'bekijk een categorie', 'categories-show-date' => 'bekijk categorie', 'categories-noCategory' => 'transacties zonder categorie', - 'csv-index' => 'csv-index', - 'csv-column-roles' => 'kolommen en rollen', - 'csv-map' => 'kolommen en data', - 'csv-download-config-page' => 'download configuratie', - 'csv-process' => 'CSV verwerken', 'currency-index' => 'valuta\'s', 'currency-create' => 'maak een nieuwe munteenheid', 'currency-edit' => 'wijzig een munteenheid', diff --git a/resources/lang/nl_NL/list.php b/resources/lang/nl_NL/list.php index c96785ecbf..05e713409d 100644 --- a/resources/lang/nl_NL/list.php +++ b/resources/lang/nl_NL/list.php @@ -8,54 +8,59 @@ */ return [ - 'buttons' => 'Knoppen', - 'icon' => 'Icoon', - 'create_date' => 'Aangemaakt op', - 'update_date' => 'Bijgewerkt op', - 'balance_before' => 'Saldo voor', - 'balance_after' => 'Saldo na', - 'name' => 'Naam', - 'role' => 'Rol', - 'currentBalance' => 'Huidig saldo', - 'active' => 'Actief?', - 'lastActivity' => 'Laatste activiteit', - 'balanceDiff' => 'Saldoverschil tussen :start en :end', - 'matchedOn' => 'Wordt herkend', - 'matchesOn' => 'Wordt herkend', - 'account_type' => 'Accounttype', - 'new_balance' => 'Nieuw saldo', - 'account' => 'Rekening', - 'matchingAmount' => 'Bedrag', - 'lastMatch' => 'Laatste keer gezien', - 'split_number' => 'Split #', - 'destination' => 'Doel', - 'expectedMatch' => 'Wordt verwacht', - 'automatch' => 'Automatisch herkennen?', - 'repeat_freq' => 'Herhaling', - 'description' => 'Omschrijving', - 'amount' => 'Bedrag', - 'date' => 'Datum', - 'interest_date' => 'Rentedatum', - 'book_date' => 'Boekdatum', - 'process_date' => 'Verwerkingsdatum', - 'from' => 'Van', - 'piggy_bank' => 'Spaarpotje', - 'to' => 'Naar', - 'budget' => 'Budget', - 'category' => 'Categorie', - 'bill' => 'Contract', - 'withdrawal' => 'Uitgave', - 'deposit' => 'Inkomsten', - 'transfer' => 'Overschrijving', - 'type' => 'Type', - 'completed' => 'Opgeslagen', - 'iban' => 'IBAN', - 'paid_current_period' => 'Betaald deze periode', - 'email' => 'E-mail', - 'registered_at' => 'Geregistreerd op', - 'is_activated' => 'Is geactiveerd', - 'is_blocked' => 'Is geblokkeerd', - 'is_admin' => 'Is beheerder', - 'has_two_factor' => 'Heeft 2FA', - 'blocked_code' => 'Reden voor blokkade', + 'buttons' => 'Knoppen', + 'icon' => 'Icoon', + 'create_date' => 'Aangemaakt op', + 'update_date' => 'Bijgewerkt op', + 'balance_before' => 'Saldo voor', + 'balance_after' => 'Saldo na', + 'name' => 'Naam', + 'role' => 'Rol', + 'currentBalance' => 'Huidig saldo', + 'active' => 'Actief?', + 'lastActivity' => 'Laatste activiteit', + 'balanceDiff' => 'Saldoverschil tussen :start en :end', + 'matchedOn' => 'Wordt herkend', + 'matchesOn' => 'Wordt herkend', + 'account_type' => 'Accounttype', + 'new_balance' => 'Nieuw saldo', + 'account' => 'Rekening', + 'matchingAmount' => 'Bedrag', + 'lastMatch' => 'Laatste keer gezien', + 'split_number' => 'Split #', + 'destination' => 'Doel', + 'source' => 'Bron', + 'expectedMatch' => 'Wordt verwacht', + 'automatch' => 'Automatisch herkennen?', + 'repeat_freq' => 'Herhaling', + 'description' => 'Omschrijving', + 'amount' => 'Bedrag', + 'date' => 'Datum', + 'interest_date' => 'Rentedatum', + 'book_date' => 'Boekdatum', + 'process_date' => 'Verwerkingsdatum', + 'from' => 'Van', + 'piggy_bank' => 'Spaarpotje', + 'to' => 'Naar', + 'budget' => 'Budget', + 'category' => 'Categorie', + 'bill' => 'Contract', + 'withdrawal' => 'Uitgave', + 'deposit' => 'Inkomsten', + 'transfer' => 'Overschrijving', + 'type' => 'Type', + 'completed' => 'Opgeslagen', + 'iban' => 'IBAN', + 'paid_current_period' => 'Betaald deze periode', + 'email' => 'E-mail', + 'registered_at' => 'Geregistreerd op', + 'is_activated' => 'Is geactiveerd', + 'is_blocked' => 'Is geblokkeerd', + 'is_admin' => 'Is beheerder', + 'has_two_factor' => 'Heeft 2FA', + 'confirmed_from' => 'Bevestigd vanaf', + 'registered_from' => 'Geregistreerd vanaf', + 'blocked_code' => 'Reden voor blokkade', + 'domain' => 'Domein', + 'registration_attempts' => 'Registratiepogingen', ]; diff --git a/resources/lang/pt_BR/csv.php b/resources/lang/pt_BR/csv.php new file mode 100644 index 0000000000..858026dad6 --- /dev/null +++ b/resources/lang/pt_BR/csv.php @@ -0,0 +1,80 @@ + 'Configure your import', + 'import_configure_intro' => 'There are some options for your CSV import. Please indicate if your CSV file contains headers on the first column, and what the date format of your date-fields is. That might require some experimentation. The field delimiter is usually a ",", but could also be a ";". Check this carefully.', + 'import_configure_form' => 'Form', + 'header_help' => 'Check this if the first row of your CSV file are the column titles', + 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', + 'import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', + + // roles + 'column_roles_title' => 'Define column roles', + 'column_roles_text' => '

    Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

    Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

    ', + 'column_roles_table' => 'Table', + 'column_name' => 'Name of column', + 'column_example' => 'Column example data', + 'column_role' => 'Column data meaning', + 'do_map_value' => 'Map these values', + 'column' => 'Column', + 'no_example_data' => 'No example data available', + 'store_column_roles' => 'Continue import', + 'do_not_map' => '(do not map)', + 'map_title' => 'Connect import data to Firefly III data', + 'map_text' => 'In the following tables, the left value shows you information found in your uploaded CSV file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', + + 'field_value' => 'Field value', + 'field_mapped_to' => 'Mapped to', + 'store_column_mapping' => 'Store mapping', + + // map things. + + + 'column__ignore' => '(ignore this column)', + 'column_account-iban' => 'Asset account (IBAN)', + 'column_account-id' => 'Asset account ID (matching Firefly)', + 'column_account-name' => 'Asset account (name)', + 'column_amount' => 'Amount', + 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', + 'column_bill-id' => 'Bill ID (matching Firefly)', + 'column_bill-name' => 'Bill name', + 'column_budget-id' => 'Budget ID (matching Firefly)', + 'column_budget-name' => 'Budget name', + 'column_category-id' => 'Category ID (matching Firefly)', + 'column_category-name' => 'Category name', + 'column_currency-code' => 'Currency code (ISO 4217)', + 'column_currency-id' => 'Currency ID (matching Firefly)', + 'column_currency-name' => 'Currency name (matching Firefly)', + 'column_currency-symbol' => 'Currency symbol (matching Firefly)', + 'column_date-interest' => 'Interest calculation date', + 'column_date-book' => 'Transaction booking date', + 'column_date-process' => 'Transaction process date', + 'column_date-transaction' => 'Date', + 'column_description' => 'Description', + 'column_opposing-iban' => 'Opposing account (IBAN)', + 'column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'column_external-id' => 'External ID', + 'column_opposing-name' => 'Opposing account (name)', + 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'column_ing-debet-credit' => 'ING specific debet/credit indicator', + 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'column_sepa-db' => 'SEPA Direct Debet', + 'column_tags-comma' => 'Tags (comma separated)', + 'column_tags-space' => 'Tags (space separated)', + 'column_account-number' => 'Asset account (account number)', + 'column_opposing-number' => 'Opposing account (account number)', +]; \ No newline at end of file diff --git a/resources/lang/pt_BR/firefly.php b/resources/lang/pt_BR/firefly.php index 599afa7b20..bbab95ef31 100644 --- a/resources/lang/pt_BR/firefly.php +++ b/resources/lang/pt_BR/firefly.php @@ -70,15 +70,15 @@ return [ 'registered' => 'Você se registrou com sucesso!', 'search' => 'Pesquisa', 'no_budget_pointer' => 'Parece que não há orçamentos ainda. Você deve criar alguns na página orçamentos. Orçamentos podem ajudá-lo a manter o controle de despesas.', - 'source_accounts' => 'Source account(s)', - 'destination_accounts' => 'Destination account(s)', + 'source_accounts' => 'Conta(s) de origem', + 'destination_accounts' => 'Conta(s) de destino', // repeat frequencies: 'repeat_freq_monthly' => 'mensal', - 'weekly' => 'Semanal', - 'quarterly' => 'Trimestral', - 'half-year' => 'Semestral', - 'yearly' => 'Anual', + 'weekly' => 'semanal', + 'quarterly' => 'trimestral', + 'half-year' => 'metade de cada ano', + 'yearly' => 'anual', // account confirmation: 'confirm_account_header' => 'Por favor, confirme sua conta', 'confirm_account_intro' => 'Um e-mail foi enviado para o endereço que você usou durante o seu registro. Por favor confira-o para mais instruções. Se não recebeu esta mensagem, podemos enviá-lo novamente.', @@ -326,98 +326,6 @@ return [ 'title_transfer' => 'Transferências', 'title_transfers' => 'Transferências', - - // csv import: - 'csv_import' => 'Importar arquivo CSV', - 'csv' => 'CSV', - 'csv_index_title' => 'Carregar e importar um arquivo CSV', - 'csv_define_column_roles' => 'Definir papeis da coluna', - 'csv_map_values' => 'Valores mapeados encontrados para valores existentes', - 'csv_download_config' => 'Download do arquivo CSV de configuração.', - 'csv_index_text' => 'Este formulário permite você importar um arquivo CSV com transações para dentro do Firefly. É baseado no excelente importador CSV feito pelo pessoal da Atlassian. Basta fazer upload de seu arquivo CSV e siga as instruções. Se você gostaria de aprender mais, por favor clique sobre o botão na parte superior desta página.', - 'csv_index_beta_warning' => 'Esta ferramenta está em beta. Por favor proceder com cautela', - 'csv_header_help' => 'Selecione esta caixa quando a primeira linha do arquivo CSV consiste em nomes de colunas, os dados não reais', - 'csv_date_help' => 'Formato de hora de data em seu CSV. Siga o formato como indica esta página. O valor padrão analisará datas que se parecem com isso: :dateExample.', - 'csv_csv_file_help' => 'Selecione o arquivo CSV aqui. Você só pode carregar um arquivo de cada vez', - 'csv_csv_config_file_help' => 'Selecione a configuração de importação CSV aqui. Se você não sabe o que é isso, ignore. Será explicado mais tarde.', - 'csv_upload_button' => 'Iniciando importação do CSV', - 'csv_column_roles_title' => 'Definir papeis da coluna', - 'csv_column_roles_text' => 'Firefly não sabe o que significa cada coluna. Você precisa indicar o que cada coluna é. Por favor confira os dados de exemplo se você não está certo mesmo. Clique sobre o ponto de interrogação (superior direito da página) para aprender o que significa que cada coluna. Se você deseja mapear dados importados para dados existentes em Firefly, use a caixa de seleção. O próximo passo irá mostrar-lhe o que esse botão faz.', - 'csv_column_roles_table' => 'Papéis da Coluna', - 'csv_column' => 'Coluna CSV', - 'csv_column_name' => 'Nome da coluna do CSV', - 'csv_column_example' => 'Exemplo de dados da coluna', - 'csv_column_role' => 'Coluna contém?', - 'csv_do_map_value' => 'Valor mapeado?', - 'csv_continue' => 'Continuar para o próximo passo', - 'csv_go_back' => 'Voltar para o passo anterior', - 'csv_map_title' => 'Valores mapeados encontrados para valores existentes', - 'csv_map_text' => 'Esta página permite mapear os valores do arquivo CSV para entradas existentes em seu banco de dados. Isso garante que as contas e outras coisas não serão criadas duas vezes.', - 'csv_field_value' => 'Valor do campo do CSV', - 'csv_field_mapped_to' => 'Deve ser mapeado para...', - 'csv_do_not_map' => 'Não mapear este valor', - 'csv_download_config_title' => 'Download do CSV de configuração ', - 'csv_download_config_text' => 'Tudo o que você configurou pode ser baixado como um arquivo de configuração. Clique no botão para fazê-lo.', - 'csv_more_information_text' => 'Se a importação falhar, você pode usar este arquivo de configuração, então você não tem que começar tudo de novo. Mas, se a importação for bem-sucedido, será mais fácil carregar arquivos CSV semelhantes.', - 'csv_do_download_config' => 'Download do arquivo de configuração.', - 'csv_empty_description' => '(descrição vazia)', - 'csv_upload_form' => 'Formulário de Upload do CSV', - 'csv_index_unsupported_warning' => 'O importador de CSV está incapaz de fazer o seguinte:', - 'csv_unsupported_map' => 'O importador não pode mapear a coluna ":columnRole" para os valores existentes no banco de dados.', - 'csv_unsupported_value' => 'O importador não sabe como lidar com valores em colunas marcadas como ":columnRole".', - 'csv_cannot_store_value' => 'O importador não reservou espaço para colunas marcadas ":columnRole" e será incapaz de processá-los.', - 'csv_process_title' => 'Importação do CSV terminou!', - 'csv_process_text' => 'O importador do CSV terminou e processou :rows linhas', - 'csv_row' => 'Linha', - 'csv_import_with_errors' => 'Houve um erro.|Houve :errors erros.', - 'csv_error_see_logs' => 'Verifique o arquivo de log para ver detalhes.', - 'csv_process_new_entries' => 'Firefly criou :imported nova(s) transação(ões)', - 'csv_start_over' => 'Importar novamente', - 'csv_to_index' => 'Voltar para tela inicial', - 'csv_upload_not_writeable' => 'Não é possível gravar no caminho mencionado aqui. Não pode fazer o upload', - 'csv_column__ignore' => '(ignorar esta coluna)', - 'csv_column_account-iban' => 'Conta de Ativo (IBAN)', - 'csv_column_account-id' => 'ID da Conta de Ativo (correspondente Firefly)', - 'csv_column_account-name' => 'Conta de Ativo (nome)', - 'csv_column_amount' => 'Valor', - 'csv_column_amount-comma-separated' => 'Auantia (vírgula como separador decimal)', - 'csv_column_bill-id' => 'ID Fatura (correspondente Firefly)', - 'csv_column_bill-name' => 'Nom da Fatura', - 'csv_column_budget-id' => 'ID do Orçamento (correspondente Firefly)', - 'csv_column_budget-name' => 'Nome do Orçamento', - 'csv_column_category-id' => 'ID da Categoria (correspondente Firefly)', - 'csv_column_category-name' => 'Nome da Categoria', - 'csv_column_currency-code' => 'Código da Moeda (ISO 4217)', - 'csv_column_currency-id' => 'ID da Moeda (correspondente Firefly)', - 'csv_column_currency-name' => 'Nome da Moeda (correspondente Firefly)', - 'csv_column_currency-symbol' => 'Símbolo da Moeda (correspondente Firefly)', - 'csv_column_date-rent' => 'Data de cálculo da renda', - 'csv_column_date-transaction' => 'Data', - 'csv_column_description' => 'Descrição', - 'csv_column_opposing-iban' => 'Opondo-se conta (IBAN)', - 'csv_column_opposing-id' => 'ID da conta opostas (Firefly equivalente)', - 'csv_column_opposing-name' => 'Opondo-se conta (nome)', - 'csv_column_rabo-debet-credit' => 'Indicador de débito/crédito do Rabobank', - 'csv_column_ing-debet-credit' => 'Indicador de débito/crédito do ING', - 'csv_column_sepa-ct-id' => 'Transferência de crédito SEPA fim-a-fim ID', - 'csv_column_sepa-ct-op' => 'Transferência de crédito SEPA conta contrária', - 'csv_column_sepa-db' => 'SEPA Débito Direto', - 'csv_column_tags-comma' => 'Tags (separadas por vírgula)', - 'csv_column_tags-space' => 'Tags (separadas por espaço)', - 'csv_column_account-number' => 'Conta de ativo (número da conta)', - 'csv_column_opposing-number' => 'Conta Contrária (número da conta)', - 'csv_specifix_RabobankDescription' => 'Selecione esta opção quando você estiver importando arquivos de exportação de CSV do Rabobank.', - 'csv_specifix_AbnAmroDescription' => 'Selecione esta opção quando você estiver importando arquivos de exportação de CSV do ABN AMRO.', - 'csv_specifix_Dummy' => 'Marcar esta não tem qualquer efeito.', - 'csv_import_account_help' => 'Se seu arquivo CSV não contém informações sobre sua(s) conta(s) ativa(s), use este combobox para selecionar para qual conta pertencem as transações em CSV.', - 'csv_delimiter_help' => 'Escolha o delimitador de campo que é usado em seu arquivo de entrada. Se não estiver claro, a vírgula é a opção mais segura.', - 'csv_date_parse_error' => 'Não foi possível analisar uma data válida de ":value", usando o formato ":formato". Tem certeza que seu CSV está correto?', - 'could_not_recover' => 'Não poderia continuar da etapa anterior. Seu progresso foi perdido :(. Os arquivos de log você dirão o que aconteceu.', - 'must_select_roles' => 'Você deve selecionar algumas funções para o conteúdo do arquivo, ou o processo não pode continuar.', - 'invalid_mapping' => 'Você se submeteu a um mapeamento inválido. O processo não pode continuar.', - 'no_file_uploaded' => 'Parece que você não enviou um arquivo.', - - // create new stuff: 'create_new_withdrawal' => 'Criar nova retirada', 'create_new_deposit' => 'Criar um novo depósito', @@ -500,6 +408,8 @@ return [ 'cannot_scan_inactive_bill' => 'Faturas inativas não podem ser verificadas.', 'rescanned_bill' => 'Tudo examinado novamente.', 'bill_date_little_relevance' => 'A única parte desta data usado pelo Firefly é o dia. Só é útil quando a conta chega exatamente na mesma data, todo mês. Se a data de pagamento de suas contas varia, basta usar o primeiro dia do mês.', + 'average_bill_amount_year' => 'Average bill amount (:year)', + 'average_bill_amount_overall' => 'Average bill amount (overall)', // accounts: 'details_for_asset' => 'Detalhes para a conta de ativo ":name"', @@ -644,19 +554,16 @@ return [ 'saveOnAccount' => 'Salvar na conta', 'unknown' => 'Desconhecido', 'daily' => 'Diário', - 'weekly' => 'Semanal', 'monthly' => 'Mensal', - 'quarterly' => 'Trimestral', - 'half-year' => 'Semestral', - 'yearly' => 'Anual', 'profile' => 'Perfil', + 'errors' => 'Errors', // reports: 'report_default' => 'Relatório financeiro padrão de :start até :end', 'report_audit' => 'Visão geral do histórico de transação de :start até :end', 'quick_link_reports' => 'Ligações rápidas', 'quick_link_default_report' => 'Relatório financeiro padrão', - 'quick_link_audit_report' => 'Transaction history overview', + 'quick_link_audit_report' => 'Visão geral do histórico de transação', 'report_this_month_quick' => 'Mês atual, todas as contas', 'report_this_year_quick' => 'Ano atual, todas as contas', 'report_this_fiscal_year_quick' => 'Ano fiscal atual, todas as contas', @@ -742,6 +649,7 @@ return [ 'balanceFor' => 'Saldo para ":name"', // piggy banks: + 'add_money_to_piggy' => 'Add money to piggy bank ":name"', 'piggy_bank' => 'Cofrinho', 'new_piggy_bank' => 'Criar novo cofrinho', 'store_piggy_bank' => 'Armazenar novo cofrinho', @@ -803,36 +711,70 @@ return [ 'user_administration' => 'Administração de usuários', 'list_all_users' => 'Todos os usuários', 'all_users' => 'Todos os usuários', + 'all_blocked_domains' => 'All blocked domains', + 'blocked_domains' => 'Blocked domains', + 'no_domains_banned' => 'No domains blocked', + 'all_user_domains' => 'All user email address domains', + 'all_domains_is_filtered' => 'This list does not include already blocked domains.', + 'domain_now_blocked' => 'Domain :domain is now blocked', + 'domain_now_unblocked' => 'Domain :domain is now unblocked', + 'manual_block_domain' => 'Block a domain by hand', + 'block_domain' => 'Block domain', + 'no_domain_filled_in' => 'No domain filled in', + 'domain_already_blocked' => 'Domain :domain is already blocked', + 'domain_is_now_blocked' => 'Domain :domain is now blocked', // split a transaction: - 'transaction_meta_data' => 'Transaction meta-data', - 'transaction_dates' => 'Transaction dates', - 'splits' => 'Splits', - 'split_title_withdrawal' => 'Split your new withdrawal', - 'split_intro_one_withdrawal' => 'Firefly supports the "splitting" of a withdrawal.', - 'split_intro_two_withdrawal' => 'It means that the amount of money you\'ve spent is divided between several destination expense accounts, budgets or categories.', - 'split_intro_three_withdrawal' => 'For example: you could split your :total groceries so you pay :split_one from your "daily groceries" budget and :split_two from your "cigarettes" budget.', - 'split_table_intro_withdrawal' => 'Split your withdrawal in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', - 'store_splitted_withdrawal' => 'Store splitted withdrawal', - 'update_splitted_withdrawal' => 'Update splitted withdrawal', - - 'split_title_deposit' => 'Split your new deposit', - 'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.', - 'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.', - 'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.', - 'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', - 'store_splitted_deposit' => 'Store splitted deposit', - - 'split_title_transfer' => 'Split your new transfer', - 'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.', - 'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.', - 'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.', - 'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', - 'store_splitted_transfer' => 'Store splitted transfer', - - 'add_another_split' => 'Add another split', - 'split-transactions' => 'Split transactions', - 'split-new-transaction' => 'Split a new transaction', - + 'transaction_meta_data' => 'Dados de transação', + 'transaction_dates' => 'Data de transação', + 'splits' => 'Divide-se', + 'split_title_withdrawal' => 'Dividir sua nova retirada', + 'split_intro_one_withdrawal' => 'Firefly suporta a "divisão" de uma retirada.', + 'split_intro_two_withdrawal' => 'Isso significa que a quantidade de dinheiro que você gastou será dividida entre várias contas de despesas do destino, orçamentos ou categorias.', + 'split_intro_three_withdrawal' => 'Por exemplo: você pode dividir seu :total de mantimentos de modo que você pague :split_one de seu orçamento diário de mantimentos e :split_two do seu orçamento para "cigarros".', + 'split_table_intro_withdrawal' => 'Dividi sua retirada em tantas coisas quanto quiser. Por padrão, a transação não será dividida, há apenas uma entrada. Adicione muitas divisões como desejar, abaixo. Lembre-se de que você não deve se desviar do seu montante total. Se o fizeres, Firefly irá avisá-lo mas não corrigirá.', + 'store_splitted_withdrawal' => 'Armazenar retirada dividida', + 'update_splitted_withdrawal' => 'Atualização de retirada dividida', + 'split_title_deposit' => 'Dividir seu novo depósito', + 'split_intro_one_deposit' => 'Firefly suporta a "divisão" de um depósito.', + 'split_intro_two_deposit' => 'Isso significa que a quantidade de dinheiro que você ganhou será dividida entre várias contas de receitas de fonte ou categorias.', + 'split_intro_three_deposit' => 'Por exemplo: você pode dividir seu :total salário para que você obtenha :split_one como seu salário-base e :split_two como um reembolso para alguma despesa.', + 'split_table_intro_deposit' => 'Dividi seu depósito em tantas coisas quanto quiser. Por padrão, a transação não será dividida, há apenas uma entrada. Adicione muitas divisões como desejar, abaixo. Lembre-se de que você não deve se desviar do seu montante total. Se o fizeres, Firefly irá avisá-lo mas não corrigirá.', + 'store_splitted_deposit' => 'Armazenar depósito dividido', + 'split_title_transfer' => 'Dividir sua nova transferência', + 'split_intro_one_transfer' => 'Firefly suporta a "divisão" de uma transferência.', + 'split_intro_two_transfer' => 'Isso significa que a quantidade de dinheiro que você está movendo será dividida entre várias categorias ou cofrinhos.', + 'split_intro_three_transfer' => 'Por exemplo: você pode dividir sua movimentação :total , para que você obtenha :split_one em um cofrinho e :split_two em outro.', + 'split_table_intro_transfer' => 'Dividi sua transferência em tantas coisas quanto quiser. Por padrão, a transação não será dividida, há apenas uma entrada. Adicione muitas divisões como desejar, abaixo. Lembre-se de que você não deve se desviar do seu montante total. Se o fizeres, Firefly irá avisá-lo mas não corrigirá.', + 'store_splitted_transfer' => 'Armazenar transferência dividida', + 'add_another_split' => 'Adicionar outra divisão', + 'split-transactions' => 'Dividir transações', + 'split-new-transaction' => 'Dividir uma nova transação', + 'do_split' => 'Do a split', + 'split_this_withdrawal' => 'Split this withdrawal', + 'split_this_deposit' => 'Split this deposit', + 'split_this_transfer' => 'Split this transfer', + 'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.', + 'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.', + 'no_edit_multiple_left' => 'You have selected no valid transactions to edit.', + // import + 'configuration_file_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you.', + 'import_data_index' => 'Index', + 'import_file_type_csv' => 'CSV (comma separated values)', + 'import_file_type_help' => 'Select the type of file you will upload', + 'import_start' => 'Start the import', + 'configure_import' => 'Further configure your import', + 'import_finish_configuration' => 'Finish configuration', + 'settings_for_import' => 'Settings', + 'import_complete' => 'Import configuration complete!', + 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you need to execute the following command in your console. Unfortunately, a web-based import is not yet possible.', + 'import_download_config' => 'Download configuration', + 'import_start_import' => 'Start import', + 'import_intro_beta' => 'The import function of Firefly III is in beta. Many users of Firefly III have tried many different files. Although each individual compontent of this import routine works (really), the combination might break. If your file cannot be imported by Firefly, please read this wiki page so I can fix the problem you have run into.', + 'import_data' => 'Import data', + 'import_data_full' => 'Import data into Firefly III', + 'import' => 'Import', + 'import_intro_text' => 'Welcome to the Firefly III data import routine. At the moment, this routine can help you import files into Firefly. To do so, you must download or export transactions from other systems or software, and upload them here. The next steps will let you help Firefly III determin what the content is of your file, and how to handle it. Please select a file, and read all instructions carefully.', + 'import_file_help' => 'Select your file', ]; diff --git a/resources/lang/pt_BR/form.php b/resources/lang/pt_BR/form.php index 1b7f25b825..639c6f6377 100644 --- a/resources/lang/pt_BR/form.php +++ b/resources/lang/pt_BR/form.php @@ -22,20 +22,20 @@ return [ 'amount_max' => 'Valor Máximo', 'match' => 'Corresponde em', 'repeat_freq' => 'Repetições', - 'journal_currency_id' => 'Currency', - 'journal_amount' => 'Amount', - 'journal_asset_source_account' => 'Asset account (source)', - 'journal_source_account_name' => 'Revenue account (source)', - 'journal_source_account_id' => 'Asset account (source)', + 'journal_currency_id' => 'Moeda', + 'journal_amount' => 'Quantia', + 'journal_asset_source_account' => 'Conta de ativo (fonte)', + 'journal_source_account_name' => 'Conta de receita (fonte)', + 'journal_source_account_id' => 'Conta de ativo (fonte)', 'account_from_id' => 'da conta', 'account_to_id' => 'para conta', - 'journal_destination_account_id' => 'Asset account (destination)', - 'asset_destination_account' => 'Asset account (destination)', - 'asset_source_account' => 'Asset account (source)', - 'journal_description' => 'Description', - 'split_journal' => 'Split this transaction', - 'split_journal_explanation' => 'Split this transaction in multiple parts', - 'currency' => 'Currency', + 'journal_destination_account_id' => 'Conta de ativo (destino)', + 'asset_destination_account' => 'Conta de ativo (destino)', + 'asset_source_account' => 'Conta de ativo (fonte)', + 'journal_description' => 'Descrição', + 'split_journal' => 'Dividir essa transação', + 'split_journal_explanation' => 'Dividir essa transação em várias partes', + 'currency' => 'Moeda', 'account_id' => 'Conta de ativo', 'budget_id' => 'Orçamento', 'openingBalance' => 'Saldo inicial', @@ -71,13 +71,9 @@ return [ 'code' => 'Código', 'iban' => 'IBAN', 'accountNumber' => 'Número de conta', - 'csv' => 'Arquivo CSV', 'has_headers' => 'Cabeçalhos', 'date_format' => 'Formato da Data', - 'csv_config' => 'Importar CSV de configuração', 'specifix' => 'Banco- ou arquivo específico corrigídos', - 'csv_import_account' => 'Conta de importação padrão', - 'csv_delimiter' => 'Delimitador de campo CSV', 'attachments[]' => 'Anexos', 'store_new_withdrawal' => 'Armazenar nova retirada', 'store_new_deposit' => 'Armazenar novo depósito', @@ -102,9 +98,6 @@ return [ 'include_config' => 'Incluir o arquivo de configuração', 'include_old_uploads' => 'Incluir dados importados', 'accounts' => 'Exportar transações destas contas', - 'csv_comma' => 'Uma vírgula (,)', - 'csv_semicolon' => 'Um ponto e vírgula (;)', - 'csv_tab' => 'Um Tab (invisível)', 'delete_account' => 'Apagar conta ":name"', 'delete_bill' => 'Apagar fatura ":name"', 'delete_budget' => 'Excluir o orçamento ":name"', @@ -137,4 +130,20 @@ return [ 'budget_keep_transactions' => 'A única transação conectada a este orçamento não será excluída.|Todos :count transações ligadas a este orçamento não serão excluídos.', 'category_keep_transactions' => 'A única transação ligada a esta categoria não será excluída.|Todos :count transações ligadas a esta categoria não serão excluídos.', 'tag_keep_transactions' => 'A única transação ligada a essa marca não será excluída.|Todos :count transações ligadas a essa marca não serão excluídos.', + + // admin + 'domain' => 'Domain', + + // import + 'import_file' => 'Import file', + 'configuration_file' => 'Configuration file', + 'import_file_type' => 'Import file type', + 'csv_comma' => 'Uma vírgula (,)', + 'csv_semicolon' => 'Um ponto e vírgula (;)', + 'csv_tab' => 'Um Tab (invisível)', + 'csv_delimiter' => 'Delimitador de campo CSV', + 'csv_import_account' => 'Conta de importação padrão', + 'csv_config' => 'Importar CSV de configuração', + + ]; diff --git a/resources/lang/pt_BR/help.php b/resources/lang/pt_BR/help.php index 9f720e032e..c0ce893390 100644 --- a/resources/lang/pt_BR/help.php +++ b/resources/lang/pt_BR/help.php @@ -55,11 +55,6 @@ return [ 'categories-show' => 'Detalhes de Categorias', 'categories-show-date' => 'Detalhes da Categoria por Data', 'categories-noCategory' => 'Sem Categorias', - 'csv-index' => 'Carregar e importar um arquivo CSV', - 'csv-column-roles' => 'CSV Papel da coluna', - 'csv-map' => 'Mapeamento CSV', - 'csv-download-config-page' => 'Baixando CSV de Configuração', - 'csv-process' => 'Processando CSV', 'currency-index' => 'Moedas', 'currency-create' => 'Criando Moedas', 'currency-edit' => 'Editando Moedas', diff --git a/resources/lang/pt_BR/list.php b/resources/lang/pt_BR/list.php index 21e94af988..5d0c5f460d 100644 --- a/resources/lang/pt_BR/list.php +++ b/resources/lang/pt_BR/list.php @@ -8,54 +8,59 @@ */ return [ - 'buttons' => 'Botões', - 'icon' => 'Ícone', - 'create_date' => 'Criado em', - 'update_date' => 'Atualizado em', - 'balance_before' => 'Saldo Antes', - 'balance_after' => 'Saldo depois', - 'name' => 'Nome', - 'role' => 'Papel', - 'currentBalance' => 'Saldo atual', - 'active' => 'Está ativo?', - 'lastActivity' => 'Última atividade', - 'balanceDiff' => 'Saldo diferente entre :start e :end', - 'matchedOn' => 'Coincide', - 'matchesOn' => 'Correspondido em', - 'account_type' => 'Account type', - 'new_balance' => 'New balance', - 'account' => 'Account', - 'matchingAmount' => 'Total', - 'lastMatch' => 'Último equivalente', - 'split_number' => 'Split #', - 'destination' => 'Destination', - 'expectedMatch' => 'Equivalente esperado', - 'automatch' => 'Auto match?', - 'repeat_freq' => 'Repetições', - 'description' => 'Descrição', - 'amount' => 'Total', - 'date' => 'Data', - 'interest_date' => 'Data de interesse', - 'book_date' => 'Data reserva', - 'process_date' => 'Data de processamento', - 'from' => 'De', - 'piggy_bank' => 'Piggy bank', - 'to' => 'Até', - 'budget' => 'Orçamento', - 'category' => 'Categoria', - 'bill' => 'Fatura', - 'withdrawal' => 'Retirada', - 'deposit' => 'Depósito', - 'transfer' => 'Transferência', - 'type' => 'Tipo', - 'completed' => 'Completo', - 'iban' => 'IBAN', - 'paid_current_period' => 'Pago este período', - 'email' => 'Email', - 'registered_at' => 'Registrado em', - 'is_activated' => 'Está ativo', - 'is_blocked' => 'Está bloqueado', - 'is_admin' => 'É admin', - 'has_two_factor' => 'Tem 2FA', - 'blocked_code' => 'Bloco de código', + 'buttons' => 'Botões', + 'icon' => 'Ícone', + 'create_date' => 'Criado em', + 'update_date' => 'Atualizado em', + 'balance_before' => 'Saldo Antes', + 'balance_after' => 'Saldo depois', + 'name' => 'Nome', + 'role' => 'Papel', + 'currentBalance' => 'Saldo atual', + 'active' => 'Está ativo?', + 'lastActivity' => 'Última atividade', + 'balanceDiff' => 'Saldo diferente entre :start e :end', + 'matchedOn' => 'Coincide', + 'matchesOn' => 'Correspondido em', + 'account_type' => 'Tipo de conta', + 'new_balance' => 'Novo saldo', + 'account' => 'Conta', + 'matchingAmount' => 'Total', + 'lastMatch' => 'Último equivalente', + 'split_number' => 'Dividir #', + 'destination' => 'Destino', + 'source' => 'Source', + 'expectedMatch' => 'Equivalente esperado', + 'automatch' => 'Auto match?', + 'repeat_freq' => 'Repetições', + 'description' => 'Descrição', + 'amount' => 'Total', + 'date' => 'Data', + 'interest_date' => 'Data de interesse', + 'book_date' => 'Data reserva', + 'process_date' => 'Data de processamento', + 'from' => 'De', + 'piggy_bank' => 'Cofrinho', + 'to' => 'Até', + 'budget' => 'Orçamento', + 'category' => 'Categoria', + 'bill' => 'Fatura', + 'withdrawal' => 'Retirada', + 'deposit' => 'Depósito', + 'transfer' => 'Transferência', + 'type' => 'Tipo', + 'completed' => 'Completo', + 'iban' => 'IBAN', + 'paid_current_period' => 'Pago este período', + 'email' => 'Email', + 'registered_at' => 'Registrado em', + 'is_activated' => 'Está ativo', + 'is_blocked' => 'Está bloqueado', + 'is_admin' => 'É admin', + 'has_two_factor' => 'Tem 2FA', + 'confirmed_from' => 'Confirmed from', + 'registered_from' => 'Registered from', + 'blocked_code' => 'Bloco de código', + 'domain' => 'Domain', + 'registration_attempts' => 'Registration attempts', ]; diff --git a/resources/lang/pt_BR/validation.php b/resources/lang/pt_BR/validation.php index f34f08e2d2..2817960330 100644 --- a/resources/lang/pt_BR/validation.php +++ b/resources/lang/pt_BR/validation.php @@ -17,7 +17,7 @@ return [ 'file_attached' => 'Arquivo carregado com sucesso ":name".', 'file_invalid_mime' => 'Arquivo ":name" é do tipo ":mime" que não é aceito como um novo upload.', 'file_too_large' => 'Arquivo ":name" é muito grande.', - 'belongs_to_user' => 'The value of :attribute is unknown', + 'belongs_to_user' => 'O valor de :attribute é desconhecido', 'accepted' => 'O campo :attribute deve ser aceito.', 'active_url' => 'O campo :attribute não contém um URL válido.', 'after' => 'O campo :attribute deverá conter uma data posterior a :date.', diff --git a/resources/lang/zh-HK/breadcrumbs.php b/resources/lang/zh-HK/breadcrumbs.php new file mode 100644 index 0000000000..bdd804cf63 --- /dev/null +++ b/resources/lang/zh-HK/breadcrumbs.php @@ -0,0 +1,46 @@ + 'Home', + 'cash_accounts' => 'Cash accounts', + 'edit_account' => 'Edit account ":name"', + 'edit_currency' => 'Edit currencies ":name"', + 'delete_currency' => 'Delete currencies ":name"', + 'newPiggyBank' => 'Create a new piggy bank', + 'edit_piggyBank' => 'Edit piggy bank ":name"', + 'preferences' => 'Preferences', + 'profile' => 'Profile', + 'changePassword' => 'Change your password', + 'bills' => 'Bills', + 'newBill' => 'New bill', + 'edit_bill' => 'Edit bill ":name"', + 'delete_bill' => 'Delete bill ":name"', + 'reports' => 'Reports', + 'monthly_report' => 'Monthly report for :date', + 'monthly_report_shared' => 'Monthly report for :date (including shared accounts)', + 'yearly_report' => 'Yearly report for :date', + 'yearly_report_shared' => 'Yearly report for :date (including shared accounts)', + 'budget_report' => 'Budget report for :date', + 'searchResult' => 'Search for ":query"', + 'withdrawal_list' => 'Expenses', + 'deposit_list' => 'Revenue, income and deposits', + 'transfer_list' => 'Transfers', + 'transfers_list' => 'Transfers', + 'create_withdrawal' => 'Create new withdrawal', + 'create_deposit' => 'Create new deposit', + 'create_transfer' => 'Create new transfer', + 'edit_journal' => 'Edit transaction ":description"', + 'delete_journal' => 'Delete transaction ":description"', + 'tags' => 'Tags', + 'createTag' => 'Create new tag', + 'edit_tag' => 'Edit tag ":tag"', + 'delete_tag' => 'Delete tag ":tag"', +]; diff --git a/resources/lang/zh-HK/config.php b/resources/lang/zh-HK/config.php new file mode 100644 index 0000000000..b8ed029a46 --- /dev/null +++ b/resources/lang/zh-HK/config.php @@ -0,0 +1,21 @@ + 'en, English, en_US, en_US.utf8', + 'month' => '%B %Y', + 'month_and_day' => '%B %e, %Y', + 'date_time' => '%B %e, %Y, @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'Week %W, %Y', + 'quarter_of_year' => '%B %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + +]; diff --git a/resources/lang/zh-HK/csv.php b/resources/lang/zh-HK/csv.php new file mode 100644 index 0000000000..858026dad6 --- /dev/null +++ b/resources/lang/zh-HK/csv.php @@ -0,0 +1,80 @@ + 'Configure your import', + 'import_configure_intro' => 'There are some options for your CSV import. Please indicate if your CSV file contains headers on the first column, and what the date format of your date-fields is. That might require some experimentation. The field delimiter is usually a ",", but could also be a ";". Check this carefully.', + 'import_configure_form' => 'Form', + 'header_help' => 'Check this if the first row of your CSV file are the column titles', + 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', + 'import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', + + // roles + 'column_roles_title' => 'Define column roles', + 'column_roles_text' => '

    Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

    Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

    ', + 'column_roles_table' => 'Table', + 'column_name' => 'Name of column', + 'column_example' => 'Column example data', + 'column_role' => 'Column data meaning', + 'do_map_value' => 'Map these values', + 'column' => 'Column', + 'no_example_data' => 'No example data available', + 'store_column_roles' => 'Continue import', + 'do_not_map' => '(do not map)', + 'map_title' => 'Connect import data to Firefly III data', + 'map_text' => 'In the following tables, the left value shows you information found in your uploaded CSV file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', + + 'field_value' => 'Field value', + 'field_mapped_to' => 'Mapped to', + 'store_column_mapping' => 'Store mapping', + + // map things. + + + 'column__ignore' => '(ignore this column)', + 'column_account-iban' => 'Asset account (IBAN)', + 'column_account-id' => 'Asset account ID (matching Firefly)', + 'column_account-name' => 'Asset account (name)', + 'column_amount' => 'Amount', + 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', + 'column_bill-id' => 'Bill ID (matching Firefly)', + 'column_bill-name' => 'Bill name', + 'column_budget-id' => 'Budget ID (matching Firefly)', + 'column_budget-name' => 'Budget name', + 'column_category-id' => 'Category ID (matching Firefly)', + 'column_category-name' => 'Category name', + 'column_currency-code' => 'Currency code (ISO 4217)', + 'column_currency-id' => 'Currency ID (matching Firefly)', + 'column_currency-name' => 'Currency name (matching Firefly)', + 'column_currency-symbol' => 'Currency symbol (matching Firefly)', + 'column_date-interest' => 'Interest calculation date', + 'column_date-book' => 'Transaction booking date', + 'column_date-process' => 'Transaction process date', + 'column_date-transaction' => 'Date', + 'column_description' => 'Description', + 'column_opposing-iban' => 'Opposing account (IBAN)', + 'column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'column_external-id' => 'External ID', + 'column_opposing-name' => 'Opposing account (name)', + 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'column_ing-debet-credit' => 'ING specific debet/credit indicator', + 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'column_sepa-db' => 'SEPA Direct Debet', + 'column_tags-comma' => 'Tags (comma separated)', + 'column_tags-space' => 'Tags (space separated)', + 'column_account-number' => 'Asset account (account number)', + 'column_opposing-number' => 'Opposing account (account number)', +]; \ No newline at end of file diff --git a/resources/lang/zh-HK/firefly.php b/resources/lang/zh-HK/firefly.php new file mode 100644 index 0000000000..08238377e7 --- /dev/null +++ b/resources/lang/zh-HK/firefly.php @@ -0,0 +1,780 @@ + 'This language is not yet fully translated', + 'test' => 'You have selected English.', + 'close' => 'Close', + 'pleaseHold' => 'Please hold...', + 'actions' => 'Actions', + 'edit' => 'Edit', + 'delete' => 'Delete', + 'welcomeBack' => 'What\'s playing?', + 'everything' => 'Everything', + 'customRange' => 'Custom range', + 'apply' => 'Apply', + 'cancel' => 'Cancel', + 'from' => 'From', + 'to' => 'To', + 'total_sum' => 'Total sum', + 'period_sum' => 'Sum for period', + 'showEverything' => 'Show everything', + 'never' => 'Never', + 'search_results_for' => 'Search results for ":query"', + 'bounced_error' => 'The message sent to :email bounced, so no access for you.', + 'deleted_error' => 'These credentials do not match our records.', + 'general_blocked_error' => 'Your account has been disabled, so you cannot login.', + 'expired_error' => 'Your account has expired, and can no longer be used.', + 'unbalanced_error' => 'Your transactions are unbalanced. This means a withdrawal, deposit or transfer was not stored properly. Please check your accounts and transactions for errors (unbalanced amount :amount).', + 'removed_amount' => 'Removed :amount', + 'added_amount' => 'Added :amount', + 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', + 'Opening balance' => 'Opening balance', + 'create_new_stuff' => 'Create new stuff', + 'new_withdrawal' => 'New withdrawal', + 'new_deposit' => 'New deposit', + 'new_transfer' => 'New transfer', + 'new_asset_account' => 'New asset account', + 'new_expense_account' => 'New expense account', + 'new_revenue_account' => 'New revenue account', + 'new_budget' => 'New budget', + 'new_bill' => 'New bill', + 'block_account_logout' => 'You have been logged out. Blocked accounts cannot use this site. Did you register with a valid email address?', + 'flash_success' => 'Success!', + 'flash_info' => 'Message', + 'flash_warning' => 'Warning!', + 'flash_error' => 'Error!', + 'flash_info_multiple' => 'There is one message|There are :count messages', + 'flash_error_multiple' => 'There is one error|There are :count errors', + 'net_worth' => 'Net worth', + 'route_has_no_help' => 'There is no help for this route, or there is no help available in your language.', + 'two_factor_welcome' => 'Hello, :user!', + 'two_factor_enter_code' => 'To continue, please enter your two factor authentication code. Your application can generate it for you.', + 'two_factor_code_here' => 'Enter code here', + 'two_factor_title' => 'Two factor authentication', + 'authenticate' => 'Authenticate', + 'two_factor_forgot_title' => 'Lost two factor authentication', + 'two_factor_forgot' => 'I forgot my two-factor thing.', + 'two_factor_lost_header' => 'Lost your two factor authentication?', + 'two_factor_lost_intro' => 'Unfortunately, this is not something you can reset from the web interface. You have two choices.', + 'two_factor_lost_fix_self' => 'If you run your own instance of Firefly III, check the logs in storage/logs for instructions.', + 'two_factor_lost_fix_owner' => 'Otherwise, email the site owner, :site_owner and ask them to reset your two factor authentication.', + 'warning_much_data' => ':days days of data may take a while to load.', + 'registered' => 'You have registered successfully!', + 'search' => 'Search', + 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', + 'source_accounts' => 'Source account(s)', + 'destination_accounts' => 'Destination account(s)', + + // repeat frequencies: + 'repeat_freq_monthly' => 'monthly', + 'weekly' => 'weekly', + 'quarterly' => 'quarterly', + 'half-year' => 'every half year', + 'yearly' => 'yearly', + // account confirmation: + 'confirm_account_header' => 'Please confirm your account', + 'confirm_account_intro' => 'An email has been sent to the address you used during your registration. Please check it out for further instructions. If you did not get this message, you can have Firefly send it again.', + 'confirm_account_resend_email' => 'Send me the confirmation message I need to activate my account.', + 'account_is_confirmed' => 'Your account has been confirmed!', + 'invalid_activation_code' => 'It seems the code you are using is not valid, or has expired.', + 'confirm_account_is_resent_header' => 'The confirmation has been resent', + 'confirm_account_is_resent_text' => 'The confirmation message has been resent. If you still did not receive the confirmation message, please contact the site owner at :owner or check the log files to see what went wrong.', + 'confirm_account_is_resent_go_home' => 'Go to the index page of Firefly', + 'confirm_account_not_resent_header' => 'Something went wrong :(', + 'confirm_account_not_resent_intro' => 'The confirmation message has been not resent. If you still did not receive the confirmation message, please contact the site owner at :owner instead. Possibly, you have tried to resend the activation message too often. You can have Firefly III try to resend the confirmation message every hour.', + 'confirm_account_not_resent_go_home' => 'Go to the index page of Firefly', + + // export data: + 'import_and_export' => 'Import and export', + 'export_data' => 'Export data', + 'export_data_intro' => 'For backup purposes, when migrating to another system or when migrating to another Firefly III installation.', + 'export_format' => 'Export format', + 'export_format_csv' => 'Comma separated values (CSV file)', + 'export_format_mt940' => 'MT940 compatible format', + 'export_included_accounts' => 'Export transactions from these accounts', + 'include_config_help' => 'For easy re-import into Firefly III', + 'include_old_uploads_help' => 'Firefly III does not throw away the original CSV files you have imported in the past. You can include them in your export.', + 'do_export' => 'Export', + 'export_status_never_started' => 'The export has not started yet', + 'export_status_make_exporter' => 'Creating exporter thing...', + 'export_status_collecting_journals' => 'Collecting your transactions...', + 'export_status_collected_journals' => 'Collected your transactions!', + 'export_status_converting_to_export_format' => 'Converting your transactions...', + 'export_status_converted_to_export_format' => 'Converted your transactions!', + 'export_status_creating_journal_file' => 'Creating the export file...', + 'export_status_created_journal_file' => 'Created the export file!', + 'export_status_collecting_attachments' => 'Collecting all your attachments...', + 'export_status_collected_attachments' => 'Collected all your attachments!', + 'export_status_collecting_old_uploads' => 'Collecting all your previous uploads...', + 'export_status_collected_old_uploads' => 'Collected all your previous uploads!', + 'export_status_creating_config_file' => 'Creating a configuration file...', + 'export_status_created_config_file' => 'Created a configuration file!', + 'export_status_creating_zip_file' => 'Creating a zip file...', + 'export_status_created_zip_file' => 'Created a zip file!', + 'export_status_finished' => 'Export has succesfully finished! Yay!', + 'export_data_please_wait' => 'Please wait...', + 'attachment_explanation' => 'The file called \':attachment_name\' (#:attachment_id) was originally uploaded to :type \':description\' (#:journal_id) dated :date for the amount of :amount.', + + // rules + 'rules' => 'Rules', + 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.', + 'rule_name' => 'Name of rule', + 'rule_triggers' => 'Rule triggers when', + 'rule_actions' => 'Rule will', + 'new_rule' => 'New rule', + 'new_rule_group' => 'New rule group', + 'rule_priority_up' => 'Give rule more priority', + 'rule_priority_down' => 'Give rule less priority', + 'make_new_rule_group' => 'Make new rule group', + 'store_new_rule_group' => 'Store new rule group', + 'created_new_rule_group' => 'New rule group ":title" stored!', + 'updated_rule_group' => 'Successfully updated rule group ":title".', + 'edit_rule_group' => 'Edit rule group ":title"', + 'delete_rule_group' => 'Delete rule group ":title"', + 'deleted_rule_group' => 'Deleted rule group ":title"', + 'update_rule_group' => 'Update rule group', + 'no_rules_in_group' => 'There are no rules in this group', + 'move_rule_group_up' => 'Move rule group up', + 'move_rule_group_down' => 'Move rule group down', + 'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:', + 'make_new_rule' => 'Make new rule in rule group ":title"', + 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', + 'rule_help_active' => 'Inactive rules will never fire.', + 'stored_new_rule' => 'Stored new rule with title ":title"', + 'deleted_rule' => 'Deleted rule with title ":title"', + 'store_new_rule' => 'Store new rule', + 'updated_rule' => 'Updated rule with title ":title"', + 'default_rule_group_name' => 'Default rules', + 'default_rule_group_description' => 'All your rules not in a particular group.', + 'default_rule_name' => 'Your first default rule', + 'default_rule_description' => 'This rule is an example. You can safely delete it.', + 'default_rule_trigger_description' => 'The Man Who Sold the World', + 'default_rule_trigger_from_account' => 'David Bowie', + 'default_rule_action_prepend' => 'Bought the world from ', + 'default_rule_action_set_category' => 'Large expenses', + 'trigger' => 'Trigger', + 'trigger_value' => 'Trigger on value', + 'stop_processing_other_triggers' => 'Stop processing other triggers', + 'add_rule_trigger' => 'Add new trigger', + 'action' => 'Action', + 'action_value' => 'Action value', + 'stop_executing_other_actions' => 'Stop executing other actions', + 'add_rule_action' => 'Add new action', + 'edit_rule' => 'Edit rule ":title"', + 'delete_rule' => 'Delete rule ":title"', + 'update_rule' => 'Update rule', + 'test_rule_triggers' => 'See matching transactions', + 'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions', + 'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.', + 'warning_no_valid_triggers' => 'No valid triggers provided.', + 'execute_on_existing_transactions' => 'Execute for existing transactions', + 'execute_on_existing_transactions_intro' => 'When a rule or group has been changed or added, you can execute it for existing transactions', + 'execute_on_existing_transactions_short' => 'Existing transactions', + 'executed_group_on_existing_transactions' => 'Executed group ":title" for existing transactions', + 'execute_group_on_existing_transactions' => 'Execute group ":title" for existing transactions', + 'include_transactions_from_accounts' => 'Include transactions from these accounts', + 'execute' => 'Execute', + + // actions and triggers + 'rule_trigger_user_action' => 'User action is ":trigger_value"', + 'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"', + 'rule_trigger_from_account_ends' => 'Source account ends with ":trigger_value"', + 'rule_trigger_from_account_is' => 'Source account is ":trigger_value"', + 'rule_trigger_from_account_contains' => 'Source account contains ":trigger_value"', + 'rule_trigger_to_account_starts' => 'Destination account starts with ":trigger_value"', + 'rule_trigger_to_account_ends' => 'Destination account ends with ":trigger_value"', + 'rule_trigger_to_account_is' => 'Destination account is ":trigger_value"', + 'rule_trigger_to_account_contains' => 'Destination account contains ":trigger_value"', + 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"', + 'rule_trigger_amount_less' => 'Amount is less than :trigger_value', + 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', + 'rule_trigger_amount_more' => 'Amount is more than :trigger_value', + 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"', + 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"', + 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', + 'rule_trigger_description_is' => 'Description is ":trigger_value"', + 'rule_trigger_from_account_starts_choice' => 'Source account starts with..', + 'rule_trigger_from_account_ends_choice' => 'Source account ends with..', + 'rule_trigger_from_account_is_choice' => 'Source account is..', + 'rule_trigger_from_account_contains_choice' => 'Source account contains..', + 'rule_trigger_to_account_starts_choice' => 'Destination account starts with..', + 'rule_trigger_to_account_ends_choice' => 'Destination account ends with..', + 'rule_trigger_to_account_is_choice' => 'Destination account is..', + 'rule_trigger_to_account_contains_choice' => 'Destination account contains..', + 'rule_trigger_transaction_type_choice' => 'Transaction is of type..', + 'rule_trigger_amount_less_choice' => 'Amount is less than..', + 'rule_trigger_amount_exactly_choice' => 'Amount is..', + 'rule_trigger_amount_more_choice' => 'Amount is more than..', + 'rule_trigger_description_starts_choice' => 'Description starts with..', + 'rule_trigger_description_ends_choice' => 'Description ends with..', + 'rule_trigger_description_contains_choice' => 'Description contains..', + 'rule_trigger_description_is_choice' => 'Description is..', + 'rule_trigger_store_journal' => 'When a journal is created', + 'rule_trigger_update_journal' => 'When a journal is updated', + 'rule_action_set_category' => 'Set category to ":action_value"', + 'rule_action_clear_category' => 'Clear category', + 'rule_action_set_budget' => 'Set budget to ":action_value"', + 'rule_action_clear_budget' => 'Clear budget', + 'rule_action_add_tag' => 'Add tag ":action_value"', + 'rule_action_remove_tag' => 'Remove tag ":action_value"', + 'rule_action_remove_all_tags' => 'Remove all tags', + 'rule_action_set_description' => 'Set description to ":action_value"', + 'rule_action_append_description' => 'Append description with ":action_value"', + 'rule_action_prepend_description' => 'Prepend description with ":action_value"', + 'rule_action_set_category_choice' => 'Set category to..', + 'rule_action_clear_category_choice' => 'Clear any category', + 'rule_action_set_budget_choice' => 'Set budget to..', + 'rule_action_clear_budget_choice' => 'Clear any budget', + 'rule_action_add_tag_choice' => 'Add tag..', + 'rule_action_remove_tag_choice' => 'Remove tag..', + 'rule_action_remove_all_tags_choice' => 'Remove all tags', + 'rule_action_set_description_choice' => 'Set description to..', + 'rule_action_append_description_choice' => 'Append description with..', + 'rule_action_prepend_description_choice' => 'Prepend description with..', + + // tags + 'store_new_tag' => 'Store new tag', + 'update_tag' => 'Update tag', + 'no_location_set' => 'No location set.', + 'meta_data' => 'Meta data', + 'location' => 'Location', + + // preferences + 'pref_home_screen_accounts' => 'Home screen accounts', + 'pref_home_screen_accounts_help' => 'Which accounts should be displayed on the home page?', + 'pref_budget_settings' => 'Budget settings', + 'pref_budget_settings_help' => 'What\'s the maximum amount of money a budget envelope may contain?', + 'pref_view_range' => 'View range', + 'pref_view_range_help' => 'Some charts are automatically grouped in periods. What period would you prefer?', + 'pref_1D' => 'One day', + 'pref_1W' => 'One week', + 'pref_1M' => 'One month', + 'pref_3M' => 'Three months (quarter)', + 'pref_6M' => 'Six months', + 'pref_1Y' => 'One year', + 'pref_languages' => 'Languages', + 'pref_languages_help' => 'Firefly III supports several languages. Which one do you prefer?', + 'pref_custom_fiscal_year' => 'Fiscal year settings', + 'pref_custom_fiscal_year_label' => 'Enabled', + 'pref_custom_fiscal_year_help' => 'In countries that use a financial year other than January 1 to December 31, you can switch this on and specify start / end days of the fiscal year', + 'pref_fiscal_year_start_label' => 'Fiscal year start date', + 'pref_two_factor_auth' => '2-step verification', + 'pref_two_factor_auth_help' => 'When you enable 2-step verification (also known as two-factor authentication), you add an extra layer of security to your account. You sign in with something you know (your password) and something you have (a verification code). Verification codes are generated by an application on your phone, such as Authy or Google Authenticator.', + 'pref_enable_two_factor_auth' => 'Enable 2-step verification', + 'pref_two_factor_auth_disabled' => '2-step verification code removed and disabled', + 'pref_two_factor_auth_remove_it' => 'Don\'t forget to remove the account from your authentication app!', + 'pref_two_factor_auth_code' => 'Verify code', + 'pref_two_factor_auth_code_help' => 'Scan the QR code with an application on your phone such as Authy or Google Authenticator and enter the generated code.', + 'pref_two_factor_auth_reset_code' => 'Reset verification code', + 'pref_two_factor_auth_remove_code' => 'Remove verification code', + 'pref_two_factor_auth_remove_will_disable' => '(this will also disable two-factor authentication)', + 'pref_save_settings' => 'Save settings', + 'saved_preferences' => 'Preferences saved!', + 'transaction_page_size_title' => 'Page size', + 'transaction_page_size_help' => 'Any list of transactions shows at most this many transactions', + 'transaction_page_size_label' => 'Page size', + 'budget_maximum' => 'Budget maximum', + 'between_dates' => '(:start and :end)', + + // profile: + 'change_your_password' => 'Change your password', + 'delete_account' => 'Delete account', + 'current_password' => 'Current password', + 'new_password' => 'New password', + 'new_password_again' => 'New password (again)', + 'delete_your_account' => 'Delete your account', + 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', + 'delete_your_account_password' => 'Enter your password to continue.', + 'password' => 'Password', + 'are_you_sure' => 'Are you sure? You cannot undo this.', + 'delete_account_button' => 'DELETE your account', + 'invalid_current_password' => 'Invalid current password!', + 'password_changed' => 'Password changed!', + 'should_change' => 'The idea is to change your password.', + 'invalid_password' => 'Invalid password!', + + + // attachments + 'nr_of_attachments' => 'One attachment|:count attachments', + 'attachments' => 'Attachments', + 'edit_attachment' => 'Edit attachment ":name"', + 'update_attachment' => 'Update attachment', + 'delete_attachment' => 'Delete attachment ":name"', + 'attachment_deleted' => 'Deleted attachment ":name"', + 'attachment_updated' => 'Updated attachment ":name"', + 'upload_max_file_size' => 'Maximum file size: :size', + + // tour: + 'prev' => 'Prev', + 'next' => 'Next', + 'end-tour' => 'End tour', + 'pause' => 'Pause', + + // transaction index + 'title_expenses' => 'Expenses', + 'title_withdrawal' => 'Expenses', + 'title_revenue' => 'Revenue / income', + 'title_deposit' => 'Revenue / income', + 'title_transfer' => 'Transfers', + 'title_transfers' => 'Transfers', + + // create new stuff: + 'create_new_withdrawal' => 'Create new withdrawal', + 'create_new_deposit' => 'Create new deposit', + 'create_new_transfer' => 'Create new transfer', + 'create_new_asset' => 'Create new asset account', + 'create_new_expense' => 'Create new expense account', + 'create_new_revenue' => 'Create new revenue account', + 'create_new_piggy_bank' => 'Create new piggy bank', + 'create_new_bill' => 'Create new bill', + + // currencies: + 'create_currency' => 'Create a new currency', + 'edit_currency' => 'Edit currency ":name"', + 'store_currency' => 'Store new currency', + 'update_currency' => 'Update currency', + 'new_default_currency' => ':name is now the default currency.', + 'cannot_delete_currency' => 'Cannot delete :name because there are still transactions attached to it!', + 'deleted_currency' => 'Currency :name deleted', + 'created_currency' => 'Currency :name created', + 'updated_currency' => 'Currency :name updated', + 'ask_site_owner' => 'Please ask :owner to add, remove or edit currencies.', + 'currencies_intro' => 'Firefly III supports various currencies which you can set and enable here.', + 'make_default_currency' => 'make default', + 'default_currency' => 'default', + + // new user: + 'submit' => 'Submit', + 'getting_started' => 'Getting started', + 'to_get_started' => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:', + 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:', + 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.', + 'stored_new_account_new_user' => 'Yay! Your new account has been stored.', + 'stored_new_accounts_new_user' => 'Yay! Your new accounts have been stored.', + + // forms: + 'mandatoryFields' => 'Mandatory fields', + 'optionalFields' => 'Optional fields', + 'options' => 'Options', + 'something' => 'Something!', + + // budgets: + 'create_new_budget' => 'Create a new budget', + 'store_new_budget' => 'Store new budget', + 'stored_new_budget' => 'Stored new budget ":name"', + 'availableIn' => 'Available in :date', + 'available_between' => 'Available between :start and :end', + 'transactionsWithoutBudget' => 'Expenses without budget', + 'transactionsWithoutBudgetDate' => 'Expenses without budget in :date', + 'transactions_no_budget' => 'Expenses without budget between :start and :end', + 'spent_between' => 'Spent between :start and :end', + 'createBudget' => 'New budget', + 'inactiveBudgets' => 'Inactive budgets', + 'without_budget_between' => 'Transactions without a budget between :start and :end', + 'budget_in_month' => ':name in :month', + 'delete_budget' => 'Delete budget ":name"', + 'deleted_budget' => 'Deleted budget ":name"', + 'edit_budget' => 'Edit budget ":name"', + 'updated_budget' => 'Updated budget ":name"', + 'update_amount' => 'Update amount', + 'update_budget' => 'Update budget', + 'update_budget_amount_range' => 'Update (expected) available amount between :start and :end', + + // bills: + 'matching_on' => 'Matching on', + 'between_amounts' => 'between :low and :high.', + 'repeats' => 'Repeats', + 'connected_journals' => 'Connected transactions', + 'auto_match_on' => 'Automatically matched by Firefly', + 'auto_match_off' => 'Not automatically matched by Firefly', + 'next_expected_match' => 'Next expected match', + 'delete_bill' => 'Delete bill ":name"', + 'deleted_bill' => 'Deleted bill ":name"', + 'edit_bill' => 'Edit bill ":name"', + 'more' => 'More', + 'rescan_old' => 'Rescan old transactions', + 'update_bill' => 'Update bill', + 'updated_bill' => 'Updated bill ":name"', + 'store_new_bill' => 'Store new bill', + 'stored_new_bill' => 'Stored new bill ":name"', + 'cannot_scan_inactive_bill' => 'Inactive bills cannot be scanned.', + 'rescanned_bill' => 'Rescanned everything.', + 'bill_date_little_relevance' => 'The only part of this date used by Firefly is the day. It is only useful when your bill arrives at exactly the same date every month. If the payment date of your bills varies, simply use the first of the month.', + 'average_bill_amount_year' => 'Average bill amount (:year)', + 'average_bill_amount_overall' => 'Average bill amount (overall)', + + // accounts: + 'details_for_asset' => 'Details for asset account ":name"', + 'details_for_expense' => 'Details for expense account ":name"', + 'details_for_revenue' => 'Details for revenue account ":name"', + 'details_for_cash' => 'Details for cash account ":name"', + 'store_new_asset_account' => 'Store new asset account', + 'store_new_expense_account' => 'Store new expense account', + 'store_new_revenue_account' => 'Store new revenue account', + 'edit_asset_account' => 'Edit asset account ":name"', + 'edit_expense_account' => 'Edit expense account ":name"', + 'edit_revenue_account' => 'Edit revenue account ":name"', + 'delete_asset_account' => 'Delete asset account ":name"', + 'delete_expense_account' => 'Delete expense account ":name"', + 'delete_revenue_account' => 'Delete revenue account ":name"', + 'asset_deleted' => 'Successfully deleted asset account ":name"', + 'expense_deleted' => 'Successfully deleted expense account ":name"', + 'revenue_deleted' => 'Successfully deleted revenue account ":name"', + 'update_asset_account' => 'Update asset account', + 'update_expense_account' => 'Update expense account', + 'update_revenue_account' => 'Update revenue account', + 'make_new_asset_account' => 'Create a new asset account', + 'make_new_expense_account' => 'Create a new expense account', + 'make_new_revenue_account' => 'Create a new revenue account', + 'asset_accounts' => 'Asset accounts', + 'expense_accounts' => 'Expense accounts', + 'revenue_accounts' => 'Revenue accounts', + 'cash_accounts' => 'Cash accounts', + 'Cash account' => 'Cash account', + 'accountExtraHelp_asset' => '', + 'accountExtraHelp_expense' => '', + 'accountExtraHelp_revenue' => '', + 'account_type' => 'Account type', + 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:', + 'stored_new_account' => 'New account ":name" stored!', + 'updated_account' => 'Updated account ":name"', + 'credit_card_options' => 'Credit card options', + + // categories: + 'new_category' => 'New category', + 'create_new_category' => 'Create a new category', + 'without_category' => 'Without a category', + 'update_category' => 'Update category', + 'updated_category' => 'Updated category ":name"', + 'categories' => 'Categories', + 'edit_category' => 'Edit category ":name"', + 'no_category' => '(no category)', + 'category' => 'Category', + 'delete_category' => 'Delete category ":name"', + 'deleted_category' => 'Deleted category ":name"', + 'store_category' => 'Store new category', + 'stored_category' => 'Stored new category ":name"', + 'without_category_between' => 'Without category between :start and :end', + + // transactions: + 'update_withdrawal' => 'Update withdrawal', + 'update_deposit' => 'Update deposit', + 'update_transfer' => 'Update transfer', + 'updated_withdrawal' => 'Updated withdrawal ":description"', + 'updated_deposit' => 'Updated deposit ":description"', + 'updated_transfer' => 'Updated transfer ":description"', + 'delete_withdrawal' => 'Delete withdrawal ":description"', + 'delete_deposit' => 'Delete deposit ":description"', + 'delete_transfer' => 'Delete transfer ":description"', + 'deleted_withdrawal' => 'Successfully deleted withdrawal ":description"', + 'deleted_deposit' => 'Successfully deleted deposit ":description"', + 'deleted_transfer' => 'Successfully deleted transfer ":description"', + 'stored_journal' => 'Successfully created new transaction ":description"', + 'select_transactions' => 'Select transactions', + 'stop_selection' => 'Stop selecting transactions', + 'edit_selected' => 'Edit selected', + 'delete_selected' => 'Delete selected', + 'mass_delete_journals' => 'Delete a number of transactions', + 'mass_edit_journals' => 'Edit a number of transactions', + 'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.', + 'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious.', + 'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).', + 'mass_edited_transactions_success' => 'Updated :amount transaction(s)', + + + // new user: + 'welcome' => 'Welcome to Firefly!', + 'createNewAsset' => 'Create a new asset account to get started. ' . + 'This will allow you to create transactions and start your financial management', + 'createNewAssetButton' => 'Create new asset account', + + // home page: + 'yourAccounts' => 'Your accounts', + 'budgetsAndSpending' => 'Budgets and spending', + 'savings' => 'Savings', + 'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel', + 'createPiggyToContinue' => 'Create piggy banks to fill this panel.', + 'newWithdrawal' => 'New expense', + 'newDeposit' => 'New deposit', + 'newTransfer' => 'New transfer', + 'moneyIn' => 'Money in', + 'moneyOut' => 'Money out', + 'billsToPay' => 'Bills to pay', + 'billsPaid' => 'Bills paid', + 'viewDetails' => 'View details', + 'divided' => 'divided', + 'toDivide' => 'left to divide', + + // menu and titles, should be recycled as often as possible: + 'toggleNavigation' => 'Toggle navigation', + 'currency' => 'Currency', + 'preferences' => 'Preferences', + 'logout' => 'Logout', + 'searchPlaceholder' => 'Search...', + 'dashboard' => 'Dashboard', + 'currencies' => 'Currencies', + 'accounts' => 'Accounts', + 'Asset account' => 'Asset account', + 'Default account' => 'Asset account', + 'Expense account' => 'Expense account', + 'Revenue account' => 'Revenue account', + 'Initial balance account' => 'Initial balance account', + 'budgets' => 'Budgets', + 'tags' => 'Tags', + 'reports' => 'Reports', + 'transactions' => 'Transactions', + 'expenses' => 'Expenses', + 'income' => 'Revenue / income', + 'transfers' => 'Transfers', + 'moneyManagement' => 'Money management', + 'piggyBanks' => 'Piggy banks', + 'bills' => 'Bills', + 'createNew' => 'Create new', + 'withdrawal' => 'Withdrawal', + 'deposit' => 'Deposit', + 'account' => 'Account', + 'transfer' => 'Transfer', + 'Withdrawal' => 'Withdrawal', + 'Deposit' => 'Deposit', + 'Transfer' => 'Transfer', + 'bill' => 'Bill', + 'yes' => 'Yes', + 'no' => 'No', + 'amount' => 'Amount', + 'newBalance' => 'New balance', + 'overview' => 'Overview', + 'saveOnAccount' => 'Save on account', + 'unknown' => 'Unknown', + 'daily' => 'Daily', + 'monthly' => 'Monthly', + 'profile' => 'Profile', + 'errors' => 'Errors', + + // reports: + 'report_default' => 'Default financial report for :start until :end', + 'report_audit' => 'Transaction history overview for :start until :end', + 'quick_link_reports' => 'Quick links', + 'quick_link_default_report' => 'Default financial report', + 'quick_link_audit_report' => 'Transaction history overview', + 'report_this_month_quick' => 'Current month, all accounts', + 'report_this_year_quick' => 'Current year, all accounts', + 'report_this_fiscal_year_quick' => 'Current fiscal year, all accounts', + 'report_all_time_quick' => 'All-time, all accounts', + 'reports_can_bookmark' => 'Remember that reports can be bookmarked.', + 'incomeVsExpenses' => 'Income vs. expenses', + 'accountBalances' => 'Account balances', + 'balanceStartOfYear' => 'Balance at start of year', + 'balanceEndOfYear' => 'Balance at end of year', + 'balanceStartOfMonth' => 'Balance at start of month', + 'balanceEndOfMonth' => 'Balance at end of month', + 'balanceStart' => 'Balance at start of period', + 'balanceEnd' => 'Balance at end of period', + 'reportsOwnAccounts' => 'Reports for your own accounts', + 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', + 'splitByAccount' => 'Split by account', + 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', + 'coveredWithTags' => 'Covered with tags', + 'leftUnbalanced' => 'Left unbalanced', + 'expectedBalance' => 'Expected balance', + 'outsideOfBudgets' => 'Outside of budgets', + 'leftInBudget' => 'Left in budget', + 'sumOfSums' => 'Sum of sums', + 'noCategory' => '(no category)', + 'notCharged' => 'Not charged (yet)', + 'inactive' => 'Inactive', + 'active' => 'Active', + 'difference' => 'Difference', + 'in' => 'In', + 'out' => 'Out', + 'topX' => 'top :number', + 'showTheRest' => 'Show everything', + 'hideTheRest' => 'Show only the top :number', + 'sum_of_year' => 'Sum of year', + 'sum_of_years' => 'Sum of years', + 'average_of_year' => 'Average of year', + 'average_of_years' => 'Average of years', + 'categories_earned_in_year' => 'Categories (by earnings)', + 'categories_spent_in_year' => 'Categories (by spendings)', + 'report_type' => 'Report type', + 'report_type_default' => 'Default financial report', + 'report_type_audit' => 'Transaction history overview (audit)', + 'report_type_meta-history' => 'Categories, budgets and bills overview', + 'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.', + 'report_included_accounts' => 'Included accounts', + 'report_date_range' => 'Date range', + 'report_include_help' => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.', + 'report_preset_ranges' => 'Pre-set ranges', + 'shared' => 'Shared', + 'fiscal_year' => 'Fiscal year', + 'income_entry' => 'Income from account ":name" between :start and :end', + 'expense_entry' => 'Expenses to account ":name" between :start and :end', + 'category_entry' => 'Expenses in category ":name" between :start and :end', + 'budget_spent_amount' => 'Expenses in budget ":budget" between :start and :end', + 'balance_amount' => 'Expenses in budget ":budget" paid from account ":account" between :start and :end', + 'no_audit_activity' => 'No activity was recorded on account :account_name between :start and :end.', + 'audit_end_balance' => 'Account balance of :account_name at the end of :end was: :balance', + + // charts: + 'chart' => 'Chart', + 'dayOfMonth' => 'Day of the month', + 'month' => 'Month', + 'budget' => 'Budget', + 'spent' => 'Spent', + 'earned' => 'Earned', + 'overspent' => 'Overspent', + 'left' => 'Left', + 'no_budget' => '(no budget)', + 'maxAmount' => 'Maximum amount', + 'minAmount' => 'Minumum amount', + 'billEntry' => 'Current bill entry', + 'name' => 'Name', + 'date' => 'Date', + 'paid' => 'Paid', + 'unpaid' => 'Unpaid', + 'day' => 'Day', + 'budgeted' => 'Budgeted', + 'period' => 'Period', + 'balance' => 'Balance', + 'summary' => 'Summary', + 'sum' => 'Sum', + 'average' => 'Average', + 'balanceFor' => 'Balance for :name', + + // piggy banks: + 'add_money_to_piggy' => 'Add money to piggy bank ":name"', + 'piggy_bank' => 'Piggy bank', + 'new_piggy_bank' => 'Create new piggy bank', + 'store_piggy_bank' => 'Store new piggy bank', + 'stored_piggy_bank' => 'Store new piggy bank ":name"', + 'account_status' => 'Account status', + 'left_for_piggy_banks' => 'Left for piggy banks', + 'sum_of_piggy_banks' => 'Sum of piggy banks', + 'saved_so_far' => 'Saved so far', + 'left_to_save' => 'Left to save', + 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', + 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', + 'add' => 'Add', + 'remove' => 'Remove', + 'max_amount_add' => 'The maximum amount you can add is', + 'max_amount_remove' => 'The maximum amount you can remove is', + 'update_piggy_button' => 'Update piggy bank', + 'update_piggy_title' => 'Update piggy bank ":name"', + 'updated_piggy_bank' => 'Updated piggy bank ":name"', + 'details' => 'Details', + 'events' => 'Events', + 'target_amount' => 'Target amount', + 'start_date' => 'Start date', + 'target_date' => 'Target date', + 'no_target_date' => 'No target date', + 'todo' => 'to do', + 'table' => 'Table', + 'piggy_bank_not_exists' => 'Piggy bank no longer exists.', + 'add_any_amount_to_piggy' => 'Add money to this piggy bank to reach your target of :amount.', + 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date', + 'delete_piggy_bank' => 'Delete piggy bank ":name"', + 'cannot_add_amount_piggy' => 'Could not add :amount to ":name".', + 'deleted_piggy_bank' => 'Deleted piggy bank ":name"', + 'added_amount_to_piggy' => 'Added :amount to ":name"', + 'removed_amount_from_piggy' => 'Removed :amount from ":name"', + 'cannot_remove_amount_piggy' => 'Could not remove :amount from ":name".', + + // tags + 'regular_tag' => 'Just a regular tag.', + 'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.', + 'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.', + 'delete_tag' => 'Delete tag ":tag"', + 'deleted_tag' => 'Deleted tag ":tag"', + 'new_tag' => 'Make new tag', + 'edit_tag' => 'Edit tag ":tag"', + 'updated_tag' => 'Updated tag ":tag"', + 'created_tag' => 'Tag ":tag" has been created!', + 'no_year' => 'No year set', + 'no_month' => 'No month set', + 'tag_title_nothing' => 'Default tags', + 'tag_title_balancingAct' => 'Balancing act tags', + 'tag_title_advancePayment' => 'Advance payment tags', + 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like expensive, bill or for-party. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called Christmas dinner with friends and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.', + 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.', + 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', + + + // administration + 'administration' => 'Administration', + 'user_administration' => 'User administration', + 'list_all_users' => 'All users', + 'all_users' => 'All users', + 'all_blocked_domains' => 'All blocked domains', + 'blocked_domains' => 'Blocked domains', + 'no_domains_banned' => 'No domains blocked', + 'all_user_domains' => 'All user email address domains', + 'all_domains_is_filtered' => 'This list does not include already blocked domains.', + 'domain_now_blocked' => 'Domain :domain is now blocked', + 'domain_now_unblocked' => 'Domain :domain is now unblocked', + 'manual_block_domain' => 'Block a domain by hand', + 'block_domain' => 'Block domain', + 'no_domain_filled_in' => 'No domain filled in', + 'domain_already_blocked' => 'Domain :domain is already blocked', + 'domain_is_now_blocked' => 'Domain :domain is now blocked', + + // split a transaction: + 'transaction_meta_data' => 'Transaction meta-data', + 'transaction_dates' => 'Transaction dates', + 'splits' => 'Splits', + 'split_title_withdrawal' => 'Split your new withdrawal', + 'split_intro_one_withdrawal' => 'Firefly supports the "splitting" of a withdrawal.', + 'split_intro_two_withdrawal' => 'It means that the amount of money you\'ve spent is divided between several destination expense accounts, budgets or categories.', + 'split_intro_three_withdrawal' => 'For example: you could split your :total groceries so you pay :split_one from your "daily groceries" budget and :split_two from your "cigarettes" budget.', + 'split_table_intro_withdrawal' => 'Split your withdrawal in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_withdrawal' => 'Store splitted withdrawal', + 'update_splitted_withdrawal' => 'Update splitted withdrawal', + 'split_title_deposit' => 'Split your new deposit', + 'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.', + 'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.', + 'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.', + 'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_deposit' => 'Store splitted deposit', + 'split_title_transfer' => 'Split your new transfer', + 'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.', + 'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.', + 'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.', + 'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_transfer' => 'Store splitted transfer', + 'add_another_split' => 'Add another split', + 'split-transactions' => 'Split transactions', + 'split-new-transaction' => 'Split a new transaction', + 'do_split' => 'Do a split', + 'split_this_withdrawal' => 'Split this withdrawal', + 'split_this_deposit' => 'Split this deposit', + 'split_this_transfer' => 'Split this transfer', + 'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.', + 'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.', + 'no_edit_multiple_left' => 'You have selected no valid transactions to edit.', + + // import + 'configuration_file_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you.', + 'import_data_index' => 'Index', + 'import_file_type_csv' => 'CSV (comma separated values)', + 'import_file_type_help' => 'Select the type of file you will upload', + 'import_start' => 'Start the import', + 'configure_import' => 'Further configure your import', + 'import_finish_configuration' => 'Finish configuration', + 'settings_for_import' => 'Settings', + 'import_complete' => 'Import configuration complete!', + 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you need to execute the following command in your console. Unfortunately, a web-based import is not yet possible.', + 'import_download_config' => 'Download configuration', + 'import_start_import' => 'Start import', + 'import_intro_beta' => 'The import function of Firefly III is in beta. Many users of Firefly III have tried many different files. Although each individual compontent of this import routine works (really), the combination might break. If your file cannot be imported by Firefly, please read this wiki page so I can fix the problem you have run into.', + 'import_data' => 'Import data', + 'import_data_full' => 'Import data into Firefly III', + 'import' => 'Import', + 'import_intro_text' => 'Welcome to the Firefly III data import routine. At the moment, this routine can help you import files into Firefly. To do so, you must download or export transactions from other systems or software, and upload them here. The next steps will let you help Firefly III determin what the content is of your file, and how to handle it. Please select a file, and read all instructions carefully.', + 'import_file_help' => 'Select your file', +]; diff --git a/resources/lang/zh-HK/form.php b/resources/lang/zh-HK/form.php new file mode 100644 index 0000000000..fa1f42a199 --- /dev/null +++ b/resources/lang/zh-HK/form.php @@ -0,0 +1,149 @@ + 'Bank name', + 'bank_balance' => 'Balance', + 'savings_balance' => 'Savings balance', + 'credit_card_limit' => 'Credit card limit', + 'automatch' => 'Match automatically', + 'skip' => 'Skip', + 'name' => 'Name', + 'active' => 'Active', + 'amount_min' => 'Minimum amount', + 'amount_max' => 'Maximum amount', + 'match' => 'Matches on', + 'repeat_freq' => 'Repeats', + 'journal_currency_id' => 'Currency', + 'journal_amount' => 'Amount', + 'journal_asset_source_account' => 'Asset account (source)', + 'journal_source_account_name' => 'Revenue account (source)', + 'journal_source_account_id' => 'Asset account (source)', + 'account_from_id' => 'From account', + 'account_to_id' => 'To account', + 'journal_destination_account_id' => 'Asset account (destination)', + 'asset_destination_account' => 'Asset account (destination)', + 'asset_source_account' => 'Asset account (source)', + 'journal_description' => 'Description', + 'split_journal' => 'Split this transaction', + 'split_journal_explanation' => 'Split this transaction in multiple parts', + 'currency' => 'Currency', + 'account_id' => 'Asset account', + 'budget_id' => 'Budget', + 'openingBalance' => 'Opening balance', + 'tagMode' => 'Tag mode', + 'tagPosition' => 'Tag location', + 'virtualBalance' => 'Virtual balance', + 'longitude_latitude' => 'Location', + 'targetamount' => 'Target amount', + 'accountRole' => 'Account role', + 'openingBalanceDate' => 'Opening balance date', + 'ccType' => 'Credit card payment plan', + 'ccMonthlyPaymentDate' => 'Credit card monthly payment date', + 'piggy_bank_id' => 'Piggy bank', + 'returnHere' => 'Return here', + 'returnHereExplanation' => 'After storing, return here to create another one.', + 'returnHereUpdateExplanation' => 'After updating, return here.', + 'description' => 'Description', + 'expense_account' => 'Expense account', + 'revenue_account' => 'Revenue account', + 'amount' => 'Amount', + 'date' => 'Date', + 'interest_date' => 'Interest date', + 'book_date' => 'Book date', + 'process_date' => 'Processing date', + 'category' => 'Category', + 'tags' => 'Tags', + 'deletePermanently' => 'Delete permanently', + 'cancel' => 'Cancel', + 'targetdate' => 'Target date', + 'tag' => 'Tag', + 'under' => 'Under', + 'symbol' => 'Symbol', + 'code' => 'Code', + 'iban' => 'IBAN', + 'accountNumber' => 'Account number', + 'has_headers' => 'Headers', + 'date_format' => 'Date format', + 'specifix' => 'Bank- or file specific fixes', + 'attachments[]' => 'Attachments', + 'store_new_withdrawal' => 'Store new withdrawal', + 'store_new_deposit' => 'Store new deposit', + 'store_new_transfer' => 'Store new transfer', + 'add_new_withdrawal' => 'Add a new withdrawal', + 'add_new_deposit' => 'Add a new deposit', + 'add_new_transfer' => 'Add a new transfer', + 'noPiggybank' => '(no piggy bank)', + 'title' => 'Title', + 'notes' => 'Notes', + 'filename' => 'File name', + 'mime' => 'Mime type', + 'size' => 'Size', + 'trigger' => 'Trigger', + 'stop_processing' => 'Stop processing', + 'start_date' => 'Start of range', + 'end_date' => 'End of range', + 'export_start_range' => 'Start of export range', + 'export_end_range' => 'End of export range', + 'export_format' => 'File format', + 'include_attachments' => 'Include uploaded attachments', + 'include_config' => 'Include configuration file', + 'include_old_uploads' => 'Include imported data', + 'accounts' => 'Export transactions from these accounts', + 'delete_account' => 'Delete account ":name"', + 'delete_bill' => 'Delete bill ":name"', + 'delete_budget' => 'Delete budget ":name"', + 'delete_category' => 'Delete category ":name"', + 'delete_currency' => 'Delete currency ":name"', + 'delete_journal' => 'Delete transaction with description ":description"', + 'delete_attachment' => 'Delete attachment ":name"', + 'delete_rule' => 'Delete rule ":title"', + 'delete_rule_group' => 'Delete rule group ":title"', + 'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?', + 'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?', + 'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?', + 'rule_areYouSure' => 'Are you sure you want to delete the rule titled ":title"?', + 'ruleGroup_areYouSure' => 'Are you sure you want to delete the rule group titled ":title"?', + 'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?', + 'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?', + 'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?', + 'piggyBank_areYouSure' => 'Are you sure you want to delete the piggy bank named ":name"?', + 'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?', + 'mass_journal_are_you_sure' => 'Are you sure you want to delete these transactions?', + 'tag_areYouSure' => 'Are you sure you want to delete the tag ":tag"?', + 'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.', + 'mass_make_selection' => 'You can still prevent items from being deleted by removing the checkbox.', + 'delete_all_permanently' => 'Delete selected permanently', + 'update_all_journals' => 'Update these transactions', + 'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.', + 'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.', + 'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.', + 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.', + 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.', + + // admin + 'domain' => 'Domain', + + // import + 'import_file' => 'Import file', + 'configuration_file' => 'Configuration file', + 'import_file_type' => 'Import file type', + 'csv_comma' => 'A comma (,)', + 'csv_semicolon' => 'A semicolon (;)', + 'csv_tab' => 'A tab (invisible)', + 'csv_delimiter' => 'CSV field delimiter', + 'csv_import_account' => 'Default import account', + 'csv_config' => 'CSV import configuration', + + +]; diff --git a/resources/lang/zh-HK/help.php b/resources/lang/zh-HK/help.php new file mode 100644 index 0000000000..d1fa572177 --- /dev/null +++ b/resources/lang/zh-HK/help.php @@ -0,0 +1,85 @@ + 'Welcome to Firefly III', + 'main-content-text' => 'Do yourself a favor and follow this short guide to make sure you know your way around.', + 'sidebar-toggle-title' => 'Sidebar to create stuff', + 'sidebar-toggle-text' => 'Hidden under the plus icon are all the buttons to create new stuff. Accounts, transactions, everything!', + 'account-menu-title' => 'All your accounts', + 'account-menu-text' => 'Here you can find all the accounts you\'ve made.', + 'budget-menu-title' => 'Budgets', + 'budget-menu-text' => 'Use this page to organise your finances and limit spending.', + 'report-menu-title' => 'Reports', + 'report-menu-text' => 'Check this out when you want a solid overview of your finances.', + 'transaction-menu-title' => 'Transactions', + 'transaction-menu-text' => 'All transactions you\'ve created can be found here.', + 'option-menu-title' => 'Options', + 'option-menu-text' => 'This is pretty self-explanatory.', + 'main-content-end-title' => 'The end!', + 'main-content-end-text' => 'Remember that every page has a small question mark at the right top. Click it to get help about the page you\'re on.', + 'index' => 'index', + 'home' => 'home', + 'accounts-index' => 'accounts.index', + 'accounts-create' => 'accounts.create', + 'accounts-edit' => 'accounts.edit', + 'accounts-delete' => 'accounts.delete', + 'accounts-show' => 'accounts.show', + 'attachments-edit' => 'attachments.edit', + 'attachments-delete' => 'attachments.delete', + 'attachments-show' => 'attachments.show', + 'attachments-preview' => 'attachments.preview', + 'bills-index' => 'bills.index', + 'bills-create' => 'bills.create', + 'bills-edit' => 'bills.edit', + 'bills-delete' => 'bills.delete', + 'bills-show' => 'bills.show', + 'budgets-index' => 'budgets.index', + 'budgets-create' => 'budgets.create', + 'budgets-edit' => 'budgets.edit', + 'budgets-delete' => 'budgets.delete', + 'budgets-show' => 'budgets.show', + 'budgets-noBudget' => 'budgets.noBudget', + 'categories-index' => 'categories.index', + 'categories-create' => 'categories.create', + 'categories-edit' => 'categories.edit', + 'categories-delete' => 'categories.delete', + 'categories-show' => 'categories.show', + 'categories-show-date' => 'categories.show.date', + 'categories-noCategory' => 'categories.noCategory', + 'currency-index' => 'currency.index', + 'currency-create' => 'currency.create', + 'currency-edit' => 'currency.edit', + 'currency-delete' => 'currency.delete', + 'new-user-index' => 'new-user.index', + 'piggy-banks-index' => 'piggy-banks.index', + 'piggy-banks-create' => 'piggy-banks.create', + 'piggy-banks-edit' => 'piggy-banks.edit', + 'piggy-banks-delete' => 'piggy-banks.delete', + 'piggy-banks-show' => 'piggy-banks.show', + 'preferences' => 'preferences', + 'profile' => 'profile', + 'profile-change-password' => 'profile.change-password', + 'profile-delete-account' => 'profile.delete-account', + 'reports-index' => 'reports.index', + 'reports-report' => 'reports.report', + 'search' => 'search', + 'tags-index' => 'tags.index', + 'tags-create' => 'tags.create', + 'tags-show' => 'tags.show', + 'tags-edit' => 'tags.edit', + 'tags-delete' => 'tags.delete', + 'transactions-index' => 'transactions.index', + 'transactions-create' => 'transactions.create', + 'transactions-edit' => 'transactions.edit', + 'transactions-delete' => 'transactions.delete', + 'transactions-show' => 'transactions.show', +]; diff --git a/resources/lang/zh-HK/list.php b/resources/lang/zh-HK/list.php new file mode 100644 index 0000000000..193740c717 --- /dev/null +++ b/resources/lang/zh-HK/list.php @@ -0,0 +1,66 @@ + 'Buttons', + 'icon' => 'Icon', + 'create_date' => 'Created at', + 'update_date' => 'Updated at', + 'balance_before' => 'Balance before', + 'balance_after' => 'Balance after', + 'name' => 'Name', + 'role' => 'Role', + 'currentBalance' => 'Current balance', + 'active' => 'Is active?', + 'lastActivity' => 'Last activity', + 'balanceDiff' => 'Balance difference between :start and :end', + 'matchedOn' => 'Matched on', + 'matchesOn' => 'Matched on', + 'account_type' => 'Account type', + 'new_balance' => 'New balance', + 'account' => 'Account', + 'matchingAmount' => 'Amount', + 'lastMatch' => 'Last match', + 'split_number' => 'Split #', + 'destination' => 'Destination', + 'source' => 'Source', + 'expectedMatch' => 'Expected match', + 'automatch' => 'Auto match?', + 'repeat_freq' => 'Repeats', + 'description' => 'Description', + 'amount' => 'Amount', + 'date' => 'Date', + 'interest_date' => 'Interest date', + 'book_date' => 'Book date', + 'process_date' => 'Processing date', + 'from' => 'From', + 'piggy_bank' => 'Piggy bank', + 'to' => 'To', + 'budget' => 'Budget', + 'category' => 'Category', + 'bill' => 'Bill', + 'withdrawal' => 'Withdrawal', + 'deposit' => 'Deposit', + 'transfer' => 'Transfer', + 'type' => 'Type', + 'completed' => 'Completed', + 'iban' => 'IBAN', + 'paid_current_period' => 'Paid this period', + 'email' => 'Email', + 'registered_at' => 'Registered at', + 'is_activated' => 'Is activated', + 'is_blocked' => 'Is blocked', + 'is_admin' => 'Is admin', + 'has_two_factor' => 'Has 2FA', + 'confirmed_from' => 'Confirmed from', + 'registered_from' => 'Registered from', + 'blocked_code' => 'Block code', + 'domain' => 'Domain', + 'registration_attempts' => 'Registration attempts', +]; diff --git a/resources/lang/zh-HK/pagination.php b/resources/lang/zh-HK/pagination.php new file mode 100644 index 0000000000..b1cf416ae8 --- /dev/null +++ b/resources/lang/zh-HK/pagination.php @@ -0,0 +1,13 @@ + '« Previous', + 'next' => 'Next »', +]; diff --git a/resources/lang/zh-HK/passwords.php b/resources/lang/zh-HK/passwords.php new file mode 100644 index 0000000000..6c382f830f --- /dev/null +++ b/resources/lang/zh-HK/passwords.php @@ -0,0 +1,17 @@ + 'Passwords must be at least six characters and match the confirmation.', + 'user' => 'We can\'t find a user with that e-mail address.', + 'token' => 'This password reset token is invalid.', + 'sent' => 'We have e-mailed your password reset link!', + 'reset' => 'Your password has been reset!', + 'blocked' => 'Nice try though.', +]; diff --git a/resources/lang/zh-HK/validation.php b/resources/lang/zh-HK/validation.php new file mode 100644 index 0000000000..fa086c9fbb --- /dev/null +++ b/resources/lang/zh-HK/validation.php @@ -0,0 +1,80 @@ + 'This is not a valid IBAN.', + 'unique_account_number_for_user' => 'It looks like this account number is already in use.', + 'rule_trigger_value' => 'This value is invalid for the selected trigger.', + 'rule_action_value' => 'This value is invalid for the selected action.', + 'invalid_domain' => 'Due to security constraints, you cannot register from this domain.', + 'file_already_attached' => 'Uploaded file ":name" is already attached to this object.', + 'file_attached' => 'Succesfully uploaded file ":name".', + 'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.', + 'file_too_large' => 'File ":name" is too large.', + 'belongs_to_user' => 'The value of :attribute is unknown', + 'accepted' => 'The :attribute must be accepted.', + 'active_url' => 'The :attribute is not a valid URL.', + 'after' => 'The :attribute must be a date after :date.', + 'alpha' => 'The :attribute may only contain letters.', + 'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.', + 'alpha_num' => 'The :attribute may only contain letters and numbers.', + 'array' => 'The :attribute must be an array.', + 'unique_for_user' => 'There already is an entry with this :attribute.', + 'before' => 'The :attribute must be a date before :date.', + 'unique_object_for_user' => 'This name is already in use', + 'unique_account_for_user' => 'This account name is already in use', + 'between.numeric' => 'The :attribute must be between :min and :max.', + 'between.file' => 'The :attribute must be between :min and :max kilobytes.', + 'between.string' => 'The :attribute must be between :min and :max characters.', + 'between.array' => 'The :attribute must have between :min and :max items.', + 'boolean' => 'The :attribute field must be true or false.', + 'confirmed' => 'The :attribute confirmation does not match.', + 'date' => 'The :attribute is not a valid date.', + 'date_format' => 'The :attribute does not match the format :format.', + 'different' => 'The :attribute and :other must be different.', + 'digits' => 'The :attribute must be :digits digits.', + 'digits_between' => 'The :attribute must be between :min and :max digits.', + 'email' => 'The :attribute must be a valid email address.', + 'filled' => 'The :attribute field is required.', + 'exists' => 'The selected :attribute is invalid.', + 'image' => 'The :attribute must be an image.', + 'in' => 'The selected :attribute is invalid.', + 'integer' => 'The :attribute must be an integer.', + 'ip' => 'The :attribute must be a valid IP address.', + 'json' => 'The :attribute must be a valid JSON string.', + 'max.numeric' => 'The :attribute may not be greater than :max.', + 'max.file' => 'The :attribute may not be greater than :max kilobytes.', + 'max.string' => 'The :attribute may not be greater than :max characters.', + 'max.array' => 'The :attribute may not have more than :max items.', + 'mimes' => 'The :attribute must be a file of type: :values.', + 'min.numeric' => 'The :attribute must be at least :min.', + 'min.file' => 'The :attribute must be at least :min kilobytes.', + 'min.string' => 'The :attribute must be at least :min characters.', + 'min.array' => 'The :attribute must have at least :min items.', + 'not_in' => 'The selected :attribute is invalid.', + 'numeric' => 'The :attribute must be a number.', + 'regex' => 'The :attribute format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_unless' => 'The :attribute field is required unless :other is in :values.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values is present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute and :other must match.', + 'size.numeric' => 'The :attribute must be :size.', + 'size.file' => 'The :attribute must be :size kilobytes.', + 'size.string' => 'The :attribute must be :size characters.', + 'size.array' => 'The :attribute must contain :size items.', + 'unique' => 'The :attribute has already been taken.', + 'string' => 'The :attribute must be a string.', + 'url' => 'The :attribute format is invalid.', + 'timezone' => 'The :attribute must be a valid zone.', + '2fa_code' => 'The :attribute field is invalid.', +]; diff --git a/resources/lang/zh-TW/breadcrumbs.php b/resources/lang/zh-TW/breadcrumbs.php new file mode 100644 index 0000000000..bdd804cf63 --- /dev/null +++ b/resources/lang/zh-TW/breadcrumbs.php @@ -0,0 +1,46 @@ + 'Home', + 'cash_accounts' => 'Cash accounts', + 'edit_account' => 'Edit account ":name"', + 'edit_currency' => 'Edit currencies ":name"', + 'delete_currency' => 'Delete currencies ":name"', + 'newPiggyBank' => 'Create a new piggy bank', + 'edit_piggyBank' => 'Edit piggy bank ":name"', + 'preferences' => 'Preferences', + 'profile' => 'Profile', + 'changePassword' => 'Change your password', + 'bills' => 'Bills', + 'newBill' => 'New bill', + 'edit_bill' => 'Edit bill ":name"', + 'delete_bill' => 'Delete bill ":name"', + 'reports' => 'Reports', + 'monthly_report' => 'Monthly report for :date', + 'monthly_report_shared' => 'Monthly report for :date (including shared accounts)', + 'yearly_report' => 'Yearly report for :date', + 'yearly_report_shared' => 'Yearly report for :date (including shared accounts)', + 'budget_report' => 'Budget report for :date', + 'searchResult' => 'Search for ":query"', + 'withdrawal_list' => 'Expenses', + 'deposit_list' => 'Revenue, income and deposits', + 'transfer_list' => 'Transfers', + 'transfers_list' => 'Transfers', + 'create_withdrawal' => 'Create new withdrawal', + 'create_deposit' => 'Create new deposit', + 'create_transfer' => 'Create new transfer', + 'edit_journal' => 'Edit transaction ":description"', + 'delete_journal' => 'Delete transaction ":description"', + 'tags' => 'Tags', + 'createTag' => 'Create new tag', + 'edit_tag' => 'Edit tag ":tag"', + 'delete_tag' => 'Delete tag ":tag"', +]; diff --git a/resources/lang/zh-TW/config.php b/resources/lang/zh-TW/config.php new file mode 100644 index 0000000000..b8ed029a46 --- /dev/null +++ b/resources/lang/zh-TW/config.php @@ -0,0 +1,21 @@ + 'en, English, en_US, en_US.utf8', + 'month' => '%B %Y', + 'month_and_day' => '%B %e, %Y', + 'date_time' => '%B %e, %Y, @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'Week %W, %Y', + 'quarter_of_year' => '%B %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + +]; diff --git a/resources/lang/zh-TW/csv.php b/resources/lang/zh-TW/csv.php new file mode 100644 index 0000000000..858026dad6 --- /dev/null +++ b/resources/lang/zh-TW/csv.php @@ -0,0 +1,80 @@ + 'Configure your import', + 'import_configure_intro' => 'There are some options for your CSV import. Please indicate if your CSV file contains headers on the first column, and what the date format of your date-fields is. That might require some experimentation. The field delimiter is usually a ",", but could also be a ";". Check this carefully.', + 'import_configure_form' => 'Form', + 'header_help' => 'Check this if the first row of your CSV file are the column titles', + 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', + 'import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', + + // roles + 'column_roles_title' => 'Define column roles', + 'column_roles_text' => '

    Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

    Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

    ', + 'column_roles_table' => 'Table', + 'column_name' => 'Name of column', + 'column_example' => 'Column example data', + 'column_role' => 'Column data meaning', + 'do_map_value' => 'Map these values', + 'column' => 'Column', + 'no_example_data' => 'No example data available', + 'store_column_roles' => 'Continue import', + 'do_not_map' => '(do not map)', + 'map_title' => 'Connect import data to Firefly III data', + 'map_text' => 'In the following tables, the left value shows you information found in your uploaded CSV file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', + + 'field_value' => 'Field value', + 'field_mapped_to' => 'Mapped to', + 'store_column_mapping' => 'Store mapping', + + // map things. + + + 'column__ignore' => '(ignore this column)', + 'column_account-iban' => 'Asset account (IBAN)', + 'column_account-id' => 'Asset account ID (matching Firefly)', + 'column_account-name' => 'Asset account (name)', + 'column_amount' => 'Amount', + 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', + 'column_bill-id' => 'Bill ID (matching Firefly)', + 'column_bill-name' => 'Bill name', + 'column_budget-id' => 'Budget ID (matching Firefly)', + 'column_budget-name' => 'Budget name', + 'column_category-id' => 'Category ID (matching Firefly)', + 'column_category-name' => 'Category name', + 'column_currency-code' => 'Currency code (ISO 4217)', + 'column_currency-id' => 'Currency ID (matching Firefly)', + 'column_currency-name' => 'Currency name (matching Firefly)', + 'column_currency-symbol' => 'Currency symbol (matching Firefly)', + 'column_date-interest' => 'Interest calculation date', + 'column_date-book' => 'Transaction booking date', + 'column_date-process' => 'Transaction process date', + 'column_date-transaction' => 'Date', + 'column_description' => 'Description', + 'column_opposing-iban' => 'Opposing account (IBAN)', + 'column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'column_external-id' => 'External ID', + 'column_opposing-name' => 'Opposing account (name)', + 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'column_ing-debet-credit' => 'ING specific debet/credit indicator', + 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'column_sepa-db' => 'SEPA Direct Debet', + 'column_tags-comma' => 'Tags (comma separated)', + 'column_tags-space' => 'Tags (space separated)', + 'column_account-number' => 'Asset account (account number)', + 'column_opposing-number' => 'Opposing account (account number)', +]; \ No newline at end of file diff --git a/resources/lang/zh-TW/firefly.php b/resources/lang/zh-TW/firefly.php new file mode 100644 index 0000000000..08238377e7 --- /dev/null +++ b/resources/lang/zh-TW/firefly.php @@ -0,0 +1,780 @@ + 'This language is not yet fully translated', + 'test' => 'You have selected English.', + 'close' => 'Close', + 'pleaseHold' => 'Please hold...', + 'actions' => 'Actions', + 'edit' => 'Edit', + 'delete' => 'Delete', + 'welcomeBack' => 'What\'s playing?', + 'everything' => 'Everything', + 'customRange' => 'Custom range', + 'apply' => 'Apply', + 'cancel' => 'Cancel', + 'from' => 'From', + 'to' => 'To', + 'total_sum' => 'Total sum', + 'period_sum' => 'Sum for period', + 'showEverything' => 'Show everything', + 'never' => 'Never', + 'search_results_for' => 'Search results for ":query"', + 'bounced_error' => 'The message sent to :email bounced, so no access for you.', + 'deleted_error' => 'These credentials do not match our records.', + 'general_blocked_error' => 'Your account has been disabled, so you cannot login.', + 'expired_error' => 'Your account has expired, and can no longer be used.', + 'unbalanced_error' => 'Your transactions are unbalanced. This means a withdrawal, deposit or transfer was not stored properly. Please check your accounts and transactions for errors (unbalanced amount :amount).', + 'removed_amount' => 'Removed :amount', + 'added_amount' => 'Added :amount', + 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', + 'Opening balance' => 'Opening balance', + 'create_new_stuff' => 'Create new stuff', + 'new_withdrawal' => 'New withdrawal', + 'new_deposit' => 'New deposit', + 'new_transfer' => 'New transfer', + 'new_asset_account' => 'New asset account', + 'new_expense_account' => 'New expense account', + 'new_revenue_account' => 'New revenue account', + 'new_budget' => 'New budget', + 'new_bill' => 'New bill', + 'block_account_logout' => 'You have been logged out. Blocked accounts cannot use this site. Did you register with a valid email address?', + 'flash_success' => 'Success!', + 'flash_info' => 'Message', + 'flash_warning' => 'Warning!', + 'flash_error' => 'Error!', + 'flash_info_multiple' => 'There is one message|There are :count messages', + 'flash_error_multiple' => 'There is one error|There are :count errors', + 'net_worth' => 'Net worth', + 'route_has_no_help' => 'There is no help for this route, or there is no help available in your language.', + 'two_factor_welcome' => 'Hello, :user!', + 'two_factor_enter_code' => 'To continue, please enter your two factor authentication code. Your application can generate it for you.', + 'two_factor_code_here' => 'Enter code here', + 'two_factor_title' => 'Two factor authentication', + 'authenticate' => 'Authenticate', + 'two_factor_forgot_title' => 'Lost two factor authentication', + 'two_factor_forgot' => 'I forgot my two-factor thing.', + 'two_factor_lost_header' => 'Lost your two factor authentication?', + 'two_factor_lost_intro' => 'Unfortunately, this is not something you can reset from the web interface. You have two choices.', + 'two_factor_lost_fix_self' => 'If you run your own instance of Firefly III, check the logs in storage/logs for instructions.', + 'two_factor_lost_fix_owner' => 'Otherwise, email the site owner, :site_owner and ask them to reset your two factor authentication.', + 'warning_much_data' => ':days days of data may take a while to load.', + 'registered' => 'You have registered successfully!', + 'search' => 'Search', + 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', + 'source_accounts' => 'Source account(s)', + 'destination_accounts' => 'Destination account(s)', + + // repeat frequencies: + 'repeat_freq_monthly' => 'monthly', + 'weekly' => 'weekly', + 'quarterly' => 'quarterly', + 'half-year' => 'every half year', + 'yearly' => 'yearly', + // account confirmation: + 'confirm_account_header' => 'Please confirm your account', + 'confirm_account_intro' => 'An email has been sent to the address you used during your registration. Please check it out for further instructions. If you did not get this message, you can have Firefly send it again.', + 'confirm_account_resend_email' => 'Send me the confirmation message I need to activate my account.', + 'account_is_confirmed' => 'Your account has been confirmed!', + 'invalid_activation_code' => 'It seems the code you are using is not valid, or has expired.', + 'confirm_account_is_resent_header' => 'The confirmation has been resent', + 'confirm_account_is_resent_text' => 'The confirmation message has been resent. If you still did not receive the confirmation message, please contact the site owner at :owner or check the log files to see what went wrong.', + 'confirm_account_is_resent_go_home' => 'Go to the index page of Firefly', + 'confirm_account_not_resent_header' => 'Something went wrong :(', + 'confirm_account_not_resent_intro' => 'The confirmation message has been not resent. If you still did not receive the confirmation message, please contact the site owner at :owner instead. Possibly, you have tried to resend the activation message too often. You can have Firefly III try to resend the confirmation message every hour.', + 'confirm_account_not_resent_go_home' => 'Go to the index page of Firefly', + + // export data: + 'import_and_export' => 'Import and export', + 'export_data' => 'Export data', + 'export_data_intro' => 'For backup purposes, when migrating to another system or when migrating to another Firefly III installation.', + 'export_format' => 'Export format', + 'export_format_csv' => 'Comma separated values (CSV file)', + 'export_format_mt940' => 'MT940 compatible format', + 'export_included_accounts' => 'Export transactions from these accounts', + 'include_config_help' => 'For easy re-import into Firefly III', + 'include_old_uploads_help' => 'Firefly III does not throw away the original CSV files you have imported in the past. You can include them in your export.', + 'do_export' => 'Export', + 'export_status_never_started' => 'The export has not started yet', + 'export_status_make_exporter' => 'Creating exporter thing...', + 'export_status_collecting_journals' => 'Collecting your transactions...', + 'export_status_collected_journals' => 'Collected your transactions!', + 'export_status_converting_to_export_format' => 'Converting your transactions...', + 'export_status_converted_to_export_format' => 'Converted your transactions!', + 'export_status_creating_journal_file' => 'Creating the export file...', + 'export_status_created_journal_file' => 'Created the export file!', + 'export_status_collecting_attachments' => 'Collecting all your attachments...', + 'export_status_collected_attachments' => 'Collected all your attachments!', + 'export_status_collecting_old_uploads' => 'Collecting all your previous uploads...', + 'export_status_collected_old_uploads' => 'Collected all your previous uploads!', + 'export_status_creating_config_file' => 'Creating a configuration file...', + 'export_status_created_config_file' => 'Created a configuration file!', + 'export_status_creating_zip_file' => 'Creating a zip file...', + 'export_status_created_zip_file' => 'Created a zip file!', + 'export_status_finished' => 'Export has succesfully finished! Yay!', + 'export_data_please_wait' => 'Please wait...', + 'attachment_explanation' => 'The file called \':attachment_name\' (#:attachment_id) was originally uploaded to :type \':description\' (#:journal_id) dated :date for the amount of :amount.', + + // rules + 'rules' => 'Rules', + 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.', + 'rule_name' => 'Name of rule', + 'rule_triggers' => 'Rule triggers when', + 'rule_actions' => 'Rule will', + 'new_rule' => 'New rule', + 'new_rule_group' => 'New rule group', + 'rule_priority_up' => 'Give rule more priority', + 'rule_priority_down' => 'Give rule less priority', + 'make_new_rule_group' => 'Make new rule group', + 'store_new_rule_group' => 'Store new rule group', + 'created_new_rule_group' => 'New rule group ":title" stored!', + 'updated_rule_group' => 'Successfully updated rule group ":title".', + 'edit_rule_group' => 'Edit rule group ":title"', + 'delete_rule_group' => 'Delete rule group ":title"', + 'deleted_rule_group' => 'Deleted rule group ":title"', + 'update_rule_group' => 'Update rule group', + 'no_rules_in_group' => 'There are no rules in this group', + 'move_rule_group_up' => 'Move rule group up', + 'move_rule_group_down' => 'Move rule group down', + 'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:', + 'make_new_rule' => 'Make new rule in rule group ":title"', + 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', + 'rule_help_active' => 'Inactive rules will never fire.', + 'stored_new_rule' => 'Stored new rule with title ":title"', + 'deleted_rule' => 'Deleted rule with title ":title"', + 'store_new_rule' => 'Store new rule', + 'updated_rule' => 'Updated rule with title ":title"', + 'default_rule_group_name' => 'Default rules', + 'default_rule_group_description' => 'All your rules not in a particular group.', + 'default_rule_name' => 'Your first default rule', + 'default_rule_description' => 'This rule is an example. You can safely delete it.', + 'default_rule_trigger_description' => 'The Man Who Sold the World', + 'default_rule_trigger_from_account' => 'David Bowie', + 'default_rule_action_prepend' => 'Bought the world from ', + 'default_rule_action_set_category' => 'Large expenses', + 'trigger' => 'Trigger', + 'trigger_value' => 'Trigger on value', + 'stop_processing_other_triggers' => 'Stop processing other triggers', + 'add_rule_trigger' => 'Add new trigger', + 'action' => 'Action', + 'action_value' => 'Action value', + 'stop_executing_other_actions' => 'Stop executing other actions', + 'add_rule_action' => 'Add new action', + 'edit_rule' => 'Edit rule ":title"', + 'delete_rule' => 'Delete rule ":title"', + 'update_rule' => 'Update rule', + 'test_rule_triggers' => 'See matching transactions', + 'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions', + 'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.', + 'warning_no_valid_triggers' => 'No valid triggers provided.', + 'execute_on_existing_transactions' => 'Execute for existing transactions', + 'execute_on_existing_transactions_intro' => 'When a rule or group has been changed or added, you can execute it for existing transactions', + 'execute_on_existing_transactions_short' => 'Existing transactions', + 'executed_group_on_existing_transactions' => 'Executed group ":title" for existing transactions', + 'execute_group_on_existing_transactions' => 'Execute group ":title" for existing transactions', + 'include_transactions_from_accounts' => 'Include transactions from these accounts', + 'execute' => 'Execute', + + // actions and triggers + 'rule_trigger_user_action' => 'User action is ":trigger_value"', + 'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"', + 'rule_trigger_from_account_ends' => 'Source account ends with ":trigger_value"', + 'rule_trigger_from_account_is' => 'Source account is ":trigger_value"', + 'rule_trigger_from_account_contains' => 'Source account contains ":trigger_value"', + 'rule_trigger_to_account_starts' => 'Destination account starts with ":trigger_value"', + 'rule_trigger_to_account_ends' => 'Destination account ends with ":trigger_value"', + 'rule_trigger_to_account_is' => 'Destination account is ":trigger_value"', + 'rule_trigger_to_account_contains' => 'Destination account contains ":trigger_value"', + 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"', + 'rule_trigger_amount_less' => 'Amount is less than :trigger_value', + 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', + 'rule_trigger_amount_more' => 'Amount is more than :trigger_value', + 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"', + 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"', + 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', + 'rule_trigger_description_is' => 'Description is ":trigger_value"', + 'rule_trigger_from_account_starts_choice' => 'Source account starts with..', + 'rule_trigger_from_account_ends_choice' => 'Source account ends with..', + 'rule_trigger_from_account_is_choice' => 'Source account is..', + 'rule_trigger_from_account_contains_choice' => 'Source account contains..', + 'rule_trigger_to_account_starts_choice' => 'Destination account starts with..', + 'rule_trigger_to_account_ends_choice' => 'Destination account ends with..', + 'rule_trigger_to_account_is_choice' => 'Destination account is..', + 'rule_trigger_to_account_contains_choice' => 'Destination account contains..', + 'rule_trigger_transaction_type_choice' => 'Transaction is of type..', + 'rule_trigger_amount_less_choice' => 'Amount is less than..', + 'rule_trigger_amount_exactly_choice' => 'Amount is..', + 'rule_trigger_amount_more_choice' => 'Amount is more than..', + 'rule_trigger_description_starts_choice' => 'Description starts with..', + 'rule_trigger_description_ends_choice' => 'Description ends with..', + 'rule_trigger_description_contains_choice' => 'Description contains..', + 'rule_trigger_description_is_choice' => 'Description is..', + 'rule_trigger_store_journal' => 'When a journal is created', + 'rule_trigger_update_journal' => 'When a journal is updated', + 'rule_action_set_category' => 'Set category to ":action_value"', + 'rule_action_clear_category' => 'Clear category', + 'rule_action_set_budget' => 'Set budget to ":action_value"', + 'rule_action_clear_budget' => 'Clear budget', + 'rule_action_add_tag' => 'Add tag ":action_value"', + 'rule_action_remove_tag' => 'Remove tag ":action_value"', + 'rule_action_remove_all_tags' => 'Remove all tags', + 'rule_action_set_description' => 'Set description to ":action_value"', + 'rule_action_append_description' => 'Append description with ":action_value"', + 'rule_action_prepend_description' => 'Prepend description with ":action_value"', + 'rule_action_set_category_choice' => 'Set category to..', + 'rule_action_clear_category_choice' => 'Clear any category', + 'rule_action_set_budget_choice' => 'Set budget to..', + 'rule_action_clear_budget_choice' => 'Clear any budget', + 'rule_action_add_tag_choice' => 'Add tag..', + 'rule_action_remove_tag_choice' => 'Remove tag..', + 'rule_action_remove_all_tags_choice' => 'Remove all tags', + 'rule_action_set_description_choice' => 'Set description to..', + 'rule_action_append_description_choice' => 'Append description with..', + 'rule_action_prepend_description_choice' => 'Prepend description with..', + + // tags + 'store_new_tag' => 'Store new tag', + 'update_tag' => 'Update tag', + 'no_location_set' => 'No location set.', + 'meta_data' => 'Meta data', + 'location' => 'Location', + + // preferences + 'pref_home_screen_accounts' => 'Home screen accounts', + 'pref_home_screen_accounts_help' => 'Which accounts should be displayed on the home page?', + 'pref_budget_settings' => 'Budget settings', + 'pref_budget_settings_help' => 'What\'s the maximum amount of money a budget envelope may contain?', + 'pref_view_range' => 'View range', + 'pref_view_range_help' => 'Some charts are automatically grouped in periods. What period would you prefer?', + 'pref_1D' => 'One day', + 'pref_1W' => 'One week', + 'pref_1M' => 'One month', + 'pref_3M' => 'Three months (quarter)', + 'pref_6M' => 'Six months', + 'pref_1Y' => 'One year', + 'pref_languages' => 'Languages', + 'pref_languages_help' => 'Firefly III supports several languages. Which one do you prefer?', + 'pref_custom_fiscal_year' => 'Fiscal year settings', + 'pref_custom_fiscal_year_label' => 'Enabled', + 'pref_custom_fiscal_year_help' => 'In countries that use a financial year other than January 1 to December 31, you can switch this on and specify start / end days of the fiscal year', + 'pref_fiscal_year_start_label' => 'Fiscal year start date', + 'pref_two_factor_auth' => '2-step verification', + 'pref_two_factor_auth_help' => 'When you enable 2-step verification (also known as two-factor authentication), you add an extra layer of security to your account. You sign in with something you know (your password) and something you have (a verification code). Verification codes are generated by an application on your phone, such as Authy or Google Authenticator.', + 'pref_enable_two_factor_auth' => 'Enable 2-step verification', + 'pref_two_factor_auth_disabled' => '2-step verification code removed and disabled', + 'pref_two_factor_auth_remove_it' => 'Don\'t forget to remove the account from your authentication app!', + 'pref_two_factor_auth_code' => 'Verify code', + 'pref_two_factor_auth_code_help' => 'Scan the QR code with an application on your phone such as Authy or Google Authenticator and enter the generated code.', + 'pref_two_factor_auth_reset_code' => 'Reset verification code', + 'pref_two_factor_auth_remove_code' => 'Remove verification code', + 'pref_two_factor_auth_remove_will_disable' => '(this will also disable two-factor authentication)', + 'pref_save_settings' => 'Save settings', + 'saved_preferences' => 'Preferences saved!', + 'transaction_page_size_title' => 'Page size', + 'transaction_page_size_help' => 'Any list of transactions shows at most this many transactions', + 'transaction_page_size_label' => 'Page size', + 'budget_maximum' => 'Budget maximum', + 'between_dates' => '(:start and :end)', + + // profile: + 'change_your_password' => 'Change your password', + 'delete_account' => 'Delete account', + 'current_password' => 'Current password', + 'new_password' => 'New password', + 'new_password_again' => 'New password (again)', + 'delete_your_account' => 'Delete your account', + 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', + 'delete_your_account_password' => 'Enter your password to continue.', + 'password' => 'Password', + 'are_you_sure' => 'Are you sure? You cannot undo this.', + 'delete_account_button' => 'DELETE your account', + 'invalid_current_password' => 'Invalid current password!', + 'password_changed' => 'Password changed!', + 'should_change' => 'The idea is to change your password.', + 'invalid_password' => 'Invalid password!', + + + // attachments + 'nr_of_attachments' => 'One attachment|:count attachments', + 'attachments' => 'Attachments', + 'edit_attachment' => 'Edit attachment ":name"', + 'update_attachment' => 'Update attachment', + 'delete_attachment' => 'Delete attachment ":name"', + 'attachment_deleted' => 'Deleted attachment ":name"', + 'attachment_updated' => 'Updated attachment ":name"', + 'upload_max_file_size' => 'Maximum file size: :size', + + // tour: + 'prev' => 'Prev', + 'next' => 'Next', + 'end-tour' => 'End tour', + 'pause' => 'Pause', + + // transaction index + 'title_expenses' => 'Expenses', + 'title_withdrawal' => 'Expenses', + 'title_revenue' => 'Revenue / income', + 'title_deposit' => 'Revenue / income', + 'title_transfer' => 'Transfers', + 'title_transfers' => 'Transfers', + + // create new stuff: + 'create_new_withdrawal' => 'Create new withdrawal', + 'create_new_deposit' => 'Create new deposit', + 'create_new_transfer' => 'Create new transfer', + 'create_new_asset' => 'Create new asset account', + 'create_new_expense' => 'Create new expense account', + 'create_new_revenue' => 'Create new revenue account', + 'create_new_piggy_bank' => 'Create new piggy bank', + 'create_new_bill' => 'Create new bill', + + // currencies: + 'create_currency' => 'Create a new currency', + 'edit_currency' => 'Edit currency ":name"', + 'store_currency' => 'Store new currency', + 'update_currency' => 'Update currency', + 'new_default_currency' => ':name is now the default currency.', + 'cannot_delete_currency' => 'Cannot delete :name because there are still transactions attached to it!', + 'deleted_currency' => 'Currency :name deleted', + 'created_currency' => 'Currency :name created', + 'updated_currency' => 'Currency :name updated', + 'ask_site_owner' => 'Please ask :owner to add, remove or edit currencies.', + 'currencies_intro' => 'Firefly III supports various currencies which you can set and enable here.', + 'make_default_currency' => 'make default', + 'default_currency' => 'default', + + // new user: + 'submit' => 'Submit', + 'getting_started' => 'Getting started', + 'to_get_started' => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:', + 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:', + 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.', + 'stored_new_account_new_user' => 'Yay! Your new account has been stored.', + 'stored_new_accounts_new_user' => 'Yay! Your new accounts have been stored.', + + // forms: + 'mandatoryFields' => 'Mandatory fields', + 'optionalFields' => 'Optional fields', + 'options' => 'Options', + 'something' => 'Something!', + + // budgets: + 'create_new_budget' => 'Create a new budget', + 'store_new_budget' => 'Store new budget', + 'stored_new_budget' => 'Stored new budget ":name"', + 'availableIn' => 'Available in :date', + 'available_between' => 'Available between :start and :end', + 'transactionsWithoutBudget' => 'Expenses without budget', + 'transactionsWithoutBudgetDate' => 'Expenses without budget in :date', + 'transactions_no_budget' => 'Expenses without budget between :start and :end', + 'spent_between' => 'Spent between :start and :end', + 'createBudget' => 'New budget', + 'inactiveBudgets' => 'Inactive budgets', + 'without_budget_between' => 'Transactions without a budget between :start and :end', + 'budget_in_month' => ':name in :month', + 'delete_budget' => 'Delete budget ":name"', + 'deleted_budget' => 'Deleted budget ":name"', + 'edit_budget' => 'Edit budget ":name"', + 'updated_budget' => 'Updated budget ":name"', + 'update_amount' => 'Update amount', + 'update_budget' => 'Update budget', + 'update_budget_amount_range' => 'Update (expected) available amount between :start and :end', + + // bills: + 'matching_on' => 'Matching on', + 'between_amounts' => 'between :low and :high.', + 'repeats' => 'Repeats', + 'connected_journals' => 'Connected transactions', + 'auto_match_on' => 'Automatically matched by Firefly', + 'auto_match_off' => 'Not automatically matched by Firefly', + 'next_expected_match' => 'Next expected match', + 'delete_bill' => 'Delete bill ":name"', + 'deleted_bill' => 'Deleted bill ":name"', + 'edit_bill' => 'Edit bill ":name"', + 'more' => 'More', + 'rescan_old' => 'Rescan old transactions', + 'update_bill' => 'Update bill', + 'updated_bill' => 'Updated bill ":name"', + 'store_new_bill' => 'Store new bill', + 'stored_new_bill' => 'Stored new bill ":name"', + 'cannot_scan_inactive_bill' => 'Inactive bills cannot be scanned.', + 'rescanned_bill' => 'Rescanned everything.', + 'bill_date_little_relevance' => 'The only part of this date used by Firefly is the day. It is only useful when your bill arrives at exactly the same date every month. If the payment date of your bills varies, simply use the first of the month.', + 'average_bill_amount_year' => 'Average bill amount (:year)', + 'average_bill_amount_overall' => 'Average bill amount (overall)', + + // accounts: + 'details_for_asset' => 'Details for asset account ":name"', + 'details_for_expense' => 'Details for expense account ":name"', + 'details_for_revenue' => 'Details for revenue account ":name"', + 'details_for_cash' => 'Details for cash account ":name"', + 'store_new_asset_account' => 'Store new asset account', + 'store_new_expense_account' => 'Store new expense account', + 'store_new_revenue_account' => 'Store new revenue account', + 'edit_asset_account' => 'Edit asset account ":name"', + 'edit_expense_account' => 'Edit expense account ":name"', + 'edit_revenue_account' => 'Edit revenue account ":name"', + 'delete_asset_account' => 'Delete asset account ":name"', + 'delete_expense_account' => 'Delete expense account ":name"', + 'delete_revenue_account' => 'Delete revenue account ":name"', + 'asset_deleted' => 'Successfully deleted asset account ":name"', + 'expense_deleted' => 'Successfully deleted expense account ":name"', + 'revenue_deleted' => 'Successfully deleted revenue account ":name"', + 'update_asset_account' => 'Update asset account', + 'update_expense_account' => 'Update expense account', + 'update_revenue_account' => 'Update revenue account', + 'make_new_asset_account' => 'Create a new asset account', + 'make_new_expense_account' => 'Create a new expense account', + 'make_new_revenue_account' => 'Create a new revenue account', + 'asset_accounts' => 'Asset accounts', + 'expense_accounts' => 'Expense accounts', + 'revenue_accounts' => 'Revenue accounts', + 'cash_accounts' => 'Cash accounts', + 'Cash account' => 'Cash account', + 'accountExtraHelp_asset' => '', + 'accountExtraHelp_expense' => '', + 'accountExtraHelp_revenue' => '', + 'account_type' => 'Account type', + 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:', + 'stored_new_account' => 'New account ":name" stored!', + 'updated_account' => 'Updated account ":name"', + 'credit_card_options' => 'Credit card options', + + // categories: + 'new_category' => 'New category', + 'create_new_category' => 'Create a new category', + 'without_category' => 'Without a category', + 'update_category' => 'Update category', + 'updated_category' => 'Updated category ":name"', + 'categories' => 'Categories', + 'edit_category' => 'Edit category ":name"', + 'no_category' => '(no category)', + 'category' => 'Category', + 'delete_category' => 'Delete category ":name"', + 'deleted_category' => 'Deleted category ":name"', + 'store_category' => 'Store new category', + 'stored_category' => 'Stored new category ":name"', + 'without_category_between' => 'Without category between :start and :end', + + // transactions: + 'update_withdrawal' => 'Update withdrawal', + 'update_deposit' => 'Update deposit', + 'update_transfer' => 'Update transfer', + 'updated_withdrawal' => 'Updated withdrawal ":description"', + 'updated_deposit' => 'Updated deposit ":description"', + 'updated_transfer' => 'Updated transfer ":description"', + 'delete_withdrawal' => 'Delete withdrawal ":description"', + 'delete_deposit' => 'Delete deposit ":description"', + 'delete_transfer' => 'Delete transfer ":description"', + 'deleted_withdrawal' => 'Successfully deleted withdrawal ":description"', + 'deleted_deposit' => 'Successfully deleted deposit ":description"', + 'deleted_transfer' => 'Successfully deleted transfer ":description"', + 'stored_journal' => 'Successfully created new transaction ":description"', + 'select_transactions' => 'Select transactions', + 'stop_selection' => 'Stop selecting transactions', + 'edit_selected' => 'Edit selected', + 'delete_selected' => 'Delete selected', + 'mass_delete_journals' => 'Delete a number of transactions', + 'mass_edit_journals' => 'Edit a number of transactions', + 'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.', + 'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious.', + 'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).', + 'mass_edited_transactions_success' => 'Updated :amount transaction(s)', + + + // new user: + 'welcome' => 'Welcome to Firefly!', + 'createNewAsset' => 'Create a new asset account to get started. ' . + 'This will allow you to create transactions and start your financial management', + 'createNewAssetButton' => 'Create new asset account', + + // home page: + 'yourAccounts' => 'Your accounts', + 'budgetsAndSpending' => 'Budgets and spending', + 'savings' => 'Savings', + 'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel', + 'createPiggyToContinue' => 'Create piggy banks to fill this panel.', + 'newWithdrawal' => 'New expense', + 'newDeposit' => 'New deposit', + 'newTransfer' => 'New transfer', + 'moneyIn' => 'Money in', + 'moneyOut' => 'Money out', + 'billsToPay' => 'Bills to pay', + 'billsPaid' => 'Bills paid', + 'viewDetails' => 'View details', + 'divided' => 'divided', + 'toDivide' => 'left to divide', + + // menu and titles, should be recycled as often as possible: + 'toggleNavigation' => 'Toggle navigation', + 'currency' => 'Currency', + 'preferences' => 'Preferences', + 'logout' => 'Logout', + 'searchPlaceholder' => 'Search...', + 'dashboard' => 'Dashboard', + 'currencies' => 'Currencies', + 'accounts' => 'Accounts', + 'Asset account' => 'Asset account', + 'Default account' => 'Asset account', + 'Expense account' => 'Expense account', + 'Revenue account' => 'Revenue account', + 'Initial balance account' => 'Initial balance account', + 'budgets' => 'Budgets', + 'tags' => 'Tags', + 'reports' => 'Reports', + 'transactions' => 'Transactions', + 'expenses' => 'Expenses', + 'income' => 'Revenue / income', + 'transfers' => 'Transfers', + 'moneyManagement' => 'Money management', + 'piggyBanks' => 'Piggy banks', + 'bills' => 'Bills', + 'createNew' => 'Create new', + 'withdrawal' => 'Withdrawal', + 'deposit' => 'Deposit', + 'account' => 'Account', + 'transfer' => 'Transfer', + 'Withdrawal' => 'Withdrawal', + 'Deposit' => 'Deposit', + 'Transfer' => 'Transfer', + 'bill' => 'Bill', + 'yes' => 'Yes', + 'no' => 'No', + 'amount' => 'Amount', + 'newBalance' => 'New balance', + 'overview' => 'Overview', + 'saveOnAccount' => 'Save on account', + 'unknown' => 'Unknown', + 'daily' => 'Daily', + 'monthly' => 'Monthly', + 'profile' => 'Profile', + 'errors' => 'Errors', + + // reports: + 'report_default' => 'Default financial report for :start until :end', + 'report_audit' => 'Transaction history overview for :start until :end', + 'quick_link_reports' => 'Quick links', + 'quick_link_default_report' => 'Default financial report', + 'quick_link_audit_report' => 'Transaction history overview', + 'report_this_month_quick' => 'Current month, all accounts', + 'report_this_year_quick' => 'Current year, all accounts', + 'report_this_fiscal_year_quick' => 'Current fiscal year, all accounts', + 'report_all_time_quick' => 'All-time, all accounts', + 'reports_can_bookmark' => 'Remember that reports can be bookmarked.', + 'incomeVsExpenses' => 'Income vs. expenses', + 'accountBalances' => 'Account balances', + 'balanceStartOfYear' => 'Balance at start of year', + 'balanceEndOfYear' => 'Balance at end of year', + 'balanceStartOfMonth' => 'Balance at start of month', + 'balanceEndOfMonth' => 'Balance at end of month', + 'balanceStart' => 'Balance at start of period', + 'balanceEnd' => 'Balance at end of period', + 'reportsOwnAccounts' => 'Reports for your own accounts', + 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', + 'splitByAccount' => 'Split by account', + 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', + 'coveredWithTags' => 'Covered with tags', + 'leftUnbalanced' => 'Left unbalanced', + 'expectedBalance' => 'Expected balance', + 'outsideOfBudgets' => 'Outside of budgets', + 'leftInBudget' => 'Left in budget', + 'sumOfSums' => 'Sum of sums', + 'noCategory' => '(no category)', + 'notCharged' => 'Not charged (yet)', + 'inactive' => 'Inactive', + 'active' => 'Active', + 'difference' => 'Difference', + 'in' => 'In', + 'out' => 'Out', + 'topX' => 'top :number', + 'showTheRest' => 'Show everything', + 'hideTheRest' => 'Show only the top :number', + 'sum_of_year' => 'Sum of year', + 'sum_of_years' => 'Sum of years', + 'average_of_year' => 'Average of year', + 'average_of_years' => 'Average of years', + 'categories_earned_in_year' => 'Categories (by earnings)', + 'categories_spent_in_year' => 'Categories (by spendings)', + 'report_type' => 'Report type', + 'report_type_default' => 'Default financial report', + 'report_type_audit' => 'Transaction history overview (audit)', + 'report_type_meta-history' => 'Categories, budgets and bills overview', + 'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.', + 'report_included_accounts' => 'Included accounts', + 'report_date_range' => 'Date range', + 'report_include_help' => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.', + 'report_preset_ranges' => 'Pre-set ranges', + 'shared' => 'Shared', + 'fiscal_year' => 'Fiscal year', + 'income_entry' => 'Income from account ":name" between :start and :end', + 'expense_entry' => 'Expenses to account ":name" between :start and :end', + 'category_entry' => 'Expenses in category ":name" between :start and :end', + 'budget_spent_amount' => 'Expenses in budget ":budget" between :start and :end', + 'balance_amount' => 'Expenses in budget ":budget" paid from account ":account" between :start and :end', + 'no_audit_activity' => 'No activity was recorded on account :account_name between :start and :end.', + 'audit_end_balance' => 'Account balance of :account_name at the end of :end was: :balance', + + // charts: + 'chart' => 'Chart', + 'dayOfMonth' => 'Day of the month', + 'month' => 'Month', + 'budget' => 'Budget', + 'spent' => 'Spent', + 'earned' => 'Earned', + 'overspent' => 'Overspent', + 'left' => 'Left', + 'no_budget' => '(no budget)', + 'maxAmount' => 'Maximum amount', + 'minAmount' => 'Minumum amount', + 'billEntry' => 'Current bill entry', + 'name' => 'Name', + 'date' => 'Date', + 'paid' => 'Paid', + 'unpaid' => 'Unpaid', + 'day' => 'Day', + 'budgeted' => 'Budgeted', + 'period' => 'Period', + 'balance' => 'Balance', + 'summary' => 'Summary', + 'sum' => 'Sum', + 'average' => 'Average', + 'balanceFor' => 'Balance for :name', + + // piggy banks: + 'add_money_to_piggy' => 'Add money to piggy bank ":name"', + 'piggy_bank' => 'Piggy bank', + 'new_piggy_bank' => 'Create new piggy bank', + 'store_piggy_bank' => 'Store new piggy bank', + 'stored_piggy_bank' => 'Store new piggy bank ":name"', + 'account_status' => 'Account status', + 'left_for_piggy_banks' => 'Left for piggy banks', + 'sum_of_piggy_banks' => 'Sum of piggy banks', + 'saved_so_far' => 'Saved so far', + 'left_to_save' => 'Left to save', + 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', + 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', + 'add' => 'Add', + 'remove' => 'Remove', + 'max_amount_add' => 'The maximum amount you can add is', + 'max_amount_remove' => 'The maximum amount you can remove is', + 'update_piggy_button' => 'Update piggy bank', + 'update_piggy_title' => 'Update piggy bank ":name"', + 'updated_piggy_bank' => 'Updated piggy bank ":name"', + 'details' => 'Details', + 'events' => 'Events', + 'target_amount' => 'Target amount', + 'start_date' => 'Start date', + 'target_date' => 'Target date', + 'no_target_date' => 'No target date', + 'todo' => 'to do', + 'table' => 'Table', + 'piggy_bank_not_exists' => 'Piggy bank no longer exists.', + 'add_any_amount_to_piggy' => 'Add money to this piggy bank to reach your target of :amount.', + 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date', + 'delete_piggy_bank' => 'Delete piggy bank ":name"', + 'cannot_add_amount_piggy' => 'Could not add :amount to ":name".', + 'deleted_piggy_bank' => 'Deleted piggy bank ":name"', + 'added_amount_to_piggy' => 'Added :amount to ":name"', + 'removed_amount_from_piggy' => 'Removed :amount from ":name"', + 'cannot_remove_amount_piggy' => 'Could not remove :amount from ":name".', + + // tags + 'regular_tag' => 'Just a regular tag.', + 'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.', + 'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.', + 'delete_tag' => 'Delete tag ":tag"', + 'deleted_tag' => 'Deleted tag ":tag"', + 'new_tag' => 'Make new tag', + 'edit_tag' => 'Edit tag ":tag"', + 'updated_tag' => 'Updated tag ":tag"', + 'created_tag' => 'Tag ":tag" has been created!', + 'no_year' => 'No year set', + 'no_month' => 'No month set', + 'tag_title_nothing' => 'Default tags', + 'tag_title_balancingAct' => 'Balancing act tags', + 'tag_title_advancePayment' => 'Advance payment tags', + 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like expensive, bill or for-party. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called Christmas dinner with friends and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.', + 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.', + 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', + + + // administration + 'administration' => 'Administration', + 'user_administration' => 'User administration', + 'list_all_users' => 'All users', + 'all_users' => 'All users', + 'all_blocked_domains' => 'All blocked domains', + 'blocked_domains' => 'Blocked domains', + 'no_domains_banned' => 'No domains blocked', + 'all_user_domains' => 'All user email address domains', + 'all_domains_is_filtered' => 'This list does not include already blocked domains.', + 'domain_now_blocked' => 'Domain :domain is now blocked', + 'domain_now_unblocked' => 'Domain :domain is now unblocked', + 'manual_block_domain' => 'Block a domain by hand', + 'block_domain' => 'Block domain', + 'no_domain_filled_in' => 'No domain filled in', + 'domain_already_blocked' => 'Domain :domain is already blocked', + 'domain_is_now_blocked' => 'Domain :domain is now blocked', + + // split a transaction: + 'transaction_meta_data' => 'Transaction meta-data', + 'transaction_dates' => 'Transaction dates', + 'splits' => 'Splits', + 'split_title_withdrawal' => 'Split your new withdrawal', + 'split_intro_one_withdrawal' => 'Firefly supports the "splitting" of a withdrawal.', + 'split_intro_two_withdrawal' => 'It means that the amount of money you\'ve spent is divided between several destination expense accounts, budgets or categories.', + 'split_intro_three_withdrawal' => 'For example: you could split your :total groceries so you pay :split_one from your "daily groceries" budget and :split_two from your "cigarettes" budget.', + 'split_table_intro_withdrawal' => 'Split your withdrawal in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_withdrawal' => 'Store splitted withdrawal', + 'update_splitted_withdrawal' => 'Update splitted withdrawal', + 'split_title_deposit' => 'Split your new deposit', + 'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.', + 'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.', + 'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.', + 'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_deposit' => 'Store splitted deposit', + 'split_title_transfer' => 'Split your new transfer', + 'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.', + 'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.', + 'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.', + 'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_transfer' => 'Store splitted transfer', + 'add_another_split' => 'Add another split', + 'split-transactions' => 'Split transactions', + 'split-new-transaction' => 'Split a new transaction', + 'do_split' => 'Do a split', + 'split_this_withdrawal' => 'Split this withdrawal', + 'split_this_deposit' => 'Split this deposit', + 'split_this_transfer' => 'Split this transfer', + 'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.', + 'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.', + 'no_edit_multiple_left' => 'You have selected no valid transactions to edit.', + + // import + 'configuration_file_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you.', + 'import_data_index' => 'Index', + 'import_file_type_csv' => 'CSV (comma separated values)', + 'import_file_type_help' => 'Select the type of file you will upload', + 'import_start' => 'Start the import', + 'configure_import' => 'Further configure your import', + 'import_finish_configuration' => 'Finish configuration', + 'settings_for_import' => 'Settings', + 'import_complete' => 'Import configuration complete!', + 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you need to execute the following command in your console. Unfortunately, a web-based import is not yet possible.', + 'import_download_config' => 'Download configuration', + 'import_start_import' => 'Start import', + 'import_intro_beta' => 'The import function of Firefly III is in beta. Many users of Firefly III have tried many different files. Although each individual compontent of this import routine works (really), the combination might break. If your file cannot be imported by Firefly, please read this wiki page so I can fix the problem you have run into.', + 'import_data' => 'Import data', + 'import_data_full' => 'Import data into Firefly III', + 'import' => 'Import', + 'import_intro_text' => 'Welcome to the Firefly III data import routine. At the moment, this routine can help you import files into Firefly. To do so, you must download or export transactions from other systems or software, and upload them here. The next steps will let you help Firefly III determin what the content is of your file, and how to handle it. Please select a file, and read all instructions carefully.', + 'import_file_help' => 'Select your file', +]; diff --git a/resources/lang/zh-TW/form.php b/resources/lang/zh-TW/form.php new file mode 100644 index 0000000000..fa1f42a199 --- /dev/null +++ b/resources/lang/zh-TW/form.php @@ -0,0 +1,149 @@ + 'Bank name', + 'bank_balance' => 'Balance', + 'savings_balance' => 'Savings balance', + 'credit_card_limit' => 'Credit card limit', + 'automatch' => 'Match automatically', + 'skip' => 'Skip', + 'name' => 'Name', + 'active' => 'Active', + 'amount_min' => 'Minimum amount', + 'amount_max' => 'Maximum amount', + 'match' => 'Matches on', + 'repeat_freq' => 'Repeats', + 'journal_currency_id' => 'Currency', + 'journal_amount' => 'Amount', + 'journal_asset_source_account' => 'Asset account (source)', + 'journal_source_account_name' => 'Revenue account (source)', + 'journal_source_account_id' => 'Asset account (source)', + 'account_from_id' => 'From account', + 'account_to_id' => 'To account', + 'journal_destination_account_id' => 'Asset account (destination)', + 'asset_destination_account' => 'Asset account (destination)', + 'asset_source_account' => 'Asset account (source)', + 'journal_description' => 'Description', + 'split_journal' => 'Split this transaction', + 'split_journal_explanation' => 'Split this transaction in multiple parts', + 'currency' => 'Currency', + 'account_id' => 'Asset account', + 'budget_id' => 'Budget', + 'openingBalance' => 'Opening balance', + 'tagMode' => 'Tag mode', + 'tagPosition' => 'Tag location', + 'virtualBalance' => 'Virtual balance', + 'longitude_latitude' => 'Location', + 'targetamount' => 'Target amount', + 'accountRole' => 'Account role', + 'openingBalanceDate' => 'Opening balance date', + 'ccType' => 'Credit card payment plan', + 'ccMonthlyPaymentDate' => 'Credit card monthly payment date', + 'piggy_bank_id' => 'Piggy bank', + 'returnHere' => 'Return here', + 'returnHereExplanation' => 'After storing, return here to create another one.', + 'returnHereUpdateExplanation' => 'After updating, return here.', + 'description' => 'Description', + 'expense_account' => 'Expense account', + 'revenue_account' => 'Revenue account', + 'amount' => 'Amount', + 'date' => 'Date', + 'interest_date' => 'Interest date', + 'book_date' => 'Book date', + 'process_date' => 'Processing date', + 'category' => 'Category', + 'tags' => 'Tags', + 'deletePermanently' => 'Delete permanently', + 'cancel' => 'Cancel', + 'targetdate' => 'Target date', + 'tag' => 'Tag', + 'under' => 'Under', + 'symbol' => 'Symbol', + 'code' => 'Code', + 'iban' => 'IBAN', + 'accountNumber' => 'Account number', + 'has_headers' => 'Headers', + 'date_format' => 'Date format', + 'specifix' => 'Bank- or file specific fixes', + 'attachments[]' => 'Attachments', + 'store_new_withdrawal' => 'Store new withdrawal', + 'store_new_deposit' => 'Store new deposit', + 'store_new_transfer' => 'Store new transfer', + 'add_new_withdrawal' => 'Add a new withdrawal', + 'add_new_deposit' => 'Add a new deposit', + 'add_new_transfer' => 'Add a new transfer', + 'noPiggybank' => '(no piggy bank)', + 'title' => 'Title', + 'notes' => 'Notes', + 'filename' => 'File name', + 'mime' => 'Mime type', + 'size' => 'Size', + 'trigger' => 'Trigger', + 'stop_processing' => 'Stop processing', + 'start_date' => 'Start of range', + 'end_date' => 'End of range', + 'export_start_range' => 'Start of export range', + 'export_end_range' => 'End of export range', + 'export_format' => 'File format', + 'include_attachments' => 'Include uploaded attachments', + 'include_config' => 'Include configuration file', + 'include_old_uploads' => 'Include imported data', + 'accounts' => 'Export transactions from these accounts', + 'delete_account' => 'Delete account ":name"', + 'delete_bill' => 'Delete bill ":name"', + 'delete_budget' => 'Delete budget ":name"', + 'delete_category' => 'Delete category ":name"', + 'delete_currency' => 'Delete currency ":name"', + 'delete_journal' => 'Delete transaction with description ":description"', + 'delete_attachment' => 'Delete attachment ":name"', + 'delete_rule' => 'Delete rule ":title"', + 'delete_rule_group' => 'Delete rule group ":title"', + 'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?', + 'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?', + 'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?', + 'rule_areYouSure' => 'Are you sure you want to delete the rule titled ":title"?', + 'ruleGroup_areYouSure' => 'Are you sure you want to delete the rule group titled ":title"?', + 'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?', + 'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?', + 'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?', + 'piggyBank_areYouSure' => 'Are you sure you want to delete the piggy bank named ":name"?', + 'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?', + 'mass_journal_are_you_sure' => 'Are you sure you want to delete these transactions?', + 'tag_areYouSure' => 'Are you sure you want to delete the tag ":tag"?', + 'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.', + 'mass_make_selection' => 'You can still prevent items from being deleted by removing the checkbox.', + 'delete_all_permanently' => 'Delete selected permanently', + 'update_all_journals' => 'Update these transactions', + 'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.', + 'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.', + 'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.', + 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.', + 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.', + + // admin + 'domain' => 'Domain', + + // import + 'import_file' => 'Import file', + 'configuration_file' => 'Configuration file', + 'import_file_type' => 'Import file type', + 'csv_comma' => 'A comma (,)', + 'csv_semicolon' => 'A semicolon (;)', + 'csv_tab' => 'A tab (invisible)', + 'csv_delimiter' => 'CSV field delimiter', + 'csv_import_account' => 'Default import account', + 'csv_config' => 'CSV import configuration', + + +]; diff --git a/resources/lang/zh-TW/help.php b/resources/lang/zh-TW/help.php new file mode 100644 index 0000000000..d1fa572177 --- /dev/null +++ b/resources/lang/zh-TW/help.php @@ -0,0 +1,85 @@ + 'Welcome to Firefly III', + 'main-content-text' => 'Do yourself a favor and follow this short guide to make sure you know your way around.', + 'sidebar-toggle-title' => 'Sidebar to create stuff', + 'sidebar-toggle-text' => 'Hidden under the plus icon are all the buttons to create new stuff. Accounts, transactions, everything!', + 'account-menu-title' => 'All your accounts', + 'account-menu-text' => 'Here you can find all the accounts you\'ve made.', + 'budget-menu-title' => 'Budgets', + 'budget-menu-text' => 'Use this page to organise your finances and limit spending.', + 'report-menu-title' => 'Reports', + 'report-menu-text' => 'Check this out when you want a solid overview of your finances.', + 'transaction-menu-title' => 'Transactions', + 'transaction-menu-text' => 'All transactions you\'ve created can be found here.', + 'option-menu-title' => 'Options', + 'option-menu-text' => 'This is pretty self-explanatory.', + 'main-content-end-title' => 'The end!', + 'main-content-end-text' => 'Remember that every page has a small question mark at the right top. Click it to get help about the page you\'re on.', + 'index' => 'index', + 'home' => 'home', + 'accounts-index' => 'accounts.index', + 'accounts-create' => 'accounts.create', + 'accounts-edit' => 'accounts.edit', + 'accounts-delete' => 'accounts.delete', + 'accounts-show' => 'accounts.show', + 'attachments-edit' => 'attachments.edit', + 'attachments-delete' => 'attachments.delete', + 'attachments-show' => 'attachments.show', + 'attachments-preview' => 'attachments.preview', + 'bills-index' => 'bills.index', + 'bills-create' => 'bills.create', + 'bills-edit' => 'bills.edit', + 'bills-delete' => 'bills.delete', + 'bills-show' => 'bills.show', + 'budgets-index' => 'budgets.index', + 'budgets-create' => 'budgets.create', + 'budgets-edit' => 'budgets.edit', + 'budgets-delete' => 'budgets.delete', + 'budgets-show' => 'budgets.show', + 'budgets-noBudget' => 'budgets.noBudget', + 'categories-index' => 'categories.index', + 'categories-create' => 'categories.create', + 'categories-edit' => 'categories.edit', + 'categories-delete' => 'categories.delete', + 'categories-show' => 'categories.show', + 'categories-show-date' => 'categories.show.date', + 'categories-noCategory' => 'categories.noCategory', + 'currency-index' => 'currency.index', + 'currency-create' => 'currency.create', + 'currency-edit' => 'currency.edit', + 'currency-delete' => 'currency.delete', + 'new-user-index' => 'new-user.index', + 'piggy-banks-index' => 'piggy-banks.index', + 'piggy-banks-create' => 'piggy-banks.create', + 'piggy-banks-edit' => 'piggy-banks.edit', + 'piggy-banks-delete' => 'piggy-banks.delete', + 'piggy-banks-show' => 'piggy-banks.show', + 'preferences' => 'preferences', + 'profile' => 'profile', + 'profile-change-password' => 'profile.change-password', + 'profile-delete-account' => 'profile.delete-account', + 'reports-index' => 'reports.index', + 'reports-report' => 'reports.report', + 'search' => 'search', + 'tags-index' => 'tags.index', + 'tags-create' => 'tags.create', + 'tags-show' => 'tags.show', + 'tags-edit' => 'tags.edit', + 'tags-delete' => 'tags.delete', + 'transactions-index' => 'transactions.index', + 'transactions-create' => 'transactions.create', + 'transactions-edit' => 'transactions.edit', + 'transactions-delete' => 'transactions.delete', + 'transactions-show' => 'transactions.show', +]; diff --git a/resources/lang/zh-TW/list.php b/resources/lang/zh-TW/list.php new file mode 100644 index 0000000000..193740c717 --- /dev/null +++ b/resources/lang/zh-TW/list.php @@ -0,0 +1,66 @@ + 'Buttons', + 'icon' => 'Icon', + 'create_date' => 'Created at', + 'update_date' => 'Updated at', + 'balance_before' => 'Balance before', + 'balance_after' => 'Balance after', + 'name' => 'Name', + 'role' => 'Role', + 'currentBalance' => 'Current balance', + 'active' => 'Is active?', + 'lastActivity' => 'Last activity', + 'balanceDiff' => 'Balance difference between :start and :end', + 'matchedOn' => 'Matched on', + 'matchesOn' => 'Matched on', + 'account_type' => 'Account type', + 'new_balance' => 'New balance', + 'account' => 'Account', + 'matchingAmount' => 'Amount', + 'lastMatch' => 'Last match', + 'split_number' => 'Split #', + 'destination' => 'Destination', + 'source' => 'Source', + 'expectedMatch' => 'Expected match', + 'automatch' => 'Auto match?', + 'repeat_freq' => 'Repeats', + 'description' => 'Description', + 'amount' => 'Amount', + 'date' => 'Date', + 'interest_date' => 'Interest date', + 'book_date' => 'Book date', + 'process_date' => 'Processing date', + 'from' => 'From', + 'piggy_bank' => 'Piggy bank', + 'to' => 'To', + 'budget' => 'Budget', + 'category' => 'Category', + 'bill' => 'Bill', + 'withdrawal' => 'Withdrawal', + 'deposit' => 'Deposit', + 'transfer' => 'Transfer', + 'type' => 'Type', + 'completed' => 'Completed', + 'iban' => 'IBAN', + 'paid_current_period' => 'Paid this period', + 'email' => 'Email', + 'registered_at' => 'Registered at', + 'is_activated' => 'Is activated', + 'is_blocked' => 'Is blocked', + 'is_admin' => 'Is admin', + 'has_two_factor' => 'Has 2FA', + 'confirmed_from' => 'Confirmed from', + 'registered_from' => 'Registered from', + 'blocked_code' => 'Block code', + 'domain' => 'Domain', + 'registration_attempts' => 'Registration attempts', +]; diff --git a/resources/lang/zh-TW/pagination.php b/resources/lang/zh-TW/pagination.php new file mode 100644 index 0000000000..b1cf416ae8 --- /dev/null +++ b/resources/lang/zh-TW/pagination.php @@ -0,0 +1,13 @@ + '« Previous', + 'next' => 'Next »', +]; diff --git a/resources/lang/zh-TW/passwords.php b/resources/lang/zh-TW/passwords.php new file mode 100644 index 0000000000..6c382f830f --- /dev/null +++ b/resources/lang/zh-TW/passwords.php @@ -0,0 +1,17 @@ + 'Passwords must be at least six characters and match the confirmation.', + 'user' => 'We can\'t find a user with that e-mail address.', + 'token' => 'This password reset token is invalid.', + 'sent' => 'We have e-mailed your password reset link!', + 'reset' => 'Your password has been reset!', + 'blocked' => 'Nice try though.', +]; diff --git a/resources/lang/zh-TW/validation.php b/resources/lang/zh-TW/validation.php new file mode 100644 index 0000000000..fa086c9fbb --- /dev/null +++ b/resources/lang/zh-TW/validation.php @@ -0,0 +1,80 @@ + 'This is not a valid IBAN.', + 'unique_account_number_for_user' => 'It looks like this account number is already in use.', + 'rule_trigger_value' => 'This value is invalid for the selected trigger.', + 'rule_action_value' => 'This value is invalid for the selected action.', + 'invalid_domain' => 'Due to security constraints, you cannot register from this domain.', + 'file_already_attached' => 'Uploaded file ":name" is already attached to this object.', + 'file_attached' => 'Succesfully uploaded file ":name".', + 'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.', + 'file_too_large' => 'File ":name" is too large.', + 'belongs_to_user' => 'The value of :attribute is unknown', + 'accepted' => 'The :attribute must be accepted.', + 'active_url' => 'The :attribute is not a valid URL.', + 'after' => 'The :attribute must be a date after :date.', + 'alpha' => 'The :attribute may only contain letters.', + 'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.', + 'alpha_num' => 'The :attribute may only contain letters and numbers.', + 'array' => 'The :attribute must be an array.', + 'unique_for_user' => 'There already is an entry with this :attribute.', + 'before' => 'The :attribute must be a date before :date.', + 'unique_object_for_user' => 'This name is already in use', + 'unique_account_for_user' => 'This account name is already in use', + 'between.numeric' => 'The :attribute must be between :min and :max.', + 'between.file' => 'The :attribute must be between :min and :max kilobytes.', + 'between.string' => 'The :attribute must be between :min and :max characters.', + 'between.array' => 'The :attribute must have between :min and :max items.', + 'boolean' => 'The :attribute field must be true or false.', + 'confirmed' => 'The :attribute confirmation does not match.', + 'date' => 'The :attribute is not a valid date.', + 'date_format' => 'The :attribute does not match the format :format.', + 'different' => 'The :attribute and :other must be different.', + 'digits' => 'The :attribute must be :digits digits.', + 'digits_between' => 'The :attribute must be between :min and :max digits.', + 'email' => 'The :attribute must be a valid email address.', + 'filled' => 'The :attribute field is required.', + 'exists' => 'The selected :attribute is invalid.', + 'image' => 'The :attribute must be an image.', + 'in' => 'The selected :attribute is invalid.', + 'integer' => 'The :attribute must be an integer.', + 'ip' => 'The :attribute must be a valid IP address.', + 'json' => 'The :attribute must be a valid JSON string.', + 'max.numeric' => 'The :attribute may not be greater than :max.', + 'max.file' => 'The :attribute may not be greater than :max kilobytes.', + 'max.string' => 'The :attribute may not be greater than :max characters.', + 'max.array' => 'The :attribute may not have more than :max items.', + 'mimes' => 'The :attribute must be a file of type: :values.', + 'min.numeric' => 'The :attribute must be at least :min.', + 'min.file' => 'The :attribute must be at least :min kilobytes.', + 'min.string' => 'The :attribute must be at least :min characters.', + 'min.array' => 'The :attribute must have at least :min items.', + 'not_in' => 'The selected :attribute is invalid.', + 'numeric' => 'The :attribute must be a number.', + 'regex' => 'The :attribute format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_unless' => 'The :attribute field is required unless :other is in :values.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values is present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute and :other must match.', + 'size.numeric' => 'The :attribute must be :size.', + 'size.file' => 'The :attribute must be :size kilobytes.', + 'size.string' => 'The :attribute must be :size characters.', + 'size.array' => 'The :attribute must contain :size items.', + 'unique' => 'The :attribute has already been taken.', + 'string' => 'The :attribute must be a string.', + 'url' => 'The :attribute format is invalid.', + 'timezone' => 'The :attribute must be a valid zone.', + '2fa_code' => 'The :attribute field is invalid.', +]; From c4ac1460f000348101bc6f0865acc4c24a4ec8d8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 10:44:27 +0200 Subject: [PATCH 86/94] Expanded translations --- resources/lang/nl_NL/csv.php | 52 +++++++++++++++++------------------ resources/lang/nl_NL/form.php | 8 +++--- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/resources/lang/nl_NL/csv.php b/resources/lang/nl_NL/csv.php index 991dc2fa08..5c87f2ee00 100644 --- a/resources/lang/nl_NL/csv.php +++ b/resources/lang/nl_NL/csv.php @@ -15,30 +15,30 @@ return [ 'import_configure_intro' => 'Hier zie je enkele opties voor jouw CSV bestand. Geef aan of je CSV bestand kolomtitels bevat, en hoe het datumveld is opgebouwd. Hier moet je wellicht wat experimenteren. Het scheidingsteken is meestal een ",", maar dat kan ook een ";" zijn. Controleer dit zorgvuldig.', 'import_configure_form' => 'Formulier', 'header_help' => 'Vink hier als de eerste rij kolomtitels bevat', - 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', - 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', - 'config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.', - 'import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.', + 'date_help' => 'Datum/tijd formaat in jouw CSV bestand. Volg het formaat zoals ze het op deze pagina uitleggen. Het standaardformaat ziet er zo uit: :dateExample.', + 'delimiter_help' => 'Kies het veldscheidingsteken dat in jouw bestand wordt gebruikt. Als je het niet zeker weet, is de komma de beste optie.', + 'config_file_help' => 'Voer hier je configuratiebestand in. Als je deze niet hebt, geen zorgen. Latere stappen leggen dit uit.', + 'import_account_help' => 'Als jouw CSV bestand geen referenties bevat naar jouw rekening(en), geef dan hier aan om welke rekening het gaat.', + 'upload_not_writeable' => 'Het grijze vlak bevat een bestandspad. Dit pad moet schrijfbaar zijn.', // roles 'column_roles_title' => 'Bepaal de inhoud van elke kolom', - 'column_roles_text' => '

    Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

    Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

    ', + 'column_roles_text' => '

    Firefly III kan niet raden welke soort gegevens er in elke kolom staan. Dat moet je er bij vertellen. Gebruik de voorbeeldgegevens en kies het juiste type uit de dropdown. Staat jouw type er niet bij? Open dan een ticket.

    Sommige waarden, zoals rekeningnummers en namen staan wellicht al in jouw Firefly III database. Als je dan het vinkje vinkt, kan je zelf de link leggen tussen wat er in het CSV bestand staat en wat er in de database staat. Hiermee kan je de import sturen.

    ', 'column_roles_table' => 'Tabel', 'column_name' => 'Kolomnaam', 'column_example' => 'Voorbeeldgegevens', - 'column_role' => 'Column data meaning', - 'do_map_value' => 'Map these values', - 'column' => 'Column', - 'no_example_data' => 'No example data available', - 'store_column_roles' => 'Continue import', - 'do_not_map' => '(do not map)', - 'map_title' => 'Connect import data to Firefly III data', - 'map_text' => 'In the following tables, the left value shows you information found in your uploaded CSV file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', + 'column_role' => 'Kolomrol', + 'do_map_value' => 'Maak een mapping', + 'column' => 'Kolom', + 'no_example_data' => 'Geen voorbeeldgegevens', + 'store_column_roles' => 'Ga verder met import', + 'do_not_map' => '(niet mappen)', + 'map_title' => 'Verbind importdata met Firefly III data', + 'map_text' => 'In deze tabellen is de linkerwaarde een waarde uit je CSV bestand. Jij moet de link leggen, als mogelijk, met een waarde uit jouw database. Firefly houdt zich hier aan. Als er geen waarde is, selecteer dan ook niets.', - 'field_value' => 'Field value', - 'field_mapped_to' => 'Mapped to', - 'store_column_mapping' => 'Store mapping', + 'field_value' => 'Veldwaarde', + 'field_mapped_to' => 'Gelinkt aan', + 'store_column_mapping' => 'Mapping opslaan', // map things. @@ -68,13 +68,13 @@ return [ 'column_opposing-id' => 'Tegenrekening (ID gelijk aan Firefly)', 'column_external-id' => 'Externe ID', 'column_opposing-name' => 'Tegenrekeningnaam', - 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', - 'column_ing-debet-credit' => 'ING specific debet/credit indicator', - 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', - 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', - 'column_sepa-db' => 'SEPA Direct Debet', - 'column_tags-comma' => 'Tags (comma separated)', - 'column_tags-space' => 'Tags (space separated)', - 'column_account-number' => 'Asset account (account number)', - 'column_opposing-number' => 'Opposing account (account number)', + 'column_rabo-debet-credit' => 'Rabobankspecifiek bij/af indicator', + 'column_ing-debet-credit' => 'ING-specifieke bij/af indicator', + 'column_sepa-ct-id' => 'SEPA transactienummer', + 'column_sepa-ct-op' => 'SEPA tegenrekeningnummer', + 'column_sepa-db' => 'SEPA "direct debet"-nummer', + 'column_tags-comma' => 'Tags (kommagescheiden)', + 'column_tags-space' => 'Tags (spatiegescheiden)', + 'column_account-number' => 'Betaalrekening (rekeningnummer)', + 'column_opposing-number' => 'Tegenrekening (rekeningnummer)', ]; \ No newline at end of file diff --git a/resources/lang/nl_NL/form.php b/resources/lang/nl_NL/form.php index b07a736e57..e6c4b74b91 100644 --- a/resources/lang/nl_NL/form.php +++ b/resources/lang/nl_NL/form.php @@ -132,12 +132,12 @@ return [ 'tag_keep_transactions' => 'De transactie verbonden aan deze tag blijft bewaard.|De :count transacties verbonden aan deze tag blijven bewaard.', // admin - 'domain' => 'Domain', + 'domain' => 'Domein', // import - 'import_file' => 'Import file', - 'configuration_file' => 'Configuration file', - 'import_file_type' => 'Import file type', + 'import_file' => 'Importbestand', + 'configuration_file' => 'Configuratiebestand', + 'import_file_type' => 'Importbestandstype', 'csv_comma' => 'Een komma (,)', 'csv_semicolon' => 'Een puntkomma (;)', 'csv_tab' => 'Een tab (onzichtbaar)', From 9850220aacc2d5e4df54490bdcf59625fc8ff85e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 10:46:22 +0200 Subject: [PATCH 87/94] Add new languages. --- config/firefly.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/firefly.php b/config/firefly.php index a053f95031..f8bfde0a4e 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -94,6 +94,8 @@ return [ 'nl_NL' => ['name_locale' => 'Nederlands', 'name_english' => 'Dutch', 'complete' => true], 'pt_BR' => ['name_locale' => 'Português do Brasil', 'name_english' => 'Portuguese (Brazil)', 'complete' => true], 'fr_FR' => ['name_locale' => 'Français', 'name_english' => 'French', 'complete' => false], + 'zh-HK' => ['name_locale' => 'Chinese Traditional, Hong Kong', 'name_english' => 'Chinese Traditional, Hong Kong', 'complete' => false], + 'zh-TW' => ['name_locale' => 'Chinese Traditional', 'name_english' => 'Chinese Traditional', 'complete' => false], ], 'transactionTypesByWhat' => [ 'expenses' => ['Withdrawal'], From 48f4cceb1952b0461b14116b50f1cd9b2961e091 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 10:47:09 +0200 Subject: [PATCH 88/94] Mention new version. --- config/firefly.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/firefly.php b/config/firefly.php index f8bfde0a4e..8e0587da2a 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -4,7 +4,7 @@ declare(strict_types = 1); return [ 'chart' => 'chartjs', - 'version' => '3.9.1', + 'version' => '3.10', 'csv_import_enabled' => true, 'maxUploadSize' => 5242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], From 015935ed5587159d6aca993af52e075434289330 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 12:55:52 +0200 Subject: [PATCH 89/94] Implemented ABN Amro specific import code. --- app/Import/Setup/CsvSetup.php | 34 +++- app/Import/Specifics/AbnAmroDescription.php | 192 +++++++++++++++++++- 2 files changed, 212 insertions(+), 14 deletions(-) diff --git a/app/Import/Setup/CsvSetup.php b/app/Import/Setup/CsvSetup.php index 5dc009b90e..cb656e37f5 100644 --- a/app/Import/Setup/CsvSetup.php +++ b/app/Import/Setup/CsvSetup.php @@ -16,6 +16,7 @@ use ExpandedForm; use FireflyIII\Crud\Account\AccountCrud; use FireflyIII\Import\Mapper\MapperInterface; use FireflyIII\Import\MapperPreProcess\PreProcessorInterface; +use FireflyIII\Import\Specifics\SpecificInterface; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; @@ -191,7 +192,7 @@ class CsvSetup implements SetupInterface { /** @var AccountCrud $repository */ $repository = app(AccountCrud::class, [auth()->user()]); - $importId = $data['csv_import_account'] ?? 0; + $importId = $data['csv_import_account'] ?? 0; $account = $repository->find(intval($importId)); $hasHeaders = isset($data['has_headers']) && intval($data['has_headers']) === 1 ? true : false; @@ -199,6 +200,7 @@ class CsvSetup implements SetupInterface $config['has-headers'] = $hasHeaders; $config['date-format'] = $data['date_format']; $config['delimiter'] = $data['csv_delimiter']; + $config['delimiter'] = $config['delimiter'] === 'tab' ? "\t" : $config['delimiter']; Log::debug('Entered import account.', ['id' => $importId]); @@ -355,11 +357,22 @@ class CsvSetup implements SetupInterface // in order to actually map we also need all possible values from the CSV file. $content = $this->job->uploadFileContents(); /** @var Reader $reader */ - $reader = Reader::createFromString($content); + $reader = Reader::createFromString($content); $reader->setDelimiter($config['delimiter']); $results = $reader->fetch(); foreach ($results as $rowIndex => $row) { + + // run specifics here: + // and this is the point where the specifix go to work. + foreach ($config['specifics'] as $name => $enabled) { + /** @var SpecificInterface $specific */ + $specific = app('FireflyIII\Import\Specifics\\' . $name); + + // it returns the row, possibly modified: + $row = $specific->run($row); + } + //do something here foreach ($indexes as $index) { // this is simply 1, 2, 3, etc. $value = $row[$index]; @@ -409,12 +422,23 @@ class CsvSetup implements SetupInterface // create CSV reader. $reader = Reader::createFromString($content); $reader->setDelimiter($config['delimiter']); - $start = $config['has-headers'] ? 1 : 0; - $end = $start + self::EXAMPLE_ROWS; // first X rows + $start = $config['has-headers'] ? 1 : 0; + $end = $start + self::EXAMPLE_ROWS; // first X rows // collect example data in $data['columns'] while ($start < $end) { $row = $reader->fetchOne($start); + + // run specifics here: + // and this is the point where the specifix go to work. + foreach ($config['specifics'] as $name => $enabled) { + /** @var SpecificInterface $specific */ + $specific = app('FireflyIII\Import\Specifics\\' . $name); + + // it returns the row, possibly modified: + $row = $specific->run($row); + } + foreach ($row as $index => $value) { $value = trim($value); if (strlen($value) > 0) { @@ -422,7 +446,7 @@ class CsvSetup implements SetupInterface } } $start++; - $data['columnCount'] = count($row); + $data['columnCount'] = count($row) > $data['columnCount'] ? count($row) : $data['columnCount']; } // make unique example data diff --git a/app/Import/Specifics/AbnAmroDescription.php b/app/Import/Specifics/AbnAmroDescription.php index f6787e11f5..33408e7c77 100644 --- a/app/Import/Specifics/AbnAmroDescription.php +++ b/app/Import/Specifics/AbnAmroDescription.php @@ -14,18 +14,17 @@ namespace FireflyIII\Import\Specifics; /** * Class AbnAmroDescription * + * Parses the description from txt files for ABN AMRO bank accounts. + * + * Based on the logic as described in the following Gist: + * https://gist.github.com/vDorst/68d555a6a90f62fec004 + * * @package FireflyIII\Import\Specifics */ class AbnAmroDescription implements SpecificInterface { - - /** - * @return string - */ - static public function getName(): string - { - return 'ABN Amro description'; - } + /** @var array */ + public $row; /** * @return string @@ -35,6 +34,14 @@ class AbnAmroDescription implements SpecificInterface return 'Fixes possible problems with ABN Amro descriptions.'; } + /** + * @return string + */ + static public function getName(): string + { + return 'ABN Amro description'; + } + /** * @param array $row * @@ -42,6 +49,173 @@ class AbnAmroDescription implements SpecificInterface */ public function run(array $row): array { - return $row; + $this->row = $row; + // Try to parse the description in known formats. + $parsed = $this->parseSepaDescription() || $this->parseTRTPDescription() || $this->parseGEABEADescription() || $this->parseABNAMRODescription(); + + // If the description could not be parsed, specify an unknown opposing + // account, as an opposing account is required + if (!$parsed) { + $this->row[7] = trans('firefly.unknown'); // opposing-account-name + } + + return $this->row; } + + /** + * Parses the current description with costs from ABN AMRO itself + * + * @return bool true if the description is GEA/BEA-format, false otherwise + */ + protected function parseABNAMRODescription() + { + // See if the current description is formatted in ABN AMRO format + if (preg_match('/ABN AMRO.{24} (.*)/', $this->row[7], $matches)) { + + $this->row[8] = 'ABN AMRO'; // this one is new (opposing account name) + $this->row[7] = $matches[1]; // this is the description + + return true; + } + + return false; + } + + /** + * Parses the current description in GEA/BEA format + * + * @return bool true if the description is GEA/BEAformat, false otherwise + */ + protected function parseGEABEADescription() + { + // See if the current description is formatted in GEA/BEA format + if (preg_match('/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/', $this->row[7], $matches)) { + + // description and opposing account will be the same. + $this->row[8] = $matches[4]; // 'opposing-account-name' + $this->row[7] = $matches[4]; // 'description' + + if ($matches[1] == 'GEA') { + $this->row[7] = 'GEA ' . $matches[4]; // 'description' + } + + return true; + } + + return false; + } + + /** + * Parses the current description in SEPA format + * + * @return bool true if the description is SEPA format, false otherwise + */ + protected function parseSepaDescription() + { + // See if the current description is formatted as a SEPA plain description + if (preg_match('/^SEPA(.{28})/', $this->row[7], $matches)) { + + $type = $matches[1]; + $reference = ''; + $name = ''; + $newDescription = ''; + + // SEPA plain descriptions contain several key-value pairs, split by a colon + preg_match_all('/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9._#-]+(?=\s|$))/', $this->row[7], $matches, PREG_SET_ORDER); + + if (is_array($matches)) { + foreach ($matches as $match) { + $key = $match[1]; + $value = trim($match[2]); + switch (strtoupper($key)) { + case 'OMSCHRIJVING': + $newDescription = $value; + break; + case 'NAAM': + $this->row[8] = $value; + $name = $value; + break; + case 'KENMERK': + $reference = $value; + break; + case 'IBAN': + $this->row[9] = $value; + break; + default: + // Ignore the rest + } + } + } + + // Set a new description for the current transaction. If none was given + // set the description to type, name and reference + $this->row[7] = $newDescription; + if (strlen($newDescription) === 0) { + $this->row[7] = sprintf('%s - %s (%s)', $type, $name, $reference); + } + + return true; + } + + return false; + } + + /** + * Parses the current description in TRTP format + * + * @return bool true if the description is TRTP format, false otherwise + */ + protected function parseTRTPDescription() + { + // See if the current description is formatted in TRTP format + if (preg_match_all('!\/([A-Z]{3,4})\/([^/]*)!', $this->row[7], $matches, PREG_SET_ORDER)) { + + $type = ''; + $name = ''; + $reference = ''; + $newDescription = ''; + + // Search for properties specified in the TRTP format. If no description + // is provided, use the type, name and reference as new description + if (is_array($matches)) { + foreach ($matches as $match) { + $key = $match[1]; + $value = trim($match[2]); + + switch (strtoupper($key)) { + case 'NAME': + $this->row[8] = $name = $value; + break; + case 'REMI': + $newDescription = $value; + break; + case 'IBAN': + $this->row[9] = $value; + break; + case 'EREF': + $reference = $value; + break; + case 'TRTP': + $type = $value; + break; + default: + // Ignore the rest + } + } + + // Set a new description for the current transaction. If none was given + // set the description to type, name and reference + $this->row[7] = $newDescription; + if (strlen($newDescription) === 0) { + $this->row[7] = sprintf('%s - %s (%s)', $type, $name, $reference); + } + } + + return true; + } + + return false; + } + + } \ No newline at end of file From 955306d8778acffb33c2e65ff94b40e69d1d172d Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 14:50:25 +0200 Subject: [PATCH 90/94] Remove travis file and fix NULL error in sqlite. --- .travis.yml | 22 ---------------------- app/Support/Migration/TestData.php | 9 +++++---- 2 files changed, 5 insertions(+), 26 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 61763a7648..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: php -sudo: false -php: - - 7 - -install: - - phpenv config-rm xdebug.ini - - composer selfupdate - - rm composer.lock - - composer update --no-scripts - - php artisan clear-compiled - - php artisan optimize - - php artisan env - - mv -v .env.testing .env - - php artisan env - - touch storage/upload/at-1.data - - touch storage/upload/at-2.data - - touch storage/database/testing.db - - php artisan migrate --seed - -script: - - phpunit diff --git a/app/Support/Migration/TestData.php b/app/Support/Migration/TestData.php index 5c87f63003..3d39cc3215 100644 --- a/app/Support/Migration/TestData.php +++ b/app/Support/Migration/TestData.php @@ -810,10 +810,11 @@ class TestData foreach ($this->data['users'] as $user) { $insert[] = [ - 'created_at' => $this->time, - 'updated_at' => $this->time, - 'email' => $user['email'], - 'password' => bcrypt($user['password']), + 'created_at' => $this->time, + 'updated_at' => $this->time, + 'email' => $user['email'], + 'remember_token' => '', + 'password' => bcrypt($user['password']), ]; } From 5c4d010bded0db2f6344985da9b06ca13cf28b3c Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 15:10:03 +0200 Subject: [PATCH 91/94] Code cleanup. --- app/Console/Commands/EncryptFile.php | 2 +- app/Console/Commands/Import.php | 9 +- app/Crud/Account/AccountCrud.php | 4 +- app/Crud/Account/AccountCrudInterface.php | 2 +- app/Crud/Split/Journal.php | 2 +- app/Crud/Split/JournalInterface.php | 2 +- app/Export/Collector/AttachmentCollector.php | 4 +- app/Export/Collector/UploadCollector.php | 2 - app/Export/Entry/EntryAccount.php | 2 +- app/Export/Entry/EntryBill.php | 2 +- app/Export/Entry/EntryBudget.php | 2 +- app/Export/Entry/EntryCategory.php | 2 +- .../Events/BudgetLimitEventHandler.php | 2 +- app/Handlers/Events/UserSaveIpAddress.php | 2 +- .../Controllers/Admin/DomainController.php | 2 +- app/Http/Controllers/Admin/UserController.php | 4 +- app/Http/Controllers/ImportController.php | 7 +- .../Transaction/MassController.php | 2 +- .../Transaction/SplitController.php | 2 +- app/Http/Middleware/Range.php | 4 +- app/Http/Requests/SplitJournalFormRequest.php | 2 +- app/Http/breadcrumbs.php | 2 +- app/Import/Converter/AccountId.php | 2 +- app/Import/Converter/Amount.php | 2 +- app/Import/Converter/AssetAccountIban.php | 2 +- app/Import/Converter/AssetAccountName.php | 2 +- app/Import/Converter/AssetAccountNumber.php | 2 +- app/Import/Converter/BasicConverter.php | 2 +- app/Import/Converter/BillId.php | 2 +- app/Import/Converter/BillName.php | 2 +- app/Import/Converter/BudgetId.php | 2 +- app/Import/Converter/BudgetName.php | 2 +- app/Import/Converter/CategoryId.php | 2 +- app/Import/Converter/CategoryName.php | 2 +- app/Import/Converter/ConverterInterface.php | 2 +- app/Import/Converter/CurrencyCode.php | 2 +- app/Import/Converter/CurrencyId.php | 2 +- app/Import/Converter/CurrencyName.php | 2 +- app/Import/Converter/CurrencySymbol.php | 2 +- app/Import/Converter/Date.php | 2 +- app/Import/Converter/Description.php | 2 +- app/Import/Converter/ExternalId.php | 2 +- app/Import/Converter/INGDebetCredit.php | 2 +- app/Import/Converter/Ignore.php | 2 +- app/Import/Converter/OpposingAccountIban.php | 2 +- app/Import/Converter/OpposingAccountName.php | 2 +- .../Converter/OpposingAccountNumber.php | 2 +- app/Import/Converter/RabobankDebetCredit.php | 2 +- app/Import/Converter/SomeConverter.php | 31 --- app/Import/Converter/TagsComma.php | 2 +- app/Import/Converter/TagsSpace.php | 2 +- app/Import/ImportEntry.php | 22 +- app/Import/ImportResult.php | 2 +- app/Import/ImportStorage.php | 2 +- app/Import/ImportValidator.php | 2 +- app/Import/Importer/CsvImporter.php | 4 +- app/Import/Importer/ImporterInterface.php | 2 +- app/Import/Logging/CommandHandler.php | 2 +- app/Import/Mapper/AssetAccountIbans.php | 2 +- app/Import/Mapper/AssetAccounts.php | 2 +- app/Import/Mapper/Bills.php | 2 +- app/Import/Mapper/Budgets.php | 2 +- app/Import/Mapper/Categories.php | 2 +- app/Import/Mapper/OpposingAccountIbans.php | 2 +- app/Import/Mapper/OpposingAccounts.php | 2 +- app/Import/Mapper/Tags.php | 2 +- app/Import/Mapper/TransactionCurrencies.php | 2 +- .../PreProcessorInterface.php | 2 +- app/Import/MapperPreProcess/TagsComma.php | 2 +- app/Import/MapperPreProcess/TagsSpace.php | 2 +- app/Import/Role/Map.php | 2 +- app/Import/Role/Role.php | 2 +- app/Import/Setup/CsvSetup.php | 2 +- app/Import/Setup/SetupInterface.php | 2 +- app/Import/Specifics/AbnAmroDescription.php | 6 +- app/Import/Specifics/RabobankDescription.php | 6 +- app/Import/Specifics/SpecificInterface.php | 6 +- app/Models/ImportJob.php | 2 +- app/Providers/CrudServiceProvider.php | 2 - .../Category/CategoryRepository.php | 6 - .../ImportJob/ImportJobRepository.php | 2 +- .../ImportJobRepositoryInterface.php | 2 +- app/Support/Models/TagSupport.php | 2 +- config/csv.php | 199 ------------------ .../2016_06_16_000002_create_main_tables.php | 1 - phpunit.xml | 0 resources/lang/en_US/csv.php | 2 +- resources/lang/fr_FR/csv.php | 2 +- resources/lang/nl_NL/csv.php | 2 +- resources/lang/pt_BR/csv.php | 2 +- resources/lang/zh-HK/csv.php | 2 +- resources/lang/zh-TW/csv.php | 2 +- resources/views/form/options.twig | 2 +- resources/views/piggy-banks/add-mobile.twig | 2 +- 94 files changed, 106 insertions(+), 363 deletions(-) delete mode 100644 app/Import/Converter/SomeConverter.php mode change 100755 => 100644 phpunit.xml diff --git a/app/Console/Commands/EncryptFile.php b/app/Console/Commands/EncryptFile.php index 3d68deacf5..25ebf9cf10 100644 --- a/app/Console/Commands/EncryptFile.php +++ b/app/Console/Commands/EncryptFile.php @@ -51,7 +51,7 @@ class EncryptFile extends Command */ public function handle() { - $file = $this->argument('file'); + $file = e($this->argument('file')); if (!file_exists($file)) { $this->error(sprintf('File "%s" does not seem to exist.', $file)); diff --git a/app/Console/Commands/Import.php b/app/Console/Commands/Import.php index 7f92e063ab..3d70737e7e 100644 --- a/app/Console/Commands/Import.php +++ b/app/Console/Commands/Import.php @@ -19,7 +19,6 @@ use FireflyIII\Import\Logging\CommandHandler; use FireflyIII\Models\ImportJob; use Illuminate\Console\Command; use Log; -use Monolog\Handler\StreamHandler; /** * Class Import @@ -73,7 +72,11 @@ class Import extends Command } $this->line('Going to import job with key "' . $job->key . '" of type ' . $job->file_type); - $class = config('firefly.import_formats.' . $job->file_type); + $valid = array_keys(config('firefly.import_formats')); + $class = 'INVALID'; + if (in_array($job->file_type, $valid)) { + $class = config('firefly.import_formats.' . $job->file_type); + } /** @var ImporterInterface $importer */ $importer = app($class); @@ -98,7 +101,7 @@ class Import extends Command $cleaned = $validator->clean(); // then import collection: - $storage = new ImportStorage($collection); + $storage = new ImportStorage($cleaned); $storage->setUser($job->user); // and run store routine: diff --git a/app/Crud/Account/AccountCrud.php b/app/Crud/Account/AccountCrud.php index 552d7e0f5f..803f88f3c4 100644 --- a/app/Crud/Account/AccountCrud.php +++ b/app/Crud/Account/AccountCrud.php @@ -119,7 +119,7 @@ class AccountCrud implements AccountCrudInterface */ public function findByIban(string $iban, array $types): Account { - $query = $this->user->accounts()->where('iban', '!=', ""); + $query = $this->user->accounts()->where('iban', '!=', ''); if (count($types) > 0) { $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); @@ -538,4 +538,4 @@ class AccountCrud implements AccountCrudInterface return true; } -} \ No newline at end of file +} diff --git a/app/Crud/Account/AccountCrudInterface.php b/app/Crud/Account/AccountCrudInterface.php index 95e794418d..460f6628e6 100644 --- a/app/Crud/Account/AccountCrudInterface.php +++ b/app/Crud/Account/AccountCrudInterface.php @@ -106,4 +106,4 @@ interface AccountCrudInterface * @return Account */ public function updateAccountType(Account $account, string $type): Account; -} \ No newline at end of file +} diff --git a/app/Crud/Split/Journal.php b/app/Crud/Split/Journal.php index cbb4e34c35..94eda45c5b 100644 --- a/app/Crud/Split/Journal.php +++ b/app/Crud/Split/Journal.php @@ -213,4 +213,4 @@ class Journal implements JournalInterface } -} \ No newline at end of file +} diff --git a/app/Crud/Split/JournalInterface.php b/app/Crud/Split/JournalInterface.php index 81c852398d..15793101f2 100644 --- a/app/Crud/Split/JournalInterface.php +++ b/app/Crud/Split/JournalInterface.php @@ -44,4 +44,4 @@ interface JournalInterface * @return TransactionJournal */ public function updateJournal(TransactionJournal $journal, array $data): TransactionJournal; -} \ No newline at end of file +} diff --git a/app/Export/Collector/AttachmentCollector.php b/app/Export/Collector/AttachmentCollector.php index 35a4a0ebb8..f3c9e83516 100644 --- a/app/Export/Collector/AttachmentCollector.php +++ b/app/Export/Collector/AttachmentCollector.php @@ -83,10 +83,10 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface /** @var TransactionJournal $journal */ $journal = $attachment->attachable; $args = [ - 'attachment_name' => $attachment->filename, + 'attachment_name' => e($attachment->filename), 'attachment_id' => $attachment->id, 'type' => strtolower($journal->transactionType->type), - 'description' => $journal->description, + 'description' => e($journal->description), 'journal_id' => $journal->id, 'date' => $journal->date->formatLocalized(strval(trans('config.month_and_day'))), 'amount' => Amount::formatJournal($journal, false), diff --git a/app/Export/Collector/UploadCollector.php b/app/Export/Collector/UploadCollector.php index 3841a52a9d..e161f89ece 100644 --- a/app/Export/Collector/UploadCollector.php +++ b/app/Export/Collector/UploadCollector.php @@ -45,8 +45,6 @@ class UploadCollector extends BasicCollector implements CollectorInterface // make storage: $this->uploadDisk = Storage::disk('upload'); $this->exportDisk = Storage::disk('export'); - - // todo needs work for new importer (potentially collect other types as well) $this->expected = 'csv-upload-' . Auth::user()->id . '-'; } diff --git a/app/Export/Entry/EntryAccount.php b/app/Export/Entry/EntryAccount.php index 4a63c466a5..da23d80b68 100644 --- a/app/Export/Entry/EntryAccount.php +++ b/app/Export/Entry/EntryAccount.php @@ -44,4 +44,4 @@ class EntryAccount $this->type = $account->accountType->type; $this->number = $account->getMeta('accountNumber'); } -} \ No newline at end of file +} diff --git a/app/Export/Entry/EntryBill.php b/app/Export/Entry/EntryBill.php index 67d48af854..696fb58985 100644 --- a/app/Export/Entry/EntryBill.php +++ b/app/Export/Entry/EntryBill.php @@ -38,4 +38,4 @@ class EntryBill } } -} \ No newline at end of file +} diff --git a/app/Export/Entry/EntryBudget.php b/app/Export/Entry/EntryBudget.php index 730de35406..0ca9d38ae5 100644 --- a/app/Export/Entry/EntryBudget.php +++ b/app/Export/Entry/EntryBudget.php @@ -38,4 +38,4 @@ class EntryBudget } } -} \ No newline at end of file +} diff --git a/app/Export/Entry/EntryCategory.php b/app/Export/Entry/EntryCategory.php index 18d19f0642..8a8de50dfa 100644 --- a/app/Export/Entry/EntryCategory.php +++ b/app/Export/Entry/EntryCategory.php @@ -37,4 +37,4 @@ class EntryCategory $this->name = $category->name; } } -} \ No newline at end of file +} diff --git a/app/Handlers/Events/BudgetLimitEventHandler.php b/app/Handlers/Events/BudgetLimitEventHandler.php index 5fb0439b2f..d7eb7327c4 100644 --- a/app/Handlers/Events/BudgetLimitEventHandler.php +++ b/app/Handlers/Events/BudgetLimitEventHandler.php @@ -104,4 +104,4 @@ class BudgetLimitEventHandler } } -} \ No newline at end of file +} diff --git a/app/Handlers/Events/UserSaveIpAddress.php b/app/Handlers/Events/UserSaveIpAddress.php index 5b34ebc6da..7dcbf11394 100644 --- a/app/Handlers/Events/UserSaveIpAddress.php +++ b/app/Handlers/Events/UserSaveIpAddress.php @@ -59,4 +59,4 @@ class UserSaveIpAddress return true; } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Admin/DomainController.php b/app/Http/Controllers/Admin/DomainController.php index ce52f103ff..3c86f45708 100644 --- a/app/Http/Controllers/Admin/DomainController.php +++ b/app/Http/Controllers/Admin/DomainController.php @@ -133,4 +133,4 @@ class DomainController extends Controller return $filtered; } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 08d4663e06..c55b586188 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -28,10 +28,12 @@ class UserController extends Controller /** * @param User $user + * + * @return int */ public function edit(User $user) { - + return $user->id; } diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index d216aa5eb1..bf1d796aa6 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -45,8 +45,7 @@ class ImportController extends Controller * * @param ImportJob $job * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector - * @throws FireflyException + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View */ public function complete(ImportJob $job) { @@ -54,7 +53,6 @@ class ImportController extends Controller if (!$this->jobInCorrectStep($job, 'complete')) { return $this->redirectToCorrectStep($job); } - $importer = $this->makeImporter($job); $subTitle = trans('firefy.import_complete'); $subTitleIcon = 'fa-star'; @@ -305,14 +303,11 @@ class ImportController extends Controller case 'configure': case 'process': return $job->status === 'import_status_never_started'; - break; case 'settings': case 'store-settings': return $job->status === 'import_configuration_saved'; - break; case 'complete': return $job->status === 'settings_complete'; - break; } return false; diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index c402817eaf..ec44c1ad11 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -230,4 +230,4 @@ class MassController extends Controller return redirect(session('transactions.mass-edit.url')); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Transaction/SplitController.php b/app/Http/Controllers/Transaction/SplitController.php index ee63c6a2ed..43e3d2e821 100644 --- a/app/Http/Controllers/Transaction/SplitController.php +++ b/app/Http/Controllers/Transaction/SplitController.php @@ -358,4 +358,4 @@ class SplitController extends Controller return $return; } -} \ No newline at end of file +} diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 8470f1e38c..91e6f4b9aa 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -107,6 +107,8 @@ class Range $ranges['next'] = [$nextStart->format('Y-m-d'), $nextEnd->format('Y-m-d')]; switch ($viewRange) { + default: + throw new FireflyException('The date picker does not yet support "' . $viewRange . '".'); case '1D': $format = (string)trans('config.month_and_day'); break; @@ -122,8 +124,6 @@ class Range case '1M': $format = (string)trans('config.month'); break; - default: - throw new FireflyException('The date picker does not yet support "' . $viewRange . '".'); case '1W': $format = (string)trans('config.week_in_year'); break; diff --git a/app/Http/Requests/SplitJournalFormRequest.php b/app/Http/Requests/SplitJournalFormRequest.php index 67ff3b438b..2a333258e6 100644 --- a/app/Http/Requests/SplitJournalFormRequest.php +++ b/app/Http/Requests/SplitJournalFormRequest.php @@ -97,4 +97,4 @@ class SplitJournalFormRequest extends Request 'piggy_bank_id.*' => 'between:1,255', ]; } -} \ No newline at end of file +} diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index e977635219..381920a156 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -599,4 +599,4 @@ Breadcrumbs::register( $breadcrumbs->parent('transactions.index', $what); $breadcrumbs->push(trans('breadcrumbs.create_' . e($what)), route('split.journal.create', [$what])); } -); \ No newline at end of file +); diff --git a/app/Import/Converter/AccountId.php b/app/Import/Converter/AccountId.php index 7fd0b56cf2..1f07603f63 100644 --- a/app/Import/Converter/AccountId.php +++ b/app/Import/Converter/AccountId.php @@ -62,4 +62,4 @@ class AccountId extends BasicConverter implements ConverterInterface return new Account; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/Amount.php b/app/Import/Converter/Amount.php index 3b8db1f12f..1e071652c0 100644 --- a/app/Import/Converter/Amount.php +++ b/app/Import/Converter/Amount.php @@ -62,4 +62,4 @@ class Amount extends BasicConverter implements ConverterInterface return round(floatval($value), 4); } -} \ No newline at end of file +} diff --git a/app/Import/Converter/AssetAccountIban.php b/app/Import/Converter/AssetAccountIban.php index a22022db3b..97f1932e14 100644 --- a/app/Import/Converter/AssetAccountIban.php +++ b/app/Import/Converter/AssetAccountIban.php @@ -81,4 +81,4 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface return $account; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/AssetAccountName.php b/app/Import/Converter/AssetAccountName.php index a8e7ef27a6..f20cd7b798 100644 --- a/app/Import/Converter/AssetAccountName.php +++ b/app/Import/Converter/AssetAccountName.php @@ -84,4 +84,4 @@ class AssetAccountName extends BasicConverter implements ConverterInterface } -} \ No newline at end of file +} diff --git a/app/Import/Converter/AssetAccountNumber.php b/app/Import/Converter/AssetAccountNumber.php index 241ebf0a3a..cbad7b39a5 100644 --- a/app/Import/Converter/AssetAccountNumber.php +++ b/app/Import/Converter/AssetAccountNumber.php @@ -90,4 +90,4 @@ class AssetAccountNumber extends BasicConverter implements ConverterInterface return $account; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/BasicConverter.php b/app/Import/Converter/BasicConverter.php index d93590c81c..dbe7b56663 100644 --- a/app/Import/Converter/BasicConverter.php +++ b/app/Import/Converter/BasicConverter.php @@ -80,4 +80,4 @@ class BasicConverter { $this->user = $user; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/BillId.php b/app/Import/Converter/BillId.php index 74ccedc513..edb8817a10 100644 --- a/app/Import/Converter/BillId.php +++ b/app/Import/Converter/BillId.php @@ -70,4 +70,4 @@ class BillId extends BasicConverter implements ConverterInterface return new Bill; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/BillName.php b/app/Import/Converter/BillName.php index a159ab4a3a..2c9eb92f42 100644 --- a/app/Import/Converter/BillName.php +++ b/app/Import/Converter/BillName.php @@ -89,4 +89,4 @@ class BillName extends BasicConverter implements ConverterInterface } -} \ No newline at end of file +} diff --git a/app/Import/Converter/BudgetId.php b/app/Import/Converter/BudgetId.php index 7d18d58de4..aa6448cb89 100644 --- a/app/Import/Converter/BudgetId.php +++ b/app/Import/Converter/BudgetId.php @@ -69,4 +69,4 @@ class BudgetId extends BasicConverter implements ConverterInterface return new Budget; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/BudgetName.php b/app/Import/Converter/BudgetName.php index bee601b682..8611e7dd2e 100644 --- a/app/Import/Converter/BudgetName.php +++ b/app/Import/Converter/BudgetName.php @@ -72,4 +72,4 @@ class BudgetName extends BasicConverter implements ConverterInterface return $budget; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/CategoryId.php b/app/Import/Converter/CategoryId.php index 1a2d3fc45c..47e5d7f965 100644 --- a/app/Import/Converter/CategoryId.php +++ b/app/Import/Converter/CategoryId.php @@ -68,4 +68,4 @@ class CategoryId extends BasicConverter implements ConverterInterface return new Category; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/CategoryName.php b/app/Import/Converter/CategoryName.php index 8d09d271b8..383a1f907d 100644 --- a/app/Import/Converter/CategoryName.php +++ b/app/Import/Converter/CategoryName.php @@ -72,4 +72,4 @@ class CategoryName extends BasicConverter implements ConverterInterface return $category; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/ConverterInterface.php b/app/Import/Converter/ConverterInterface.php index ac127f1275..e474587a33 100644 --- a/app/Import/Converter/ConverterInterface.php +++ b/app/Import/Converter/ConverterInterface.php @@ -51,4 +51,4 @@ interface ConverterInterface * @param User $user */ public function setUser(User $user); -} \ No newline at end of file +} diff --git a/app/Import/Converter/CurrencyCode.php b/app/Import/Converter/CurrencyCode.php index 3e78e80d80..e49bea2e2f 100644 --- a/app/Import/Converter/CurrencyCode.php +++ b/app/Import/Converter/CurrencyCode.php @@ -63,4 +63,4 @@ class CurrencyCode extends BasicConverter implements ConverterInterface return $currency; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/CurrencyId.php b/app/Import/Converter/CurrencyId.php index 2dd402dafe..59b6e2e13f 100644 --- a/app/Import/Converter/CurrencyId.php +++ b/app/Import/Converter/CurrencyId.php @@ -67,4 +67,4 @@ class CurrencyId extends BasicConverter implements ConverterInterface return new TransactionCurrency; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/CurrencyName.php b/app/Import/Converter/CurrencyName.php index 7babdef1c4..e2976852a5 100644 --- a/app/Import/Converter/CurrencyName.php +++ b/app/Import/Converter/CurrencyName.php @@ -72,4 +72,4 @@ class CurrencyName extends BasicConverter implements ConverterInterface return $currency; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/CurrencySymbol.php b/app/Import/Converter/CurrencySymbol.php index 83b86dded2..2855f2bd48 100644 --- a/app/Import/Converter/CurrencySymbol.php +++ b/app/Import/Converter/CurrencySymbol.php @@ -72,4 +72,4 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface return $currency; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/Date.php b/app/Import/Converter/Date.php index e5ccc28e57..e873f9c128 100644 --- a/app/Import/Converter/Date.php +++ b/app/Import/Converter/Date.php @@ -46,4 +46,4 @@ class Date extends BasicConverter implements ConverterInterface $this->setCertainty(100); return $date; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/Description.php b/app/Import/Converter/Description.php index a8ce132873..d7a2cde245 100644 --- a/app/Import/Converter/Description.php +++ b/app/Import/Converter/Description.php @@ -34,4 +34,4 @@ class Description extends BasicConverter implements ConverterInterface return strval($value); } -} \ No newline at end of file +} diff --git a/app/Import/Converter/ExternalId.php b/app/Import/Converter/ExternalId.php index 272dc5b7d7..5f8c493fed 100644 --- a/app/Import/Converter/ExternalId.php +++ b/app/Import/Converter/ExternalId.php @@ -34,4 +34,4 @@ class ExternalId extends BasicConverter implements ConverterInterface return strval(trim($value)); } -} \ No newline at end of file +} diff --git a/app/Import/Converter/INGDebetCredit.php b/app/Import/Converter/INGDebetCredit.php index 378fad1130..2e3ca15dbd 100644 --- a/app/Import/Converter/INGDebetCredit.php +++ b/app/Import/Converter/INGDebetCredit.php @@ -42,4 +42,4 @@ class INGDebetCredit extends BasicConverter implements ConverterInterface return 1; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/Ignore.php b/app/Import/Converter/Ignore.php index 24a4895155..e96b5fabb1 100644 --- a/app/Import/Converter/Ignore.php +++ b/app/Import/Converter/Ignore.php @@ -29,4 +29,4 @@ class Ignore extends BasicConverter implements ConverterInterface return null; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/OpposingAccountIban.php b/app/Import/Converter/OpposingAccountIban.php index bf227412da..4fc2a1cc9d 100644 --- a/app/Import/Converter/OpposingAccountIban.php +++ b/app/Import/Converter/OpposingAccountIban.php @@ -73,4 +73,4 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface return $account; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/OpposingAccountName.php b/app/Import/Converter/OpposingAccountName.php index 973a8b4da5..d5fe3fb7d6 100644 --- a/app/Import/Converter/OpposingAccountName.php +++ b/app/Import/Converter/OpposingAccountName.php @@ -82,4 +82,4 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface return $account; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/OpposingAccountNumber.php b/app/Import/Converter/OpposingAccountNumber.php index cb97af5cf0..ede513646a 100644 --- a/app/Import/Converter/OpposingAccountNumber.php +++ b/app/Import/Converter/OpposingAccountNumber.php @@ -82,4 +82,4 @@ class OpposingAccountNumber extends BasicConverter implements ConverterInterface return $account; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/RabobankDebetCredit.php b/app/Import/Converter/RabobankDebetCredit.php index 3908b3a062..2cd9e2ed98 100644 --- a/app/Import/Converter/RabobankDebetCredit.php +++ b/app/Import/Converter/RabobankDebetCredit.php @@ -42,4 +42,4 @@ class RabobankDebetCredit extends BasicConverter implements ConverterInterface return 1; } -} \ No newline at end of file +} diff --git a/app/Import/Converter/SomeConverter.php b/app/Import/Converter/SomeConverter.php deleted file mode 100644 index 3b6d3b31c1..0000000000 --- a/app/Import/Converter/SomeConverter.php +++ /dev/null @@ -1,31 +0,0 @@ -defaultImportAccount = new Account; /** @var string $value */ foreach ($this->validFields as $value) { $this->fields[$value] = null; @@ -63,21 +62,6 @@ class ImportEntry } } - /** - * @return ImportResult - */ - public function import(): ImportResult - { - - $validation = $this->validate(); - - if ($validation->valid()) { - return $this->doImport(); - } - - return $validation; - } - /** * @param string $role * @param int $certainty @@ -91,14 +75,11 @@ class ImportEntry default: Log::error('Import entry cannot handle object.', ['role' => $role]); throw new FireflyException('Import entry cannot handle object of type "' . $role . '".'); - break; - case 'amount': /* * Easy enough. */ $this->setFloat('amount', $convertedValue, $certainty); - $this->applyMultiplier('amount'); // if present. return; @@ -147,7 +128,7 @@ class ImportEntry case 'sepa-ct-id': case 'sepa-db': case 'sepa-ct-op': - case'description': + case 'description': $this->setAppendableString('description', $convertedValue); break; case '_ignore': @@ -160,6 +141,7 @@ class ImportEntry case 'tags-comma': case 'tags-space': $this->appendCollection('tags', $convertedValue); + break; case 'external-id': $this->externalID = $convertedValue; break; diff --git a/app/Import/ImportResult.php b/app/Import/ImportResult.php index e7be183e85..afc68bee82 100644 --- a/app/Import/ImportResult.php +++ b/app/Import/ImportResult.php @@ -165,4 +165,4 @@ class ImportResult } -} \ No newline at end of file +} diff --git a/app/Import/ImportStorage.php b/app/Import/ImportStorage.php index 0f2b45414b..b789363e8e 100644 --- a/app/Import/ImportStorage.php +++ b/app/Import/ImportStorage.php @@ -187,4 +187,4 @@ class ImportStorage } -} \ No newline at end of file +} diff --git a/app/Import/ImportValidator.php b/app/Import/ImportValidator.php index 596252a10c..af55598b53 100644 --- a/app/Import/ImportValidator.php +++ b/app/Import/ImportValidator.php @@ -386,4 +386,4 @@ class ImportValidator } -} \ No newline at end of file +} diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index ff9845dd5c..3ce6694247 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -86,6 +86,8 @@ class CsvImporter implements ImporterInterface // create import object. This is where each entry ends up. $object = new ImportEntry; + Log::debug(sprintf('Now at row %d', $index)); + // set some vars: $object->setUser($this->job->user); $config = $this->job->configuration; @@ -129,4 +131,4 @@ class CsvImporter implements ImporterInterface return $object; } -} \ No newline at end of file +} diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php index 08fbb591fe..91dcbd3a7d 100644 --- a/app/Import/Importer/ImporterInterface.php +++ b/app/Import/Importer/ImporterInterface.php @@ -32,4 +32,4 @@ interface ImporterInterface * */ public function setJob(ImportJob $job); -} \ No newline at end of file +} diff --git a/app/Import/Logging/CommandHandler.php b/app/Import/Logging/CommandHandler.php index eeab715e22..c629a00e49 100644 --- a/app/Import/Logging/CommandHandler.php +++ b/app/Import/Logging/CommandHandler.php @@ -82,4 +82,4 @@ class CommandHandler extends AbstractProcessingHandler break; } } -} \ No newline at end of file +} diff --git a/app/Import/Mapper/AssetAccountIbans.php b/app/Import/Mapper/AssetAccountIbans.php index d3a4c246e6..8ff105ff76 100644 --- a/app/Import/Mapper/AssetAccountIbans.php +++ b/app/Import/Mapper/AssetAccountIbans.php @@ -53,4 +53,4 @@ class AssetAccountIbans implements MapperInterface return $list; } -} \ No newline at end of file +} diff --git a/app/Import/Mapper/AssetAccounts.php b/app/Import/Mapper/AssetAccounts.php index fdf0a64daa..21e4007361 100644 --- a/app/Import/Mapper/AssetAccounts.php +++ b/app/Import/Mapper/AssetAccounts.php @@ -50,4 +50,4 @@ class AssetAccounts implements MapperInterface return $list; } -} \ No newline at end of file +} diff --git a/app/Import/Mapper/Bills.php b/app/Import/Mapper/Bills.php index 0dcdc06f10..b1629d4865 100644 --- a/app/Import/Mapper/Bills.php +++ b/app/Import/Mapper/Bills.php @@ -43,4 +43,4 @@ class Bills implements MapperInterface return $list; } -} \ No newline at end of file +} diff --git a/app/Import/Mapper/Budgets.php b/app/Import/Mapper/Budgets.php index a1b9c2edd8..fad817ed32 100644 --- a/app/Import/Mapper/Budgets.php +++ b/app/Import/Mapper/Budgets.php @@ -44,4 +44,4 @@ class Budgets implements MapperInterface return $list; } -} \ No newline at end of file +} diff --git a/app/Import/Mapper/Categories.php b/app/Import/Mapper/Categories.php index 0f85392c8f..784e748848 100644 --- a/app/Import/Mapper/Categories.php +++ b/app/Import/Mapper/Categories.php @@ -44,4 +44,4 @@ class Categories implements MapperInterface return $list; } -} \ No newline at end of file +} diff --git a/app/Import/Mapper/OpposingAccountIbans.php b/app/Import/Mapper/OpposingAccountIbans.php index f788345483..2e1b7c8511 100644 --- a/app/Import/Mapper/OpposingAccountIbans.php +++ b/app/Import/Mapper/OpposingAccountIbans.php @@ -58,4 +58,4 @@ class OpposingAccountIbans implements MapperInterface return $list; } -} \ No newline at end of file +} diff --git a/app/Import/Mapper/OpposingAccounts.php b/app/Import/Mapper/OpposingAccounts.php index 3f165e6a7a..37f67dccae 100644 --- a/app/Import/Mapper/OpposingAccounts.php +++ b/app/Import/Mapper/OpposingAccounts.php @@ -54,4 +54,4 @@ class OpposingAccounts implements MapperInterface return $list; } -} \ No newline at end of file +} diff --git a/app/Import/Mapper/Tags.php b/app/Import/Mapper/Tags.php index 4f608f2146..f38d1e0f08 100644 --- a/app/Import/Mapper/Tags.php +++ b/app/Import/Mapper/Tags.php @@ -43,4 +43,4 @@ class Tags implements MapperInterface return $list; } -} \ No newline at end of file +} diff --git a/app/Import/Mapper/TransactionCurrencies.php b/app/Import/Mapper/TransactionCurrencies.php index 6f21ebb355..ff6d4a1feb 100644 --- a/app/Import/Mapper/TransactionCurrencies.php +++ b/app/Import/Mapper/TransactionCurrencies.php @@ -39,4 +39,4 @@ class TransactionCurrencies implements MapperInterface return $list; } -} \ No newline at end of file +} diff --git a/app/Import/MapperPreProcess/PreProcessorInterface.php b/app/Import/MapperPreProcess/PreProcessorInterface.php index d577b90cc7..3f6187a1eb 100644 --- a/app/Import/MapperPreProcess/PreProcessorInterface.php +++ b/app/Import/MapperPreProcess/PreProcessorInterface.php @@ -25,4 +25,4 @@ interface PreProcessorInterface */ public function run(string $value): array; -} \ No newline at end of file +} diff --git a/app/Import/MapperPreProcess/TagsComma.php b/app/Import/MapperPreProcess/TagsComma.php index f9a45110db..19ec5f60a9 100644 --- a/app/Import/MapperPreProcess/TagsComma.php +++ b/app/Import/MapperPreProcess/TagsComma.php @@ -28,4 +28,4 @@ class TagsComma implements PreProcessorInterface { return explode(',', $value); } -} \ No newline at end of file +} diff --git a/app/Import/MapperPreProcess/TagsSpace.php b/app/Import/MapperPreProcess/TagsSpace.php index a3384de75a..c87108885d 100644 --- a/app/Import/MapperPreProcess/TagsSpace.php +++ b/app/Import/MapperPreProcess/TagsSpace.php @@ -27,4 +27,4 @@ class TagsSpace implements PreProcessorInterface { return explode(' ', $value); } -} \ No newline at end of file +} diff --git a/app/Import/Role/Map.php b/app/Import/Role/Map.php index ca5c173975..594578e97f 100644 --- a/app/Import/Role/Map.php +++ b/app/Import/Role/Map.php @@ -15,4 +15,4 @@ namespace FireflyIII\Import\Role; class Map { -} \ No newline at end of file +} diff --git a/app/Import/Role/Role.php b/app/Import/Role/Role.php index ec5246290c..a0781a18b7 100644 --- a/app/Import/Role/Role.php +++ b/app/Import/Role/Role.php @@ -15,4 +15,4 @@ namespace FireflyIII\Import\Role; class Role { -} \ No newline at end of file +} diff --git a/app/Import/Setup/CsvSetup.php b/app/Import/Setup/CsvSetup.php index cb656e37f5..9295183689 100644 --- a/app/Import/Setup/CsvSetup.php +++ b/app/Import/Setup/CsvSetup.php @@ -469,4 +469,4 @@ class CsvSetup implements SetupInterface } -} \ No newline at end of file +} diff --git a/app/Import/Setup/SetupInterface.php b/app/Import/Setup/SetupInterface.php index 9055c5f98e..b45c4337c0 100644 --- a/app/Import/Setup/SetupInterface.php +++ b/app/Import/Setup/SetupInterface.php @@ -82,4 +82,4 @@ interface SetupInterface * */ public function setJob(ImportJob $job); -} \ No newline at end of file +} diff --git a/app/Import/Specifics/AbnAmroDescription.php b/app/Import/Specifics/AbnAmroDescription.php index 33408e7c77..a98d36f728 100644 --- a/app/Import/Specifics/AbnAmroDescription.php +++ b/app/Import/Specifics/AbnAmroDescription.php @@ -29,7 +29,7 @@ class AbnAmroDescription implements SpecificInterface /** * @return string */ - static public function getDescription(): string + public static function getDescription(): string { return 'Fixes possible problems with ABN Amro descriptions.'; } @@ -37,7 +37,7 @@ class AbnAmroDescription implements SpecificInterface /** * @return string */ - static public function getName(): string + public static function getName(): string { return 'ABN Amro description'; } @@ -218,4 +218,4 @@ class AbnAmroDescription implements SpecificInterface } -} \ No newline at end of file +} diff --git a/app/Import/Specifics/RabobankDescription.php b/app/Import/Specifics/RabobankDescription.php index 7372ba3dd0..75e074acb9 100644 --- a/app/Import/Specifics/RabobankDescription.php +++ b/app/Import/Specifics/RabobankDescription.php @@ -23,7 +23,7 @@ class RabobankDescription implements SpecificInterface /** * @return string */ - static public function getDescription(): string + public static function getDescription(): string { return 'Fixes possible problems with Rabobank descriptions.'; } @@ -31,7 +31,7 @@ class RabobankDescription implements SpecificInterface /** * @return string */ - static public function getName(): string + public static function getName(): string { return 'Rabobank description'; } @@ -61,4 +61,4 @@ class RabobankDescription implements SpecificInterface return $row; } -} \ No newline at end of file +} diff --git a/app/Import/Specifics/SpecificInterface.php b/app/Import/Specifics/SpecificInterface.php index 847e930af9..b3e0b7d7df 100644 --- a/app/Import/Specifics/SpecificInterface.php +++ b/app/Import/Specifics/SpecificInterface.php @@ -21,12 +21,12 @@ interface SpecificInterface /** * @return string */ - static public function getName(): string; + public static function getName(): string; /** * @return string */ - static public function getDescription(): string; + public static function getDescription(): string; /** * @param array $row @@ -35,4 +35,4 @@ interface SpecificInterface */ public function run(array $row): array; -} \ No newline at end of file +} diff --git a/app/Models/ImportJob.php b/app/Models/ImportJob.php index 1f679e0d70..3b106bbbb2 100644 --- a/app/Models/ImportJob.php +++ b/app/Models/ImportJob.php @@ -28,7 +28,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property string $key * @property string $file_type * @property string $status - * @property string $configuration + * @property array $configuration * @property-read \FireflyIII\User $user * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereId($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereCreatedAt($value) diff --git a/app/Providers/CrudServiceProvider.php b/app/Providers/CrudServiceProvider.php index 815236b605..1eeacfa735 100644 --- a/app/Providers/CrudServiceProvider.php +++ b/app/Providers/CrudServiceProvider.php @@ -58,8 +58,6 @@ class CrudServiceProvider extends ServiceProvider if (!isset($arguments[0]) && !$app->auth->check()) { throw new FireflyException('There is no user present.'); } - // Log::debug('AccountCrud constructor, run with default arguments.', $arguments); - return app('FireflyIII\Crud\Account\AccountCrud', $arguments); } ); diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 380578e174..e0a662b355 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -444,12 +444,6 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function store(array $data): Category { - // TODO use validation, not this. - if (strlen($data['name']) > 200 || strlen($data['name']) === 0) { - - } - - $newCategory = Category::firstOrCreateEncrypted( [ 'user_id' => $data['user'], diff --git a/app/Repositories/ImportJob/ImportJobRepository.php b/app/Repositories/ImportJob/ImportJobRepository.php index 15c02b5e60..8badf97189 100644 --- a/app/Repositories/ImportJob/ImportJobRepository.php +++ b/app/Repositories/ImportJob/ImportJobRepository.php @@ -78,4 +78,4 @@ class ImportJobRepository implements ImportJobRepositoryInterface return $result; } -} \ No newline at end of file +} diff --git a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php index 24cdd2aca9..3d6c46b94d 100644 --- a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php +++ b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php @@ -33,4 +33,4 @@ interface ImportJobRepositoryInterface * @return ImportJob */ public function findByKey(string $key): ImportJob; -} \ No newline at end of file +} diff --git a/app/Support/Models/TagSupport.php b/app/Support/Models/TagSupport.php index 5af0e7c8fc..a0decf17e4 100644 --- a/app/Support/Models/TagSupport.php +++ b/app/Support/Models/TagSupport.php @@ -88,4 +88,4 @@ class TagSupport extends Model } -} \ No newline at end of file +} diff --git a/config/csv.php b/config/csv.php index d4561c8a1a..5644b0f60f 100644 --- a/config/csv.php +++ b/config/csv.php @@ -276,203 +276,4 @@ return [ 'field' => 'description', ], ], - - - /* - - - 'specifix' => [ - 'RabobankDescription', - 'AbnAmroDescription', - 'Dummy' - ], - 'post_processors' => [ - 'Description', - 'Amount', - 'Currency', - 'Bill', - 'OpposingAccount', // must be after Amount! - 'AssetAccount', - - ], - 'roles' => [ - '_ignore' => [ - 'mappable' => false, - 'converter' => 'Ignore', - 'field' => 'ignored', - ], - 'bill-id' => [ - 'mappable' => false, - 'field' => 'bill', - 'converter' => 'BillId', - 'mapper' => 'Bill', - ], - 'bill-name' => [ - 'mappable' => true, - 'converter' => 'BillName', - 'field' => 'bill', - 'mapper' => 'Bill', - ], - 'currency-id' => [ - 'mappable' => true, - 'converter' => 'CurrencyId', - 'field' => 'currency', - 'mapper' => 'TransactionCurrency' - ], - 'currency-name' => [ - 'mappable' => true, - 'converter' => 'CurrencyName', - 'field' => 'currency', - 'mapper' => 'TransactionCurrency' - ], - 'currency-code' => [ - 'mappable' => true, - 'converter' => 'CurrencyCode', - 'field' => 'currency', - 'mapper' => 'TransactionCurrency' - ], - 'currency-symbol' => [ - 'mappable' => true, - 'converter' => 'CurrencySymbol', - 'field' => 'currency', - 'mapper' => 'TransactionCurrency' - ], - 'description' => [ - 'mappable' => false, - 'converter' => 'Description', - 'field' => 'description', - ], - 'date-transaction' => [ - 'mappable' => false, - 'converter' => 'Date', - 'field' => 'date', - ], - 'date-rent' => [ - 'mappable' => false, - 'converter' => 'Date', - 'field' => 'date-rent', - ], - 'budget-id' => [ - 'mappable' => true, - 'converter' => 'BudgetId', - 'field' => 'budget', - 'mapper' => 'Budget', - ], - 'budget-name' => [ - 'mappable' => true, - 'converter' => 'BudgetName', - 'field' => 'budget', - 'mapper' => 'Budget', - ], - 'rabo-debet-credit' => [ - 'mappable' => false, - 'converter' => 'RabobankDebetCredit', - 'field' => 'amount-modifier', - ], - 'ing-debet-credit' => [ - 'mappable' => false, - 'converter' => 'INGDebetCredit', - 'field' => 'amount-modifier', - ], - 'category-id' => [ - 'mappable' => true, - 'converter' => 'CategoryId', - 'field' => 'category', - 'mapper' => 'Category', - ], - 'category-name' => [ - 'mappable' => true, - 'converter' => 'CategoryName', - 'field' => 'category', - 'mapper' => 'Category', - ], - 'tags-comma' => [ - 'mappable' => true, - 'field' => 'tags', - 'converter' => 'TagsComma', - 'mapper' => 'Tag', - ], - 'tags-space' => [ - 'mappable' => true, - 'field' => 'tags', - 'converter' => 'TagsSpace', - 'mapper' => 'Tag', - ], - 'account-id' => [ - 'mappable' => true, - 'mapper' => 'AssetAccount', - 'field' => 'asset-account-id', - 'converter' => 'AccountId' - ], - 'account-name' => [ - 'mappable' => true, - 'mapper' => 'AssetAccount', - 'field' => 'asset-account-name', - 'converter' => 'AssetAccountName' - ], - 'account-iban' => [ - 'mappable' => true, - 'converter' => 'AssetAccountIban', - 'field' => 'asset-account-iban', - 'mapper' => 'AssetAccount' - ], - 'account-number' => [ - 'mappable' => true, - 'converter' => 'AssetAccountNumber', - 'field' => 'asset-account-number', - 'mapper' => 'AssetAccount' - ], - 'opposing-id' => [ - 'mappable' => true, - 'field' => 'opposing-account-id', - 'converter' => 'OpposingAccountId', - 'mapper' => 'AnyAccount', - ], - 'opposing-name' => [ - 'mappable' => true, - 'field' => 'opposing-account-name', - 'converter' => 'OpposingAccountName', - 'mapper' => 'AnyAccount', - ], - 'opposing-iban' => [ - 'mappable' => true, - 'field' => 'opposing-account-iban', - 'converter' => 'OpposingAccountIban', - 'mapper' => 'AnyAccount', - ], - 'opposing-number' => [ - 'mappable' => true, - 'field' => 'opposing-account-number', - 'converter' => 'OpposingAccountNumber', - 'mapper' => 'AnyAccount', - ], - 'amount' => [ - 'mappable' => false, - 'converter' => 'Amount', - 'field' => 'amount', - ], - 'amount-comma-separated' => [ - 'mappable' => false, - 'converter' => 'AmountComma', - 'field' => 'amount', - ], - 'sepa-ct-id' => [ - 'mappable' => false, - 'converter' => 'Description', - 'field' => 'description', - ], - 'sepa-ct-op' => [ - 'mappable' => false, - 'converter' => 'Description', - 'field' => 'description', - ], - 'sepa-db' => [ - 'mappable' => false, - 'converter' => 'Description', - 'field' => 'description', - ], - ] - - - */ ]; diff --git a/database/migrations/2016_06_16_000002_create_main_tables.php b/database/migrations/2016_06_16_000002_create_main_tables.php index b46f633232..fb89335598 100644 --- a/database/migrations/2016_06_16_000002_create_main_tables.php +++ b/database/migrations/2016_06_16_000002_create_main_tables.php @@ -56,7 +56,6 @@ class CreateMainTables extends Migration $this->createBudgetTables(); $this->createCategoriesTable(); $this->createExportJobsTable(); - // $this->createImportJobsTable(); $this->createPreferencesTable(); $this->createRoleTable(); $this->createRuleTables(); diff --git a/phpunit.xml b/phpunit.xml old mode 100755 new mode 100644 diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index 858026dad6..fef82823e3 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (space separated)', 'column_account-number' => 'Asset account (account number)', 'column_opposing-number' => 'Opposing account (account number)', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/csv.php b/resources/lang/fr_FR/csv.php index 858026dad6..fef82823e3 100644 --- a/resources/lang/fr_FR/csv.php +++ b/resources/lang/fr_FR/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (space separated)', 'column_account-number' => 'Asset account (account number)', 'column_opposing-number' => 'Opposing account (account number)', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/csv.php b/resources/lang/nl_NL/csv.php index 5c87f2ee00..668cbb1748 100644 --- a/resources/lang/nl_NL/csv.php +++ b/resources/lang/nl_NL/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (spatiegescheiden)', 'column_account-number' => 'Betaalrekening (rekeningnummer)', 'column_opposing-number' => 'Tegenrekening (rekeningnummer)', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/csv.php b/resources/lang/pt_BR/csv.php index 858026dad6..fef82823e3 100644 --- a/resources/lang/pt_BR/csv.php +++ b/resources/lang/pt_BR/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (space separated)', 'column_account-number' => 'Asset account (account number)', 'column_opposing-number' => 'Opposing account (account number)', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh-HK/csv.php b/resources/lang/zh-HK/csv.php index 858026dad6..fef82823e3 100644 --- a/resources/lang/zh-HK/csv.php +++ b/resources/lang/zh-HK/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (space separated)', 'column_account-number' => 'Asset account (account number)', 'column_opposing-number' => 'Opposing account (account number)', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh-TW/csv.php b/resources/lang/zh-TW/csv.php index 858026dad6..fef82823e3 100644 --- a/resources/lang/zh-TW/csv.php +++ b/resources/lang/zh-TW/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (space separated)', 'column_account-number' => 'Asset account (account number)', 'column_opposing-number' => 'Opposing account (account number)', -]; \ No newline at end of file +]; diff --git a/resources/views/form/options.twig b/resources/views/form/options.twig index 4692b300b8..83212c903b 100644 --- a/resources/views/form/options.twig +++ b/resources/views/form/options.twig @@ -45,4 +45,4 @@
    -{% endif %} \ No newline at end of file +{% endif %} diff --git a/resources/views/piggy-banks/add-mobile.twig b/resources/views/piggy-banks/add-mobile.twig index 3a4854efe3..ed169cd487 100644 --- a/resources/views/piggy-banks/add-mobile.twig +++ b/resources/views/piggy-banks/add-mobile.twig @@ -36,4 +36,4 @@
    {{ Form.close|raw }} -{% endblock %} \ No newline at end of file +{% endblock %} From 2947ec00024facb9536f024e0c77d03291b41e97 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 15:27:44 +0200 Subject: [PATCH 92/94] Small optimisations. --- app/Console/Commands/EncryptFile.php | 4 +- app/Http/Controllers/ImportController.php | 5 +- .../2016_06_16_000002_create_main_tables.php | 188 +++++++++--------- 3 files changed, 102 insertions(+), 95 deletions(-) diff --git a/app/Console/Commands/EncryptFile.php b/app/Console/Commands/EncryptFile.php index 25ebf9cf10..1039c8ec80 100644 --- a/app/Console/Commands/EncryptFile.php +++ b/app/Console/Commands/EncryptFile.php @@ -51,7 +51,7 @@ class EncryptFile extends Command */ public function handle() { - $file = e($this->argument('file')); + $file = e(strval($this->argument('file'))); if (!file_exists($file)) { $this->error(sprintf('File "%s" does not seem to exist.', $file)); @@ -59,7 +59,7 @@ class EncryptFile extends Command } $content = file_get_contents($file); $content = Crypt::encrypt($content); - $newName = $this->argument('key') . '.upload'; + $newName = e($this->argument('key')) . '.upload'; $path = storage_path('upload') . '/' . $newName; file_put_contents($path, $content); diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index bf1d796aa6..2d10dc6c1f 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -168,7 +168,7 @@ class ImportController extends Controller // return redirect to settings. // this could loop until the user is done. - return redirect(route('import.settings', $job->key)); + return redirect(route('import.settings', [$job->key])); } /** @@ -345,17 +345,14 @@ class ImportController extends Controller Log::debug('Will redirect to configure()'); return redirect(route('import.configure', [$job->key])); - break; case 'import_configuration_saved': Log::debug('Will redirect to settings()'); return redirect(route('import.settings', [$job->key])); - break; case 'settings_complete': Log::debug('Will redirect to complete()'); return redirect(route('import.complete', [$job->key])); - break; } throw new FireflyException('Cannot redirect for job state ' . $job->status); diff --git a/database/migrations/2016_06_16_000002_create_main_tables.php b/database/migrations/2016_06_16_000002_create_main_tables.php index fb89335598..003bc03d2f 100644 --- a/database/migrations/2016_06_16_000002_create_main_tables.php +++ b/database/migrations/2016_06_16_000002_create_main_tables.php @@ -290,6 +290,103 @@ class CreateMainTables extends Migration } + private function createJournalTables() + { + if (!Schema::hasTable('transaction_journals')) { + Schema::create( + 'transaction_journals', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + + $table->integer('user_id', false, true); + $table->integer('transaction_type_id', false, true); + $table->integer('bill_id', false, true)->nullable(); + $table->integer('transaction_currency_id', false, true); + + $table->string('description', 1024); + + $table->date('date'); + $table->date('interest_date')->nullable(); + $table->date('book_date')->nullable(); + $table->date('process_date')->nullable(); + + $table->integer('order', false, true); + $table->integer('tag_count', false, true); + + $table->boolean('encrypted')->default(1); + $table->boolean('completed')->default(1); + + // links to other tables: + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('transaction_type_id')->references('id')->on('transaction_types')->onDelete('cascade'); + $table->foreign('bill_id')->references('id')->on('bills')->onDelete('set null'); + $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade'); + } + ); + } + + if (!Schema::hasTable('journal_meta')) { + Schema::create( + 'journal_meta', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('transaction_journal_id', false, true); + $table->string('name', 255); + $table->text('data'); + $table->string('hash', 64); + + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); + } + ); + } + } + + private function createMoreJournalTables() + { + if (!Schema::hasTable('tag_transaction_journal')) { + Schema::create( + 'tag_transaction_journal', function (Blueprint $table) { + $table->increments('id'); + $table->integer('tag_id', false, true); + $table->integer('transaction_journal_id', false, true); + + $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade'); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); + + + } + ); + } + + if (!Schema::hasTable('budget_transaction_journal')) { + Schema::create( + 'budget_transaction_journal', function (Blueprint $table) { + $table->increments('id'); + $table->integer('budget_id', false, true); + $table->integer('transaction_journal_id', false, true); + + $table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade'); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); + } + ); + } + + if (!Schema::hasTable('category_transaction_journal')) { + Schema::create( + 'category_transaction_journal', function (Blueprint $table) { + $table->increments('id'); + $table->integer('category_id', false, true); + $table->integer('transaction_journal_id', false, true); + + $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade'); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); + } + ); + } + + } + /** * */ @@ -497,96 +594,9 @@ class CreateMainTables extends Migration */ private function createTransactionTables() { + $this->createJournalTables(); + $this->createMoreJournalTables(); - if (!Schema::hasTable('transaction_journals')) { - Schema::create( - 'transaction_journals', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - - $table->integer('user_id', false, true); - $table->integer('transaction_type_id', false, true); - $table->integer('bill_id', false, true)->nullable(); - $table->integer('transaction_currency_id', false, true); - - $table->string('description', 1024); - - $table->date('date'); - $table->date('interest_date')->nullable(); - $table->date('book_date')->nullable(); - $table->date('process_date')->nullable(); - - $table->integer('order', false, true); - $table->integer('tag_count', false, true); - - $table->boolean('encrypted')->default(1); - $table->boolean('completed')->default(1); - - // links to other tables: - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - $table->foreign('transaction_type_id')->references('id')->on('transaction_types')->onDelete('cascade'); - $table->foreign('bill_id')->references('id')->on('bills')->onDelete('set null'); - $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade'); - } - ); - } - - if (!Schema::hasTable('journal_meta')) { - Schema::create( - 'journal_meta', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->integer('transaction_journal_id', false, true); - $table->string('name', 255); - $table->text('data'); - $table->string('hash', 64); - - $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - } - ); - } - - if (!Schema::hasTable('tag_transaction_journal')) { - Schema::create( - 'tag_transaction_journal', function (Blueprint $table) { - $table->increments('id'); - $table->integer('tag_id', false, true); - $table->integer('transaction_journal_id', false, true); - - $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade'); - $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - - - } - ); - } - - if (!Schema::hasTable('budget_transaction_journal')) { - Schema::create( - 'budget_transaction_journal', function (Blueprint $table) { - $table->increments('id'); - $table->integer('budget_id', false, true); - $table->integer('transaction_journal_id', false, true); - - $table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade'); - $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - } - ); - } - - if (!Schema::hasTable('category_transaction_journal')) { - Schema::create( - 'category_transaction_journal', function (Blueprint $table) { - $table->increments('id'); - $table->integer('category_id', false, true); - $table->integer('transaction_journal_id', false, true); - - $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade'); - $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); - } - ); - } if (!Schema::hasTable('piggy_bank_events')) { Schema::create( From ffb699cb06c9c89583a517e94f5eaeca2546aa61 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 15:34:15 +0200 Subject: [PATCH 93/94] Clean up code. --- app/Import/ImportStorage.php | 191 +++++++++++++++++++++++------------ 1 file changed, 124 insertions(+), 67 deletions(-) diff --git a/app/Import/ImportStorage.php b/app/Import/ImportStorage.php index b789363e8e..4ebcc5b8ce 100644 --- a/app/Import/ImportStorage.php +++ b/app/Import/ImportStorage.php @@ -80,44 +80,13 @@ class ImportStorage } /** - * @param int $index - * @param ImportEntry $entry + * @param $entry * + * @return array * @throws FireflyException */ - private function storeSingle(int $index, ImportEntry $entry) + private function storeAccounts($entry): array { - if ($entry->valid === false) { - Log::error(sprintf('Cannot import row %d, because valid=false', $index)); - - return; - } - - Log::debug(sprintf('Going to store row %d', $index)); - $billId = is_null($entry->fields['bill']) ? null : $entry->fields['bill']->id; - $journalData = [ - 'user_id' => $entry->user->id, - 'transaction_type_id' => $entry->fields['transaction-type']->id, - 'bill_id' => $billId, - 'transaction_currency_id' => $entry->fields['currency']->id, - 'description' => $entry->fields['description'], - 'date' => $entry->fields['date-transaction'], - 'interest_date' => $entry->fields['date-interest'], - 'book_date' => $entry->fields['date-book'], - 'process_date' => $entry->fields['date-process'], - 'completed' => 0, - ]; - /** @var TransactionJournal $journal */ - $journal = TransactionJournal::create($journalData); - - foreach ($journal->getErrors()->all() as $err) { - Log::error($err); - } - - $amount = $this->makePositive($entry->fields['amount']); - - Log::debug('Created journal', ['id' => $journal->id]); - // then create transactions. Single ones, unfortunately. switch ($entry->fields['transaction-type']->type) { default: @@ -143,47 +112,135 @@ class ImportStorage break; } - // create new transactions. This is something that needs a rewrite for multiple/split transactions. - $sourceData = [ - 'account_id' => $source->id, - 'transaction_journal_id' => $journal->id, - 'description' => $journalData['description'], - 'amount' => bcmul($amount, '-1'), + return [ + 'source' => $source, + 'destination' => $destination, ]; + } - $destinationData = [ - 'account_id' => $destination->id, - 'transaction_journal_id' => $journal->id, - 'description' => $journalData['description'], - 'amount' => $amount, - ]; - - $one = Transaction::create($sourceData); - $two = Transaction::create($destinationData); - Log::debug('Created transaction 1', ['id' => $one->id, 'account' => $one->account_id, 'account_name' => $source->name]); - Log::debug('Created transaction 2', ['id' => $two->id, 'account' => $two->account_id, 'account_name' => $destination->name]); - - $journal->completed = 1; - $journal->save(); - - // now attach budget and so on. - if (!is_null($entry->fields['budget']) && !is_null($entry->fields['budget']->id)) { - $journal->budgets()->save($entry->fields['budget']); - Log::debug('Attached budget', ['id' => $entry->fields['budget']->id, 'name' => $entry->fields['budget']->name]); - $journal->save(); - } - - if (!is_null($entry->fields['category']) && !is_null($entry->fields['category']->id)) { - $journal->categories()->save($entry->fields['category']); - Log::debug('Attached category', ['id' => $entry->fields['category']->id, 'name' => $entry->fields['category']->name]); - $journal->save(); - } + /** + * @param TransactionJournal $journal + * @param ImportEntry $entry + */ + private function storeBill(TransactionJournal $journal, ImportEntry $entry) + { if (!is_null($entry->fields['bill']) && !is_null($entry->fields['bill']->id)) { $journal->bill()->associate($entry->fields['bill']); Log::debug('Attached bill', ['id' => $entry->fields['bill']->id, 'name' => $entry->fields['bill']->name]); $journal->save(); } + } + + /** + * @param TransactionJournal $journal + * @param ImportEntry $entry + */ + private function storeBudget(TransactionJournal $journal, ImportEntry $entry) + { + if (!is_null($entry->fields['budget']) && !is_null($entry->fields['budget']->id)) { + $journal->budgets()->save($entry->fields['budget']); + Log::debug('Attached budget', ['id' => $entry->fields['budget']->id, 'name' => $entry->fields['budget']->name]); + $journal->save(); + } + + } + + /** + * @param TransactionJournal $journal + * @param ImportEntry $entry + */ + private function storeCategory(TransactionJournal $journal, ImportEntry $entry) + { + if (!is_null($entry->fields['category']) && !is_null($entry->fields['category']->id)) { + $journal->categories()->save($entry->fields['category']); + Log::debug('Attached category', ['id' => $entry->fields['category']->id, 'name' => $entry->fields['category']->name]); + $journal->save(); + } + } + + /** + * @param $entry + * + * @return TransactionJournal + */ + private function storeJournal($entry): TransactionJournal + { + $billId = is_null($entry->fields['bill']) ? null : $entry->fields['bill']->id; + $journalData = [ + 'user_id' => $entry->user->id, + 'transaction_type_id' => $entry->fields['transaction-type']->id, + 'bill_id' => $billId, + 'transaction_currency_id' => $entry->fields['currency']->id, + 'description' => $entry->fields['description'], + 'date' => $entry->fields['date-transaction'], + 'interest_date' => $entry->fields['date-interest'], + 'book_date' => $entry->fields['date-book'], + 'process_date' => $entry->fields['date-process'], + 'completed' => 0, + ]; + /** @var TransactionJournal $journal */ + $journal = TransactionJournal::create($journalData); + + foreach ($journal->getErrors()->all() as $err) { + Log::error($err); + } + Log::debug('Created journal', ['id' => $journal->id]); + + return $journal; + } + + /** + * @param int $index + * @param ImportEntry $entry + * + * @return ImportResult + * @throws FireflyException + */ + private function storeSingle(int $index, ImportEntry $entry): ImportResult + { + if ($entry->valid === false) { + Log::error(sprintf('Cannot import row %d, because valid=false', $index)); + $result = new ImportResult(); + $result->failed(); + $result->appendError(sprintf('Cannot import row %d, because valid=false', $index)); + + return $result; + } + Log::debug(sprintf('Going to store row %d', $index)); + + + $journal = $this->storeJournal($entry); + $amount = $this->makePositive($entry->fields['amount']); + $accounts = $this->storeAccounts($entry); + + // create new transactions. This is something that needs a rewrite for multiple/split transactions. + $sourceData = [ + 'account_id' => $accounts['source']->id, + 'transaction_journal_id' => $journal->id, + 'description' => $journal->description, + 'amount' => bcmul($amount, '-1'), + ]; + + $destinationData = [ + 'account_id' => $accounts['destination']->id, + 'transaction_journal_id' => $journal->id, + 'description' => $journal->description, + 'amount' => $amount, + ]; + + $one = Transaction::create($sourceData); + $two = Transaction::create($destinationData); + Log::debug('Created transaction 1', ['id' => $one->id, 'account' => $one->account_id, 'account_name' => $accounts['source']->name]); + Log::debug('Created transaction 2', ['id' => $two->id, 'account' => $two->account_id, 'account_name' => $destination->name]); + + $journal->completed = 1; + $journal->save(); + + // now attach budget and so on. + $this->storeBudget($journal, $entry); + $this->storeCategory($journal, $entry); + $this->storeBill($journal, $entry); } From 99b3e24836a3dcd2f4d26fb847b76e1d626270b0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 12 Aug 2016 15:50:52 +0200 Subject: [PATCH 94/94] Code optimalization --- app/Console/Commands/EncryptFile.php | 2 +- app/Http/Requests/SplitJournalFormRequest.php | 66 ++++++++++++------- app/Import/ImportStorage.php | 2 +- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/app/Console/Commands/EncryptFile.php b/app/Console/Commands/EncryptFile.php index 1039c8ec80..174cc3917e 100644 --- a/app/Console/Commands/EncryptFile.php +++ b/app/Console/Commands/EncryptFile.php @@ -59,7 +59,7 @@ class EncryptFile extends Command } $content = file_get_contents($file); $content = Crypt::encrypt($content); - $newName = e($this->argument('key')) . '.upload'; + $newName = e(strval($this->argument('key'))) . '.upload'; $path = storage_path('upload') . '/' . $newName; file_put_contents($path, $content); diff --git a/app/Http/Requests/SplitJournalFormRequest.php b/app/Http/Requests/SplitJournalFormRequest.php index 2a333258e6..7e49abb331 100644 --- a/app/Http/Requests/SplitJournalFormRequest.php +++ b/app/Http/Requests/SplitJournalFormRequest.php @@ -49,25 +49,9 @@ class SplitJournalFormRequest extends Request 'interest_date' => $this->get('interest_date') ? new Carbon($this->get('interest_date')) : null, 'book_date' => $this->get('book_date') ? new Carbon($this->get('book_date')) : null, 'process_date' => $this->get('process_date') ? new Carbon($this->get('process_date')) : null, - 'transactions' => [], + 'transactions' => $this->getTransactionData(), ]; - // description is leading because it is one of the mandatory fields. - foreach ($this->get('description') as $index => $description) { - $transaction = [ - 'description' => $description, - 'amount' => round($this->get('amount')[$index], 2), - 'budget_id' => $this->get('budget_id')[$index] ? intval($this->get('budget_id')[$index]) : 0, - 'category' => $this->get('category')[$index] ?? '', - 'source_account_id' => isset($this->get('source_account_id')[$index]) ? intval($this->get('source_account_id')[$index]) : intval($this->get('journal_source_account_id')), - 'source_account_name' => $this->get('source_account_name')[$index] ?? '', - 'piggy_bank_id' => isset($this->get('piggy_bank_id')[$index]) ? intval($this->get('piggy_bank_id')[$index]) : 0, - 'destination_account_id' => isset($this->get('destination_account_id')[$index]) ? intval($this->get('destination_account_id')[$index]) : intval($this->get('journal_destination_account_id')), - 'destination_account_name' => $this->get('destination_account_name')[$index] ?? '', - ]; - $data['transactions'][] = $transaction; - } - return $data; } @@ -87,14 +71,46 @@ class SplitJournalFormRequest extends Request 'interest_date' => 'date', 'book_date' => 'date', 'process_date' => 'date', - - 'description.*' => 'required|between:1,255', - 'destination_account_id.*' => 'numeric|belongsToUser:accounts,id', - 'destination_account_name.*' => 'between:1,255', - 'amount.*' => 'required|numeric', - 'budget_id.*' => 'belongsToUser:budgets,id', - 'category.*' => 'between:1,255', - 'piggy_bank_id.*' => 'between:1,255', + 'description.*' => 'required|between:1,255', + 'destination_account_id.*' => 'numeric|belongsToUser:accounts,id', + 'destination_account_name.*' => 'between:1,255', + 'amount.*' => 'required|numeric', + 'budget_id.*' => 'belongsToUser:budgets,id', + 'category.*' => 'between:1,255', + 'piggy_bank_id.*' => 'between:1,255', ]; } + + /** + * @return array + */ + private function getTransactionData(): array + { + $return = []; + // description is leading because it is one of the mandatory fields. + foreach ($this->get('description') as $index => $description) { + $transaction = [ + 'description' => $description, + 'amount' => round($this->get('amount')[$index], 2), + 'budget_id' => $this->get('budget_id')[$index] ? intval($this->get('budget_id')[$index]) : 0, + 'category' => $this->get('category')[$index] ?? '', + 'source_account_id' => isset($this->get('source_account_id')[$index]) + ? intval($this->get('source_account_id')[$index]) + : intval( + $this->get('journal_source_account_id') + ), + 'source_account_name' => $this->get('source_account_name')[$index] ?? '', + 'piggy_bank_id' => isset($this->get('piggy_bank_id')[$index]) ? intval($this->get('piggy_bank_id')[$index]) : 0, + 'destination_account_id' => isset($this->get('destination_account_id')[$index]) + ? intval($this->get('destination_account_id')[$index]) + : intval( + $this->get('journal_destination_account_id') + ), + 'destination_account_name' => $this->get('destination_account_name')[$index] ?? '', + ]; + $return[] = $transaction; + } + + return $return; + } } diff --git a/app/Import/ImportStorage.php b/app/Import/ImportStorage.php index 4ebcc5b8ce..e016e93d24 100644 --- a/app/Import/ImportStorage.php +++ b/app/Import/ImportStorage.php @@ -232,7 +232,7 @@ class ImportStorage $one = Transaction::create($sourceData); $two = Transaction::create($destinationData); Log::debug('Created transaction 1', ['id' => $one->id, 'account' => $one->account_id, 'account_name' => $accounts['source']->name]); - Log::debug('Created transaction 2', ['id' => $two->id, 'account' => $two->account_id, 'account_name' => $destination->name]); + Log::debug('Created transaction 2', ['id' => $two->id, 'account' => $two->account_id, 'account_name' => $accounts['destination']->name]); $journal->completed = 1; $journal->save();