Varioux fixes and cleaning up.

This commit is contained in:
James Cole 2014-09-28 08:47:51 +02:00
parent 9015d6ca16
commit aa9eb8ca64
15 changed files with 949 additions and 628 deletions

View File

@ -1,9 +1,6 @@
<?php
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use Firefly\Storage\Budget\BudgetRepositoryInterface as Bud;
use Firefly\Storage\Category\CategoryRepositoryInterface as Cat;
use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI;
use Firefly\Helper\Controllers\JsonInterface as JI;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Builder;
@ -14,226 +11,60 @@ use LaravelBook\Ardent\Builder;
*/
class JsonController extends BaseController
{
protected $_accounts;
protected $_categories;
protected $_budgets;
/** @var TJRI $_journals */
protected $_journals;
/** @var \Firefly\Helper\Controllers\JsonInterface $helper */
protected $helper;
/**
* @param ARI $accounts
* @param Cat $categories
* @param Bud $budgets
* @param TJRI $journals
*/
public function __construct(ARI $accounts, Cat $categories, Bud $budgets, TJRI $journals)
public function __construct(JI $helper)
{
$this->_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 '<pre>';
print_r($parameters);
echo '<hr>';
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 '<pre>';
print_r($parameters);
echo '<hr>';
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 '<pre>';
print_r($parameters);
echo '<hr>';
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);
}
}

View File

@ -1,5 +1,6 @@
<?php
use Firefly\Exception\FireflyException;
use Firefly\Storage\RecurringTransaction\RecurringTransactionRepositoryInterface as RTR;
/**
@ -51,7 +52,7 @@ class RecurringController extends BaseController
*/
public function destroy(RecurringTransaction $recurringTransaction)
{
Event::fire('recurring.destroy', [$recurringTransaction]);
//Event::fire('recurring.destroy', [$recurringTransaction]);
$result = $this->_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 {

View File

@ -0,0 +1,369 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 27/09/14
* Time: 07:39
*/
namespace Firefly\Helper\Controllers;
use LaravelBook\Ardent\Builder;
/**
* Class Json
*
* @package Firefly\Helper\Controllers
*/
class Json implements JsonInterface
{
/**
* Grabs all the parameters entered by the DataTables JQuery plugin and creates
* a nice array to be used by the other methods. It's also cleaning up and what-not.
*
* @return array
*/
public function dataTableParameters()
{
/*
* Process all parameters!
*/
if (intval(\Input::get('length')) < 0) {
$length = 10000; // we get them all if no length is defined.
} else {
$length = intval(\Input::get('length'));
}
$parameters = [
'start' => 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;
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace Firefly\Helper\Controllers;
use LaravelBook\Ardent\Builder;
/**
* Interface JsonInterface
*
* @package Firefly\Helper\Controllers
*/
interface JsonInterface
{
/**
* Grabs all the parameters entered by the DataTables JQuery plugin and creates
* a nice array to be used by the other methods. It's also cleaning up and what-not.
*
* @return array
*/
public function dataTableParameters();
/**
* 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);
/**
* 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);
/**
* 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);
/**
* Create a query that will pick up all recurring transactions from the database.
*
* @param array $parameters
*
* @return Builder
*/
public function recurringTransactionsQuery(array $parameters);
}

View File

@ -247,7 +247,8 @@ class Transaction implements TransactionInterface
/*
* Add a custom error when they are the same.
*/
if ($to->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;

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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']);

View File

@ -1,182 +1,186 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">Use recurring transactions to track repeated expenses</p>
<p class="text-info">
Bla bla.
</p>
</div>
</div>
{{Form::open(['class' => 'form-horizontal','url' => route('recurring.store')])}}
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-6">
<h4>Mandatory fields</h4>
<!-- name -->
<div class="form-group">
<label for="name" class="col-sm-4 control-label">Name</label>
<div class="col-sm-8">
<input type="text" name="name" class="form-control" id="name" value="{{Input::old('name')}}" placeholder="Name">
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@else
<span class="help-block">For example: rent, gas, insurance</span>
@endif
<!-- panel for mandatory fields -->
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-exclamation-circle"></i> Mandatory fields
</div>
</div>
<div class="form-group">
<label for="match" class="col-sm-4 control-label">Matches on</label>
<div class="col-sm-8">
<input type="text" name="match" class="form-control" id="match" value="{{Input::old('match')}}" data-role="tagsinput">
@if($errors->has('match'))
<p class="text-danger">{{$errors->first('match')}}</p>
@else
<span class="help-block">For example: rent, [company name]. All matches need to
be present for the recurring transaction to be recognized. This field is not case-sensitive. <em>Press enter after every match</em></span>
@endif
</div>
</div>
<div class="form-group">
{{ Form::label('amount_min', 'Minimum amount', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
<div class="input-group">
<span class="input-group-addon">&euro;</span>
{{Form::input('number','amount_min', Input::old('amount_min'), ['step' => 'any', 'class' => 'form-control'])}}
<div class="panel-body">
<!-- name -->
<div class="form-group">
<label for="name" class="col-sm-4 control-label">Name</label>
<div class="col-sm-8">
<input type="text" name="name" class="form-control" id="name" value="{{Input::old('name')}}" placeholder="Name">
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@endif
</div>
</div>
<div class="form-group">
<label for="match" class="col-sm-4 control-label">Matches on</label>
<div class="col-sm-8">
<input type="text" name="match" class="form-control" id="match" value="{{Input::old('match')}}" data-role="tagsinput">
@if($errors->has('match'))
<p class="text-danger">{{$errors->first('match')}}</p>
@endif
</div>
</div>
@if($errors->has('amount_min'))
<p class="text-danger">{{$errors->first('amount_min')}}</p>
@else
<span class="help-block">Firefly will only include transactions with a higher amount than this. If your rent
is usually around &euro; 500,-, enter <code>450</code> to be safe.</span>
@endif
</div>
</div>
<div class="form-group">
{{ Form::label('amount_min', 'Minimum amount', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
<div class="input-group">
<span class="input-group-addon">&euro;</span>
{{Form::input('number','amount_min', Input::old('amount_min'), ['step' => 'any', 'class' => 'form-control'])}}
</div>
<div class="form-group">
{{ Form::label('amount_max', 'Maximum amount', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
<div class="input-group">
<span class="input-group-addon">&euro;</span>
{{Form::input('number','amount_max', Input::old('amount_max'), ['step' => 'any', 'class' => 'form-control'])}}
@if($errors->has('amount_min'))
<p class="text-danger">{{$errors->first('amount_min')}}</p>
@endif
</div>
</div>
@if($errors->has('amount_max'))
<p class="text-danger">{{$errors->first('amount_max')}}</p>
@else
<span class="help-block">Firefly will only include transactions with a lower amount than this. If your rent
is usually around &euro; 500,-, enter <code>550</code> to be safe.</span>
@endif
<div class="form-group">
{{ Form::label('amount_max', 'Maximum amount', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
<div class="input-group">
<span class="input-group-addon">&euro;</span>
{{Form::input('number','amount_max', Input::old('amount_max'), ['step' => 'any', 'class' => 'form-control'])}}
</div>
@if($errors->has('amount_max'))
<p class="text-danger">{{$errors->first('amount_max')}}</p>
@endif
</div>
</div>
<div class="form-group">
{{ Form::label('date', 'Date', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
{{ Form::input('date','date', Input::old('date') ?: Carbon\Carbon::now()->addDay()->format('Y-m-d'), ['class'
=> 'form-control']) }}
@if($errors->has('date'))
<p class="text-danger">{{$errors->first('date')}}</p>
@endif
</div>
</div>
<div class="form-group">
<label for="period" class="col-sm-4 control-label">Recurrence</label>
<div class="col-sm-8">
{{Form::select('repeat_freq',$periods,Input::old('repeat_freq') ?: 'monthly',['class' => 'form-control'])}}
@if($errors->has('repeat_freq'))
<p class="text-danger">{{$errors->first('repeat_freq')}}</p>
@endif
</div>
</div>
</div>
</div>
<div class="form-group">
{{ Form::label('date', 'Date', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
{{ Form::input('date','date', Input::old('date') ?: date('Y-m-d'), ['class'
=> 'form-control']) }}
@if($errors->has('date'))
<p class="text-danger">{{$errors->first('date')}}</p>
@else
<span class="help-block">Select the next date you expect the transaction to occur.</span>
@endif
</div>
</div>
<div class="form-group">
<label for="period" class="col-sm-4 control-label">Recurrence</label>
<div class="col-sm-8">
{{Form::select('repeat_freq',$periods,Input::old('repeat_freq') ?: 'monthly',['class' => 'form-control'])}}
@if($errors->has('repeat_freq'))
<p class="text-danger">{{$errors->first('repeat_freq')}}</p>
@else
<span class="help-block">Select the period over which this transaction repeats</span>
@endif
</div>
</div>
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new recurring transaction
</button>
</p>
</div>
<div class="col-lg-6 col-md-12 col-sm-6">
<h4>Optional fields</h4>
<!-- panel for optional fields -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-smile-o"></i> Optional fields
</div>
<div class="panel-body">
<div class="form-group">
{{ Form::label('skip', 'Skip', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
{{Form::input('number','skip', Input::old('skip') ?: 0, ['class' => 'form-control'])}}
<div class="form-group">
{{ Form::label('skip', 'Skip', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
{{Form::input('number','skip', Input::old('skip') ?: 0, ['class' => 'form-control'])}}
@if($errors->has('skip'))
<p class="text-danger">{{$errors->first('skip')}}</p>
@else
<span class="help-block">Make Firefly skip every <em>n</em> times. Fill in <code>2</code>, and Firefly
will match, skip, skip and match a transaction.</span>
@endif
@if($errors->has('skip'))
<p class="text-danger">{{$errors->first('skip')}}</p>
@else
<span class="help-block">Make Firefly skip every <em>n</em> times. Fill in <code>2</code>, and Firefly
will match, skip, skip and match a transaction.</span>
@endif
</div>
</div>
<div class="form-group">
<label for="automatch" class="col-sm-4 control-label">Auto-match</label>
<div class="col-sm-8">
<div class="checkbox">
<label>
{{Form::checkbox('automatch',1,Input::old('automatch') == '1' || !Input::old('automatch'))}}
Yes
</label>
</div>
<span class="help-block">Firefly will automatically match transactions.</span>
</div>
</div>
<div class="form-group">
<label for="active" class="col-sm-4 control-label">Active</label>
<div class="col-sm-8">
<div class="checkbox">
<label>
{{Form::checkbox('active',1,Input::old('active') == '1' || !Input::old('active'))}}
Yes
</label>
</div>
<span class="help-block">This recurring transaction is actually active.</span>
</div>
</div>
</div>
</div>
<!-- select budget -->
<!-- select category -->
<!-- select beneficiary -->
<div class="form-group">
<label for="automatch" class="col-sm-4 control-label">Auto-match</label>
<div class="col-sm-8">
<div class="checkbox">
<!-- panel for options -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bolt"></i> Options
</div>
<div class="panel-body">
<div class="form-group">
<label for="default" class="col-sm-4 control-label">
Store
</label>
<div class="col-sm-8">
<div class="radio">
<label>
{{Form::radio('post_submit_action','store',true)}}
Store the recurring transaction
</label>
</div>
</div>
</div>
<div class="form-group">
<label for="validate_only" class="col-sm-4 control-label">
Validate only
</label>
<div class="col-sm-8">
<div class="radio">
<label>
{{Form::radio('post_submit_action','validate_only')}}
Only validate, do not save
</label>
</div>
</div>
</div>
<div class="form-group">
<label for="return_to_form" class="col-sm-4 control-label">
Return here
</label>
<div class="col-sm-8">
<div class="radio">
<label>
{{Form::checkbox('automatch',1,Input::old('automatch') == '1' || !Input::old('automatch'))}}
Yes
{{Form::radio('post_submit_action','create_another')}}
After storing, return here to create another one.
</label>
</div>
<span class="help-block">Firefly will automatically match transactions.</span>
</div>
</div>
<div class="form-group">
<label for="active" class="col-sm-4 control-label">Active</label>
<div class="col-sm-8">
<div class="checkbox">
<label>
{{Form::checkbox('active',1,Input::old('active') == '1' || !Input::old('active'))}}
Yes
</label>
</div>
<span class="help-block">This recurring transaction is actually active.</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-6">
<!-- add another after this one? -->
<div class="form-group">
<label for="create" class="col-sm-4 control-label">&nbsp;</label>
<div class="col-sm-8">
<div class="checkbox">
<label>
{{Form::checkbox('create',1,Input::old('create') == '1')}}
Create another (return to this form)
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button type="submit" class="btn btn-default btn-success">Create the recurring transaction</button>
</div>
</div>
</div>
</div>

View File

@ -1,20 +1,15 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">Use recurring transactions to track repeated expenses</p>
<p class="text-info">
Bla bla.
</p>
</div>
</div>
{{Form::open(['class' => 'form-horizontal','url' => route('recurring.update', $recurringTransaction->id)])}}
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-6">
<h4>Mandatory fields</h4>
<!-- panel for mandatory fields -->
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-exclamation-circle"></i> Mandatory fields
</div>
<div class="panel-body">
<!-- name -->
<div class="form-group">
<label for="name" class="col-sm-4 control-label">Name</label>
@ -23,8 +18,6 @@
value="{{{Input::old('name') ?: $recurringTransaction->name}}}" placeholder="Name">
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@else
<span class="help-block">For example: rent, gas, insurance</span>
@endif
</div>
</div>
@ -36,9 +29,6 @@
data-role="tagsinput">
@if($errors->has('match'))
<p class="text-danger">{{$errors->first('match')}}</p>
@else
<span class="help-block">For example: rent, [company name]. All matches need to
be present for the recurring transaction to be recognized. This field is not case-sensitive.</span>
@endif
</div>
</div>
@ -54,9 +44,6 @@
@if($errors->has('amount_min'))
<p class="text-danger">{{$errors->first('amount_min')}}</p>
@else
<span class="help-block">Firefly will only include transactions with a higher amount than this. If your rent
is usually around &euro; 500,-, enter <code>450</code> to be safe.</span>
@endif
</div>
</div>
@ -72,9 +59,6 @@
@if($errors->has('amount_max'))
<p class="text-danger">{{$errors->first('amount_max')}}</p>
@else
<span class="help-block">Firefly will only include transactions with a lower amount than this.
If your rent is usually around &euro; 500,-, enter <code>550</code> to be safe.</span>
@endif
</div>
</div>
@ -86,8 +70,6 @@
['class' => 'form-control']) }}
@if($errors->has('date'))
<p class="text-danger">{{$errors->first('date')}}</p>
@else
<span class="help-block">Select the next date you expect the transaction to occur.</span>
@endif
</div>
</div>
@ -99,16 +81,26 @@
['class' => 'form-control'])}}
@if($errors->has('repeat_freq'))
<p class="text-danger">{{$errors->first('repeat_freq')}}</p>
@else
<span class="help-block">Select the period over which this transaction repeats</span>
@endif
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-md-12 col-sm-6">
<h4>Optional fields</h4>
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Update recurring transasction
</button>
</p>
</div>
<div class="col-lg-6 col-md-12 col-sm-6">
<!-- panel for optional fields -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-smile-o"></i> Optional fields
</div>
<div class="panel-body">
<div class="form-group">
{{ Form::label('skip', 'Skip', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
@ -117,21 +109,10 @@
@if($errors->has('skip'))
<p class="text-danger">{{$errors->first('skip')}}</p>
@else
<span class="help-block">Make Firefly skip every <em>n</em> times. Fill in <code>2</code>, and Firefly
will match, skip, skip and match a transaction.</span>
@endif
</div>
</div>
<!-- select budget -->
<!-- select category -->
<!-- select beneficiary -->
<div class="form-group">
<label for="automatch" class="col-sm-4 control-label">Auto-match</label>
<div class="col-sm-8">
@ -159,25 +140,57 @@
<span class="help-block">This recurring transaction is actually active.</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-6">
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button type="submit" class="btn btn-default btn-success">Update the recurring transaction</button>
</div>
<!-- panel for options -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bolt"></i> Options
</div>
<div class="panel-body">
<div class="form-group">
<label for="default" class="col-sm-4 control-label">
Update
</label>
<div class="col-sm-8">
<div class="radio">
<label>
{{Form::radio('post_submit_action','store',true)}}
Update the recurring transaction
</label>
</div>
</div>
</div>
<div class="form-group">
<label for="validate_only" class="col-sm-4 control-label">
Validate only
</label>
<div class="col-sm-8">
<div class="radio">
<label>
{{Form::radio('post_submit_action','validate_only')}}
Only validate, do not save changes
</label>
</div>
</div>
</div>
<div class="form-group">
<label for="return_to_form" class="col-sm-4 control-label">
Return here
</label>
<div class="col-sm-8">
<div class="radio">
<label>
{{Form::radio('post_submit_action','return_to_edit')}}
After update, return here again.
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{Form::close()}}

View File

@ -1,73 +1,41 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">Use recurring transactions to track repeated withdrawals</p>
<p class="text-info">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.</p>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-sm-12 col-md-12">
<table class="table table-striped">
<tr>
<th>Name</th>
<th>Matches on</th>
<th>Amount between</th>
<th>Expected every</th>
<th>Next expected match</th>
<th>Auto-match</th>
<th>Active</th>
<th></th>
</tr>
@foreach($list as $entry)
<tr>
<td><a href="{{route('recurring.show',$entry->id)}}">{{{$entry->name}}}</a></td>
<td>
@foreach(explode(' ',$entry->match) as $word)
<span class="label label-info">{{{$word}}}</span>
@endforeach
</td>
<td>
{{mf($entry->amount_min)}} &ndash;
{{mf($entry->amount_max)}}
</td>
<td>
{{$entry->repeat_freq}}
</td>
<td>
{{$entry->next()->format('d-m-Y')}}
</td>
<td>
@if($entry->automatch)
<span class="glyphicon glyphicon-ok"></span>
@else
<span class="glyphicon glyphicon-remove"></span>
@endif
</td>
<td>
@if($entry->active)
<span class="glyphicon glyphicon-ok"></span>
@else
<span class="glyphicon glyphicon-remove"></span>
@endif
</td>
<td>
<div class="btn-group btn-group-xs">
<a href="{{route('recurring.edit',$entry->id)}}" class="btn btn-default"><span class="glyphicon glyphicon-pencil"></span></a>
<a href="{{route('recurring.delete',$entry->id)}}" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span></a>
</div>
</td>
</tr>
@endforeach
</table>
<p>
<a href="{{route('recurring.create')}}" class="btn btn-success btn-large">Create new recurring transaction</a>
</p>
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa {{$mainTitleIcon}}"></i> {{{$title}}}
</div>
<div class="panel-body">
<table class="table table-striped" id="recurringTable">
<thead>
<tr>
<th>name</th>
<th>match</th>
<th>amount_min</th>
<th>amount_max</th>
<th>date</th>
<th>active</th>
<th>automatch</th>
<th>repeat_freq</th>
<th>id</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
@stop
@section('scripts')
<script type="text/javascript">
var URL = '{{route('json.recurring')}}';
</script>
{{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

View File

@ -12,7 +12,7 @@
<tr>
<th>Date</th>
<th>Description</th>
<th data-dynatable-column="amount">Amount (&euro;)</th>
<th>Amount (&euro;)</th>
<th>From</th>
<th>To</th>
<th>ID</th>

View File

@ -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 '<a href="' + data.url + '" title="' + data.name + '">' + data.name + '</a>';
}
},
{
name: 'match',
data: 'match',
searchable: true,
title: 'Matches on',
render: function (data) {
var str = '';
for (x in data) {
str += '<span class="label label-info">' + data[x] + '</span> ';
}
return str;//return '<a href="' + data.url + '" title="' + data.name + '">' + data.name + '</a>';
}
},
{
name: 'amount_min',
data: 'amount_min',
searchable: false,
title: '&rarr;',
render: function (data) {
return '<span class="text-info">\u20AC ' + data.toFixed(2) + '</span>';
}
},
{
name: 'amount_max',
data: 'amount_max',
searchable: false,
title: '&larr;',
render: function (data) {
return '<span class="text-info">\u20AC ' + data.toFixed(2) + '</span>';
}
},
{
name: 'date',
data: 'date',
title: 'Expected on',
searchable: false
},
{
name: 'active',
data: 'active',
searchable: false,
sortable: false,
render: function(data) {
if(data == 1) {
return '<i class="fa fa-check fa-faw"></i>';
} else {
return '<i class="fa fa-remove fa-faw"></i>';
}
},
title: 'Is active?'
},
{
name: 'automatch',
data: 'automatch',
sortable: false,
searchable: false,
render: function(data) {
if(data == 1) {
return '<i class="fa fa-check fa-faw"></i>';
} else {
return '<i class="fa fa-remove fa-faw"></i>';
}
},
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 '<div class="btn-group btn-group-xs">' +
'<a class="btn btn-default btn-xs" href="' + data.edit + '">' +
'<span class="glyphicon glyphicon-pencil"</a>' +
'<a class="btn btn-danger btn-xs" href="' + data.delete + '">' +
'<span class="glyphicon glyphicon-trash"</a>' +
'</a></div>';
}
}
]
}
);
});

View File

@ -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 '<div class="btn-group btn-group-xs">' +
'<a class="btn btn-default btn-xs" href="' + data.edit + '">' +