mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-04 03:43:07 +00:00
All kinds of new stuff: finished most of the importing routines, extended the database (single table inheritance) and expanded some other stuff.
This commit is contained in:
@@ -11,6 +11,9 @@ class HomeController extends BaseController {
|
||||
public function index()
|
||||
{
|
||||
$count = $this->accounts->count();
|
||||
|
||||
// build the home screen:
|
||||
|
||||
return View::make('index')->with('count',$count);
|
||||
}
|
||||
}
|
@@ -19,6 +19,7 @@ class MigrationController extends BaseController
|
||||
|
||||
public function postIndex()
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (Input::hasFile('exportFile')) {
|
||||
|
||||
// get content:
|
||||
@@ -31,487 +32,10 @@ class MigrationController extends BaseController
|
||||
return View::make('error')->with('message', 'Invalid JSON content.');
|
||||
}
|
||||
$this->migration->migrate();
|
||||
return 'busy!';
|
||||
}
|
||||
|
||||
// then, start migrating!
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
// // map old and new id's.
|
||||
// $map = [
|
||||
// 'accounts' => [],
|
||||
// 'beneficiaries' => [],
|
||||
// ];
|
||||
//
|
||||
// // get the account types we need
|
||||
// $beneficiaryAT = AccountType::where('description', 'Beneficiary account')->first();
|
||||
//
|
||||
// // save all accounts:
|
||||
// foreach ($JSON->accounts as $entry) {
|
||||
// // create account:
|
||||
// if ($entry->openingbalance == 0) {
|
||||
// $account = $this->accounts->store(['name' => $entry->name]);
|
||||
// } else {
|
||||
// $account = $this->accounts->storeWithInitialBalance(
|
||||
// ['name' => $entry->name],
|
||||
// new Carbon($entry->openingbalancedate),
|
||||
// floatval($entry->openingbalance)
|
||||
// );
|
||||
// }
|
||||
// $map['accounts'][$entry->id] = $account->id;
|
||||
// }
|
||||
// unset($entry);
|
||||
//
|
||||
// // save all components:
|
||||
// foreach ($JSON->components as $entry) {
|
||||
// switch ($entry->type->type) {
|
||||
// case 'beneficiary':
|
||||
// // create new account for beneficiary:
|
||||
// $account = $this->accounts->store(['name' => $entry->name]);
|
||||
// $map['beneficiaries'][$entry->id] = $account;
|
||||
// break;
|
||||
// case 'category':
|
||||
// // create new component for category:
|
||||
//// $account = $this->accounts->store(['name' => $entry->name]);
|
||||
//// $map['beneficiaries'][$entry->id] = $account;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// unset($JSON->accounts);
|
||||
//
|
||||
//
|
||||
// var_dump($JSON);
|
||||
// var_dump($map);
|
||||
//
|
||||
// exit;
|
||||
// }
|
||||
//
|
||||
return View::make('error')->with('message', 'No file found.');
|
||||
}
|
||||
|
||||
/*
|
||||
public function index()
|
||||
{
|
||||
// check if database connection is present.
|
||||
$configValue = Config::get('database.connections.old-firefly');
|
||||
if (is_null($configValue)) {
|
||||
return View::make('migrate.index');
|
||||
}
|
||||
|
||||
// try to connect to it:
|
||||
$error = '';
|
||||
try {
|
||||
DB::connection('old-firefly')->select('SELECT * from `users`;');
|
||||
} catch (PDOException $e) {
|
||||
$error = $e->getMessage();
|
||||
return View::make('migrate.index')->with('error', $error);
|
||||
}
|
||||
|
||||
return Redirect::route('migrate.upload');
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function selectUser()
|
||||
{
|
||||
$oldUsers = [];
|
||||
try {
|
||||
$oldUsers = DB::connection('old-firefly')->select('SELECT * from `users`;');
|
||||
} catch (PDOException $e) {
|
||||
$error = $e->getMessage();
|
||||
return View::make('migrate.index')->with('error', $error);
|
||||
}
|
||||
|
||||
return View::make('migrate.select-user')->with('oldUsers', $oldUsers);
|
||||
}
|
||||
|
||||
public function postSelectUser()
|
||||
{
|
||||
$userID = Input::get('user');
|
||||
$count = DB::connection('old-firefly')->table('users')->where('id', $userID)->count();
|
||||
if ($count == 1) {
|
||||
return Redirect::route('migrate.migrate', $userID);
|
||||
return Redirect::route('index');
|
||||
} else {
|
||||
return View::make('error')->with('message', 'No such user!');
|
||||
return View::make('error')->with('message', 'No file selected');
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
public function migrate($userID)
|
||||
{
|
||||
// import the data.
|
||||
$user = Auth::user();
|
||||
$previousUser = DB::connection('old-firefly')->table('users')->where('id', $userID)->first();
|
||||
|
||||
|
||||
// current user already migrated?
|
||||
if ($user->migrated) {
|
||||
return View::make('error')->with('message', 'This user has already been migrated.');
|
||||
}
|
||||
|
||||
// a map to keep the connections intact:
|
||||
$map = [
|
||||
'accounts' => [],
|
||||
'beneficiaries' => [],
|
||||
'categories' => [],
|
||||
'budgets' => [],
|
||||
];
|
||||
|
||||
// messages to show in result screen:
|
||||
$messages = [];
|
||||
|
||||
// grab account types:
|
||||
$defaultAT = AccountType::where('description', 'Default account')->first();
|
||||
$initialBalanceAT = AccountType::where('description', 'Initial balance account')->first();
|
||||
$beneficiaryAT = AccountType::where('description', 'Beneficiary account')->first();
|
||||
|
||||
// grab transaction types:
|
||||
$initialBalanceTT = TransactionType::where('type', 'Opening balance')->first();
|
||||
$depositTT = TransactionType::where('type', 'Deposit')->first();
|
||||
$withdrawalTT = TransactionType::where('type', 'Withdrawal')->first();
|
||||
$transferTT = TransactionType::where('type', 'Transfer')->first();
|
||||
|
||||
// grab currency:
|
||||
$currency = TransactionCurrency::where('code', 'EUR')->first();
|
||||
|
||||
// grab component types:
|
||||
$categoryType = ComponentType::where('type', 'category')->first();
|
||||
$budgetType = ComponentType::where('type', 'budget')->first();
|
||||
|
||||
// create a special cash account for this user:
|
||||
$cashAccount = new Account;
|
||||
$cashAccount->name = 'Cash account';
|
||||
$cashAccount->active = true;
|
||||
$cashAccount->accountType()->associate($beneficiaryAT);
|
||||
$cashAccount->user()->associate($user);
|
||||
$cashAccount->save();
|
||||
$messages[] = 'Created "cash" account.';
|
||||
|
||||
// get the old accounts:
|
||||
$accounts = DB::connection('old-firefly')->table('accounts')->where('user_id', $previousUser->id)->get();
|
||||
foreach ($accounts as $account) {
|
||||
|
||||
// already had one?
|
||||
$existing = Account::where('name', $account->name)->where('account_type_id', $defaultAT->id)->where(
|
||||
'user_id', $previousUser->id
|
||||
)->first();
|
||||
|
||||
if (!is_null($existing)) {
|
||||
$map['accounts'][$account->id] = $existing;
|
||||
$messages[] = 'Skipped creating account ' . $account->name . ' because it already exists.';
|
||||
continue;
|
||||
}
|
||||
unset($existing);
|
||||
// create account:
|
||||
$current = new Account;
|
||||
$current->name = $account->name;
|
||||
$current->active = true;
|
||||
$current->accountType()->associate($defaultAT);
|
||||
$current->user()->associate($user);
|
||||
$current->save();
|
||||
|
||||
// map and information
|
||||
$map['accounts'][$account->id] = $current;
|
||||
$messages[] = 'Account "' . $current->name . '" recreated.';
|
||||
|
||||
// recreate opening balance, if relevant:
|
||||
if (floatval($account->openingbalance) != 0) {
|
||||
|
||||
// now create another account, and create the first transfer indicating
|
||||
$initial = new Account;
|
||||
$initial->name = $account->name . ' initial balance';
|
||||
$initial->accountType()->associate($initialBalanceAT);
|
||||
$initial->active = 1;
|
||||
$initial->user()->associate($user);
|
||||
$initial->save();
|
||||
|
||||
// create a journal, and two transfers:
|
||||
$journal = new TransactionJournal;
|
||||
$journal->transactionType()->associate($initialBalanceTT);
|
||||
$journal->transactionCurrency()->associate($currency);
|
||||
$journal->description = $account->name . ' opening balance';
|
||||
$journal->date = $account->openingbalancedate;
|
||||
$journal->save();
|
||||
|
||||
// coming from a virtual account is:
|
||||
$credit = new Transaction;
|
||||
$credit->account()->associate($current);
|
||||
$credit->transactionJournal()->associate($journal);
|
||||
$credit->description = null;
|
||||
$credit->amount = $account->openingbalance;
|
||||
$credit->save();
|
||||
|
||||
// transfer into this new account
|
||||
$debet = new Transaction;
|
||||
$debet->account()->associate($initial);
|
||||
$debet->transactionJournal()->associate($journal);
|
||||
$debet->description = null;
|
||||
$debet->amount = $account->openingbalance * -1;
|
||||
$debet->save();
|
||||
$messages[]
|
||||
= 'Saved initial balance for ' . $current->name . ' (' . mf($account->openingbalance, true) . ')';
|
||||
unset($initial, $journal, $credit, $debet);
|
||||
}
|
||||
unset($current);
|
||||
}
|
||||
|
||||
// save all old components
|
||||
$components = DB::connection('old-firefly')->table('components')->leftJoin(
|
||||
'types', 'types.id', '=', 'components.type_id'
|
||||
)->where('user_id', $previousUser->id)->get(['components.*', 'types.type']);
|
||||
|
||||
foreach ($components as $component) {
|
||||
$id = $component->id;
|
||||
switch ($component->type) {
|
||||
case 'beneficiary':
|
||||
if (!isset($map['beneficiaries'][$id])) {
|
||||
// if it exists, skip:
|
||||
$existing = Account::where('name', $component->name)->where('user_id', $user->id)->where(
|
||||
'account_type_id', $beneficiaryAT->id
|
||||
)->first();
|
||||
if (!is_null($existing)) {
|
||||
$map['beneficiaries'][$id] = $existing;
|
||||
$messages[]
|
||||
= 'Skipped creating beneficiary "' . $component->name . '" because it already exists.';
|
||||
unset($existing);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// new account for this beneficiary
|
||||
$beneficiary = new Account;
|
||||
$beneficiary->name = $component->name;
|
||||
$beneficiary->accountType()->associate($beneficiaryAT);
|
||||
$beneficiary->user()->associate($user);
|
||||
$beneficiary->active = 1;
|
||||
$beneficiary->save();
|
||||
$map['beneficiaries'][$id] = $beneficiary;
|
||||
$messages[] = 'Recreated beneficiary "' . $beneficiary->name . '".';
|
||||
unset($beneficiary);
|
||||
}
|
||||
break;
|
||||
case 'category':
|
||||
// if it exists, skip:
|
||||
$existing = Component::where('name', $component->name)->where('user_id', $user->id)->where(
|
||||
'component_type_id', $categoryType->id
|
||||
)->first();
|
||||
if (!is_null($existing)) {
|
||||
$map['categories'][$id] = $existing;
|
||||
$messages[] = 'Skipped creating category "' . $component->name . '" because it already exists.';
|
||||
unset($existing);
|
||||
continue;
|
||||
}
|
||||
|
||||
// new component for this category:
|
||||
$category = new Component;
|
||||
$category->componentType()->associate($categoryType);
|
||||
$category->name = $component->name;
|
||||
$category->user()->associate($user);
|
||||
$category->save();
|
||||
$map['categories'][$id] = $category;
|
||||
$messages[] = 'Recreated category "' . $category->name . '".';
|
||||
unset($category);
|
||||
break;
|
||||
case 'budget':
|
||||
// if it exists, skip:
|
||||
$existing = Component::where('name', $component->name)->where('user_id', $user->id)->where(
|
||||
'component_type_id', $budgetType->id
|
||||
)->first();
|
||||
if (!is_null($existing)) {
|
||||
$map['budgets'][$id] = $existing;
|
||||
$messages[] = 'Skipped creating budget "' . $component->name . '" because it already exists.';
|
||||
unset($existing);
|
||||
continue;
|
||||
}
|
||||
|
||||
// new component for this budget:
|
||||
$budget = new Component;
|
||||
$budget->componentType()->associate($budgetType);
|
||||
$budget->user()->associate($user);
|
||||
$budget->name = $component->name;
|
||||
$budget->save();
|
||||
$map['budgets'][$id] = $budget;
|
||||
$messages[] = 'Recreated budget "' . $budget->name . '".';
|
||||
unset($budget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// grab all old transactions:
|
||||
$transactions = DB::connection('old-firefly')->table('transactions')->where('user_id', $user->id)->orderBy(
|
||||
'date'
|
||||
)
|
||||
->get();
|
||||
|
||||
foreach ($transactions as $transaction) {
|
||||
$accountID = $transaction->account_id;
|
||||
|
||||
// grab the components for this transaction
|
||||
$components = DB::connection('old-firefly')->table('component_transaction')
|
||||
->leftJoin('components', 'components.id', '=', 'component_transaction.component_id')
|
||||
->leftJoin('types', 'types.id', '=', 'components.type_id')
|
||||
->where('transaction_id', $transaction->id)
|
||||
->get(['components.id', 'types.type']);
|
||||
|
||||
$beneficiary = null;
|
||||
$budget = null;
|
||||
$category = null;
|
||||
|
||||
// loop components, get the right id's:
|
||||
foreach ($components as $component) {
|
||||
$id = $component->id;
|
||||
switch ($component->type) {
|
||||
case 'beneficiary':
|
||||
$beneficiary = $map['beneficiaries'][$id];
|
||||
break;
|
||||
case 'budget':
|
||||
$budget = $map['budgets'][$id];
|
||||
break;
|
||||
case 'category':
|
||||
$category = $map['categories'][$id];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// get a fall back for empty beneficiaries:
|
||||
if (is_null($beneficiary)) {
|
||||
$beneficiary = $cashAccount;
|
||||
}
|
||||
|
||||
// create the transaction journal:
|
||||
$journal = new TransactionJournal;
|
||||
if ($transaction->amount < 0) {
|
||||
$journal->transactionType()->associate($withdrawalTT);
|
||||
} else {
|
||||
$journal->transactionType()->associate($depositTT);
|
||||
}
|
||||
$journal->transactionCurrency()->associate($currency);
|
||||
$journal->description = $transaction->description;
|
||||
$journal->date = $transaction->date;
|
||||
$journal->save();
|
||||
|
||||
// create two transactions:
|
||||
|
||||
if ($transaction->amount < 0) {
|
||||
// credit the beneficiary
|
||||
$credit = new Transaction;
|
||||
$credit->account()->associate($beneficiary);
|
||||
$credit->transactionJournal()->associate($journal);
|
||||
$credit->description = null;
|
||||
$credit->amount = floatval($transaction->amount) * -1;
|
||||
$credit->save();
|
||||
// add budget / category:
|
||||
if (!is_null($budget)) {
|
||||
$credit->components()->attach($budget);
|
||||
}
|
||||
if (!is_null($category)) {
|
||||
$credit->components()->attach($category);
|
||||
}
|
||||
|
||||
// debet ourselves:
|
||||
$debet = new Transaction;
|
||||
$debet->account()->associate($map['accounts'][$accountID]);
|
||||
$debet->transactionJournal()->associate($journal);
|
||||
$debet->description = null;
|
||||
$debet->amount = floatval($transaction->amount);
|
||||
$debet->save();
|
||||
if (!is_null($budget)) {
|
||||
$debet->components()->attach($budget);
|
||||
}
|
||||
if (!is_null($category)) {
|
||||
$debet->components()->attach($category);
|
||||
}
|
||||
$messages[]
|
||||
= 'Recreated expense "' . $transaction->description . '" (' . mf($transaction->amount, true) . ')';
|
||||
|
||||
} else {
|
||||
$credit = new Transaction;
|
||||
$credit->account()->associate($map['accounts'][$accountID]);
|
||||
$credit->transactionJournal()->associate($journal);
|
||||
$credit->description = null;
|
||||
$credit->amount = floatval($transaction->amount);
|
||||
$credit->save();
|
||||
if (!is_null($budget)) {
|
||||
$credit->components()->attach($budget);
|
||||
}
|
||||
if (!is_null($category)) {
|
||||
$credit->components()->attach($category);
|
||||
}
|
||||
|
||||
// from whoever!
|
||||
$debet = new Transaction;
|
||||
$debet->account()->associate($beneficiary);
|
||||
$debet->transactionJournal()->associate($journal);
|
||||
$debet->description = null;
|
||||
$debet->amount = floatval($transaction->amount) * -1;
|
||||
$debet->save();
|
||||
if (!is_null($budget)) {
|
||||
$debet->components()->attach($budget);
|
||||
}
|
||||
if (!is_null($category)) {
|
||||
$debet->components()->attach($category);
|
||||
}
|
||||
$messages[]
|
||||
= 'Recreated income "' . $transaction->description . '" (' . mf($transaction->amount, true) . ')';
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
unset($transaction);
|
||||
|
||||
// recreate the transfers:
|
||||
$transfers = DB::connection('old-firefly')->table('transfers')->where('user_id', $user->id)->get();
|
||||
foreach ($transfers as $transfer) {
|
||||
|
||||
// if it exists already, we don't need to recreate it:
|
||||
$existingJournal = TransactionJournal::where('description', $transfer->description)->where(
|
||||
'date', $transfer->date
|
||||
)->where('transaction_type_id', $transferTT->id)->first();
|
||||
if (!is_null($existingJournal)) {
|
||||
// grab transaction from journal to make sure:
|
||||
$firstTransaction = $existingJournal->transactions()->first();
|
||||
if ($firstTransaction->amount == $transfer->amount
|
||||
|| $firstTransaction->amount == $transfer->amount * -1
|
||||
) {
|
||||
// probably the same:
|
||||
$messages[] = 'Skipped transfer "' . $transfer->description . '" because it already exists.';
|
||||
unset($existingJournal, $firstTransaction);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$fromID = $transfer->accountfrom_id;
|
||||
$toID = $transfer->accountto_id;
|
||||
// create a journak:
|
||||
$journal = new TransactionJournal;
|
||||
$journal->transactionType()->associate($transferTT);
|
||||
$journal->transactionCurrency()->associate($currency);
|
||||
$journal->description = $transfer->description;
|
||||
$journal->date = $transfer->date;
|
||||
$journal->save();
|
||||
|
||||
// from account (debet) to another account (credit)
|
||||
$debet = new Transaction;
|
||||
$debet->account()->associate($map['accounts'][$fromID]);
|
||||
$debet->transactionJournal()->associate($journal);
|
||||
$debet->description = null;
|
||||
$debet->amount = floatval($transfer->amount * -1);
|
||||
$debet->save();
|
||||
|
||||
// to another account!
|
||||
$credit = new Transaction;
|
||||
$credit->account()->associate($map['accounts'][$toID]);
|
||||
$credit->transactionJournal()->associate($journal);
|
||||
$credit->description = null;
|
||||
$credit->amount = floatval($transfer->amount);
|
||||
$credit->save();
|
||||
$messages[] = 'Recreated transfer "' . $transfer->description . '" (' . mf($transfer->amount) . ')';
|
||||
}
|
||||
$user->migrated = true;
|
||||
$user->save();
|
||||
|
||||
return View::make('migrate.result')->with('messages', $messages);
|
||||
}
|
||||
*/
|
||||
}
|
@@ -26,6 +26,17 @@ class UserController extends BaseController
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
// $user = User::find(1);
|
||||
// Auth::login($user);
|
||||
// /** @var Firefly\Helper\Migration\MigrationHelperInterface $migration */
|
||||
// $migration = App::make('Firefly\Helper\Migration\MigrationHelperInterface');
|
||||
// $file = '/Library/WebServer/Documents/projects/firefly-iii/app/storage/firefly-export-2014-07-03.json';
|
||||
// $migration->loadFile($file);
|
||||
// if($migration->validFile()) {
|
||||
// $migration->migrate();
|
||||
// }
|
||||
|
||||
|
||||
return View::make('user.login');
|
||||
}
|
||||
|
||||
|
@@ -18,18 +18,13 @@ class CreateComponentsTable extends Migration {
|
||||
$table->timestamps();
|
||||
$table->string('name',50);
|
||||
$table->integer('user_id')->unsigned();
|
||||
$table->integer('component_type_id')->unsigned();
|
||||
$table->string('class',20);
|
||||
|
||||
// connect components to users
|
||||
$table->foreign('user_id')
|
||||
->references('id')->on('users')
|
||||
->onDelete('cascade');
|
||||
|
||||
// connect components to component types
|
||||
$table->foreign('component_type_id')
|
||||
->references('id')->on('component_types')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class CreatePiggybanksTable extends Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create(
|
||||
'piggybanks', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->timestamps();
|
||||
$table->integer('account_id')->unsigned();
|
||||
$table->date('targetdate')->nullable();
|
||||
$table->string('name', 500);
|
||||
$table->decimal('amount', 10, 2);
|
||||
$table->decimal('target', 10, 2)->nullable();
|
||||
$table->integer('order')->unsigned();
|
||||
|
||||
// connect account to piggybank.
|
||||
$table->foreign('account_id')
|
||||
->references('id')->on('accounts')
|
||||
->onDelete('cascade');
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('piggybanks');
|
||||
}
|
||||
|
||||
}
|
@@ -3,7 +3,7 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateComponentTypesTable extends Migration {
|
||||
class CreateRecurringTransactionsTable extends Migration {
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
@@ -12,11 +12,10 @@ class CreateComponentTypesTable extends Migration {
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('component_types', function(Blueprint $table)
|
||||
Schema::create('recurringtransactions', function(Blueprint $table)
|
||||
{
|
||||
$table->increments('id');
|
||||
$table->timestamps();
|
||||
$table->string('type',50);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,7 +26,7 @@ class CreateComponentTypesTable extends Migration {
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('component_types');
|
||||
Schema::drop('recurringtransactions');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateComponentTransactionJournalTable extends Migration {
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('component_transaction_journal', function(Blueprint $table)
|
||||
{
|
||||
$table->increments('id');
|
||||
$table->integer('component_id')->unsigned();
|
||||
$table->integer('transaction_journal_id')->unsigned();
|
||||
|
||||
// link components with component_id
|
||||
$table->foreign('component_id')
|
||||
->references('id')->on('components')
|
||||
->onDelete('cascade');
|
||||
|
||||
// link transaction journals with transaction_journal_id
|
||||
$table->foreign('transaction_journal_id')
|
||||
->references('id')->on('transaction_journals')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('component_transaction_journal');
|
||||
}
|
||||
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
class ComponentTypeSeeder extends Seeder
|
||||
{
|
||||
|
||||
public function run()
|
||||
{
|
||||
|
||||
DB::table('component_types')->delete();
|
||||
|
||||
ComponentType::create(['type' => 'category']);
|
||||
ComponentType::create(['type' => 'budget']);
|
||||
}
|
||||
|
||||
}
|
@@ -15,7 +15,6 @@ class DatabaseSeeder extends Seeder
|
||||
$this->call('AccountTypeSeeder');
|
||||
$this->call('TransactionCurrencySeeder');
|
||||
$this->call('TransactionTypeSeeder');
|
||||
$this->call('ComponentTypeSeeder');
|
||||
$this->call('DefaultUserSeeder');
|
||||
}
|
||||
|
||||
|
78
app/lib/Firefly/Database/SingleTableInheritanceEntity.php
Normal file
78
app/lib/Firefly/Database/SingleTableInheritanceEntity.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Firefly\Database;
|
||||
|
||||
|
||||
abstract class SingleTableInheritanceEntity extends \Elegant
|
||||
{
|
||||
/**
|
||||
* The field that stores the subclass
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $subclassField = null;
|
||||
/**
|
||||
* must be overridden and set to true in subclasses
|
||||
* @var bool
|
||||
*/
|
||||
protected $isSubclass = false;
|
||||
|
||||
public function newFromBuilder($attributes = array())
|
||||
{
|
||||
$instance = $this->mapData((array)$attributes)->newInstance(array(), true);
|
||||
$instance->setRawAttributes((array)$attributes, true);
|
||||
return $instance;
|
||||
}
|
||||
|
||||
// if no subclass is defined, function as normal
|
||||
|
||||
public function mapData(array $attributes)
|
||||
{
|
||||
if (!$this->subclassField) {
|
||||
return $this->newInstance();
|
||||
}
|
||||
|
||||
return new $attributes[$this->subclassField];
|
||||
}
|
||||
|
||||
// instead of using $this->newInstance(), call
|
||||
// newInstance() on the object from mapData
|
||||
|
||||
public function newQuery($excludeDeleted = true)
|
||||
{
|
||||
// If using Laravel 4.0.x then use the following commented version of this command
|
||||
// $builder = new Builder($this->newBaseQueryBuilder());
|
||||
// newEloquentBuilder() was added in 4.1
|
||||
$builder = $this->newEloquentBuilder($this->newBaseQueryBuilder());
|
||||
|
||||
// Once we have the query builders, we will set the model instances so the
|
||||
// builder can easily access any information it may need from the model
|
||||
// while it is constructing and executing various queries against it.
|
||||
$builder->setModel($this)->with($this->with);
|
||||
|
||||
if ($excludeDeleted && $this->softDelete) {
|
||||
$builder->whereNull($this->getQualifiedDeletedAtColumn());
|
||||
}
|
||||
|
||||
if ($this->subclassField && $this->isSubclass()) {
|
||||
$builder->where($this->subclassField, '=', get_class($this));
|
||||
}
|
||||
|
||||
return $builder;
|
||||
}
|
||||
|
||||
public function isSubclass()
|
||||
{
|
||||
return $this->isSubclass;
|
||||
}
|
||||
|
||||
// ensure that the subclass field is assigned on save
|
||||
|
||||
public function save(array $options = array())
|
||||
{
|
||||
if ($this->subclassField) {
|
||||
$this->attributes[$this->subclassField] = get_class($this);
|
||||
}
|
||||
return parent::save($options);
|
||||
}
|
||||
}
|
@@ -49,9 +49,18 @@ class MigrationHelper implements MigrationHelperInterface
|
||||
\DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// create cash account:
|
||||
$this->_createCashAccount();
|
||||
|
||||
$this->_importAccounts();
|
||||
$this->_importComponents();
|
||||
$this->_importPiggybanks();
|
||||
//$this->_importPiggybanks();
|
||||
|
||||
// create transactions:
|
||||
$this->_importTransactions();
|
||||
|
||||
// create transfers:
|
||||
$this->_importTransfers();
|
||||
|
||||
|
||||
} catch (\Firefly\Exception\FireflyException $e) {
|
||||
@@ -66,6 +75,15 @@ class MigrationHelper implements MigrationHelperInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function _createCashAccount()
|
||||
{
|
||||
$cashAT = \AccountType::where('description', 'Cash account')->first();
|
||||
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
|
||||
$accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
|
||||
$cash = $accounts->store(['name' => 'Cash account', 'account_type' => $cashAT, 'active' => false]);
|
||||
$this->map['cash'] = $cash;
|
||||
}
|
||||
|
||||
protected function _importAccounts()
|
||||
{
|
||||
|
||||
@@ -83,7 +101,7 @@ class MigrationHelper implements MigrationHelperInterface
|
||||
floatval($entry->openingbalance)
|
||||
);
|
||||
}
|
||||
$this->map['accounts'][$entry->id] = $account->id;
|
||||
$this->map['accounts'][$entry->id] = $account;
|
||||
\Log::info('Imported account "' . $entry->name . '" with balance ' . $entry->openingbalance);
|
||||
}
|
||||
}
|
||||
@@ -91,38 +109,25 @@ class MigrationHelper implements MigrationHelperInterface
|
||||
protected function _importComponents()
|
||||
{
|
||||
$beneficiaryAT = \AccountType::where('description', 'Beneficiary account')->first();
|
||||
$budgetType = \ComponentType::where('type', 'budget')->first();
|
||||
$categoryType = \ComponentType::where('type', 'category')->first();
|
||||
foreach ($this->JSON->components as $entry) {
|
||||
switch ($entry->type->type) {
|
||||
case 'beneficiary':
|
||||
$beneficiary = $this->_importBeneficiary($entry, $beneficiaryAT);
|
||||
$this->map['accounts'][$entry->id] = $beneficiary->id;
|
||||
$this->map['accounts'][$entry->id] = $beneficiary;
|
||||
break;
|
||||
case 'category':
|
||||
$component = $this->_importComponent($entry, $categoryType);
|
||||
$this->map['components'][$entry->id] = $component->id;
|
||||
$component = $this->_importCategory($entry);
|
||||
$this->map['categories'][$entry->id] = $component;
|
||||
break;
|
||||
case 'budget':
|
||||
$component = $this->_importComponent($entry, $budgetType);
|
||||
$this->map['components'][$entry->id] = $component->id;
|
||||
$component = $this->_importBudget($entry);
|
||||
$this->map['budgets'][$entry->id] = $component;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function _importPiggybanks() {
|
||||
|
||||
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
|
||||
$accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
|
||||
|
||||
// get type for piggy:
|
||||
$piggyAT = \AccountType::where('description', 'Piggy bank')->first();
|
||||
foreach($this->JSON->piggybanks as $piggyBank) {
|
||||
}
|
||||
}
|
||||
|
||||
protected function _importBeneficiary($component, \AccountType $beneficiaryAT)
|
||||
{
|
||||
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
|
||||
@@ -135,10 +140,98 @@ class MigrationHelper implements MigrationHelperInterface
|
||||
);
|
||||
}
|
||||
|
||||
protected function _importComponent($component, \ComponentType $type)
|
||||
protected function _importCategory($component)
|
||||
{
|
||||
/** @var \Firefly\Storage\Component\ComponentRepositoryInterface $components */
|
||||
$components = \App::make('Firefly\Storage\Component\ComponentRepositoryInterface');
|
||||
return $components->store(['name' => $component->name, 'component_type' => $type]);
|
||||
return $components->store(['name' => $component->name, 'class' => 'Category']);
|
||||
}
|
||||
|
||||
protected function _importBudget($component)
|
||||
{
|
||||
/** @var \Firefly\Storage\Component\ComponentRepositoryInterface $components */
|
||||
$components = \App::make('Firefly\Storage\Component\ComponentRepositoryInterface');
|
||||
return $components->store(['name' => $component->name, 'class' => 'Budget']);
|
||||
}
|
||||
|
||||
protected function _importTransactions()
|
||||
{
|
||||
|
||||
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */
|
||||
$journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
|
||||
|
||||
// loop component_transaction to find beneficiaries, categories and budgets:
|
||||
$beneficiaries = [];
|
||||
$categories = [];
|
||||
$budgets = [];
|
||||
foreach ($this->JSON->component_transaction as $entry) {
|
||||
// beneficiaries
|
||||
if (isset($this->map['accounts'][$entry->component_id])) {
|
||||
$beneficiaries[$entry->transaction_id] = $this->map['accounts'][$entry->component_id];
|
||||
}
|
||||
|
||||
// categories
|
||||
if (isset($this->map['categories'][$entry->component_id])) {
|
||||
$categories[$entry->transaction_id] = $this->map['categories'][$entry->component_id];
|
||||
}
|
||||
|
||||
// budgets:
|
||||
if (isset($this->map['budgets'][$entry->component_id])) {
|
||||
$budgets[$entry->transaction_id] = $this->map['budgets'][$entry->component_id];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->JSON->transactions as $entry) {
|
||||
$id = $entry->id;
|
||||
|
||||
// to properly save the amount, do it times -1:
|
||||
$amount = $entry->amount * -1;
|
||||
|
||||
/** @var \Account $fromAccount */
|
||||
$fromAccount = isset($this->map['accounts'][$entry->account_id])
|
||||
? $this->map['accounts'][$entry->account_id] : false;
|
||||
|
||||
/** @var \Account $toAccount */
|
||||
$toAccount = isset($beneficiaries[$entry->id]) ? $beneficiaries[$entry->id] : $this->map['cash'];
|
||||
|
||||
$date = new \Carbon\Carbon($entry->date);
|
||||
$journal = $journals->createSimpleJournal($fromAccount, $toAccount, $entry->description, $amount, $date);
|
||||
|
||||
// save budgets and categories, on the journal
|
||||
if(isset($budgets[$entry->id])) {
|
||||
$budget = $budgets[$entry->id];
|
||||
$journal->budgets()->save($budget);
|
||||
}
|
||||
if(isset($categories[$entry->id])) {
|
||||
$category = $categories[$entry->id];
|
||||
$journal->categories()->save($category);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function _importTransfers()
|
||||
{
|
||||
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */
|
||||
$journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
|
||||
|
||||
foreach ($this->JSON->transfers as $entry) {
|
||||
$id = $entry->id;
|
||||
|
||||
// to properly save the amount, do it times 1 (?):
|
||||
$amount = $entry->amount * -1;
|
||||
|
||||
/** @var \Account $fromAccount */
|
||||
$fromAccount = isset($this->map['accounts'][$entry->accountfrom_id])
|
||||
? $this->map['accounts'][$entry->accountto_id] : false;
|
||||
|
||||
/** @var \Account $toAccount */
|
||||
$toAccount = isset($this->map['accounts'][$entry->accountto_id])
|
||||
? $this->map['accounts'][$entry->accountfrom_id] : false;
|
||||
|
||||
$date = new \Carbon\Carbon($entry->date);
|
||||
$journals->createSimpleJournal($fromAccount, $toAccount, $entry->description, $amount, $date);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,6 +9,8 @@ interface AccountRepositoryInterface
|
||||
|
||||
public function count();
|
||||
|
||||
public function get();
|
||||
|
||||
public function store($data);
|
||||
public function storeWithInitialBalance($data,\Carbon\Carbon $date, $amount = 0);
|
||||
|
||||
|
@@ -13,6 +13,10 @@ class EloquentAccountRepository implements AccountRepositoryInterface
|
||||
{
|
||||
}
|
||||
|
||||
public function get() {
|
||||
return \Auth::user()->accounts()->get();
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return \Auth::user()->accounts()->count();
|
||||
|
@@ -20,18 +20,26 @@ class EloquentComponentRepository implements ComponentRepositoryInterface
|
||||
|
||||
public function store($data)
|
||||
{
|
||||
if (!isset($data['component_type'])) {
|
||||
throw new \Firefly\Exception\FireflyException('No component type present.');
|
||||
if (!isset($data['class'])) {
|
||||
throw new \Firefly\Exception\FireflyException('No class type present.');
|
||||
}
|
||||
switch ($data['class']) {
|
||||
case 'Budget':
|
||||
$component = new \Budget;
|
||||
break;
|
||||
case 'Category':
|
||||
$component = new \Category;
|
||||
break;
|
||||
|
||||
}
|
||||
$component = new \Component;
|
||||
$component->componentType()->associate($data['component_type']);
|
||||
$component->name = $data['name'];
|
||||
$component->user()->associate(\Auth::user());
|
||||
try {
|
||||
$component->save();
|
||||
} catch (\Illuminate\Database\QueryException $e) {
|
||||
\Log::error('DB ERROR: ' . $e->getMessage());
|
||||
throw new \Firefly\Exception\FireflyException('Could not save component ' . $data['name']);
|
||||
throw new \Firefly\Exception\FireflyException('Could not save component ' . $data['name'] . ' of type'
|
||||
. $data['class']);
|
||||
}
|
||||
|
||||
return $component;
|
||||
|
@@ -15,7 +15,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
|
||||
public function createSimpleJournal(\Account $from, \Account $to, $description, $amount, \Carbon\Carbon $date)
|
||||
{
|
||||
|
||||
|
||||
\Log::debug('Creating tranaction "'.$description.'".');
|
||||
/*
|
||||
* We're building this thinking the money goes from A to B.
|
||||
* If the amount is negative however, the money still goes
|
||||
@@ -67,6 +67,10 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
|
||||
$journalType = \TransactionType::where('type', 'Deposit')->first();
|
||||
break;
|
||||
}
|
||||
|
||||
// some debug information:
|
||||
\Log::debug($journalType->type.': AccountFrom "'.$from->name.'" will gain/lose '.$amountFrom.' and AccountTo "'.$to->name.'" will gain/lose '.$amountTo);
|
||||
|
||||
if (is_null($journalType)) {
|
||||
\Log::error('Could not figure out transacion type!');
|
||||
throw new \Firefly\Exception\FireflyException('Could not figure out transaction type.');
|
||||
@@ -122,10 +126,6 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
|
||||
|
||||
$journal->completed = true;
|
||||
$journal->save();
|
||||
return;
|
||||
|
||||
|
||||
echo 'saved!';
|
||||
|
||||
return $journal;
|
||||
}
|
||||
}
|
14
app/models/Budget.php
Normal file
14
app/models/Budget.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: sander
|
||||
* Date: 05/07/14
|
||||
* Time: 18:24
|
||||
*/
|
||||
|
||||
class Budget extends Component {
|
||||
protected $isSubclass = true;
|
||||
|
||||
|
||||
|
||||
}
|
6
app/models/Category.php
Normal file
6
app/models/Category.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
class Category extends Component
|
||||
{
|
||||
protected $isSubclass = true;
|
||||
}
|
@@ -1,8 +1,12 @@
|
||||
<?php
|
||||
|
||||
|
||||
class Component extends Elegant
|
||||
class Component extends Firefly\Database\SingleTableInheritanceEntity
|
||||
{
|
||||
|
||||
protected $table = 'components';
|
||||
protected $subclassField = 'class';
|
||||
|
||||
public static $rules
|
||||
= [
|
||||
'user_id' => 'exists:users,id|required',
|
||||
@@ -11,15 +15,14 @@ class Component extends Elegant
|
||||
];
|
||||
|
||||
|
||||
public function componentType()
|
||||
{
|
||||
return $this->belongsTo('ComponentType');
|
||||
}
|
||||
|
||||
public function transactions()
|
||||
{
|
||||
return $this->belongsToMany('Transaction');
|
||||
}
|
||||
public function transactionjournals()
|
||||
{
|
||||
return $this->belongsToMany('TransactionJournal');
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
|
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
class ComponentType extends Eloquent
|
||||
{
|
||||
public function components()
|
||||
{
|
||||
return $this->hasMany('Component');
|
||||
}
|
||||
|
||||
}
|
@@ -26,4 +26,13 @@ class Transaction extends Elegant
|
||||
{
|
||||
return $this->belongsToMany('Component');
|
||||
}
|
||||
|
||||
public function budgets()
|
||||
{
|
||||
return $this->belongsToMany('Budget');
|
||||
}
|
||||
public function categories()
|
||||
{
|
||||
return $this->belongsToMany('Category');
|
||||
}
|
||||
}
|
@@ -28,6 +28,21 @@ class TransactionJournal extends Elegant
|
||||
return $this->hasMany('Transaction');
|
||||
}
|
||||
|
||||
public function components()
|
||||
{
|
||||
return $this->belongsToMany('Component');
|
||||
}
|
||||
|
||||
public function budgets()
|
||||
{
|
||||
return $this->belongsToMany('Budget','component_transaction_journal','transaction_journal_id','component_id');
|
||||
}
|
||||
|
||||
public function categories()
|
||||
{
|
||||
return $this->belongsToMany('Category','component_transaction_journal','transaction_journal_id','component_id');
|
||||
}
|
||||
|
||||
public function getDates()
|
||||
{
|
||||
return array('created_at', 'updated_at', 'date');
|
||||
|
@@ -15,10 +15,13 @@ Route::group(['before' => 'auth'], function () {
|
||||
|
||||
// migration controller
|
||||
Route::get('/migrate', ['uses' => 'MigrationController@index', 'as' => 'migrate']);
|
||||
|
||||
// account controller:
|
||||
Route::get('/accounts/create', ['uses' => 'AccountController@create', 'as' => 'accounts.create']);
|
||||
}
|
||||
);
|
||||
|
||||
// protected + csrf routes
|
||||
// protected + csrf routes (POST)
|
||||
Route::group(['before' => 'csrf|auth'], function () {
|
||||
// profile controller
|
||||
Route::post('/profile/change-password', ['uses' => 'ProfileController@postChangePassword']);
|
||||
@@ -26,6 +29,9 @@ Route::group(['before' => 'csrf|auth'], function () {
|
||||
// migration controller
|
||||
Route::post('/migrate', ['uses' => 'MigrationController@postIndex']);
|
||||
|
||||
// account controller:
|
||||
Route::get('/accounts/store', ['uses' => 'AccountController@store', 'as' => 'accounts.store']);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
19
app/tests/controllers/AccountControllerTest.php
Normal file
19
app/tests/controllers/AccountControllerTest.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
class AccountControllerTest extends TestCase {
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testCreate()
|
||||
{
|
||||
// mock:
|
||||
View::shouldReceive('make')->with('accounts.create');
|
||||
|
||||
// call
|
||||
$this->call('GET', '/accounts/create');
|
||||
|
||||
// test
|
||||
$this->assertResponseOk();
|
||||
}
|
||||
}
|
21
app/tests/controllers/MigrationControllerTest.php
Normal file
21
app/tests/controllers/MigrationControllerTest.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
class MigrationControllerTest extends TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testIndex()
|
||||
{
|
||||
// mock:
|
||||
View::shouldReceive('make')->with('migrate.index');
|
||||
|
||||
// call
|
||||
$this->call('GET', '/migrate');
|
||||
|
||||
// test
|
||||
$this->assertResponseOk();
|
||||
}
|
||||
}
|
@@ -211,34 +211,6 @@ class UserControllerTest extends TestCase
|
||||
|
||||
}
|
||||
|
||||
public function testVerification()
|
||||
{
|
||||
|
||||
$repository = $this->mock('Firefly\Storage\User\UserRepositoryInterface');
|
||||
$email = $this->mock('Firefly\Helper\Email\EmailHelper');
|
||||
|
||||
$repository->shouldReceive('findByVerification')->once()->andReturn(new User);
|
||||
$email->shouldReceive('sendPasswordMail')->once()->andReturn(true);
|
||||
|
||||
// test
|
||||
$crawler = $this->client->request('GET', '/verify/blabla');
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
$this->assertCount(1, $crawler->filter('h1:contains("Password sent")'));
|
||||
|
||||
}
|
||||
|
||||
public function testVerificationFails()
|
||||
{
|
||||
$repository = $this->mock('Firefly\Storage\User\UserRepositoryInterface');
|
||||
$repository->shouldReceive('findByVerification')->once()->andReturn(null);
|
||||
|
||||
// test
|
||||
$crawler = $this->client->request('GET', '/verify/blabla');
|
||||
$this->assertTrue($this->client->getResponse()->isOk());
|
||||
$this->assertCount(1, $crawler->filter('h1:contains("Error")'));
|
||||
$this->assertViewHas('message');
|
||||
}
|
||||
|
||||
public function testReset()
|
||||
{
|
||||
|
||||
|
Reference in New Issue
Block a user