Compare commits

...

132 Commits
3.4.8 ... 3.5.0

Author SHA1 Message Date
James Cole
d485270e1f Merge branch 'release/3.5.0' 2015-08-13 17:37:55 +02:00
James Cole
3716668e0c Updated read me. 2015-08-13 17:37:45 +02:00
James Cole
ae7fd18c34 New composer file. 2015-08-13 17:35:48 +02:00
James Cole
4f59b1d32f No Google thing when no Analytics ID present. 2015-08-13 17:35:41 +02:00
James Cole
90cb3279df Better example env file. 2015-08-13 17:34:08 +02:00
James Cole
cf0c7ef6b2 New version. 2015-08-13 17:32:22 +02:00
James Cole
47c23781d9 Fixed password reset. 2015-08-13 17:32:15 +02:00
James Cole
e258c050f7 Merge branch 'release/3.4.11' 2015-08-10 20:13:38 +02:00
James Cole
57801b2f34 Merge branch 'release/3.4.11' into develop 2015-08-10 20:13:38 +02:00
James Cole
710e9c9423 new version. 2015-08-10 20:13:33 +02:00
James Cole
deefef83bd Added sum for the current period, see issue #99 2015-08-09 17:01:12 +02:00
James Cole
51e30aed66 Added a sum of the current page and the sum of the entire category, in reference to issue #99. 2015-08-09 16:56:38 +02:00
James Cole
8d109a3cfe Fixed a null pointer exception. 2015-08-09 13:54:58 +02:00
James Cole
3424e019b5 Removed animation, again [skip ci] 2015-08-06 16:39:53 +02:00
James Cole
c6b4bceb67 Animation test [skip ci] 2015-08-06 16:39:06 +02:00
James Cole
afb4155015 Remove animation thing. [skip ci] 2015-08-06 16:37:53 +02:00
James Cole
8d99baf38a Update charts.js 2015-08-05 09:02:33 +02:00
James Cole
b91cb60328 Fix translations [skip ci] 2015-08-02 09:01:13 +02:00
James Cole
c0d62237fc Made the date thing throw a FF error. 2015-08-02 08:53:34 +02:00
James Cole
223ea80860 Fixed some embarrassing spelling errors in the CSV importer. [skip ci] 2015-08-02 08:53:19 +02:00
James Cole
5a77bef494 Sort chart and code cleanup [skip ci] 2015-08-02 07:41:47 +02:00
James Cole
80c0efe821 Small rearrangement of front page boxes. [skip ci] 2015-08-02 07:35:09 +02:00
James Cole
8044d89557 Display correct amount [skip ci] 2015-08-02 07:08:47 +02:00
James Cole
4f0ed97410 Fixed a bug where the category list in a monthly report would be empty. 2015-08-02 07:04:43 +02:00
James Cole
af7952f204 Removed old references to Google [skip ci] 2015-08-01 07:22:48 +02:00
James Cole
d8dcae856b Remove log. 2015-08-01 07:12:34 +02:00
James Cole
7296796ed9 Fix chart. 2015-08-01 07:12:03 +02:00
James Cole
a2c2bb4948 Forgot a dot [skip ci] 2015-08-01 07:09:51 +02:00
James Cole
72ebfdc20e Debug log. 2015-08-01 07:09:12 +02:00
James Cole
16b95ea78a New chart. 2015-08-01 07:04:41 +02:00
James Cole
c04f08dfd8 Filter empty budgets. 2015-07-31 18:18:54 +02:00
James Cole
a30793e818 Fix chart. Related to #99 2015-07-31 18:14:24 +02:00
James Cole
e39e1eaf21 Included opening balance. 2015-07-31 18:10:55 +02:00
James Cole
ab22d2cbaa Fixed the overview chart for categories, so it will properly reflect income and expenses. See bug #99 2015-07-31 14:26:22 +02:00
James Cole
96ddbe7227 Reorganized the category charts in the year report to properly reflect income and expenses. Necessary to facilitate the changes needed for bug #99 2015-07-31 14:20:18 +02:00
James Cole
4d09235aef Update composer.lock 2015-07-31 14:17:49 +02:00
James Cole
136b8975e3 Sort piggy bank list. 2015-07-31 13:44:56 +02:00
James Cole
e21b1eca17 Remove script. 2015-07-31 07:31:05 +02:00
James Cole
244b90b1d4 Fix bug #98 2015-07-30 21:32:58 +02:00
James Cole
b318f3f940 Merge branch 'release/3.4.10' 2015-07-27 21:23:20 +02:00
James Cole
e211c9812e Fixed some math things. 2015-07-26 19:42:28 +02:00
James Cole
eef28d96f4 Code cleanup [skip ci] 2015-07-26 19:13:06 +02:00
James Cole
c8227e09ee id was ambiguous. 2015-07-26 19:10:31 +02:00
James Cole
3e05fd91d9 Lots of cleaning up. 2015-07-26 19:07:02 +02:00
James Cole
450baba56a Removed some dead code and fixed some other. 2015-07-26 17:03:05 +02:00
James Cole
17a8c4918c Code cleanup. 2015-07-26 15:51:07 +02:00
James Cole
0e2419d61a New translations [skip ci] 2015-07-26 09:44:31 +02:00
James Cole
79b1a2ca6d Gave cron controller a new line. [skip ci] 2015-07-26 07:42:34 +02:00
James Cole
2213c68155 Added a missing breadcrumb. 2015-07-26 07:41:10 +02:00
James Cole
2492b1fa96 Expanded the message a user may get when his credentials do not work. 2015-07-26 07:39:21 +02:00
James Cole
6c6598dac5 Lots of new translations. 2015-07-26 07:39:04 +02:00
James Cole
a137112e66 New read me. 2015-07-25 19:28:19 +02:00
James Cole
8642ae8180 New version. 2015-07-25 19:27:14 +02:00
James Cole
894c4dc5a7 New composer.lock. 2015-07-25 19:24:07 +02:00
James Cole
d96063ea6e Also expand interface. 2015-07-25 19:23:00 +02:00
James Cole
c3dc193f3e First attempt at new last activity thing. 2015-07-25 19:22:41 +02:00
James Cole
3c2952009e Some new stuff. 2015-07-25 19:04:39 +02:00
James Cole
f4ade470df Remove if statements. 2015-07-25 18:48:48 +02:00
James Cole
2e33b43389 CSS and invalid account warning. 2015-07-25 18:40:45 +02:00
James Cole
92799699bc Better attachment handling. 2015-07-25 18:33:19 +02:00
James Cole
7ab0508167 Some new translations. 2015-07-25 18:31:05 +02:00
James Cole
3c65c28936 Some translations. 2015-07-25 16:48:32 +02:00
James Cole
43892da07e may edit fields [skip ci] 2015-07-25 07:05:27 +02:00
James Cole
7c436920a4 Some formatting and translations. [skip ci] 2015-07-25 07:04:09 +02:00
James Cole
89d565e63b Check for block code. [skip ci] 2015-07-25 07:03:50 +02:00
James Cole
150b6fe5b6 Add block code [skip ci] 2015-07-25 07:03:42 +02:00
James Cole
0e77574c26 Also give block code. [skip ci] 2015-07-25 07:03:35 +02:00
James Cole
df23863443 Remove bounce error thing. 2015-07-24 22:08:38 +02:00
James Cole
581bf11b21 Fixed a translation [skip ci] 2015-07-24 13:34:22 +02:00
James Cole
d602d4b429 Add some debug logging. 2015-07-24 13:26:42 +02:00
James Cole
d1d4a52934 Catch empty send grid credentials. 2015-07-24 13:23:02 +02:00
James Cole
375d113769 Find users not already blocked only. 2015-07-24 13:20:09 +02:00
James Cole
9b83974bff Improve the cron controller. Force blocked users to logout. 2015-07-24 13:17:47 +02:00
James Cole
3c68c99bd5 Fixed some translations. 2015-07-24 09:03:40 +02:00
James Cole
ec4b37c596 Updated chart. 2015-07-24 08:36:49 +02:00
James Cole
ba9601d21c Better display for piggy bank events. 2015-07-24 08:34:30 +02:00
James Cole
50c13fd469 Clear cache. 2015-07-22 22:13:40 +02:00
James Cole
7af072b8fc Show message. 2015-07-22 19:35:39 +02:00
James Cole
faa128d41e Made a cron controller. 2015-07-22 19:09:17 +02:00
James Cole
868fe46932 Some more debug stuff. 2015-07-22 18:44:51 +02:00
James Cole
e9e4307ce5 Better debug. 2015-07-22 18:09:14 +02:00
James Cole
774d4844a9 Another try to fix csrf 2015-07-22 17:58:06 +02:00
James Cole
586c53e670 Remove CSRF check. 2015-07-22 17:52:55 +02:00
James Cole
68e073fbff A new controller that can be used in combination with SendGrid. 2015-07-22 17:50:02 +02:00
James Cole
8101dc37b1 New route for attachment preview. 2015-07-19 22:19:36 +02:00
James Cole
63f16c458d Small fixes for piggy banks data seed. 2015-07-19 22:19:26 +02:00
James Cole
821e007e95 If zero, other thing. 2015-07-19 18:39:06 +02:00
James Cole
1656a2f11a Experimenting with a preview for attachments. 2015-07-19 18:37:29 +02:00
James Cole
4dbc135dce Added max file size for uploads. 2015-07-19 14:30:20 +02:00
James Cole
fc886f6bc1 Seed some tags. 2015-07-19 13:46:41 +02:00
James Cole
f93e480466 Better notifications. 2015-07-19 13:46:34 +02:00
James Cole
fe807e23f8 Fixed sorting in tags. 2015-07-19 13:46:20 +02:00
James Cole
ecf61c31f1 Add new line to file. 2015-07-19 12:23:27 +02:00
James Cole
4feff18af5 Fix test data. 2015-07-19 12:21:51 +02:00
James Cole
a07c52e0d8 Fix some route names. 2015-07-19 12:21:38 +02:00
James Cole
7bb07d7f55 Add non-breaking space to fix issue #95. 2015-07-19 12:20:35 +02:00
James Cole
f12dfc8a14 Icons for attachments. 2015-07-19 11:47:56 +02:00
James Cole
be030f15c4 New composer.lock. 2015-07-19 09:57:26 +02:00
James Cole
f5fb6c063b Also delete attachments. 2015-07-19 09:53:58 +02:00
James Cole
fb722f06b9 Some added newlines. 2015-07-19 09:38:44 +02:00
James Cole
c0ea19e15e Test data no longer runs into the future. 2015-07-19 09:37:52 +02:00
James Cole
cdeb1ad87c Some model updates. 2015-07-19 09:37:37 +02:00
James Cole
0dbe4e94fa Allow to edit an attachment. 2015-07-19 09:37:28 +02:00
James Cole
b5e2e8aa1d Edit attachment page. 2015-07-18 23:51:51 +02:00
James Cole
9502010248 Some new routes. 2015-07-18 23:06:51 +02:00
James Cole
fea0557b47 Going to allow edit of attachment. 2015-07-18 22:49:27 +02:00
James Cole
ed4fcc9011 Some optimisation. 2015-07-18 22:17:31 +02:00
James Cole
ed12ea7cfb Check for double files and some code clean up. 2015-07-18 21:46:16 +02:00
James Cole
73e526645e Uncomment providers. 2015-07-18 21:33:52 +02:00
James Cole
72aeafb2b5 Some model code block updates 2015-07-18 21:33:10 +02:00
James Cole
cc1af60cb4 Basic attachment download function. 2015-07-18 21:32:31 +02:00
James Cole
359fab315f A fix in the model and a simple view for attachments. 2015-07-18 21:12:34 +02:00
James Cole
6a9574bab9 Allow jpeg and PDF. 2015-07-18 19:49:35 +02:00
James Cole
83d6158483 Basic upload working. 2015-07-18 09:49:59 +02:00
James Cole
63ef89b6cc Basic interface for upload. 2015-07-18 09:49:29 +02:00
James Cole
b0beab4cd3 Attachment model and database changes. 2015-07-18 09:49:19 +02:00
James Cole
a34782575f Fix form and upload thing. 2015-07-18 08:59:33 +02:00
James Cole
142bdc9430 Add attachment thing to upload form. 2015-07-17 21:45:58 +02:00
James Cole
14b79cb0a4 Fixed a bug where deposits and/or transfers would be assigned budgets if you had selected a budget at the withdrawal screen earlier. 2015-07-17 21:03:13 +02:00
James Cole
ce5beeaf2c Better compare for amounts because floatval can be inaccurate. 2015-07-17 17:35:04 +02:00
James Cole
31114a2ca5 Fixed a bug where tags would be recreated instead of "found". 2015-07-17 17:34:49 +02:00
James Cole
32528094ad Updated composer file. 2015-07-16 20:39:20 +02:00
James Cole
0a2a01c44c Code reformat. 2015-07-15 21:06:26 +02:00
James Cole
c1888dc3ac Merge branch 'release/3.4.9' 2015-07-15 21:02:36 +02:00
James Cole
4d76afbe01 Merge branch 'release/3.4.9' into develop 2015-07-15 21:02:36 +02:00
James Cole
76d7a97f93 New release with all changes so far. Change log coming soon. 2015-07-15 21:02:27 +02:00
James Cole
8b1366b20a Merge pull request #93 from RonaldvanMeer/master
fix issue #91
2015-07-15 21:00:29 +02:00
RonaldvanMeer
e0f9685578 Spacing fixes 2015-07-15 12:29:11 +02:00
RonaldvanMeer
5235657954 Fixing missing IBAN field on Create New User request 2015-07-15 12:25:09 +02:00
James Cole
a15fbc8094 Now committing to correct branch. 2015-07-14 22:48:34 +02:00
James Cole
546f1d9c50 Revert "Some login and session updates."
This reverts commit 74231f552a.
2015-07-14 22:45:14 +02:00
James Cole
74231f552a Some login and session updates. 2015-07-14 22:45:00 +02:00
163 changed files with 3611 additions and 2143 deletions

View File

@@ -1,6 +1,6 @@
APP_ENV=production
APP_DEBUG=false
APP_KEY=SomeRandomString
APP_KEY=SomeRandomStringOf32CharsExactly
DB_CONNECTION=mysql
DB_HOST=localhost
@@ -19,3 +19,6 @@ ANALYTICS_ID=
EMAIL_PRETEND=false
RUNCLEANUP=true
SITE_OWNER=mail@example.com
SENDGRID_USERNAME=
SENDGRID_PASSWORD=

View File

@@ -82,7 +82,7 @@ If you're still interested please read [the installation guide](https://github.c
and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site.
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. Accounts on the demo sites will stop working after one week.
## Credits
@@ -90,6 +90,7 @@ Firefly III uses the following libraries and tools:
* The AdminLTE template by [Almsaseed Studio](https://almsaeedstudio.com/)
* The [Google charts](https://developers.google.com/chart/) library.
* [Chart.js](http://www.chartjs.org/)
* [Bootstrap](http://getbootstrap.com/)
* [Laravel](http://laravel.com/)
* [Twig](http://twig.sensiolabs.org/)

View File

@@ -40,4 +40,13 @@ interface AccountChartGenerator
* @return array
*/
public function single(Account $account, Carbon $start, Carbon $end);
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end);
}

View File

@@ -32,6 +32,65 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
return $this->frontpage($accounts, $start, $end);
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end)
{
// language:
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.spent'),
'data' => []
]
],
];
$ids = [];
foreach ($accounts as $account) {
$ids[] = $account->id;
}
$start->subDay();
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$accounts->each(
function (Account $account) use ($startBalances, $endBalances) {
$id = $account->id;
$startBalance = isset($startBalances[$id]) ? $startBalances[$id] : 0;
$endBalance = isset($endBalances[$id]) ? $endBalances[$id] : 0;
$diff = $endBalance - $startBalance;
$account->difference = round($diff, 2);
}
);
$accounts = $accounts->sortByDesc(
function (Account $account) {
return $account->difference;
}
);
foreach ($accounts as $account) {
if ($account->difference > 0) {
$data['labels'][] = $account->name;
$data['datasets'][0]['data'][] = $account->difference;
}
}
return $data;
}
/**
* @param Collection $accounts
* @param Carbon $start

View File

@@ -1,97 +0,0 @@
<?php
namespace FireflyIII\Generator\Chart\Account;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Steam;
/**
* Class GoogleAccountChartGenerator
*
* @package FireflyIII\Generator\Chart\Account
*/
class GoogleAccountChartGenerator implements AccountChartGenerator
{
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function all(Collection $accounts, Carbon $start, Carbon $end)
{
// make chart (original code):
$chart = new GChart;
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return $chart->getData();
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function frontpage(Collection $accounts, Carbon $start, Carbon $end)
{
return $this->all($accounts, $start, $end);
}
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function single(Account $account, Carbon $start, Carbon $end)
{
$current = clone $start;
$today = new Carbon;
$chart = new GChart;
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty(1);
while ($end >= $current) {
$certain = $current < $today;
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
$current->addDay();
}
$chart->generate();
return $chart->getData();
}
}

View File

@@ -38,7 +38,7 @@ class ChartJsBillChartGenerator implements BillChartGenerator
/** @var Bill $entry */
foreach ($unpaid as $entry) { // loop unpaid:
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$amount = bcdiv(bcadd($entry[0]->amount_max, $entry[0]->amount_min), 2);
$unpaidDescriptions[] = $description;
$unpaidAmount = bcadd($unpaidAmount, $amount);
unset($amount, $description);

View File

@@ -1,94 +0,0 @@
<?php
namespace FireflyIII\Generator\Chart\Bill;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
/**
* Class GoogleBillChartGenerator
*
* @package FireflyIII\Generator\Chart\Bill
*/
class GoogleBillChartGenerator implements BillChartGenerator
{
/**
* @param Collection $paid
* @param Collection $unpaid
*
* @return array
*/
public function frontpage(Collection $paid, Collection $unpaid)
{
// loop paid and create single entry:
$paidDescriptions = [];
$paidAmount = 0;
$unpaidDescriptions = [];
$unpaidAmount = 0;
bcscale(2);
/** @var TransactionJournal $entry */
foreach ($paid as $entry) {
$paidDescriptions[] = $entry->description;
$paidAmount = bcadd($paidAmount, $entry->amount);
}
// loop unpaid:
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$unpaidDescriptions[] = $description;
$unpaidAmount = bcadd($unpaidAmount, $amount);
unset($amount, $description);
}
$chart = new GChart;
$chart->addColumn(trans('firefly.name'), 'string');
$chart->addColumn(trans('firefly.amount'), 'number');
$chart->addRow(trans('firefly.unpaid') . ': ' . join(', ', $unpaidDescriptions), $unpaidAmount);
$chart->addRow(trans('firefly.paid') . ': ' . join(', ', $paidDescriptions), $paidAmount);
$chart->generate();
return $chart->getData();
}
/**
* @param Bill $bill
* @param Collection $entries
*
* @return mixed
*/
public function single(Bill $bill, Collection $entries)
{
// make chart:
$chart = new GChart;
$chart->addColumn(trans('firefly.date'), 'date');
$chart->addColumn(trans('firefly.maxAmount'), 'number');
$chart->addColumn(trans('firefly.minAmount'), 'number');
$chart->addColumn(trans('firefly.billEntry'), 'number');
/** @var TransactionJournal $result */
foreach ($entries as $result) {
$chart->addRow(
clone $result->date,
floatval($bill->amount_max),
floatval($bill->amount_min),
floatval($result->amount)
);
}
$chart->generate();
return $chart->getData();
}
}

View File

@@ -1,102 +0,0 @@
<?php
namespace FireflyIII\Generator\Chart\Budget;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
/**
* Class GoogleBudgetChartGenerator
*
* @package FireflyIII\Generator\Chart\Budget
*/
class GoogleBudgetChartGenerator implements BudgetChartGenerator
{
/**
* @param Collection $entries
*
* @return array
*/
public function budget(Collection $entries)
{
$chart = new GChart;
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
/** @var array $entry */
foreach ($entries as $entry) {
$chart->addRow($entry[0], $entry[1]);
}
$chart->generate();
return $chart->getData();
}
/**
* @codeCoverageIgnore
*
* @param Collection $entries
*
* @return array
*/
public function budgetLimit(Collection $entries)
{
return $this->budget($entries);
}
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries)
{
$chart = new GChart;
$chart->addColumn(trans('firefly.budget'), 'string');
$chart->addColumn(trans('firefly.left'), 'number');
$chart->addColumn(trans('firefly.spent'), 'number');
$chart->addColumn(trans('firefly.overspent'), 'number');
/** @var array $entry */
foreach ($entries as $entry) {
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]);
}
}
$chart->generate();
return $chart->getData();
}
/**
* @param Collection $budgets
* @param Collection $entries
*
* @return array
*/
public function year(Collection $budgets, Collection $entries)
{
$chart = new GChart;
// add columns:
$chart->addColumn(trans('firefly.month'), 'date');
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
/** @var array $entry */
foreach ($entries as $entry) {
$chart->addRowArray($entry);
}
$chart->generate();
return $chart->getData();
}
}

View File

@@ -40,5 +40,13 @@ interface CategoryChartGenerator
*
* @return array
*/
public function year(Collection $categories, Collection $entries);
public function spentInYear(Collection $categories, Collection $entries);
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInYear(Collection $categories, Collection $entries);
}

View File

@@ -29,19 +29,30 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
$format = Config::get('firefly.' . $dateFormat . '.' . $language);
$data = [
'count' => 1,
'count' => 2,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.spent'),
'data' => []
],
[
'label' => trans('firefly.earned'),
'data' => []
]
],
];
foreach ($entries as $entry) {
$data['labels'][] = $entry[0]->formatLocalized($format);
$data['datasets'][0]['data'][] = round($entry[1], 2);
$amount = round($entry[1], 2);
if ($amount > 0) {
$data['datasets'][0]['data'][] = null;
$data['datasets'][1]['data'][] = $amount;
} else {
$data['datasets'][0]['data'][] = $amount * -1;
$data['datasets'][1]['data'][] = null;
}
}
return $data;
@@ -93,7 +104,41 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
*
* @return array
*/
public function year(Collection $categories, Collection $entries)
public function spentInYear(Collection $categories, Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
foreach ($categories as $category) {
$data['labels'][] = $category->name;
}
foreach ($entries as $entry) {
$date = $entry[0]->formatLocalized($format);
array_shift($entry);
$data['count']++;
$data['datasets'][] = ['label' => $date, 'data' => $entry];
}
return $data;
}
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInYear(Collection $categories, Collection $entries)
{
// language:

View File

@@ -1,106 +0,0 @@
<?php
namespace FireflyIII\Generator\Chart\Category;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
/**
* Class GoogleCategoryChartGenerator
*
* @package FireflyIII\Generator\Chart\Category
*/
class GoogleCategoryChartGenerator implements CategoryChartGenerator
{
/**
* @param Collection $entries
*
* @return array
*/
public function all(Collection $entries)
{
$chart = new GChart;
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
/** @var array $entry */
foreach ($entries as $entry) {
$chart->addRow($entry[0], $entry[1]);
}
$chart->generate();
return $chart->getData();
}
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries)
{
$chart = new GChart;
$chart->addColumn(trans('firefly.category'), 'string');
$chart->addColumn(trans('firefly.spent'), 'number');
/** @var array $entry */
foreach ($entries as $entry) {
$sum = $entry['sum'];
if ($sum != 0) {
$chart->addRow($entry['name'], $sum);
}
}
$chart->generate();
return $chart->getData();
}
/**
* @param Collection $entries
*
* @return array
*/
public function month(Collection $entries)
{
$chart = new GChart;
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
/** @var array $entry */
foreach ($entries as $entry) {
$chart->addRow($entry[0], $entry[1]);
}
$chart->generate();
return $chart->getData();
}
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function year(Collection $categories, Collection $entries)
{
$chart = new GChart;
$chart->addColumn(trans('firefly.month'), 'date');
foreach ($categories as $category) {
$chart->addColumn($category->name, 'number');
}
/** @var array $entry */
foreach ($entries as $entry) {
$chart->addRowArray($entry);
}
$chart->generate();
return $chart->getData();
}
}

View File

@@ -9,7 +9,7 @@ use Preferences;
/**
* Class GooglePiggyBankChartGenerator
* Class ChartJsPiggyBankChartGenerator
*
* @package FireflyIII\Generator\Chart\PiggyBank
*/
@@ -26,7 +26,7 @@ class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = Config::get('firefly.monthAndDay.' . $language);
$data = [
'count' => 1,

View File

@@ -1,41 +0,0 @@
<?php
namespace FireflyIII\Generator\Chart\PiggyBank;
use Carbon\Carbon;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
/**
* Class GooglePiggyBankChartGenerator
*
* @package FireflyIII\Generator\Chart\PiggyBank
*/
class GooglePiggyBankChartGenerator implements PiggyBankChartGenerator
{
/**
* @param Collection $set
*
* @return array
*/
public function history(Collection $set)
{
$chart = new GChart;
$chart->addColumn(trans('firefly.date'), 'date');
$chart->addColumn(trans('firefly.balance'), 'number');
$sum = '0';
bcscale(2);
foreach ($set as $entry) {
$sum = bcadd($sum, $entry->sum);
$chart->addRow(new Carbon($entry->date), $sum);
}
$chart->generate();
return $chart->getData();
}
}

View File

@@ -7,7 +7,7 @@ use Illuminate\Support\Collection;
use Preferences;
/**
* Class GoogleReportChartGenerator
* Class ChartJsReportChartGenerator
*
* @package FireflyIII\Generator\Chart\Report
*/

View File

@@ -1,58 +0,0 @@
<?php
namespace FireflyIII\Generator\Chart\Report;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
/**
* Class GoogleReportChartGenerator
*
* @package FireflyIII\Generator\Chart\Report
*/
class GoogleReportChartGenerator implements ReportChartGenerator
{
/**
* @param Collection $entries
*
* @return array
*/
public function yearInOut(Collection $entries)
{
$chart = new GChart;
$chart->addColumn(trans('firefly.month'), 'date');
$chart->addColumn(trans('firefly.income'), 'number');
$chart->addColumn(trans('firefly.expenses'), 'number');
/** @var array $entry */
foreach ($entries as $entry) {
$chart->addRowArray($entry);
}
$chart->generate();
return $chart->getData();
}
/**
* @param string $income
* @param string $expense
* @param int $count
*
* @return array
*/
public function yearInOutSummarized($income, $expense, $count)
{
$chart = new GChart;
$chart->addColumn(trans('firefly.summary'), 'string');
$chart->addColumn(trans('firefly.income'), 'number');
$chart->addColumn(trans('firefly.expenses'), 'number');
$chart->addRow(trans('firefly.sum'), $income, $expense);
$chart->addRow(trans('firefly.average'), ($income / $count), ($expense / $count));
$chart->generate();
return $chart->getData();
}
}

View File

@@ -49,10 +49,12 @@ class UpdateJournalConnection
if (is_null($repetition)) {
return;
}
$amount = $journal->amount;
$diff = $amount - $event->amount; // update current repetition
bcscale(2);
$repetition->currentamount += $diff;
$amount = $journal->amount;
$diff = bcsub($amount, $event->amount); // update current repetition
$repetition->currentamount = bcadd($repetition->currentamount, $diff);
$repetition->save();

View File

@@ -0,0 +1,222 @@
<?php
namespace FireflyIII\Helpers\Attachments;
use Auth;
use Config;
use Crypt;
use FireflyIII\Models\Attachment;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\MessageBag;
use Input;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Class AttachmentHelper
*
* @package FireflyIII\Helpers\Attachments
*/
class AttachmentHelper implements AttachmentHelperInterface
{
/** @var int */
protected $maxUploadSize;
/** @var array */
protected $allowedMimes;
/** @var MessageBag */
public $errors;
/** @var MessageBag */
public $messages;
/**
*
*/
public function __construct()
{
$this->maxUploadSize = Config::get('firefly.maxUploadSize');
$this->allowedMimes = Config::get('firefly.allowedMimes');
$this->errors = new MessageBag;
$this->messages = new MessageBag;
}
/**
* @param Attachment $attachment
*
* @return string
*/
public function getAttachmentLocation(Attachment $attachment)
{
$path = storage_path('upload') . DIRECTORY_SEPARATOR . 'at-' . $attachment->id . '.data';
return $path;
}
/**
* @param Model $model
*
* @return bool
*/
public function saveAttachmentsForModel(Model $model)
{
$files = Input::file('attachments');
if (is_array($files)) {
foreach ($files as $entry) {
if (!is_null($entry)) {
$this->processFile($entry, $model);
}
}
} else {
$this->processFile($files, $model);
}
return true;
}
/**
* @param UploadedFile $file
* @param Model $model
*
* @return bool
*/
protected function hasFile(UploadedFile $file, Model $model)
{
$md5 = md5_file($file->getRealPath());
$name = $file->getClientOriginalName();
$class = get_class($model);
$count = Auth::user()->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count();
if ($count > 0) {
$msg = trans('validation.file_already_attached', ['name' => $name]);
$this->errors->add('attachments', $msg);
return true;
}
return false;
}
/**
* @param UploadedFile $file
* @param Model $model
*
* @return bool
*/
protected function validateUpload(UploadedFile $file, Model $model)
{
if (!$this->validMime($file)) {
return false;
}
if (!$this->validSize($file)) {
return false;
}
if ($this->hasFile($file, $model)) {
return false;
}
return true;
}
/**
* @param UploadedFile $file
* @param Model $model
*
* @return bool|Attachment
*/
protected function processFile(UploadedFile $file, Model $model)
{
$validation = $this->validateUpload($file, $model);
if ($validation === false) {
return false;
}
$attachment = new Attachment; // create Attachment object.
$attachment->user()->associate(Auth::user());
$attachment->attachable()->associate($model);
$attachment->md5 = md5_file($file->getRealPath());
$attachment->filename = $file->getClientOriginalName();
$attachment->mime = $file->getMimeType();
$attachment->size = $file->getSize();
$attachment->uploaded = 0;
$attachment->save();
$path = $file->getRealPath(); // encrypt and move file to storage.
$content = file_get_contents($path);
$encrypted = Crypt::encrypt($content);
// store it:
$upload = $this->getAttachmentLocation($attachment);
if (is_writable(dirname($upload))) {
file_put_contents($upload, $encrypted);
}
$attachment->uploaded = 1; // update attachment
$attachment->save();
$name = e($file->getClientOriginalName()); // add message:
$msg = trans('validation.file_attached', ['name' => $name]);
$this->messages->add('attachments', $msg);
// return it.
return $attachment;
}
/**
* @param UploadedFile $file
*
* @return bool
*/
protected function validMime(UploadedFile $file)
{
$mime = e($file->getMimeType());
$name = e($file->getClientOriginalName());
if (!in_array($mime, $this->allowedMimes)) {
$msg = trans('validation.file_invalid_mime', ['name' => $name, 'mime' => $mime]);
$this->errors->add('attachments', $msg);
return false;
}
return true;
}
/**
* @param UploadedFile $file
*
* @return bool
*/
protected function validSize(UploadedFile $file)
{
$size = $file->getSize();
$name = e($file->getClientOriginalName());
if ($size > $this->maxUploadSize) {
$msg = trans('validation.file_too_large', ['name' => $name]);
$this->errors->add('attachments', $msg);
return false;
}
return true;
}
/**
* @return MessageBag
*/
public function getErrors()
{
return $this->errors;
}
/**
* @return MessageBag
*/
public function getMessages()
{
return $this->messages;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace FireflyIII\Helpers\Attachments;
use FireflyIII\Models\Attachment;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\MessageBag;
/**
* Interface AttachmentHelperInterface
*
* @package FireflyIII\Helpers\Attachments
*/
interface AttachmentHelperInterface
{
/**
* @param Model $model
*
* @return bool
*/
public function saveAttachmentsForModel(Model $model);
/**
* @return MessageBag
*/
public function getErrors();
/**
* @return MessageBag
*/
public function getMessages();
/**
* @param Attachment $attachment
*
* @return mixed
*/
public function getAttachmentLocation(Attachment $attachment);
}

View File

@@ -34,7 +34,8 @@ class Category
*/
public function addCategory(CategoryModel $category)
{
if ($category->spent > 0) {
// spent is minus zero for an expense report:
if ($category->spent < 0) {
$this->categories->push($category);
}
}

View File

@@ -3,6 +3,9 @@
namespace FireflyIII\Helpers\Csv\Converter;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use InvalidArgumentException;
use Log;
use Session;
/**
@@ -19,8 +22,16 @@ class Date extends BasicConverter implements ConverterInterface
public function convert()
{
$format = Session::get('csv-date-format');
try {
$date = Carbon::createFromFormat($format, $this->value);
} catch (InvalidArgumentException $e) {
Log::error('Date conversion error: ' . $e->getMessage() . '. Value was "' . $this->value . '", format was "' . $format . '".');
$message = trans('firefly.csv_date_parse_error', ['format' => $format, 'value' => $this->value]);
throw new FireflyException($message);
}
return $date;
}

View File

@@ -124,7 +124,7 @@ class Importer
*/
protected function parseRow($index)
{
return (($this->data->hasHeaders() && $index > 1) || !$this->data->hasHeaders());
return (($this->data->hasHeaders() && $index >= 1) || !$this->data->hasHeaders());
}
/**

View File

@@ -7,9 +7,10 @@ use Crypt;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Csv\Mapper\MapperInterface;
use League\Csv\Reader;
use Log;
use ReflectionException;
use Session;
use Log;
/**
* Class Wizard
*
@@ -110,6 +111,7 @@ class Wizard implements WizardInterface
foreach ($fields as $field) {
if (!Session::has($field)) {
Log::error('Session is missing field: ' . $field);
return false;
}
}

View File

@@ -79,7 +79,7 @@ class ReportHelper implements ReportHelperInterface
foreach ($accounts as $account) {
$start = bcadd($start, $account->startBalance);
$end = bcadd($end, $account->endBalance);
$diff = bcadd($diff, ($account->endBalance - $account->startBalance));
$diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance));
}
$object = new AccountCollection;
@@ -255,13 +255,15 @@ class ReportHelper implements ReportHelperInterface
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$set = $repository->getBudgets();
bcscale(2);
foreach ($set as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
// no repetition(s) for this budget:
if ($repetitions->count() == 0) {
$spent = $repository->spentInPeriodCorrected($budget, $start, $end, $shared);
$spent = $repository->balanceInPeriod($budget, $start, $end, $shared);
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setOverspent($spent);
@@ -276,10 +278,10 @@ class ReportHelper implements ReportHelperInterface
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setRepetition($repetition);
$expenses = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, $shared);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? 0 : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, $shared);
$left = $expenses < $repetition->amount ? bcsub($repetition->amount, $expenses) : 0;
$spent = $expenses > $repetition->amount ? 0 : $expenses;
$overspent = $expenses > $repetition->amount ? bcsub($expenses, $repetition->amount) : 0;
$budgetLine->setLeft($left);
$budgetLine->setSpent($spent);
@@ -325,7 +327,7 @@ class ReportHelper implements ReportHelperInterface
$repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
$set = $repository->getCategories();
foreach ($set as $category) {
$spent = $repository->spentInPeriodCorrected($category, $start, $end, $shared);
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
$category->spent = $spent;
$object->addCategory($category);
$object->addTotal($spent);

View File

@@ -212,7 +212,9 @@ class ReportQuery implements ReportQueryInterface
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end)
{
return floatval(
bcscale(2);
return bcmul(
Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
@@ -221,8 +223,8 @@ class ReportQuery implements ReportQueryInterface
->before($end)
->after($start)
->where('budget_transaction_journal.budget_id', $budget->id)
->get(['transaction_journals.*'])->sum('amount')
) * -1;
->get(['transaction_journals.*'])->sum('amount'), -1
);
}
/**

View File

@@ -130,7 +130,7 @@ class AccountController extends Controller
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
'openingBalance' => $openingBalanceAmount,
'virtualBalance' => floatval($account->virtual_balance)
'virtualBalance' => round($account->virtual_balance, 2)
];
Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'accounts');
@@ -167,10 +167,11 @@ class AccountController extends Controller
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$activities = Steam::getLastActivities($ids);
$accounts->each(
function (Account $account) use ($startBalances, $endBalances) {
$account->lastActivityDate = null;//$repository->getLastActivity($account);
function (Account $account) use ($activities, $startBalances, $endBalances) {
$account->lastActivityDate = isset($activities[$account->id]) ? $activities[$account->id] : null;
$account->startBalance = isset($startBalances[$account->id]) ? $startBalances[$account->id] : null;
$account->endBalance = isset($endBalances[$account->id]) ? $endBalances[$account->id] : null;
}
@@ -209,12 +210,12 @@ class AccountController extends Controller
$accountData = [
'name' => $request->input('name'),
'accountType' => $request->input('what'),
'virtualBalance' => floatval($request->input('virtualBalance')),
'virtualBalance' => round($request->input('virtualBalance'), 2),
'active' => true,
'user' => Auth::user()->id,
'iban' => $request->input('iban'),
'accountRole' => $request->input('accountRole'),
'openingBalance' => floatval($request->input('openingBalance')),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
@@ -252,8 +253,8 @@ class AccountController extends Controller
'user' => Auth::user()->id,
'iban' => $request->input('iban'),
'accountRole' => $request->input('accountRole'),
'virtualBalance' => floatval($request->input('virtualBalance')),
'openingBalance' => floatval($request->input('openingBalance')),
'virtualBalance' => round($request->input('virtualBalance'), 2),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'ccType' => $request->input('ccType'),

View File

@@ -0,0 +1,173 @@
<?php
namespace FireflyIII\Http\Controllers;
use Crypt;
use File;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Requests\AttachmentFormRequest;
use FireflyIII\Models\Attachment;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Input;
use Preferences;
use Response;
use Session;
use URL;
use View;
/**
* Class AttachmentController
*
* @package FireflyIII\Http\Controllers
*/
class AttachmentController extends Controller
{
/**
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-paperclip');
View::share('title', trans('firefly.attachments'));
}
/**
* @param Attachment $attachment
*
* @return \Illuminate\View\View
*/
public function edit(Attachment $attachment)
{
$subTitleIcon = 'fa-pencil';
$subTitle = trans('firefly.edit_attachment', ['name' => $attachment->filename]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('attachments.edit.fromUpdate') !== true) {
Session::put('attachments.edit.url', URL::previous());
}
Session::forget('attachments.edit.fromUpdate');
return view('attachments.edit', compact('attachment', 'subTitleIcon', 'subTitle'));
}
/**
* @param Attachment $attachment
*
* @return \Illuminate\View\View
*/
public function delete(Attachment $attachment)
{
$subTitle = trans('firefly.delete_attachment', ['name' => $attachment->filename]);
// put previous url in session
Session::put('attachments.delete.url', URL::previous());
Session::flash('gaEventCategory', 'attachments');
Session::flash('gaEventAction', 'delete-attachment');
return view('attachments.delete', compact('attachment', 'subTitle'));
}
/**
* @param AttachmentRepositoryInterface $repository
* @param Attachment $attachment
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(AttachmentRepositoryInterface $repository, Attachment $attachment)
{
$name = $attachment->filename;
$repository->destroy($attachment);
Session::flash('success', trans('firefly.attachment_deleted', ['name' => $name]));
Preferences::mark();
return redirect(Session::get('attachments.delete.url'));
}
/**
* @param Attachment $attachment
*/
public function download(Attachment $attachment, AttachmentHelperInterface $helper)
{
$file = $helper->getAttachmentLocation($attachment);
if (file_exists($file)) {
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . $quoted);
header('Content-Transfer-Encoding: binary');
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . $attachment->size);
echo Crypt::decrypt(file_get_contents($file));
} else {
abort(404);
}
}
/**
* @param Attachment $attachment
*
* @return \Illuminate\Http\Response
*/
public function preview(Attachment $attachment)
{
if ($attachment->mime == 'application/pdf') {
$file = public_path('images/page_white_acrobat.png');
} else {
$file = public_path('images/page_green.png');
}
$response = Response::make(File::get($file));
$response->header('Content-Type', 'image/png');
return $response;
}
/**
* @param AttachmentFormRequest $request
* @param AttachmentRepositoryInterface $repository
* @param Attachment $attachment
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(AttachmentFormRequest $request, AttachmentRepositoryInterface $repository, Attachment $attachment)
{
$attachmentData = [
'title' => $request->input('title'),
'description' => $request->input('description'),
'notes' => $request->input('notes'),
];
$repository->update($attachment, $attachmentData);
Session::flash('success', 'Attachment "' . $attachment->filename . '" updated.');
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('attachments.edit.fromUpdate', true);
return redirect(route('attachments.edit', [$attachment->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return redirect(Session::get('attachments.edit.url'));
}
}

View File

@@ -5,9 +5,11 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Role;
use FireflyIII\User;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Mail;
use Request as Rq;
use Session;
use Twig;
use Validator;
@@ -19,19 +21,81 @@ use Validator;
*/
class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
/*
|--------------------------------------------------------------------------
| Registration & Login Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
/**
* Show the application registration form.
*
* @return \Illuminate\Http\Response
*/
public function getRegister()
{
$host = Rq::getHttpHost();
return view('auth.register', compact('host'));
}
/**
* Handle a login request to the application.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function postLogin(Request $request)
{
$this->validate(
$request, [
$this->loginUsername() => 'required', 'password' => 'required',
]
);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
return $this->sendLockoutResponse($request);
}
$credentials = $this->getCredentials($request);
$credentials['blocked'] = 0; // most not be blocked.
if (Auth::attempt($credentials, $request->has('remember'))) {
return $this->handleUserWasAuthenticated($request, $throttles);
}
// default error message:
$message = $this->getFailedLoginMessage();
// try to find a blocked user with this email address.
/** @var User $foundUser */
$foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first();
if (!is_null($foundUser)) {
// if it exists, show message:
$code = $foundUser->blocked_code;
$message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]);
}
// try
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
if ($throttles) {
$this->incrementLoginAttempts($request);
}
return redirect($this->loginPath())
->withInput($request->only($this->loginUsername(), 'remember'))
->withErrors(
[
$this->loginUsername() => $message,
]
);
}
use AuthenticatesAndRegistersUsers;
public $redirectTo = '/';

View File

@@ -152,7 +152,7 @@ class BudgetController extends Controller
foreach ($budgets as $budget) {
$date = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$budget->spent = $repository->spentInPeriodCorrected($budget, $date, $end);
$budget->spent = $repository->balanceInPeriod($budget, $date, $end);
$budget->currentRep = $repository->getCurrentRepetition($budget, $date);
if ($budget->currentRep) {
$budgeted = bcadd($budgeted, $budget->currentRep->amount);

View File

@@ -151,10 +151,12 @@ class CategoryController extends Controller
$page = intval(Input::get('page'));
$set = $repository->getJournals($category, $page);
$count = $repository->countJournals($category);
$totalSum = $repository->journalsSum($category);
$periodSum = $repository->journalsSum($category, Session::get('start'), Session::get('end'));
$journals = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('categories/show/' . $category->id);
return view('categories.show', compact('category', 'journals', 'hideCategory'));
return view('categories.show', compact('category', 'journals', 'hideCategory', 'totalSum', 'periodSum'));
}
/**

View File

@@ -79,6 +79,36 @@ class AccountController extends Controller
return Response::json($data);
}
/**
* Shows the balances for all the user's expense accounts.
*
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function expenseAccounts(AccountRepositoryInterface $repository)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = clone Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getAccounts(['Expense account', 'Beneficiary account']);
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expenseAccounts');
$cache->addProperty('accounts');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$data = $this->generator->expenseAccounts($accounts, $start, $end);
$cache->store($data);
return Response::json($data);
}
/**
* Shows the balances for all the user's frontpage accounts.
*

View File

@@ -68,7 +68,7 @@ class BudgetController extends Controller
$end->subDay();
$chartDate = clone $end;
$chartDate->startOfMonth();
$spent = $repository->spentInPeriodCorrected($budget, $first, $end);
$spent = $repository->balanceInPeriod($budget, $first, $end);
$entries->push([$chartDate, $spent]);
$first = Navigation::addPeriod($first, $range, 0);
}
@@ -156,13 +156,13 @@ class BudgetController extends Controller
foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
if ($repetitions->count() == 0) {
$expenses = $repository->spentInPeriodCorrected($budget, $start, $end, true);
$expenses = $repository->balanceInPeriod($budget, $start, $end, true);
$allEntries->push([$budget->name, 0, 0, $expenses, 0, 0]);
continue;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, true);
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, true);
// $left can be less than zero.
// $overspent can be more than zero ( = overspending)
@@ -202,7 +202,8 @@ class BudgetController extends Controller
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$budgets = $repository->getBudgets();
$allBudgets = $repository->getBudgets();
$budgets = new Collection;
// chart properties for cache:
$cache = new CacheProperties();
@@ -214,6 +215,15 @@ class BudgetController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore
}
// filter empty budgets:
foreach ($allBudgets as $budget) {
$spent = $repository->balanceInPeriod($budget, $start, $end, $shared);
if ($spent != 0) {
$budgets->push($budget);
}
}
$entries = new Collection;
while ($start < $end) {
@@ -224,14 +234,14 @@ class BudgetController extends Controller
// each budget, fill the row:
foreach ($budgets as $budget) {
$spent = $repository->spentInPeriodCorrected($budget, $start, $month, $shared);
$row[] = $spent;
$spent = $repository->balanceInPeriod($budget, $start, $month, $shared);
$row[] = $spent * -1;
}
$entries->push($row);
$start->endOfMonth()->addDay();
}
$data = $this->generator->year($budgets, $entries);
$data = $this->generator->year($allBudgets, $entries);
$cache->store($data);
return Response::json($data);

View File

@@ -50,7 +50,6 @@ class CategoryController extends Controller
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = new Carbon;
$entries = new Collection;
@@ -66,7 +65,7 @@ class CategoryController extends Controller
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriodCorrected($category, $start, $currentEnd);
$spent = $repository->balanceInPeriod($category, $start, $currentEnd);
$entries->push([clone $start, $spent]);
$start = Navigation::addPeriod($start, $range, 0);
@@ -170,7 +169,7 @@ class CategoryController extends Controller
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function year(CategoryRepositoryInterface $repository, $year, $shared = false)
public function spentInYear(CategoryRepositoryInterface $repository, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
@@ -179,14 +178,24 @@ class CategoryController extends Controller
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category');
$cache->addProperty('year');
$cache->addProperty('spent-in-year');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$shared = $shared == 'shared' ? true : false;
$categories = $repository->getCategories();
$allCategories = $repository->getCategories();
$entries = new Collection;
$categories = $allCategories->filter(
function (Category $category) use ($repository, $start, $end, $shared) {
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
if ($spent < 0) {
return $category;
}
return null;
}
);
while ($start < $end) {
$month = clone $start; // month is the current end of the period
@@ -194,15 +203,76 @@ class CategoryController extends Controller
$row = [clone $start]; // make a row:
foreach ($categories as $category) { // each budget, fill the row
$spent = $repository->spentInPeriodCorrected($category, $start, $month, $shared);
$row[] = $spent;
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
if ($spent < 0) {
$row[] = $spent * -1;
} else {
$row[] = 0;
}
}
$entries->push($row);
$start->addMonth();
}
$data = $this->generator->spentInYear($categories, $entries);
$cache->store($data);
$data = $this->generator->year($categories, $entries);
return Response::json($data);
}
/**
* This chart will only show income.
*
* @param CategoryRepositoryInterface $repository
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function earnedInYear(CategoryRepositoryInterface $repository, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$cache = new CacheProperties; // chart properties for cache:
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category');
$cache->addProperty('earned-in-year');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$shared = $shared == 'shared' ? true : false;
$allCategories = $repository->getCategories();
$allEntries = new Collection;
$categories = $allCategories->filter(
function (Category $category) use ($repository, $start, $end, $shared) {
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
if ($spent > 0) {
return $category;
}
return null;
}
);
while ($start < $end) {
$month = clone $start; // month is the current end of the period
$month->endOfMonth();
$row = [clone $start]; // make a row:
foreach ($categories as $category) { // each budget, fill the row
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
if ($spent > 0) {
$row[] = $spent;
} else {
$row[] = 0;
}
}
$allEntries->push($row);
$start->addMonth();
}
$data = $this->generator->earnedInYear($categories, $allEntries);
$cache->store($data);
return Response::json($data);

View File

@@ -0,0 +1,64 @@
<?php
namespace FireflyIII\Http\Controllers;
use FireflyIII\User;
/**
* Class WebhookController
*
* @package FireflyIII\Http\Controllers
*/
class CronController extends Controller
{
/**
* Firefly doesn't have anything that should be in the a cron job, except maybe this one, and it's fairly exceptional.
*
* If you use SendGrid like I do, you can detect bounces and thereby check if users gave an invalid address. If they did,
* it's easy to block them and change their password. Optionally, you could notify yourself about it and send them a message.
*
* But thats something not supported right now.
*/
public function sendgrid()
{
if (strlen(env('SENDGRID_USERNAME')) > 0 && strlen(env('SENDGRID_PASSWORD')) > 0) {
$URL = 'https://api.sendgrid.com/api/bounces.get.json';
$parameters = [
'api_user' => env('SENDGRID_USERNAME'),
'api_key' => env('SENDGRID_PASSWORD'),
'date' => 1,
'days' => 7
];
$fullURL = $URL . '?' . http_build_query($parameters);
$data = json_decode(file_get_contents($fullURL));
/*
* Loop the result, if any.
*/
if (is_array($data)) {
echo 'Found ' . count($data) . ' entries in the SendGrid bounce list.' . "\n";
foreach ($data as $entry) {
$address = $entry->email;
$user = User::where('email', $address)->where('blocked', 0)->first();
if (!is_null($user)) {
echo 'Found a user: ' . $address . ', who is now blocked.' . "\n";
$user->blocked = 1;
$user->blocked_code = 'bounced';
$user->password = 'bounced';
$user->save();
} else {
echo 'Found no user: ' . $address . ', did nothing.' . "\n";
}
}
}
echo 'Done!' . "\n";
} else {
echo 'Please fill in SendGrid details.';
}
}
}

View File

@@ -1,5 +1,6 @@
<?php namespace FireflyIII\Http\Controllers;
use Artisan;
use Auth;
use Carbon\Carbon;
use Config;
@@ -58,6 +59,7 @@ class HomeController extends Controller
Session::clear();
Artisan::call('cache:clear');
return redirect(route('index'));
}
@@ -85,10 +87,8 @@ class HomeController extends Controller
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$showTour = Preferences::get('tour', true)->data;
$accounts = $repository->getFrontpageAccounts($frontPage);
$savings = $repository->getSavingsAccounts();
$piggyBankAccounts = $repository->getPiggyBankAccounts();

View File

@@ -161,7 +161,7 @@ class JsonController extends Controller
}
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$current = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$current = bcdiv(bcadd($entry[0]->amount_max, $entry[0]->amount_min), 2);
$amount = bcadd($amount, $current);
}

View File

@@ -53,12 +53,13 @@ class NewUserController extends Controller
// create normal asset account:
$assetAccount = [
'name' => $request->get('bank_name'),
'iban' => null,
'accountType' => 'asset',
'virtualBalance' => 0,
'active' => true,
'user' => Auth::user()->id,
'accountRole' => 'defaultAsset',
'openingBalance' => floatval($request->input('bank_balance')),
'openingBalance' => round($request->input('bank_balance'), 2),
'openingBalanceDate' => new Carbon,
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
];
@@ -69,12 +70,13 @@ class NewUserController extends Controller
if (strlen($request->get('savings_balance') > 0)) {
$savingsAccount = [
'name' => $request->get('bank_name') . ' savings account',
'iban' => null,
'accountType' => 'asset',
'virtualBalance' => 0,
'active' => true,
'user' => Auth::user()->id,
'accountRole' => 'savingAsset',
'openingBalance' => floatval($request->input('savings_balance')),
'openingBalance' => round($request->input('savings_balance'), 2),
'openingBalanceDate' => new Carbon,
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
];
@@ -86,8 +88,9 @@ class NewUserController extends Controller
if (strlen($request->get('credit_card_limit') > 0)) {
$creditAccount = [
'name' => 'Credit card',
'iban' => null,
'accountType' => 'asset',
'virtualBalance' => floatval($request->get('credit_card_limit')),
'virtualBalance' => round($request->get('credit_card_limit'), 2),
'active' => true,
'user' => Auth::user()->id,
'accountRole' => 'ccAsset',

View File

@@ -47,10 +47,11 @@ class PiggyBankController extends Controller
*/
public function add(AccountRepositoryInterface $repository, PiggyBank $piggyBank)
{
bcscale(2);
$date = Session::get('end', Carbon::now()->endOfMonth());
$leftOnAccount = $repository->leftOnAccount($piggyBank->account, $date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar;
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
$maxAmount = min($leftOnAccount, $leftToSave);
return view('piggy-banks.add', compact('piggyBank', 'maxAmount'));
@@ -66,7 +67,7 @@ class PiggyBankController extends Controller
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$subTitle = trans('firefly.create_new_piggybank');
$subTitle = trans('firefly.new_piggy_bank');
$subTitleIcon = 'fa-plus';
// put previous url in session if not redirect from store (not "create another").
@@ -171,9 +172,9 @@ class PiggyBankController extends Controller
$accounts = [];
/** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) {
$piggyBank->savedSoFar = floatval($piggyBank->currentRelevantRep()->currentamount);
$piggyBank->savedSoFar = round($piggyBank->currentRelevantRep()->currentamount, 2);
$piggyBank->percentage = $piggyBank->savedSoFar != 0 ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0;
$piggyBank->leftToSave = $piggyBank->targetamount - $piggyBank->savedSoFar;
$piggyBank->leftToSave = bcsub($piggyBank->targetamount, $piggyBank->savedSoFar);
/*
* Fill account information:
@@ -185,7 +186,7 @@ class PiggyBankController extends Controller
'balance' => Steam::balance($account, $end, true),
'leftForPiggyBanks' => $repository->leftOnAccount($account, $end),
'sumOfSaved' => $piggyBank->savedSoFar,
'sumOfTargets' => floatval($piggyBank->targetamount),
'sumOfTargets' => round($piggyBank->targetamount, 2),
'leftToSave' => $piggyBank->leftToSave
];
} else {
@@ -211,7 +212,7 @@ class PiggyBankController extends Controller
if (is_array($data)) {
foreach ($data as $order => $id) {
$repository->setOrder(intval($id), (intval($order) + 1));
$repository->setOrder(intval($id), ($order + 1));
}
}
}
@@ -225,13 +226,13 @@ class PiggyBankController extends Controller
*/
public function postAdd(PiggyBankRepositoryInterface $repository, AccountRepositoryInterface $accounts, PiggyBank $piggyBank)
{
$amount = round(floatval(Input::get('amount')), 2);
bcscale(2);
$amount = round(Input::get('amount'), 2);
$date = Session::get('end', Carbon::now()->endOfMonth());
$leftOnAccount = $accounts->leftOnAccount($piggyBank->account, $date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar;
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
$maxAmount = round(min($leftOnAccount, $leftToSave), 2);
bcscale(2);
if ($amount <= $maxAmount) {
$repetition = $piggyBank->currentRelevantRep();
@@ -258,7 +259,7 @@ class PiggyBankController extends Controller
*/
public function postRemove(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$amount = floatval(Input::get('amount'));
$amount = round(Input::get('amount'), 2);
bcscale(2);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
@@ -319,7 +320,7 @@ class PiggyBankController extends Controller
'name' => $request->get('name'),
'startdate' => new Carbon,
'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')),
'targetamount' => round($request->get('targetamount'), 2),
'remind_me' => false,
'reminder_skip' => 0,
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
@@ -354,7 +355,7 @@ class PiggyBankController extends Controller
'name' => $request->get('name'),
'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate,
'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')),
'targetamount' => round($request->get('targetamount'), 2),
'remind_me' => false,
'reminder_skip' => 0,
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,

View File

@@ -4,6 +4,7 @@ use Auth;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
use FireflyIII\Http\Requests\ProfileFormRequest;
use FireflyIII\User;
use Hash;
use Session;
@@ -20,7 +21,7 @@ class ProfileController extends Controller
*/
public function changePassword()
{
return view('profile.change-password')->with('title', Auth::user()->email)->with('subTitle', 'Change your password')->with(
return view('profile.change-password')->with('title', Auth::user()->email)->with('subTitle', trans('firefly.change_your_password'))->with(
'mainTitleIcon', 'fa-user'
);
}
@@ -30,7 +31,7 @@ class ProfileController extends Controller
*/
public function deleteAccount()
{
return view('profile.delete-account')->with('title', Auth::user()->email)->with('subTitle', 'Delete account')->with(
return view('profile.delete-account')->with('title', Auth::user()->email)->with('subTitle', trans('firefly.delete_account'))->with(
'mainTitleIcon', 'fa-user'
);
}
@@ -41,7 +42,7 @@ class ProfileController extends Controller
*/
public function index()
{
return view('profile.index')->with('title', 'Profile')->with('subTitle', Auth::user()->email)->with('mainTitleIcon', 'fa-user');
return view('profile.index')->with('title', trans('firefly.profile'))->with('subTitle', Auth::user()->email)->with('mainTitleIcon', 'fa-user');
}
/**
@@ -53,7 +54,7 @@ class ProfileController extends Controller
{
// old, new1, new2
if (!Hash::check($request->get('current_password'), Auth::user()->password)) {
Session::flash('error', 'Invalid current password!');
Session::flash('error', trans('firefly.invalid_current_password'));
return redirect(route('profile.change-password'));
}
@@ -68,7 +69,7 @@ class ProfileController extends Controller
Auth::user()->password = $request->get('new_password');
Auth::user()->save();
Session::flash('success', 'Password changed!');
Session::flash('success', trans('firefly.password_changed'));
return redirect(route('profile'));
}
@@ -83,7 +84,7 @@ class ProfileController extends Controller
protected function validatePassword($old, $new1)
{
if ($new1 == $old) {
return 'The idea is to change your password.';
return trans('firefly.should_change');
}
return true;
@@ -100,17 +101,28 @@ class ProfileController extends Controller
{
// old, new1, new2
if (!Hash::check($request->get('password'), Auth::user()->password)) {
Session::flash('error', 'Invalid password!');
Session::flash('error', trans('firefly.invalid_password'));
return redirect(route('profile.delete-account'));
}
// DELETE!
$email = Auth::user()->email;
Auth::user()->delete();
Session::flush();
Session::flash('gaEventCategory', 'user');
Session::flash('gaEventAction', 'delete-account');
// create a new user with the same email address so re-registration is blocked.
User::create(
[
'email' => $email,
'password' => 'deleted',
'blocked' => 1,
'blocked_code' => 'deleted'
]
);
return redirect(route('index'));
}

View File

@@ -188,21 +188,24 @@ class TagController extends Controller
/** @var Collection $tags */
$tags = Auth::user()->tags()->where('tagMode', $type)->orderBy('date', 'ASC')->get();
$tags = $tags->sortBy(
function (Tag $tag) {
return strtolower($tag->tag);
$date = !is_null($tag->date) ? $tag->date->format('Ymd') : '000000';
return strtolower($date . $tag->tag);
}
);
/** @var Tag $tag */
foreach ($tags as $tag) {
$year = is_null($tag->date) ? trans('firefly.no_year') : $tag->date->year;
$month = is_null($tag->date) ? trans('firefly.no_month') : $tag->date->formatLocalized($this->monthFormat);
$collection[$type][$year][$month][] = $tag;
}
}
$year = is_null($tag->date) ? trans('firefly.no_year') : $tag->date->year;
$monthFormatted = is_null($tag->date) ? trans('firefly.no_month') : $tag->date->formatLocalized($this->monthFormat);
$collection[$type][$year][$monthFormatted][] = $tag;
}
}
return view('tags.index', compact('title', 'mainTitleIcon', 'types', 'helpHidden', 'collection'));
}

View File

@@ -1,12 +1,15 @@
<?php namespace FireflyIII\Http\Controllers;
use Amount;
use Auth;
use Carbon\Carbon;
use Config;
use ExpandedForm;
use FireflyIII\Events\JournalCreated;
use FireflyIII\Events\JournalSaved;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Requests\JournalFormRequest;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -15,6 +18,7 @@ use Input;
use Preferences;
use Response;
use Session;
use Steam;
use URL;
use View;
@@ -43,10 +47,21 @@ class TransactionController extends Controller
*/
public function create(AccountRepositoryInterface $repository, $what = 'deposit')
{
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
$uploadSize = min($maxFileSize, $maxPostSize);
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = trans('form.noBudget');
$piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get());
// piggy bank list:
$piggyBanks = Auth::user()->piggyBanks()->orderBy('order', 'ASC')->get();
/** @var PiggyBank $piggy */
foreach ($piggyBanks as $piggy) {
$piggy->name = $piggy->name . ' (' . Amount::format($piggy->currentRelevantRep()->currentamount, false) . ')';
}
$piggies = ExpandedForm::makeSelectList($piggyBanks);
$piggies[0] = trans('form.noPiggybank');
$preFilled = Session::has('preFilled') ? Session::get('preFilled') : [];
$respondTo = ['account_id', 'account_from_id'];
@@ -68,7 +83,7 @@ class TransactionController extends Controller
asort($piggies);
return view('transactions.create', compact('accounts', 'budgets', 'what', 'piggies', 'subTitle'));
return view('transactions.create', compact('accounts', 'uploadSize', 'budgets', 'what', 'piggies', 'subTitle'));
}
/**
@@ -121,6 +136,9 @@ class TransactionController extends Controller
*/
public function edit(AccountRepositoryInterface $repository, TransactionJournal $journal)
{
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
$uploadSize = min($maxFileSize, $maxPostSize);
$what = strtolower($journal->transactionType->type);
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
@@ -179,7 +197,7 @@ class TransactionController extends Controller
Session::forget('transactions.edit.fromUpdate');
return view('transactions.edit', compact('journal', 'accounts', 'what', 'budgets', 'piggies', 'subTitle'))->with('data', $preFilled);
return view('transactions.edit', compact('journal', 'uploadSize', 'accounts', 'what', 'budgets', 'piggies', 'subTitle'))->with('data', $preFilled);
}
/**
@@ -238,10 +256,11 @@ class TransactionController extends Controller
*/
public function show(JournalRepositoryInterface $repository, TransactionJournal $journal)
{
bcscale(2);
$journal->transactions->each(
function (Transaction $t) use ($journal, $repository) {
$t->before = $repository->getAmountBefore($journal, $t);
$t->after = $t->before + $t->amount;
$t->after = bcadd($t->before, $t->amount);
}
);
$what = strtolower($journal->transactionType->type);
@@ -256,15 +275,33 @@ class TransactionController extends Controller
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository, AttachmentHelperInterface $att)
{
$journalData = $request->getJournalData();
// if not withdrawal, unset budgetid.
if ($journalData['what'] != 'withdrawal') {
$journalData['budget_id'] = 0;
}
$journal = $repository->store($journalData);
// save attachments:
$att->saveAttachmentsForModel($journal);
// flash errors
if (count($att->getErrors()->get('attachments')) > 0) {
Session::flash('error', $att->getErrors()->get('attachments'));
}
// flash messages
if (count($att->getMessages()->get('attachments')) > 0) {
Session::flash('info', $att->getMessages()->get('attachments'));
}
// rescan journal, UpdateJournalConnection
event(new JournalSaved($journal));
// ConnectJournalToPiggyBank
if ($journal->transactionType->type == 'Transfer' && intval($request->get('piggy_bank_id')) > 0) {
event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
}
@@ -288,16 +325,29 @@ class TransactionController extends Controller
/**
* @param JournalFormRequest $request
* @param JournalRepositoryInterface $repository
* @param AttachmentHelperInterface $att
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\RedirectResponse
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, AttachmentHelperInterface $att, TransactionJournal $journal)
{
$journalData = $request->getJournalData();
$repository->update($journal, $journalData);
// save attachments:
$att->saveAttachmentsForModel($journal);
// flash errors
if (count($att->getErrors()->get('attachments')) > 0) {
Session::flash('error', $att->getErrors()->get('attachments'));
}
// flash messages
if (count($att->getMessages()->get('attachments')) > 0) {
Session::flash('info', $att->getMessages()->get('attachments'));
}
event(new JournalSaved($journal));
// update, get events by date and sort DESC

View File

@@ -52,6 +52,11 @@ class Authenticate
return redirect()->guest('auth/login');
}
}
if (intval($this->auth->user()->blocked) == 1) {
return redirect()->route('logout');
}
// if logged in, set user language:
$pref = Preferences::get('language', 'en');
App::setLocale($pref->data);

View File

@@ -24,5 +24,4 @@ class VerifyCsrfToken extends BaseVerifier
{
return parent::handle($request, $next);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace FireflyIII\Http\Requests;
use Auth;
/**
* Class AttachmentFormRequest
*
* @codeCoverageIgnore
* @package FireflyIII\Http\Requests
*/
class AttachmentFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
return [
'title' => 'between:1,255',
'description' => 'between:1,65536',
'notes' => 'between:1,65536',
];
}
}

View File

@@ -31,9 +31,9 @@ class BillFormRequest extends Request
return [
'name' => $this->get('name'),
'match' => $this->get('match'),
'amount_min' => floatval($this->get('amount_min')),
'amount_currency_id' => floatval($this->get('amount_currency_id')),
'amount_max' => floatval($this->get('amount_max')),
'amount_min' => round($this->get('amount_min'), 2),
'amount_currency_id' => round($this->get('amount_currency_id'), 2),
'amount_max' => round($this->get('amount_max'), 2),
'date' => new Carbon($this->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $this->get('repeat_freq'),

View File

@@ -37,7 +37,7 @@ class JournalFormRequest extends Request
'account_to_id' => intval($this->get('account_to_id')),
'expense_account' => $this->get('expense_account'),
'revenue_account' => $this->get('revenue_account'),
'amount' => floatval($this->get('amount')),
'amount' => round($this->get('amount'), 2),
'user' => Auth::user()->id,
'amount_currency_id' => intval($this->get('amount_currency_id')),
'date' => new Carbon($this->get('date')),

View File

@@ -280,9 +280,16 @@ Breadcrumbs::register(
}
);
Breadcrumbs::register(
'change-password', function (Generator $breadcrumbs) {
'profile.change-password', function (Generator $breadcrumbs) {
$breadcrumbs->parent('profile');
$breadcrumbs->push(trans('breadcrumbs.changePassword'), route('change-password'));
$breadcrumbs->push(trans('breadcrumbs.changePassword'), route('profile.change-password'));
}
);
Breadcrumbs::register(
'profile.delete-account', function (Generator $breadcrumbs) {
$breadcrumbs->parent('profile');
$breadcrumbs->push(trans('firefly.delete_account'), route('profile.delete-account'));
}
);

View File

@@ -1,5 +1,6 @@
<?php
use FireflyIII\Models\Account;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
@@ -42,6 +43,19 @@ Route::bind(
}
);
Route::bind(
'attachment', function ($value) {
if (Auth::check()) {
$object = Attachment::where('id', $value)->where('user_id', Auth::user()->id)->first();
if ($object) {
return $object;
}
}
throw new NotFoundHttpException;
}
);
Route::bind(
'currency', function ($value) {
if (Auth::check()) {
@@ -144,6 +158,7 @@ Route::bind(
* Auth\AuthController
*/
Route::get('/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'register']);
Route::get('/cron/sendgrid', ['uses' => 'CronController@sendgrid']);
Route::controllers(
[
@@ -177,6 +192,21 @@ Route::group(
Route::post('/accounts/update/{account}', ['uses' => 'AccountController@update', 'as' => 'accounts.update']);
Route::post('/accounts/destroy/{account}', ['uses' => 'AccountController@destroy', 'as' => 'accounts.destroy']);
/**
* Attachment Controller
*/
Route::post('/attachment/update/{attachment}', ['uses' => 'AttachmentController@update', 'as' => 'attachments.update']);
Route::post('/attachment/destroy/{attachment}', ['uses' => 'AttachmentController@destroy', 'as' => 'attachments.destroy']);
Route::get('/attachment/edit/{attachment}', ['uses' => 'AttachmentController@edit', 'as' => 'attachments.edit']);
Route::get('/attachment/delete/{attachment}', ['uses' => 'AttachmentController@delete', 'as' => 'attachments.delete']);
Route::get('/attachment/show/{attachment}', ['uses' => 'AttachmentController@show', 'as' => 'attachments.show']);
Route::get('/attachment/preview/{attachment}', ['uses' => 'AttachmentController@preview', 'as' => 'attachments.preview']);
Route::get('/attachment/download/{attachment}', ['uses' => 'AttachmentController@download', 'as' => 'attachments.download']);
/**
* Bills Controller
*/
@@ -252,6 +282,7 @@ Route::group(
*/
// accounts:
Route::get('/chart/account/frontpage', ['uses' => 'Chart\AccountController@frontpage']);
Route::get('/chart/account/expense', ['uses' => 'Chart\AccountController@expenseAccounts']);
Route::get('/chart/account/month/{year}/{month}/{shared?}', ['uses' => 'Chart\AccountController@all'])->where(
['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']
);
@@ -270,7 +301,12 @@ Route::group(
// categories:
Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']);
Route::get('/chart/category/year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/chart/category/spent-in-year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@spentInYear'])->where(
['year' => '[0-9]{4}', 'shared' => 'shared']
);
Route::get('/chart/category/earned-in-year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@earnedInYear'])->where(
['year' => '[0-9]{4}', 'shared' => 'shared']
);
Route::get('/chart/category/{category}/month', ['uses' => 'Chart\CategoryController@month']); // should be period.
Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']);

190
app/Models/Attachment.php Normal file
View File

@@ -0,0 +1,190 @@
<?php
namespace FireflyIII\Models;
use Crypt;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class Attachment
*
* @package FireflyIII\Models
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $deleted_at
* @property integer $attachable_id
* @property string $attachable_type
* @property integer $user_id
* @property string $md5
* @property string $filename
* @property string $mime
* @property integer $size
* @property boolean $uploaded
* @property-read \ $attachable
* @property-read \FireflyIII\User $user
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereDeletedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereAttachableId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereAttachableType($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereMd5($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereFilename($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereMime($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereSize($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereUploaded($value)
* @property string $title
* @property string $description
* @property string $notes
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereTitle($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereDescription($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereNotes($value)
*/
class Attachment extends Model
{
use SoftDeletes;
protected $fillable = ['attachable_id', 'attachable_type', 'user_id', 'md5', 'filename', 'mime', 'title', 'notes', 'description', 'size', 'uploaded'];
/**
* Get all of the owning imageable models.
*/
public function attachable()
{
return $this->morphTo();
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return null|string
*/
public function getFilenameAttribute($value)
{
if (is_null($value)) {
return null;
}
return Crypt::decrypt($value);
}
/**
* @param string $value
*/
public function setFilenameAttribute($value)
{
$this->attributes['filename'] = Crypt::encrypt($value);
}
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return null|string
*/
public function getMimeAttribute($value)
{
if (is_null($value)) {
return null;
}
return Crypt::decrypt($value);
}
/**
* @param string $value
*/
public function setMimeAttribute($value)
{
$this->attributes['mime'] = Crypt::encrypt($value);
}
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return null|string
*/
public function getTitleAttribute($value)
{
if (is_null($value)) {
return null;
}
return Crypt::decrypt($value);
}
/**
* @param string $value
*/
public function setTitleAttribute($value)
{
$this->attributes['title'] = Crypt::encrypt($value);
}
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return null|string
*/
public function getDescriptionAttribute($value)
{
if (is_null($value)) {
return null;
}
return Crypt::decrypt($value);
}
/**
* @param string $value
*/
public function setDescriptionAttribute($value)
{
$this->attributes['description'] = Crypt::encrypt($value);
}
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return null|string
*/
public function getNotesAttribute($value)
{
if (is_null($value)) {
return null;
}
return Crypt::decrypt($value);
}
/**
* @param string $value
*/
public function setNotesAttribute($value)
{
$this->attributes['notes'] = Crypt::encrypt($value);
}
}

View File

@@ -25,7 +25,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereEncrypted($value)
* @property-read float $spent
* @property-read \Carbon\Carbon $lastActivity
* @property \Carbon\Carbon $lastActivity
*/
class Category extends Model
{

View File

@@ -38,6 +38,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereRemindMe($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereReminderSkip($value)
* @property-read \FireflyIII\Models\PiggyBankRepetition $currentRep
* @property string $reminder
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereReminder($value)
*/
class PiggyBank extends Model
{

View File

@@ -64,7 +64,7 @@ class Tag extends Model
// everything but the tag:
unset($fields['tagMode']);
$search = $fields;
unset($search['name']);
unset($search['tag']);
$query = Tag::orderBy('id');
foreach ($search as $name => $value) {

View File

@@ -66,8 +66,9 @@ use Watson\Validating\ValidatingTrait;
* @property-read bool $joinedTransactions
* @property-read bool $joinedTransactionTypes
* @property-read int $account_id
* @property-read string $name
* @property string $name
* @property-read string $symbol
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Attachment[] $attachments
*/
class TransactionJournal extends Model
{
@@ -467,6 +468,14 @@ class TransactionJournal extends Model
$this->attributes['encrypted'] = true;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function attachments()
{
return $this->morphMany('FireflyIII\Models\Attachment', 'attachable');
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo

View File

@@ -88,13 +88,16 @@ class FireflyServiceProvider extends ServiceProvider
$this->app->bind('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface', 'FireflyIII\Repositories\PiggyBank\PiggyBankRepository');
$this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository');
$this->app->bind('FireflyIII\Repositories\Tag\TagRepositoryInterface', 'FireflyIII\Repositories\Tag\TagRepository');
$this->app->bind('FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface', 'FireflyIII\Repositories\Attachment\AttachmentRepository');
$this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
// CSV import
$this->app->bind('FireflyIII\Helpers\Csv\WizardInterface', 'FireflyIII\Helpers\Csv\Wizard');
// attachments
$this->app->bind('FireflyIII\Helpers\Attachments\AttachmentHelperInterface', 'FireflyIII\Helpers\Attachments\AttachmentHelper');
// make charts:
// alternative is Google instead of ChartJs
$this->app->bind('FireflyIII\Generator\Chart\Account\AccountChartGenerator', 'FireflyIII\Generator\Chart\Account\ChartJsAccountChartGenerator');
$this->app->bind('FireflyIII\Generator\Chart\Bill\BillChartGenerator', 'FireflyIII\Generator\Chart\Bill\ChartJsBillChartGenerator');
$this->app->bind('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator', 'FireflyIII\Generator\Chart\Budget\ChartJsBudgetChartGenerator');

View File

@@ -128,17 +128,17 @@ class AccountRepository implements AccountRepositoryInterface
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$query = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account']);
if ($preference->data == []) {
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
} else {
$accounts = Auth::user()->accounts()->whereIn('id', $preference->data)->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
if (count($preference->data) > 0) {
$query->whereIn('accounts.id', $preference->data);
}
$cache->store($accounts);
$result = $query->get(['accounts.*']);
return $accounts;
$cache->store($result);
return $result;
}
/**
@@ -207,23 +207,6 @@ class AccountRepository implements AccountRepositoryInterface
}
/**
* @param Account $account
*
* @return Carbon|null
*/
public function getLastActivity(Account $account)
{
$lastTransaction = $account->transactions()->leftJoin(
'transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
)->orderBy('transaction_journals.date', 'DESC')->first(['transactions.account_id', 'transaction_journals.date']);
if ($lastTransaction) {
return $lastTransaction->date;
}
return null;
}
/**
* Get the accounts of a user that have piggy banks connected to them.
*
@@ -253,6 +236,7 @@ class AccountRepository implements AccountRepositoryInterface
if (count($ids) > 0) {
$accounts = Auth::user()->accounts()->whereIn('id', $ids)->get();
}
bcscale(2);
$accounts->each(
function (Account $account) use ($start, $end) {
@@ -266,7 +250,7 @@ class AccountRepository implements AccountRepositoryInterface
// sum of piggy bank amounts on this account:
// diff between endBalance and piggyBalance.
// then, percentage.
$difference = $account->endBalance - $account->piggyBalance;
$difference = bcsub($account->endBalance, $account->piggyBalance);
$account->difference = $difference;
$account->percentage = $difference != 0 && $account->endBalance != 0 ? round((($difference / $account->endBalance) * 100)) : 100;
@@ -294,13 +278,15 @@ class AccountRepository implements AccountRepositoryInterface
$start = clone Session::get('start', new Carbon);
$end = clone Session::get('end', new Carbon);
bcscale(2);
$accounts->each(
function (Account $account) use ($start, $end) {
$account->startBalance = Steam::balance($account, $start);
$account->endBalance = Steam::balance($account, $end);
// diff (negative when lost, positive when gained)
$diff = $account->endBalance - $account->startBalance;
$diff = bcsub($account->endBalance, $account->startBalance);
if ($diff < 0 && $account->startBalance > 0) {
// percentage lost compared to start.
@@ -429,11 +415,11 @@ class AccountRepository implements AccountRepositoryInterface
}
/**
* @return float
* @return string
*/
public function sumOfEverything()
{
return floatval(Auth::user()->transactions()->sum('amount'));
return Auth::user()->transactions()->sum('amount');
}
/**
@@ -562,10 +548,8 @@ class AccountRepository implements AccountRepositoryInterface
*/
protected function storeInitialBalance(Account $account, Account $opposing, array $data)
{
$type = $data['openingBalance'] < 0 ? 'Withdrawal' : 'Deposit';
$transactionType = TransactionType::whereType($type)->first();
$journal = new TransactionJournal(
$transactionType = TransactionType::whereType('Opening balance')->first();
$journal = TransactionJournal::create(
[
'user_id' => $data['user'],
'transaction_type_id' => $transactionType->id,
@@ -577,7 +561,6 @@ class AccountRepository implements AccountRepositoryInterface
'encrypted' => true
]
);
$journal->save();
if ($data['openingBalance'] < 0) {
$firstAccount = $opposing;
@@ -590,6 +573,7 @@ class AccountRepository implements AccountRepositoryInterface
$firstAmount = $data['openingBalance'];
$secondAmount = $data['openingBalance'] * -1;
}
$one = new Transaction(['account_id' => $firstAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $firstAmount]);
$one->save();// first transaction: from

View File

@@ -96,14 +96,7 @@ interface AccountRepositoryInterface
public function getJournals(Account $account, $page);
/**
* @param Account $account
*
* @return Carbon|null
*/
public function getLastActivity(Account $account);
/**
* @return float
* @return string
*/
public function sumOfEverything();

View File

@@ -0,0 +1,47 @@
<?php
namespace FireflyIII\Repositories\Attachment;
use FireflyIII\Models\Attachment;
/**
* Class AttachmentRepository
*
* @package FireflyIII\Repositories\Attachment
*/
class AttachmentRepository implements AttachmentRepositoryInterface
{
/**
* @param Attachment $attachment
*
* @return bool
*/
public function destroy(Attachment $attachment)
{
/** @var \FireflyIII\Helpers\Attachments\AttachmentHelperInterface $helper */
$helper = app('FireflyIII\Helpers\Attachments\AttachmentHelperInterface');
$file = $helper->getAttachmentLocation($attachment);
unlink($file);
$attachment->delete();
}
/**
* @param Attachment $attachment
* @param array $data
*
* @return Attachment
*/
public function update(Attachment $attachment, array $data)
{
$attachment->title = $data['title'];
$attachment->description = $data['description'];
$attachment->notes = $data['notes'];
$attachment->save();
return $attachment;
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace FireflyIII\Repositories\Attachment;
use FireflyIII\Models\Attachment;
/**
* Interface AttachmentRepositoryInterface
*
* @package FireflyIII\Repositories\Attachment
*/
interface AttachmentRepositoryInterface
{
/**
* @param Attachment $attachment
*
* @return bool
*/
public function destroy(Attachment $attachment);
/**
* @param Attachment $attachment
* @param array $attachmentData
*
* @return Attachment
*/
public function update(Attachment $attachment, array $attachmentData);
}

View File

@@ -52,9 +52,10 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
*/
public function expensesOnDayCorrected(Budget $budget, Carbon $date)
{
$sum = floatval($budget->transactionjournals()->transactionTypes(['Withdrawal'])->onDate($date)->get(['transaction_journals.*'])->sum('amount'));
bcscale(2);
$sum = $budget->transactionjournals()->transactionTypes(['Withdrawal'])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
return $sum * -1;
return bcmul($sum, -1);
}
/**
@@ -247,7 +248,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
->first(['limit_repetitions.*']);
if ($repetition) {
return floatval($repetition->amount);
return $repetition->amount;
}
return null;
@@ -299,7 +300,9 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
->transactionTypes(['Withdrawal'])
->get(['transaction_journals.*'])->sum('amount');
return floatval($noBudgetSet) * -1;
bcscale(2);
return bcmul($noBudgetSet, -1);
}
/**
@@ -310,9 +313,9 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
*
* @return string
*/
public function spentInPeriodCorrected(Budget $budget, Carbon $start, Carbon $end, $shared = true)
public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true)
{
return $this->spentInPeriod($budget, $start, $end, $shared);
return $this->commonBalanceInPeriod($budget, $start, $end, $shared);
}
/**

View File

@@ -136,7 +136,7 @@ interface BudgetRepositoryInterface
*
* @return string
*/
public function spentInPeriodCorrected(Budget $budget, Carbon $start, Carbon $end, $shared = true);
public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true);
/**
* @param array $data

View File

@@ -57,6 +57,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
return $set;
}
/**
*
* @param Carbon $start
@@ -64,7 +65,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
*
* @return array
*/
public function getCategoriesAndExpensesCorrected($start, $end)
public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end)
{
$set = Auth::user()->transactionjournals()
->leftJoin(
@@ -89,7 +90,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$result[$categoryId] = [
'name' => $name,
'sum' => floatval($entry->amount),
'sum' => $entry->amount,
];
}
@@ -183,9 +184,9 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
*
* @return string
*/
public function spentInPeriodCorrected(Category $category, Carbon $start, Carbon $end, $shared = false)
public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, $shared = false)
{
return $this->spentInPeriod($category, $start, $end, $shared);
return $this->commonBalanceInPeriod($category, $start, $end, $shared);
}
/**
@@ -194,11 +195,11 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
* @param Category $category
* @param Carbon $date
*
* @return float
* @return string
*/
public function spentOnDaySumCorrected(Category $category, Carbon $date)
{
return floatval($category->transactionjournals()->onDate($date)->get(['transaction_journals.*'])->sum('amount'));
return $category->transactionjournals()->onDate($date)->get(['transaction_journals.*'])->sum('correct_amount');
}
/**
@@ -233,4 +234,32 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
return $category;
}
/**
* This method returns the sum of the journals in the category, optionally
* limited by a start or end date.
*
* @param Category $category
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function journalsSum(Category $category, Carbon $start = null, Carbon $end = null)
{
$query = $category->transactionJournals()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC');
if (!is_null($start)) {
$query->after($start);
}
if (!is_null($end)) {
$query->before($end);
}
return $query->get(['transaction_journals.*'])->sum('correct_amount');
}
}

View File

@@ -40,7 +40,7 @@ interface CategoryRepositoryInterface
*
* @return array
*/
public function getCategoriesAndExpensesCorrected($start, $end);
public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end);
/**
* @param Category $category
@@ -57,6 +57,18 @@ interface CategoryRepositoryInterface
*/
public function getJournals(Category $category, $page);
/**
* This method returns the sum of the journals in the category, optionally
* limited by a start or end date.
*
* @param Category $category
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function journalsSum(Category $category, Carbon $start = null, Carbon $end = null);
/**
* @param Category $category
*
@@ -83,7 +95,7 @@ interface CategoryRepositoryInterface
*
* @return string
*/
public function spentInPeriodCorrected(Category $category, Carbon $start, Carbon $end, $shared = false);
public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, $shared = false);
/**
*

View File

@@ -252,13 +252,13 @@ class JournalRepository implements JournalRepositoryInterface
// update the from and to transaction.
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if (floatval($transaction->amount) < 0) {
if ($transaction->amount < 0) {
// this is the from transaction, negative amount:
$transaction->amount = $data['amount'] * -1;
$transaction->account_id = $fromAccount->id;
$transaction->save();
}
if (floatval($transaction->amount) > 0) {
if ($transaction->amount > 0) {
$transaction->amount = $data['amount'];
$transaction->account_id = $toAccount->id;
$transaction->save();

View File

@@ -82,10 +82,11 @@ class PiggyBankPart
*/
public function percentage()
{
bcscale(2);
if ($this->getCurrentamount() < $this->getCumulativeAmount()) {
$pct = 0;
// calculate halfway point?
if ($this->getCumulativeAmount() - $this->getCurrentamount() < $this->getAmountPerBar()) {
if (bcsub($this->getCumulativeAmount(), $this->getCurrentamount()) < $this->getAmountPerBar()) {
$left = $this->getCurrentamount() % $this->getAmountPerBar();
$pct = round($left / $this->getAmountPerBar() * 100);
}

View File

@@ -24,7 +24,7 @@ class ComponentRepository
*
* @return string
*/
protected function spentInPeriod($object, Carbon $start, Carbon $end, $shared = false)
protected function commonBalanceInPeriod($object, Carbon $start, Carbon $end, $shared = false)
{
$cache = new CacheProperties; // we must cache this.
$cache->addProperty($object->id);
@@ -32,7 +32,7 @@ class ComponentRepository
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($shared);
$cache->addProperty('spentInPeriod');
$cache->addProperty('balanceInPeriod');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
@@ -45,14 +45,14 @@ class ComponentRepository
// do something else, SEE budgets.
// get all journals in this month where the asset account is NOT shared.
$sum = $object->transactionjournals()->before($end)->after($start)
->transactionTypes(['Withdrawal'])
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->transactionTypes(['Withdrawal', 'Deposit', 'Opening balance'])
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)->where('account_meta.data', '!=', '"sharedAsset"')->get(['transaction_journals.*'])->sum('amount');
)->where('account_meta.data', '!=', '"sharedAsset"')->get(['transaction_journals.*'])->sum('correct_amount');
}
$cache->store($sum);

View File

@@ -64,16 +64,16 @@ class Amount
if ($coloured === true) {
if ($amount === 0.0) {
return '<span style="color:#999">' . $symbol . ' ' . $string . '</span>';
return '<span style="color:#999">' . $symbol . '&nbsp;' . $string . '</span>';
}
if ($amount > 0) {
return '<span class="text-success">' . $symbol . ' ' . $string . '</span>';
return '<span class="text-success">' . $symbol . '&nbsp;' . $string . '</span>';
}
return '<span class="text-danger">' . $symbol . ' ' . $string . '</span>';
return '<span class="text-danger">' . $symbol . '&nbsp;' . $string . '</span>';
}
return $symbol . ' ' . $string;
return $symbol . '&nbsp;' . $string;
}
/**

View File

@@ -20,6 +20,25 @@ use Session;
class ExpandedForm
{
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
*/
public function staticText($name, $value, array $options = [])
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$html = view('form.static', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
/**
* @param $name
* @param null $value

View File

@@ -2,6 +2,7 @@
namespace FireflyIII\Support;
use Auth;
use Carbon\Carbon;
use DB;
use FireflyIII\Models\Account;
@@ -14,6 +15,28 @@ use FireflyIII\Models\Transaction;
*/
class Steam
{
/**
* @param array $accounts
*
* @return array
*/
public function getLastActivities(array $accounts)
{
$list = [];
$set = Auth::user()->transactions()
->whereIn('account_id', $accounts)
->groupBy('account_id')
->get(['transactions.account_id', DB::Raw('MAX(`transaction_journals`.`date`) as `max_date`')]);
foreach ($set as $entry) {
$list[intval($entry->account_id)] = new Carbon($entry->max_date);
}
return $list;
}
/**
*
* @param \FireflyIII\Models\Account $account
@@ -90,4 +113,33 @@ class Steam
return $result;
}
// parse PHP size:
/**
* @param $string
*
* @return int
*/
public function phpBytes($string)
{
$string = strtolower($string);
if (!(strpos($string, 'k') === false)) {
// has a K in it, remove the K and multiply by 1024.
$bytes = bcmul(rtrim($string, 'k'), 1024);
return intval($bytes);
}
if (!(strpos($string, 'm') === false)) {
// has a M in it, remove the M and multiply by 1048576.
$bytes = bcmul(rtrim($string, 'm'), 1048576);
return intval($bytes);
}
return $string;
}
}

View File

@@ -35,7 +35,9 @@ class General extends Twig_Extension
$this->formatAmountPlain(),
$this->formatJournal(),
$this->balance(),
$this->getAccountRole()
$this->getAccountRole(),
$this->formatFilesize(),
$this->mimeIcon(),
];
}
@@ -66,6 +68,50 @@ class General extends Twig_Extension
return 'FireflyIII\Support\Twig\General';
}
/**
* @return Twig_SimpleFilter
*/
protected function formatFilesize()
{
return new Twig_SimpleFilter(
'filesize', function ($size) {
$size = intval($size);
// less than one GB, more than one MB
if ($size < (1024 * 1024 * 2014) && $size >= (1024 * 1024)) {
return round($size / (1024 * 1024), 2) . ' MB';
}
// less than one MB
if ($size < (1024 * 1024)) {
return round($size / 1024, 2) . ' KB';
}
return $size . ' bytes';
}
);
}
/**
* @return Twig_SimpleFilter
*/
protected function mimeIcon()
{
return new Twig_SimpleFilter(
'mimeIcon', function ($string) {
switch ($string) {
default:
return 'fa-file-o';
case 'application/pdf':
return 'fa-file-pdf-o';
case 'image/png':
case 'image/jpeg':
return 'fa-file-image-o';
}
}, ['is_safe' => ['html']]
);
}
/**
* @return Twig_SimpleFilter
*/

View File

@@ -33,6 +33,11 @@ use Zizaco\Entrust\Traits\EntrustUserTrait;
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\User wherePassword($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereReset($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereRememberToken($value)
* @property boolean $blocked
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Attachment[] $attachments
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereBlocked($value)
* @property string $blocked_code
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereBlockedCode($value)
*/
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
@@ -44,7 +49,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
*
* @var array
*/
protected $fillable = ['email', 'password'];
protected $fillable = ['email', 'password', 'blocked', 'blocked_code'];
/**
* The attributes excluded from the model's JSON form.
*
@@ -66,6 +71,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return $this->hasMany('FireflyIII\Models\Account');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function attachments()
{
return $this->hasMany('FireflyIII\Models\Attachment');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/

View File

@@ -30,18 +30,12 @@
"rcrowe/twigbridge": "0.7.x@dev",
"zizaco/entrust": "dev-laravel-5",
"codeception/codeception": "*",
"league/csv": "^7.1"
"league/csv": "^7.1",
"nesbot/carbon": "^1.20"
},
"require-dev": {
"barryvdh/laravel-debugbar": "@stable",
"barryvdh/laravel-ide-helper": "~2.0",
"phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1",
"satooshi/php-coveralls": "0.6.1",
"mockery/mockery": "0.9.*",
"league/factory-muffin": "~2.1",
"codeclimate/php-test-reporter": "^0.1.2",
"fzaninotto/faker": "^1.4"
"barryvdh/laravel-ide-helper": "~2.0"
},
"autoload": {
"classmap": [

607
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -147,8 +147,8 @@ return [
* Application Service Providers...
*/
'FireflyIII\Providers\AppServiceProvider',
//'FireflyIII\Providers\BusServiceProvider',
//'FireflyIII\Providers\ConfigServiceProvider',
'FireflyIII\Providers\BusServiceProvider',
'FireflyIII\Providers\ConfigServiceProvider',
'FireflyIII\Providers\EventServiceProvider',
'FireflyIII\Providers\RouteServiceProvider',
'FireflyIII\Providers\FireflyServiceProvider',

View File

@@ -28,6 +28,7 @@ return [
'default' => env('DB_CONNECTION', 'mysql'),
/*
|--------------------------------------------------------------------------
| Database Connections

View File

@@ -2,10 +2,12 @@
return [
'chart' => 'chartjs',
'version' => '3.4.8',
'version' => '3.5.0',
'index_periods' => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'],
'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
'csv_import_enabled' => true,
'maxUploadSize' => 5242880,
'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'],
'piggy_bank_periods' => [
'week' => 'Week',
'month' => 'Month',

View File

@@ -2,22 +2,6 @@
return [
/*
|--------------------------------------------------------------------------
| Default Session Driver
|--------------------------------------------------------------------------
|
| This option controls the default session "driver" that will be used on
| requests. By default, we will use the lightweight native driver but
| you may specify any of the other wonderful drivers provided here.
|
| Supported: "file", "cookie", "database", "apc",
| "memcached", "redis", "array"
|
*/
'driver' => env('SESSION_DRIVER', 'file'),
/*
|--------------------------------------------------------------------------
| Session Lifetime
@@ -33,18 +17,6 @@ return [
'expire_on_close' => false,
/*
|--------------------------------------------------------------------------
| Session Encryption
|--------------------------------------------------------------------------
|
| This option allows you to easily specify that all of your session data
| should be encrypted before it is stored. All encryption will be run
| automatically by Laravel and you can use the Session like normal.
|
*/
'encrypt' => false,
/*
|--------------------------------------------------------------------------
@@ -59,18 +31,6 @@ return [
'files' => storage_path() . '/framework/sessions',
/*
|--------------------------------------------------------------------------
| Session Database Connection
|--------------------------------------------------------------------------
|
| When using the "database" or "redis" session drivers, you may specify a
| connection that should be used to manage these sessions. This should
| correspond to a connection in your database configuration options.
|
*/
'connection' => null,
/*
|--------------------------------------------------------------------------
@@ -98,18 +58,13 @@ return [
'lottery' => [2, 100],
/*
|--------------------------------------------------------------------------
| Session Cookie Name
|--------------------------------------------------------------------------
|
| Here you may change the name of the cookie used to identify a session
| instance by ID. The name specified here will get used every time a
| new session cookie is created by the framework for every driver.
|
*/
'driver' => env('SESSION_DRIVER', 'database'),
'cookie' => 'firefly_session',
'cookie' => 'laravel_session',
'connection' => env('DB_CONNECTION', 'mysql'),
'encrypt' => true,
/*
|--------------------------------------------------------------------------

View File

@@ -145,7 +145,7 @@ return [
'ExpandedForm' => [
'is_safe' => [
'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location',
'multiRadio','file','multiCheckbox'
'multiRadio', 'file', 'multiCheckbox', 'staticText'
]
],
'Form' => [

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* Class ChangesForV349
*/
class ChangesForV349 extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// add "blocked" to users:
Schema::table(
'users', function (Blueprint $table) {
$table->boolean('blocked')->default(0);
}
);
}
}

View File

@@ -0,0 +1,56 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* Class ChangesForV3410
*/
class ChangesForV3410 extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('attachments');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create(
'attachments', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->integer('attachable_id')->unsigned();
$table->string('attachable_type');
$table->integer('user_id')->unsigned();
$table->string('md5', 32);
$table->text('filename');
$table->text('title')->nullable();
$table->text('description')->nullable();
$table->text('notes')->nullable();
$table->text('mime');
$table->integer('size')->unsigned();
$table->tinyInteger('uploaded', false, true)->default(0);
}
);
// add "blocked_code" to users:
Schema::table(
'users', function (Blueprint $table) {
$table->string('blocked_code', 25)->nullable();
}
);
}
}

View File

@@ -68,6 +68,9 @@ class TestDataSeeder extends Seeder
// pay daily groceries:
$this->createGroceries($current);
// create tag (each type of tag, for date):
$this->createTags($current);
// go out for drinks:
$this->createDrinksAndOthers($current);
@@ -84,6 +87,23 @@ class TestDataSeeder extends Seeder
}
/**
* @param Carbon $date
*/
protected function createTags(Carbon $date)
{
Tag::create(
[
'user_id' => $this->user->id,
'tag' => 'SomeTag' . $date->month . '.' . $date->year . '.nothing',
'tagMode' => 'nothing',
'date' => $date->format('Y-m-d'),
]
);
}
/**
*
*/
@@ -267,7 +287,7 @@ class TestDataSeeder extends Seeder
'startdate' => '2015-04-01',
'reminder_skip' => 0,
'remind_me' => 0,
'order' => 1,
'order' => 2,
]
);
$repetition = $phone->piggyBankRepetitions()->first();
@@ -305,7 +325,7 @@ class TestDataSeeder extends Seeder
'startdate' => '2015-04-01',
'reminder_skip' => 0,
'remind_me' => 0,
'order' => 1,
'order' => 3,
]
);
$repetition = $couch->piggyBankRepetitions()->first();
@@ -334,6 +354,20 @@ class TestDataSeeder extends Seeder
'amount' => '40'
]
);
// empty one.
PiggyBank::create(
[
'account_id' => $account->id,
'name' => 'New head set',
'targetamount' => 500,
'startdate' => '2015-04-01',
'reminder_skip' => 0,
'remind_me' => 0,
'order' => 4,
]
);
}
/**
@@ -364,6 +398,10 @@ class TestDataSeeder extends Seeder
protected function createIncome($description, Carbon $date, $amount)
{
$date = new Carbon($date->format('Y-m') . '-23'); // paid on 23rd.
$today = new Carbon;
if ($date >= $today) {
return null;
}
$toAccount = $this->findAccount('MyBank Checking Account');
$fromAccount = $this->findAccount('Job');
$category = Category::firstOrCreateEncrypted(['name' => 'Salary', 'user_id' => $this->user->id]);
@@ -596,6 +634,7 @@ class TestDataSeeder extends Seeder
{
$start = clone $date;
$end = clone $date;
$today = new Carbon;
$start->startOfMonth();
$end->endOfMonth();
@@ -605,7 +644,7 @@ class TestDataSeeder extends Seeder
$budget = Budget::firstOrCreateEncrypted(['name' => 'Groceries', 'user_id' => $this->user->id]);
$current = clone $start;
while ($current < $end) {
while ($current < $end && $current < $today) {
// daily groceries:
$amount = rand(1000, 2500) / 100;
$toAccount = $this->findAccount($stores[rand(0, count($stores) - 1)]);
@@ -651,10 +690,11 @@ class TestDataSeeder extends Seeder
{
$start = clone $date;
$end = clone $date;
$today = new Carbon;
$start->startOfMonth();
$end->endOfMonth();
$current = clone $start;
while ($current < $end) {
while ($current < $end && $current < $today) {
// weekly drink:
$thisDate = clone $current;

40
pu.sh
View File

@@ -1,40 +0,0 @@
#!/bin/bash
# set testing environment
cp .env.testing .env
# test!
if [ -z "$1" ]
then
phpunit --verbose
fi
# directories to look in:
dirs=("controllers" "database" "factories" "generators" "helpers" "models" "middleware" "repositories" "support")
if [ ! -z "$1" ]
then
for i in "${dirs[@]}"
do
firstFile="./tests/$i/$1.php"
secondFile="./tests/$i/$1Test.php"
if [ -f "$firstFile" ]
then
# run it!
phpunit --verbose $firstFile
exit $?
fi
if [ -f "$secondFile" ]
then
# run it!
phpunit --verbose $secondFile
exit $?
fi
done
fi
# restore .env file
cp .env.local .env

View File

@@ -1,5 +1,5 @@
#daterange {cursor:pointer;}
.google-chart-error {height:30px;background:url('/images/error.png') no-repeat center center;}
.general-chart-error {height:30px;background:url('/images/error.png') no-repeat center center;}
.handle {cursor:move;}
.ui-sortable-placeholder {
@@ -11,3 +11,9 @@
margin: 0;
padding: 0;
}
/* fix login box */
.login-box {width:600px;}
.login-logo {width:360px;margin-left:110px;}
.login-box-body {width:360px;margin-left:110px;}
.register-box-body {width:360px;margin-left:110px;}

BIN
public/images/image.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

BIN
public/images/page_green.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

View File

@@ -18,18 +18,9 @@
|
*/
/**
* Adding c3.php for code coverage during codeception tests
* ref: https://github.com/Codeception/c3
*/
if (file_exists(__DIR__ . '/../c3.php')) {
require __DIR__ . '/../c3.php';
}
require __DIR__ . '/../bootstrap/autoload.php';
/*
|--------------------------------------------------------------------------
| Turn On The Lights
@@ -65,5 +56,4 @@ $response = $kernel->handle(
$response->send();
$kernel->terminate($request, $response);

View File

@@ -3,18 +3,6 @@
/*
Make some colours:
*/
/*
#555299
#4285f4
#
#
#
#
#
#
#
#", "#", "#", "#"],
*/
var colourSet = [
[53, 124, 165],
[0, 141, 76],
@@ -54,6 +42,7 @@ var defaultAreaOptions = {
datasetStrokeWidth: 1,
pointHitDetectionRadius: 5,
datasetFill: true,
animation: false,
scaleFontSize: 10,
responsive: false,
scaleLabel: " <%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
@@ -68,6 +57,7 @@ var defaultPieOptions = {
datasetStrokeWidth: 1,
pointHitDetectionRadius: 5,
datasetFill: false,
animation: false,
scaleFontSize: 10,
responsive: false,
tooltipFillColor: "rgba(0,0,0,0.5)",
@@ -81,6 +71,7 @@ var defaultLineOptions = {
pointDotRadius: 2,
datasetStrokeWidth: 1,
pointHitDetectionRadius: 5,
animation: false,
datasetFill: false,
scaleFontSize: 10,
responsive: false,
@@ -98,6 +89,7 @@ var defaultColumnOptions = {
datasetFill: false,
scaleFontSize: 10,
responsive: false,
animation: false,
scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
tooltipFillColor: "rgba(0,0,0,0.5)",
tooltipTemplate: "<%if (label){%><%=label%>: <%}%>" + currencySymbol + " <%= value %>",
@@ -110,6 +102,7 @@ var defaultStackedColumnOptions = {
barStrokeWidth: 1,
pointHitDetectionRadius: 5,
datasetFill: false,
animation: false,
scaleFontSize: 10,
responsive: false,
scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
@@ -147,7 +140,7 @@ function lineChart(URL, container, options) {
new Chart(ctx).Line(newData, options);
}).fail(function () {
$('#' + container).addClass('google-chart-error');
$('#' + container).addClass('general-chart-error');
});
console.log('URL for line chart : ' + URL);
}
@@ -182,7 +175,7 @@ function areaChart(URL, container, options) {
new Chart(ctx).Line(newData, options);
}).fail(function () {
$('#' + container).addClass('google-chart-error');
$('#' + container).addClass('general-chart-error');
});
console.log('URL for area chart: ' + URL);
@@ -218,7 +211,7 @@ function columnChart(URL, container, options) {
new Chart(ctx).Bar(newData, options);
}).fail(function () {
$('#' + container).addClass('google-chart-error');
$('#' + container).addClass('general-chart-error');
});
console.log('URL for column chart : ' + URL);
}
@@ -253,7 +246,7 @@ function stackedColumnChart(URL, container, options) {
new Chart(ctx).StackedBar(newData, options);
}).fail(function () {
$('#' + container).addClass('google-chart-error');
$('#' + container).addClass('general-chart-error');
});
console.log('URL for stacked column chart : ' + URL);
}
@@ -286,7 +279,7 @@ function pieChart(URL, container, options) {
new Chart(ctx).Pie(data, options);
}).fail(function () {
$('#' + container).addClass('google-chart-error');
$('#' + container).addClass('general-chart-error');
});

View File

@@ -1,319 +0,0 @@
/* globals currencyCode, language */
/* exported lineChart, googleColumnChart, stackedColumnChart, comboChart, pieChart, defaultLineChartOptions, defaultAreaChartOptions, defaultBarChartOptions, defaultComboChartOptions, defaultColumnChartOptions, defaultStackedColumnChartOptions, defaultPieChartOptions */
var google = google || {};
google.load('visualization', '1.1', {'packages': ['corechart', 'bar', 'line'], 'language': language});
/* exported */
var defaultLineChartOptions = {
curveType: 'function',
legend: {
position: 'none'
},
interpolateNulls: true,
lineWidth: 1,
chartArea: {
left: 50,
top: 10,
width: '95%',
height: '90%'
},
height: 400,
colors: ["#357ca5", "#008d4c", "#db8b0b", "#ca195a", "#555299", "#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
hAxis: {
textStyle: {
color: '#838383',
},
baselineColor: '#aaaaaa',
gridlines: {
color: 'transparent'
}
},
fontSize: 11,
vAxis: {
textStyle: {
color: '#838383',
},
baselineColor: '#aaaaaa',
format: '\u20AC #'
}
};
var defaultAreaChartOptions = {
curveType: 'function',
legend: {
position: 'none'
},
interpolateNulls: true,
lineWidth: 1,
chartArea: {
left: 50,
top: 10,
width: '95%',
height: '90%'
},
height: 400,
colors: ["#357ca5", "#008d4c", "#db8b0b", "#ca195a", "#555299", "#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
hAxis: {
textStyle: {
color: '#838383',
},
baselineColor: '#aaaaaa',
gridlines: {
color: 'transparent'
}
},
fontSize: 11,
vAxis: {
textStyle: {
color: '#838383',
},
baselineColor: '#aaaaaa',
format: '\u20AC #'
}
};
var defaultBarChartOptions = {
height: 400,
bars: 'horizontal',
hAxis: {
textStyle: {
color: '#838383',
},
baselineColor: '#aaaaaa',
format: '\u20AC #'
},
fontSize: 11,
colors: ["#357ca5", "#008d4c", "#db8b0b", "#ca195a", "#555299", "#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
vAxis: {
textStyle: {
color: '#838383'
},
textPosition: 'in',
gridlines: {
color: 'transparent'
},
baselineColor: '#aaaaaa'
},
chartArea: {
left: 15,
top: 10,
width: '100%',
height: '90%'
},
legend: {
position: 'none'
}
};
var defaultComboChartOptions = {
height: 300,
chartArea: {
left: 75,
top: 10,
width: '100%',
height: '90%'
},
vAxis: {
minValue: 0,
format: '\u20AC #'
},
colors: ["#357ca5", "#008d4c", "#db8b0b", "#ca195a", "#555299", "#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
fontSize: 11,
legend: {
position: 'none'
},
series: {
0: {type: 'line'},
1: {type: 'line'},
2: {type: 'bars'}
},
bar: {groupWidth: 20}
};
var defaultColumnChartOptions = {
height: 400,
chartArea: {
left: 50,
top: 10,
width: '85%',
height: '80%'
},
fontSize: 11,
hAxis: {
textStyle: {
color: '#838383'
},
gridlines: {
color: 'transparent'
},
baselineColor: '#aaaaaa'
},
colors: ["#357ca5", "#008d4c", "#db8b0b", "#ca195a", "#555299", "#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
vAxis: {
textStyle: {
color: '#838383'
},
baselineColor: '#aaaaaa',
format: '\u20AC #'
},
legend: {
position: 'none'
}
};
var defaultStackedColumnChartOptions = {
height: 400,
chartArea: {
left: 50,
top: 10,
width: '85%',
height: '80%'
},
legend: {
position: 'none'
},
fontSize: 11,
isStacked: true,
colors: ["#357ca5", "#008d4c", "#db8b0b", "#ca195a", "#555299", "#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
hAxis: {
textStyle: {
color: '#838383',
},
gridlines: {
color: 'transparent'
}
},
vAxis: {
textStyle: {
color: '#838383',
},
format: '\u20AC #'
}
};
var defaultPieChartOptions = {
chartArea: {
left: 0,
top: 0,
width: '100%',
height: '100%'
},
fontSize: 11,
height: 200,
legend: {
position: 'none'
},
colors: ["#357ca5", "#008d4c", "#db8b0b", "#ca195a", "#555299", "#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
};
function googleChart(chartType, URL, container, options) {
"use strict";
if ($('#' + container).length === 1) {
$.getJSON(URL).success(function (data) {
/*
Get the data from the JSON
*/
var gdata = new google.visualization.DataTable(data);
/*
Format as money
*/
var money = new google.visualization.NumberFormat({
decimalSymbol: ',',
groupingSymbol: '.',
prefix: currencyCode + ' '
});
for (var i = 1; i < gdata.getNumberOfColumns(); i++) {
money.format(gdata, i);
}
/*
Create a new google charts object.
*/
var chart = false;
var options = false;
if (chartType === 'line') {
chart = new google.visualization.LineChart(document.getElementById(container));
options = options || defaultLineChartOptions;
}
if (chartType === 'area') {
chart = new google.visualization.AreaChart(document.getElementById(container));
options = options || defaultAreaChartOptions;
}
if (chartType === 'column') {
chart = new google.visualization.ColumnChart(document.getElementById(container));
options = options || defaultColumnChartOptions;
}
if (chartType === 'pie') {
chart = new google.visualization.PieChart(document.getElementById(container));
options = options || defaultPieChartOptions;
}
if (chartType === 'bar') {
chart = new google.visualization.BarChart(document.getElementById(container));
options = options || defaultBarChartOptions;
}
if (chartType === 'stackedColumn') {
chart = new google.visualization.ColumnChart(document.getElementById(container));
options = options || defaultStackedColumnChartOptions;
}
if (chartType === 'combo') {
chart = new google.visualization.ComboChart(document.getElementById(container));
options = options || defaultComboChartOptions;
}
if (chart === false) {
alert('Cannot draw chart of type "' + chartType + '".');
} else {
chart.draw(gdata, options);
}
}).fail(function () {
$('#' + container).addClass('google-chart-error');
});
} else {
console.log('No container found called "' + container + '"');
}
}
function lineChart(URL, container, options) {
"use strict";
return googleChart('line', URL, container, options);
}
function areaChart(URL, container, options) {
"use strict";
return googleChart('area', URL, container, options);
}
function columnChart(URL, container, options) {
"use strict";
return googleChart('column', URL, container, options);
}
function stackedColumnChart(URL, container, options) {
"use strict";
return googleChart('stackedColumn', URL, container, options);
}
function comboChart(URL, container, options) {
"use strict";
return googleChart('combo', URL, container, options);
}
function pieChart(URL, container, options) {
"use strict";
return googleChart('pie', URL, container, options);
}

View File

@@ -2,13 +2,8 @@
$(function () {
"use strict";
if (typeof google !== 'undefined') {
// do google charts:
google.setOnLoadCallback(drawChart);
} else {
// do chart JS stuff.
drawChart();
}
if (showTour) {
$.getJSON('json/tour').success(function (data) {
var tour = new Tour(
@@ -41,6 +36,7 @@ function drawChart() {
pieChart('chart/bill/frontpage', 'bills-chart');
stackedColumnChart('chart/budget/frontpage', 'budgets-chart');
columnChart('chart/category/frontpage', 'categories-chart');
columnChart('chart/account/expense', 'expense-accounts-chart');
getBoxAmounts();

View File

@@ -2,11 +2,7 @@
$(function () {
"use strict";
if (typeof(google) !== 'undefined') {
google.setOnLoadCallback(drawChart);
} else {
drawChart();
}
});
@@ -18,7 +14,8 @@ function drawChart() {
}
if (typeof stackedColumnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
stackedColumnChart('chart/budget/year/' + year + shared, 'budgets');
stackedColumnChart('chart/category/year/' + year + shared, 'categories');
stackedColumnChart('chart/category/spent-in-year/' + year + shared, 'categories-spent-in-year');
stackedColumnChart('chart/category/earned-in-year/' + year + shared, 'categories-earned-in-year');
}
if (typeof lineChart !== 'undefined' && typeof month !== 'undefined') {
lineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart');

View File

@@ -15,9 +15,69 @@ return [
'cancel' => 'Cancel',
'from' => 'From',
'to' => 'To',
'total_sum' => 'Total sum',
'period_sum' => 'Sum for period',
'showEverything' => 'Show everything',
'never' => 'Never',
'search_results_for' => 'Search results for ":query"',
'bounced_error' => 'The message sent to :email bounced, so no access for you.',
'deleted_error' => 'These credentials do not match our records.',
'removed_amount' => 'Removed :amount',
'added_amount' => 'Added :amount',
'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.',
// tags
'store_new_tag' => 'Store new tag',
'update_tag' => 'Update tag',
'no_location_set' => 'No location set.',
'meta_data' => 'Meta data',
'edit_tag' => 'Edit tag',
'delete_tag' => 'Delete tag',
'location' => 'Location',
// preferences
'pref_home_screen_accounts' => 'Home screen accounts',
'pref_home_screen_accounts_help' => 'Which accounts should be displayed on the home page?',
'pref_budget_settings' => 'Budget settings',
'pref_budget_settings_help' => 'What\'s the maximum amount of money a budget envelope may contain?',
'pref_view_range' => 'View range',
'pref_view_range_help' => 'Some charts are automatically grouped in periods. What period would you prefer?',
'pref_1D' => 'One day',
'pref_1W' => 'One week',
'pref_1M' => 'One month',
'pref_3M' => 'Three months (quarter)',
'pref_6M' => 'Six months',
'pref_languages' => 'Languages',
'pref_languages_help' => 'Firefly III supports several languages. Which one do you prefer?',
'pref_save_settings' => 'Save settings',
// profile:
'change_your_password' => 'Change your password',
'delete_account' => 'Delete account',
'current_password' => 'Current password',
'new_password' => 'New password',
'new_password_again' => 'New password (again)',
'delete_your_account' => 'Delete your account',
'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, <em>anything</em> you might have saved' .
' into Firefly III. It\'ll be GONE.',
'delete_your_account_password' => 'Enter your password to continue.',
'password' => 'Password',
'are_you_sure' => 'Are you sure? You cannot undo this.',
'delete_account_button' => 'DELETE your account',
'invalid_current_password' => 'Invalid current password!',
'password_changed' => 'Password changed!',
'should_change' => 'The idea is to change your password.',
'invalid_password' => 'Invalid password!',
// attachments
'nr_of_attachments' => 'One attachment|:count attachments',
'attachments' => 'Attachments',
'edit_attachment' => 'Edit attachment ":name"',
'update_attachment' => 'Update attachment',
'delete_attachment' => 'Delete attachment ":name"',
'attachment_deleted' => 'Deleted attachment ":name"',
'upload_max_file_size' => 'Maximum file size: :size',
// tour:
'prev' => 'Prev',
@@ -40,42 +100,41 @@ return [
'csv_define_column_roles' => 'Define column roles',
'csv_map_values' => 'Map found values to existing values',
'csv_download_config' => 'Download CSV configuration file.',
'csv_index_text' =>
'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by' .
' the folks at <a href="https://www.atlassian.com/">Atlassian</a>. Simply upload your CSV file and follow the instructions.' .
' If you would like to learn more, please click on the <i class="fa fa-question-circle"></i> button at the top of this page.',
'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV' .
' importer made by the folks at <a href="https://www.atlassian.com/">Atlassian</a>. Simply upload your CSV' .
' file and follow the instructions. If you would like to learn more, please click on the <i ' .
'class="fa fa-question-circle"></i> button at the top of this page.',
'csv_index_beta_warning' => 'This tool is very much in beta. Please proceed with caution',
'csv_header_help' => 'Check this box when your CSV file\'s first row consists of column names, not actual data',
'csv_date_help' => 'Date time format in your CSV. Follow the format like <a href="https://secure.' .
'php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">this' .
' page</a> indicates. The default value will parse dates that look like this: ' . date('Ymd'),
'csv_date_help' => 'Date time format in your CSV. Follow the format like <a href="https://secure.php.net/manual/en/' .
'datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">this page</a> ' .
'indicates. The default value will parse dates that look like this: ' . date('Ymd'),
'csv_csv_file_help' => 'Select the CSV file here. You can only upload one file at a time',
'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.',
'csv_upload_button' => 'Start importing CSV',
'csv_column_roles_title' => 'Define column roles',
'csv_column_roles_text' =>
'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example '
. 'data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what'
. ' each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. '
. 'The next step will show you what this button does.',
'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. ' .
'Please check out the example data if you\'re not sure yourself. Click on the question mark ' .
'(top right of the page) to learn what each column means. If you want to map imported data ' .
'onto existing data in Firefly, use the checkbox. The next step will show you what this button does.',
'csv_column_roles_table' => 'Column roles',
'csv_column' => 'CSV column',
'cvs_column_name' => 'CSV column name',
'cvs_column_example' => 'Column example data',
'cvs_column_role' => 'Column contains?',
'csv_column_name' => 'CSV column name',
'csv_column_example' => 'Column example data',
'csv_column_role' => 'Column contains?',
'csv_do_map_value' => 'Map value?',
'csv_continue' => 'Continue to the next step',
'csv_go_back' => 'Go back to the previous step',
'csv_map_title' => 'Map found values to existing values',
'csv_map_text' =>
'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other'
. ' things won\'t be created twice.',
'cvs_field_value' => 'Field value from CSV',
'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your ' .
'database. This ensures that accounts and other things won\'t be created twice.',
'csv_field_value' => 'Field value from CSV',
'csv_field_mapped_to' => 'Must be mapped to...',
'csv_do_not_map' => 'Do not map this value',
'csv_download_config_title' => 'Download CSV configuration',
'csv_download_config_text' => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.',
'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all over again.'
. ' But, if the import succeeds, it will be easier to upload similar CSV files.',
'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all ' .
'over again. But, if the import succeeds, it will be easier to upload similar CSV files.',
'csv_do_download_config' => 'Download configuration file.',
'csv_empty_description' => '(empty description)',
'csv_upload_form' => 'CSV upload form',
@@ -83,17 +142,15 @@ return [
'csv_unsupported_map' => 'The importer cannot map the column ":columnRole" to existing values in the database.',
'csv_unsupported_value' => 'The importer does not know how to handle values in columns marked as ":columnRole".',
'csv_cannot_store_value' => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.',
'csv_process_title' => 'CVS import finished!',
'csv_process_text' => 'The CVS importer has finished and has processed :rows rows',
'csv_process_title' => 'CSV import finished!',
'csv_process_text' => 'The CSV importer has finished and has processed :rows rows',
'csv_row' => 'Row',
'csv_import_with_errors' => 'There was one error.|There were :errors errors.',
'csv_error_see_logs' => 'Check the log files to see details.',
'csv_process_new_entries' => 'Firefly has created :imported new transaction(s).',
'csv_start_over' => 'Import again',
'csv_to_index' => 'Back home',
'csv_do_not_map' => 'Do not map this value',
'csv_upload_not_writeable' => 'Cannot write to the path mentioned here. Cannot upload',
'csv_column__ignore' => '(ignore this column)',
'csv_column_account-iban' => 'Asset account (IBAN)',
'csv_column_account-id' => 'Asset account ID (matching Firefly)',
@@ -123,8 +180,9 @@ return [
'csv_column_tags-space' => 'Tags (space separated)',
'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.',
'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.',
'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which' .
' account the transactions in the CSV belong to.',
'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which'
. ' account the transactions in the CSV belong to.',
'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?',
// create new stuff:
@@ -137,9 +195,12 @@ return [
'create_new_piggy_bank' => 'Create new piggy bank',
'create_new_bill' => 'Create new bill',
// currencies:
'create_currency' => 'Create a new currency',
'edit_currency' => 'Edit currency ":name"',
'store_currency' => 'Store new currency',
'update_currency' => 'Update currency',
// new user:
'submit' => 'Submit',
@@ -167,10 +228,13 @@ return [
'delete_budget' => 'Delete budget ":name"',
'edit_budget' => 'Edit budget ":name"',
'update_amount' => 'Update amount',
'update_budget' => 'Update budget',
// bills:
'delete_bill' => 'Delete bill ":name"',
'edit_bill' => 'Edit bill ":name"',
'update_bill' => 'Update bill',
'store_new_bill' => 'Store new bill',
// accounts:
'details_for_asset' => 'Details for asset account ":name"',
@@ -222,6 +286,7 @@ return [
'no_category' => '(no category)',
'category' => 'Category',
'delete_category' => 'Delete category ":name"',
'store_category' => 'Store new category',
// transactions:
'update_withdrawal' => 'Update withdrawal',
@@ -299,6 +364,7 @@ return [
'quarterly' => 'Quarterly',
'half-year' => 'Every six months',
'yearly' => 'Yearly',
'profile' => 'Profile',
// reports:
'reportForYear' => 'Yearly report for :year',
@@ -333,12 +399,15 @@ return [
'hideTheRest' => 'Show only the top :number',
'sum_of_year' => 'Sum of year',
'average_of_year' => 'Average of year',
'categories_earned_in_year' => 'Categories (by earnings)',
'categories_spent_in_year' => 'Categories (by spendings)',
// charts:
'dayOfMonth' => 'Day of the month',
'month' => 'Month',
'budget' => 'Budget',
'spent' => 'Spent',
'earned' => 'Earned',
'overspent' => 'Overspent',
'left' => 'Left',
'noBudget' => '(no budget)',
@@ -361,7 +430,7 @@ return [
// piggy banks:
'piggy_bank' => 'Piggy bank',
'new_piggy_bank' => 'Create new piggy bank',
'create_new_piggybank' => 'Create new piggy bank',
'store_piggy_bank' => 'Store new piggy bank',
'account_status' => 'Account status',
'left_for_piggy_banks' => 'Left for piggy banks',
'sum_of_piggy_banks' => 'Sum of piggy banks',
@@ -397,6 +466,17 @@ return [
'tag_title_nothing' => 'Default tags',
'tag_title_balancingAct' => 'Balancing act tags',
'tag_title_advancePayment' => 'Advance payment tags',
'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like' .
' <span class="label label-info">expensive</span>, <span class="label label-info">bill</span>' .
' or <span class="label label-info">for-party</span>. In Firefly III, tags can have more properties' .
' such as a date, description and location. This allows you to join transactions together in a more' .
' meaningful way. For example, you could make a tag called <span class="label label-success">' .
'Christmas dinner with friends</span> and add information about the restaurant. Such tags are "singular",' .
' you would only use them for a single occasion, perhaps with multiple transactions.',
'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money' .
' for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where ' .
'expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you.' .
' Using tags the old-fashioned way is of course always possible. ',
'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.',
];

View File

@@ -52,7 +52,7 @@ return [
'csv_config' => 'CSV import configuration',
'specifix' => 'Bank- or file specific fixes',
'csv_import_account' => 'Default import account',
'attachments[]' => 'Attachments',
'store_new_withdrawal' => 'Store new withdrawal',
'store_new_deposit' => 'Store new deposit',
'store_new_transfer' => 'Store new transfer',
@@ -61,6 +61,12 @@ return [
'add_new_transfer' => 'Add a new transfer',
'noPiggybank' => '(no piggy bank)',
'noBudget' => '(no budget)',
'title' => 'Title',
'notes' => 'Notes',
'filename' => 'File name',
'mime' => 'Mime type',
'size' => 'Size',
'delete_account' => 'Delete account ":name"',
'delete_bill' => 'Delete bill ":name"',
@@ -69,7 +75,9 @@ return [
'delete_currency' => 'Delete currency ":name"',
'delete_piggyBank' => 'Delete piggy bank ":name"',
'delete_journal' => 'Delete transaction with description ":description"',
'delete_attachment' => 'Delete attachment ":name"',
'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?',
'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?',
'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?',
'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?',
@@ -77,6 +85,7 @@ return [
'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?',
'piggyBank_areYouSure' => 'Are you sure you want to delete the piggy bank named ":name"?',
'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?',
'tag_areYouSure' => 'Are you sure you want to delete the tag ":tag"?',
'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.',
'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.' .
@@ -89,4 +98,6 @@ return [
'|All :count transactions connected to this budget will spared deletion.',
'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.' .
'|All :count transactions connected to this category will spared deletion.',
'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.' .
'|All :count transactions connected to this tag will spared deletion.',
];

View File

@@ -13,6 +13,10 @@ return [
|
*/
'file_already_attached' => 'Uploaded file ":name" is already attached to this object.',
'file_attached' => 'Succesfully uploaded file ":name".',
'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.',
'file_too_large' => 'File ":name" is too large.',
"accepted" => "The :attribute must be accepted.",
"active_url" => "The :attribute is not a valid URL.",
"after" => "The :attribute must be a date after :date.",

View File

@@ -15,9 +15,66 @@ return [
'cancel' => 'Annuleren',
'from' => 'Van',
'to' => 'Tot',
'total_sum' => 'Totale som',
'period_sum' => 'Som van periode',
'showEverything' => 'Laat alles zien',
'never' => 'Nooit',
'search_results_for' => 'Zoekresultaten voor ":query"',
'bounced_error' => 'Het emailtje naar :email kwam nooit aan.',
'deleted_error' => 'These credentials do not match our records.',
'removed_amount' => ':amount weggehaald',
'added_amount' => ':amount toegevoegd',
'asset_account_role_help' => 'Voorkeuren die voortkomen uit je keuze hier kan je later aangeven.',
// tags
'store_new_tag' => 'Sla tag op',
'update_tag' => 'Sla wijzigingen op',
'no_location_set' => 'Zonder plaats',
'location' => 'Plaats',
'meta_data' => 'Metagegevens',
// preferences
'pref_home_screen_accounts' => 'Voorpaginarekeningen',
'pref_home_screen_accounts_help' => 'Welke betaalrekeningen wil je op de voorpagina zien?',
'pref_budget_settings' => 'Budgetinstellingen',
'pref_budget_settings_help' => 'Wat is het maximale bedrag dat je voor een budget kan instellen?',
'pref_view_range' => 'Bereik',
'pref_view_range_help' => 'Sommige pagina\'s springen naar een standaard bereik. Welk bereik heeft jouw voorkeur?',
'pref_1D' => 'Eén dag',
'pref_1W' => 'Eén week',
'pref_1M' => 'Eén maand',
'pref_3M' => 'Drie maanden (kwartaal)',
'pref_6M' => 'Zes maanden',
'pref_languages' => 'Talen',
'pref_languages_help' => 'Firefly III ondersteunt meerdere talen. Welke heeft jouw voorkeur?',
'pref_save_settings' => 'Instellingen opslaan',
// profile:
'change_your_password' => 'Verander je wachtwoord',
'delete_account' => 'Verwijder je account',
'current_password' => 'Huidige wachtwoord',
'new_password' => 'Nieuw wachtwoord',
'new_password_again' => 'Nieuw wachtwoord (bevestiging)',
'delete_your_account' => 'Verwijder je account',
'delete_your_account_help' => 'Als je je account verwijderd worden ook al je rekeningen, transacties en <em>alle andere zaken</em> verwijderd.' .
' Alles is dan WEG.',
'delete_your_account_password' => 'Voer je wachtwoord in om door te gaan.',
'password' => 'Wachtwoord',
'are_you_sure' => 'Zeker weten? Je kan niet meer terug!',
'delete_account_button' => 'VERWIJDER je account',
'invalid_current_password' => 'Huidige wachtwoord is niet geldig!',
'password_changed' => 'Je wachtwoord is veranderd!',
'should_change' => 'Vul ook echt een ander wachtwoord in.',
'invalid_password' => 'Ongeldig wachtwoord!',
// attach
'nr_of_attachments' => 'Eén bijlage|:count bijlagen',
'attachments' => 'Bijlagen',
'edit_attachment' => 'Wijzig bijlage ":name"',
'update_attachment' => 'Update bijlage',
'delete_attachment' => 'Verwijder bijlage ":name"',
'attachment_deleted' => 'Bijlage ":name" verwijderd',
'upload_max_file_size' => 'Maximale grootte: :size',
// tour:
'prev' => 'Vorige',
@@ -37,32 +94,65 @@ return [
'csv_import' => 'Importeer CSV-bestand',
'csv' => 'CSV',
'csv_index_title' => 'Upload en importeer een kommagescheiden tekstbestand',
'csv_define_column_roles' => 'Bepaal kolominhoud',
'csv_map_values' => 'Leg relaties met kolomwaardes',
'csv_download_config' => 'Download CSV configuratiebestand.',
'csv_index_text' => 'Met deze (en de komende) pagina\'s kan je kommagescheiden tekstbestanden importeren. Deze tool is gebaseerd '
. 'op de prachtige tool van <a href="https://www.atlassian.com/">Atlassian</a>. Om te beginnen selecteer' .
' je jouw tekstbestand bij "CSV-bestand". '
. 'op de prachtige tool van <a href="https://www.atlassian.com/">Atlassian</a>. Om te beginnen selecteer'
. ' je jouw tekstbestand bij "CSV-bestand". '
. 'Als je hulp nodig hebt, klik dan op het <i class="fa fa-question-circle"></i>-icoontje rechtsboven.',
'csv_index_beta_warning' => 'Deze tool is nog erg experimenteel. Wees dus voorzichtig.',
'csv_header_help' => 'Zet hier een vinkje als de eerste rij van je tekstbestand bestaat uit kolomnamen,' .
'en niet uit daadwerkelijke gegevens.',
'csv_date_help' => 'Het gebruikte datumformaat in jouw bestand. Gebruik het formaat zoals <a href="https://secure.' .
'php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">deze' .
' pagina</a> het uitlegt (Engels). Het standaardformaat kan omgaan met data zoals deze: ' . date('Ymd'),
'csv_header_help' => 'Zet hier een vinkje als de eerste rij van je tekstbestand bestaat uit kolomnamen,'
. 'en niet uit daadwerkelijke gegevens.',
'csv_date_help' => 'Het gebruikte datumformaat in jouw bestand. Gebruik het formaat zoals <a href="https://secure.'
. 'php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">deze'
. ' pagina</a> het uitlegt (Engels). Het standaardformaat kan omgaan met data zoals deze: ' . date('Ymd'),
'csv_csv_file_help' => 'Voer hier je kommagescheiden tekstbestand in. Je kan er maar één tegelijkertijd invoeren.',
'csv_csv_config_file_help' => 'Voer hier je configuratiebestand in. Als je deze niet hebt, geen zorgen. Latere stappen leggen dit uit.',
'csv_upload_button' => 'Begin de import',
'csv_define_column_roles' => 'Bepaal kolominhoud',
'csv_column_roles_title' => 'Bepaal de inhoud van elke kolom',
'csv_column_roles_text' => 'Firefly kan niet automatisch ontdekken wat elke kolom betekent. Je moet het zelf aangeven. Gebruik de' .
' voorbeeldgegevens als je het ook niet zeker weet. Klik op het <i class="fa fa-question-circle"></i>-icoontje ' .
'rechtsboven om te ontdekken wat elke kolomsoort precies is. Als de kolominhoud een directe' .
' relatie heeft met gegevens'
.
' die al in Firefly staan, gebruik dan het vinkje. Tijdens de volgende stap komt Firefly hier dan op terug.',
'csv_column_roles_text' => 'Firefly kan niet automatisch ontdekken wat elke kolom betekent. Je moet het zelf aangeven. Gebruik de'
. ' voorbeeldgegevens als je het ook niet zeker weet. Klik op het <i class="fa fa-question-circle"></i>-icoontje '
. 'rechtsboven om te ontdekken wat elke kolomsoort precies is. Als de kolominhoud een directe'
. ' relatie heeft met gegevens'
. ' die al in Firefly staan, gebruik dan het vinkje. Tijdens de volgende stap komt Firefly hier dan op terug.',
'csv_column_roles_table' => 'Kolominhoud',
'csv_column' => 'CSV-kolom',
'cvs_column_name' => 'CSV-kolomnaam',
'cvs_column_example' => 'Voorbeeldgegevens',
'cvs_column_role' => 'Kolom bevat?',
'csv_column_name' => 'CSV-kolomnaam',
'csv_column_example' => 'Voorbeeldgegevens',
'csv_column_role' => 'Kolom bevat?',
'csv_do_map_value' => 'Directe relatie?',
'csv_continue' => 'Naar de volgende stap',
'csv_go_back' => 'Terug naar de vorige stap',
'csv_map_title' => 'Leg relaties met kolomwaardes',
'csv_map_text' => 'Sommige kolommen bevatten waardes die misschien al in Firefly bestaan. Selecteer hier de juiste combinaties'
. 'zodat het importeren netjes aansluit bij je huidige gegevens.',
'csv_field_value' => 'Veldwaarde',
'csv_field_mapped_to' => 'Is gelijk aan',
'csv_do_not_map' => 'Geen relatie',
'csv_download_config_title' => 'Download importconfiguratie',
'csv_download_config_text' => 'Firefly is klaar om je bestand te importeren. De instellingen en selecties die je zojuist hebt gemaakt' .
' kan je downloaden en opslaan. Bij de volgende keer kan je dit bestand ook uploaden. Als je' .
' kommagescheiden bestand dezelfde indeling heeft, zullen alle selecties goed staan. Dat scheelt weer!',
'csv_more_information_text' => 'Ook als het importeren fout gaat is dit bestand handig. Na het importeren krijg je nogmaals' .
' de gelegenheid dit bestand te downloaden.',
'csv_do_download_config' => 'Download het configuratiebestand',
'csv_empty_description' => '(geen beschrijving)',
'csv_upload_form' => 'CSV upload formulier',
'csv_index_unsupported_warning' => 'Het volgende wordt nog niet ondersteund:',
'csv_unsupported_map' => 'The importer cannot map the column ":columnRole" to existing values in the database.',
'csv_unsupported_value' => 'The importer does not know how to handle values in columns marked as ":columnRole".',
'csv_cannot_store_value' => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.',
'csv_process_title' => 'Het importeren is klaar',
'csv_process_text' => ':rows rijen zijn verwerkt.',
'csv_row' => 'Rij',
'csv_import_with_errors' => 'Er was één fout. Deze foutmelding is mogelijk in het Engels.|Er zijn :errors fouten opgetreden. De foutmeldingen'
. ' zijn mogelijk in het Engels.',
'csv_error_see_logs' => 'De logboeken bevatten mogelijk meer details.',
'csv_process_new_entries' => 'Firefly heeft :imported nieuwe transactie(s) gemaakt.',
'csv_start_over' => 'Begin opnieuw',
'csv_to_index' => 'Naar de index',
'csv_upload_not_writeable' => 'Kan niet naar onderstaand pad schrijven. Kan dus niet uploaden.',
'csv_column__ignore' => '(negeer deze kolom)',
'csv_column_account-iban' => 'Betaalrekening (IBAN)',
'csv_column_account-id' => 'Betaalrekening (ID gelijk aan Firefly)',
@@ -90,36 +180,12 @@ return [
'csv_column_sepa-db' => 'SEPA "direct debet"-nummer',
'csv_column_tags-comma' => 'Tags (kommagescheiden)',
'csv_column_tags-space' => 'Tags (spatiegescheiden)',
'csv_column_roles_table' => 'Kolominhoud',
'csv_continue' => 'Naar de volgende stap',
'csv_go_back' => 'Terug naar de vorige stap',
'csv_map_values' => 'Leg relaties met kolomwaardes',
'csv_map_title' => 'Leg relaties met kolomwaardes',
'csv_map_text' => 'Sommige kolommen bevatten waardes die misschien al in Firefly bestaan. Selecteer hier de juiste combinaties' .
'zodat het importeren netjes aansluit bij je huidige gegevens.',
'cvs_field_value' => 'Veldwaarde',
'csv_field_mapped_to' => 'Is gelijk aan',
'csv_do_not_map' => 'Geen relatie',
'csv_download_config_title' => 'Download importconfiguratie',
'csv_download_config_text' =>
'Firefly is klaar om je bestand te importeren. De instellingen en selecties die je zojuist hebt gemaakt kan je downloaden'
. ' en opslaan. Bij de volgende keer kan je dit bestand ook uploaden. Als je kommagescheiden bestand dezelfde indeling'
. ' heeft, zullen alle selecties goed staan. Dat scheelt weer!',
'csv_more_information_text' =>
'Ook als het importeren fout gaat is dit bestand handig. Na het importeren krijg je nogmaals de gelegenheid dit bestand'
. 'te downloaden.',
'csv_do_download_config' => 'Download het configuratiebestand',
'csv_process_title' => 'Het importeren is klaar',
'csv_row' => 'Rij',
'csv_error_see_logs' => 'De logboeken bevatten mogelijk meer details.',
'csv_process_new_entries' => 'Firefly heeft :imported nieuwe transactie(s) gemaakt.',
'csv_start_over' => 'Begin opnieuw',
'csv_to_index' => 'Naar de index',
'csv_process_text' => ':rows rijen zijn verwerkt.',
'csv_import_with_errors' => 'Er was één fout. Deze foutmelding is mogelijk in het Engels.|Er zijn :errors fouten opgetreden. De foutmeldingen'
. ' zijn mogelijk in het Engels.',
'csv_specifix_RabobankDescription' => 'Vink dit aan als je Rabobank bestanden importeert.',
'csv_specifix_Dummy' => 'Dit vinkje doet niks (dummy).',
'csv_import_account_help' => 'Als jouw CSV bestand geen referenties bevat naar jouw rekening(en), geef dan hier aan om welke rekening het gaat.',
'csv_date_parse_error' => 'Kan geen chocola maken van ":value" (met hulp van configuratie ":format").' .
' Weet je zeker dat je CSV bestand geen fouten bevat?',
// create new stuff:
'create_new_withdrawal' => 'Nieuwe uitgave',
'create_new_deposit' => 'Nieuwe inkomsten',
@@ -133,6 +199,8 @@ return [
// currencies:
'create_currency' => 'Voeg nieuwe valuta toe',
'edit_currency' => 'Wijzig valuta ":name"',
'store_currency' => 'Sla nieuwe valuta op',
'update_currency' => 'Wijzig valuta',
// new user:
'submit' => 'Invoeren',
@@ -160,10 +228,13 @@ return [
'delete_budget' => 'Verwijder budget ":name"',
'edit_budget' => 'Wijzig budget ":name"',
'update_amount' => 'Bedrag bijwerken',
'update_budget' => 'Budget bijwerken',
// bills:
'delete_bill' => 'Verwijder contract ":name"',
'update_bill' => 'Wijzig contract',
'edit_bill' => 'Wijzig contract ":name"',
'store_new_bill' => 'Sla nieuw contract op',
// accounts:
'details_for_asset' => 'Overzicht voor betaalrekening ":name"',
@@ -224,6 +295,7 @@ return [
'no_category' => '(geen categorie)',
'category' => 'Categorie',
'delete_category' => 'Verwijder categorie ":name"',
'store_category' => 'Sla nieuwe categorie op',
// transactions:
'update_withdrawal' => 'Wijzig uitgave',
@@ -287,6 +359,7 @@ return [
'Withdrawal' => 'Uitgave',
'Deposit' => 'Inkomsten',
'Transfer' => 'Overschrijving',
'profile' => 'Profiel',
'bill' => 'Contract',
'yes' => 'Ja',
'no' => 'Nee',
@@ -307,8 +380,8 @@ return [
// reports:
'reportForYear' => 'Jaaroverzicht :year',
'reportForYearShared' => 'Jaaroverzicht :year (inclusief gedeelde rekeningen)',
'reportForMonth' => 'Maandoverzicht van :date',
'reportForMonthShared' => 'Maandoverzicht van :date (inclusief gedeelde rekeningen)',
'reportForMonth' => 'Maandoverzicht voor :month',
'reportForMonthShared' => 'Maandoverzicht voor :month (inclusief gedeelde rekeningen)',
'incomeVsExpenses' => 'Inkomsten tegenover uitgaven',
'accountBalances' => 'Rekeningsaldi',
'balanceStartOfYear' => 'Saldo aan het begin van het jaar',
@@ -335,12 +408,15 @@ return [
'topX' => 'top :number',
'showTheRest' => 'Laat alles zien',
'hideTheRest' => 'Laat alleen de top :number zien',
'categories_earned_in_year' => 'Categorieën (inkomsten)',
'categories_spent_in_year' => 'Categorieën (uitgaven)',
// charts:
'dayOfMonth' => 'Dag vd maand',
'month' => 'Maand',
'budget' => 'Budget',
'spent' => 'Uitgegeven',
'earned' => 'Verdiend',
'overspent' => 'Teveel uitgegeven',
'left' => 'Over',
'noBudget' => '(geen budget)',
@@ -363,7 +439,7 @@ return [
// piggy banks:
'piggy_bank' => 'Spaarpotje',
'new_piggy_bank' => 'Nieuw spaarpotje',
'create_new_piggybank' => 'Nieuw spaarpotje',
'store_piggy_bank' => 'Sla spaarpotje op',
'account_status' => 'Rekeningoverzicht',
'left_for_piggy_banks' => 'Over voor spaarpotjes',
'sum_of_piggy_banks' => 'Som van spaarpotjes',
@@ -395,9 +471,17 @@ return [
'new_tag' => 'Maak nieuwe tag',
'edit_tag' => 'Wijzig tag ":tag"',
'no_year' => 'Zonder jaar',
'no_maand' => 'Zonder jaar',
'no_month' => 'Zonder maand',
'tag_title_nothing' => 'Standaard tags',
'tag_title_balancingAct' => 'Balancing act tags',
'tag_title_advancePayment' => 'Advance payment tags',
'tag_title_balancingAct' => 'Balancerende tags',
'tag_title_advancePayment' => 'Vooruitbetaalde tags',
'tags_introduction' => 'Normaal gesproken zijn tags enkele woorden, gebruikt om gerelateerde zaken snel aan elkaar te plakken. ' .
'<span class="label label-info">dure-aanschaf</span>, <span class="label label-info">rekening</span>, ' .
'<span class="label label-info">feestje</span>. In Firefly III hebben tags meer betekenis en kan je er een datum' .
', beschrijving en locatie aan geven. Daarmee kan je je transacties op een wat zinvollere manier aan elkaar ' .
'koppelen. Je kan bijvoorbeeld een tag <span class="label label-success">Kerstdiner</span> maken en informatie over' .
' het restaurant meenemen. Zulke tags zijn enkelvoudig; je gebruikt ze maar bij één gelegenheid.',
'tags_group' => 'Omdat tags transacties groeperen kan je er teruggaves, vergoedingen en andere geldzaken mee aanduiden, zolang' .
' de transacties elkaar "opheffen". Hoe je dit aanpakt is aan jou. De gewone manier kan natuurlijk ook.',
'tags_start' => 'Maak hieronder een tag, of voer nieuwe tags in als je nieuwe transacties maakt.',
];

View File

@@ -51,6 +51,14 @@ return [
'date_format' => 'Datumformaat',
'csv_config' => 'Configuratiebestand',
'specifix' => 'Bank- or of bestandsspecifieke opties',
'csv_import_account' => 'Standaard rekening voor importeren',
'attachments[]' => 'Bijlagen',
'title' => 'Titel',
'notes' => 'Notities',
'filename' => 'Bestandsnaam',
'mime' => 'Bestandstype',
'size' => 'Grootte',
'store_new_withdrawal' => 'Nieuwe uitgave opslaan',
'store_new_deposit' => 'Nieuwe inkomsten opslaan',
@@ -68,7 +76,10 @@ return [
'delete_currency' => 'Verwijder valuta ":name"',
'delete_piggyBank' => 'Verwijder spaarpotje ":name"',
'delete_journal' => 'Verwijder transactie met omschrijving ":description"',
'delete_attachment' => 'Verwijder bijlage ":name"',
'tag_areYouSure' => 'Weet je zeker dat je de tag met naam ":tag" wilt verwijderen?',
'attachment_areYouSure' => 'Weet je zeker dat je de bijlage met naam ":name" wilt verwijderen?',
'account_areYouSure' => 'Weet je zeker dat je de rekening met naam ":name" wilt verwijderen?',
'bill_areYouSure' => 'Weet je zeker dat je het contract met naam ":name" wilt verwijderen?',
'budget_areYouSure' => 'Weet je zeker dat je het budget met naam ":name" wilt verwijderen?',
@@ -88,4 +99,6 @@ return [
'|De :count transacties verbonden aan dit budget blijven bewaard.',
'category_keep_transactions' => 'De transactie verbonden aan deze categorie blijft bewaard.' .
'|De :count transacties verbonden aan deze categorie blijven bewaard.',
'tag_keep_transactions' => 'De transactie verbonden aan deze tag blijft bewaard.' .
'|De :count transacties verbonden aan deze tag blijven bewaard.',
];

View File

@@ -1,5 +1,25 @@
<?php
return [
// tour!
'main-content-title' => 'Welkom bij Firefly III',
'main-content-text' => 'Doe jezelf een lol en volg deze korte tour. Je weet dan precies hoe alles werkt.',
'sidebar-toggle-title' => 'Sidebar om nieuwe dingen te maken',
'sidebar-toggle-text' => 'Verstopt onder het plusje vind je de knoppen die je nodig hebt om nieuwe dingen te maken.',
'account-menu-title' => 'Alle rekeningen',
'account-menu-text' => 'Hier vind je al je rekeningen.',
'budget-menu-title' => 'Budgetten',
'budget-menu-text' => 'Gebruik deze pagina voor budgetten.',
'report-menu-title' => 'Overzichten',
'report-menu-text' => 'Hier vind je allerlei financiele rapportages.',
'transaction-menu-title' => 'Transacties',
'transaction-menu-text' => 'Hier vind je al je bijschrijvingen, afschrijvingen en overboekingen.',
'option-menu-title' => 'Opties',
'option-menu-text' => 'Hier vind je alle opties.',
'main-content-end-title' => 'Einde!',
'main-content-end-text' => 'Elke pagina heeft een vraagtekentje rechtsboven. Gebruik deze voor meer hulp. Veel plezier!',
'csv-index' => 'csv-index',
'register' => 'register',
'index' => 'index',
'home' => 'home',

View File

@@ -13,6 +13,10 @@ return [
|
*/
'file_already_attached' => 'Het geuploade bestand ":name" is al gelinkt aan deze transactie.',
'file_attached' => 'Bestand met naam ":name" is met succes geuploaded.',
'file_invalid_mime' => 'Bestand ":name" is van het type ":mime", en die kan je niet uploaden.',
'file_too_large' => 'Bestand ":name" is te groot.',
"accepted" => "The :attribute must be accepted.",
"active_url" => "The :attribute is not a valid URL.",
"after" => "The :attribute must be a date after :date.",

View File

@@ -32,7 +32,7 @@
{{ ExpandedForm.text('iban') }}
{{ ExpandedForm.balance('openingBalance') }}
{{ ExpandedForm.date('openingBalanceDate', phpdate('Y-m-d')) }}
{{ ExpandedForm.select('accountRole', Config.get('firefly.accountRoles'),null,{'helpText' : 'Any extra options resulting from your choice can be set later.'}) }}
{{ ExpandedForm.select('accountRole', Config.get('firefly.accountRoles'),null,{'helpText' : 'asset_account_role_help'|_}) }}
{{ ExpandedForm.balance('virtualBalance') }}
</div>

View File

@@ -30,9 +30,11 @@
{{ Lang.choice('form.also_delete_piggyBanks', account.piggyBanks|length, {count: account.piggyBanks|length}) }}
{% endif %}
</p>
<p class="text-success">
{{ 'save_transactions_by_moving'|_ }}
</p>
<p>
{{ Form.select('move_account_before_delete', accountList, null, {class: 'form-control'}) }}
</p>

View File

@@ -0,0 +1,34 @@
{% extends "./layout/default.twig" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), attachment) }}
{% endblock %}
{% block content %}
{{ Form.open({'class' : 'form-horizontal','id' : 'destroy','url' : route('attachments.destroy',attachment.id)}) }}
<div class="row">
<div class="col-lg-6 col-lg-offset-3 col-md-12 col-sm-12">
<div class="box box-danger">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('form.delete_attachment', {'name': attachment.filename}) }}</h3>
</div>
<div class="box-body">
<p class="text-danger">
{{ trans('form.permDeleteWarning') }}
</p>
<p>
{{ trans('form.attachment_areYouSure', {'name': attachment.filename}) }}
</p>
</div>
<div class="box-footer">
<input type="submit" name="submit" value="{{ trans('form.deletePermanently') }}" class="btn pull-right btn-danger"/>
<a href="{{ URL.previous() }}" class="btn-default btn">{{ trans('form.cancel') }}</a>
</div>
</div>
</div>
</div>
{{ Form.close|raw }}
{% endblock %}

View File

@@ -0,0 +1,59 @@
{% extends "./layout/default.twig" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), attachment) }}
{% endblock %}
{% block content %}
<form method="POST" action="{{ route('attachments.update', attachment.id) }}" accept-charset="UTF-8" class="form-horizontal" id="update">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
<input type="hidden" name="id" value="{{ attachment.id }}"/>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">{{ 'mandatoryFields'|_ }}</h3>
</div>
<div class="box-body">
{{ ExpandedForm.staticText('filename',attachment.filename) }}
{{ ExpandedForm.staticText('mime',attachment.mime) }}
{{ ExpandedForm.staticText('size',attachment.size|filesize) }}
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'optionalFields'|_ }}</h3>
</div>
<div class="box-body">
{{ ExpandedForm.text('title', attachment.title) }}
{{ ExpandedForm.textarea('description', attachment.description) }}
{{ ExpandedForm.textarea('notes', attachment.notes) }}
</div>
</div>
<!-- panel for options -->
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'options'|_ }}</h3>
</div>
<div class="box-body">
{{ ExpandedForm.optionsList('update','attachment') }}
</div>
<div class="box-footer">
<button type="submit" class="btn pull-right btn-success">
{{ ('update_attachment')|_ }}
</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

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