Included bill controller.

This commit is contained in:
James Cole 2015-04-05 18:20:06 +02:00
parent ed8e392616
commit 9136e592d3
7 changed files with 506 additions and 123 deletions

View File

@ -1,6 +1,6 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Config;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BillFormRequest;
use FireflyIII\Models\Account;
@ -8,6 +8,7 @@ use FireflyIII\Models\AccountType;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Input;
use Redirect;
@ -37,15 +38,14 @@ class BillController extends Controller
*
* @return \Illuminate\Http\RedirectResponse
*/
public function add(Bill $bill)
public function add(Bill $bill, AccountRepositoryInterface $repository)
{
$matches = explode(',', $bill->match);
$description = [];
$expense = null;
// get users expense accounts:
$type = AccountType::where('type', 'Expense account')->first();
$accounts = Auth::user()->accounts()->where('account_type_id', $type->id)->get();
$accounts = $repository->getAccounts(Config::get('firefly.accountTypesByIdentifier.expense'), -1);
foreach ($matches as $match) {
$match = strtolower($match);
@ -81,7 +81,7 @@ class BillController extends Controller
*/
public function create()
{
$periods = \Config::get('firefly.periods_to_text');
$periods = Config::get('firefly.periods_to_text');
return view('bills.create')->with('periods', $periods)->with('subTitle', 'Create new');
}
@ -101,9 +101,10 @@ class BillController extends Controller
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Bill $bill)
public function destroy(Bill $bill, BillRepositoryInterface $repository)
{
$bill->delete();
$repository->destroy($bill);
Session::flash('success', 'The bill was deleted.');
return Redirect::route('bills.index');
@ -117,7 +118,7 @@ class BillController extends Controller
*/
public function edit(Bill $bill)
{
$periods = \Config::get('firefly.periods_to_text');
$periods = Config::get('firefly.periods_to_text');
return view('bills.edit')->with('periods', $periods)->with('bill', $bill)->with('subTitle', 'Edit "' . e($bill->name) . '"');
}
@ -129,15 +130,11 @@ class BillController extends Controller
*/
public function index(BillRepositoryInterface $repository)
{
$bills = Auth::user()->bills()->orderBy('name', 'ASC')->get();
$bills = $repository->getBills();
$bills->each(
function (Bill $bill) use ($repository) {
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$last = $bill->transactionjournals()->orderBy('date', 'DESC')->first();
$bill->lastFoundMatch = null;
if ($last) {
$bill->lastFoundMatch = $last->date;
}
$bill->lastFoundMatch = $repository->lastFoundMatch($bill);
}
);
@ -154,25 +151,15 @@ class BillController extends Controller
if (intval($bill->active) == 0) {
Session::flash('warning', 'Inactive bills cannot be scanned.');
return Redirect::intended('/');
return Redirect::to(URL::previous());
}
$set = \DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $bill->amount_min)->where('amount', '<=', $bill->amount_max)->get(
['transaction_journal_id']
);
$ids = [];
$journals = $repository->getPossiblyRelatedJournals($bill);
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$repository->scan($bill, $journal);
}
/** @var Transaction $entry */
foreach ($set as $entry) {
$ids[] = intval($entry->transaction_journal_id);
}
if (count($ids) > 0) {
$journals = Auth::user()->transactionjournals()->whereIn('id', $ids)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$repository->scan($bill, $journal);
}
}
Session::flash('success', 'Rescanned everything.');
@ -186,15 +173,10 @@ class BillController extends Controller
*/
public function show(Bill $bill, BillRepositoryInterface $repository)
{
$journals = $bill->transactionjournals()->withRelevantData()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get();
$journals = $repository->getJournals($bill);
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$hideBill = true;
return view('bills.show', compact('journals', 'hideBill', 'bill'))->with('subTitle', e($bill->name));
}

View File

@ -88,7 +88,7 @@ class TransactionController extends Controller
// put previous url in session
Session::put('transactions.delete.url', URL::previous());
return View::make('transactions.delete', compact('journal', 'subTitle'));
return view('transactions.delete', compact('journal', 'subTitle'));
}

View File

@ -59,14 +59,20 @@ class AccountRepository implements AccountRepositoryInterface
*/
public function getAccounts(array $types, $page)
{
$size = 50;
$offset = ($page - 1) * $size;
return Auth::user()->accounts()->with(
$query = Auth::user()->accounts()->with(
['accountmeta' => function (HasMany $query) {
$query->where('name', 'accountRole');
}]
)->accountTypeIn($types)->take($size)->offset($offset)->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
)->accountTypeIn($types)->orderBy('accounts.name', 'ASC');
if ($page == -1) {
return $query->get(['accounts.*']);
} else {
$size = 50;
$offset = ($page - 1) * $size;
return $query->take($size)->offset($offset)->get(['accounts.*']);
}
}
/**

View File

@ -2,9 +2,12 @@
namespace FireflyIII\Repositories\Bill;
use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use Log;
use Navigation;
@ -15,6 +18,62 @@ use Navigation;
*/
class BillRepository implements BillRepositoryInterface
{
/**
* @param Bill $bill
*
* @return mixed
*/
public function destroy(Bill $bill)
{
return $bill->delete();
}
/**
* @return Collection
*/
public function getBills()
{
return Auth::user()->bills()->orderBy('name', 'ASC')->get();
}
/**
* @param Bill $bill
*
* @return Collection
*/
public function getJournals(Bill $bill)
{
return $bill->transactionjournals()->withRelevantData()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get();
}
/**
* @param Bill $bill
*
* @return Collection
*/
public function getPossiblyRelatedJournals(Bill $bill)
{
$set = \DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $bill->amount_min)->where('amount', '<=', $bill->amount_max)->get(
['transaction_journal_id']
);
$ids = [];
/** @var Transaction $entry */
foreach ($set as $entry) {
$ids[] = intval($entry->transaction_journal_id);
}
$journals = new Collection;
if (count($ids) > 0) {
$journals = Auth::user()->transactionjournals()->whereIn('id', $ids)->get();
}
return $journals;
}
/**
* Every bill repeats itself weekly, monthly or yearly (or whatever). This method takes a date-range (usually the view-range of Firefly itself)
* and returns date ranges that fall within the given range; those ranges are the bills expected. When a bill is due on the 14th of the month and
@ -58,6 +117,21 @@ class BillRepository implements BillRepositoryInterface
return $validRanges;
}
/**
* @param Bill $bill
*
* @return Carbon|null
*/
public function lastFoundMatch(Bill $bill)
{
$last = $bill->transactionjournals()->orderBy('date', 'DESC')->first();
if ($last) {
return $last->date;
}
return null;
}
/**
* @param Bill $bill
*

View File

@ -5,6 +5,7 @@ namespace FireflyIII\Repositories\Bill;
use Carbon\Carbon;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
/**
* Interface BillRepositoryInterface
@ -17,9 +18,28 @@ interface BillRepositoryInterface
/**
* @param Bill $bill
*
* @return Carbon|null
* @return mixed
*/
public function nextExpectedMatch(Bill $bill);
public function destroy(Bill $bill);
/**
* @return Collection
*/
public function getBills();
/**
* @param Bill $bill
*
* @return Collection
*/
public function getPossiblyRelatedJournals(Bill $bill);
/**
* @param Bill $bill
*
* @return Collection
*/
public function getJournals(Bill $bill);
/**
* Every bill repeats itself weekly, monthly or yearly (or whatever). This method takes a date-range (usually the view-range of Firefly itself)
@ -34,6 +54,28 @@ interface BillRepositoryInterface
*/
public function getRanges(Bill $bill, Carbon $start, Carbon $end);
/**
* @param Bill $bill
*
* @return Carbon|null
*/
public function lastFoundMatch(Bill $bill);
/**
* @param Bill $bill
*
* @return Carbon|null
*/
public function nextExpectedMatch(Bill $bill);
/**
* @param Bill $bill
* @param TransactionJournal $journal
*
* @return bool
*/
public function scan(Bill $bill, TransactionJournal $journal);
/**
* @param array $data
*
@ -49,12 +91,4 @@ interface BillRepositoryInterface
*/
public function update(Bill $bill, array $data);
/**
* @param Bill $bill
* @param TransactionJournal $journal
*
* @return bool
*/
public function scan(Bill $bill, TransactionJournal $journal);
}

View File

@ -0,0 +1,249 @@
<?php
use Carbon\Carbon;
use Illuminate\Support\Collection;
use League\FactoryMuffin\Facade as FactoryMuffin;
/**
* Class BillControllerTest
*/
class BillControllerTest extends TestCase
{
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
*/
public function setUp()
{
parent::setUp();
}
/**
* This method is called before the first test of this test class is run.
*
* @since Method available since Release 3.4.0
*/
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
}
/**
* Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed.
*/
public function tearDown()
{
parent::tearDown();
}
public function testAdd()
{
// create a bill:
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$this->be($bill->user);
// create an expense account:
$expense = FactoryMuffin::create('FireflyIII\Models\Account');
// fix the name of the expense account to match one of the words
// in the bill:
$words = explode(',', $bill->match);
$word = $words[1];
$expense->name = $word;
$expense->save();
// mock repository:
$repository = $this->mock('FireflyIII\Repositories\Account\AccountRepositoryInterface');
$repository->shouldReceive('getAccounts')->andReturn([$expense]);
// go!
$this->call('GET', '/bills/add/' . $bill->id);
$this->assertSessionHas('preFilled');
$this->assertRedirectedToRoute('transactions.create', ['withdrawal']);
}
public function testCreate()
{
// go!
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$this->be($bill->user);
// CURRENCY:
$currency = FactoryMuffin::create('FireflyIII\Models\TransactionCurrency');
Amount::shouldReceive('getDefaultCurrency')->once()->andReturn($currency);
Amount::shouldReceive('getAllCurrencies')->once()->andReturn([$currency]);
$this->call('GET', '/bills/create');
$this->assertViewHas('subTitle', 'Create new');
$this->assertResponseOk();
}
public function testDelete()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$this->be($bill->user);
$this->call('GET', '/bills/delete/' . $bill->id);
$this->assertViewHas('subTitle', 'Delete "' . e($bill->name) . '"');
$this->assertResponseOk();
}
public function testDestroy()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$this->be($bill->user);
$this->call('POST', '/bills/destroy/' . $bill->id, ['_token' => 'replaceMe']);
$this->assertSessionHas('success', 'The bill was deleted.');
$this->assertResponseStatus(302);
}
public function testEdit()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$this->be($bill->user);
// CURRENCY:
$currency = FactoryMuffin::create('FireflyIII\Models\TransactionCurrency');
Amount::shouldReceive('getDefaultCurrency')->once()->andReturn($currency);
Amount::shouldReceive('getAllCurrencies')->once()->andReturn([$currency]);
$this->call('GET', '/bills/edit/' . $bill->id);
$this->assertViewHas('subTitle', 'Edit "' . e($bill->name) . '"');
$this->assertResponseOk();
}
public function testIndex()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$this->be($bill->user);
$collection = new Collection;
$collection->push($bill);
Amount::shouldReceive('format')->andReturn('XX');
$repository = $this->mock('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$repository->shouldReceive('getBills')->once()->andReturn($collection);
$repository->shouldReceive('nextExpectedMatch')->with($bill)->andReturn(new Carbon);
$repository->shouldReceive('lastFoundMatch')->with($bill)->andReturn(new Carbon);
$this->call('GET', '/bills');
$this->assertResponseOk();
}
public function testRescan()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$journal = FactoryMuffin::create('FireflyIII\Models\TransactionJournal');
$collection = new Collection;
$this->be($bill->user);
$collection->push($journal);
$repository = $this->mock('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$repository->shouldReceive('getPossiblyRelatedJournals')->once()->andReturn($collection);
$repository->shouldReceive('scan');
$this->call('GET', '/bills/rescan/' . $bill->id);
$this->assertResponseStatus(302);
$this->assertSessionHas('success', 'Rescanned everything.');
}
public function testRescanInactive()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$bill->active = 0;
$bill->save();
$this->be($bill->user);
$this->call('GET', '/bills/rescan/' . $bill->id);
$this->assertResponseStatus(302);
$this->assertSessionHas('warning', 'Inactive bills cannot be scanned.');
}
public function testShow()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$journal = FactoryMuffin::create('FireflyIII\Models\TransactionJournal');
$collection = new Collection;
$bill->save();
$this->be($bill->user);
$collection->push($journal);
$repository = $this->mock('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$repository->shouldReceive('getJournals')->once()->andReturn($collection);
$repository->shouldReceive('nextExpectedMatch')->once()->andReturn(new Carbon);
Amount::shouldReceive('format')->andReturn('XX');
Amount::shouldReceive('getCurrencyCode')->andReturn('XX');
$this->call('GET', '/bills/show/' . $bill->id);
}
public function testStore()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$repository = $this->mock('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$request = $this->mock('FireflyIII\Http\Requests\BillFormRequest');
$this->be($bill->user);
$request->shouldReceive('getBillData')->once()->andReturn([]);
$repository->shouldReceive('store')->with([])->andReturn($bill);
$this->call('POST', '/bills/store', ['_token' => 'replaceMe']);
$this->assertResponseStatus(302);
$this->assertSessionHas('success', 'Bill "' . e($bill->name) . '" stored.');
}
public function testStoreAndRedirect()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$repository = $this->mock('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$request = $this->mock('FireflyIII\Http\Requests\BillFormRequest');
$this->be($bill->user);
$request->shouldReceive('getBillData')->once()->andReturn([]);
$repository->shouldReceive('store')->with([])->andReturn($bill);
$this->call('POST', '/bills/store', ['_token' => 'replaceMe', 'create_another' => 1]);
$this->assertResponseStatus(302);
$this->assertSessionHas('success', 'Bill "' . e($bill->name) . '" stored.');
}
public function testUpdate()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$repository = $this->mock('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$request = $this->mock('FireflyIII\Http\Requests\BillFormRequest');
$this->be($bill->user);
$request->shouldReceive('getBillData')->once()->andReturn([]);
$repository->shouldReceive('update')->andReturn($bill);
$this->call('POST', '/bills/update/' . $bill->id, ['_token' => 'replaceMe']);
$this->assertResponseStatus(302);
$this->assertSessionHas('success', 'Bill "' . e($bill->name) . '" updated.');
}
public function testUpdateAndRedirect()
{
$bill = FactoryMuffin::create('FireflyIII\Models\Bill');
$repository = $this->mock('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$request = $this->mock('FireflyIII\Http\Requests\BillFormRequest');
$this->be($bill->user);
$request->shouldReceive('getBillData')->once()->andReturn([]);
$repository->shouldReceive('update')->andReturn($bill);
$this->call('POST', '/bills/update/' . $bill->id, ['_token' => 'replaceMe', 'return_to_edit' => 1]);
$this->assertResponseStatus(302);
}
}

View File

@ -27,109 +27,147 @@ if (!class_exists('RandomString')) {
}
}
FactoryMuffin::define(
'FireflyIII\Models\Account', [
'user_id' => 'factory|FireflyIII\User',
'account_type_id' => 'factory|FireflyIII\Models\AccountType',
'name' => 'word',
'active' => 'boolean',
'encrypted' => 'boolean',
'virtual_balance' => 0
]
'FireflyIII\Models\Bill',
[
'user_id' => 'factory|FireflyIII\User',
'name' => 'sentence',
'match' => function () {
$words = [];
for ($i = 0; $i < 3; $i++) {
$words[] = RandomString::generateRandomString(5);
}
return join(',', $words);
},
'amount_min' => 10,
'amount_max' => 20,
'date' => 'date',
'active' => 1,
'automatch' => 1,
'repeat_freq' => 'monthly',
'skip' => 0,
'name_encrypted' => 1,
'match_encrypted' => 1,
]
);
FactoryMuffin::define(
'FireflyIII\Models\Budget', [
'user_id' => 'factory|FireflyIII\User',
'name' => 'sentence',
'active' => 'boolean',
'encrypted' => 1,
]
'FireflyIII\Models\Account',
[
'user_id' => 'factory|FireflyIII\User',
'account_type_id' => 'factory|FireflyIII\Models\AccountType',
'name' => 'word',
'active' => 'boolean',
'encrypted' => 'boolean',
'virtual_balance' => 0
]
);
FactoryMuffin::define(
'FireflyIII\Models\LimitRepetition', [
'budget_limit_id' => 'factory|FireflyIII\Models\BudgetLimit',
'startdate' => 'date',
'enddate' => 'date',
'amount' => 'integer',
]
'FireflyIII\Models\Budget',
[
'user_id' => 'factory|FireflyIII\User',
'name' => 'sentence',
'active' => 'boolean',
'encrypted' => 1,
]
);
FactoryMuffin::define(
'FireflyIII\Models\BudgetLimit', [
'budget_id' => 'factory|FireflyIII\Models\Budget',
'startdate' => 'date',
'amount' => 'integer',
'repeats' => 'false',
'repeat_freq' => 'monthly',
'FireflyIII\Models\LimitRepetition',
[
'budget_limit_id' => 'factory|FireflyIII\Models\BudgetLimit',
'startdate' => 'date',
'enddate' => 'date',
'amount' => 'integer',
]
);
]
FactoryMuffin::define(
'FireflyIII\Models\BudgetLimit',
[
'budget_id' => 'factory|FireflyIII\Models\Budget',
'startdate' => 'date',
'amount' => 'integer',
'repeats' => 'false',
'repeat_freq' => 'monthly',
]
);
FactoryMuffin::define(
'FireflyIII\Models\Preference', [
'name' => 'word',
'data' => 'sentence',
'user_id' => 'factory|FireflyIII\User',
]
'FireflyIII\Models\Preference',
[
'name' => 'word',
'data' => 'sentence',
'user_id' => 'factory|FireflyIII\User',
]
);
FactoryMuffin::define(
'FireflyIII\Models\AccountType', [
'type' => function () {
$types = ['Expense account', 'Revenue account', 'Asset account'];
$count = DB::table('account_types')->count();
'FireflyIII\Models\AccountType',
[
'type' => function () {
$types = ['Expense account', 'Revenue account', 'Asset account'];
$count = DB::table('account_types')->count();
return $types[$count];
},
'editable' => 1,
]
return $types[$count];
},
'editable' => 1,
]
);
FactoryMuffin::define(
'FireflyIII\Models\TransactionCurrency', [
'code' => function () {
return RandomString::generateRandomString(3);
},
'symbol' => function () {
return RandomString::generateRandomString(1);
},
'name' => 'word'
]
'FireflyIII\Models\TransactionCurrency',
[
'code' => function () {
return RandomString::generateRandomString(3);
},
'symbol' => function () {
return RandomString::generateRandomString(1);
},
'name' => 'word'
]
);
FactoryMuffin::define(
'FireflyIII\User', [
'email' => 'email',
'password' => bcrypt('james'),
]
'FireflyIII\User',
[
'email' => 'email',
'password' => bcrypt('james'),
]
);
FactoryMuffin::define(
'FireflyIII\Models\Transaction', [
'transaction_journal_id' => 'factory|FireflyIII\Models\TransactionJournal',
'amount' => 'integer',
'account_id' => 'factory|FireflyIII\Models\Account'
]
'FireflyIII\Models\Transaction',
[
'transaction_journal_id' => 'factory|FireflyIII\Models\TransactionJournal',
'amount' => 'integer',
'account_id' => 'factory|FireflyIII\Models\Account'
]
);
FactoryMuffin::define(
'FireflyIII\Models\TransactionType', [
'type' => 'word',
]
'FireflyIII\Models\TransactionType',
[
'type' => 'word',
]
);
FactoryMuffin::define(
'FireflyIII\Models\TransactionJournal', [
'user_id' => 'factory|FireflyIII\User',
'transaction_type_id' => 'factory|FireflyIII\Models\TransactionType',
'transaction_currency_id' => 'factory|FireflyIII\Models\TransactionCurrency',
'description' => 'sentence',
'completed' => '1',
'date' => 'date',
'encrypted' => '1',
'order' => '0',
]
'FireflyIII\Models\TransactionJournal',
[
'user_id' => 'factory|FireflyIII\User',
'transaction_type_id' => 'factory|FireflyIII\Models\TransactionType',
'transaction_currency_id' => 'factory|FireflyIII\Models\TransactionCurrency',
'description' => 'sentence',
'completed' => '1',
'date' => 'date',
'encrypted' => '1',
'order' => '0',
]
);