mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-23 22:35:03 +00:00
Basic export function for #2667
This commit is contained in:
63
app/Console/Commands/Export/ExportData.php
Normal file
63
app/Console/Commands/Export/ExportData.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* ExportData.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class ExportData
|
||||
*/
|
||||
class ExportData extends Command
|
||||
{
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'I export data.';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:export-data';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
109
app/Http/Controllers/Export/IndexController.php
Normal file
109
app/Http/Controllers/Export/IndexController.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* IndexController.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Export;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Support\Export\ExportFileGenerator;
|
||||
use Illuminate\Http\Response as LaravelResponse;
|
||||
|
||||
/**
|
||||
* Class IndexController
|
||||
*/
|
||||
class IndexController extends Controller
|
||||
{
|
||||
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepository;
|
||||
|
||||
/**
|
||||
* IndexController constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// translations:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('mainTitleIcon', 'fa-life-bouy');
|
||||
app('view')->share('title', (string)trans('firefly.export_data_title'));
|
||||
$this->journalRepository = app(JournalRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
/** @var ExportFileGenerator $generator */
|
||||
$generator = app(ExportFileGenerator::class);
|
||||
|
||||
// get first transaction in DB:
|
||||
$firstDate = new Carbon;
|
||||
$firstDate->subYear();
|
||||
$journal = $this->journalRepository->firstNull();
|
||||
if (null !== $journal) {
|
||||
$firstDate = clone $journal->date;
|
||||
}
|
||||
$generator->setStart($firstDate);
|
||||
$result = $generator->export();
|
||||
|
||||
$name = sprintf('%s_transaction_export.csv', date('Y_m_d'));
|
||||
$quoted = sprintf('"%s"', addcslashes($name, '"\\'));
|
||||
// headers for CSV file.
|
||||
/** @var LaravelResponse $response */
|
||||
$response = response($result['transactions'], 200);
|
||||
$response
|
||||
->header('Content-Description', 'File Transfer')
|
||||
->header('Content-Type', 'text/x-csv')
|
||||
->header('Content-Disposition', 'attachment; filename=' . $quoted)
|
||||
//->header('Content-Transfer-Encoding', 'binary')
|
||||
->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['transactions']));
|
||||
|
||||
return $response;
|
||||
|
||||
// return CSV file made from 'transactions' array.
|
||||
return $result['transactions'];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('export.index');
|
||||
}
|
||||
|
||||
}
|
166
app/Support/Export/ExportFileGenerator.php
Normal file
166
app/Support/Export/ExportFileGenerator.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* ExportFileGenerator.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
@@ -132,6 +132,9 @@ return [
|
||||
'single_user_mode' => true,
|
||||
'is_demo_site' => false,
|
||||
],
|
||||
'feature_flags' => [
|
||||
'export' => false,
|
||||
],
|
||||
'encryption' => null === env('USE_ENCRYPTION') || env('USE_ENCRYPTION') === true,
|
||||
'version' => '5.0.0-alpha.1',
|
||||
'api_version' => '1.0.0',
|
||||
|
@@ -613,6 +613,16 @@ return [
|
||||
'login_provider_local_only' => 'This action is not available when authenticating through ":login_provider".',
|
||||
'delete_local_info_only' => 'Because you authenticate through ":login_provider", this will only delete local Firefly III information.',
|
||||
|
||||
// export data:
|
||||
'import_and_export_menu' => 'Import and export',
|
||||
'export_data_title' => 'Export data from Firefly III',
|
||||
'export_data_menu' => 'Export data',
|
||||
'export_data_bc' => 'Export data from Firefly III',
|
||||
'export_data_main_title' => 'Export data from Firefly III',
|
||||
'export_data_expl' => 'This link allows you to export all transactions from Firefly III, with all the relevant meta-data. Please refer to the help (top right (?)-icon) for more information about the process.',
|
||||
'export_data_all_transactions' => 'Export all transactions',
|
||||
'export_data_advanced_expl' => 'If you want to have more advanced options, consult the command line options. More information is available in the help (top right (?)-icon) or consult the console command: <code>php artisan help firefly-iii:export-data</code>',
|
||||
|
||||
// attachments
|
||||
'nr_of_attachments' => 'One attachment|:count attachments',
|
||||
'attachments' => 'Attachments',
|
||||
|
35
resources/views/v1/export/index.twig
Normal file
35
resources/views/v1/export/index.twig
Normal file
@@ -0,0 +1,35 @@
|
||||
{% extends "./layout/default" %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{{ Breadcrumbs.render(Route.getCurrentRoute.getName) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'export_data_main_title'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p>
|
||||
{{ 'export_data_expl'|_ }}
|
||||
</p>
|
||||
<ul>
|
||||
<li><i class="fa fa-fw fa-download"></i> <a href="{{ route('export.export') }}" title="{{ 'export_data_all_transactions'|_ }}">{{ 'export_data_all_transactions'|_ }}</a></li>
|
||||
</ul>
|
||||
<p>
|
||||
{{ 'export_data_advanced_expl'|_ }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
@@ -58,13 +58,36 @@
|
||||
<span>{{ 'reports'|_ }}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="{{ activeRoutePartial('import') }} {{ activeRoutePartial('export') }} treeview" id="transaction-menu">
|
||||
<a href="#">
|
||||
<i class="fa fa-hdd-o fa-fw"></i>
|
||||
{% if config('firefly.feature_flags.export') %}
|
||||
<span>{{ 'import_and_export_menu'|_ }}</span>
|
||||
{% else %}
|
||||
<span>{{ 'import_transactions'|_ }}</span>
|
||||
{% endif %}
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li class="{{ activeRoutePartial('import') }}">
|
||||
<a href="{{ route('import.index') }}">
|
||||
<i class="fa fa-archive fa-fw"></i>
|
||||
<span>{{ 'import_transactions'|_ }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% if config('firefly.feature_flags.export') %}
|
||||
<li class="{{ activeRoutePartial('export') }}">
|
||||
<a href="{{ route('export.index') }}">
|
||||
<i class="fa fa-life-bouy fa-fw"></i>
|
||||
<span>{{ 'export_data_menu'|_ }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="{{ activeRoutePartial('transactions') }} treeview" id="transaction-menu">
|
||||
<a href="#">
|
||||
|
@@ -563,7 +563,7 @@ try {
|
||||
'export.index',
|
||||
function (BreadcrumbsGenerator $breadcrumbs) {
|
||||
$breadcrumbs->parent('home');
|
||||
$breadcrumbs->push(trans('firefly.export_data'), route('export.index'));
|
||||
$breadcrumbs->push(trans('firefly.export_data_bc'), route('export.index'));
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -22,14 +22,3 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Console Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file is where you may define all of your Closure based console
|
||||
| commands. Each Closure is bound to a command instance allowing a
|
||||
| simple approach to interacting with each command's IO methods.
|
||||
|
|
||||
*/
|
||||
|
@@ -488,7 +488,17 @@ Route::group(
|
||||
|
||||
}
|
||||
);
|
||||
/**
|
||||
* Export controller
|
||||
*/
|
||||
Route::group(
|
||||
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers', 'prefix' => 'export', 'as' => 'export.'], static function () {
|
||||
|
||||
// index
|
||||
Route::get('', ['uses' => 'Export\IndexController@index', 'as' => 'index']);
|
||||
Route::get('export', ['uses' => 'Export\IndexController@export', 'as' => 'export']);
|
||||
|
||||
});
|
||||
/**
|
||||
* Import Controller
|
||||
*/
|
||||
|
Reference in New Issue
Block a user