Merge branch 'release/4.3.0'

This commit is contained in:
James Cole 2016-12-26 10:46:22 +01:00
commit ad1e9c27e9
345 changed files with 4750 additions and 5695 deletions

View File

@ -38,9 +38,14 @@ SEND_REGISTRATION_MAIL=true
SEND_ERROR_MESSAGE=true
SHOW_INCOMPLETE_TRANSLATIONS=false
CACHE_PREFIX=firefly
ANALYTICS_ID=
SITE_OWNER=mail@example.com
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_APP_ID=
DEMO_USERNAME=
DEMO_PASSWORD=

View File

@ -5,14 +5,13 @@ php:
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
- ./test.sh -r
- php artisan env
- cp .env.testing .env
- mv storage/database/databasecopy.sqlite storage/database/database.sqlite
script:
- phpunit

View File

@ -2,6 +2,28 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [4.3.0] - 2015-12-26
### Added
- New method of keeping track of available budget, see issue #489
- Support for Spanish
- Firefly III now has an extended demo mode. Will expand further in the future.
### Changed
- New favicon
- Import routine no longer gives transactions a description #483
### Removed
- All test data generation code.
### Fixed
- Removed import accounts from search results #478
- Redirect after delete will no longer go back to deleted item #477
- Cannot math #482
- Fixed bug in virtual balance field #479
## [4.2.2] - 2016-12-18
### Added
- New budget report (still a bit of a beta)

View File

@ -1,8 +1,8 @@
# Firefly III [![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php#v7.0.4) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master)
# Firefly III: A personal finances manager
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii)
[![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php#v7.0.4) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master)
## A personal finances manager
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
[![The index of Firefly III](https://i.nder.be/hurdhgyg/400)](https://i.nder.be/h2b37243) [![The account overview of Firefly III](https://i.nder.be/hnkfkdpr/400)](https://i.nder.be/hv70pbwc)
@ -12,6 +12,10 @@ _(You can click on the images for a better view)_
"Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money.
## Try it out!
Try out Firefly III on the [demo site](https://firefly-iii.nder.be/).
## Installation
To install Firefly III, you'll need a web server (preferrably on Linux) and access to the command line. Then, please read the [installation guide](https://firefly-iii.github.io/installation-guide/).
@ -31,4 +35,6 @@ Firefly works on the principle that if you know where you're money is going, you
Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://firefly-iii.github.io/).
If you like Firefly and if it helps you save lots of money, why not send me [a dime for every dollar saved](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) (this is a joke, although the Paypal form works just fine, try it!)
If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com).

View File

@ -50,11 +50,10 @@ class CreateImport extends Command
}
/**
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped
*/
public function handle()
{
// find the file
/** @var UserRepositoryInterface $userRepository */
$userRepository = app(UserRepositoryInterface::class);
$file = $this->argument('file');
@ -67,7 +66,6 @@ class CreateImport extends Command
return;
}
// try to parse configuration data:
$configurationData = json_decode(file_get_contents($configuration));
if (is_null($configurationData)) {
$this->error(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
@ -82,21 +80,17 @@ class CreateImport extends Command
/** @var ImportJobRepositoryInterface $jobRepository */
$jobRepository = app(ImportJobRepositoryInterface::class, [$user]);
$job = $jobRepository->create($type);
$job = $jobRepository->create($type);
$this->line(sprintf('Created job "%s"...', $job->key));
// put the file in the proper place:
Artisan::call('firefly:encrypt', ['file' => $file, 'key' => $job->key]);
$this->line('Stored import data...');
// store the configuration in the job:
$job->configuration = $configurationData;
$job->status = 'settings_complete';
$job->save();
$this->line('Stored configuration...');
// if user wants to run it, do!
if ($this->option('start') === true) {
$this->line('The import will start in a moment. This process is not visible...');
Log::debug('Go for import!');
@ -109,10 +103,10 @@ class CreateImport extends Command
/**
* @return bool
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly.
*/
private function validArguments(): bool
{
// find the file
/** @var UserRepositoryInterface $userRepository */
$userRepository = app(UserRepositoryInterface::class);
$file = $this->argument('file');

View File

@ -18,6 +18,7 @@ use FireflyIII\Import\Logging\CommandHandler;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Log;
/**
@ -69,32 +70,15 @@ class Import extends Command
$monolog = Log::getMonolog();
$handler = new CommandHandler($this);
$monolog->pushHandler($handler);
$importProcedure = new ImportProcedure;
$result = $importProcedure->runImport($job);
$result = ImportProcedure::runImport($job);
/**
* @var int $index
* @var TransactionJournal $journal
*/
foreach ($result as $index => $journal) {
if (!is_null($journal->id)) {
$this->line(sprintf('Line #%d has been imported as transaction #%d.', $index, $journal->id));
continue;
}
$this->error(sprintf('Could not store line #%d', $index));
}
// display result to user:
$this->presentResults($result);
$this->line('The import has completed.');
// get any errors from the importer:
$extendedStatus = $job->extended_status;
if (isset($extendedStatus['errors']) && count($extendedStatus['errors']) > 0) {
$this->line(sprintf('The following %d error(s) occured during the import:', count($extendedStatus['errors'])));
foreach ($extendedStatus['errors'] as $error) {
$this->error($error);
}
}
$this->presentErrors($job);
return;
}
@ -120,4 +104,36 @@ class Import extends Command
return true;
}
/**
* @param ImportJob $job
*/
private function presentErrors(ImportJob $job)
{
$extendedStatus = $job->extended_status;
if (isset($extendedStatus['errors']) && count($extendedStatus['errors']) > 0) {
$this->line(sprintf('The following %d error(s) occured during the import:', count($extendedStatus['errors'])));
foreach ($extendedStatus['errors'] as $error) {
$this->error($error);
}
}
}
/**
* @param Collection $result
*/
private function presentResults(Collection $result)
{
/**
* @var int $index
* @var TransactionJournal $journal
*/
foreach ($result as $index => $journal) {
if (!is_null($journal->id)) {
$this->line(sprintf('Line #%d has been imported as transaction #%d.', $index, $journal->id));
continue;
}
$this->error(sprintf('Could not store line #%d', $index));
}
}
}

View File

@ -60,42 +60,26 @@ class ScanAttachments extends Command
/** @var Attachment $attachment */
foreach ($attachments as $attachment) {
$fileName = $attachment->fileName();
// try to grab file content:
try {
$content = $disk->get($fileName);
} catch (FileNotFoundException $e) {
$this->error(sprintf('Could not find data for attachment #%d', $attachment->id));
continue;
}
// try to decrypt content.
try {
$decrypted = Crypt::decrypt($content);
} catch (DecryptException $e) {
$this->error(sprintf('Could not decrypt data of attachment #%d', $attachment->id));
continue;
}
// make temp file:
$tmpfname = tempnam(sys_get_temp_dir(), 'FireflyIII');
// store content in temp file:
file_put_contents($tmpfname, $decrypted);
// get md5 and mime
$md5 = md5_file($tmpfname);
$mime = mime_content_type($tmpfname);
// update attachment:
$md5 = md5_file($tmpfname);
$mime = mime_content_type($tmpfname);
$attachment->md5 = $md5;
$attachment->mime = $mime;
$attachment->save();
$this->line(sprintf('Fixed attachment #%d', $attachment->id));
// find file:
}
}
}

View File

@ -124,16 +124,16 @@ class VerifyDatabase extends Command
{
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
->groupBy(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email'])
->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email'])
->whereNull('budget_limits.id')
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']);
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']);
/** @var stdClass $entry */
/** @var Budget $entry */
foreach ($set as $entry) {
$name = $entry->encrypted ? Crypt::decrypt($entry->name) : $entry->name;
$line = sprintf(
'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.',
$entry->user_id, $entry->email, $entry->id, Crypt::decrypt($entry->name)
$entry->user_id, $entry->email, $entry->id, $name
);
$this->line($line);
}

0
app/Console/Kernel.php Executable file → Normal file
View File

View File

@ -35,4 +35,4 @@ class DeletedUser extends Event
{
$this->email = $email;
}
}
}

View File

@ -43,4 +43,4 @@ class RequestedNewPassword extends Event
$this->ipAddress = $ipAddress;
}
}
}

0
app/Exceptions/Handler.php Executable file → Normal file
View File

View File

@ -64,4 +64,4 @@ interface ProcessorInterface
* @return Collection
*/
public function getFiles(): Collection;
}
}

View File

@ -31,6 +31,8 @@ class ChartJsGenerator implements GeneratorInterface
* 0: [
* 'label' => 'label of set',
* 'type' => bar or line, optional
* 'yAxisID' => ID of yAxis, optional, will not be included when unused.
* 'fill' => if to fill a line? optional, will not be included when unused.
* 'entries' =>
* [
* 'label-of-entry' => 'value'
@ -39,6 +41,8 @@ class ChartJsGenerator implements GeneratorInterface
* 1: [
* 'label' => 'label of another set',
* 'type' => bar or line, optional
* 'yAxisID' => ID of yAxis, optional, will not be included when unused.
* 'fill' => if to fill a line? optional, will not be included when unused.
* 'entries' =>
* [
* 'label-of-entry' => 'value'
@ -64,11 +68,19 @@ class ChartJsGenerator implements GeneratorInterface
unset($first, $labels);
foreach ($data as $set) {
$chartData['datasets'][] = [
$currentSet = [
'label' => $set['label'],
'type' => $set['type'] ?? 'line',
'data' => array_values($set['entries']),
];
if (isset($set['yAxisID'])) {
$currentSet['yAxisID'] = $set['yAxisID'];
}
if (isset($set['fill'])) {
$currentSet['fill'] = $set['fill'];
}
$chartData['datasets'][] = $currentSet;
}
return $chartData;
@ -134,4 +146,4 @@ class ChartJsGenerator implements GeneratorInterface
return $chartData;
}
}
}

View File

@ -70,4 +70,4 @@ interface GeneratorInterface
*/
public function singleSet(string $setLabel, array $data): array;
}
}

View File

@ -153,4 +153,4 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this;
}
}
}

View File

@ -24,4 +24,4 @@ class MultiYearReportGenerator extends MonthReportGenerator
/**
* Doesn't do anything different.
*/
}
}

View File

@ -25,4 +25,4 @@ class YearReportGenerator extends MonthReportGenerator
/**
* Doesn't do anything different.
*/
}
}

View File

@ -244,4 +244,4 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $result;
}
}
}

View File

@ -24,4 +24,4 @@ class MultiYearReportGenerator extends MonthReportGenerator
/**
* Doesn't do anything different.
*/
}
}

View File

@ -25,4 +25,4 @@ class YearReportGenerator extends MonthReportGenerator
/**
* Doesn't do anything different.
*/
}
}

View File

@ -325,4 +325,4 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $result;
}
}
}

View File

@ -24,4 +24,4 @@ class MultiYearReportGenerator extends MonthReportGenerator
/**
* Doesn't do anything different.
*/
}
}

View File

@ -25,4 +25,4 @@ class YearReportGenerator extends MonthReportGenerator
/**
* Doesn't do anything different.
*/
}
}

View File

@ -57,4 +57,4 @@ class ReportGeneratorFactory
}
throw new FireflyException(sprintf('Cannot generate report. There is no "%s"-report for period "%s".', $type, $period));
}
}
}

View File

@ -64,4 +64,4 @@ interface ReportGeneratorInterface
*/
public function setStartDate(Carbon $date): ReportGeneratorInterface;
}
}

View File

@ -106,4 +106,4 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this;
}
}
}

View File

@ -103,4 +103,4 @@ class MultiYearReportGenerator implements ReportGeneratorInterface
return $this;
}
}
}

View File

@ -103,4 +103,4 @@ class YearReportGenerator implements ReportGeneratorInterface
return $this;
}
}
}

View File

@ -88,4 +88,4 @@ class Support
return $result;
}
}
}

View File

@ -60,6 +60,7 @@ class JournalCollector implements JournalCollectorInterface
'transaction_types.type as transaction_type_type',
'transaction_journals.bill_id',
'bills.name as bill_name',
'bills.name_encrypted as bill_name_encrypted',
'transactions.id as id',
'transactions.amount as transaction_amount',
'transactions.description as transaction_description',
@ -180,10 +181,12 @@ class JournalCollector implements JournalCollectorInterface
$set->each(
function (Transaction $transaction) {
$transaction->date = new Carbon($transaction->date);
$transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description;
$transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : '';
$transaction->description = $transaction->encrypted ? Crypt::decrypt($transaction->description) : $transaction->description;
if (!is_null($transaction->bill_name)) {
$transaction->bill_name = $transaction->bill_name_encrypted ? Crypt::decrypt($transaction->bill_name) : $transaction->bill_name;
}
// optionally decrypted:
try {
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
} catch (DecryptException $e) {
@ -726,4 +729,4 @@ class JournalCollector implements JournalCollectorInterface
return $query;
}
}
}

View File

@ -173,4 +173,4 @@ interface JournalCollectorInterface
* @return JournalCollectorInterface
*/
public function withoutCategory(): JournalCollectorInterface;
}
}

View File

@ -26,6 +26,7 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Log;
/**
* Class BalanceReportHelper
@ -59,19 +60,23 @@ class BalanceReportHelper implements BalanceReportHelperInterface
*/
public function getBalanceReport(Collection $accounts, Carbon $start, Carbon $end): Balance
{
Log::debug('Start of balance report');
$balance = new Balance;
$header = new BalanceHeader;
$limitRepetitions = $this->budgetRepository->getAllBudgetLimitRepetitions($start, $end);
foreach ($accounts as $account) {
Log::debug(sprintf('Add account %s to headers.', $account->name));
$header->addAccount($account);
}
/** @var LimitRepetition $repetition */
foreach ($limitRepetitions as $repetition) {
$budget = $this->budgetRepository->find($repetition->budget_id);
$line = $this->createBalanceLine($budget, $repetition, $accounts);
Log::debug(sprintf('Create balance line for budget #%d ("%s") and repetition #%d', $budget->id, $budget->name, $repetition->id));
$line = $this->createBalanceLine($budget, $repetition, $accounts);
$balance->addBalanceLine($line);
}
Log::debug('Create rest of the things.');
$noBudgetLine = $this->createNoBudgetLine($accounts, $start, $end);
$coveredByTagLine = $this->createTagsBalanceLine($accounts, $start, $end);
$leftUnbalancedLine = $this->createLeftUnbalancedLine($noBudgetLine, $coveredByTagLine);
@ -81,9 +86,12 @@ class BalanceReportHelper implements BalanceReportHelperInterface
$balance->addBalanceLine($leftUnbalancedLine);
$balance->setBalanceHeader($header);
Log::debug('Clear unused budgets.');
// remove budgets without expenses from balance lines:
$balance = $this->removeUnusedBudgets($balance);
Log::debug('Return report.');
return $balance;
}

View File

@ -124,17 +124,24 @@ class AccountController extends Controller
*/
public function destroy(ARI $repository, Account $account)
{
$type = $account->accountType->type;
$typeName = config('firefly.shortNamesByFullName.' . $type);
$name = $account->name;
$moveTo = $repository->find(intval(Input::get('move_account_before_delete')));
$type = $account->accountType->type;
$typeName = config('firefly.shortNamesByFullName.' . $type);
$name = $account->name;
$accountId = $account->id;
$moveTo = $repository->find(intval(Input::get('move_account_before_delete')));
$repository->destroy($account, $moveTo);
Session::flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name])));
Preferences::mark();
return redirect(session('accounts.delete.url'));
$uri = session('accounts.delete.url');
if (!(strpos($uri, sprintf('accounts/show/%s', $accountId)) === false)) {
// uri would point back to account
$uri = route('accounts.index', [$typeName]);
}
return redirect($uri);
}
/**
@ -192,8 +199,7 @@ class AccountController extends Controller
*/
public function index(ARI $repository, string $what)
{
$what = $what ?? 'asset';
$what = $what ?? 'asset';
$subTitle = trans('firefly.' . $what . '_accounts');
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
$types = config('firefly.accountTypesByIdentifier.' . $what);
@ -214,6 +220,7 @@ class AccountController extends Controller
$account->lastActivityDate = $this->isInArray($activities, $account->id);
$account->startBalance = $this->isInArray($startBalances, $account->id);
$account->endBalance = $this->isInArray($endBalances, $account->id);
$account->difference = bcsub($account->endBalance, $account->startBalance);
}
);
@ -235,12 +242,12 @@ class AccountController extends Controller
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$subTitle = $account->name;
$range = Preferences::get('viewRange', '1M')->data;
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$chartUri = route('chart.account.single', [$account->id]);
// grab those journals:
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
$journals = $collector->getPaginatedJournals();
@ -379,7 +386,7 @@ class AccountController extends Controller
return $array[$entryId];
}
return '';
return '0';
}
/**

View File

@ -95,16 +95,12 @@ class AttachmentController extends Controller
* @throws FireflyException
*
*/
public function download(Attachment $attachment)
public function download(AttachmentRepositoryInterface $repository, Attachment $attachment)
{
// create a disk.
$disk = Storage::disk('upload');
$file = $attachment->fileName();
if ($disk->exists($file)) {
if ($repository->exists($attachment)) {
$content = $repository->getContent($attachment);
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
$content = Crypt::decrypt($disk->get($file));
Log::debug('Send file to user', ['file' => $quoted, 'size' => strlen($content)]);
@ -118,8 +114,8 @@ class AttachmentController extends Controller
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->header('Pragma', 'public')
->header('Content-Length', strlen($content));
}
throw new FireflyException('Could not find the indicated attachment. The file is no longer there.');
}

37
app/Http/Controllers/Auth/ForgotPasswordController.php Executable file → Normal file
View File

@ -13,7 +13,9 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\User;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
/**
* Class ForgotPasswordController
@ -33,4 +35,39 @@ class ForgotPasswordController extends Controller
parent::__construct();
$this->middleware('guest');
}
/**
* Send a reset link to the given user.
*
* @param Request $request
*
* @return \Illuminate\Http\RedirectResponse
*/
public function sendResetLinkEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
// verify if the user is not a demo user. If so, we give him back an error.
$user = User::where('email', $request->get('email'))->first();
if (!is_null($user) && $user->hasRole('demo')) {
return back()->withErrors(
['email' => trans('firefly.cannot_reset_demo_user')]
);
}
$response = $this->broker()->sendResetLink(
$request->only('email')
);
if ($response === Password::RESET_LINK_SENT) {
return back()->with('status', trans($response));
}
// If an error was returned by the password broker, we will get this message
// translated so we can notify a user of the problem. We'll redirect back
// to where the users came from so they can attempt this process again.
return back()->withErrors(
['email' => trans($response)]
);
}
}

4
app/Http/Controllers/Auth/LoginController.php Executable file → Normal file
View File

@ -53,9 +53,9 @@ class LoginController extends Controller
/**
* Handle a login request to the application.
*
* @param \Illuminate\Http\Request $request
* @param Request $request
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response
*/
public function login(Request $request)
{

View File

@ -26,8 +26,8 @@ use Illuminate\Support\Facades\Password;
*
* @package FireflyIII\Http\Controllers\Auth
* @method getEmailSubject()
* @method getSendResetLinkEmailSuccessResponse()
* @method getSendResetLinkEmailFailureResponse()
* @method getSendResetLinkEmailSuccessResponse(string $response)
* @method getSendResetLinkEmailFailureResponse(string $response)
*/
class PasswordController extends Controller
{

3
app/Http/Controllers/Auth/RegisterController.php Executable file → Normal file
View File

@ -22,11 +22,8 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Log;
use Mail;
use Session;
use Swift_TransportException;
use Validator;
/**

0
app/Http/Controllers/Auth/ResetPasswordController.php Executable file → Normal file
View File

View File

@ -99,13 +99,20 @@ class BillController extends Controller
*/
public function destroy(BillRepositoryInterface $repository, Bill $bill)
{
$name = $bill->name;
$name = $bill->name;
$billId = $bill->id;
$repository->destroy($bill);
Session::flash('success', strval(trans('firefly.deleted_bill', ['name' => $name])));
Preferences::mark();
return redirect(session('bills.delete.url'));
$uri = session('bills.delete.url');
if (!(strpos($uri, sprintf('bills/show/%s', $billId)) === false)) {
// uri would point back to bill
$uri = route('bills.index');
}
return redirect($uri);
}
/**

View File

@ -15,10 +15,10 @@ namespace FireflyIII\Http\Controllers;
use Amount;
use Carbon\Carbon;
use Config;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Requests\BudgetFormRequest;
use FireflyIII\Http\Requests\BudgetIncomeRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
@ -26,8 +26,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Support\Collection;
use Input;
use Log;
use Navigation;
use Preferences;
use Response;
use Session;
@ -42,6 +40,9 @@ use View;
class BudgetController extends Controller
{
/** @var BudgetRepositoryInterface */
private $repository;
/**
*
*/
@ -55,6 +56,7 @@ class BudgetController extends Controller
function ($request, $next) {
View::share('title', trans('firefly.budgets'));
View::share('mainTitleIcon', 'fa-tasks');
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);
}
@ -73,21 +75,15 @@ class BudgetController extends Controller
/** @var Carbon $start */
$start = session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */
$end = session('end', Carbon::now()->endOfMonth());
$viewRange = Preferences::get('viewRange', '1M')->data;
// is custom view range?
if (session('is_custom_range') === true) {
$viewRange = 'custom';
}
$end = session('end', Carbon::now()->endOfMonth());
$viewRange = Preferences::get('viewRange', '1M')->data;
$limitRepetition = $repository->updateLimitAmount($budget, $start, $end, $viewRange, $amount);
if ($amount == 0) {
$limitRepetition = null;
}
Preferences::mark();
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition ? $limitRepetition->id : 0]);
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition ? $limitRepetition->id : 0, 'amount' => $amount]);
}
@ -134,15 +130,21 @@ class BudgetController extends Controller
public function destroy(Budget $budget, BudgetRepositoryInterface $repository)
{
$name = $budget->name;
$name = $budget->name;
$budgetId = $budget->id;
$repository->destroy($budget);
Session::flash('success', strval(trans('firefly.deleted_budget', ['name' => e($name)])));
Preferences::mark();
$uri = session('budgets.delete.url');
if (!(strpos($uri, sprintf('budgets/show/%s', $budgetId)) === false)) {
// uri would point back to budget
$uri = route('budgets.index');
}
return redirect(session('budgets.delete.url'));
return redirect($uri);
}
/**
@ -167,82 +169,27 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
* @param AccountRepositoryInterface $accountRepository
*
* @return View
*
*/
public function index(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
public function index()
{
$repository->cleanupBudgets();
$this->repository->cleanupBudgets();
$budgets = $repository->getActiveBudgets();
$inactive = $repository->getInactiveBudgets();
$spent = '0';
$budgeted = '0';
$range = Preferences::get('viewRange', '1M')->data;
$repeatFreq = Config::get('firefly.range_to_repeat_freq.' . $range);
if (session('is_custom_range') === true) {
$repeatFreq = 'custom';
}
/** @var Carbon $start */
$start = session('start', new Carbon);
/** @var Carbon $end */
$budgets = $this->repository->getActiveBudgets();
$inactive = $this->repository->getInactiveBudgets();
$start = session('start', new Carbon);
$end = session('end', new Carbon);
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
$budgetIncomeTotal = Preferences::get($key, 1000)->data;
$period = Navigation::periodShow($start, $range);
$periodStart = $start->formatLocalized($this->monthAndDayFormat);
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
$startAsString = $start->format('Y-m-d');
$endAsString = $end->format('Y-m-d');
Log::debug('Now at /budgets');
// loop the budgets:
/** @var Budget $budget */
foreach ($budgets as $budget) {
Log::debug(sprintf('Now at budget #%d ("%s")', $budget->id, $budget->name));
$budget->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);
$allRepetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$otherRepetitions = new Collection;
/** @var LimitRepetition $repetition */
foreach ($allRepetitions as $repetition) {
if ($repetition->budget_id == $budget->id) {
if ($repetition->budgetLimit->repeat_freq == $repeatFreq
&& $repetition->startdate->format('Y-m-d') == $startAsString
&& $repetition->enddate->format('Y-m-d') == $endAsString
) {
// do something
$budget->currentRep = $repetition;
continue;
}
$otherRepetitions->push($repetition);
}
}
$budget->otherRepetitions = $otherRepetitions;
if (!is_null($budget->currentRep) && !is_null($budget->currentRep->id)) {
$budgeted = bcadd($budgeted, $budget->currentRep->amount);
}
$spent = bcadd($spent, $budget->spent);
}
$defaultCurrency = Amount::getDefaultCurrency();
$budgetInformation = $this->collectBudgetInformation($budgets, $start, $end);
$defaultCurrency = Amount::getDefaultCurrency();
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
$spent = array_sum(array_column($budgetInformation, 'spent'));
$budgeted = array_sum(array_column($budgetInformation, 'budgeted'));
return view(
'budgets.index', compact(
'periodStart', 'periodEnd',
'period', 'range', 'budgetIncomeTotal',
'defaultCurrency', 'inactive', 'budgets',
'spent', 'budgeted'
)
'budgets.index',
compact('available', 'periodStart', 'periodEnd', 'budgetInformation', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
);
}
@ -274,16 +221,14 @@ class BudgetController extends Controller
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function postUpdateIncome()
public function postUpdateIncome(BudgetIncomeRequest $request)
{
$range = Preferences::get('viewRange', '1M')->data;
/** @var Carbon $date */
$date = session('start', new Carbon);
$start = Navigation::startOfPeriod($date, $range);
$end = Navigation::endOfPeriod($start, $range);
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
$start = session('start', new Carbon);
$end = session('end', new Carbon);
$defaultCurrency = Amount::getDefaultCurrency();
$amount = $request->get('amount');
Preferences::set($key, intval(Input::get('amount')));
$this->repository->setAvailableBudget($defaultCurrency, $start, $end, $amount);
Preferences::mark();
return redirect(route('budgets.index'));
@ -424,19 +369,57 @@ class BudgetController extends Controller
*/
public function updateIncome()
{
$range = Preferences::get('viewRange', '1M')->data;
$format = strval(trans('config.month_and_day'));
$start = session('start', new Carbon);
$end = session('end', new Carbon);
$defaultCurrency = Amount::getDefaultCurrency();
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
/** @var Carbon $date */
$date = session('start', new Carbon);
$start = Navigation::startOfPeriod($date, $range);
$end = Navigation::endOfPeriod($start, $range);
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
$amount = Preferences::get($key, 1000);
$displayStart = $start->formatLocalized($format);
$displayEnd = $end->formatLocalized($format);
return view('budgets.income', compact('amount', 'displayStart', 'displayEnd'));
return view('budgets.income', compact('available', 'start', 'end'));
}
/**
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function collectBudgetInformation(Collection $budgets, Carbon $start, Carbon $end): array
{
// get account information
$accountRepository = app(AccountRepositoryInterface::class);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
$return = [];
/** @var Budget $budget */
foreach ($budgets as $budget) {
$budgetId = $budget->id;
$return[$budgetId] = [
'spent' => $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end),
'budgeted' => '0',
'currentRep' => false,
];
$allRepetitions = $this->repository->getAllBudgetLimitRepetitions($start, $end);
$otherRepetitions = new Collection;
// get all the limit repetitions relevant between start and end and examine them:
/** @var LimitRepetition $repetition */
foreach ($allRepetitions as $repetition) {
if ($repetition->budget_id == $budget->id) {
if ($repetition->startdate->isSameDay($start) && $repetition->enddate->isSameDay($end)
) {
$return[$budgetId]['currentRep'] = $repetition;
$return[$budgetId]['budgeted'] = $repetition->amount;
continue;
}
// otherwise it's just one of the many relevant repetitions:
$otherRepetitions->push($repetition);
}
}
$return[$budgetId]['otherRepetitions'] = $otherRepetitions;
}
return $return;
}
}

View File

@ -98,13 +98,20 @@ class CategoryController extends Controller
public function destroy(CRI $repository, Category $category)
{
$name = $category->name;
$name = $category->name;
$categoryId = $category->id;
$repository->destroy($category);
Session::flash('success', strval(trans('firefly.deleted_category', ['name' => e($name)])));
Preferences::mark();
return redirect(session('categories.delete.url'));
$uri = session('categories.delete.url');
if (!(strpos($uri, sprintf('categories/show/%s', $categoryId)) === false)) {
// uri would point back to category
$uri = route('categories.index');
}
return redirect($uri);
}
/**
@ -262,7 +269,6 @@ class CategoryController extends Controller
$journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id . '/' . $date);
return view('categories.show-by-date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
}

View File

@ -204,7 +204,7 @@ class BudgetController extends Controller
}
// for no budget:
$row = $this->spentInPeriodWithout($start, $end);
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['repetition_left'], '0') !== 0) {
if (bccomp($row['repetition_overspent'], '0') !== 0) {
$chartData[0]['entries'][$row['name']] = bcmul($row['spent'], '-1');
$chartData[1]['entries'][$row['name']] = $row['repetition_left'];
$chartData[2]['entries'][$row['name']] = bcmul($row['repetition_overspent'], '-1');
@ -284,8 +284,8 @@ class BudgetController extends Controller
foreach (array_keys($periods) as $period) {
$label = $periods[$period];
$spent = isset($entries[$budget->id]['entries'][$period]) ? $entries[$budget->id]['entries'][$period] : '0';
$limit = isset($entries[$period]) ? $budgeted[$period] : 0;
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
$limit = isset($budgeted[$period]) ? $budgeted[$period] : 0;
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 2);
$chartData[1]['entries'][$label] = $limit;
}
@ -386,7 +386,7 @@ class BudgetController extends Controller
}
$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;
$spent = $expenses;
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
$return[] = [
'name' => $name,
@ -454,8 +454,8 @@ class BudgetController extends Controller
$array = [
'name' => strval(trans('firefly.no_budget')),
'repetition_left' => '0',
'repetition_overspent' => '0',
'spent' => $sum,
'repetition_overspent' => $sum,
'spent' => '0',
];
return $array;

View File

@ -20,16 +20,17 @@ use FireflyIII\Generator\Report\Category\MonthReportGenerator;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Log;
use Navigation;
use Response;
/**
* Separate controller because many helper functions are shared.
*
@ -83,6 +84,7 @@ class BudgetReportController extends Controller
$cache->addProperty($budgets);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($others);
if ($cache->has()) {
return Response::json($cache->get());
}
@ -139,6 +141,7 @@ class BudgetReportController extends Controller
$cache->addProperty($budgets);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($others);
if ($cache->has()) {
return Response::json($cache->get());
}
@ -195,21 +198,39 @@ class BudgetReportController extends Controller
if ($cache->has()) {
return Response::json($cache->get());
}
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
$function = Navigation::preferredEndOfPeriod($start, $end);
$chartData = [];
$currentStart = clone $start;
$limits = $repository->getAllBudgetLimitRepetitions($start, $end); // also for ALL budgets.
// prep chart data:
foreach ($budgets as $budget) {
$chartData[$budget->id] = [
'label' => $budget->name,
$chartData[$budget->id] = [
'label' => strval(trans('firefly.spent_in_specific_budget', ['budget' => $budget->name])),
'type' => 'bar',
'yAxisID' => 'y-axis-0',
'entries' => [],
];
$chartData[$budget->id . '-sum'] = [
'label' => strval(trans('firefly.sum_of_expenses_in_budget', ['budget' => $budget->name])),
'type' => 'line',
'fill' => false,
'yAxisID' => 'y-axis-1',
'entries' => [],
];
$chartData[$budget->id . '-left'] = [
'label' => strval(trans('firefly.left_in_budget_limit', ['budget' => $budget->name])),
'type' => 'bar',
'fill' => false,
'yAxisID' => 'y-axis-0',
'entries' => [],
];
}
$sumOfExpenses = [];
$leftOfLimits = [];
while ($currentStart < $end) {
$currentEnd = clone $currentStart;
$currentEnd = $currentEnd->$function();
@ -218,7 +239,20 @@ class BudgetReportController extends Controller
/** @var Budget $budget */
foreach ($budgets as $budget) {
$chartData[$budget->id]['entries'][$label] = $expenses[$budget->id] ?? '0';
$currentExpenses = $expenses[$budget->id] ?? '0';
$sumOfExpenses[$budget->id] = $sumOfExpenses[$budget->id] ?? '0';
$sumOfExpenses[$budget->id] = bcadd($currentExpenses, $sumOfExpenses[$budget->id]);
$chartData[$budget->id]['entries'][$label] = round(bcmul($currentExpenses, '-1'), 2);
$chartData[$budget->id . '-sum']['entries'][$label] = round(bcmul($sumOfExpenses[$budget->id], '-1'), 2);
$limit = $this->filterLimits($limits, $budget, $currentStart);
if (!is_null($limit->id)) {
$leftOfLimits[$limit->id] = $leftOfLimits[$limit->id] ?? strval($limit->amount);
$leftOfLimits[$limit->id] = bcadd($leftOfLimits[$limit->id], $currentExpenses);
$chartData[$budget->id . '-left']['entries'][$label] = round($leftOfLimits[$limit->id], 2);
}
}
$currentStart = clone $currentEnd;
$currentStart->addDay();
@ -230,6 +264,44 @@ class BudgetReportController extends Controller
return Response::json($data);
}
/**
* @param $limits
* @param $budget
* @param $currentStart
*
* @return LimitRepetition
*/
private function filterLimits(Collection $limits, Budget $budget, Carbon $date): LimitRepetition
{
Log::debug(sprintf('Start of filterLimits with %d limits.', $limits->count()));
$filtered = $limits->filter(
function (LimitRepetition $limit) use ($budget, $date) {
if ($limit->budget_id !== $budget->id) {
Log::debug(sprintf('LimitRepetition has budget #%d but expecting #%d', $limit->budget_id, $budget->id));
return false;
}
if ($date < $limit->startdate || $date > $limit->enddate) {
Log::debug(
sprintf(
'Date %s is not between %s and %s',
$date->format('Y-m-d'), $limit->startdate->format('Y-m-d'), $limit->enddate->format('Y-m-d')
)
);
return false;
}
return $limit;
}
);
if ($filtered->count() === 1) {
return $filtered->first();
}
return new LimitRepetition;
}
/**
* @param Collection $accounts
@ -289,4 +361,4 @@ class BudgetReportController extends Controller
return $grouped;
}
}
}

View File

@ -83,6 +83,7 @@ class CategoryReportController extends Controller
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($others);
if ($cache->has()) {
return Response::json($cache->get());
}
@ -138,6 +139,7 @@ class CategoryReportController extends Controller
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($others);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
@ -194,6 +196,7 @@ class CategoryReportController extends Controller
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($others);
if ($cache->has()) {
return Response::json($cache->get());
}
@ -250,6 +253,7 @@ class CategoryReportController extends Controller
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($others);
if ($cache->has()) {
return Response::json($cache->get());
}
@ -314,14 +318,33 @@ class CategoryReportController extends Controller
$chartData[$category->id . '-in'] = [
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.income'))) . ')',
'type' => 'bar',
'yAxisID' => 'y-axis-0',
'entries' => [],
];
$chartData[$category->id . '-out'] = [
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')',
'type' => 'bar',
'yAxisID' => 'y-axis-0',
'entries' => [],
];
// total in, total out:
$chartData[$category->id . '-total-in'] = [
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.sum_of_income'))) . ')',
'type' => 'line',
'fill' => false,
'yAxisID' => 'y-axis-1',
'entries' => [],
];
$chartData[$category->id . '-total-out'] = [
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.sum_of_expenses'))) . ')',
'type' => 'line',
'fill' => false,
'yAxisID' => 'y-axis-1',
'entries' => [],
];
}
$sumOfIncome = [];
$sumOfExpense = [];
while ($currentStart < $end) {
$currentEnd = clone $currentStart;
@ -332,17 +355,40 @@ class CategoryReportController extends Controller
/** @var Category $category */
foreach ($categories as $category) {
$labelIn = $category->id . '-in';
$labelOut = $category->id . '-out';
// get sum, and get label:
$chartData[$labelIn]['entries'][$label] = $income[$category->id] ?? '0';
$chartData[$labelOut]['entries'][$label] = $expenses[$category->id] ?? '0';
$labelIn = $category->id . '-in';
$labelOut = $category->id . '-out';
$labelSumIn = $category->id . '-total-in';
$labelSumOut = $category->id . '-total-out';
$currentIncome = $income[$category->id] ?? '0';
$currentExpense = $expenses[$category->id] ?? '0';
// add to sum:
$sumOfIncome[$category->id] = $sumOfIncome[$category->id] ?? '0';
$sumOfExpense[$category->id] = $sumOfExpense[$category->id] ?? '0';
$sumOfIncome[$category->id] = bcadd($sumOfIncome[$category->id], $currentIncome);
$sumOfExpense[$category->id] = bcadd($sumOfExpense[$category->id], $currentExpense);
// add to chart:
$chartData[$labelIn]['entries'][$label] = $currentIncome;
$chartData[$labelOut]['entries'][$label] = $currentExpense;
$chartData[$labelSumIn]['entries'][$label] = $sumOfIncome[$category->id];
$chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$category->id];
}
$currentStart = clone $currentEnd;
$currentStart->addDay();
}
$data = $this->generator->multiSet($chartData);
// remove all empty entries to prevent cluttering:
$newSet = [];
foreach ($chartData as $key => $entry) {
if (!array_sum($entry['entries']) == 0) {
$newSet[$key] = $chartData[$key];
}
}
if (count($newSet) === 0) {
$newSet = $chartData;
}
$data = $this->generator->multiSet($newSet);
$cache->store($data);
return Response::json($data);
@ -427,4 +473,4 @@ class CategoryReportController extends Controller
return $grouped;
}
}
}

6
app/Http/Controllers/Controller.php Executable file → Normal file
View File

@ -23,6 +23,7 @@ use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Session;
use View;
use FireflyConfig;
/**
* Class Controller
@ -49,7 +50,10 @@ class Controller extends BaseController
View::share('hideCategories', false);
View::share('hideBills', false);
View::share('hideTags', false);
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
View::share('IS_DEMO_SITE', $isDemoSite);
View::share('DEMO_USERNAME', env('DEMO_USERNAME',''));
View::share('DEMO_PASSWORD', env('DEMO_PASSWORD',''));
// translations:

View File

@ -22,6 +22,7 @@ use FireflyIII\Http\Requests\ExportFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\ExportJob;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface;
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface as EJRI;
use Preferences;
use Response;
@ -59,21 +60,22 @@ class ExportController extends Controller
* @return \Symfony\Component\HttpFoundation\Response|\Illuminate\Contracts\Routing\ResponseFactory
* @throws FireflyException
*/
public function download(ExportJob $job)
public function download(ExportJobRepositoryInterface $repository, ExportJob $job)
{
$disk = Storage::disk('export');
$file = $job->key . '.zip';
$date = date('Y-m-d \a\t H-i-s');
$name = 'Export job on ' . $date . '.zip';
$quoted = sprintf('"%s"', addcslashes($name, '"\\'));
if (!$disk->exists($file)) {
if (!$repository->exists($job)) {
throw new FireflyException('Against all expectations, zip file "' . $file . '" does not exist.');
}
$content = $repository->getContent($job);
$job->change('export_downloaded');
return response($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)
@ -82,7 +84,7 @@ class ExportController 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));
}

View File

@ -63,6 +63,7 @@ class HomeController extends Controller
// a possible problem with the budgets.
if ($label === strval(trans('firefly.everything')) || $label === strval(trans('firefly.customRange'))) {
$isCustomRange = true;
//Preferences::set('viewRange', 'custom');
Log::debug('Range is now marked as "custom".');
}
@ -107,7 +108,7 @@ class HomeController extends Controller
$journal->save();
}
}
Session::forget(['start', 'end', 'viewRange', 'range', 'is_custom_range']);
Session::clear();
Artisan::call('cache:clear');

View File

@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers;
use Crypt;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Requests\ImportUploadRequest;
use FireflyIII\Import\ImportProcedure;
use FireflyIII\Import\ImportProcedureInterface;
use FireflyIII\Import\Setup\SetupInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
@ -315,13 +315,13 @@ class ImportController extends Controller
}
/**
* @param ImportJob $job
* @param ImportProcedureInterface $importProcedure
* @param ImportJob $job
*/
public function start(ImportJob $job)
public function start(ImportProcedureInterface $importProcedure, ImportJob $job)
{
set_time_limit(0);
if ($job->status == 'settings_complete') {
$importProcedure = new ImportProcedure;
$importProcedure->runImport($job);
}
}
@ -334,7 +334,7 @@ class ImportController extends Controller
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function status(ImportJob $job)
{
{ //
Log::debug('Now in status()', ['job' => $job->key]);
if (!$this->jobInCorrectStep($job, 'status')) {
return $this->redirectToCorrectStep($job);

View File

@ -24,6 +24,7 @@ use Illuminate\Support\Collection;
use Input;
use Log;
use Preferences;
use Response;
use Session;
use Steam;
use URL;
@ -149,13 +150,18 @@ class PiggyBankController extends Controller
*/
public function destroy(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
Session::flash('success', strval(trans('firefly.deleted_piggy_bank', ['name' => e($piggyBank->name)])));
Preferences::mark();
$piggyBankId = $piggyBank->id;
$repository->destroy($piggyBank);
return redirect(session('piggy-banks.delete.url'));
$uri = session('piggy-banks.delete.url');
if (!(strpos($uri, sprintf('piggy-banks/show/%s', $piggyBankId)) === false)) {
// uri would point back to piggy bank
$uri = route('piggy-banks.index');
}
return redirect($uri);
}
/**
@ -243,6 +249,8 @@ class PiggyBankController extends Controller
/**
* @param PiggyBankRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
*/
public function order(PiggyBankRepositoryInterface $repository)
{
@ -257,6 +265,8 @@ class PiggyBankController extends Controller
$repository->setOrder(intval($id), ($order + 1));
}
}
return Response::json(['result' => 'ok']);
}
/**

View File

@ -133,8 +133,8 @@ class ReportController extends Controller
$budget->name = strval(trans('firefly.leftUnbalanced'));
$journals = $journals->filter(
function (TransactionJournal $journal) {
$tags = $journal->tags()->where('tagMode', 'balancingAct')->count();
function (Transaction $transaction) {
$tags = $transaction->transactionJournal->tags()->where('tagMode', 'balancingAct')->count();
if ($tags === 0) {
return true;
}

View File

@ -150,7 +150,7 @@ class PreferencesController extends Controller
// custom fiscal year
$customFiscalYear = intval($request->get('customFiscalYear')) === 1;
$fiscalYearStart = date('m-d', strtotime($request->get('fiscalYearStart')));
$fiscalYearStart = date('m-d', strtotime(strval($request->get('fiscalYearStart'))));
Preferences::set('customFiscalYear', $customFiscalYear);
Preferences::set('fiscalYearStart', $fiscalYearStart);
@ -166,13 +166,17 @@ class PreferencesController extends Controller
Preferences::set('transactionPageSize', 50);
}
// two factor auth
$twoFactorAuthEnabled = intval($request->get('twoFactorAuthEnabled'));
$hasTwoFactorAuthSecret = !is_null(Preferences::get('twoFactorAuthSecret'));
$twoFactorAuthEnabled = false;
$hasTwoFactorAuthSecret = false;
if (!auth()->user()->hasRole('demo')) {
// two factor auth
$twoFactorAuthEnabled = intval($request->get('twoFactorAuthEnabled'));
$hasTwoFactorAuthSecret = !is_null(Preferences::get('twoFactorAuthSecret'));
// If we already have a secret, just set the two factor auth enabled to 1, and let the user continue with the existing secret.
if ($hasTwoFactorAuthSecret) {
Preferences::set('twoFactorAuthEnabled', $twoFactorAuthEnabled);
// If we already have a secret, just set the two factor auth enabled to 1, and let the user continue with the existing secret.
if ($hasTwoFactorAuthSecret) {
Preferences::set('twoFactorAuthEnabled', $twoFactorAuthEnabled);
}
}
// language:

View File

@ -51,6 +51,12 @@ class ProfileController extends Controller
*/
public function changePassword()
{
if (auth()->user()->hasRole('demo')) {
Session::flash('info', strval(trans('firefly.cannot_change_demo')));
return redirect(route('profile.index'));
}
$title = auth()->user()->email;
$subTitle = strval(trans('firefly.change_your_password'));
$subTitleIcon = 'fa-key';
@ -63,6 +69,12 @@ class ProfileController extends Controller
*/
public function deleteAccount()
{
if (auth()->user()->hasRole('demo')) {
Session::flash('info', strval(trans('firefly.cannot_delete_demo')));
return redirect(route('profile.index'));
}
$title = auth()->user()->email;
$subTitle = strval(trans('firefly.delete_account'));
$subTitleIcon = 'fa-trash';
@ -83,12 +95,19 @@ class ProfileController extends Controller
}
/**
* @param ProfileFormRequest $request
* @param ProfileFormRequest $request
* @param UserRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function postChangePassword(ProfileFormRequest $request)
public function postChangePassword(ProfileFormRequest $request, UserRepositoryInterface $repository)
{
if (auth()->user()->hasRole('demo')) {
Session::flash('info', strval(trans('firefly.cannot_change_demo')));
return redirect(route('profile.index'));
}
// old, new1, new2
if (!Hash::check($request->get('current_password'), auth()->user()->password)) {
Session::flash('error', strval(trans('firefly.invalid_current_password')));
@ -103,9 +122,7 @@ class ProfileController extends Controller
}
// update the user with the new password.
auth()->user()->password = bcrypt($request->get('new_password'));
auth()->user()->save();
$repository->changePassword(auth()->user(), $request->get('new_password'));
Session::flash('success', strval(trans('firefly.password_changed')));
return redirect(route('profile.index'));
@ -119,6 +136,12 @@ class ProfileController extends Controller
*/
public function postDeleteAccount(UserRepositoryInterface $repository, DeleteAccountFormRequest $request)
{
if (auth()->user()->hasRole('demo')) {
Session::flash('info', strval(trans('firefly.cannot_delete_demo')));
return redirect(route('profile.index'));
}
// old, new1, new2
if (!Hash::check($request->get('password'), auth()->user()->password)) {
Session::flash('error', strval(trans('firefly.invalid_password')));

View File

@ -57,4 +57,4 @@ class BalanceController extends Controller
return $result;
}
}
}

View File

@ -120,4 +120,4 @@ class BudgetController extends Controller
return $data;
}
}
}

View File

@ -15,7 +15,6 @@ namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
@ -170,4 +169,4 @@ class CategoryController extends Controller
}
}
}

View File

@ -241,4 +241,4 @@ class OperationsController extends Controller
return $expenses;
}
}
}

View File

@ -22,6 +22,7 @@ use FireflyIII\Models\RuleTrigger;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\Rules\TransactionMatcher;
use Illuminate\Http\Request;
use Input;
use Preferences;
use Response;
@ -86,10 +87,10 @@ class RuleController extends Controller
$subTitle = trans('firefly.make_new_rule', ['title' => $ruleGroup->title]);
// put previous url in session if not redirect from store (not "create another").
if (session('rules.rule.create.fromStore') !== true) {
Session::put('rules.rule.create.url', URL::previous());
if (session('rules.create.fromStore') !== true) {
Session::put('rules.create.url', URL::previous());
}
Session::forget('rules.rule.create.fromStore');
Session::forget('rules.create.fromStore');
Session::flash('gaEventCategory', 'rules');
Session::flash('gaEventAction', 'create-rule');
@ -110,7 +111,7 @@ class RuleController extends Controller
$subTitle = trans('firefly.delete_rule', ['title' => $rule->title]);
// put previous url in session
Session::put('rules.rule.delete.url', URL::previous());
Session::put('rules.delete.url', URL::previous());
Session::flash('gaEventCategory', 'rules');
Session::flash('gaEventAction', 'delete-rule');
@ -135,7 +136,7 @@ class RuleController extends Controller
Preferences::mark();
return redirect(session('rules.rule.delete.url'));
return redirect(session('rules.delete.url'));
}
/**
@ -178,10 +179,10 @@ class RuleController extends Controller
$subTitle = trans('firefly.edit_rule', ['title' => $rule->title]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (session('rules.rule.edit.fromUpdate') !== true) {
Session::put('rules.rule.edit.url', URL::previous());
if (session('rules.edit.fromUpdate') !== true) {
Session::put('rules.edit.url', URL::previous());
}
Session::forget('rules.rule.edit.fromUpdate');
Session::forget('rules.edit.fromUpdate');
Session::flash('gaEventCategory', 'rules');
Session::flash('gaEventAction', 'edit-rule');
@ -203,14 +204,15 @@ class RuleController extends Controller
}
/**
* @param Request $request
* @param RuleRepositoryInterface $repository
* @param Rule $rule
*
* @return \Illuminate\Http\JsonResponse
*/
public function reorderRuleActions(RuleRepositoryInterface $repository, Rule $rule)
public function reorderRuleActions(Request $request, RuleRepositoryInterface $repository, Rule $rule)
{
$ids = Input::get('actions');
$ids = $request->get('actions');
if (is_array($ids)) {
$repository->reorderRuleActions($rule, $ids);
}
@ -220,14 +222,15 @@ class RuleController extends Controller
}
/**
* @param Request $request
* @param RuleRepositoryInterface $repository
* @param Rule $rule
*
* @return \Illuminate\Http\JsonResponse
*/
public function reorderRuleTriggers(RuleRepositoryInterface $repository, Rule $rule)
public function reorderRuleTriggers(Request $request, RuleRepositoryInterface $repository, Rule $rule)
{
$ids = Input::get('triggers');
$ids = $request->get('triggers');
if (is_array($ids)) {
$repository->reorderRuleTriggers($rule, $ids);
}
@ -254,13 +257,13 @@ class RuleController extends Controller
if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('rules.rule.create.fromStore', true);
Session::put('rules.create.fromStore', true);
return redirect(route('rules.rule.create', [$ruleGroup]))->withInput();
return redirect(route('rules.create', [$ruleGroup]))->withInput();
}
// redirect to previous URL.
return redirect(session('rules.rule.create.url'));
return redirect(session('rules.create.url'));
}
@ -341,13 +344,13 @@ class RuleController extends Controller
if (intval(Input::get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('rules.rule.edit.fromUpdate', true);
Session::put('rules.edit.fromUpdate', true);
return redirect(route('rules.rule.edit', [$rule->id]))->withInput(['return_to_edit' => 1]);
return redirect(route('rules.edit', [$rule->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return redirect(session('rules.rule.edit.url'));
return redirect(session('rules.edit.url'));
}
private function createDefaultRule()

View File

@ -62,10 +62,10 @@ class RuleGroupController extends Controller
$subTitle = trans('firefly.make_new_rule_group');
// put previous url in session if not redirect from store (not "create another").
if (session('rules.rule-group.create.fromStore') !== true) {
Session::put('rules.rule-group.create.url', URL::previous());
if (session('rule-groups.create.fromStore') !== true) {
Session::put('rule-groups.create.url', URL::previous());
}
Session::forget('rules.rule-group.create.fromStore');
Session::forget('rule-groups.create.fromStore');
Session::flash('gaEventCategory', 'rules');
Session::flash('gaEventAction', 'create-rule-group');
@ -86,7 +86,7 @@ class RuleGroupController extends Controller
unset($ruleGroupList[$ruleGroup->id]);
// put previous url in session
Session::put('rules.rule-group.delete.url', URL::previous());
Session::put('rule-groups.delete.url', URL::previous());
Session::flash('gaEventCategory', 'rules');
Session::flash('gaEventAction', 'delete-rule-group');
@ -113,7 +113,7 @@ class RuleGroupController extends Controller
Preferences::mark();
return redirect(session('rules.rule-group.delete.url'));
return redirect(session('rule-groups.delete.url'));
}
/**
@ -140,10 +140,10 @@ class RuleGroupController extends Controller
$subTitle = trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (session('rules.rule-group.edit.fromUpdate') !== true) {
Session::put('rules.rule-group.edit.url', URL::previous());
if (session('rule-groups.edit.fromUpdate') !== true) {
Session::put('rule-groups.edit.url', URL::previous());
}
Session::forget('rules.rule-group.edit.fromUpdate');
Session::forget('rule-groups.edit.fromUpdate');
Session::flash('gaEventCategory', 'rules');
Session::flash('gaEventAction', 'edit-rule-group');
@ -220,13 +220,13 @@ class RuleGroupController extends Controller
if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('rules.rule-group.create.fromStore', true);
Session::put('rule-groups.create.fromStore', true);
return redirect(route('rules.rule-group.create'))->withInput();
return redirect(route('rule-groups.create'))->withInput();
}
// redirect to previous URL.
return redirect(session('rules.rule-group.create.url'));
return redirect(session('rule-groups.create.url'));
}
/**
@ -265,13 +265,13 @@ class RuleGroupController extends Controller
if (intval(Input::get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('rules.rule-group.edit.fromUpdate', true);
Session::put('rule-groups.edit.fromUpdate', true);
return redirect(route('rules.rule-group.edit', [$ruleGroup->id]))->withInput(['return_to_edit' => 1]);
return redirect(route('rule-groups.edit', [$ruleGroup->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return redirect(session('rules.rule-group.edit.url'));
return redirect(session('rule-groups.edit.url'));
}
}

View File

@ -244,4 +244,4 @@ class ConvertController extends Controller
}
}
}

View File

@ -153,16 +153,21 @@ class SingleController extends Controller
if ($this->isOpeningBalance($transactionJournal)) {
return $this->redirectToAccount($transactionJournal);
}
$type = TransactionJournal::transactionTypeStr($transactionJournal);
$journalId = $transactionJournal->id;
$type = TransactionJournal::transactionTypeStr($transactionJournal);
Session::flash('success', strval(trans('firefly.deleted_' . strtolower($type), ['description' => e($transactionJournal->description)])));
$repository->delete($transactionJournal);
Preferences::mark();
// redirect to previous URL:
return redirect(session('transactions.delete.url'));
$uri = session('transactions.delete.url');
if (!(strpos($uri, sprintf('transactions/show/%s', $journalId)) === false)) {
// uri would point back to transaction
$uri = route('transactions.index', [strtolower($type)]);
}
return redirect($uri);
}
/**

0
app/Http/Kernel.php Executable file → Normal file
View File

0
app/Http/Middleware/EncryptCookies.php Executable file → Normal file
View File

0
app/Http/Middleware/RedirectIfAuthenticated.php Executable file → Normal file
View File

0
app/Http/Middleware/VerifyCsrfToken.php Executable file → Normal file
View File

View File

@ -0,0 +1,42 @@
<?php
/**
* BudgetIncomeRequest.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
/**
* Class BudgetIncomeRequest
*
*
* @package FireflyIII\Http\Requests
*/
class BudgetIncomeRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return auth()->check();
}
/**
* @return array
*/
public function rules()
{
return [
'amount' => 'numeric|required|min:0',
];
}
}

View File

@ -42,8 +42,8 @@ class PiggyBankFormRequest extends Request
'startdate' => new Carbon,
'account_id' => intval($this->get('account_id')),
'targetamount' => round($this->get('targetamount'), 2),
'targetdate' => strlen($this->get('targetdate')) > 0 ? new Carbon($this->get('targetdate')) : null,
'note' => trim($this->get('note')),
'targetdate' => strlen(strval($this->get('targetdate'))) > 0 ? new Carbon($this->get('targetdate')) : null,
'note' => trim(strval($this->get('note'))),
];
}
@ -64,7 +64,7 @@ class PiggyBankFormRequest extends Request
'name' => $nameRule,
'account_id' => 'required|belongsToUser:accounts',
'targetamount' => 'required|min:0.01',
'amount_currency_id_targetamount' => 'exists:transaction_currencies,id',
'amount_currency_id_targetamount' => 'required|exists:transaction_currencies,id',
'startdate' => 'date',
'targetdate' => $targetDateRule,
'order' => 'integer|min:1',

View File

@ -10,13 +10,6 @@
*/
declare(strict_types = 1);
/**
* RuleGroupFormRequest.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Http\Requests;

View File

@ -413,7 +413,22 @@ Breadcrumbs::register(
'piggy-banks.show', function (BreadCrumbGenerator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.index');
$breadcrumbs->push(e($piggyBank->name), route('piggy-banks.show', [$piggyBank->id]));
}
);
Breadcrumbs::register(
'piggy-banks.add-money-mobile', function (BreadCrumbGenerator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push(trans('firefly.add_money_to_piggy', ['name' => $piggyBank->name]), route('piggy-banks.add-money-mobile', [$piggyBank->id]));
}
);
Breadcrumbs::register(
'piggy-banks.remove-money-mobile', function (BreadCrumbGenerator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push(
trans('firefly.remove_money_from_piggy_title', ['name' => $piggyBank->name]), route('piggy-banks.remove-money-mobile', [$piggyBank->id])
);
}
);
@ -500,16 +515,63 @@ Breadcrumbs::register(
);
Breadcrumbs::register(
'reports.report', function (BreadCrumbGenerator $breadcrumbs, Carbon $start, Carbon $end, $reportType, $accountIds) {
'reports.report.audit', function (BreadCrumbGenerator $breadcrumbs, string $accountIds, Carbon $start, Carbon $end) {
$breadcrumbs->parent('reports.index');
$monthFormat = (string)trans('config.month_and_day');
$title = (string)trans(
'firefly.report_' . $reportType,
['start' => $start->formatLocalized($monthFormat), 'end' => $end->formatLocalized($monthFormat)]
);
$startString = $start->formatLocalized($monthFormat);
$endString = $end->formatLocalized($monthFormat);
$title = (string)trans('firefly.report_audit', ['start' => $startString, 'end' => $endString]);
$breadcrumbs->push($title, route('reports.report', [$reportType, $start->format('Ymd'), $end->format('Ymd'), $accountIds]));
$breadcrumbs->push($title, route('reports.report.audit', [$accountIds, $start->format('Ymd'), $end->format('Ymd')]));
}
);
Breadcrumbs::register(
'reports.report.budget', function (BreadCrumbGenerator $breadcrumbs, string $accountIds, string $budgetIds, Carbon $start, Carbon $end) {
$breadcrumbs->parent('reports.index');
$monthFormat = (string)trans('config.month_and_day');
$startString = $start->formatLocalized($monthFormat);
$endString = $end->formatLocalized($monthFormat);
$title = (string)trans('firefly.report_budget', ['start' => $startString, 'end' => $endString]);
$breadcrumbs->push($title, route('reports.report.budget', [$accountIds, $budgetIds, $start->format('Ymd'), $end->format('Ymd')]));
}
);
Breadcrumbs::register(
'reports.report.category', function (BreadCrumbGenerator $breadcrumbs, string $accountIds, string $categoryIds, Carbon $start, Carbon $end) {
$breadcrumbs->parent('reports.index');
$monthFormat = (string)trans('config.month_and_day');
$startString = $start->formatLocalized($monthFormat);
$endString = $end->formatLocalized($monthFormat);
$title = (string)trans('firefly.report_category', ['start' => $startString, 'end' => $endString]);
$breadcrumbs->push($title, route('reports.report.category', [$accountIds, $categoryIds, $start->format('Ymd'), $end->format('Ymd')]));
}
);
Breadcrumbs::register(
'reports.report.default', function (BreadCrumbGenerator $breadcrumbs, string $accountIds, Carbon $start, Carbon $end) {
$breadcrumbs->parent('reports.index');
$monthFormat = (string)trans('config.month_and_day');
$startString = $start->formatLocalized($monthFormat);
$endString = $end->formatLocalized($monthFormat);
$title = (string)trans('firefly.report_default', ['start' => $startString, 'end' => $endString]);
$breadcrumbs->push($title, route('reports.report.default', [$accountIds, $start->format('Ymd'), $end->format('Ymd')]));
}
);
/**
* New user Controller
*/
Breadcrumbs::register(
'new-user.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('firefly.getting_started'), route('new-user.index'));
}
);
@ -524,47 +586,54 @@ Breadcrumbs::register(
);
Breadcrumbs::register(
'rules.rule.create', function (BreadCrumbGenerator $breadcrumbs, RuleGroup $ruleGroup) {
'rules.create', function (BreadCrumbGenerator $breadcrumbs, RuleGroup $ruleGroup) {
$breadcrumbs->parent('rules.index');
$breadcrumbs->push(trans('firefly.make_new_rule', ['title' => $ruleGroup->title]), route('rules.rule.create', [$ruleGroup]));
$breadcrumbs->push(trans('firefly.make_new_rule', ['title' => $ruleGroup->title]), route('rules.create', [$ruleGroup]));
}
);
Breadcrumbs::register(
'rules.rule.edit', function (BreadCrumbGenerator $breadcrumbs, Rule $rule) {
'rules.edit', function (BreadCrumbGenerator $breadcrumbs, Rule $rule) {
$breadcrumbs->parent('rules.index');
$breadcrumbs->push(trans('firefly.edit_rule', ['title' => $rule->title]), route('rules.rule.edit', [$rule]));
$breadcrumbs->push(trans('firefly.edit_rule', ['title' => $rule->title]), route('rules.edit', [$rule]));
}
);
Breadcrumbs::register(
'rules.rule.delete', function (BreadCrumbGenerator $breadcrumbs, Rule $rule) {
'rules.delete', function (BreadCrumbGenerator $breadcrumbs, Rule $rule) {
$breadcrumbs->parent('rules.index');
$breadcrumbs->push(trans('firefly.delete_rule', ['title' => $rule->title]), route('rules.rule.delete', [$rule]));
$breadcrumbs->push(trans('firefly.delete_rule', ['title' => $rule->title]), route('rules.delete', [$rule]));
}
);
Breadcrumbs::register(
'rules.rule-group.create', function (BreadCrumbGenerator $breadcrumbs) {
'rule-groups.create', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('rules.index');
$breadcrumbs->push(trans('firefly.make_new_rule_group'), route('rules.rule-group.create'));
$breadcrumbs->push(trans('firefly.make_new_rule_group'), route('rule-groups.create'));
}
);
Breadcrumbs::register(
'rules.rule-group.edit', function (BreadCrumbGenerator $breadcrumbs, RuleGroup $ruleGroup) {
'rule-groups.edit', function (BreadCrumbGenerator $breadcrumbs, RuleGroup $ruleGroup) {
$breadcrumbs->parent('rules.index');
$breadcrumbs->push(trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]), route('rules.rule-group.edit', [$ruleGroup]));
$breadcrumbs->push(trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]), route('rule-groups.edit', [$ruleGroup]));
}
);
Breadcrumbs::register(
'rules.rule-group.delete', function (BreadCrumbGenerator $breadcrumbs, RuleGroup $ruleGroup) {
'rule-groups.delete', function (BreadCrumbGenerator $breadcrumbs, RuleGroup $ruleGroup) {
$breadcrumbs->parent('rules.index');
$breadcrumbs->push(trans('firefly.delete_rule_group', ['title' => $ruleGroup->title]), route('rules.rule-group.delete', [$ruleGroup]));
$breadcrumbs->push(trans('firefly.delete_rule_group', ['title' => $ruleGroup->title]), route('rule-groups.delete', [$ruleGroup]));
}
);
Breadcrumbs::register(
'rules.rule-group.select_transactions', function (BreadCrumbGenerator $breadcrumbs, RuleGroup $ruleGroup) {
'rule-groups.select-transactions', function (BreadCrumbGenerator $breadcrumbs, RuleGroup $ruleGroup) {
$breadcrumbs->parent('rules.index');
$breadcrumbs->push(trans('firefly.rule_group_select_transactions', ['title' => $ruleGroup->title]), route('rule-groups.select-transactions', [$ruleGroup]));
}
);
Breadcrumbs::register(
'rule-groups.select_transactions', function (BreadCrumbGenerator $breadcrumbs, RuleGroup $ruleGroup) {
$breadcrumbs->parent('rules.index');
$breadcrumbs->push(
trans('firefly.execute_group_on_existing_transactions', ['title' => $ruleGroup->title]), route('rules.rule-group.select_transactions', [$ruleGroup])
trans('firefly.execute_group_on_existing_transactions', ['title' => $ruleGroup->title]), route('rule-groups.select_transactions', [$ruleGroup])
);
}
);

View File

@ -82,4 +82,4 @@ class TagSplit
return $set;
}
}
}

View File

@ -96,6 +96,7 @@ class ImportEntry
case 'account-id':
case 'account-iban':
case 'account-name':
case 'account-number':
$this->setObject('asset-account', $convertedValue, $certainty);
break;
case 'opposing-number':

View File

@ -23,7 +23,7 @@ use Illuminate\Support\Collection;
*
* @package FireflyIII\Import
*/
class ImportProcedure
class ImportProcedure implements ImportProcedureInterface
{
/**

View File

@ -0,0 +1,33 @@
<?php
/**
* ImportProcedureInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Import;
use FireflyIII\Models\ImportJob;
use Illuminate\Support\Collection;
/**
* Interface ImportProcedureInterface
*
* @package FireflyIII\Import
*/
interface ImportProcedureInterface
{
/**
* @param ImportJob $job
*
* @return Collection
*/
public function runImport(ImportJob $job): Collection;
}

View File

@ -376,14 +376,14 @@ class ImportStorage
$sourceData = [
'account_id' => $accounts['source']->id,
'transaction_journal_id' => $journal->id,
'description' => $journal->description,
'description' => null,
'amount' => bcmul($amount, '-1'),
];
$destinationData = [
'account_id' => $accounts['destination']->id,
'transaction_journal_id' => $journal->id,
'description' => $journal->description,
'description' => null,
'amount' => $amount,
];

View File

@ -81,7 +81,7 @@ class MailError extends Job implements ShouldQueue
['emails.error-html', 'emails.error-text'], $args,
function (Message $message) use ($email) {
if ($email != 'mail@example.com') {
$message->to($email, $email)->subject('Caught an error in Firely III.');
$message->to($email, $email)->subject('Caught an error in Firely III');
}
}
);

View File

@ -36,12 +36,25 @@ class Account extends Model
{
use SoftDeletes, ValidatingTrait;
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'active' => 'boolean',
'encrypted' => 'boolean',
];
/** @var array */
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
/** @var array */
protected $fillable = ['user_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban'];
/** @var array */
protected $hidden = ['virtual_balance_encrypted', 'encrypted'];
protected $hidden = ['encrypted'];
protected $rules
= [
'user_id' => 'required|exists:users,id',
@ -184,7 +197,7 @@ class Account extends Model
public function getNameAttribute($value): string
{
if (intval($this->encrypted) == 1) {
if ($this->encrypted) {
return Crypt::decrypt($value);
}

View File

@ -24,7 +24,18 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AccountMeta extends Model
{
protected $dates = ['created_at', 'updated_at'];
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
];
/** @var array */
protected $dates = ['created_at', 'updated_at'];
protected $fillable = ['account_id', 'name', 'data'];
protected $table = 'account_meta';

View File

@ -33,6 +33,18 @@ class AccountType extends Model
const IMPORT = 'Import account';
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
];
/** @var array */
protected $dates = ['created_at', 'updated_at'];
//

View File

@ -29,6 +29,21 @@ class Attachment extends Model
{
use SoftDeletes;
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'uploaded' => 'boolean',
];
/** @var array */
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
/** @var array */
protected $fillable = ['attachable_id', 'attachable_type', 'user_id', 'md5', 'filename', 'mime', 'title', 'notes', 'description', 'size', 'uploaded'];
/**

View File

@ -0,0 +1,63 @@
<?php
/**
* AvailableBudget.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class AvailableBudget
*
* @package FireflyIII\Models
*/
class AvailableBudget extends Model
{
use SoftDeletes;
/** @var array */
protected $fillable = ['user_id', 'transaction_currency_id', 'amount', 'start_date', 'end_date'];
/** @var array */
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'start_date' => 'date',
'end_date' => 'date',
];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function transactionCurrency()
{
return $this->belongsTo('FireflyIII\Models\TransactionCurrency');
}
/**
* @return BelongsTo
*/
public function user(): BelongsTo
{
return $this->belongsTo('FireflyIII\User');
}
}

View File

@ -29,8 +29,26 @@ class Bill extends Model
{
use ValidatingTrait;
/** @var array */
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
protected $dates = ['created_at', 'updated_at', 'date'];
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'date' => 'date',
'skip' => 'int',
'automatch' => 'boolean',
'active' => 'boolean',
'name_encrypted' => 'boolean',
'match_encrypted' => 'boolean',
];
protected $fillable
= ['name', 'match', 'amount_min', 'match_encrypted', 'name_encrypted', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip',
'automatch', 'active',];

View File

@ -30,7 +30,23 @@ class Budget extends Model
use SoftDeletes, ValidatingTrait;
protected $dates = ['created_at', 'updated_at', 'deleted_at', 'startdate', 'enddate'];
/** @var array */
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'active' => 'boolean',
'encrypted' => 'boolean',
];
protected $fillable = ['user_id', 'name', 'active'];
protected $hidden = ['encrypted'];
protected $rules = ['name' => 'required|between:1,200',];
@ -95,7 +111,7 @@ class Budget extends Model
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
if ($this->encrypted) {
return Crypt::decrypt($value);
}
@ -115,8 +131,8 @@ class Budget extends Model
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
$this->attributes['name'] = $value;
$this->attributes['encrypted'] = false;
}
/**

View File

@ -23,7 +23,20 @@ use Illuminate\Database\Eloquent\Model;
class BudgetLimit extends Model
{
protected $dates = ['created_at', 'updated_at', 'startdate'];
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'startdate' => 'date',
'repeats' => 'boolean',
];
/** @var array */
protected $dates = ['created_at', 'updated_at'];
protected $hidden = ['amount_encrypted'];
/**

View File

@ -29,10 +29,26 @@ class Category extends Model
{
use SoftDeletes, ValidatingTrait;
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'encrypted' => 'boolean',
];
/** @var array */
protected $fillable = ['user_id', 'name'];
/** @var array */
protected $hidden = ['encrypted'];
/** @var array */
protected $rules = ['name' => 'required|between:1,200',];
/** @var array */
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
/**
* @param array $fields
@ -86,7 +102,7 @@ class Category extends Model
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
if ($this->encrypted) {
return Crypt::decrypt($value);
}
@ -99,8 +115,8 @@ class Category extends Model
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
$this->attributes['name'] = $value;
$this->attributes['encrypted'] = false;
}
/**

View File

@ -25,6 +25,17 @@ class Configuration extends Model
{
use SoftDeletes;
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
];
/** @var array */
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
protected $table = 'configuration';

View File

@ -23,6 +23,15 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class ExportJob extends Model
{
/** @var array */
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
];
/** @var array */
protected $dates = ['created_at', 'updated_at'];
/**
* @param $value
*

View File

@ -27,6 +27,19 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class ImportJob extends Model
{
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
];
/** @var array */
protected $dates = ['created_at', 'updated_at'];
protected $validStatus
= [
'import_status_never_started', // initial state

View File

@ -26,6 +26,18 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class LimitRepetition extends Model
{
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'startdate' => 'date',
'enddate' => 'date',
];
protected $dates = ['created_at', 'updated_at', 'startdate', 'enddate'];
protected $hidden = ['amount_encrypted'];
@ -38,10 +50,10 @@ class LimitRepetition extends Model
{
if (auth()->check()) {
$object = self::where('limit_repetitions.id', $value)
->leftJoin('budget_limits', 'budget_limits.id', '=', 'limit_repetitions.budget_limit_id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->where('budgets.user_id', auth()->user()->id)
->first(['limit_repetitions.*']);
->leftJoin('budget_limits', 'budget_limits.id', '=', 'limit_repetitions.budget_limit_id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->where('budgets.user_id', auth()->user()->id)
->first(['limit_repetitions.*']);
if ($object) {
return $object;
}

View File

@ -23,10 +23,20 @@ use League\CommonMark\CommonMarkConverter;
*/
class Note extends Model
{
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
];
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
protected $fillable = ['title', 'text'];
/**
* @return string
*/

View File

@ -29,10 +29,25 @@ class PiggyBank extends Model
{
use SoftDeletes;
protected $dates = ['created_at', 'updated_at', 'deleted_at', 'startdate', 'targetdate'];
protected $fillable
= ['name', 'account_id', 'order', 'targetamount', 'startdate', 'targetdate'];
protected $hidden = ['targetamount_encrypted', 'encrypted'];
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'startdate' => 'date',
'targetdate' => 'date',
'order' => 'int',
'active' => 'boolean',
'encrypted' => 'boolean',
];
protected $dates = ['created_at', 'updated_at', 'deleted_at', 'startdate', 'targetdate'];
protected $fillable = ['name', 'account_id', 'order', 'targetamount', 'startdate', 'targetdate'];
protected $hidden = ['targetamount_encrypted', 'encrypted'];
/**
* @param PiggyBank $value
@ -86,7 +101,7 @@ class PiggyBank extends Model
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
if ($this->encrypted) {
return Crypt::decrypt($value);
}
@ -144,8 +159,8 @@ class PiggyBank extends Model
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
$this->attributes['name'] = $value;
$this->attributes['encrypted'] = false;
}
/**

View File

@ -23,6 +23,17 @@ use Illuminate\Database\Eloquent\Model;
class PiggyBankEvent extends Model
{
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'date' => 'date',
];
protected $dates = ['created_at', 'updated_at', 'date'];
protected $fillable = ['piggy_bank_id', 'transaction_journal_id', 'date', 'amount'];
protected $hidden = ['amount_encrypted'];

View File

@ -25,9 +25,21 @@ use Illuminate\Database\Eloquent\Model;
class PiggyBankRepetition extends Model
{
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'startdate' => 'date',
'targetdate' => 'date',
];
protected $dates = ['created_at', 'updated_at', 'startdate', 'targetdate'];
protected $fillable = ['piggy_bank_id', 'startdate', 'targetdate', 'currentamount'];
protected $hidden = ['currentamount_encrypted'];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo

View File

@ -27,6 +27,16 @@ use Log;
class Preference extends Model
{
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
];
protected $dates = ['created_at', 'updated_at'];
protected $fillable = ['user_id', 'data', 'name', 'data'];

View File

@ -23,6 +23,17 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
*/
class Role extends Model
{
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
];
protected $dates = ['created_at', 'updated_at'];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany

View File

@ -26,6 +26,23 @@ class Rule extends Model
{
use SoftDeletes;
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'active' => 'boolean',
'order' => 'int',
'stop_processing' => 'boolean',
];
/** @var array */
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
/**
* @param Rule $value
*

View File

@ -10,13 +10,6 @@
*/
declare(strict_types = 1);
/**
* RuleAction.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Models;
@ -29,6 +22,22 @@ use Illuminate\Database\Eloquent\Model;
*/
class RuleAction extends Model
{
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'active' => 'boolean',
'order' => 'int',
'stop_processing' => 'boolean',
];
/** @var array */
protected $dates = ['created_at', 'updated_at'];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/

View File

@ -25,6 +25,22 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class RuleGroup extends Model
{
use SoftDeletes;
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'active' => 'boolean',
'order' => 'int',
];
/** @var array */
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
protected $fillable = ['user_id', 'order', 'title', 'description', 'active'];

View File

@ -22,6 +22,22 @@ use Illuminate\Database\Eloquent\Model;
*/
class RuleTrigger extends Model
{
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'active' => 'boolean',
'order' => 'int',
'stop_processing' => 'boolean',
];
/** @var array */
protected $dates = ['created_at', 'updated_at'];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/

View File

@ -15,6 +15,7 @@ namespace FireflyIII\Models;
use Crypt;
use FireflyIII\Support\Models\TagSupport;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Watson\Validating\ValidatingTrait;
@ -25,11 +26,25 @@ use Watson\Validating\ValidatingTrait;
*/
class Tag extends TagSupport
{
protected $dates = ['created_at', 'updated_at', 'date'];
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts
= [
'created_at' => 'date',
'updated_at' => 'date',
'deleted_at' => 'date',
'date' => 'date',
'zoomLevel' => 'int',
];
protected $dates = ['created_at', 'updated_at', 'date', 'deleted_at'];
protected $fillable = ['user_id', 'tag', 'date', 'description', 'longitude', 'latitude', 'zoomLevel', 'tagMode'];
protected $rules = ['tag' => 'required|between:1,200',];
use ValidatingTrait;
use ValidatingTrait, SoftDeletes;
/**
* @param array $fields

Some files were not shown because too many files have changed in this diff Show More