diff --git a/app/Api/V1/Controllers/Data/Export/ExportController.php b/app/Api/V1/Controllers/Data/Export/ExportController.php new file mode 100644 index 0000000000..7f4aa32dbf --- /dev/null +++ b/app/Api/V1/Controllers/Data/Export/ExportController.php @@ -0,0 +1,209 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Data\Export; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Data\Export\ExportRequest; +use FireflyIII\Support\Export\ExportDataGenerator; +use FireflyIII\User; +use Illuminate\Http\Response as LaravelResponse; + +/** + * Class ExportController + */ +class ExportController extends Controller +{ + private ExportDataGenerator $exporter; + + /** + * ExportController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + /** @var ExportDataGenerator $exporter */ + $this->exporter = app(ExportDataGenerator::class); + $this->exporter->setUser($user); + + return $next($request); + } + ); + } + + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function accounts(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportAccounts(true); + + return $this->returnExport('accounts'); + + } + + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function bills(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportBills(true); + + return $this->returnExport('bills'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function budgets(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportBudgets(true); + + return $this->returnExport('budgets'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function categories(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportCategories(true); + + return $this->returnExport('categories'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function piggyBanks(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportPiggies(true); + + return $this->returnExport('piggies'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function recurring(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportRecurring(true); + + return $this->returnExport('recurrences'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function rules(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportRules(true); + + return $this->returnExport('rules'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function tags(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportTags(true); + + return $this->returnExport('tags'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function transactions(ExportRequest $request): LaravelResponse + { + $params = $request->getAll(); + $this->exporter->setStart($params['start']); + $this->exporter->setEnd($params['end']); + $this->exporter->setAccounts($params['accounts']); + $this->exporter->setExportTransactions(true); + + return $this->returnExport('transactions'); + } + + /** + * @param string $key + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + private function returnExport(string $key): LaravelResponse + { + $date = date('Y-m-d-H-i-s'); + $fileName = sprintf('%s-export-%s.csv', $date, $key); + $data = $this->exporter->export(); + + /** @var LaravelResponse $response */ + $response = response($data[$key]); + $response + ->header('Content-Description', 'File Transfer') + ->header('Content-Type', 'application/octet-stream') + ->header('Content-Disposition', 'attachment; filename=' . $fileName) + ->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($data[$key])); + + return $response; + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Data/DestroyController.php b/app/Api/V1/Controllers/Destruction/DestroyController.php similarity index 100% rename from app/Api/V1/Controllers/Data/DestroyController.php rename to app/Api/V1/Controllers/Destruction/DestroyController.php diff --git a/app/Api/V1/Controllers/Insight/Expense/AccountController.php b/app/Api/V1/Controllers/Insight/Expense/AccountController.php index 7ef58c2c57..16c937591f 100644 --- a/app/Api/V1/Controllers/Insight/Expense/AccountController.php +++ b/app/Api/V1/Controllers/Insight/Expense/AccountController.php @@ -135,7 +135,7 @@ class AccountController extends Controller 'name' => $accountNames[$accountId], 'difference' => bcmul($diff, '-1'), 'difference_float' => ((float)$diff) * -1, - 'currency_id' => $currencyId, + 'currency_id' => (string) $currencyId, 'currency_code' => $currencies[$currencyId]->code, ]; } diff --git a/app/Api/V1/Controllers/Insight/Income/DateController.php b/app/Api/V1/Controllers/Insight/Income/AccountController.php similarity index 94% rename from app/Api/V1/Controllers/Insight/Income/DateController.php rename to app/Api/V1/Controllers/Insight/Income/AccountController.php index 0856979df4..fd401baecf 100644 --- a/app/Api/V1/Controllers/Insight/Income/DateController.php +++ b/app/Api/V1/Controllers/Insight/Income/AccountController.php @@ -35,12 +35,9 @@ use FireflyIII\User; use Illuminate\Http\JsonResponse; /** - * Class DateController - * - * Shows income information grouped or limited by date. - * Ie. all income grouped by revenue + currency. + * Class AccountController */ -class DateController extends Controller +class AccountController extends Controller { use ApiSupport; @@ -72,9 +69,11 @@ class DateController extends Controller } /** + * @param DateRequest $request * + * @return JsonResponse */ - public function basic(DateRequest $request): JsonResponse + public function revenue(DateRequest $request): JsonResponse { // parameters for chart: $dates = $request->getAll(); diff --git a/app/Api/V1/Requests/Data/Export/ExportRequest.php b/app/Api/V1/Requests/Data/Export/ExportRequest.php new file mode 100644 index 0000000000..41ed66d0b8 --- /dev/null +++ b/app/Api/V1/Requests/Data/Export/ExportRequest.php @@ -0,0 +1,80 @@ +. + */ + +namespace FireflyIII\Api\V1\Requests\Data\Export; + + +use Carbon\Carbon; +use FireflyIII\Models\AccountType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Support\Request\ChecksLogin; +use FireflyIII\Support\Request\ConvertsDataTypes; +use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Collection; + +/** + * Class ExportRequest + */ +class ExportRequest extends FormRequest +{ + use ChecksLogin, ConvertsDataTypes; + + /** + * The rules that the incoming request must be matched against. + * + * @return array + */ + public function rules(): array + { + return [ + 'type' => 'in:csv', + 'accounts' => 'min:1', + 'start' => 'date|before:end', + 'end' => 'date|after:start', + ]; + } + + public function getAll(): array + { + $result = [ + 'start' => $this->date('start') ?? Carbon::now()->subYear(), + 'end' => $this->date('end') ?? Carbon::now(), + 'type' => $this->string('type'), + ]; + $parts = explode(',', $this->string('accounts')); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser(auth()->user()); + + $accounts = new Collection; + foreach ($parts as $part) { + $accountId = (int)$part; + if (0 !== $accountId) { + $account = $repository->findNull($accountId); + if (null !== $account && AccountType::ASSET === $account->accountType->type) { + $accounts->push($account); + } + } + } + $result['accounts'] = $accounts; + + return $result; + } +} \ No newline at end of file diff --git a/app/Console/Commands/Export/ExportData.php b/app/Console/Commands/Export/ExportData.php index 55b05d9824..3634c3c4ff 100644 --- a/app/Console/Commands/Export/ExportData.php +++ b/app/Console/Commands/Export/ExportData.php @@ -111,8 +111,12 @@ class ExportData extends Command /** @var ExportDataGenerator $exporter */ $exporter = app(ExportDataGenerator::class); $exporter->setUser($this->user); + $exporter->setStart($options['start']); $exporter->setEnd($options['end']); + $exporter->setAccounts($options['accounts']); + + $exporter->setExportTransactions($options['export']['transactions']); $exporter->setExportAccounts($options['export']['accounts']); $exporter->setExportBudgets($options['export']['budgets']); diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index 0fec36eaab..e54ce6c6d8 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -50,6 +50,7 @@ use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; use FireflyIII\User; +use Illuminate\Support\Collection; use League\Csv\Writer; /** @@ -57,35 +58,24 @@ use League\Csv\Writer; */ 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; + private Carbon $end; + private bool $exportTransactions; + private Carbon $start; + private bool $exportAccounts; + private bool $exportBudgets; + private bool $exportCategories; + private bool $exportTags; + private bool $exportRecurring; + private bool $exportRules; + private bool $exportBills; + private bool $exportPiggies; + private User $user; + private Collection $accounts; public function __construct() { - $this->start = today(config('app.timezone')); + $this->accounts = new Collection; + $this->start = today(config('app.timezone')); $this->start->subYear(); $this->end = today(config('app.timezone')); $this->exportTransactions = false; @@ -107,6 +97,14 @@ class ExportDataGenerator $this->user = $user; } + /** + * @param Collection $accounts + */ + public function setAccounts(Collection $accounts): void + { + $this->accounts = $accounts; + } + /** * @return array * @throws \League\Csv\CannotInsertRecord @@ -506,7 +504,7 @@ class ExportDataGenerator $currency ? $currency->code : null, $piggy->targetamount, $repetition ? $repetition->currentamount : null, - $piggy->startdate->format('Y-m-d'), + $piggy->startdate ? $piggy->startdate->format('Y-m-d') : null, $piggy->targetdate ? $piggy->targetdate->format('Y-m-d') : null, $piggy->order, $piggy->active, @@ -670,6 +668,10 @@ class ExportDataGenerator $collector->setUser($this->user); $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation() ->withBudgetInformation()->withTagInformation()->withNotes(); + if(0 !== $this->accounts->count()) { + $collector->setAccounts($this->accounts); + } + $journals = $collector->getExtractedJournals(); // get repository for meta data: diff --git a/routes/api.php b/routes/api.php index 4b0eb37a8b..89b3bbd782 100644 --- a/routes/api.php +++ b/routes/api.php @@ -64,6 +64,46 @@ Route::group( Route::get('overview', ['uses' => 'AccountController@overview', 'as' => 'overview']); } ); + +/** + * DATA ROUTES + */ +// EXPORT +Route::group( + ['namespace' => 'FireflyIII\Api\V1\Controllers\Data\Export', 'prefix' => 'data/export', + 'as' => 'api.v1.data.export.',], + static function () { + Route::get('accounts', ['uses' => 'ExportController@accounts', 'as' => 'accounts']); + Route::get('bills', ['uses' => 'ExportController@bills', 'as' => 'bills']); + Route::get('budgets', ['uses' => 'ExportController@budgets', 'as' => 'budgets']); + Route::get('categories', ['uses' => 'ExportController@categories', 'as' => 'categories']); + Route::get('piggy-banks', ['uses' => 'ExportController@piggyBanks', 'as' => 'piggy-banks']); + Route::get('recurring', ['uses' => 'ExportController@recurring', 'as' => 'recurring']); + Route::get('rules', ['uses' => 'ExportController@rules', 'as' => 'rules']); + Route::get('tags', ['uses' => 'ExportController@tags', 'as' => 'tags']); + Route::get('transactions', ['uses' => 'ExportController@transactions', 'as' => 'transactions']); + } +); + + + + + + + + + + + + + + + + + + + + /** * SUMMARY CONTROLLER @@ -82,14 +122,14 @@ Route::group( * */ -// EXPORT TODO -Route::group( - ['namespace' => 'FireflyIII\Api\V1\Controllers\Data\Export', 'prefix' => 'data/export', - 'as' => 'api.v1.data.export.',], - static function () { - Route::get('transactions', ['uses' => 'TransactionController@export', 'as' => 'transactions']); - } -); +//// EXPORT +//Route::group( +// ['namespace' => 'FireflyIII\Api\V1\Controllers\Data\Export', 'prefix' => 'data/export', +// 'as' => 'api.v1.data.export.',], +// static function () { +// Route::get('transactions', ['uses' => 'TransactionController@export', 'as' => 'transactions']); +// } +//); /** * INSIGHT CONTROLLERS @@ -102,7 +142,7 @@ Route::group( static function () { // Insight in expenses. - // grouped by expense account or asset account: + // TODO grouped by expense account or asset account: Route::get('expense', ['uses' => 'AccountController@expense', 'as' => 'expense']); Route::get('asset', ['uses' => 'AccountController@asset', 'as' => 'asset']); @@ -111,17 +151,48 @@ Route::group( // TODO Budget/no budget and budget limit Route::get('budget', ['uses' => 'BudgetController@budget', 'as' => 'budget']); - Route::get('no-budget', ['uses' => 'BudgetController@budget', 'as' => 'budget']); + Route::get('no-budget', ['uses' => 'BudgetController@noBudget', 'as' => 'no-budget']); // TODO category and no category + Route::get('category', ['uses' => 'CategoryController@category', 'as' => 'category']); + Route::get('no-category', ['uses' => 'CategoryController@noCategory', 'as' => 'no-category']); // TODO bill and no bill + Route::get('bill', ['uses' => 'BillController@bill', 'as' => 'bill']); + Route::get('no-bill', ['uses' => 'BillController@noBill', 'as' => 'no-bill']); } ); -//// Insight in income. -// // Insight in income by date. -// Route::get('income/date/basic', ['uses' => 'Income\DateController@basic', 'as' => 'income.date.basic']); +// Insight in expenses: +Route::group( + ['namespace' => 'FireflyIII\Api\V1\Controllers\Insight\Income', 'prefix' => 'insight/income', + 'as' => 'api.v1.insight.income.',], + static function () { + // Insight in income + + // TODO grouped by expense account or asset account: + Route::get('revenue', ['uses' => 'AccountController@revenue', 'as' => 'expense']); + Route::get('asset', ['uses' => 'AccountController@asset', 'as' => 'asset']); + + // TODO total: + Route::get('total', ['uses' => 'PeriodController@total', 'as' => 'total']); + + // TODO category and no category + Route::get('category', ['uses' => 'CategoryController@category', 'as' => 'category']); + Route::get('no-category', ['uses' => 'CategoryController@noCategory', 'as' => 'no-category']); + + } +); + + + + + + + + + +