diff --git a/app/controllers/JsonController.php b/app/controllers/JsonController.php index 3e62c3470c..39dd28369d 100644 --- a/app/controllers/JsonController.php +++ b/app/controllers/JsonController.php @@ -1,9 +1,6 @@ _accounts = $accounts; - $this->_categories = $categories; - $this->_budgets = $budgets; - $this->_journals = $journals; + $this->helper = $helper; + + + } /** + * Returns a list of categories. * + * @return \Illuminate\Http\JsonResponse */ - public function revenue() + public function categories() { - $parameters = $this->_datatableParameters(); - $parameters['transactionTypes'] = ['Deposit']; - $parameters['amount'] = 'positive'; - - $query = $this->_datatableQuery($parameters); - $resultSet = $this->_datatableResultset($parameters, $query); - - - /* - * Build return data: - */ - - if (Input::get('debug') == 'true') { - echo '
';
-            print_r($parameters);
-            echo '
'; - print_r($resultSet); - return ''; - - } else { - return Response::json($resultSet); + /** @var \Firefly\Storage\Category\EloquentCategoryRepository $categories */ + $categories = App::make('Firefly\Storage\Category\CategoryRepositoryInterface'); + $list = $categories->get(); + $return = []; + foreach ($list as $entry) { + $return[] = $entry->name; } + return Response::json($return); + } /** + * Returns a JSON list of all beneficiaries. * + * @return \Illuminate\Http\JsonResponse */ - public function transfers() + public function expenseAccounts() { - $parameters = $this->_datatableParameters(); - $parameters['transactionTypes'] = ['Transfer']; - $parameters['amount'] = 'positive'; - - $query = $this->_datatableQuery($parameters); - $resultSet = $this->_datatableResultset($parameters, $query); - - - /* - * Build return data: - */ - - if (Input::get('debug') == 'true') { - echo '
';
-            print_r($parameters);
-            echo '
'; - print_r($resultSet); - return ''; - - } else { - return Response::json($resultSet); + /** @var \Firefly\Storage\Account\EloquentAccountRepository $accounts */ + $accounts = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); + $list = $accounts->getOfTypes(['Expense account', 'Beneficiary account']); + $return = []; + foreach ($list as $entry) { + $return[] = $entry->name; } + return Response::json($return); } - /** - * @return array - */ - protected function _datatableParameters() - { - /* - * Process all parameters! - */ - $parameters = [ - 'start' => intval(Input::get('start')), - 'length' => intval(Input::get('length')) < 0 ? 100000 : intval(Input::get('length')), - 'draw' => intval(Input::get('draw')), - ]; - - - /* - * Columns: - */ - if (!is_null(Input::get('columns')) && is_array(Input::get('columns'))) { - foreach (Input::get('columns') as $column) { - $parameters['columns'][] = [ - 'data' => $column['data'], - 'name' => $column['name'], - 'searchable' => $column['searchable'] == 'true' ? true : false, - 'orderable' => $column['orderable'] == 'true' ? true : false, - 'search' => [ - 'value' => $column['search']['value'], - 'regex' => $column['search']['regex'] == 'true' ? true : false, - ] - ]; - } - } - - - /* - * Sorting. - */ - $parameters['orderOnAccount'] = false; - if (!is_null(Input::get('order')) && is_array(Input::get('order'))) { - foreach (Input::get('order') as $order) { - $columnIndex = intval($order['column']); - $columnName = $parameters['columns'][$columnIndex]['name']; - $parameters['order'][] = [ - 'name' => $columnName, - 'dir' => strtoupper($order['dir']) - ]; - if ($columnName == 'to' || $columnName == 'from') { - $parameters['orderOnAccount'] = true; - } - } - } - /* - * Search parameters: - */ - if (!is_null(Input::get('search')) && is_array(Input::get('search'))) { - $search = Input::get('search'); - $parameters['search'] = [ - 'value' => $search['value'], - 'regex' => $search['regex'] == 'true' ? true : false - ]; - } - return $parameters; - } - - /** - * @param array $parameters - * - * @return Builder - */ - protected function _datatableQuery(array $parameters) - { - /* - * We need the following vars to fine tune the query: - */ - if ($parameters['amount'] == 'negative') { - $operator = '<'; - $operatorNegated = '>'; - $function = 'lessThan'; - } else { - $operator = '>'; - $operatorNegated = '<'; - $function = 'moreThan'; - } - - /* - * Build query: - */ - $query = \TransactionJournal::transactionTypes($parameters['transactionTypes'])->withRelevantData(); - $query->where('completed',1); - /* - * This is complex. Join `transactions` twice, once for the "to" account and once for the - * "from" account. Then get the amount from one of these (depends on type). - * - * Only need to do this when there's a sort order for "from" or "to". - * - * Also need the table prefix for this to work. - */ - if ($parameters['orderOnAccount'] === true) { - $connection = \Config::get('database.default'); - $prefix = \Config::get('database.connections.' . $connection . '.prefix'); - // left join first table for "from" account: - $query->leftJoin( - 'transactions AS ' . $prefix . 't1', function ($join) use ($operator) { - $join->on('t1.transaction_journal_id', '=', 'transaction_journals.id') - ->on('t1.amount', $operator, \DB::Raw(0)); - } - ); - // left join second table for "to" account: - $query->leftJoin( - 'transactions AS ' . $prefix . 't2', function ($join) use ($operatorNegated) { - $join->on('t2.transaction_journal_id', '=', 'transaction_journals.id') - ->on('t2.amount', $operatorNegated, \DB::Raw(0)); - } - ); - - // also join accounts twice to get the account's name, which we need for sorting. - $query->leftJoin('accounts as ' . $prefix . 'a1', 'a1.id', '=', 't1.account_id'); - $query->leftJoin('accounts as ' . $prefix . 'a2', 'a2.id', '=', 't2.account_id'); - } else { - // less complex - $query->$function(0); - } - - /* - * Add sort parameters to query: - */ - if (isset($parameters['order']) && count($parameters['order']) > 0) { - foreach ($parameters['order'] as $order) { - $query->orderBy($order['name'], $order['dir']); - } - } else { - $query->defaultSorting(); - } - return $query; - } - /** * Returns a list of transactions, expenses only, using the given parameters. + * + * @return \Illuminate\Http\JsonResponse */ public function expenses() { @@ -241,7 +72,7 @@ class JsonController extends BaseController /* * Gets most parameters from the Input::all() array: */ - $parameters = $this->_datatableParameters(); + $parameters = $this->helper->dataTableParameters(); /* * Add some more parameters to fine tune the query: @@ -252,137 +83,58 @@ class JsonController extends BaseController /* * Get the query: */ - $query = $this->_datatableQuery($parameters); + $query = $this->helper->journalQuery($parameters); /* * Build result set: */ - $resultSet = $this->_datatableResultset($parameters, $query); + $resultSet = $this->helper->journalDataset($parameters, $query); /* * Build return data: */ - - if (Input::get('debug') == 'true') { - echo '
';
-            print_r($parameters);
-            echo '
'; - print_r($resultSet); - return ''; - - } else { - return Response::json($resultSet); - } + return Response::json($resultSet); } - protected function _datatableResultset(array $parameters, Builder $query) + public function recurring() { - /* - * Count query: - */ - $count = $query->count(); - - /* - * Update the selection: - */ - - $query->take($parameters['length']); - $query->skip($parameters['start']); - - /* - * Input search parameters: - */ - $filtered = $count; - if(strlen($parameters['search']['value']) > 0) { - $query->where('transaction_journals.description','LIKE','%'.e($parameters['search']['value']).'%'); - $filtered = $query->count(); - } - - - /* - * Build return array: - */ - $data = [ - 'draw' => $parameters['draw'], - 'recordsTotal' => $count, - 'recordsFiltered' => $filtered, - 'data' => [], - - ]; - - /* - * Get paginated result set: - */ - if ($parameters['orderOnAccount'] === true) { - /** @var Collection $set */ - $set = $query->get( - [ - 'transaction_journals.*', - 't1.amount', - 't1.account_id AS from_id', - 'a1.name AS from', - 't2.account_id AS to_id', - 'a2.name AS to', - ] - ); - } else { - /** @var Collection $set */ - $set = $query->get( - [ - 'transaction_journals.*', - 'transactions.amount', - ] - ); - } - - /* - * Loop set and create entries to return. - */ - foreach ($set as $entry) { - $from = $entry->transactions[0]->account; - $to = $entry->transactions[1]->account; - $data['data'][] = [ - 'date' => $entry->date->format('j F Y'), - 'description' => [ - 'description' => $entry->description, - 'url' => route('transactions.show', $entry->id) - ], - 'amount' => floatval($entry->amount), - 'from' => ['name' => $from->name, 'url' => route('accounts.show', $from->id)], - 'to' => ['name' => $to->name, 'url' => route('accounts.show', $to->id)], - 'id' => [ - 'edit' => route('transactions.edit', $entry->id), - 'delete' => route('transactions.delete', $entry->id) - ] - ]; - - } - return $data; + $parameters = $this->helper->dataTableParameters(); + $query = $this->helper->recurringTransactionsQuery($parameters); + $resultSet = $this->helper->recurringTransactionsDataset($parameters, $query); + return Response::json($resultSet); } /** - * Returns a JSON list of all beneficiaries. + * @return \Illuminate\Http\JsonResponse|string */ - public function expenseAccounts() + public function revenue() { - $list = $this->_accounts->getOfTypes(['Expense account', 'Beneficiary account']); - $return = []; - foreach ($list as $entry) { - $return[] = $entry->name; - } + $parameters = $this->helper->dataTableParameters(); + $parameters['transactionTypes'] = ['Deposit']; + $parameters['amount'] = 'positive'; - return Response::json($return); + $query = $this->helper->journalQuery($parameters); + $resultSet = $this->helper->journalDataset($parameters, $query); + + /* + * Build return data: + */ + return Response::json($resultSet); } /** * Returns a JSON list of all revenue accounts. + * + * @return \Illuminate\Http\JsonResponse */ public function revenueAccounts() { - $list = $this->_accounts->getOfTypes(['Revenue account']); - $return = []; + /** @var \Firefly\Storage\Account\EloquentAccountRepository $accounts */ + $accounts = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); + $list = $accounts->getOfTypes(['Revenue account']); + $return = []; foreach ($list as $entry) { $return[] = $entry->name; } @@ -392,18 +144,23 @@ class JsonController extends BaseController } /** - * Responds some JSON for typeahead fields. + * Returns a list of all transfers. + * + * @return \Illuminate\Http\JsonResponse */ - public function categories() + public function transfers() { - $list = $this->_categories->get(); - $return = []; - foreach ($list as $entry) { - $return[] = $entry->name; - } + $parameters = $this->helper->dataTableParameters(); + $parameters['transactionTypes'] = ['Transfer']; + $parameters['amount'] = 'positive'; - return Response::json($return); + $query = $this->helper->journalQuery($parameters); + $resultSet = $this->helper->journalDataset($parameters, $query); + /* + * Build return data: + */ + return Response::json($resultSet); } } \ No newline at end of file diff --git a/app/controllers/RecurringController.php b/app/controllers/RecurringController.php index 404a9f3c46..146a52d77c 100644 --- a/app/controllers/RecurringController.php +++ b/app/controllers/RecurringController.php @@ -1,5 +1,6 @@ _repository->destroy($recurringTransaction); if ($result === true) { Session::flash('success', 'The recurring transaction was deleted.'); @@ -84,11 +85,7 @@ class RecurringController extends BaseController */ public function index() { - $list = $this->_repository->get(); - - - - return View::make('recurring.index')->with('list', $list); + return View::make('recurring.index'); } /** @@ -106,10 +103,18 @@ class RecurringController extends BaseController */ public function store() { + + switch (Input::get('post_submit_action')) { + default: + throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.'); + break; + } + $recurringTransaction = $this->_repository->store(Input::all()); - if ($recurringTransaction->validate()) { + + if ($recurringTransaction->errors()->count() == 0) { Session::flash('success', 'Recurring transaction "' . $recurringTransaction->name . '" saved!'); - Event::fire('recurring.store', [$recurringTransaction]); + //Event::fire('recurring.store', [$recurringTransaction]); if (Input::get('create') == '1') { return Redirect::route('recurring.create')->withInput(); } else { @@ -135,7 +140,7 @@ class RecurringController extends BaseController $recurringTransaction = $this->_repository->update($recurringTransaction, Input::all()); if ($recurringTransaction->errors()->count() == 0) { Session::flash('success', 'The recurring transaction has been updated.'); - Event::fire('recurring.update', [$recurringTransaction]); + //Event::fire('recurring.update', [$recurringTransaction]); return Redirect::route('recurring.index'); } else { diff --git a/app/lib/Firefly/Helper/Controllers/Json.php b/app/lib/Firefly/Helper/Controllers/Json.php new file mode 100644 index 0000000000..25be47a7b4 --- /dev/null +++ b/app/lib/Firefly/Helper/Controllers/Json.php @@ -0,0 +1,369 @@ + intval(\Input::get('start')), + 'length' => $length, + 'draw' => intval(\Input::get('draw')), + ]; + + + /* + * Columns: + */ + if (!is_null(\Input::get('columns')) && is_array(\Input::get('columns'))) { + foreach (\Input::get('columns') as $column) { + $parameters['columns'][] = [ + 'data' => $column['data'], + 'name' => $column['name'], + 'searchable' => $column['searchable'] == 'true' ? true : false, + 'orderable' => $column['orderable'] == 'true' ? true : false, + 'search' => [ + 'value' => $column['search']['value'], + 'regex' => $column['search']['regex'] == 'true' ? true : false, + ] + ]; + } + } + + + /* + * Sorting. + */ + $parameters['orderOnAccount'] = false; + if (!is_null(\Input::get('order')) && is_array(\Input::get('order'))) { + foreach (\Input::get('order') as $order) { + $columnIndex = intval($order['column']); + $columnName = $parameters['columns'][$columnIndex]['name']; + $parameters['order'][] = [ + 'name' => $columnName, + 'dir' => strtoupper($order['dir']) + ]; + if ($columnName == 'to' || $columnName == 'from') { + $parameters['orderOnAccount'] = true; + } + } + } + /* + * Search parameters: + */ + $parameters['search'] = [ + 'value' => '', + 'regex' => false + ]; + if (!is_null(\Input::get('search')) && is_array(\Input::get('search'))) { + $search = \Input::get('search'); + $parameters['search'] = [ + 'value' => $search['value'], + 'regex' => $search['regex'] == 'true' ? true : false + ]; + } + return $parameters; + } + + /** + * Do some sorting, counting and ordering on the query and return a nicely formatted array + * that can be used by the DataTables JQuery plugin. + * + * @param array $parameters + * @param Builder $query + * + * @return array + */ + public function journalDataset(array $parameters, Builder $query) + { + /* + * Count query: + */ + $count = $query->count(); + + /* + * Update the selection: + */ + + $query->take($parameters['length']); + if ($parameters['start'] > 0) { + $query->skip($parameters['start']); + } + + /* + * Input search parameters: + */ + $filtered = $count; + if (strlen($parameters['search']['value']) > 0) { + $query->where('transaction_journals.description', 'LIKE', '%' . e($parameters['search']['value']) . '%'); + $filtered = $query->count(); + } + + + /* + * Build return array: + */ + $data = [ + 'draw' => $parameters['draw'], + 'recordsTotal' => $count, + 'recordsFiltered' => $filtered, + 'data' => [], + + ]; + + /* + * Get paginated result set: + */ + if ($parameters['orderOnAccount'] === true) { + /** @var Collection $set */ + $set = $query->get( + [ + 'transaction_journals.*', + 't1.amount', + 't1.account_id AS from_id', + 'a1.name AS from', + 't2.account_id AS to_id', + 'a2.name AS to', + ] + ); + } else { + /** @var Collection $set */ + $set = $query->get( + [ + 'transaction_journals.*', + 'transactions.amount', + ] + ); + } + + /* + * Loop set and create entries to return. + */ + foreach ($set as $entry) { + $from = $entry->transactions[0]->account; + $to = $entry->transactions[1]->account; + $data['data'][] = [ + 'date' => $entry->date->format('j F Y'), + 'description' => [ + 'description' => $entry->description, + 'url' => route('transactions.show', $entry->id) + ], + 'amount' => floatval($entry->amount), + 'from' => ['name' => $from->name, 'url' => route('accounts.show', $from->id)], + 'to' => ['name' => $to->name, 'url' => route('accounts.show', $to->id)], + 'id' => [ + 'edit' => route('transactions.edit', $entry->id), + 'delete' => route('transactions.delete', $entry->id) + ] + ]; + + } + return $data; + } + + /** + * Builds most of the query required to grab transaction journals from the database. + * This is useful because all three pages showing different kinds of transactions use + * the exact same query with only slight differences. + * + * @param array $parameters + * + * @return Builder + */ + public function journalQuery(array $parameters) + { + /* + * We need the following vars to fine tune the query: + */ + if ($parameters['amount'] == 'negative') { + $operator = '<'; + $operatorNegated = '>'; + $function = 'lessThan'; + } else { + $operator = '>'; + $operatorNegated = '<'; + $function = 'moreThan'; + } + + /* + * Build query: + */ + $query = \TransactionJournal::transactionTypes($parameters['transactionTypes'])->withRelevantData(); + $query->where('user_id', \Auth::user()->id); + $query->where('completed', 1); + /* + * This is complex. Join `transactions` twice, once for the "to" account and once for the + * "from" account. Then get the amount from one of these (depends on type). + * + * Only need to do this when there's a sort order for "from" or "to". + * + * Also need the table prefix for this to work. + */ + if ($parameters['orderOnAccount'] === true) { + $connection = \Config::get('database.default'); + $prefix = \Config::get('database.connections.' . $connection . '.prefix'); + // left join first table for "from" account: + $query->leftJoin( + 'transactions AS ' . $prefix . 't1', function ($join) use ($operator) { + $join->on('t1.transaction_journal_id', '=', 'transaction_journals.id') + ->on('t1.amount', $operator, \DB::Raw(0)); + } + ); + // left join second table for "to" account: + $query->leftJoin( + 'transactions AS ' . $prefix . 't2', function ($join) use ($operatorNegated) { + $join->on('t2.transaction_journal_id', '=', 'transaction_journals.id') + ->on('t2.amount', $operatorNegated, \DB::Raw(0)); + } + ); + + // also join accounts twice to get the account's name, which we need for sorting. + $query->leftJoin('accounts as ' . $prefix . 'a1', 'a1.id', '=', 't1.account_id'); + $query->leftJoin('accounts as ' . $prefix . 'a2', 'a2.id', '=', 't2.account_id'); + } else { + // less complex + $query->$function(0); + } + + /* + * Add sort parameters to query: + */ + if (isset($parameters['order']) && count($parameters['order']) > 0) { + foreach ($parameters['order'] as $order) { + $query->orderBy($order['name'], $order['dir']); + } + } else { + $query->defaultSorting(); + } + return $query; + } + + /** + * Do some sorting, counting and ordering on the query and return a nicely formatted array + * that can be used by the DataTables JQuery plugin. + * + * @param array $parameters + * @param Builder $query + * + * @return array + */ + public function recurringTransactionsDataset(array $parameters, Builder $query) + { + /* + * Count query: + */ + $count = $query->count(); + + /* + * Update the selection: + */ + + $query->take($parameters['length']); + if ($parameters['start'] > 0) { + $query->skip($parameters['start']); + } + + /* + * Input search parameters: + */ + $filtered = $count; + if (strlen($parameters['search']['value']) > 0) { + $query->where('recurring_transactions.description', 'LIKE', '%' . e($parameters['search']['value']) . '%'); + $filtered = $query->count(); + } + + + /* + * Build return array: + */ + $data = [ + 'draw' => $parameters['draw'], + 'recordsTotal' => $count, + 'recordsFiltered' => $filtered, + 'data' => [], + + ]; + + /* + * Get paginated result set: + */ + /** @var Collection $set */ + $set = $query->get( + [ + 'recurring_transactions.*', + ] + ); + + /* + * Loop set and create entries to return. + */ + foreach ($set as $entry) { + $data['data'][] = [ + + 'name' => ['name' => $entry->name,'url' => route('recurring.show',$entry->id)], + 'match' => explode(' ',$entry->match), + 'amount_max' => floatval($entry->amount_max), + 'amount_min' => floatval($entry->amount_min), + 'date' => $entry->date->format('j F Y'), + 'active' => intval($entry->active), + 'automatch' => intval($entry->automatch), + 'repeat_freq' => $entry->repeat_freq, + 'id' => [ + 'edit' => route('recurring.edit', $entry->id), + 'delete' => route('recurring.delete', $entry->id) + ] + ]; + + } + return $data; + } + + /** + * Create a query that will pick up all recurring transactions from the database. + * + * @param array $parameters + * + * @return Builder + */ + public function recurringTransactionsQuery(array $parameters) + { + $query = \RecurringTransaction::where('user_id', \Auth::user()->id); + + if (isset($parameters['order']) && count($parameters['order']) > 0) { + foreach ($parameters['order'] as $order) { + $query->orderBy($order['name'], $order['dir']); + } + } else { + $query->orderBy('name', 'ASC'); + } + return $query; + } +} \ No newline at end of file diff --git a/app/lib/Firefly/Helper/Controllers/JsonInterface.php b/app/lib/Firefly/Helper/Controllers/JsonInterface.php new file mode 100644 index 0000000000..4e05d2f02e --- /dev/null +++ b/app/lib/Firefly/Helper/Controllers/JsonInterface.php @@ -0,0 +1,64 @@ +id == $from->id) { + if ($to->id == + $from->id) { $bag = new MessageBag; $bag->add('account_from_id', 'The account from cannot be the same as the account to.'); return $bag; diff --git a/app/lib/Firefly/Helper/HelperServiceProvider.php b/app/lib/Firefly/Helper/HelperServiceProvider.php index 1e42d710e0..fc7338a642 100644 --- a/app/lib/Firefly/Helper/HelperServiceProvider.php +++ b/app/lib/Firefly/Helper/HelperServiceProvider.php @@ -27,6 +27,11 @@ class HelperServiceProvider extends ServiceProvider 'Firefly\Helper\Controllers\Chart' ); + $this->app->bind( + 'Firefly\Helper\Controllers\JsonInterface', + 'Firefly\Helper\Controllers\Json' + ); + $this->app->bind( 'Firefly\Helper\Controllers\SearchInterface', 'Firefly\Helper\Controllers\Search' diff --git a/app/lib/Firefly/Storage/RecurringTransaction/EloquentRecurringTransactionRepository.php b/app/lib/Firefly/Storage/RecurringTransaction/EloquentRecurringTransactionRepository.php index a658725017..78d1261682 100644 --- a/app/lib/Firefly/Storage/RecurringTransaction/EloquentRecurringTransactionRepository.php +++ b/app/lib/Firefly/Storage/RecurringTransaction/EloquentRecurringTransactionRepository.php @@ -24,6 +24,26 @@ class EloquentRecurringTransactionRepository implements RecurringTransactionRepo $this->_user = \Auth::user(); } + /** + * @param \RecurringTransaction $recurringTransaction + * + * @return bool|mixed + */ + public function destroy(\RecurringTransaction $recurringTransaction) + { + $recurringTransaction->delete(); + + return true; + } + + /** + * @return mixed + */ + public function get() + { + return $this->_user->recurringtransactions()->get(); + } + /** * @param Job $job * @param array $payload @@ -115,25 +135,43 @@ class EloquentRecurringTransactionRepository implements RecurringTransactionRepo */ public function store($data) { - $recurringTransaction = new \RecurringTransaction; - $recurringTransaction->user()->associate($this->_user); - $recurringTransaction->name = $data['name']; - $recurringTransaction->match = join(' ', explode(',', $data['match'])); - $recurringTransaction->amount_max = floatval($data['amount_max']); - $recurringTransaction->amount_min = floatval($data['amount_min']); + $recurringTransaction = new \RecurringTransaction( + [ + 'user_id' => $this->_user->id, + 'name' => $data['name'], + 'match' => join(' ', explode(',', $data['match'])), + 'amount_max' => floatval($data['amount_max']), + 'amount_min' => floatval($data['amount_min']), + 'date' => new Carbon($data['date']), + 'active' => isset($data['active']) ? intval($data['active']) : 0, + 'automatch' => isset($data['automatch']) ? intval($data['automatch']) : 0, + 'skip' => isset($data['skip']) ? intval($data['skip']) : 0, + 'repeat_freq' => $data['repeat_freq'], + ] + ); - // both amounts zero: + // both amounts zero?: if ($recurringTransaction->amount_max == 0 && $recurringTransaction->amount_min == 0) { $recurringTransaction->errors()->add('amount_max', 'Amount max and min cannot both be zero.'); return $recurringTransaction; } - $recurringTransaction->date = new Carbon($data['date']); - $recurringTransaction->active = isset($data['active']) ? intval($data['active']) : 0; - $recurringTransaction->automatch = isset($data['automatch']) ? intval($data['automatch']) : 0; - $recurringTransaction->skip = isset($data['skip']) ? intval($data['skip']) : 0; - $recurringTransaction->repeat_freq = $data['repeat_freq']; + if ($recurringTransaction->amount_max < $recurringTransaction->amount_min) { + $recurringTransaction->errors()->add('amount_max', 'Amount max must be more than amount min.'); + return $recurringTransaction; + } + + if ($recurringTransaction->amount_min > $recurringTransaction->amount_max) { + $recurringTransaction->errors()->add('amount_max', 'Amount min must be less than amount max.'); + return $recurringTransaction; + } + + if($recurringTransaction->date < Carbon::now()) { + $recurringTransaction->errors()->add('date', 'Must be in the future.'); + return $recurringTransaction; + } + if ($recurringTransaction->validate()) { $recurringTransaction->save(); @@ -142,26 +180,6 @@ class EloquentRecurringTransactionRepository implements RecurringTransactionRepo return $recurringTransaction; } - /** - * @param \RecurringTransaction $recurringTransaction - * - * @return bool|mixed - */ - public function destroy(\RecurringTransaction $recurringTransaction) - { - $recurringTransaction->delete(); - - return true; - } - - /** - * @return mixed - */ - public function get() - { - return $this->_user->recurringtransactions()->get(); - } - /** * @param \RecurringTransaction $recurringTransaction * @param $data diff --git a/app/models/RecurringTransaction.php b/app/models/RecurringTransaction.php index 4a829f73e8..0e098cf17e 100644 --- a/app/models/RecurringTransaction.php +++ b/app/models/RecurringTransaction.php @@ -51,6 +51,7 @@ class RecurringTransaction extends Ardent 'skip' => 'required|between:0,31', ]; + protected $fillable = ['user_id','name','match','amount_min','amount_max','date','repeat_freq','skip','active','automatch']; /** * @return array diff --git a/app/routes.php b/app/routes.php index 67b14fef4e..7caec07c9c 100644 --- a/app/routes.php +++ b/app/routes.php @@ -184,6 +184,7 @@ Route::group(['before' => 'auth'], function () { Route::get('/json/expenses', ['uses' => 'JsonController@expenses', 'as' => 'json.expenses']); Route::get('/json/revenue', ['uses' => 'JsonController@revenue', 'as' => 'json.revenue']); Route::get('/json/transfers', ['uses' => 'JsonController@transfers', 'as' => 'json.transfers']); + Route::get('/json/recurring', ['uses' => 'JsonController@recurring', 'as' => 'json.recurring']); // limit controller: Route::get('/budgets/limits/create/{budget?}',['uses' => 'LimitController@create','as' => 'budgets.limits.create']); diff --git a/app/views/recurring/create.blade.php b/app/views/recurring/create.blade.php index 9c28f49f17..7b13c2e20c 100644 --- a/app/views/recurring/create.blade.php +++ b/app/views/recurring/create.blade.php @@ -1,182 +1,186 @@ @extends('layouts.default') @section('content') -
-
-

Use recurring transactions to track repeated expenses

-

- Bla bla. -

-
-
- {{Form::open(['class' => 'form-horizontal','url' => route('recurring.store')])}}
-

Mandatory fields

- - -
- -
- - @if($errors->has('name')) -

{{$errors->first('name')}}

- @else - For example: rent, gas, insurance - @endif + +
+
+ Mandatory fields
-
-
- -
- - @if($errors->has('match')) -

{{$errors->first('match')}}

- @else - For example: rent, [company name]. All matches need to - be present for the recurring transaction to be recognized. This field is not case-sensitive. Press enter after every match - @endif -
-
- -
- {{ Form::label('amount_min', 'Minimum amount', ['class' => 'col-sm-4 control-label'])}} -
-
- - {{Form::input('number','amount_min', Input::old('amount_min'), ['step' => 'any', 'class' => 'form-control'])}} +
+ +
+ +
+ + @if($errors->has('name')) +

{{$errors->first('name')}}

+ @endif +
+
+
+ +
+ + @if($errors->has('match')) +

{{$errors->first('match')}}

+ @endif +
- @if($errors->has('amount_min')) -

{{$errors->first('amount_min')}}

- @else - Firefly will only include transactions with a higher amount than this. If your rent - is usually around € 500,-, enter 450 to be safe. - @endif -
-
+
+ {{ Form::label('amount_min', 'Minimum amount', ['class' => 'col-sm-4 control-label'])}} +
+
+ + {{Form::input('number','amount_min', Input::old('amount_min'), ['step' => 'any', 'class' => 'form-control'])}} +
-
- {{ Form::label('amount_max', 'Maximum amount', ['class' => 'col-sm-4 control-label'])}} -
-
- - {{Form::input('number','amount_max', Input::old('amount_max'), ['step' => 'any', 'class' => 'form-control'])}} + @if($errors->has('amount_min')) +

{{$errors->first('amount_min')}}

+ @endif +
- @if($errors->has('amount_max')) -

{{$errors->first('amount_max')}}

- @else - Firefly will only include transactions with a lower amount than this. If your rent - is usually around € 500,-, enter 550 to be safe. - @endif +
+ {{ Form::label('amount_max', 'Maximum amount', ['class' => 'col-sm-4 control-label'])}} +
+
+ + {{Form::input('number','amount_max', Input::old('amount_max'), ['step' => 'any', 'class' => 'form-control'])}} +
+ + @if($errors->has('amount_max')) +

{{$errors->first('amount_max')}}

+ @endif +
+
+ +
+ {{ Form::label('date', 'Date', ['class' => 'col-sm-4 control-label'])}} +
+ {{ Form::input('date','date', Input::old('date') ?: Carbon\Carbon::now()->addDay()->format('Y-m-d'), ['class' + => 'form-control']) }} + @if($errors->has('date')) +

{{$errors->first('date')}}

+ @endif +
+
+ +
+ +
+ {{Form::select('repeat_freq',$periods,Input::old('repeat_freq') ?: 'monthly',['class' => 'form-control'])}} + @if($errors->has('repeat_freq')) +

{{$errors->first('repeat_freq')}}

+ @endif +
+
- -
- {{ Form::label('date', 'Date', ['class' => 'col-sm-4 control-label'])}} -
- {{ Form::input('date','date', Input::old('date') ?: date('Y-m-d'), ['class' - => 'form-control']) }} - @if($errors->has('date')) -

{{$errors->first('date')}}

- @else - Select the next date you expect the transaction to occur. - @endif -
-
- -
- -
- {{Form::select('repeat_freq',$periods,Input::old('repeat_freq') ?: 'monthly',['class' => 'form-control'])}} - @if($errors->has('repeat_freq')) -

{{$errors->first('repeat_freq')}}

- @else - Select the period over which this transaction repeats - @endif -
-
- +

+ +

-

Optional fields

+ +
+
+ Optional fields +
+
+
+ {{ Form::label('skip', 'Skip', ['class' => 'col-sm-4 control-label'])}} +
+ {{Form::input('number','skip', Input::old('skip') ?: 0, ['class' => 'form-control'])}} -
- {{ Form::label('skip', 'Skip', ['class' => 'col-sm-4 control-label'])}} -
- {{Form::input('number','skip', Input::old('skip') ?: 0, ['class' => 'form-control'])}} - - @if($errors->has('skip')) -

{{$errors->first('skip')}}

- @else - Make Firefly skip every n times. Fill in 2, and Firefly - will match, skip, skip and match a transaction. - @endif + @if($errors->has('skip')) +

{{$errors->first('skip')}}

+ @else + Make Firefly skip every n times. Fill in 2, and Firefly + will match, skip, skip and match a transaction. + @endif +
+
+
+ +
+
+ +
+ Firefly will automatically match transactions. +
+
+
+ +
+
+ +
+ This recurring transaction is actually active. +
+
- - - - - - - - -
- -
-
+ +
+
+ Options +
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
- Firefly will automatically match transactions.
- -
- -
-
- -
- This recurring transaction is actually active. -
- - -
-
-
-
- - -
- -
-
- -
-
-
- -
-
- -
-
diff --git a/app/views/recurring/edit.blade.php b/app/views/recurring/edit.blade.php index 256d945488..3a33641f84 100644 --- a/app/views/recurring/edit.blade.php +++ b/app/views/recurring/edit.blade.php @@ -1,20 +1,15 @@ @extends('layouts.default') @section('content') -
-
-

Use recurring transactions to track repeated expenses

-

- Bla bla. -

-
-
- {{Form::open(['class' => 'form-horizontal','url' => route('recurring.update', $recurringTransaction->id)])}}
-

Mandatory fields

- + +
+
+ Mandatory fields +
+
@@ -23,8 +18,6 @@ value="{{{Input::old('name') ?: $recurringTransaction->name}}}" placeholder="Name"> @if($errors->has('name'))

{{$errors->first('name')}}

- @else - For example: rent, gas, insurance @endif
@@ -36,9 +29,6 @@ data-role="tagsinput"> @if($errors->has('match'))

{{$errors->first('match')}}

- @else - For example: rent, [company name]. All matches need to - be present for the recurring transaction to be recognized. This field is not case-sensitive. @endif
@@ -54,9 +44,6 @@ @if($errors->has('amount_min'))

{{$errors->first('amount_min')}}

- @else - Firefly will only include transactions with a higher amount than this. If your rent - is usually around € 500,-, enter 450 to be safe. @endif
@@ -72,9 +59,6 @@ @if($errors->has('amount_max'))

{{$errors->first('amount_max')}}

- @else - Firefly will only include transactions with a lower amount than this. - If your rent is usually around € 500,-, enter 550 to be safe. @endif
@@ -86,8 +70,6 @@ ['class' => 'form-control']) }} @if($errors->has('date'))

{{$errors->first('date')}}

- @else - Select the next date you expect the transaction to occur. @endif
@@ -99,16 +81,26 @@ ['class' => 'form-control'])}} @if($errors->has('repeat_freq'))

{{$errors->first('repeat_freq')}}

- @else - Select the period over which this transaction repeats @endif
+
-
-

Optional fields

+

+ +

+
+
+ +
+
+ Optional fields +
+
{{ Form::label('skip', 'Skip', ['class' => 'col-sm-4 control-label'])}}
@@ -117,21 +109,10 @@ @if($errors->has('skip'))

{{$errors->first('skip')}}

- @else - Make Firefly skip every n times. Fill in 2, and Firefly - will match, skip, skip and match a transaction. @endif
- - - - - - - -
@@ -159,25 +140,57 @@ This recurring transaction is actually active.
- - - -
- -
-
- - - -
-
- +
+ +
+
+ Options +
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
-
+
+
- {{Form::close()}} diff --git a/app/views/recurring/index.blade.php b/app/views/recurring/index.blade.php index 2e2e651e6f..ac3831d090 100644 --- a/app/views/recurring/index.blade.php +++ b/app/views/recurring/index.blade.php @@ -1,73 +1,41 @@ @extends('layouts.default') @section('content') -
-
-

Use recurring transactions to track repeated withdrawals

-

We all have bills to pay. Firefly can help you organize those bills into recurring transactions, - which are exactly what the name suggests. Firefly can match new (and existing) transactions to such a recurring transaction - and help you organize these expenses into manageable groups. The front page of Firefly will show you which recurring - transactions you have missed, which are yet to come and which have been paid.

- -
-
-
- - - - - - - - - - - - @foreach($list as $entry) - - - - - - - - - - - @endforeach -
NameMatches onAmount betweenExpected everyNext expected matchAuto-matchActive
{{{$entry->name}}} - @foreach(explode(' ',$entry->match) as $word) - {{{$word}}} - @endforeach - - {{mf($entry->amount_min)}} – - {{mf($entry->amount_max)}} - - {{$entry->repeat_freq}} - - {{$entry->next()->format('d-m-Y')}} - - @if($entry->automatch) - - @else - - @endif - - @if($entry->active) - - @else - - @endif - -
- - -
-
-

- Create new recurring transaction -

+
+
+ {{{$title}}} +
+
+ + + + + + + + + + + + + + +
namematchamount_minamount_maxdateactiveautomatchrepeat_freqid
+
+
+@stop +@section('scripts') + +{{HTML::script('assets/javascript/typeahead/bootstrap3-typeahead.min.js')}} +{{HTML::script('assets/javascript/datatables/jquery.dataTables.min.js')}} +{{HTML::script('assets/javascript/datatables/dataTables.bootstrap.js')}} +{{HTML::script('assets/javascript/firefly/recurring.js')}} +@stop +@section('styles') +{{HTML::style('assets/stylesheets/datatables/dataTables.bootstrap.css')}} @stop \ No newline at end of file diff --git a/app/views/transactions/list.blade.php b/app/views/transactions/list.blade.php index 7c434f56c6..c3610bf4a6 100644 --- a/app/views/transactions/list.blade.php +++ b/app/views/transactions/list.blade.php @@ -12,7 +12,7 @@ Date Description - Amount (€) + Amount (€) From To ID diff --git a/public/assets/javascript/firefly/recurring.js b/public/assets/javascript/firefly/recurring.js new file mode 100644 index 0000000000..7580654bcc --- /dev/null +++ b/public/assets/javascript/firefly/recurring.js @@ -0,0 +1,112 @@ +$(document).ready(function () { + $('#recurringTable').DataTable( + { + serverSide: true, + ajax: URL, + paging: true, + processing: true, + order: [], + "lengthMenu": [[50, 100, 250, -1], [50, 100, 250, "All"]], + columns: [ + { + name: 'name', + data: 'name', + searchable: true, + title: 'Name', + render: function (data) { + return '' + data.name + ''; + } + }, + { + name: 'match', + data: 'match', + searchable: true, + title: 'Matches on', + render: function (data) { + var str = ''; + for (x in data) { + str += '' + data[x] + ' '; + } + return str;//return '' + data.name + ''; + } + }, + { + name: 'amount_min', + data: 'amount_min', + searchable: false, + title: '→', + render: function (data) { + return '\u20AC ' + data.toFixed(2) + ''; + } + }, + { + name: 'amount_max', + data: 'amount_max', + searchable: false, + title: '←', + render: function (data) { + return '\u20AC ' + data.toFixed(2) + ''; + } + + }, + { + name: 'date', + data: 'date', + title: 'Expected on', + searchable: false + }, + + { + name: 'active', + data: 'active', + searchable: false, + sortable: false, + render: function(data) { + if(data == 1) { + return ''; + } else { + return ''; + } + }, + title: 'Is active?' + }, + { + name: 'automatch', + data: 'automatch', + sortable: false, + searchable: false, + render: function(data) { + if(data == 1) { + return ''; + } else { + return ''; + } + }, + title: 'Automatch?' + }, + { + name: 'repeat_freq', + data: 'repeat_freq', + searchable: false, + sortable: false, + title: 'Repeat frequency' + }, + { + name: 'id', + data: 'id', + searchable: false, + sortable: false, + title: '', + render: function (data, type, full, meta) { + return ''; + } + } + ] + } + ); +}); \ No newline at end of file diff --git a/public/assets/javascript/firefly/transactions.js b/public/assets/javascript/firefly/transactions.js index befbbde945..7eced49f24 100644 --- a/public/assets/javascript/firefly/transactions.js +++ b/public/assets/javascript/firefly/transactions.js @@ -51,6 +51,7 @@ $(document).ready(function () { { name: 'amount', data: 'amount', + 'title': 'Amount (\u20AC)', searchable: false, render: function (data, type, full, meta) { if (display == 'expenses') { @@ -84,6 +85,8 @@ $(document).ready(function () { name: 'id', data: 'id', searchable: false, + sortable: false, + title: '', render: function (data, type, full, meta) { return '
' + '' +