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_ENV=production
APP_DEBUG=false APP_DEBUG=false
APP_KEY=SomeRandomString APP_KEY=SomeRandomStringOf32CharsExactly
DB_CONNECTION=mysql DB_CONNECTION=mysql
DB_HOST=localhost DB_HOST=localhost
@@ -19,3 +19,6 @@ ANALYTICS_ID=
EMAIL_PRETEND=false EMAIL_PRETEND=false
RUNCLEANUP=true RUNCLEANUP=true
SITE_OWNER=mail@example.com 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)**. 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/). 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 ## Credits
@@ -90,6 +90,7 @@ Firefly III uses the following libraries and tools:
* The AdminLTE template by [Almsaseed Studio](https://almsaeedstudio.com/) * The AdminLTE template by [Almsaseed Studio](https://almsaeedstudio.com/)
* The [Google charts](https://developers.google.com/chart/) library. * The [Google charts](https://developers.google.com/chart/) library.
* [Chart.js](http://www.chartjs.org/)
* [Bootstrap](http://getbootstrap.com/) * [Bootstrap](http://getbootstrap.com/)
* [Laravel](http://laravel.com/) * [Laravel](http://laravel.com/)
* [Twig](http://twig.sensiolabs.org/) * [Twig](http://twig.sensiolabs.org/)

View File

@@ -40,4 +40,13 @@ interface AccountChartGenerator
* @return array * @return array
*/ */
public function single(Account $account, Carbon $start, Carbon $end); 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); 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 Collection $accounts
* @param Carbon $start * @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 */ /** @var Bill $entry */
foreach ($unpaid as $entry) { // loop unpaid: foreach ($unpaid as $entry) { // loop unpaid:
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')'; $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; $unpaidDescriptions[] = $description;
$unpaidAmount = bcadd($unpaidAmount, $amount); $unpaidAmount = bcadd($unpaidAmount, $amount);
unset($amount, $description); 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 * @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); $format = Config::get('firefly.' . $dateFormat . '.' . $language);
$data = [ $data = [
'count' => 1, 'count' => 2,
'labels' => [], 'labels' => [],
'datasets' => [ 'datasets' => [
[ [
'label' => trans('firefly.spent'), 'label' => trans('firefly.spent'),
'data' => [] 'data' => []
],
[
'label' => trans('firefly.earned'),
'data' => []
] ]
], ],
]; ];
foreach ($entries as $entry) { foreach ($entries as $entry) {
$data['labels'][] = $entry[0]->formatLocalized($format); $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; return $data;
@@ -93,7 +104,41 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
* *
* @return array * @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: // 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 * @package FireflyIII\Generator\Chart\PiggyBank
*/ */
@@ -26,7 +26,7 @@ class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
// language: // language:
$language = Preferences::get('language', 'en')->data; $language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language); $format = Config::get('firefly.monthAndDay.' . $language);
$data = [ $data = [
'count' => 1, '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; use Preferences;
/** /**
* Class GoogleReportChartGenerator * Class ChartJsReportChartGenerator
* *
* @package FireflyIII\Generator\Chart\Report * @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)) { if (is_null($repetition)) {
return; return;
} }
$amount = $journal->amount; bcscale(2);
$diff = $amount - $event->amount; // update current repetition
$repetition->currentamount += $diff; $amount = $journal->amount;
$diff = bcsub($amount, $event->amount); // update current repetition
$repetition->currentamount = bcadd($repetition->currentamount, $diff);
$repetition->save(); $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) 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); $this->categories->push($category);
} }
} }

View File

@@ -3,6 +3,9 @@
namespace FireflyIII\Helpers\Csv\Converter; namespace FireflyIII\Helpers\Csv\Converter;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use InvalidArgumentException;
use Log;
use Session; use Session;
/** /**
@@ -19,8 +22,16 @@ class Date extends BasicConverter implements ConverterInterface
public function convert() public function convert()
{ {
$format = Session::get('csv-date-format'); $format = Session::get('csv-date-format');
try {
$date = Carbon::createFromFormat($format, $this->value); $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; return $date;
} }

View File

@@ -124,7 +124,7 @@ class Importer
*/ */
protected function parseRow($index) 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\Exceptions\FireflyException;
use FireflyIII\Helpers\Csv\Mapper\MapperInterface; use FireflyIII\Helpers\Csv\Mapper\MapperInterface;
use League\Csv\Reader; use League\Csv\Reader;
use Log;
use ReflectionException; use ReflectionException;
use Session; use Session;
use Log;
/** /**
* Class Wizard * Class Wizard
* *
@@ -110,6 +111,7 @@ class Wizard implements WizardInterface
foreach ($fields as $field) { foreach ($fields as $field) {
if (!Session::has($field)) { if (!Session::has($field)) {
Log::error('Session is missing field: ' . $field); Log::error('Session is missing field: ' . $field);
return false; return false;
} }
} }

View File

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

View File

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

View File

@@ -130,7 +130,7 @@ class AccountController extends Controller
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'), 'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null, 'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
'openingBalance' => $openingBalanceAmount, 'openingBalance' => $openingBalanceAmount,
'virtualBalance' => floatval($account->virtual_balance) 'virtualBalance' => round($account->virtual_balance, 2)
]; ];
Session::flash('preFilled', $preFilled); Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'accounts'); Session::flash('gaEventCategory', 'accounts');
@@ -167,10 +167,11 @@ class AccountController extends Controller
$startBalances = Steam::balancesById($ids, $start); $startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end); $endBalances = Steam::balancesById($ids, $end);
$activities = Steam::getLastActivities($ids);
$accounts->each( $accounts->each(
function (Account $account) use ($startBalances, $endBalances) { function (Account $account) use ($activities, $startBalances, $endBalances) {
$account->lastActivityDate = null;//$repository->getLastActivity($account); $account->lastActivityDate = isset($activities[$account->id]) ? $activities[$account->id] : null;
$account->startBalance = isset($startBalances[$account->id]) ? $startBalances[$account->id] : null; $account->startBalance = isset($startBalances[$account->id]) ? $startBalances[$account->id] : null;
$account->endBalance = isset($endBalances[$account->id]) ? $endBalances[$account->id] : null; $account->endBalance = isset($endBalances[$account->id]) ? $endBalances[$account->id] : null;
} }
@@ -209,12 +210,12 @@ class AccountController extends Controller
$accountData = [ $accountData = [
'name' => $request->input('name'), 'name' => $request->input('name'),
'accountType' => $request->input('what'), 'accountType' => $request->input('what'),
'virtualBalance' => floatval($request->input('virtualBalance')), 'virtualBalance' => round($request->input('virtualBalance'), 2),
'active' => true, 'active' => true,
'user' => Auth::user()->id, 'user' => Auth::user()->id,
'iban' => $request->input('iban'), 'iban' => $request->input('iban'),
'accountRole' => $request->input('accountRole'), 'accountRole' => $request->input('accountRole'),
'openingBalance' => floatval($request->input('openingBalance')), 'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')), 'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')), 'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
@@ -252,8 +253,8 @@ class AccountController extends Controller
'user' => Auth::user()->id, 'user' => Auth::user()->id,
'iban' => $request->input('iban'), 'iban' => $request->input('iban'),
'accountRole' => $request->input('accountRole'), 'accountRole' => $request->input('accountRole'),
'virtualBalance' => floatval($request->input('virtualBalance')), 'virtualBalance' => round($request->input('virtualBalance'), 2),
'openingBalance' => floatval($request->input('openingBalance')), 'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')), 'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')), 'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'ccType' => $request->input('ccType'), '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\Models\Role;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers; use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Mail\Message; use Illuminate\Mail\Message;
use Mail; use Mail;
use Request as Rq;
use Session; use Session;
use Twig; use Twig;
use Validator; use Validator;
@@ -19,19 +21,81 @@ use Validator;
*/ */
class AuthController extends Controller class AuthController extends Controller
{ {
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
/* /**
|-------------------------------------------------------------------------- * Show the application registration form.
| Registration & Login Controller *
|-------------------------------------------------------------------------- * @return \Illuminate\Http\Response
|
| 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?
|
*/ */
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 = '/'; public $redirectTo = '/';

View File

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

View File

@@ -151,10 +151,12 @@ class CategoryController extends Controller
$page = intval(Input::get('page')); $page = intval(Input::get('page'));
$set = $repository->getJournals($category, $page); $set = $repository->getJournals($category, $page);
$count = $repository->countJournals($category); $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 = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('categories/show/' . $category->id); $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); 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. * Shows the balances for all the user's frontpage accounts.
* *

View File

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

View File

@@ -50,7 +50,6 @@ class CategoryController extends Controller
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range); $start = Navigation::startOfPeriod($start, $range);
$end = new Carbon; $end = new Carbon;
$entries = new Collection; $entries = new Collection;
@@ -66,7 +65,7 @@ class CategoryController extends Controller
while ($start <= $end) { while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range); $currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriodCorrected($category, $start, $currentEnd); $spent = $repository->balanceInPeriod($category, $start, $currentEnd);
$entries->push([clone $start, $spent]); $entries->push([clone $start, $spent]);
$start = Navigation::addPeriod($start, $range, 0); $start = Navigation::addPeriod($start, $range, 0);
@@ -170,7 +169,7 @@ class CategoryController extends Controller
* *
* @return \Symfony\Component\HttpFoundation\Response * @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'); $start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31'); $end = new Carbon($year . '-12-31');
@@ -179,14 +178,24 @@ class CategoryController extends Controller
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('category'); $cache->addProperty('category');
$cache->addProperty('year'); $cache->addProperty('spent-in-year');
if ($cache->has()) { if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
$shared = $shared == 'shared' ? true : false; $shared = $shared == 'shared' ? true : false;
$categories = $repository->getCategories(); $allCategories = $repository->getCategories();
$entries = new Collection; $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) { while ($start < $end) {
$month = clone $start; // month is the current end of the period $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: $row = [clone $start]; // make a row:
foreach ($categories as $category) { // each budget, fill the row foreach ($categories as $category) { // each budget, fill the row
$spent = $repository->spentInPeriodCorrected($category, $start, $month, $shared); $spent = $repository->balanceInPeriod($category, $start, $month, $shared);
$row[] = $spent; if ($spent < 0) {
$row[] = $spent * -1;
} else {
$row[] = 0;
}
} }
$entries->push($row); $entries->push($row);
$start->addMonth(); $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); $cache->store($data);
return Response::json($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; <?php namespace FireflyIII\Http\Controllers;
use Artisan;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Config; use Config;
@@ -58,6 +59,7 @@ class HomeController extends Controller
Session::clear(); Session::clear();
Artisan::call('cache:clear');
return redirect(route('index')); return redirect(route('index'));
} }
@@ -85,10 +87,8 @@ class HomeController extends Controller
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$showTour = Preferences::get('tour', true)->data; $showTour = Preferences::get('tour', true)->data;
$accounts = $repository->getFrontpageAccounts($frontPage); $accounts = $repository->getFrontpageAccounts($frontPage);
$savings = $repository->getSavingsAccounts(); $savings = $repository->getSavingsAccounts();
$piggyBankAccounts = $repository->getPiggyBankAccounts(); $piggyBankAccounts = $repository->getPiggyBankAccounts();

View File

@@ -161,7 +161,7 @@ class JsonController extends Controller
} }
/** @var Bill $entry */ /** @var Bill $entry */
foreach ($unpaid as $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); $amount = bcadd($amount, $current);
} }

View File

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

View File

@@ -47,10 +47,11 @@ class PiggyBankController extends Controller
*/ */
public function add(AccountRepositoryInterface $repository, PiggyBank $piggyBank) public function add(AccountRepositoryInterface $repository, PiggyBank $piggyBank)
{ {
bcscale(2);
$date = Session::get('end', Carbon::now()->endOfMonth()); $date = Session::get('end', Carbon::now()->endOfMonth());
$leftOnAccount = $repository->leftOnAccount($piggyBank->account, $date); $leftOnAccount = $repository->leftOnAccount($piggyBank->account, $date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount; $savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar; $leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
$maxAmount = min($leftOnAccount, $leftToSave); $maxAmount = min($leftOnAccount, $leftToSave);
return view('piggy-banks.add', compact('piggyBank', 'maxAmount')); return view('piggy-banks.add', compact('piggyBank', 'maxAmount'));
@@ -66,7 +67,7 @@ class PiggyBankController extends Controller
$periods = Config::get('firefly.piggy_bank_periods'); $periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account'])); $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$subTitle = trans('firefly.create_new_piggybank'); $subTitle = trans('firefly.new_piggy_bank');
$subTitleIcon = 'fa-plus'; $subTitleIcon = 'fa-plus';
// put previous url in session if not redirect from store (not "create another"). // put previous url in session if not redirect from store (not "create another").
@@ -171,9 +172,9 @@ class PiggyBankController extends Controller
$accounts = []; $accounts = [];
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $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->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: * Fill account information:
@@ -185,7 +186,7 @@ class PiggyBankController extends Controller
'balance' => Steam::balance($account, $end, true), 'balance' => Steam::balance($account, $end, true),
'leftForPiggyBanks' => $repository->leftOnAccount($account, $end), 'leftForPiggyBanks' => $repository->leftOnAccount($account, $end),
'sumOfSaved' => $piggyBank->savedSoFar, 'sumOfSaved' => $piggyBank->savedSoFar,
'sumOfTargets' => floatval($piggyBank->targetamount), 'sumOfTargets' => round($piggyBank->targetamount, 2),
'leftToSave' => $piggyBank->leftToSave 'leftToSave' => $piggyBank->leftToSave
]; ];
} else { } else {
@@ -211,7 +212,7 @@ class PiggyBankController extends Controller
if (is_array($data)) { if (is_array($data)) {
foreach ($data as $order => $id) { 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) 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()); $date = Session::get('end', Carbon::now()->endOfMonth());
$leftOnAccount = $accounts->leftOnAccount($piggyBank->account, $date); $leftOnAccount = $accounts->leftOnAccount($piggyBank->account, $date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount; $savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar; $leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
$maxAmount = round(min($leftOnAccount, $leftToSave), 2); $maxAmount = round(min($leftOnAccount, $leftToSave), 2);
bcscale(2);
if ($amount <= $maxAmount) { if ($amount <= $maxAmount) {
$repetition = $piggyBank->currentRelevantRep(); $repetition = $piggyBank->currentRelevantRep();
@@ -258,7 +259,7 @@ class PiggyBankController extends Controller
*/ */
public function postRemove(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank) public function postRemove(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{ {
$amount = floatval(Input::get('amount')); $amount = round(Input::get('amount'), 2);
bcscale(2); bcscale(2);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount; $savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
@@ -319,7 +320,7 @@ class PiggyBankController extends Controller
'name' => $request->get('name'), 'name' => $request->get('name'),
'startdate' => new Carbon, 'startdate' => new Carbon,
'account_id' => intval($request->get('account_id')), 'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')), 'targetamount' => round($request->get('targetamount'), 2),
'remind_me' => false, 'remind_me' => false,
'reminder_skip' => 0, 'reminder_skip' => 0,
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null, 'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
@@ -354,7 +355,7 @@ class PiggyBankController extends Controller
'name' => $request->get('name'), 'name' => $request->get('name'),
'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate, 'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate,
'account_id' => intval($request->get('account_id')), 'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')), 'targetamount' => round($request->get('targetamount'), 2),
'remind_me' => false, 'remind_me' => false,
'reminder_skip' => 0, 'reminder_skip' => 0,
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null, '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;
use FireflyIII\Http\Requests\DeleteAccountFormRequest; use FireflyIII\Http\Requests\DeleteAccountFormRequest;
use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\Http\Requests\ProfileFormRequest;
use FireflyIII\User;
use Hash; use Hash;
use Session; use Session;
@@ -20,7 +21,7 @@ class ProfileController extends Controller
*/ */
public function changePassword() 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' 'mainTitleIcon', 'fa-user'
); );
} }
@@ -30,7 +31,7 @@ class ProfileController extends Controller
*/ */
public function deleteAccount() 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' 'mainTitleIcon', 'fa-user'
); );
} }
@@ -41,7 +42,7 @@ class ProfileController extends Controller
*/ */
public function index() 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 // old, new1, new2
if (!Hash::check($request->get('current_password'), Auth::user()->password)) { 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')); return redirect(route('profile.change-password'));
} }
@@ -68,7 +69,7 @@ class ProfileController extends Controller
Auth::user()->password = $request->get('new_password'); Auth::user()->password = $request->get('new_password');
Auth::user()->save(); Auth::user()->save();
Session::flash('success', 'Password changed!'); Session::flash('success', trans('firefly.password_changed'));
return redirect(route('profile')); return redirect(route('profile'));
} }
@@ -83,7 +84,7 @@ class ProfileController extends Controller
protected function validatePassword($old, $new1) protected function validatePassword($old, $new1)
{ {
if ($new1 == $old) { if ($new1 == $old) {
return 'The idea is to change your password.'; return trans('firefly.should_change');
} }
return true; return true;
@@ -100,17 +101,28 @@ class ProfileController extends Controller
{ {
// old, new1, new2 // old, new1, new2
if (!Hash::check($request->get('password'), Auth::user()->password)) { 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')); return redirect(route('profile.delete-account'));
} }
// DELETE! // DELETE!
$email = Auth::user()->email;
Auth::user()->delete(); Auth::user()->delete();
Session::flush(); Session::flush();
Session::flash('gaEventCategory', 'user'); Session::flash('gaEventCategory', 'user');
Session::flash('gaEventAction', 'delete-account'); 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')); return redirect(route('index'));
} }

View File

@@ -188,21 +188,24 @@ class TagController extends Controller
/** @var Collection $tags */ /** @var Collection $tags */
$tags = Auth::user()->tags()->where('tagMode', $type)->orderBy('date', 'ASC')->get(); $tags = Auth::user()->tags()->where('tagMode', $type)->orderBy('date', 'ASC')->get();
$tags = $tags->sortBy( $tags = $tags->sortBy(
function (Tag $tag) { 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 */ /** @var Tag $tag */
foreach ($tags as $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')); return view('tags.index', compact('title', 'mainTitleIcon', 'types', 'helpHidden', 'collection'));
} }

View File

@@ -1,12 +1,15 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Amount;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Config; use Config;
use ExpandedForm; use ExpandedForm;
use FireflyIII\Events\JournalCreated; use FireflyIII\Events\JournalCreated;
use FireflyIII\Events\JournalSaved; use FireflyIII\Events\JournalSaved;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Requests\JournalFormRequest; use FireflyIII\Http\Requests\JournalFormRequest;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -15,6 +18,7 @@ use Input;
use Preferences; use Preferences;
use Response; use Response;
use Session; use Session;
use Steam;
use URL; use URL;
use View; use View;
@@ -43,10 +47,21 @@ class TransactionController extends Controller
*/ */
public function create(AccountRepositoryInterface $repository, $what = 'deposit') 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'])); $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get()); $budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = trans('form.noBudget'); $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'); $piggies[0] = trans('form.noPiggybank');
$preFilled = Session::has('preFilled') ? Session::get('preFilled') : []; $preFilled = Session::has('preFilled') ? Session::get('preFilled') : [];
$respondTo = ['account_id', 'account_from_id']; $respondTo = ['account_id', 'account_from_id'];
@@ -68,7 +83,7 @@ class TransactionController extends Controller
asort($piggies); 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) 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); $what = strtolower($journal->transactionType->type);
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account'])); $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get()); $budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
@@ -179,7 +197,7 @@ class TransactionController extends Controller
Session::forget('transactions.edit.fromUpdate'); 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) public function show(JournalRepositoryInterface $repository, TransactionJournal $journal)
{ {
bcscale(2);
$journal->transactions->each( $journal->transactions->each(
function (Transaction $t) use ($journal, $repository) { function (Transaction $t) use ($journal, $repository) {
$t->before = $repository->getAmountBefore($journal, $t); $t->before = $repository->getAmountBefore($journal, $t);
$t->after = $t->before + $t->amount; $t->after = bcadd($t->before, $t->amount);
} }
); );
$what = strtolower($journal->transactionType->type); $what = strtolower($journal->transactionType->type);
@@ -256,15 +275,33 @@ class TransactionController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
*/ */
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository) public function store(JournalFormRequest $request, JournalRepositoryInterface $repository, AttachmentHelperInterface $att)
{ {
$journalData = $request->getJournalData(); $journalData = $request->getJournalData();
// if not withdrawal, unset budgetid.
if ($journalData['what'] != 'withdrawal') {
$journalData['budget_id'] = 0;
}
$journal = $repository->store($journalData); $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 // rescan journal, UpdateJournalConnection
event(new JournalSaved($journal)); event(new JournalSaved($journal));
// ConnectJournalToPiggyBank
if ($journal->transactionType->type == 'Transfer' && intval($request->get('piggy_bank_id')) > 0) { if ($journal->transactionType->type == 'Transfer' && intval($request->get('piggy_bank_id')) > 0) {
event(new JournalCreated($journal, intval($request->get('piggy_bank_id')))); event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
} }
@@ -288,16 +325,29 @@ class TransactionController extends Controller
/** /**
* @param JournalFormRequest $request * @param JournalFormRequest $request
* @param JournalRepositoryInterface $repository * @param JournalRepositoryInterface $repository
* @param AttachmentHelperInterface $att
* @param TransactionJournal $journal * @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(); $journalData = $request->getJournalData();
$repository->update($journal, $journalData); $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)); event(new JournalSaved($journal));
// update, get events by date and sort DESC // update, get events by date and sort DESC

View File

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

View File

@@ -24,5 +24,4 @@ class VerifyCsrfToken extends BaseVerifier
{ {
return parent::handle($request, $next); 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 [ return [
'name' => $this->get('name'), 'name' => $this->get('name'),
'match' => $this->get('match'), 'match' => $this->get('match'),
'amount_min' => floatval($this->get('amount_min')), 'amount_min' => round($this->get('amount_min'), 2),
'amount_currency_id' => floatval($this->get('amount_currency_id')), 'amount_currency_id' => round($this->get('amount_currency_id'), 2),
'amount_max' => floatval($this->get('amount_max')), 'amount_max' => round($this->get('amount_max'), 2),
'date' => new Carbon($this->get('date')), 'date' => new Carbon($this->get('date')),
'user' => Auth::user()->id, 'user' => Auth::user()->id,
'repeat_freq' => $this->get('repeat_freq'), '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')), 'account_to_id' => intval($this->get('account_to_id')),
'expense_account' => $this->get('expense_account'), 'expense_account' => $this->get('expense_account'),
'revenue_account' => $this->get('revenue_account'), 'revenue_account' => $this->get('revenue_account'),
'amount' => floatval($this->get('amount')), 'amount' => round($this->get('amount'), 2),
'user' => Auth::user()->id, 'user' => Auth::user()->id,
'amount_currency_id' => intval($this->get('amount_currency_id')), 'amount_currency_id' => intval($this->get('amount_currency_id')),
'date' => new Carbon($this->get('date')), 'date' => new Carbon($this->get('date')),

View File

@@ -280,9 +280,16 @@ Breadcrumbs::register(
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'change-password', function (Generator $breadcrumbs) { 'profile.change-password', function (Generator $breadcrumbs) {
$breadcrumbs->parent('profile'); $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 <?php
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\Category; 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( Route::bind(
'currency', function ($value) { 'currency', function ($value) {
if (Auth::check()) { if (Auth::check()) {
@@ -144,6 +158,7 @@ Route::bind(
* Auth\AuthController * Auth\AuthController
*/ */
Route::get('/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'register']); Route::get('/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'register']);
Route::get('/cron/sendgrid', ['uses' => 'CronController@sendgrid']);
Route::controllers( Route::controllers(
[ [
@@ -177,6 +192,21 @@ Route::group(
Route::post('/accounts/update/{account}', ['uses' => 'AccountController@update', 'as' => 'accounts.update']); Route::post('/accounts/update/{account}', ['uses' => 'AccountController@update', 'as' => 'accounts.update']);
Route::post('/accounts/destroy/{account}', ['uses' => 'AccountController@destroy', 'as' => 'accounts.destroy']); 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 * Bills Controller
*/ */
@@ -252,6 +282,7 @@ Route::group(
*/ */
// accounts: // accounts:
Route::get('/chart/account/frontpage', ['uses' => 'Chart\AccountController@frontpage']); 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( Route::get('/chart/account/month/{year}/{month}/{shared?}', ['uses' => 'Chart\AccountController@all'])->where(
['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared'] ['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']
); );
@@ -270,7 +301,12 @@ Route::group(
// categories: // categories:
Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']); 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}/month', ['uses' => 'Chart\CategoryController@month']); // should be period.
Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']); 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 whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereEncrypted($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereEncrypted($value)
* @property-read float $spent * @property-read float $spent
* @property-read \Carbon\Carbon $lastActivity * @property \Carbon\Carbon $lastActivity
*/ */
class Category extends Model 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 whereRemindMe($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereReminderSkip($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereReminderSkip($value)
* @property-read \FireflyIII\Models\PiggyBankRepetition $currentRep * @property-read \FireflyIII\Models\PiggyBankRepetition $currentRep
* @property string $reminder
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereReminder($value)
*/ */
class PiggyBank extends Model class PiggyBank extends Model
{ {

View File

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

View File

@@ -66,8 +66,9 @@ use Watson\Validating\ValidatingTrait;
* @property-read bool $joinedTransactions * @property-read bool $joinedTransactions
* @property-read bool $joinedTransactionTypes * @property-read bool $joinedTransactionTypes
* @property-read int $account_id * @property-read int $account_id
* @property-read string $name * @property string $name
* @property-read string $symbol * @property-read string $symbol
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Attachment[] $attachments
*/ */
class TransactionJournal extends Model class TransactionJournal extends Model
{ {
@@ -467,6 +468,14 @@ class TransactionJournal extends Model
$this->attributes['encrypted'] = true; $this->attributes['encrypted'] = true;
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function attachments()
{
return $this->morphMany('FireflyIII\Models\Attachment', 'attachable');
}
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @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\PiggyBank\PiggyBankRepositoryInterface', 'FireflyIII\Repositories\PiggyBank\PiggyBankRepository');
$this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository'); $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\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'); $this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
// CSV import // CSV import
$this->app->bind('FireflyIII\Helpers\Csv\WizardInterface', 'FireflyIII\Helpers\Csv\Wizard'); $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: // 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\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\Bill\BillChartGenerator', 'FireflyIII\Generator\Chart\Bill\ChartJsBillChartGenerator');
$this->app->bind('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator', 'FireflyIII\Generator\Chart\Budget\ChartJsBudgetChartGenerator'); $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()) { if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
} }
$query = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account']);
if (count($preference->data) > 0) {
if ($preference->data == []) { $query->whereIn('accounts.id', $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.*']);
} }
$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. * 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) { if (count($ids) > 0) {
$accounts = Auth::user()->accounts()->whereIn('id', $ids)->get(); $accounts = Auth::user()->accounts()->whereIn('id', $ids)->get();
} }
bcscale(2);
$accounts->each( $accounts->each(
function (Account $account) use ($start, $end) { function (Account $account) use ($start, $end) {
@@ -266,7 +250,7 @@ class AccountRepository implements AccountRepositoryInterface
// sum of piggy bank amounts on this account: // sum of piggy bank amounts on this account:
// diff between endBalance and piggyBalance. // diff between endBalance and piggyBalance.
// then, percentage. // then, percentage.
$difference = $account->endBalance - $account->piggyBalance; $difference = bcsub($account->endBalance, $account->piggyBalance);
$account->difference = $difference; $account->difference = $difference;
$account->percentage = $difference != 0 && $account->endBalance != 0 ? round((($difference / $account->endBalance) * 100)) : 100; $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); $start = clone Session::get('start', new Carbon);
$end = clone Session::get('end', new Carbon); $end = clone Session::get('end', new Carbon);
bcscale(2);
$accounts->each( $accounts->each(
function (Account $account) use ($start, $end) { function (Account $account) use ($start, $end) {
$account->startBalance = Steam::balance($account, $start); $account->startBalance = Steam::balance($account, $start);
$account->endBalance = Steam::balance($account, $end); $account->endBalance = Steam::balance($account, $end);
// diff (negative when lost, positive when gained) // diff (negative when lost, positive when gained)
$diff = $account->endBalance - $account->startBalance; $diff = bcsub($account->endBalance, $account->startBalance);
if ($diff < 0 && $account->startBalance > 0) { if ($diff < 0 && $account->startBalance > 0) {
// percentage lost compared to start. // percentage lost compared to start.
@@ -429,11 +415,11 @@ class AccountRepository implements AccountRepositoryInterface
} }
/** /**
* @return float * @return string
*/ */
public function sumOfEverything() 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) protected function storeInitialBalance(Account $account, Account $opposing, array $data)
{ {
$type = $data['openingBalance'] < 0 ? 'Withdrawal' : 'Deposit'; $transactionType = TransactionType::whereType('Opening balance')->first();
$transactionType = TransactionType::whereType($type)->first(); $journal = TransactionJournal::create(
$journal = new TransactionJournal(
[ [
'user_id' => $data['user'], 'user_id' => $data['user'],
'transaction_type_id' => $transactionType->id, 'transaction_type_id' => $transactionType->id,
@@ -577,7 +561,6 @@ class AccountRepository implements AccountRepositoryInterface
'encrypted' => true 'encrypted' => true
] ]
); );
$journal->save();
if ($data['openingBalance'] < 0) { if ($data['openingBalance'] < 0) {
$firstAccount = $opposing; $firstAccount = $opposing;
@@ -590,6 +573,7 @@ class AccountRepository implements AccountRepositoryInterface
$firstAmount = $data['openingBalance']; $firstAmount = $data['openingBalance'];
$secondAmount = $data['openingBalance'] * -1; $secondAmount = $data['openingBalance'] * -1;
} }
$one = new Transaction(['account_id' => $firstAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $firstAmount]); $one = new Transaction(['account_id' => $firstAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $firstAmount]);
$one->save();// first transaction: from $one->save();// first transaction: from

View File

@@ -96,14 +96,7 @@ interface AccountRepositoryInterface
public function getJournals(Account $account, $page); public function getJournals(Account $account, $page);
/** /**
* @param Account $account * @return string
*
* @return Carbon|null
*/
public function getLastActivity(Account $account);
/**
* @return float
*/ */
public function sumOfEverything(); 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) 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.*']); ->first(['limit_repetitions.*']);
if ($repetition) { if ($repetition) {
return floatval($repetition->amount); return $repetition->amount;
} }
return null; return null;
@@ -299,7 +300,9 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
->transactionTypes(['Withdrawal']) ->transactionTypes(['Withdrawal'])
->get(['transaction_journals.*'])->sum('amount'); ->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 * @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 * @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 * @param array $data

View File

@@ -57,6 +57,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
return $set; return $set;
} }
/** /**
* *
* @param Carbon $start * @param Carbon $start
@@ -64,7 +65,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
* *
* @return array * @return array
*/ */
public function getCategoriesAndExpensesCorrected($start, $end) public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end)
{ {
$set = Auth::user()->transactionjournals() $set = Auth::user()->transactionjournals()
->leftJoin( ->leftJoin(
@@ -89,7 +90,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
$name = $isEncrypted ? Crypt::decrypt($name) : $name; $name = $isEncrypted ? Crypt::decrypt($name) : $name;
$result[$categoryId] = [ $result[$categoryId] = [
'name' => $name, 'name' => $name,
'sum' => floatval($entry->amount), 'sum' => $entry->amount,
]; ];
} }
@@ -183,9 +184,9 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
* *
* @return string * @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 Category $category
* @param Carbon $date * @param Carbon $date
* *
* @return float * @return string
*/ */
public function spentOnDaySumCorrected(Category $category, Carbon $date) 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; 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 * @return array
*/ */
public function getCategoriesAndExpensesCorrected($start, $end); public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end);
/** /**
* @param Category $category * @param Category $category
@@ -57,6 +57,18 @@ interface CategoryRepositoryInterface
*/ */
public function getJournals(Category $category, $page); 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 * @param Category $category
* *
@@ -83,7 +95,7 @@ interface CategoryRepositoryInterface
* *
* @return string * @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. // update the from and to transaction.
/** @var Transaction $transaction */ /** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) { foreach ($journal->transactions()->get() as $transaction) {
if (floatval($transaction->amount) < 0) { if ($transaction->amount < 0) {
// this is the from transaction, negative amount: // this is the from transaction, negative amount:
$transaction->amount = $data['amount'] * -1; $transaction->amount = $data['amount'] * -1;
$transaction->account_id = $fromAccount->id; $transaction->account_id = $fromAccount->id;
$transaction->save(); $transaction->save();
} }
if (floatval($transaction->amount) > 0) { if ($transaction->amount > 0) {
$transaction->amount = $data['amount']; $transaction->amount = $data['amount'];
$transaction->account_id = $toAccount->id; $transaction->account_id = $toAccount->id;
$transaction->save(); $transaction->save();

View File

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

View File

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

View File

@@ -64,16 +64,16 @@ class Amount
if ($coloured === true) { if ($coloured === true) {
if ($amount === 0.0) { if ($amount === 0.0) {
return '<span style="color:#999">' . $symbol . ' ' . $string . '</span>'; return '<span style="color:#999">' . $symbol . '&nbsp;' . $string . '</span>';
} }
if ($amount > 0) { 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 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 $name
* @param null $value * @param null $value

View File

@@ -2,6 +2,7 @@
namespace FireflyIII\Support; namespace FireflyIII\Support;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use DB; use DB;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
@@ -14,6 +15,28 @@ use FireflyIII\Models\Transaction;
*/ */
class Steam 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 * @param \FireflyIII\Models\Account $account
@@ -90,4 +113,33 @@ class Steam
return $result; 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->formatAmountPlain(),
$this->formatJournal(), $this->formatJournal(),
$this->balance(), $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 '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 * @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 wherePassword($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereReset($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereReset($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereRememberToken($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 class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{ {
@@ -44,7 +49,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
* *
* @var array * @var array
*/ */
protected $fillable = ['email', 'password']; protected $fillable = ['email', 'password', 'blocked', 'blocked_code'];
/** /**
* The attributes excluded from the model's JSON form. * 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 $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 * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */

View File

@@ -30,18 +30,12 @@
"rcrowe/twigbridge": "0.7.x@dev", "rcrowe/twigbridge": "0.7.x@dev",
"zizaco/entrust": "dev-laravel-5", "zizaco/entrust": "dev-laravel-5",
"codeception/codeception": "*", "codeception/codeception": "*",
"league/csv": "^7.1" "league/csv": "^7.1",
"nesbot/carbon": "^1.20"
}, },
"require-dev": { "require-dev": {
"barryvdh/laravel-debugbar": "@stable", "barryvdh/laravel-debugbar": "@stable",
"barryvdh/laravel-ide-helper": "~2.0", "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"
}, },
"autoload": { "autoload": {
"classmap": [ "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... * Application Service Providers...
*/ */
'FireflyIII\Providers\AppServiceProvider', 'FireflyIII\Providers\AppServiceProvider',
//'FireflyIII\Providers\BusServiceProvider', 'FireflyIII\Providers\BusServiceProvider',
//'FireflyIII\Providers\ConfigServiceProvider', 'FireflyIII\Providers\ConfigServiceProvider',
'FireflyIII\Providers\EventServiceProvider', 'FireflyIII\Providers\EventServiceProvider',
'FireflyIII\Providers\RouteServiceProvider', 'FireflyIII\Providers\RouteServiceProvider',
'FireflyIII\Providers\FireflyServiceProvider', 'FireflyIII\Providers\FireflyServiceProvider',

View File

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

View File

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

View File

@@ -2,22 +2,6 @@
return [ 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 | Session Lifetime
@@ -33,18 +17,6 @@ return [
'expire_on_close' => false, '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', '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], 'lottery' => [2, 100],
/* 'driver' => env('SESSION_DRIVER', 'database'),
|-------------------------------------------------------------------------- 'cookie' => 'firefly_session',
| 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.
|
*/
'cookie' => 'laravel_session', 'connection' => env('DB_CONNECTION', 'mysql'),
'encrypt' => true,
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@@ -145,7 +145,7 @@ return [
'ExpandedForm' => [ 'ExpandedForm' => [
'is_safe' => [ 'is_safe' => [
'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location', 'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location',
'multiRadio','file','multiCheckbox' 'multiRadio', 'file', 'multiCheckbox', 'staticText'
] ]
], ],
'Form' => [ '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: // pay daily groceries:
$this->createGroceries($current); $this->createGroceries($current);
// create tag (each type of tag, for date):
$this->createTags($current);
// go out for drinks: // go out for drinks:
$this->createDrinksAndOthers($current); $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', 'startdate' => '2015-04-01',
'reminder_skip' => 0, 'reminder_skip' => 0,
'remind_me' => 0, 'remind_me' => 0,
'order' => 1, 'order' => 2,
] ]
); );
$repetition = $phone->piggyBankRepetitions()->first(); $repetition = $phone->piggyBankRepetitions()->first();
@@ -305,7 +325,7 @@ class TestDataSeeder extends Seeder
'startdate' => '2015-04-01', 'startdate' => '2015-04-01',
'reminder_skip' => 0, 'reminder_skip' => 0,
'remind_me' => 0, 'remind_me' => 0,
'order' => 1, 'order' => 3,
] ]
); );
$repetition = $couch->piggyBankRepetitions()->first(); $repetition = $couch->piggyBankRepetitions()->first();
@@ -334,6 +354,20 @@ class TestDataSeeder extends Seeder
'amount' => '40' '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) protected function createIncome($description, Carbon $date, $amount)
{ {
$date = new Carbon($date->format('Y-m') . '-23'); // paid on 23rd. $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'); $toAccount = $this->findAccount('MyBank Checking Account');
$fromAccount = $this->findAccount('Job'); $fromAccount = $this->findAccount('Job');
$category = Category::firstOrCreateEncrypted(['name' => 'Salary', 'user_id' => $this->user->id]); $category = Category::firstOrCreateEncrypted(['name' => 'Salary', 'user_id' => $this->user->id]);
@@ -596,6 +634,7 @@ class TestDataSeeder extends Seeder
{ {
$start = clone $date; $start = clone $date;
$end = clone $date; $end = clone $date;
$today = new Carbon;
$start->startOfMonth(); $start->startOfMonth();
$end->endOfMonth(); $end->endOfMonth();
@@ -605,7 +644,7 @@ class TestDataSeeder extends Seeder
$budget = Budget::firstOrCreateEncrypted(['name' => 'Groceries', 'user_id' => $this->user->id]); $budget = Budget::firstOrCreateEncrypted(['name' => 'Groceries', 'user_id' => $this->user->id]);
$current = clone $start; $current = clone $start;
while ($current < $end) { while ($current < $end && $current < $today) {
// daily groceries: // daily groceries:
$amount = rand(1000, 2500) / 100; $amount = rand(1000, 2500) / 100;
$toAccount = $this->findAccount($stores[rand(0, count($stores) - 1)]); $toAccount = $this->findAccount($stores[rand(0, count($stores) - 1)]);
@@ -651,10 +690,11 @@ class TestDataSeeder extends Seeder
{ {
$start = clone $date; $start = clone $date;
$end = clone $date; $end = clone $date;
$today = new Carbon;
$start->startOfMonth(); $start->startOfMonth();
$end->endOfMonth(); $end->endOfMonth();
$current = clone $start; $current = clone $start;
while ($current < $end) { while ($current < $end && $current < $today) {
// weekly drink: // weekly drink:
$thisDate = clone $current; $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;} #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;} .handle {cursor:move;}
.ui-sortable-placeholder { .ui-sortable-placeholder {
@@ -11,3 +11,9 @@
margin: 0; margin: 0;
padding: 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'; require __DIR__ . '/../bootstrap/autoload.php';
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Turn On The Lights | Turn On The Lights
@@ -65,5 +56,4 @@ $response = $kernel->handle(
$response->send(); $response->send();
$kernel->terminate($request, $response); $kernel->terminate($request, $response);

View File

@@ -3,18 +3,6 @@
/* /*
Make some colours: Make some colours:
*/ */
/*
#555299
#4285f4
#
#
#
#
#
#
#
#", "#", "#", "#"],
*/
var colourSet = [ var colourSet = [
[53, 124, 165], [53, 124, 165],
[0, 141, 76], [0, 141, 76],
@@ -54,6 +42,7 @@ var defaultAreaOptions = {
datasetStrokeWidth: 1, datasetStrokeWidth: 1,
pointHitDetectionRadius: 5, pointHitDetectionRadius: 5,
datasetFill: true, datasetFill: true,
animation: false,
scaleFontSize: 10, scaleFontSize: 10,
responsive: false, responsive: false,
scaleLabel: " <%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>", scaleLabel: " <%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
@@ -68,6 +57,7 @@ var defaultPieOptions = {
datasetStrokeWidth: 1, datasetStrokeWidth: 1,
pointHitDetectionRadius: 5, pointHitDetectionRadius: 5,
datasetFill: false, datasetFill: false,
animation: false,
scaleFontSize: 10, scaleFontSize: 10,
responsive: false, responsive: false,
tooltipFillColor: "rgba(0,0,0,0.5)", tooltipFillColor: "rgba(0,0,0,0.5)",
@@ -81,6 +71,7 @@ var defaultLineOptions = {
pointDotRadius: 2, pointDotRadius: 2,
datasetStrokeWidth: 1, datasetStrokeWidth: 1,
pointHitDetectionRadius: 5, pointHitDetectionRadius: 5,
animation: false,
datasetFill: false, datasetFill: false,
scaleFontSize: 10, scaleFontSize: 10,
responsive: false, responsive: false,
@@ -98,6 +89,7 @@ var defaultColumnOptions = {
datasetFill: false, datasetFill: false,
scaleFontSize: 10, scaleFontSize: 10,
responsive: false, responsive: false,
animation: false,
scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>", scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
tooltipFillColor: "rgba(0,0,0,0.5)", tooltipFillColor: "rgba(0,0,0,0.5)",
tooltipTemplate: "<%if (label){%><%=label%>: <%}%>" + currencySymbol + " <%= value %>", tooltipTemplate: "<%if (label){%><%=label%>: <%}%>" + currencySymbol + " <%= value %>",
@@ -110,6 +102,7 @@ var defaultStackedColumnOptions = {
barStrokeWidth: 1, barStrokeWidth: 1,
pointHitDetectionRadius: 5, pointHitDetectionRadius: 5,
datasetFill: false, datasetFill: false,
animation: false,
scaleFontSize: 10, scaleFontSize: 10,
responsive: false, responsive: false,
scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>", scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
@@ -147,7 +140,7 @@ function lineChart(URL, container, options) {
new Chart(ctx).Line(newData, options); new Chart(ctx).Line(newData, options);
}).fail(function () { }).fail(function () {
$('#' + container).addClass('google-chart-error'); $('#' + container).addClass('general-chart-error');
}); });
console.log('URL for line chart : ' + URL); console.log('URL for line chart : ' + URL);
} }
@@ -182,7 +175,7 @@ function areaChart(URL, container, options) {
new Chart(ctx).Line(newData, options); new Chart(ctx).Line(newData, options);
}).fail(function () { }).fail(function () {
$('#' + container).addClass('google-chart-error'); $('#' + container).addClass('general-chart-error');
}); });
console.log('URL for area chart: ' + URL); console.log('URL for area chart: ' + URL);
@@ -218,7 +211,7 @@ function columnChart(URL, container, options) {
new Chart(ctx).Bar(newData, options); new Chart(ctx).Bar(newData, options);
}).fail(function () { }).fail(function () {
$('#' + container).addClass('google-chart-error'); $('#' + container).addClass('general-chart-error');
}); });
console.log('URL for column chart : ' + URL); console.log('URL for column chart : ' + URL);
} }
@@ -253,7 +246,7 @@ function stackedColumnChart(URL, container, options) {
new Chart(ctx).StackedBar(newData, options); new Chart(ctx).StackedBar(newData, options);
}).fail(function () { }).fail(function () {
$('#' + container).addClass('google-chart-error'); $('#' + container).addClass('general-chart-error');
}); });
console.log('URL for stacked column chart : ' + URL); console.log('URL for stacked column chart : ' + URL);
} }
@@ -286,7 +279,7 @@ function pieChart(URL, container, options) {
new Chart(ctx).Pie(data, options); new Chart(ctx).Pie(data, options);
}).fail(function () { }).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 () { $(function () {
"use strict"; "use strict";
if (typeof google !== 'undefined') {
// do google charts:
google.setOnLoadCallback(drawChart);
} else {
// do chart JS stuff. // do chart JS stuff.
drawChart(); drawChart();
}
if (showTour) { if (showTour) {
$.getJSON('json/tour').success(function (data) { $.getJSON('json/tour').success(function (data) {
var tour = new Tour( var tour = new Tour(
@@ -41,6 +36,7 @@ function drawChart() {
pieChart('chart/bill/frontpage', 'bills-chart'); pieChart('chart/bill/frontpage', 'bills-chart');
stackedColumnChart('chart/budget/frontpage', 'budgets-chart'); stackedColumnChart('chart/budget/frontpage', 'budgets-chart');
columnChart('chart/category/frontpage', 'categories-chart'); columnChart('chart/category/frontpage', 'categories-chart');
columnChart('chart/account/expense', 'expense-accounts-chart');
getBoxAmounts(); getBoxAmounts();

View File

@@ -2,11 +2,7 @@
$(function () { $(function () {
"use strict"; "use strict";
if (typeof(google) !== 'undefined') {
google.setOnLoadCallback(drawChart);
} else {
drawChart(); drawChart();
}
}); });
@@ -18,7 +14,8 @@ function drawChart() {
} }
if (typeof stackedColumnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') { if (typeof stackedColumnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
stackedColumnChart('chart/budget/year/' + year + shared, 'budgets'); 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') { if (typeof lineChart !== 'undefined' && typeof month !== 'undefined') {
lineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart'); lineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart');

View File

@@ -15,9 +15,69 @@ return [
'cancel' => 'Cancel', 'cancel' => 'Cancel',
'from' => 'From', 'from' => 'From',
'to' => 'To', 'to' => 'To',
'total_sum' => 'Total sum',
'period_sum' => 'Sum for period',
'showEverything' => 'Show everything', 'showEverything' => 'Show everything',
'never' => 'Never', 'never' => 'Never',
'search_results_for' => 'Search results for ":query"', '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: // tour:
'prev' => 'Prev', 'prev' => 'Prev',
@@ -40,42 +100,41 @@ return [
'csv_define_column_roles' => 'Define column roles', 'csv_define_column_roles' => 'Define column roles',
'csv_map_values' => 'Map found values to existing values', 'csv_map_values' => 'Map found values to existing values',
'csv_download_config' => 'Download CSV configuration file.', 'csv_download_config' => 'Download CSV configuration file.',
'csv_index_text' => 'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV' .
'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by' . ' importer made by the folks at <a href="https://www.atlassian.com/">Atlassian</a>. Simply upload your CSV' .
' the folks at <a href="https://www.atlassian.com/">Atlassian</a>. Simply upload your CSV file and follow the instructions.' . ' file and follow the instructions. If you would like to learn more, please click on the <i ' .
' 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.', '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_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_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.' . 'csv_date_help' => 'Date time format in your CSV. Follow the format like <a href="https://secure.php.net/manual/en/' .
'php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">this' . 'datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">this page</a> ' .
' page</a> indicates. The default value will parse dates that look like this: ' . date('Ymd'), '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_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_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_upload_button' => 'Start importing CSV',
'csv_column_roles_title' => 'Define column roles', 'csv_column_roles_title' => 'Define column roles',
'csv_column_roles_text' => 'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. ' .
'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example ' 'Please check out the example data if you\'re not sure yourself. Click on the question mark ' .
. 'data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what' '(top right of the page) to learn what each column means. If you want to map imported data ' .
. ' each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. ' 'onto existing data in Firefly, use the checkbox. The next step will show you what this button does.',
. 'The next step will show you what this button does.',
'csv_column_roles_table' => 'Column roles', 'csv_column_roles_table' => 'Column roles',
'csv_column' => 'CSV column', 'csv_column' => 'CSV column',
'cvs_column_name' => 'CSV column name', 'csv_column_name' => 'CSV column name',
'cvs_column_example' => 'Column example data', 'csv_column_example' => 'Column example data',
'cvs_column_role' => 'Column contains?', 'csv_column_role' => 'Column contains?',
'csv_do_map_value' => 'Map value?', 'csv_do_map_value' => 'Map value?',
'csv_continue' => 'Continue to the next step', 'csv_continue' => 'Continue to the next step',
'csv_go_back' => 'Go back to the previous step', 'csv_go_back' => 'Go back to the previous step',
'csv_map_title' => 'Map found values to existing values', 'csv_map_title' => 'Map found values to existing values',
'csv_map_text' => 'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your ' .
'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other' 'database. This ensures that accounts and other things won\'t be created twice.',
. ' things won\'t be created twice.', 'csv_field_value' => 'Field value from CSV',
'cvs_field_value' => 'Field value from CSV',
'csv_field_mapped_to' => 'Must be mapped to...', '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_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_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.' 'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all ' .
. ' But, if the import succeeds, it will be easier to upload similar CSV files.', 'over again. But, if the import succeeds, it will be easier to upload similar CSV files.',
'csv_do_download_config' => 'Download configuration file.', 'csv_do_download_config' => 'Download configuration file.',
'csv_empty_description' => '(empty description)', 'csv_empty_description' => '(empty description)',
'csv_upload_form' => 'CSV upload form', '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_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_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_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_title' => 'CSV import finished!',
'csv_process_text' => 'The CVS importer has finished and has processed :rows rows', 'csv_process_text' => 'The CSV importer has finished and has processed :rows rows',
'csv_row' => 'Row', 'csv_row' => 'Row',
'csv_import_with_errors' => 'There was one error.|There were :errors errors.', 'csv_import_with_errors' => 'There was one error.|There were :errors errors.',
'csv_error_see_logs' => 'Check the log files to see details.', 'csv_error_see_logs' => 'Check the log files to see details.',
'csv_process_new_entries' => 'Firefly has created :imported new transaction(s).', 'csv_process_new_entries' => 'Firefly has created :imported new transaction(s).',
'csv_start_over' => 'Import again', 'csv_start_over' => 'Import again',
'csv_to_index' => 'Back home', '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_upload_not_writeable' => 'Cannot write to the path mentioned here. Cannot upload',
'csv_column__ignore' => '(ignore this column)', 'csv_column__ignore' => '(ignore this column)',
'csv_column_account-iban' => 'Asset account (IBAN)', 'csv_column_account-iban' => 'Asset account (IBAN)',
'csv_column_account-id' => 'Asset account ID (matching Firefly)', 'csv_column_account-id' => 'Asset account ID (matching Firefly)',
@@ -123,8 +180,9 @@ return [
'csv_column_tags-space' => 'Tags (space separated)', 'csv_column_tags-space' => 'Tags (space separated)',
'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.',
'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', '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' . '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.', . ' 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: // create new stuff:
@@ -137,9 +195,12 @@ return [
'create_new_piggy_bank' => 'Create new piggy bank', 'create_new_piggy_bank' => 'Create new piggy bank',
'create_new_bill' => 'Create new bill', 'create_new_bill' => 'Create new bill',
// currencies: // currencies:
'create_currency' => 'Create a new currency', 'create_currency' => 'Create a new currency',
'edit_currency' => 'Edit currency ":name"', 'edit_currency' => 'Edit currency ":name"',
'store_currency' => 'Store new currency',
'update_currency' => 'Update currency',
// new user: // new user:
'submit' => 'Submit', 'submit' => 'Submit',
@@ -167,10 +228,13 @@ return [
'delete_budget' => 'Delete budget ":name"', 'delete_budget' => 'Delete budget ":name"',
'edit_budget' => 'Edit budget ":name"', 'edit_budget' => 'Edit budget ":name"',
'update_amount' => 'Update amount', 'update_amount' => 'Update amount',
'update_budget' => 'Update budget',
// bills: // bills:
'delete_bill' => 'Delete bill ":name"', 'delete_bill' => 'Delete bill ":name"',
'edit_bill' => 'Edit bill ":name"', 'edit_bill' => 'Edit bill ":name"',
'update_bill' => 'Update bill',
'store_new_bill' => 'Store new bill',
// accounts: // accounts:
'details_for_asset' => 'Details for asset account ":name"', 'details_for_asset' => 'Details for asset account ":name"',
@@ -222,6 +286,7 @@ return [
'no_category' => '(no category)', 'no_category' => '(no category)',
'category' => 'Category', 'category' => 'Category',
'delete_category' => 'Delete category ":name"', 'delete_category' => 'Delete category ":name"',
'store_category' => 'Store new category',
// transactions: // transactions:
'update_withdrawal' => 'Update withdrawal', 'update_withdrawal' => 'Update withdrawal',
@@ -299,6 +364,7 @@ return [
'quarterly' => 'Quarterly', 'quarterly' => 'Quarterly',
'half-year' => 'Every six months', 'half-year' => 'Every six months',
'yearly' => 'Yearly', 'yearly' => 'Yearly',
'profile' => 'Profile',
// reports: // reports:
'reportForYear' => 'Yearly report for :year', 'reportForYear' => 'Yearly report for :year',
@@ -333,12 +399,15 @@ return [
'hideTheRest' => 'Show only the top :number', 'hideTheRest' => 'Show only the top :number',
'sum_of_year' => 'Sum of year', 'sum_of_year' => 'Sum of year',
'average_of_year' => 'Average of year', 'average_of_year' => 'Average of year',
'categories_earned_in_year' => 'Categories (by earnings)',
'categories_spent_in_year' => 'Categories (by spendings)',
// charts: // charts:
'dayOfMonth' => 'Day of the month', 'dayOfMonth' => 'Day of the month',
'month' => 'Month', 'month' => 'Month',
'budget' => 'Budget', 'budget' => 'Budget',
'spent' => 'Spent', 'spent' => 'Spent',
'earned' => 'Earned',
'overspent' => 'Overspent', 'overspent' => 'Overspent',
'left' => 'Left', 'left' => 'Left',
'noBudget' => '(no budget)', 'noBudget' => '(no budget)',
@@ -361,7 +430,7 @@ return [
// piggy banks: // piggy banks:
'piggy_bank' => 'Piggy bank', 'piggy_bank' => 'Piggy bank',
'new_piggy_bank' => 'Create new 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', 'account_status' => 'Account status',
'left_for_piggy_banks' => 'Left for piggy banks', 'left_for_piggy_banks' => 'Left for piggy banks',
'sum_of_piggy_banks' => 'Sum of piggy banks', 'sum_of_piggy_banks' => 'Sum of piggy banks',
@@ -397,6 +466,17 @@ return [
'tag_title_nothing' => 'Default tags', 'tag_title_nothing' => 'Default tags',
'tag_title_balancingAct' => 'Balancing act tags', 'tag_title_balancingAct' => 'Balancing act tags',
'tag_title_advancePayment' => 'Advance payment 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', 'csv_config' => 'CSV import configuration',
'specifix' => 'Bank- or file specific fixes', 'specifix' => 'Bank- or file specific fixes',
'csv_import_account' => 'Default import account', 'csv_import_account' => 'Default import account',
'attachments[]' => 'Attachments',
'store_new_withdrawal' => 'Store new withdrawal', 'store_new_withdrawal' => 'Store new withdrawal',
'store_new_deposit' => 'Store new deposit', 'store_new_deposit' => 'Store new deposit',
'store_new_transfer' => 'Store new transfer', 'store_new_transfer' => 'Store new transfer',
@@ -61,6 +61,12 @@ return [
'add_new_transfer' => 'Add a new transfer', 'add_new_transfer' => 'Add a new transfer',
'noPiggybank' => '(no piggy bank)', 'noPiggybank' => '(no piggy bank)',
'noBudget' => '(no budget)', 'noBudget' => '(no budget)',
'title' => 'Title',
'notes' => 'Notes',
'filename' => 'File name',
'mime' => 'Mime type',
'size' => 'Size',
'delete_account' => 'Delete account ":name"', 'delete_account' => 'Delete account ":name"',
'delete_bill' => 'Delete bill ":name"', 'delete_bill' => 'Delete bill ":name"',
@@ -69,7 +75,9 @@ return [
'delete_currency' => 'Delete currency ":name"', 'delete_currency' => 'Delete currency ":name"',
'delete_piggyBank' => 'Delete piggy bank ":name"', 'delete_piggyBank' => 'Delete piggy bank ":name"',
'delete_journal' => 'Delete transaction with description ":description"', '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"?', '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"?', '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"?', '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"?', '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"?', '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"?', '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.', '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.' . '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.', '|All :count transactions connected to this budget will spared deletion.',
'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.' . 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.' .
'|All :count transactions connected to this category will spared deletion.', '|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.", "accepted" => "The :attribute must be accepted.",
"active_url" => "The :attribute is not a valid URL.", "active_url" => "The :attribute is not a valid URL.",
"after" => "The :attribute must be a date after :date.", "after" => "The :attribute must be a date after :date.",

View File

@@ -15,9 +15,66 @@ return [
'cancel' => 'Annuleren', 'cancel' => 'Annuleren',
'from' => 'Van', 'from' => 'Van',
'to' => 'Tot', 'to' => 'Tot',
'total_sum' => 'Totale som',
'period_sum' => 'Som van periode',
'showEverything' => 'Laat alles zien', 'showEverything' => 'Laat alles zien',
'never' => 'Nooit', 'never' => 'Nooit',
'search_results_for' => 'Zoekresultaten voor ":query"', '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: // tour:
'prev' => 'Vorige', 'prev' => 'Vorige',
@@ -37,32 +94,65 @@ return [
'csv_import' => 'Importeer CSV-bestand', 'csv_import' => 'Importeer CSV-bestand',
'csv' => 'CSV', 'csv' => 'CSV',
'csv_index_title' => 'Upload en importeer een kommagescheiden tekstbestand', '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 ' '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' . . 'op de prachtige tool van <a href="https://www.atlassian.com/">Atlassian</a>. Om te beginnen selecteer'
' je jouw tekstbestand bij "CSV-bestand". ' . ' je jouw tekstbestand bij "CSV-bestand". '
. 'Als je hulp nodig hebt, klik dan op het <i class="fa fa-question-circle"></i>-icoontje rechtsboven.', . '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_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,' . 'csv_header_help' => 'Zet hier een vinkje als de eerste rij van je tekstbestand bestaat uit kolomnamen,'
'en niet uit daadwerkelijke gegevens.', . 'en niet uit daadwerkelijke gegevens.',
'csv_date_help' => 'Het gebruikte datumformaat in jouw bestand. Gebruik het formaat zoals <a href="https://secure.' . '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' . . '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'), . ' 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_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_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_upload_button' => 'Begin de import',
'csv_define_column_roles' => 'Bepaal kolominhoud',
'csv_column_roles_title' => 'Bepaal de inhoud van elke kolom', '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' . '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 ' . . ' 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' . . 'rechtsboven om te ontdekken wat elke kolomsoort precies is. Als de kolominhoud een directe'
' relatie heeft met gegevens' . ' relatie heeft met gegevens'
. . ' die al in Firefly staan, gebruik dan het vinkje. Tijdens de volgende stap komt Firefly hier dan op terug.',
' 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', 'csv_column' => 'CSV-kolom',
'cvs_column_name' => 'CSV-kolomnaam', 'csv_column_name' => 'CSV-kolomnaam',
'cvs_column_example' => 'Voorbeeldgegevens', 'csv_column_example' => 'Voorbeeldgegevens',
'cvs_column_role' => 'Kolom bevat?', 'csv_column_role' => 'Kolom bevat?',
'csv_do_map_value' => 'Directe relatie?', '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__ignore' => '(negeer deze kolom)',
'csv_column_account-iban' => 'Betaalrekening (IBAN)', 'csv_column_account-iban' => 'Betaalrekening (IBAN)',
'csv_column_account-id' => 'Betaalrekening (ID gelijk aan Firefly)', 'csv_column_account-id' => 'Betaalrekening (ID gelijk aan Firefly)',
@@ -90,36 +180,12 @@ return [
'csv_column_sepa-db' => 'SEPA "direct debet"-nummer', 'csv_column_sepa-db' => 'SEPA "direct debet"-nummer',
'csv_column_tags-comma' => 'Tags (kommagescheiden)', 'csv_column_tags-comma' => 'Tags (kommagescheiden)',
'csv_column_tags-space' => 'Tags (spatiegescheiden)', '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_RabobankDescription' => 'Vink dit aan als je Rabobank bestanden importeert.',
'csv_specifix_Dummy' => 'Dit vinkje doet niks (dummy).', '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 stuff:
'create_new_withdrawal' => 'Nieuwe uitgave', 'create_new_withdrawal' => 'Nieuwe uitgave',
'create_new_deposit' => 'Nieuwe inkomsten', 'create_new_deposit' => 'Nieuwe inkomsten',
@@ -133,6 +199,8 @@ return [
// currencies: // currencies:
'create_currency' => 'Voeg nieuwe valuta toe', 'create_currency' => 'Voeg nieuwe valuta toe',
'edit_currency' => 'Wijzig valuta ":name"', 'edit_currency' => 'Wijzig valuta ":name"',
'store_currency' => 'Sla nieuwe valuta op',
'update_currency' => 'Wijzig valuta',
// new user: // new user:
'submit' => 'Invoeren', 'submit' => 'Invoeren',
@@ -160,10 +228,13 @@ return [
'delete_budget' => 'Verwijder budget ":name"', 'delete_budget' => 'Verwijder budget ":name"',
'edit_budget' => 'Wijzig budget ":name"', 'edit_budget' => 'Wijzig budget ":name"',
'update_amount' => 'Bedrag bijwerken', 'update_amount' => 'Bedrag bijwerken',
'update_budget' => 'Budget bijwerken',
// bills: // bills:
'delete_bill' => 'Verwijder contract ":name"', 'delete_bill' => 'Verwijder contract ":name"',
'update_bill' => 'Wijzig contract',
'edit_bill' => 'Wijzig contract ":name"', 'edit_bill' => 'Wijzig contract ":name"',
'store_new_bill' => 'Sla nieuw contract op',
// accounts: // accounts:
'details_for_asset' => 'Overzicht voor betaalrekening ":name"', 'details_for_asset' => 'Overzicht voor betaalrekening ":name"',
@@ -224,6 +295,7 @@ return [
'no_category' => '(geen categorie)', 'no_category' => '(geen categorie)',
'category' => 'Categorie', 'category' => 'Categorie',
'delete_category' => 'Verwijder categorie ":name"', 'delete_category' => 'Verwijder categorie ":name"',
'store_category' => 'Sla nieuwe categorie op',
// transactions: // transactions:
'update_withdrawal' => 'Wijzig uitgave', 'update_withdrawal' => 'Wijzig uitgave',
@@ -287,6 +359,7 @@ return [
'Withdrawal' => 'Uitgave', 'Withdrawal' => 'Uitgave',
'Deposit' => 'Inkomsten', 'Deposit' => 'Inkomsten',
'Transfer' => 'Overschrijving', 'Transfer' => 'Overschrijving',
'profile' => 'Profiel',
'bill' => 'Contract', 'bill' => 'Contract',
'yes' => 'Ja', 'yes' => 'Ja',
'no' => 'Nee', 'no' => 'Nee',
@@ -307,8 +380,8 @@ return [
// reports: // reports:
'reportForYear' => 'Jaaroverzicht :year', 'reportForYear' => 'Jaaroverzicht :year',
'reportForYearShared' => 'Jaaroverzicht :year (inclusief gedeelde rekeningen)', 'reportForYearShared' => 'Jaaroverzicht :year (inclusief gedeelde rekeningen)',
'reportForMonth' => 'Maandoverzicht van :date', 'reportForMonth' => 'Maandoverzicht voor :month',
'reportForMonthShared' => 'Maandoverzicht van :date (inclusief gedeelde rekeningen)', 'reportForMonthShared' => 'Maandoverzicht voor :month (inclusief gedeelde rekeningen)',
'incomeVsExpenses' => 'Inkomsten tegenover uitgaven', 'incomeVsExpenses' => 'Inkomsten tegenover uitgaven',
'accountBalances' => 'Rekeningsaldi', 'accountBalances' => 'Rekeningsaldi',
'balanceStartOfYear' => 'Saldo aan het begin van het jaar', 'balanceStartOfYear' => 'Saldo aan het begin van het jaar',
@@ -335,12 +408,15 @@ return [
'topX' => 'top :number', 'topX' => 'top :number',
'showTheRest' => 'Laat alles zien', 'showTheRest' => 'Laat alles zien',
'hideTheRest' => 'Laat alleen de top :number zien', 'hideTheRest' => 'Laat alleen de top :number zien',
'categories_earned_in_year' => 'Categorieën (inkomsten)',
'categories_spent_in_year' => 'Categorieën (uitgaven)',
// charts: // charts:
'dayOfMonth' => 'Dag vd maand', 'dayOfMonth' => 'Dag vd maand',
'month' => 'Maand', 'month' => 'Maand',
'budget' => 'Budget', 'budget' => 'Budget',
'spent' => 'Uitgegeven', 'spent' => 'Uitgegeven',
'earned' => 'Verdiend',
'overspent' => 'Teveel uitgegeven', 'overspent' => 'Teveel uitgegeven',
'left' => 'Over', 'left' => 'Over',
'noBudget' => '(geen budget)', 'noBudget' => '(geen budget)',
@@ -363,7 +439,7 @@ return [
// piggy banks: // piggy banks:
'piggy_bank' => 'Spaarpotje', 'piggy_bank' => 'Spaarpotje',
'new_piggy_bank' => 'Nieuw spaarpotje', 'new_piggy_bank' => 'Nieuw spaarpotje',
'create_new_piggybank' => 'Nieuw spaarpotje', 'store_piggy_bank' => 'Sla spaarpotje op',
'account_status' => 'Rekeningoverzicht', 'account_status' => 'Rekeningoverzicht',
'left_for_piggy_banks' => 'Over voor spaarpotjes', 'left_for_piggy_banks' => 'Over voor spaarpotjes',
'sum_of_piggy_banks' => 'Som van spaarpotjes', 'sum_of_piggy_banks' => 'Som van spaarpotjes',
@@ -395,9 +471,17 @@ return [
'new_tag' => 'Maak nieuwe tag', 'new_tag' => 'Maak nieuwe tag',
'edit_tag' => 'Wijzig tag ":tag"', 'edit_tag' => 'Wijzig tag ":tag"',
'no_year' => 'Zonder jaar', 'no_year' => 'Zonder jaar',
'no_maand' => 'Zonder jaar', 'no_month' => 'Zonder maand',
'tag_title_nothing' => 'Standaard tags', 'tag_title_nothing' => 'Standaard tags',
'tag_title_balancingAct' => 'Balancing act tags', 'tag_title_balancingAct' => 'Balancerende tags',
'tag_title_advancePayment' => 'Advance payment 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', 'date_format' => 'Datumformaat',
'csv_config' => 'Configuratiebestand', 'csv_config' => 'Configuratiebestand',
'specifix' => 'Bank- or of bestandsspecifieke opties', '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_withdrawal' => 'Nieuwe uitgave opslaan',
'store_new_deposit' => 'Nieuwe inkomsten opslaan', 'store_new_deposit' => 'Nieuwe inkomsten opslaan',
@@ -68,7 +76,10 @@ return [
'delete_currency' => 'Verwijder valuta ":name"', 'delete_currency' => 'Verwijder valuta ":name"',
'delete_piggyBank' => 'Verwijder spaarpotje ":name"', 'delete_piggyBank' => 'Verwijder spaarpotje ":name"',
'delete_journal' => 'Verwijder transactie met omschrijving ":description"', '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?', '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?', '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?', '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.', '|De :count transacties verbonden aan dit budget blijven bewaard.',
'category_keep_transactions' => 'De transactie verbonden aan deze categorie blijft bewaard.' . 'category_keep_transactions' => 'De transactie verbonden aan deze categorie blijft bewaard.' .
'|De :count transacties verbonden aan deze categorie blijven 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 <?php
return [ 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', 'register' => 'register',
'index' => 'index', 'index' => 'index',
'home' => 'home', '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.", "accepted" => "The :attribute must be accepted.",
"active_url" => "The :attribute is not a valid URL.", "active_url" => "The :attribute is not a valid URL.",
"after" => "The :attribute must be a date after :date.", "after" => "The :attribute must be a date after :date.",

View File

@@ -32,7 +32,7 @@
{{ ExpandedForm.text('iban') }} {{ ExpandedForm.text('iban') }}
{{ ExpandedForm.balance('openingBalance') }} {{ ExpandedForm.balance('openingBalance') }}
{{ ExpandedForm.date('openingBalanceDate', phpdate('Y-m-d')) }} {{ 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') }} {{ ExpandedForm.balance('virtualBalance') }}
</div> </div>

View File

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