diff --git a/app/Console/Commands/Export/ExportData.php b/app/Console/Commands/Export/ExportData.php index 6b59689527..c9a1e28145 100644 --- a/app/Console/Commands/Export/ExportData.php +++ b/app/Console/Commands/Export/ExportData.php @@ -19,27 +19,65 @@ * along with this program. If not, see . */ -namespace FireflyIII\Console\Commands; +namespace FireflyIII\Console\Commands\Export; +use Carbon\Carbon; +use FireflyIII\Console\Commands\VerifiesAccessToken; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\AccountType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Support\Export\ExportDataGenerator; +use FireflyIII\User; use Illuminate\Console\Command; +use Illuminate\Support\Collection; +use InvalidArgumentException; +use Log; /** * Class ExportData */ class ExportData extends Command { + use VerifiesAccessToken; + + /** @var JournalRepositoryInterface */ + private $journalRepository; + + /** @var AccountRepositoryInterface */ + private $accountRepository; + + /** @var User */ + private $user; /** * The console command description. * * @var string */ - protected $description = 'I export data.'; + protected $description = 'Command to export data from Firefly III.'; /** * The name and signature of the console command. * * @var string */ - protected $signature = 'firefly-iii:export-data'; + protected $signature = 'firefly-iii:export-data + {--user=1 : The user ID that the export should run for.} + {--token= : The user\'s access token.} + {--start= : First transaction to export. Defaults to your very first transaction. Only applies to transaction export.} + {--end= : Last transaction to export. Defaults to today. Only applies to transaction export.} + {--accounts= : From which accounts or liabilities to export. Only applies to transaction export. Defaults to all of your asset accounts.} + {--export_directory=./ : Where to store the export files.} + {--export-transactions : Create a file with all your transactions and their meta data. This flag and the other flags can be combined.} + {--export-accounts : Create a file with all your accounts and some meta data.} + {--export-budgets : Create a file with all your budgets and some meta data.} + {--export-categories : Create a file with all your categories and some meta data.} + {--export-tags : Create a file with all your tags and some meta data.} + {--export-recurring : Create a file with all your recurring transactions and some meta data.} + {--export-rules : Create a file with all your rules and some meta data.} + {--export-bills : Create a file with all your bills and some meta data.} + {--export-piggies : Create a file with all your piggy banks and some meta data.} + {--force : Force overwriting of previous exports if found.}'; + /** * Create a new command instance. @@ -54,10 +92,206 @@ class ExportData extends Command /** * Execute the console command. * - * @return mixed + * @return int + * @throws FireflyException */ - public function handle() + public function handle(): int { - // + // verify access token + if (!$this->verifyAccessToken()) { + $this->error('Invalid access token.'); + + return 1; + } + // set up repositories. + $this->stupidLaravel(); + $this->user = $this->getUser(); + $this->journalRepository->setUser($this->user); + $this->accountRepository->setUser($this->user); + // get the options. + try { + $options = $this->parseOptions(); + } catch (FireflyException $e) { + $this->error(sprintf('Could not work with your options: %s', $e)); + + return 1; + } + // make export object and configure it. + + /** @var ExportDataGenerator $exporter */ + $exporter = app(ExportDataGenerator::class); + $exporter->setUser($this->user); + $exporter->setStart($options['start']); + $exporter->setEnd($options['end']); + $exporter->setExportTransactions($options['export']['transactions']); + $exporter->setExportAccounts($options['export']['accounts']); + $exporter->setExportBudgets($options['export']['budgets']); + $exporter->setExportCategories($options['export']['categories']); + $exporter->setExportTags($options['export']['tags']); + $exporter->setExportRecurring($options['export']['recurring']); + $exporter->setExportRules($options['export']['rules']); + $exporter->setExportBills($options['export']['bills']); + $exporter->setExportPiggies($options['export']['piggies']); + + $data = $exporter->export(); + + try { + $this->exportData($options, $data); + } catch (FireflyException $e) { + $this->error(sprintf('Could not store data: %s', $e->getMessage())); + } + + return 0; } + + /** + * @param array $options + * @param array $data + * + * @throws FireflyException + */ + private function exportData(array $options, array $data): void + { + $date = date('Y_m_d'); + foreach ($data as $key => $content) { + $file = sprintf('%s%s_%s.csv', $options['directory'], $date, $key); + if (false === $options['force'] && file_exists($file)) { + throw new FireflyException(sprintf('File "%s" exists already. Use --force to overwrite.', $file)); + } + if (true === $options['force'] && file_exists($file)) { + $this->warn(sprintf('File "%s" exists already but will be replaced.', $file)); + } + // continue to write to file. + file_put_contents($file, $content); + $this->info(sprintf('Wrote %s-export to file "%s".', $key, $file)); + } + } + + /** + * @return Collection + * @throws FireflyException + */ + private function getAccountsParameter(): Collection + { + $final = new Collection; + $accounts = new Collection; + $accountList = $this->option('accounts'); + $types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; + if (null !== $accountList && '' !== (string)$accountList) { + $accountIds = explode(',', $accountList); + $accounts = $this->accountRepository->getAccountsById($accountIds); + } + if (null === $accountList) { + $accounts = $this->accountRepository->getAccountsByType($types); + } + // filter accounts, + /** @var AccountType $account */ + foreach ($accounts as $account) { + if (in_array($account->accountType->type, $types, true)) { + $final->push($account); + } + } + if (0 === $final->count()) { + throw new FireflyException('Ended up with zero valid accounts to export from.'); + } + + return $final; + } + + /** + * @param string $field + * + * @return Carbon + * @throws FireflyException + */ + private function getDateParameter(string $field): Carbon + { + $date = null; + if (null !== $this->option($field)) { + try { + $date = Carbon::createFromFormat('Y-m-d', $this->option($field)); + } catch (InvalidArgumentException $e) { + Log::error($e->getMessage()); + throw new FireflyException(sprintf('%s date "%s" must be formatted YYYY-MM-DD', $field, $this->option('start'))); + } + } + if (null === $date && 'start' === $field) { + $journal = $this->journalRepository->firstNull(); + $date = null === $journal ? Carbon::now()->subYear() : $date; + } + if (null === $date && 'end' === $field) { + $date = new Carbon; + } + if ('start' === $date) { + $date->startOfDay(); + } + if ('end' === $date) { + $date->endOfDay(); + } + + return $date; + } + + /** + * @return string + * @throws FireflyException + */ + private function getExportDirectory(): string + { + $directory = $this->option('export_directory'); + if (null === $directory) { + $directory = './'; + } + if (!is_writable($directory)) { + throw new FireflyException(sprintf('Directory "%s" isn\'t writeable.', $directory)); + } + + return $directory; + } + + /** + * @return array + * @throws FireflyException + */ + private function parseOptions(): array + { + $start = $this->getDateParameter('start'); + $end = $this->getDateParameter('end'); + $accounts = $this->getAccountsParameter(); + $export = $this->getExportDirectory(); + + return [ + 'export' => [ + 'transactions' => $this->option('export-transactions'), + 'accounts' => $this->option('export-accounts'), + 'budgets' => $this->option('export-budgets'), + 'categories' => $this->option('export-categories'), + 'tags' => $this->option('export-tags'), + 'recurring' => $this->option('export-recurring'), + 'rules' => $this->option('export-rules'), + 'bills' => $this->option('export-bills'), + 'piggies' => $this->option('export-piggies'), + ], + 'start' => $start, + 'end' => $end, + 'accounts' => $accounts, + 'directory' => $export, + 'force' => $this->option('force'), + ]; + } + + /** + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. + * + * @codeCoverageIgnore + */ + private function stupidLaravel(): void + { + $this->journalRepository = app(JournalRepositoryInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); + } + + } diff --git a/app/Http/Controllers/Export/IndexController.php b/app/Http/Controllers/Export/IndexController.php index 24cd52de0c..ae3a4f85e1 100644 --- a/app/Http/Controllers/Export/IndexController.php +++ b/app/Http/Controllers/Export/IndexController.php @@ -25,7 +25,7 @@ namespace FireflyIII\Http\Controllers\Export; use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; -use FireflyIII\Support\Export\ExportFileGenerator; +use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Http\Response as LaravelResponse; /** @@ -63,8 +63,11 @@ class IndexController extends Controller */ public function export() { - /** @var ExportFileGenerator $generator */ - $generator = app(ExportFileGenerator::class); + /** @var ExportDataGenerator $generator */ + $generator = app(ExportDataGenerator::class); + $generator->setUser(auth()->user()); + + $generator->setExportTransactions(true); // get first transaction in DB: $firstDate = new Carbon; diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php new file mode 100644 index 0000000000..4058fffc88 --- /dev/null +++ b/app/Support/Export/ExportDataGenerator.php @@ -0,0 +1,708 @@ +. + */ + +namespace FireflyIII\Support\Export; + +use Carbon\Carbon; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\Account; +use FireflyIII\Models\Bill; +use FireflyIII\Models\Budget; +use FireflyIII\Models\BudgetLimit; +use FireflyIII\Models\Category; +use FireflyIII\Models\PiggyBank; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Models\RecurrenceTransaction; +use FireflyIII\Models\Rule; +use FireflyIII\Models\RuleAction; +use FireflyIII\Models\RuleTrigger; +use FireflyIII\Models\Tag; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use FireflyIII\User; +use League\Csv\Writer; + +/** + * Class ExportDataGenerator + */ +class ExportDataGenerator +{ + /** @var Carbon */ + private $end; + /** @var bool */ + private $exportTransactions; + /** @var Carbon */ + private $start; + /** @var bool */ + private $exportAccounts; + /** @var bool */ + private $exportBudgets; + /** @var bool */ + private $exportCategories; + /** @var bool */ + private $exportTags; + /** @var bool */ + private $exportRecurring; + /** @var bool */ + private $exportRules; + /** @var bool */ + private $exportBills; + /** @var bool */ + private $exportPiggies; + + /** @var User */ + private $user; + + public function __construct() + { + $this->start = new Carbon; + $this->start->subYear(); + $this->end = new Carbon; + $this->exportTransactions = false; + $this->exportAccounts = false; + $this->exportBudgets = false; + $this->exportCategories = false; + $this->exportTags = false; + $this->exportRecurring = false; + $this->exportRules = false; + $this->exportBills = false; + $this->exportPiggies = false; + } + + /** + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + + /** + * @return array + * @throws \League\Csv\CannotInsertRecord + */ + public function export(): array + { + $return = []; + if ($this->exportAccounts) { + $return['accounts'] = $this->exportAccounts(); + } + if ($this->exportBills) { + $return['bills'] = $this->exportBills(); + } + if ($this->exportBudgets) { + $return['budgets'] = $this->exportBudgets(); + } + if ($this->exportCategories) { + $return['categories'] = $this->exportCategories(); + } + if ($this->exportPiggies) { + $return['piggies'] = $this->exportPiggies(); + } + if ($this->exportRecurring) { + $return['recurrences'] = $this->exportRecurring(); + } + if ($this->exportRules) { + $return['rules'] = $this->exportRules(); + } + if ($this->exportTags) { + $return['tags'] = $this->exportTags(); + } + if ($this->exportTransactions) { + $return['transactions'] = $this->exportTransactions(); + } + + return $return; + } + + /** + * @param bool $exportAccounts + */ + public function setExportAccounts(bool $exportAccounts): void + { + $this->exportAccounts = $exportAccounts; + } + + /** + * @param bool $exportBudgets + */ + public function setExportBudgets(bool $exportBudgets): void + { + $this->exportBudgets = $exportBudgets; + } + + /** + * @param bool $exportCategories + */ + public function setExportCategories(bool $exportCategories): void + { + $this->exportCategories = $exportCategories; + } + + /** + * @param bool $exportTags + */ + public function setExportTags(bool $exportTags): void + { + $this->exportTags = $exportTags; + } + + /** + * @param bool $exportRecurring + */ + public function setExportRecurring(bool $exportRecurring): void + { + $this->exportRecurring = $exportRecurring; + } + + /** + * @param bool $exportRules + */ + public function setExportRules(bool $exportRules): void + { + $this->exportRules = $exportRules; + } + + /** + * @param bool $exportBills + */ + public function setExportBills(bool $exportBills): void + { + $this->exportBills = $exportBills; + } + + /** + * @param bool $exportPiggies + */ + public function setExportPiggies(bool $exportPiggies): void + { + $this->exportPiggies = $exportPiggies; + } + + /** + * @param Carbon $end + */ + public function setEnd(Carbon $end): void + { + $this->end = $end; + } + + /** + * @param bool $exportTransactions + */ + public function setExportTransactions(bool $exportTransactions): void + { + $this->exportTransactions = $exportTransactions; + } + + /** + * @param Carbon $start + */ + public function setStart(Carbon $start): void + { + $this->start = $start; + } + + /** + * @return string + */ + private function exportRules(): string + { + $header = ['user_id', 'rule_id', 'row_contains', 'created_at', 'updated_at', 'group_id', 'group_name', 'title', 'description', 'order', 'active', + 'stop_processing', 'strict', 'trigger_type', 'trigger_value', 'trigger_order', 'trigger_active', 'trigger_stop_processing', 'action_type', + 'action_value', 'action_order', 'action_active', 'action_stop_processing',]; + $ruleRepos = app(RuleRepositoryInterface::class); + $ruleRepos->setUser($this->user); + $rules = $ruleRepos->getAll(); + $records = []; + /** @var Rule $rule */ + foreach ($rules as $rule) { + $records[] = [ + $this->user->id, $rule->id, 'rule', + $rule->created_at->toAtomString(), $rule->updated_at->toAtomString(), + $rule->ruleGroup->id, $rule->ruleGroup->name, + $rule->title, $rule->description, $rule->order, $rule->active, $rule->stop_processing, $rule->strict, + ]; + /** @var RuleTrigger $trigger */ + foreach ($rule->ruleTriggers as $trigger) { + $records[] = [ + $this->user->id, $rule->id, 'trigger', + null, null, + null, null, + null, null, null, null, null, null, + $trigger->trigger_type, $trigger->trigger_value, $trigger->order, $trigger->active, $trigger->stop_processing, + ]; + } + + /** @var RuleAction $action */ + foreach ($rule->ruleActions as $action) { + $records[] = [ + $this->user->id, $rule->id, 'action', + null, null, + null, null, + null, null, null, null, null, null, + null, null, null, null, null, + $action->action_type, $action->action_value, $action->order, $action->active, $action->stop_processing, + ]; + } + } + + //load the CSV document from a string + $csv = Writer::createFromString(''); + + //insert the header + $csv->insertOne($header); + + //insert all the records + $csv->insertAll($records); + + return $csv->getContent(); //returns the CSV document as a string + } + + /** + * @return string + */ + private function exportAccounts(): string + { + $header = ['user_id', 'account_id', 'created_at', 'updated_at', 'type', 'name', 'virtual_balance', 'iban', 'number', 'active', 'currency_code', 'role', + 'cc_type', 'cc_payment_date', 'in_net_worth', 'interest', 'interest_period',]; + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); + $accounts = $repository->getAccountsByType([]); + $records = []; + /** @var Account $account */ + foreach ($accounts as $account) { + $currency = $repository->getAccountCurrency($account); + $records[] = [ + $this->user->id, + $account->id, + $account->created_at->toAtomString(), + $account->updated_at->toAtomString(), + $account->accountType->type, + $account->name, + $account->virtual_balance, + $account->iban, + $account->account_number, + $account->active, + $currency ? $currency->code : null, + $repository->getMetaValue($account, 'account_role'), + $repository->getMetaValue($account, 'cc_type'), + $repository->getMetaValue($account, 'cc_monthly_payment_date'), + $repository->getMetaValue($account, 'include_net_worth'), + $repository->getMetaValue($account, 'interest'), + $repository->getMetaValue($account, 'interest_period'), + ]; + } + + //load the CSV document from a string + $csv = Writer::createFromString(''); + + //insert the header + $csv->insertOne($header); + + //insert all the records + $csv->insertAll($records); + + return $csv->getContent(); //returns the CSV document as a string + } + + /** + * @return string + */ + private function exportBills(): string + { + /** @var BillRepositoryInterface $repository */ + $repository = app(BillRepositoryInterface::class); + $repository->setUser($this->user); + $bills = $repository->getBills(); + $header = ['user_id', 'bill_id', 'created_at', 'updated_at', 'currency_code', 'name', 'amount_min', 'amount_max', 'date', 'repeat_freq', 'skip', + 'active',]; + $records = []; + + /** @var Bill $bill */ + foreach ($bills as $bill) { + $records[] = [ + $this->user->id, + $bill->id, + $bill->created_at->toAtomString(), + $bill->updated_at->toAtomString(), + $bill->transactionCurrency->code, + $bill->name, + $bill->amount_min, + $bill->amount_max, + $bill->date->format('Y-m-d'), + $bill->repeat_freq, + $bill->skip, + $bill->active, + ]; + } + + //load the CSV document from a string + $csv = Writer::createFromString(''); + + //insert the header + $csv->insertOne($header); + + //insert all the records + $csv->insertAll($records); + + return $csv->getContent(); //returns the CSV document as a string + } + + /** + * @return string + * @throws \League\Csv\CannotInsertRecord + */ + private function exportBudgets(): string + { + $header = [ + 'user_id', + 'budget_id', + 'name', + 'active', + 'order', + 'start_date', + 'end_date', + 'currency_code', + 'amount', + ]; + + $budgetRepos = app(BudgetRepositoryInterface::class); + $budgetRepos->setUser($this->user); + $limitRepos = app(BudgetLimitRepositoryInterface::class); + $budgets = $budgetRepos->getBudgets(); + $records = []; + /** @var Budget $budget */ + foreach ($budgets as $budget) { + $limits = $limitRepos->getBudgetLimits($budget); + /** @var BudgetLimit $limit */ + foreach ($limits as $limit) { + $records[] = [ + $this->user->id, + $budget->id, + $budget->name, + $budget->active, + $budget->order, + $limit->start_date->format('Y-m-d'), + $limit->end_date->format('Y-m-d'), + $limit->transactionCurrency->code, + $limit->amount, + ]; + } + } + + //load the CSV document from a string + $csv = Writer::createFromString(''); + + //insert the header + $csv->insertOne($header); + + //insert all the records + $csv->insertAll($records); + + return $csv->getContent(); //returns the CSV document as a string + + } + + /** + * @return string + */ + private function exportCategories(): string + { + $header = ['user_id', 'category_id', 'created_at', 'updated_at', 'name']; + + /** @var CategoryRepositoryInterface $catRepos */ + $catRepos = app(CategoryRepositoryInterface::class); + $catRepos->setUser($this->user); + + $records = []; + $categories = $catRepos->getCategories(); + + /** @var Category $category */ + foreach ($categories as $category) { + $records[] = [ + $this->user->id, + $category->id, + $category->created_at->toAtomString(), + $category->updated_at->toAtomString(), + $category->name, + ]; + } + + //load the CSV document from a string + $csv = Writer::createFromString(''); + + //insert the header + $csv->insertOne($header); + + //insert all the records + $csv->insertAll($records); + + return $csv->getContent(); //returns the CSV document as a string + } + + /** + * @return string + */ + private function exportPiggies(): string + { + /** @var PiggyBankRepositoryInterface $piggyRepos */ + $piggyRepos = app(PiggyBankRepositoryInterface::class); + $piggyRepos->setUser($this->user); + + /** @var AccountRepositoryInterface $accountRepos */ + $accountRepos = app(AccountRepositoryInterface::class); + $accountRepos->setUser($this->user); + + $header = ['user_id', 'piggy_bank_id', 'created_at', 'updated_at', 'account_name', 'account_type', 'name', + 'currency_code', 'target_amount', 'current_amount', 'start_date', 'target_date', 'order', + 'active']; + $records = []; + $piggies = $piggyRepos->getPiggyBanks(); + + /** @var PiggyBank $piggy */ + foreach ($piggies as $piggy) { + $repetition = $piggyRepos->getRepetition($piggy); + $currency = $accountRepos->getAccountCurrency($piggy->account); + $records[] = [ + $this->user->id, + $piggy->id, + $piggy->created_at->toAtomString(), + $piggy->updated_at->toAtomString(), + $piggy->account->name, + $piggy->account->accountType->type, + $piggy->name, + $currency ? $currency->code : null, + $piggy->targetamount, + $repetition ? $repetition->currentamount : null, + $piggy->startdate->format('Y-m-d'), + $piggy->targetdate ? $piggy->targetdate->format('Y-m-d') : null, + $piggy->order, + $piggy->active, + ]; + } + + //load the CSV document from a string + $csv = Writer::createFromString(''); + + //insert the header + $csv->insertOne($header); + + //insert all the records + $csv->insertAll($records); + + return $csv->getContent(); //returns the CSV document as a string + } + + /** + * @return string + */ + private function exportRecurring(): string + { + /** @var RecurringRepositoryInterface $recurringRepos */ + $recurringRepos = app(RecurringRepositoryInterface::class); + $recurringRepos->setUser($this->user); + $header = [ + // recurrence: + 'user_id', 'recurrence_id', 'row_contains', 'created_at', 'updated_at', 'type', 'title', 'description', 'first_date', 'repeat_until', + 'latest_date', 'repetitions', 'apply_rules', 'active', + + // repetition info: + 'type', 'moment', 'skip', 'weekend', + // transactions + meta: + 'currency_code', 'foreign_currency_code', 'source_name', 'source_type', 'destination_name', 'destination_type', 'amount', 'foreign_amount', + 'category', 'budget', 'piggy_bank', 'tags', + ]; + $records = []; + $recurrences = $recurringRepos->getAll(); + /** @var Recurrence $recurrence */ + foreach ($recurrences as $recurrence) { + // add recurrence: + $records[] = [ + $this->user->id, + $recurrence->id, + 'recurrence', + $recurrence->created_at->toAtomString(), + $recurrence->updated_at->toAtomString(), + $recurrence->transactionType->type, + $recurrence->title, + $recurrence->description, + $recurrence->first_date ? $recurrence->first_date->format('Y-m-d') : null, + $recurrence->repeat_until ? $recurrence->repeat_until->format('Y-m-d') : null, + $recurrence->latest_date ? $recurrence->repeat_until->format('Y-m-d') : null, + $recurrence->repetitions, + $recurrence->apply_rules, + $recurrence->active, + ]; + // add new row for each repetition + /** @var RecurrenceRepetition $repetition */ + foreach ($recurrence->recurrenceRepetitions as $repetition) { + $records[] = [ + // recurrence + $this->user->id, + $recurrence->id, 'repetition', null, null, null, null, null, null, null, null, null, null, null, + + // repetition: + $repetition->repetition_type, $repetition->repetition_moment, $repetition->repetition_skip, $repetition->weekend, + ]; + } + /** @var RecurrenceTransaction $transaction */ + foreach ($recurrence->recurrenceTransactions as $transaction) { + $categoryName = $recurringRepos->getCategory($transaction); + $budgetId = $recurringRepos->getBudget($transaction); + $piggyBankId = $recurringRepos->getPiggyBank($transaction); + $tags = $recurringRepos->getTags($transaction); + + $records[] = [ + // recurrence + $this->user->id, + $recurrence->id, 'transaction', null, null, null, null, null, null, null, null, null, null, null, + + // repetition: + null, null, null, null, + + // transaction: + $transaction->transactionCurrency->code, $transaction->foreignCurrency ? $transaction->foreignCurrency->code : null, + $transaction->sourceAccount->name, $transaction->sourceAccount->accountType->type, $transaction->destinationAccount->name, + $transaction->destinationAccount->accountType->type, $transaction->amount, $transaction->foreign_amount, + $categoryName, $budgetId, $piggyBankId, implode(',', $tags), + ]; + } + } + //load the CSV document from a string + $csv = Writer::createFromString(''); + + //insert the header + $csv->insertOne($header); + + //insert all the records + $csv->insertAll($records); + + return $csv->getContent(); //returns the CSV document as a string + } + + /** + * @return string + */ + private function exportTags(): string + { + $header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoom_level']; + + $tagRepos = app(TagRepositoryInterface::class); + $tagRepos->setUser($this->user); + $tags = $tagRepos->get(); + $records = []; + /** @var Tag $tag */ + foreach ($tags as $tag) { + $records[] = [ + $this->user->id, + $tag->id, + $tag->created_at->toAtomString(), + $tag->updated_at->toAtomString(), + $tag->tag, + $tag->date ? $tag->date->format('Y-m-d') : null, + $tag->description, + $tag->latitude, + $tag->longitude, + $tag->zoomLevel, + ]; + } + + //load the CSV document from a string + $csv = Writer::createFromString(''); + + //insert the header + $csv->insertOne($header); + + //insert all the records + $csv->insertAll($records); + + return $csv->getContent(); //returns the CSV document as a string + } + + /** + * @return string + */ + private function exportTransactions(): string + { + // TODO better place for keys? + $header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'amount', 'foreign_amount', 'currency_code', + 'foreign_currency_code', 'description', 'date', 'source_name', 'source_iban', 'source_type', 'destination_name', 'destination_iban', + 'destination_type', 'reconciled', 'category', 'budget', 'bill', 'tags',]; + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user); + $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation() + ->withBudgetInformation(); + $journals = $collector->getExtractedJournals(); + + $records = []; + /** @var array $journal */ + foreach ($journals as $journal) { + $records[] = [ + $journal['user_id'], + $journal['transaction_group_id'], + $journal['transaction_journal_id'], + $journal['created_at']->toAtomString(), + $journal['updated_at']->toAtomString(), + $journal['transaction_group_title'], + $journal['transaction_type_type'], + $journal['amount'], + $journal['foreign_amount'], + $journal['currency_code'], + $journal['foreign_currency_code'], + $journal['description'], + $journal['date']->toAtomString(), + $journal['source_account_name'], + $journal['source_account_iban'], + $journal['source_account_type'], + $journal['destination_account_name'], + $journal['destination_account_iban'], + $journal['destination_account_type'], + $journal['reconciled'], + $journal['category_name'], + $journal['budget_name'], + $journal['bill_name'], + implode(',', $journal['tags']), + ]; + } + + //load the CSV document from a string + $csv = Writer::createFromString(''); + + //insert the header + $csv->insertOne($header); + + //insert all the records + $csv->insertAll($records); + + return $csv->getContent(); //returns the CSV document as a string + } + +} diff --git a/app/Support/Export/ExportFileGenerator.php b/app/Support/Export/ExportFileGenerator.php deleted file mode 100644 index 1a947c1c38..0000000000 --- a/app/Support/Export/ExportFileGenerator.php +++ /dev/null @@ -1,166 +0,0 @@ -. - */ - -namespace FireflyIII\Support\Export; - -use Carbon\Carbon; -use FireflyIII\Helpers\Collector\GroupCollectorInterface; -use League\Csv\Writer; - -/** - * Class ExportFileGenerator - */ -class ExportFileGenerator -{ - /** @var Carbon */ - private $end; - /** @var bool */ - private $exportTransactions; - /** @var Carbon */ - private $start; - - public function __construct() - { - $this->start = new Carbon; - $this->start->subYear(); - $this->end = new Carbon; - $this->exportTransactions = true; - } - - /** - * @return array - */ - public function export(): array - { - $return = []; - if ($this->exportTransactions) { - $return['transactions'] = $this->exportTransactions(); - } - - return $return; - } - - /** - * @param Carbon $end - */ - public function setEnd(Carbon $end): void - { - $this->end = $end; - } - - /** - * @param bool $exportTransactions - */ - public function setExportTransactions(bool $exportTransactions): void - { - $this->exportTransactions = $exportTransactions; - } - - /** - * @param Carbon $start - */ - public function setStart(Carbon $start): void - { - $this->start = $start; - } - - /** - * @return string - */ - private function exportTransactions(): string - { - // TODO better place for keys? - $header = [ - 'user_id', - 'group_id', - 'journal_id', - 'created_at', - 'updated_at', - 'group_title', - 'type', - 'amount', - 'foreign_amount', - 'currency_code', - 'foreign_currency_code', - 'description', - 'date', - 'source_name', - 'source_iban', - 'source_type', - 'destination_name', - 'destination_iban', - 'destination_type', - 'reconciled', - 'category', - 'budget', - 'bill', - 'tags', - ]; - - $collector = app(GroupCollectorInterface::class); - $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation() - ->withBudgetInformation(); - $journals = $collector->getExtractedJournals(); - - $records = []; - /** @var array $journal */ - foreach ($journals as $journal) { - $records[] = [ - $journal['user_id'], - $journal['transaction_group_id'], - $journal['transaction_journal_id'], - $journal['created_at']->toAtomString(), - $journal['updated_at']->toAtomString(), - $journal['transaction_group_title'], - $journal['transaction_type_type'], - $journal['amount'], - $journal['foreign_amount'], - $journal['currency_code'], - $journal['foreign_currency_code'], - $journal['description'], - $journal['date']->toAtomString(), - $journal['source_account_name'], - $journal['source_account_iban'], - $journal['source_account_type'], - $journal['destination_account_name'], - $journal['destination_account_iban'], - $journal['destination_account_type'], - $journal['reconciled'], - $journal['category_name'], - $journal['budget_name'], - $journal['bill_name'], - implode(',', $journal['tags']), - ]; - } - - //load the CSV document from a string - $csv = Writer::createFromString(''); - - //insert the header - $csv->insertOne($header); - - //insert all the records - $csv->insertAll($records); - - return $csv->getContent(); //returns the CSV document as a string - } - -}