Allow editing of liabilities.

This commit is contained in:
James Cole 2018-08-05 18:59:15 +02:00
parent 0a89f4000d
commit 33294dd9f0
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
17 changed files with 228 additions and 107 deletions

View File

@ -27,6 +27,7 @@ namespace FireflyIII\Http\Controllers\Account;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Http\Request;
@ -85,6 +86,26 @@ class EditController extends Controller
$roles[$role] = (string)trans('firefly.account_role_' . $role);
}
// types of liability:
$debt = $this->repository->getAccountTypeByType(AccountType::DEBT);
$loan = $this->repository->getAccountTypeByType(AccountType::LOAN);
$mortgage = $this->repository->getAccountTypeByType(AccountType::MORTGAGE);
$creditCard = $this->repository->getAccountTypeByType(AccountType::CREDITCARD);
$liabilityTypes = [
$debt->id => (string)trans('firefly.account_type_' . AccountType::DEBT),
$loan->id => (string)trans('firefly.account_type_' . AccountType::LOAN),
$mortgage->id => (string)trans('firefly.account_type_' . AccountType::MORTGAGE),
$creditCard->id => (string)trans('firefly.account_type_' . AccountType::CREDITCARD),
];
asort($liabilityTypes);
// interest calculation periods:
$interestPeriods = [
'daily' => (string)trans('firefly.interest_calc_daily'),
'monthly' => (string)trans('firefly.interest_calc_monthly'),
'yearly' => (string)trans('firefly.interest_calc_yearly'),
];
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('accounts.edit.fromUpdate')) {
$this->rememberPreviousUri('accounts.edit.uri');
@ -108,16 +129,24 @@ class EditController extends Controller
'ccMonthlyPaymentDate' => $repository->getMetaValue($account, 'ccMonthlyPaymentDate'),
'BIC' => $repository->getMetaValue($account, 'BIC'),
'openingBalanceDate' => $openingBalanceDate,
'liability_type_id' => $account->account_type_id,
'openingBalance' => $openingBalanceAmount,
'virtualBalance' => $account->virtual_balance,
'currency_id' => $currency->id,
'interest' => $repository->getMetaValue($account, 'interest'),
'interest_period' => $repository->getMetaValue($account, 'interest_period'),
'notes' => $this->repository->getNoteText($account),
'active' => $hasOldInput ? (bool)$request->old('active') : $account->active,
];
if ('liabilities' === $what) {
$preFilled['openingBalance'] = bcmul($preFilled['openingBalance'], '-1');
}
$request->session()->flash('preFilled', $preFilled);
return view('accounts.edit', compact('account', 'currency', 'subTitle', 'subTitleIcon', 'what', 'roles', 'preFilled'));
return view(
'accounts.edit', compact('account', 'currency', 'subTitle', 'subTitleIcon', 'what', 'roles', 'preFilled', 'liabilityTypes', 'interestPeriods')
);
}

View File

@ -101,10 +101,13 @@ class IndexController extends Controller
$accounts->each(
function (Account $account) use ($activities, $startBalances, $endBalances) {
$account->lastActivityDate = $this->isInArray($activities, $account->id);
$account->startBalance = $this->isInArray($startBalances, $account->id);
$account->endBalance = $this->isInArray($endBalances, $account->id);
$account->difference = bcsub($account->endBalance, $account->startBalance);
$account->lastActivityDate = $this->isInArray($activities, $account->id);
$account->startBalance = $this->isInArray($startBalances, $account->id);
$account->endBalance = $this->isInArray($endBalances, $account->id);
$account->difference = bcsub($account->endBalance, $account->startBalance);
$account->interest = round($this->repository->getMetaValue($account, 'interest'), 6);
$account->interestPeriod = (string)trans('firefly.interest_calc_' . $this->repository->getMetaValue($account, 'interest_period'));
$account->accountTypeString = (string)trans('firefly.account_type_' . $account->accountType->type);
}
);

View File

@ -276,10 +276,10 @@ class BillController extends Controller
public function show(Request $request, Bill $bill)
{
// add info about rules:
$rules = $this->billRepository->getRulesForBill($bill);
$subTitle = $bill->name;
$rules = $this->billRepository->getRulesForBill($bill);
$subTitle = $bill->name;
/** @var Carbon $start */
$start = session('start');
$start = session('start');
/** @var Carbon $end */
$end = session('end');
$year = $start->year;
@ -342,30 +342,7 @@ class BillController extends Controller
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
}
// do return to original bill form?
$return = 'false';
if (1 === (int)$request->get('create_another')) {
$return = 'true';
}
$group = null;
// find first rule group, or create one:
$count = $this->ruleGroupRepos->count();
if (0 === $count) {
$data = [
'title' => (string)trans('firefly.rulegroup_for_bills_title'),
'description' => (string)trans('firefly.rulegroup_for_bills_description'),
];
$group = $this->ruleGroupRepos->store($data);
}
if ($count > 0) {
$group = $this->ruleGroupRepos->getActiveGroups($bill->user)->first();
}
// redirect to page that will create a new rule.
$params = http_build_query(['fromBill' => $bill->id, 'return' => $return]);
return redirect(route('rules.create', [$group->id]) . '?' . $params);
return redirect(route('rules.create-from-bill', [$bill->id]));
}
/**

View File

@ -70,8 +70,6 @@ class CreateController extends Controller
/**
* Create a new rule. It will be stored under the given $ruleGroup.
*
* TODO reinstate bill specific code.
*
* @param Request $request
* @param RuleGroup $ruleGroup
*
@ -119,6 +117,55 @@ class CreateController extends Controller
);
}
/**
* Create a new rule. It will be stored under the given $ruleGroup.
*
* @param Request $request
* @param Bill $bill
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function createFromBill(Request $request, Bill $bill)
{
$request->session()->flash('info', (string)trans('firefly.instructions_rule_from_bill', ['name' => $bill->name]));
$this->createDefaultRuleGroup();
$this->createDefaultRule();
$preFilled = [
'strict' => true,
'title' => (string)trans('firefly.new_rule_for_bill_title', ['name' => $bill->name]),
'description' => (string)trans('firefly.new_rule_for_bill_description', ['name' => $bill->name]),
];
// make triggers and actions from the bill itself.
// get triggers and actions for bill:
$oldTriggers = $this->getTriggersForBill($bill);
$oldActions = $this->getActionsForBill($bill);
$triggerCount = \count($oldTriggers);
$actionCount = \count($oldActions);
$subTitleIcon = 'fa-clone';
// title depends on whether or not there is a rule group:
$subTitle = (string)trans('firefly.make_new_rule_no_group');
// flash old data
$request->session()->flash('preFilled', $preFilled);
// put previous url in session if not redirect from store (not "create another").
if (true !== session('rules.create.fromStore')) {
$this->rememberPreviousUri('rules.create.uri');
}
session()->forget('rules.create.fromStore');
return view(
'rules.rule.create', compact('subTitleIcon', 'oldTriggers', 'preFilled', 'oldActions', 'triggerCount', 'actionCount', 'subTitle')
);
}
/**
* Store the new rule.
*

View File

@ -53,8 +53,11 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @property Carbon lastActivityDate
* @property Collection accountMeta
* @property bool encrypted
* @property int account_type_id
* @property Collection piggyBanks
* @property int account_type_id
* @property Collection piggyBanks
* @property string $interest
* @property string $interestPeriod
* @property string accountTypeString
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/

View File

@ -299,10 +299,10 @@ class AccountRepository implements AccountRepositoryInterface
public function getNoteText(Account $account): ?string
{
$note = $account->notes()->first();
if (null === $note) {
return null;
}
return $note->text;
}

View File

@ -41,7 +41,6 @@ use Illuminate\Support\HtmlString;
use Illuminate\Support\MessageBag;
use Log;
use RuntimeException;
use Session;
use Throwable;
/**
@ -246,7 +245,7 @@ class ExpandedForm
$value = $value ?? 1;
$options['checked'] = true === $checked;
if (Session::has('preFilled')) {
if (app('session')->has('preFilled')) {
$preFilled = session('preFilled');
$options['checked'] = $preFilled[$name] ?? $options['checked'];
}
@ -528,34 +527,6 @@ class ExpandedForm
return $html;
}
/**
* Function to render a percentage.
*
* @param string $name
* @param mixed $value
* @param array $options
*
* @return string
*
*/
public function percentage(string $name, $value = null, array $options = null): string
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['step'] = 'any';
unset($options['placeholder']);
try {
$html = view('form.percentage', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render percentage(): %s', $e->getMessage()));
$html = 'Could not render percentage.';
}
return $html;
}
/**
* @param string $type
* @param string $name
@ -598,6 +569,34 @@ class ExpandedForm
return $html;
}
/**
* Function to render a percentage.
*
* @param string $name
* @param mixed $value
* @param array $options
*
* @return string
*
*/
public function percentage(string $name, $value = null, array $options = null): string
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['step'] = 'any';
unset($options['placeholder']);
try {
$html = view('form.percentage', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render percentage(): %s', $e->getMessage()));
$html = 'Could not render percentage.';
}
return $html;
}
/**
* @param string $name
* @param mixed $value
@ -784,12 +783,16 @@ class ExpandedForm
*/
public function textarea(string $name, $value = null, array $options = null): string
{
$value = $value ?? '';
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['rows'] = 4;
if (null === $value) {
$value = '';
}
try {
$html = view('form.textarea', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (Throwable $e) {
@ -880,12 +883,13 @@ class ExpandedForm
* @return mixed
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function fillFieldValue(string $name, $value)
protected function fillFieldValue(string $name, $value = null)
{
if (Session::has('preFilled')) {
if (app('session')->has('preFilled')) {
$preFilled = session('preFilled');
$value = isset($preFilled[$name]) && null === $value ? $preFilled[$name] : $value;
}
try {
if (null !== request()->old($name)) {
$value = request()->old($name);
@ -894,6 +898,7 @@ class ExpandedForm
// don't care about session errors.
Log::debug(sprintf('Run time: %s', $e->getMessage()));
}
if ($value instanceof Carbon) {
$value = $value->format('Y-m-d');
}

View File

@ -47,36 +47,36 @@ trait RuleManagement
if (0 === $ruleRepository->count()) {
$data = [
'rule_group_id' => $ruleRepository->getFirstRuleGroup()->id,
'stop-processing' => 0,
'stop_processing' => 0,
'title' => (string)trans('firefly.default_rule_name'),
'description' => (string)trans('firefly.default_rule_description'),
'trigger' => 'store-journal',
'strict' => true,
'rule-triggers' => [
'rule_triggers' => [
[
'name' => 'description_is',
'value' => (string)trans('firefly.default_rule_trigger_description'),
'stop-processing' => false,
'stop_processing' => false,
],
[
'name' => 'from_account_is',
'value' => (string)trans('firefly.default_rule_trigger_from_account'),
'stop-processing' => false,
'stop_processing' => false,
],
],
'rule-actions' => [
'rule_actions' => [
[
'name' => 'prepend_description',
'value' => (string)trans('firefly.default_rule_action_prepend'),
'stop-processing' => false,
'stop_processing' => false,
],
[
'name' => 'set_category',
'value' => (string)trans('firefly.default_rule_action_set_category'),
'stop-processing' => false,
'stop_processing' => false,
],
],
];

View File

@ -271,6 +271,7 @@ return [
'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:',
'make_new_rule' => 'Make a new rule in rule group ":title"',
'make_new_rule_no_group' => 'Make a new rule',
'instructions_rule_from_bill' => 'In order to match transactions to your new bill ":name", Firefly III can create a rule that will automatically be checked against any transactions you store. Please verify the details below and store the rule to have Firefly III automatically match transactions to your new bill.',
'rule_is_strict' => 'strict rule',
'rule_is_not_strict' => 'non-strict rule',
'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.',
@ -694,6 +695,7 @@ return [
'expense_deleted' => 'Successfully deleted expense account ":name"',
'revenue_deleted' => 'Successfully deleted revenue account ":name"',
'update_asset_account' => 'Update asset account',
'update_liabilities_account' => 'Update liability',
'update_expense_account' => 'Update expense account',
'update_revenue_account' => 'Update revenue account',
'make_new_asset_account' => 'Create a new asset account',
@ -899,6 +901,7 @@ return [
'debt_start_amount' => 'Start amount of debt',
'debt_start_amount_help' => 'Please enter a positive amount. Feel free to enter the current amount and date.',
'store_new_liabilities_account' => 'Store new liability',
'edit_liabilities_account' => 'Edit liability ":name"',
// reports:
'report_default' => 'Default financial report between :start and :end',

View File

@ -130,4 +130,7 @@ return [
'transaction_s' => 'Transaction(s)',
'field' => 'Field',
'value' => 'Value',
'interest' => 'Interest',
'interest_period' => 'interest period',
'liability_type' => 'Type of liability',
];

View File

@ -17,7 +17,7 @@
</div>
<div class="box-body">
{{ ExpandedForm.text('name') }}
{% if what == 'asset' or what=='liabilities' %}
{% if what == 'asset' or what == 'liabilities' %}
{{ ExpandedForm.currencyList('currency_id', null, {helpText:'account_default_currency'|_}) }}
{% endif %}
{% if what == 'liabilities' %}
@ -26,7 +26,6 @@
{{ ExpandedForm.date('openingBalanceDate', null, {label:'debt_start_date'|_}) }}
{{ ExpandedForm.percentage('interest') }}
{{ ExpandedForm.select('interest_period', interestPeriods) }}
{% endif %}
</div>
</div>

View File

@ -17,9 +17,17 @@
</div>
<div class="box-body">
{{ ExpandedForm.text('name') }}
{% if account.accountType.type == 'Default account' or account.accountType.type == 'Asset account' %}
{% if account.accountType.type == 'Default account' or account.accountType.type == 'Asset account' or what == 'liabilities' %}
{{ ExpandedForm.currencyList('currency_id', null, {helpText:'account_default_currency'|_}) }}
{% endif %}
{% if what == 'liabilities' %}
{{ ExpandedForm.select('liability_type_id', liabilityTypes) }}
{{ ExpandedForm.amountNoCurrency('openingBalance', null, {label:'debt_start_amount'|_, helpText: 'debt_start_amount_help'|_}) }}
{{ ExpandedForm.date('openingBalanceDate', null, {label:'debt_start_date'|_}) }}
{{ ExpandedForm.percentage('interest') }}
{{ ExpandedForm.select('interest_period', interestPeriods) }}
{% endif %}
</div>
</div>
@ -47,7 +55,7 @@
{{ ExpandedForm.amountNoCurrency('virtualBalance',null) }}
{% endif %}
{{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }}
{{ ExpandedForm.textarea('notes',preFilled.notes,{helpText: trans('firefly.field_supports_markdown')}) }}
{# only correct way to do active checkbox #}
{{ ExpandedForm.checkbox('active', 1) }}

View File

@ -9,10 +9,17 @@
{% if what == 'asset' %}
<th data-defaultsign="az" class="hidden-sm hidden-xs hidden-md">{{ trans('list.role') }}</th>
{% endif %}
{% if what == 'liabilities' %}
<th data-defaultsign="az">{{ trans('list.liability_type') }}</th>
<th data-defaultsign="_19">{{ trans('list.interest') }} ({{ trans('list.interest_period') }})</th>
{% endif %}
<th data-defaultsign="az" class="hidden-sm hidden-xs">{{ trans('form.accountNumber') }}</th>
<th data-defaultsign="_19">{{ trans('list.currentBalance') }}</th>
<th class="hidden-sm hidden-xs">{{ trans('list.active') }}</th>
<th data-defaultsign="month" class="hidden-sm hidden-xs hidden-md">{{ trans('list.lastActivity') }}</th>
{# hide last activity to make room for other stuff #}
{% if what != 'liabilities' %}
<th data-defaultsign="month" class="hidden-sm hidden-xs hidden-md">{{ trans('list.lastActivity') }}</th>
{% endif %}
<th data-defaultsign="_19" style="width:15%;"
class="hidden-sm hidden-xs hidden-md">{{ trans('list.balanceDiff') }}</th>
</tr>
@ -40,6 +47,12 @@
{% endfor %}
</td>
{% endif %}
{% if what == 'liabilities' %}
<td>{{ account.accountTypeString }}</td>
<td data-value="{{ account.interest }}">
{{ account.interest }}% ({{ account.interestPeriod|lower }})
</td>
{% endif %}
<td class="hidden-sm hidden-xs">{{ account.iban }}{% if account.iban == '' %}{{ accountGetMetaField(account, 'accountNumber') }}{% endif %}</td>
<td data-value="{{ account.endBalance }}" style="text-align: right;">
<span style="margin-right:5px;">
@ -53,14 +66,17 @@
<i class="fa fa-fw fa-ban"></i>
{% endif %}
</td>
{% if account.lastActivityDate %}
<td class="hidden-sm hidden-xs hidden-md" data-value="{{ account.lastActivityDate.format('Y-m-d H-i-s') }} ">
{{ account.lastActivityDate.formatLocalized(monthAndDayFormat) }}
</td>
{% else %}
<td class="hidden-sm hidden-xs hidden-md" data-value="0000-00-00 00-00-00">
<em>{{ 'never'|_ }}</em>
</td>
{# hide last activity to make room for other stuff #}
{% if what != 'liabilities' %}
{% if account.lastActivityDate %}
<td class="hidden-sm hidden-xs hidden-md" data-value="{{ account.lastActivityDate.format('Y-m-d H-i-s') }} ">
{{ account.lastActivityDate.formatLocalized(monthAndDayFormat) }}
</td>
{% else %}
<td class="hidden-sm hidden-xs hidden-md" data-value="0000-00-00 00-00-00">
<em>{{ 'never'|_ }}</em>
</td>
{% endif %}
{% endif %}
<td class="hidden-sm hidden-xs hidden-md" data-value="{{ account.difference }}" style="text-align: right;">
<span style="margin-right:5px;">

View File

@ -57,6 +57,15 @@
</div>
</a>
</li>
<li>
<a href="{{ route('accounts.create', 'liabilities') }}">
<i class="menu-icon fa fa-ticket bg-maroon"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">{{ 'new_liabilities_account'|_ }}</h4>
</div>
</a>
</li>
<li>
<a href="{{ route('budgets.create') }}">

View File

@ -44,23 +44,25 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\User;
use Illuminate\Support\Collection;
/**
* Cuts away the middle of a string when it's very long.
*
* @param string $string
*
* @return string
*/
function limitStringLength(string $string): string
{
$maxChars = 75;
$length = \strlen($string);
$result = $string;
if ($length > $maxChars) {
$result = substr_replace($string, ' ... ', $maxChars / 2, $length - $maxChars);
}
if (!function_exists('limitStringLength')) {
/**
* Cuts away the middle of a string when it's very long.
*
* @param string $string
*
* @return string
*/
function limitStringLength(string $string): string
{
$maxChars = 75;
$length = \strlen($string);
$result = $string;
if ($length > $maxChars) {
$result = substr_replace($string, ' ... ', $maxChars / 2, $length - $maxChars);
}
return $result;
return $result;
}
}
try {
@ -849,6 +851,14 @@ try {
}
);
Breadcrumbs::register(
'rules.create-from-bill',
function (BreadcrumbsGenerator $breadcrumbs, RuleGroup $ruleGroup = null) {
$breadcrumbs->parent('rules.index');
$breadcrumbs->push(trans('firefly.make_new_rule_no_group'), route('rules.create'));
}
);
Breadcrumbs::register(
'rules.edit',
function (BreadcrumbsGenerator $breadcrumbs, Rule $rule) {
@ -1033,7 +1043,7 @@ try {
Breadcrumbs::register(
'transactions.show',
function (BreadcrumbsGenerator $breadcrumbs, TransactionJournal $journal) {
$what = strtolower($journal->transactionType->type);
$what = strtolower($journal->transactionType->type);
$title = limitStringLength($journal->description);
$breadcrumbs->parent('transactions.index', $what);

View File

@ -781,6 +781,7 @@ Route::group(
// create controller
Route::get('create/{ruleGroup?}', ['uses' => 'Rule\CreateController@create', 'as' => 'create']);
Route::get('create-from-bill/{bill}', ['uses' => 'Rule\CreateController@createFromBill', 'as' => 'create-from-bill']);
Route::post('store', ['uses' => 'Rule\CreateController@store', 'as' => 'store']);
// delete controller

View File

@ -25,7 +25,9 @@ namespace Tests\Feature\Controllers\Account;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
@ -62,6 +64,12 @@ class CreateControllerTest extends TestCase
$repository->shouldReceive('get')->andReturn(new Collection);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
// get all types:
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Debt'])->andReturn(AccountType::find(11))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Loan'])->andReturn(AccountType::find(9))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Mortgage'])->andReturn(AccountType::find(12))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Credit card'])->andReturn(AccountType::find(13))->once();
$this->be($this->user());
$response = $this->get(route('accounts.create', ['asset']));
$response->assertStatus(200);