mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-02-25 18:45:27 -06:00 
			
		
		
		
	Finally implemented repeated expenses properly. [skip ci]
This commit is contained in:
		| @@ -32,6 +32,11 @@ class HelpController extends BaseController | ||||
|         } catch (ErrorException $e) { | ||||
|             $content = '<p>There is no help for this route.</p>'; | ||||
|         } | ||||
|         if (strlen($content) == 0) { | ||||
|             $content = '<p>There is no help for this route.</p>'; | ||||
|         } | ||||
|         \Log::debug('Found help for ' . $route); | ||||
|         \Log::debug('Help text length is ' . strlen($content)); | ||||
|         $helpText  = \Michelf\Markdown::defaultTransform($content); | ||||
|         $helpTitle = $route; | ||||
|  | ||||
|   | ||||
| @@ -35,11 +35,68 @@ class RepeatedExpenseController extends BaseController | ||||
|  | ||||
|         $accounts = FFForm::makeSelectList($acct->getAssetAccounts()); | ||||
|  | ||||
|         return View::make('repeatedexpense.create', compact('accounts', 'periods'))->with('subTitle', 'Create new repeated expense')->with( | ||||
|         return View::make('repeatedExpense.create', compact('accounts', 'periods'))->with('subTitle', 'Create new repeated expense')->with( | ||||
|             'subTitleIcon', 'fa-plus' | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param PiggyBank $repeatedExpense | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function delete(PiggyBank $repeatedExpense) | ||||
|     { | ||||
|         $subTitle = 'Delete "' . e($repeatedExpense->name) . '"'; | ||||
|  | ||||
|         return View::make('repeatedExpense.delete', compact('repeatedExpense', 'subTitle')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param PiggyBank $repeatedExpense | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function destroy(PiggyBank $repeatedExpense) | ||||
|     { | ||||
|  | ||||
|         Session::flash('success', 'Repeated expense "' . e($repeatedExpense->name) . '" deleted.'); | ||||
|         $this->_repository->destroy($repeatedExpense); | ||||
|  | ||||
|         return Redirect::route('repeated.index'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param PiggyBank $repeatedExpense | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function edit(PiggyBank $repeatedExpense) | ||||
|     { | ||||
|  | ||||
|         /** @var \FireflyIII\Database\Account\Account $acct */ | ||||
|         $acct = App::make('FireflyIII\Database\Account\Account'); | ||||
|  | ||||
|         $periods      = Config::get('firefly.piggy_bank_periods'); | ||||
|         $accounts     = FFForm::makeSelectList($acct->getAssetAccounts()); | ||||
|         $subTitle     = 'Edit repeated expense "' . e($repeatedExpense->name) . '"'; | ||||
|         $subTitleIcon = 'fa-pencil'; | ||||
|  | ||||
|         /* | ||||
|          * Flash some data to fill the form. | ||||
|          */ | ||||
|         $preFilled = ['name'         => $repeatedExpense->name, | ||||
|                       'account_id'   => $repeatedExpense->account_id, | ||||
|                       'targetamount' => $repeatedExpense->targetamount, | ||||
|                       'targetdate'   => $repeatedExpense->targetdate->format('Y-m-d'), | ||||
|                       'reminder'     => $repeatedExpense->reminder, | ||||
|                       'remind_me'    => intval($repeatedExpense->remind_me) == 1 || !is_null($repeatedExpense->reminder) ? true : false | ||||
|         ]; | ||||
|         Session::flash('preFilled', $preFilled); | ||||
|  | ||||
|         return View::make('repeatedExpense.edit', compact('subTitle', 'subTitleIcon', 'repeatedExpense', 'accounts', 'periods', 'preFilled')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
| @@ -48,50 +105,51 @@ class RepeatedExpenseController extends BaseController | ||||
|  | ||||
|         $subTitle = 'Overview'; | ||||
|  | ||||
|         /** @var \FireflyIII\Database\PiggyBank\RepeatedExpense $repository */ | ||||
|         $repository = App::make('FireflyIII\Database\PiggyBank\RepeatedExpense'); | ||||
|  | ||||
|         $expenses = $repository->get(); | ||||
|         $expenses = $this->_repository->get(); | ||||
|         $expenses->each( | ||||
|             function (PiggyBank $piggyBank) use ($repository) { | ||||
|             function (PiggyBank $piggyBank) { | ||||
|                 $piggyBank->currentRelevantRep(); | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return View::make('repeatedexpense.index', compact('expenses', 'subTitle')); | ||||
|         return View::make('repeatedExpense.index', compact('expenses', 'subTitle')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param PiggyBank $piggyBank | ||||
|      * @param PiggyBank $repeatedExpense | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function show(PiggyBank $piggyBank) | ||||
|     public function show(PiggyBank $repeatedExpense) | ||||
|     { | ||||
|         $subTitle = $piggyBank->name; | ||||
|         $subTitle = $repeatedExpense->name; | ||||
|         $today    = Carbon::now(); | ||||
|  | ||||
|         /** @var \FireflyIII\Database\PiggyBank\RepeatedExpense $repository */ | ||||
|         $repository = App::make('FireflyIII\Database\PiggyBank\RepeatedExpense'); | ||||
|  | ||||
|         $repetitions = $piggyBank->piggyBankRepetitions()->get(); | ||||
|         $repetitions = $repeatedExpense->piggyBankRepetitions()->get(); | ||||
|         $repetitions->each( | ||||
|             function (PiggyBankRepetition $repetition) use ($repository) { | ||||
|                 $repetition->bars = $repository->calculateParts($repetition); | ||||
|             function (PiggyBankRepetition $repetition) { | ||||
|                 $repetition->bars = $this->_repository->calculateParts($repetition); | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return View::make('repeatedexpense.show', compact('repetitions', 'piggyBank', 'today', 'subTitle')); | ||||
|         return View::make('repeatedExpense.show', compact('repetitions', 'repeatedExpense', 'today', 'subTitle')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return $this | ||||
|      * @throws FireflyException | ||||
|      * | ||||
|      */ | ||||
|     public function store() | ||||
|     { | ||||
|         $data            = Input::except('_token'); | ||||
|         $data['repeats'] = 1; | ||||
|         $data                  = Input::all(); | ||||
|         $data['repeats']       = 1; | ||||
|         $data['user_id']       = Auth::user()->id; | ||||
|         $targetDate            = new Carbon($data['targetdate']); | ||||
|         $startDate             = \DateKit::subtractPeriod($targetDate, $data['rep_length']); | ||||
|         $data['startdate']     = $startDate->format('Y-m-d'); | ||||
|         $data['targetdate']    = $targetDate->format('Y-m-d'); | ||||
|         $data['reminder_skip'] = 0; | ||||
|         $data['remind_me']     = isset($data['remind_me']) ? 1 : 0; | ||||
|         $data['order']         = 0; | ||||
|  | ||||
|         // always validate: | ||||
|         $messages = $this->_repository->validate($data); | ||||
| @@ -101,25 +159,70 @@ class RepeatedExpenseController extends BaseController | ||||
|         Session::flash('successes', $messages['successes']); | ||||
|         Session::flash('errors', $messages['errors']); | ||||
|         if ($messages['errors']->count() > 0) { | ||||
|             Session::flash('error', 'Could not validate repeated expense: ' . $messages['errors']->first()); | ||||
|             Session::flash('error', 'Could not store repeated expense: ' . $messages['errors']->first()); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // return to create screen: | ||||
|         if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) { | ||||
|             return Redirect::route('repeated.create')->withInput(); | ||||
|         } | ||||
|  | ||||
|         // store: | ||||
|         $this->_repository->store($data); | ||||
|         Session::flash('success', 'Budget "' . e($data['name']) . '" stored.'); | ||||
|         $piggyBank = $this->_repository->store($data); | ||||
|         Event::fire('piggy_bank.store', [$piggyBank]); // new and used. | ||||
|         Session::flash('success', 'Piggy bank "' . e($data['name']) . '" stored.'); | ||||
|         if ($data['post_submit_action'] == 'store') { | ||||
|             return Redirect::route('repeated.index'); | ||||
|         } | ||||
|  | ||||
|         // create another. | ||||
|         if ($data['post_submit_action'] == 'create_another') { | ||||
|             return Redirect::route('repeated.create')->withInput(); | ||||
|         return Redirect::route('repeated.create')->withInput(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param PiggyBank $repeatedExpense | ||||
|      * | ||||
|      * @return $this | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function update(PiggyBank $repeatedExpense) | ||||
|     { | ||||
|  | ||||
|         $data                  = Input::except('_token'); | ||||
|         $data['rep_every']     = 0; | ||||
|         $data['reminder_skip'] = 0; | ||||
|         $data['order']         = 0; | ||||
|         $data['repeats']       = 1; | ||||
|         $data['remind_me']     = isset($data['remind_me']) ? 1 : 0; | ||||
|         $data['user_id']       = Auth::user()->id; | ||||
|  | ||||
|         // always validate: | ||||
|         $messages = $this->_repository->validate($data); | ||||
|  | ||||
|         // flash messages: | ||||
|         Session::flash('warnings', $messages['warnings']); | ||||
|         Session::flash('successes', $messages['successes']); | ||||
|         Session::flash('errors', $messages['errors']); | ||||
|         if ($messages['errors']->count() > 0) { | ||||
|             Session::flash('error', 'Could not update repeated expense: ' . $messages['errors']->first()); | ||||
|         } | ||||
|  | ||||
|         return Redirect::route('repeated.index'); | ||||
|         // return to update screen: | ||||
|         if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) { | ||||
|             return Redirect::route('repeated.edit', $repeatedExpense->id)->withInput(); | ||||
|         } | ||||
|  | ||||
|         // update | ||||
|         $this->_repository->update($repeatedExpense, $data); | ||||
|         Session::flash('success', 'Repeated expense "' . e($data['name']) . '" updated.'); | ||||
|  | ||||
|         // go back to list | ||||
|         if ($data['post_submit_action'] == 'update') { | ||||
|             return Redirect::route('repeated.index'); | ||||
|         } | ||||
|  | ||||
|         // go back to update screen. | ||||
|         return Redirect::route('repeated.edit', $repeatedExpense->id)->withInput(['post_submit_action' => 'return_to_edit']); | ||||
|  | ||||
|     } | ||||
| }  | ||||
| @@ -4,223 +4,18 @@ namespace FireflyIII\Database\PiggyBank; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Database\CommonDatabaseCalls; | ||||
| use FireflyIII\Database\CUD; | ||||
| use FireflyIII\Database\SwitchUser; | ||||
| use FireflyIII\Exception\FireflyException; | ||||
| use FireflyIII\Exception\NotImplementedException; | ||||
| use Illuminate\Database\Eloquent\Model as Eloquent; | ||||
| use Illuminate\Support\Collection; | ||||
| use Illuminate\Support\MessageBag; | ||||
|  | ||||
| /** | ||||
|  * Class PiggyBank | ||||
|  * | ||||
|  * @package FireflyIII\Database | ||||
|  */ | ||||
| class PiggyBank implements CUD, CommonDatabaseCalls, PiggyBankInterface | ||||
| class PiggyBank extends PiggyBankShared implements CUD, CommonDatabaseCalls, PiggyBankInterface | ||||
| { | ||||
|     use SwitchUser; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->setUser(\Auth::user()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Eloquent $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function destroy(Eloquent $model) | ||||
|     { | ||||
|         $model->delete(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return \Eloquent | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function store(array $data) | ||||
|     { | ||||
|         if (!isset($data['remind_me']) || (isset($data['remind_me']) && $data['remind_me'] == 0)) { | ||||
|             $data['reminder'] = null; | ||||
|         } | ||||
|         $piggyBank = new \PiggyBank($data); | ||||
|         $piggyBank->save(); | ||||
|  | ||||
|         return $piggyBank; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Eloquent $model | ||||
|      * @param array    $data | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function update(Eloquent $model, array $data) | ||||
|     { | ||||
|         /** @var \PiggyBank $model */ | ||||
|         $model->name          = $data['name']; | ||||
|         $model->account_id    = intval($data['account_id']); | ||||
|         $model->targetamount  = floatval($data['targetamount']); | ||||
|         $model->targetdate    = isset($data['targetdate']) && $data['targetdate'] != '' ? $data['targetdate'] : null; | ||||
|         $model->rep_every     = intval($data['rep_every']); | ||||
|         $model->reminder_skip = intval($data['reminder_skip']); | ||||
|         $model->order         = intval($data['order']); | ||||
|         $model->remind_me     = intval($data['remind_me']); | ||||
|         $model->reminder      = isset($data['reminder']) ? $data['reminder'] : 'month'; | ||||
|  | ||||
|         if ($model->remind_me == 0) { | ||||
|             $model->reminder = null; | ||||
|         } | ||||
|  | ||||
|         $model->save(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Validates an array. Returns an array containing MessageBags | ||||
|      * errors/warnings/successes. | ||||
|      * | ||||
|      * Ignore PHPMD rules because Laravel 5.0 will make this method superfluous anyway. | ||||
|      * | ||||
|      * @param array $model | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function validate(array $model) | ||||
|     { | ||||
|         $warnings  = new MessageBag; | ||||
|         $successes = new MessageBag; | ||||
|         $errors    = new MessageBag; | ||||
|  | ||||
|         /* | ||||
|          * Name validation: | ||||
|          */ | ||||
|         if (!isset($model['name'])) { | ||||
|             $errors->add('name', 'Name is mandatory'); | ||||
|         } | ||||
|  | ||||
|         if (isset($model['name']) && strlen($model['name']) == 0) { | ||||
|             $errors->add('name', 'Name is too short'); | ||||
|         } | ||||
|         if (isset($model['name']) && strlen($model['name']) > 100) { | ||||
|             $errors->add('name', 'Name is too long'); | ||||
|         } | ||||
|  | ||||
|         if (intval($model['account_id']) == 0) { | ||||
|             $errors->add('account_id', 'Account is mandatory'); | ||||
|         } | ||||
|         if ($model['targetdate'] == '' && isset($model['remind_me']) && intval($model['remind_me']) == 1) { | ||||
|             $errors->add('targetdate', 'Target date is mandatory when setting reminders.'); | ||||
|         } | ||||
|         if ($model['targetdate'] != '') { | ||||
|             try { | ||||
|                 new Carbon($model['targetdate']); | ||||
|             } catch (\Exception $e) { | ||||
|                 $errors->add('targetdate', 'Invalid date.'); | ||||
|             } | ||||
|         } | ||||
|         if (floatval($model['targetamount']) < 0.01) { | ||||
|             $errors->add('targetamount', 'Amount should be above 0.01.'); | ||||
|         } | ||||
|         if (!in_array(ucfirst($model['reminder']), \Config::get('firefly.piggy_bank_periods'))) { | ||||
|             $errors->add('reminder', 'Invalid reminder period (' . $model['reminder'] . ')'); | ||||
|         } | ||||
|         // check period. | ||||
|         if (!$errors->has('reminder') && !$errors->has('targetdate') && isset($model['remind_me']) && intval($model['remind_me']) == 1) { | ||||
|             $today  = new Carbon; | ||||
|             $target = new Carbon($model['targetdate']); | ||||
|             switch ($model['reminder']) { | ||||
|                 case 'week': | ||||
|                     $today->addWeek(); | ||||
|                     break; | ||||
|                 case 'month': | ||||
|                     $today->addMonth(); | ||||
|                     break; | ||||
|                 case 'year': | ||||
|                     $today->addYear(); | ||||
|                     break; | ||||
|             } | ||||
|             if ($today > $target) { | ||||
|                 $errors->add('reminder', 'Target date is too close to today to set reminders.'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $validator = \Validator::make($model, \PiggyBank::$rules); | ||||
|         if ($validator->invalid()) { | ||||
|             $errors->merge($errors); | ||||
|         } | ||||
|  | ||||
|         // add ok messages. | ||||
|         $list = ['name', 'account_id', 'targetamount', 'targetdate', 'remind_me', 'reminder']; | ||||
|         foreach ($list as $entry) { | ||||
|             if (!$errors->has($entry) && !$warnings->has($entry)) { | ||||
|                 $successes->add($entry, 'OK'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns an object with id $id. | ||||
|      * | ||||
|      * @param int $objectId | ||||
|      * | ||||
|      * @return \Eloquent | ||||
|      */ | ||||
|     public function find($objectId) | ||||
|     { | ||||
|         return \PiggyBank:: | ||||
|         leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('piggy_banks.id', '=', $objectId)->where( | ||||
|             'accounts.user_id', $this->getUser()->id | ||||
|         ) | ||||
|                          ->first(['piggy_banks.*']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc. | ||||
|      * | ||||
|      * @param $what | ||||
|      * | ||||
|      * @return \AccountType|null | ||||
|      * @throws NotImplementedException | ||||
|      */ | ||||
|     public function findByWhat($what) | ||||
|     { | ||||
|         // TODO: Implement findByWhat() method. | ||||
|         throw new NotImplementedException; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns all objects. | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function get() | ||||
|     { | ||||
|         return $this->getUser()->piggyBanks()->where('repeats', 0)->orderBy('name')->get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $ids | ||||
|      * | ||||
|      * @return Collection | ||||
|      * @throws NotImplementedException | ||||
|      */ | ||||
|     public function getByIds(array $ids) | ||||
|     { | ||||
|         // TODO: Implement getByIds() method. | ||||
|         throw new NotImplementedException; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \PiggyBank $piggyBank | ||||
|      * @param Carbon     $date | ||||
| @@ -257,21 +52,12 @@ class PiggyBank implements CUD, CommonDatabaseCalls, PiggyBankInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * Returns all objects. | ||||
|      * | ||||
|      * @return float | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function leftOnAccount(\Account $account) | ||||
|     public function get() | ||||
|     { | ||||
|         \Log::debug('Now in leftOnAccount() for account #'.$account->id.' ('.$account->name.')'); | ||||
|         $balance = \Steam::balance($account); | ||||
|         \Log::debug('Steam says: ' . $balance); | ||||
|         /** @var \PiggyBank $p */ | ||||
|         foreach ($account->piggyBanks()->get() as $p) { | ||||
|             $balance -= $p->currentRelevantRep()->currentamount; | ||||
|         } | ||||
|  | ||||
|         return $balance; | ||||
|  | ||||
|         return $this->getUser()->piggyBanks()->where('repeats', 0)->orderBy('name')->get(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										220
									
								
								app/lib/FireflyIII/Database/PiggyBank/PiggyBankShared.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								app/lib/FireflyIII/Database/PiggyBank/PiggyBankShared.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,220 @@ | ||||
| <?php | ||||
|  | ||||
| namespace FireflyIII\Database\PiggyBank; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Database\SwitchUser; | ||||
| use FireflyIII\Exception\NotImplementedException; | ||||
| use Illuminate\Database\Eloquent\Model as Eloquent; | ||||
| use Illuminate\Support\Collection; | ||||
| use Illuminate\Support\MessageBag; | ||||
|  | ||||
| /** | ||||
|  * Class PiggyBankShared | ||||
|  * | ||||
|  * @package FireflyIII\Database\PiggyBank | ||||
|  */ | ||||
| class PiggyBankShared | ||||
| { | ||||
|     use SwitchUser; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->setUser(\Auth::user()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Eloquent $model | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function destroy(Eloquent $model) | ||||
|     { | ||||
|         $model->delete(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns an object with id $id. | ||||
|      * | ||||
|      * @param int $objectId | ||||
|      * | ||||
|      * @return \Eloquent | ||||
|      */ | ||||
|     public function find($objectId) | ||||
|     { | ||||
|         return \PiggyBank:: | ||||
|         leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('piggy_banks.id', '=', $objectId)->where( | ||||
|             'accounts.user_id', $this->getUser()->id | ||||
|         ) | ||||
|                          ->first(['piggy_banks.*']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc. | ||||
|      * | ||||
|      * @param $what | ||||
|      * | ||||
|      * @return \AccountType|null | ||||
|      * @throws NotImplementedException | ||||
|      */ | ||||
|     public function findByWhat($what) | ||||
|     { | ||||
|         // TODO: Implement findByWhat() method. | ||||
|         throw new NotImplementedException; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $ids | ||||
|      * | ||||
|      * @return Collection | ||||
|      * @throws NotImplementedException | ||||
|      */ | ||||
|     public function getByIds(array $ids) | ||||
|     { | ||||
|         return \PiggyBank:: | ||||
|         leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->whereIn('piggy_banks.id', [$ids])->where( | ||||
|             'accounts.user_id', $this->getUser()->id | ||||
|         ) | ||||
|                          ->first(['piggy_banks.*']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * | ||||
|      * @return float | ||||
|      */ | ||||
|     public function leftOnAccount(\Account $account) | ||||
|     { | ||||
|         \Log::debug('Now in leftOnAccount() for account #' . $account->id . ' (' . $account->name . ')'); | ||||
|         $balance = \Steam::balance($account); | ||||
|         \Log::debug('Steam says: ' . $balance); | ||||
|         /** @var \PiggyBank $p */ | ||||
|         foreach ($account->piggyBanks()->get() as $p) { | ||||
|             $balance -= $p->currentRelevantRep()->currentamount; | ||||
|         } | ||||
|  | ||||
|         return $balance; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return \Eloquent | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function store(array $data) | ||||
|     { | ||||
|         if (!isset($data['remind_me']) || (isset($data['remind_me']) && $data['remind_me'] == 0)) { | ||||
|             $data['reminder'] = null; | ||||
|         } | ||||
|         $piggyBank = new \PiggyBank($data); | ||||
|         $piggyBank->save(); | ||||
|  | ||||
|         return $piggyBank; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Eloquent $model | ||||
|      * @param array    $data | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function update(Eloquent $model, array $data) | ||||
|     { | ||||
|         /** @var \PiggyBank $model */ | ||||
|         $model->name          = $data['name']; | ||||
|         $model->account_id    = intval($data['account_id']); | ||||
|         $model->targetamount  = floatval($data['targetamount']); | ||||
|         $model->targetdate    = isset($data['targetdate']) && $data['targetdate'] != '' ? $data['targetdate'] : null; | ||||
|         $model->rep_every     = intval($data['rep_every']); | ||||
|         $model->reminder_skip = intval($data['reminder_skip']); | ||||
|         $model->order         = intval($data['order']); | ||||
|         $model->remind_me     = intval($data['remind_me']); | ||||
|         $model->reminder      = isset($data['reminder']) ? $data['reminder'] : 'month'; | ||||
|  | ||||
|         if ($model->remind_me == 0) { | ||||
|             $model->reminder = null; | ||||
|         } | ||||
|  | ||||
|         $model->save(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Validates an array. Returns an array containing MessageBags | ||||
|      * errors/warnings/successes. | ||||
|      * | ||||
|      * Ignore PHPMD rules because Laravel 5.0 will make this method superfluous anyway. | ||||
|      * | ||||
|      * @param array $model | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function validate(array $model) | ||||
|     { | ||||
|         $warnings  = new MessageBag; | ||||
|         $successes = new MessageBag; | ||||
|         $model     = new \PiggyBank($model); | ||||
|         $model->isValid(); | ||||
|         $errors = $model->getErrors(); | ||||
|  | ||||
|         // add ok messages. | ||||
|         $list = ['name', 'account_id', 'targetamount', 'targetdate', 'remind_me', 'reminder']; | ||||
|         foreach ($list as $entry) { | ||||
|             if (!$errors->has($entry) && !$warnings->has($entry)) { | ||||
|                 $successes->add($entry, 'OK'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; | ||||
|  | ||||
|         exit; | ||||
|  | ||||
|         if (!in_array(ucfirst($model['reminder']), \Config::get('firefly.piggy_bank_periods'))) { | ||||
|             $errors->add('reminder', 'Invalid reminder period (' . $model['reminder'] . ')'); | ||||
|         } | ||||
|         // check period. | ||||
|         if (!$errors->has('reminder') && !$errors->has('targetdate') && isset($model['remind_me']) && intval($model['remind_me']) == 1) { | ||||
|             $today  = new Carbon; | ||||
|             $target = new Carbon($model['targetdate']); | ||||
|             switch ($model['reminder']) { | ||||
|                 case 'week': | ||||
|                     $today->addWeek(); | ||||
|                     break; | ||||
|                 case 'month': | ||||
|                     $today->addMonth(); | ||||
|                     break; | ||||
|                 case 'year': | ||||
|                     $today->addYear(); | ||||
|                     break; | ||||
|             } | ||||
|             if ($today > $target) { | ||||
|                 $errors->add('reminder', 'Target date is too close to today to set reminders.'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $validator = \Validator::make($model, \PiggyBank::$rules); | ||||
|         if ($validator->invalid()) { | ||||
|             $errors->merge($errors); | ||||
|         } | ||||
|  | ||||
|         // add ok messages. | ||||
|         $list = ['name', 'account_id', 'targetamount', 'targetdate', 'remind_me', 'reminder']; | ||||
|         foreach ($list as $entry) { | ||||
|             if (!$errors->has($entry) && !$warnings->has($entry)) { | ||||
|                 $successes->add($entry, 'OK'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -3,7 +3,6 @@ | ||||
| namespace FireflyIII\Database\PiggyBank; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Collection\PiggyBankPart; | ||||
| use FireflyIII\Database\CommonDatabaseCalls; | ||||
| use FireflyIII\Database\CUD; | ||||
| @@ -11,24 +10,14 @@ use FireflyIII\Database\SwitchUser; | ||||
| use FireflyIII\Exception\NotImplementedException; | ||||
| use Illuminate\Database\Eloquent\Model as Eloquent; | ||||
| use Illuminate\Support\Collection; | ||||
| use Illuminate\Support\MessageBag; | ||||
|  | ||||
| /** | ||||
|  * Class RepeatedExpense | ||||
|  * | ||||
|  * @package FireflyIII\Database | ||||
|  */ | ||||
| class RepeatedExpense implements CUD, CommonDatabaseCalls, PiggyBankInterface | ||||
| class RepeatedExpense extends PiggyBankShared implements CUD, CommonDatabaseCalls, PiggyBankInterface | ||||
| { | ||||
|     use SwitchUser; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->setUser(\Auth::user()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Based on the piggy bank, the reminder-setting and | ||||
| @@ -97,17 +86,6 @@ class RepeatedExpense implements CUD, CommonDatabaseCalls, PiggyBankInterface | ||||
|         return $part; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Eloquent $model | ||||
|      * | ||||
|      * @return bool | ||||
|      * @throws NotImplementedException | ||||
|      */ | ||||
|     public function destroy(Eloquent $model) | ||||
|     { | ||||
|         // TODO: Implement destroy() method. | ||||
|         throw new NotImplementedException; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $data | ||||
| @@ -134,144 +112,6 @@ class RepeatedExpense implements CUD, CommonDatabaseCalls, PiggyBankInterface | ||||
|         return $repeated; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Eloquent $model | ||||
|      * @param array    $data | ||||
|      * | ||||
|      * @return bool | ||||
|      * @throws NotImplementedException | ||||
|      */ | ||||
|     public function update(Eloquent $model, array $data) | ||||
|     { | ||||
|         // TODO: Implement update() method. | ||||
|         throw new NotImplementedException; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Validates an array. Returns an array containing MessageBags | ||||
|      * errors/warnings/successes. | ||||
|      * | ||||
|      * | ||||
|      * ignored because this method will be gone soon. | ||||
|      * | ||||
|      * @param array $model | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function validate(array $model) | ||||
|     { | ||||
|         $warnings  = new MessageBag; | ||||
|         $successes = new MessageBag; | ||||
|         $errors    = new MessageBag; | ||||
|  | ||||
|         /* | ||||
|          * Name validation: | ||||
|          */ | ||||
|         if (!isset($model['name'])) { | ||||
|             $errors->add('name', 'Name is mandatory'); | ||||
|         } | ||||
|  | ||||
|         if (isset($model['name']) && strlen($model['name']) == 0) { | ||||
|             $errors->add('name', 'Name is too short'); | ||||
|         } | ||||
|         if (isset($model['name']) && strlen($model['name']) > 100) { | ||||
|             $errors->add('name', 'Name is too long'); | ||||
|         } | ||||
|  | ||||
|         if (intval($model['account_id']) == 0) { | ||||
|             $errors->add('account_id', 'Account is mandatory'); | ||||
|         } | ||||
|         if ($model['targetdate'] == '' && isset($model['remind_me']) && intval($model['remind_me']) == 1) { | ||||
|             $errors->add('targetdate', 'Target date is mandatory when setting reminders.'); | ||||
|         } | ||||
|         if ($model['targetdate'] != '') { | ||||
|             try { | ||||
|                 new Carbon($model['targetdate']); | ||||
|             } catch (\Exception $e) { | ||||
|                 $errors->add('targetdate', 'Invalid date.'); | ||||
|             } | ||||
|             $diff = Carbon::now()->diff(new Carbon($model['targetdate'])); | ||||
|             if ($diff->days > 365) { | ||||
|                 $errors->add('targetdate', 'First target date should a a year or less from now.'); | ||||
|             } | ||||
|         } else { | ||||
|             $errors->add('targetdate', 'Invalid target date.'); | ||||
|         } | ||||
|         if (floatval($model['targetamount']) < 0.01) { | ||||
|             $errors->add('targetamount', 'Amount should be above 0.01.'); | ||||
|         } | ||||
|         if (!in_array(ucfirst($model['reminder']), \Config::get('firefly.piggy_bank_periods'))) { | ||||
|             $errors->add('reminder', 'Invalid reminder period (' . $model['reminder'] . ')'); | ||||
|         } | ||||
|  | ||||
|         if (!in_array(ucfirst($model['rep_length']), \Config::get('firefly.piggy_bank_periods'))) { | ||||
|             $errors->add('rep_length', 'Invalid repeat period (' . $model['rep_length'] . ')'); | ||||
|         } | ||||
|  | ||||
|         // check period. | ||||
|         if (!$errors->has('reminder') && !$errors->has('targetdate') && isset($model['remind_me']) && intval($model['remind_me']) == 1) { | ||||
|             $today  = new Carbon; | ||||
|             $target = new Carbon($model['targetdate']); | ||||
|             switch ($model['reminder']) { | ||||
|                 case 'week': | ||||
|                     $today->addWeek(); | ||||
|                     break; | ||||
|                 case 'month': | ||||
|                     $today->addMonth(); | ||||
|                     break; | ||||
|                 case 'year': | ||||
|                     $today->addYear(); | ||||
|                     break; | ||||
|             } | ||||
|             if ($today > $target) { | ||||
|                 $errors->add('reminder', 'Target date is too close to today to set reminders.'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $validator = \Validator::make($model, \PiggyBank::$rules); | ||||
|         if ($validator->invalid()) { | ||||
|             $errors->merge($errors); | ||||
|         } | ||||
|  | ||||
|         // add ok messages. | ||||
|         $list = ['name', 'account_id', 'rep_every', 'rep_times', 'rep_length', 'targetamount', 'targetdate', 'remind_me', 'reminder']; | ||||
|         foreach ($list as $entry) { | ||||
|             if (!$errors->has($entry) && !$warnings->has($entry)) { | ||||
|                 $successes->add($entry, 'OK'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns an object with id $id. | ||||
|      * | ||||
|      * @param int $objectId | ||||
|      * | ||||
|      * @return \Eloquent | ||||
|      * @throws NotImplementedException | ||||
|      */ | ||||
|     public function find($objectId) | ||||
|     { | ||||
|         // TODO: Implement find() method. | ||||
|         throw new NotImplementedException; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc. | ||||
|      * | ||||
|      * @param $what | ||||
|      * | ||||
|      * @return \AccountType|null | ||||
|      * @throws NotImplementedException | ||||
|      */ | ||||
|     public function findByWhat($what) | ||||
|     { | ||||
|         // TODO: Implement findByWhat() method. | ||||
|         throw new NotImplementedException; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns all objects. | ||||
|      * | ||||
| @@ -282,27 +122,4 @@ class RepeatedExpense implements CUD, CommonDatabaseCalls, PiggyBankInterface | ||||
|         return $this->getUser()->piggyBanks()->where('repeats', 1)->get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $ids | ||||
|      * | ||||
|      * @return Collection | ||||
|      * @throws NotImplementedException | ||||
|      */ | ||||
|     public function getByIds(array $ids) | ||||
|     { | ||||
|         // TODO: Implement getByIds() method. | ||||
|         throw new NotImplementedException; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * | ||||
|      * @return float | ||||
|      * @throws NotImplementedException | ||||
|      */ | ||||
|     public function leftOnAccount(\Account $account) | ||||
|     { | ||||
|         // TODO: Implement leftOnAccount() method. | ||||
|         throw new NotImplementedException; | ||||
|     } | ||||
| } | ||||
| @@ -241,12 +241,15 @@ class Date | ||||
|             default: | ||||
|                 throw new FireflyException('Cannot do subtractPeriod for $repeat_freq ' . $repeatFreq); | ||||
|                 break; | ||||
|             case 'day': | ||||
|             case 'daily': | ||||
|                 $date->subDays($subtract); | ||||
|                 break; | ||||
|             case 'week': | ||||
|             case 'weekly': | ||||
|                 $date->subWeeks($subtract); | ||||
|                 break; | ||||
|             case 'month': | ||||
|             case 'monthly': | ||||
|                 $date->subMonths($subtract); | ||||
|                 break; | ||||
|   | ||||
| @@ -9,10 +9,10 @@ use Watson\Validating\ValidatingTrait; | ||||
| class PiggyBank extends Eloquent | ||||
| { | ||||
|     use ValidatingTrait; | ||||
|     public static $rules | ||||
|     protected $rules | ||||
|         = ['account_id'    => 'required|exists:accounts,id', // link to Account | ||||
|            'name'          => 'required|between:1,255', // name | ||||
|            'targetamount'  => 'required|min:0', // amount you want to save | ||||
|            'targetamount'  => 'required|min:0.01|numeric', // amount you want to save | ||||
|            'startdate'     => 'date', // when you started | ||||
|            'targetdate'    => 'date', // when its due | ||||
|            'repeats'       => 'required|boolean', // does it repeat? | ||||
|   | ||||
| @@ -119,7 +119,7 @@ Route::bind( | ||||
| ); | ||||
|  | ||||
| Route::bind( | ||||
|     'piggy_bank', function ($value, $route) { | ||||
|     'piggyBank', function ($value, $route) { | ||||
|     if (Auth::check()) { | ||||
|         return PiggyBank:: | ||||
|         where('piggy_banks.id', $value) | ||||
| @@ -132,6 +132,20 @@ Route::bind( | ||||
| } | ||||
| ); | ||||
|  | ||||
| Route::bind( | ||||
|     'repeatedExpense', function ($value, $route) { | ||||
|     if (Auth::check()) { | ||||
|         return PiggyBank:: | ||||
|         where('piggy_banks.id', $value) | ||||
|                         ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') | ||||
|                         ->where('accounts.user_id', Auth::user()->id) | ||||
|                         ->where('repeats', 1)->first(['piggy_banks.*']); | ||||
|     } | ||||
|  | ||||
|     return null; | ||||
| } | ||||
| ); | ||||
|  | ||||
| Route::bind( | ||||
|     'repeated', function ($value, $route) { | ||||
|     if (Auth::check()) { | ||||
| @@ -198,7 +212,7 @@ Route::group( | ||||
|     Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']); | ||||
|     Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']); | ||||
|     Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']); | ||||
|     Route::get('/chart/piggy_history/{piggy_bank}', ['uses' => 'GoogleChartController@piggyBankHistory']); | ||||
|     Route::get('/chart/piggy_history/{piggyBank}', ['uses' => 'GoogleChartController@piggyBankHistory']); | ||||
|  | ||||
|     // google chart for components (categories + budgets combined) | ||||
|     Route::get('/chart/budget/{budget}/spending/{year}', ['uses' => 'GoogleChartController@budgetsAndSpending']); | ||||
| @@ -219,13 +233,13 @@ Route::group( | ||||
|  | ||||
|     // piggy bank controller | ||||
|     Route::get('/piggy_banks', ['uses' => 'PiggyBankController@index', 'as' => 'piggy_banks.index']); | ||||
|     Route::get('/piggy_banks/add/{piggy_bank}', ['uses' => 'PiggyBankController@add']); # add money | ||||
|     Route::get('/piggy_banks/remove/{piggy_bank}', ['uses' => 'PiggyBankController@remove']); #remove money | ||||
|     Route::get('/piggy_banks/add/{piggyBank}', ['uses' => 'PiggyBankController@add']); # add money | ||||
|     Route::get('/piggy_banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@remove']); #remove money | ||||
|  | ||||
|     Route::get('/piggy_banks/create', ['uses' => 'PiggyBankController@create', 'as' => 'piggy_banks.create']); | ||||
|     Route::get('/piggy_banks/edit/{piggy_bank}', ['uses' => 'PiggyBankController@edit', 'as' => 'piggy_banks.edit']); | ||||
|     Route::get('/piggy_banks/delete/{piggy_bank}', ['uses' => 'PiggyBankController@delete', 'as' => 'piggy_banks.delete']); | ||||
|     Route::get('/piggy_banks/show/{piggy_bank}', ['uses' => 'PiggyBankController@show', 'as' => 'piggy_banks.show']); | ||||
|     Route::get('/piggy_banks/edit/{piggyBank}', ['uses' => 'PiggyBankController@edit', 'as' => 'piggy_banks.edit']); | ||||
|     Route::get('/piggy_banks/delete/{piggyBank}', ['uses' => 'PiggyBankController@delete', 'as' => 'piggy_banks.delete']); | ||||
|     Route::get('/piggy_banks/show/{piggyBank}', ['uses' => 'PiggyBankController@show', 'as' => 'piggy_banks.show']); | ||||
|  | ||||
|     // preferences controller | ||||
|     Route::get('/preferences', ['uses' => 'PreferencesController@index', 'as' => 'preferences']); | ||||
| @@ -245,7 +259,9 @@ Route::group( | ||||
|     // repeated expenses controller: | ||||
|     Route::get('/repeatedexpenses', ['uses' => 'RepeatedExpenseController@index', 'as' => 'repeated.index']); | ||||
|     Route::get('/repeatedexpenses/create', ['uses' => 'RepeatedExpenseController@create', 'as' => 'repeated.create']); | ||||
|     Route::get('/repeatedexpenses/show/{repeated}', ['uses' => 'RepeatedExpenseController@show', 'as' => 'repeated.show']); | ||||
|     Route::get('/repeatedexpenses/edit/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@edit', 'as' => 'repeated.edit']); | ||||
|     Route::get('/repeatedexpenses/delete/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@delete', 'as' => 'repeated.delete']); | ||||
|     Route::get('/repeatedexpenses/show/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@show', 'as' => 'repeated.show']); | ||||
|  | ||||
|     // report controller: | ||||
|     Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']); | ||||
| @@ -314,13 +330,15 @@ Route::group( | ||||
|  | ||||
|     // piggy bank controller | ||||
|     Route::post('/piggy_banks/store', ['uses' => 'PiggyBankController@store', 'as' => 'piggy_banks.store']); | ||||
|     Route::post('/piggy_banks/update/{piggy_bank}', ['uses' => 'PiggyBankController@update', 'as' => 'piggy_banks.update']); | ||||
|     Route::post('/piggy_banks/destroy/{piggy_bank}', ['uses' => 'PiggyBankController@destroy', 'as' => 'piggy_banks.destroy']); | ||||
|     Route::post('/piggy_banks/add/{piggy_bank}', ['uses' => 'PiggyBankController@postAdd', 'as' => 'piggy_banks.add']); # add money | ||||
|     Route::post('/piggy_banks/remove/{piggy_bank}', ['uses' => 'PiggyBankController@postRemove', 'as' => 'piggy_banks.remove']); # remove money. | ||||
|     Route::post('/piggy_banks/update/{piggyBank}', ['uses' => 'PiggyBankController@update', 'as' => 'piggy_banks.update']); | ||||
|     Route::post('/piggy_banks/destroy/{piggyBank}', ['uses' => 'PiggyBankController@destroy', 'as' => 'piggy_banks.destroy']); | ||||
|     Route::post('/piggy_banks/add/{piggyBank}', ['uses' => 'PiggyBankController@postAdd', 'as' => 'piggy_banks.add']); # add money | ||||
|     Route::post('/piggy_banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@postRemove', 'as' => 'piggy_banks.remove']); # remove money. | ||||
|  | ||||
|     // repeated expense controller | ||||
|     Route::post('/repeatedexpense/store', ['uses' => 'RepeatedExpenseController@store', 'as' => 'repeated.store']); | ||||
|     Route::post('/repeatedexpense/update/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@update', 'as' => 'repeated.update']); | ||||
|     Route::post('/repeatedexpense/destroy/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@destroy', 'as' => 'repeated.destroy']); | ||||
|  | ||||
|     // preferences controller | ||||
|     Route::post('/preferences', ['uses' => 'PreferencesController@postIndex']); | ||||
|   | ||||
| @@ -1,12 +1,34 @@ | ||||
| @extends('layouts.default') | ||||
| @section('content') | ||||
| {{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) }} | ||||
| <div class="row"> | ||||
|     <div class="col-lg-12 col-md-12 col-sm-12"> | ||||
|         <p> | ||||
|             <a href="{{route('piggy_banks.create')}}" class="btn btn-success">Create new piggy bank</a> | ||||
|         </p> | ||||
|     </div> | ||||
| </div> | ||||
| @foreach($piggyBanks as $piggyBank) | ||||
| <div class="row"> | ||||
|     <div class="col-lg-12 col-md-12 col-sm-12"> | ||||
|         <div class="panel panel-default"> | ||||
|             <div class="panel-heading"> | ||||
|                 <i class="fa fa-fw fa-rocket"></i> <a href="{{route('piggy_banks.show',$piggyBank->id)}}" title="{{{$piggyBank->name}}}">{{{$piggyBank->name}}}</a> | ||||
|  | ||||
|                 <!-- ACTIONS MENU --> | ||||
|                 <div class="pull-right"> | ||||
|                     <div class="btn-group"> | ||||
|                         <button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown"> | ||||
|                             Actions | ||||
|                             <span class="caret"></span> | ||||
|                         </button> | ||||
|                         <ul class="dropdown-menu pull-right" role="menu"> | ||||
|                             <li><a href="{{route('piggy_banks.edit',$piggyBank->id)}}"><i class="fa fa-pencil fa-fw"></i> Edit</a></li> | ||||
|                             <li><a href="{{route('piggy_banks.delete',$piggyBank->id)}}"><i class="fa fa-trash fa-fw"></i> Delete</a></li> | ||||
|                         </ul> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  | ||||
|             </div> | ||||
|             <div class="panel-body"> | ||||
|                 <div class="row"> | ||||
| @@ -61,19 +83,10 @@ | ||||
| </div> | ||||
| @endforeach | ||||
| <div class="row"> | ||||
|     <div class="col-lg-6 col-md-6 col-sm-12"> | ||||
|         <div class="panel panel-default"> | ||||
|             <div class="panel-heading"> | ||||
|                 <i class="fa fa-fw fa-plus"></i> Create piggy bank | ||||
|             </div> | ||||
|             <div class="panel-body"> | ||||
|                 <div class="row"> | ||||
|                     <div class="col-lg-8 col-md-6 col-sm-4 col-lg-offset-2 col-md-offset-3 col-sm-offset-4"> | ||||
|                         <a href="{{route('piggy_banks.create')}}" class="btn btn-success">Create new piggy bank</a> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     <div class="col-lg-12 col-md-12 col-sm-12"> | ||||
|         <p> | ||||
|             <a href="{{route('piggy_banks.create')}}" class="btn btn-success">Create new piggy bank</a> | ||||
|         </p> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
|   | ||||
							
								
								
									
										37
									
								
								app/views/repeatedexpense/delete.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								app/views/repeatedexpense/delete.blade.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| @extends('layouts.default') | ||||
| @section('content') | ||||
| {{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $repeatedExpense) }} | ||||
| {{Form::open(['class' => 'form-horizontal','id' => 'destroy','url' => route('repeated.destroy',$repeatedExpense->id)])}} | ||||
| <div class="row"> | ||||
|     <div class="col-lg-6 col-md-12 col-sm-12"> | ||||
|         <div class="panel panel-red"> | ||||
|             <div class="panel-heading"> | ||||
|                 Delete repeated expense "{{{$repeatedExpense->name}}}" | ||||
|             </div> | ||||
|             <div class="panel-body"> | ||||
|                 <p> | ||||
|                 Are you sure? | ||||
|                 </p> | ||||
|  | ||||
|                 <p> | ||||
|                     <button type="submit" class="btn btn-default btn-danger">Delete permanently</button> | ||||
|                     <a href="{{URL::previous()}}" class="btn-default btn">Cancel</a > | ||||
|                 </p> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
| <div class="row"> | ||||
|     <div class="col-lg-6"> | ||||
|         <div class="form-group"> | ||||
|             <div class="col-sm-8"> | ||||
|  | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
|  | ||||
| {{Form::close()}} | ||||
| @stop | ||||
							
								
								
									
										97
									
								
								app/views/repeatedexpense/edit.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								app/views/repeatedexpense/edit.blade.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| @extends('layouts.default') | ||||
| @section('content') | ||||
| {{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $repeatedExpense) }} | ||||
| {{Form::model($repeatedExpense, ['class' => 'form-horizontal','id' => 'update','url' => route('repeated.update',$repeatedExpense->id)])}} | ||||
|  | ||||
| <div class="row"> | ||||
|     <div class="col-lg-6 col-md-12 col-sm-6"> | ||||
|         <div class="panel panel-primary"> | ||||
|             <div class="panel-heading"> | ||||
|                 <i class="fa fa-fw fa-exclamation"></i> Mandatory fields | ||||
|             </div> | ||||
|             <div class="panel-body"> | ||||
|                 {{Form::ffText('name')}} | ||||
|                 {{Form::ffSelect('account_id',$accounts,null,['label' => 'Save on account'])}} | ||||
|                 {{Form::ffAmount('targetamount')}} | ||||
|                 {{Form::ffDate('targetdate',null,['label' => 'First target date'])}} | ||||
|                 {{Form::ffSelect('rep_length',$periods,null,['label' => 'Repeats every'])}} | ||||
|                 {{Form::ffInteger('rep_every',null,['label' => 'Skip period'])}} | ||||
|                 {{Form::ffInteger('rep_times',null,['label' => 'Repeat times'])}} | ||||
|  | ||||
|             </div> | ||||
|         </div> | ||||
|         <p> | ||||
|             <button type="submit" class="btn btn-lg btn-success"> | ||||
|                 <i class="fa fa-pencil"></i> Update repeated expense | ||||
|             </button> | ||||
|         </p> | ||||
|     </div> | ||||
|     <div class="col-lg-6 col-md-12 col-sm-12"> | ||||
|         <!-- 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"> | ||||
|                 {{Form::ffCheckbox('remind_me','1',$preFilled['remind_me'],['label' => 'Remind me'])}} | ||||
|                 {{Form::ffSelect('reminder',$periods,$preFilled['reminder'],['label' => 'Remind every'])}} | ||||
|             </div> | ||||
|         </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"> | ||||
|                 {{Form::ffOptionsList('update','piggy bank')}} | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|     </div> | ||||
| </div> | ||||
| {{-- | ||||
|  | ||||
|         <h4>Mandatory fields</h4> | ||||
|  | ||||
|         <h4>Optional fields</h4> | ||||
|  | ||||
|  | ||||
|         <div class="form-group"> | ||||
|             {{ Form::label('reminder', 'Remind you every', ['class' => 'col-sm-4 control-label'])}} | ||||
|             <div class="col-sm-8"> | ||||
|                 <input type="number" step="1" min="1" value="{{Input::old('reminder_skip') ?: 1}}" style="width:50px;display:inline;" max="100" name="reminder_skip" class="form-control" /> | ||||
|  | ||||
|                 <select class="form-control" name="reminder" style="width:150px;display: inline"> | ||||
|                     <option value="none" label="do not remind me">do not remind me</option> | ||||
|                     @foreach($periods as $period) | ||||
|                         <option value="{{$period}}" label="{{$period}}">{{$period}}</option> | ||||
|                     @endforeach | ||||
|                 </select> | ||||
|                  @if($errors->has('reminder')) | ||||
|                                 <p class="text-danger">{{$errors->first('reminder')}}</p> | ||||
|                                 @else | ||||
|                 <span class="help-block">Enter a number and a period and Firefly will remind you to add money | ||||
|                     to this piggy bank every now and then.</span> | ||||
|                     @endif | ||||
|             </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">Create the piggy bank</button> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| --}} | ||||
|  | ||||
| {{Form::close()}} | ||||
| @stop | ||||
| @@ -4,24 +4,34 @@ | ||||
| <div class="row"> | ||||
|     <div class="col-lg-12 col-md-12 col-sm-12"> | ||||
|         <p> | ||||
|         <a class="btn btn-lg btn-success" href="{{route('repeated.create')}}">Create new repeated expense</a> | ||||
|         <a class="btn btn-success" href="{{route('repeated.create')}}">Create new repeated expense</a> | ||||
|         </p> | ||||
|  | ||||
|     </div> | ||||
| </div> | ||||
| <!-- TODO create update and destroy --> | ||||
|  | ||||
| @foreach($expenses as $entry) | ||||
| <div class="row"> | ||||
|     <div class="col-lg-12 col-md-12 col-sm-12"> | ||||
|  | ||||
|     </div> | ||||
| </div> | ||||
| <!-- TODO create update and destroy --> | ||||
| <div class="row"> | ||||
| @foreach($expenses as $entry) | ||||
|     <div class="col-lg-3 col-md-4 col-sm-6"> | ||||
|         <div class="panel panel-default"> | ||||
|             <div class="panel-heading"> | ||||
|                 <a href="{{route('repeated.show',$entry->id)}}" title="{{{$entry->name}}}">{{{$entry->name}}}</a> | ||||
|                 ({{mf($entry->targetamount)}}) | ||||
|  | ||||
|                 <!-- ACTIONS MENU --> | ||||
|                 <div class="pull-right"> | ||||
|                     <div class="btn-group"> | ||||
|                         <button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown"> | ||||
|                             Actions | ||||
|                             <span class="caret"></span> | ||||
|                         </button> | ||||
|                         <ul class="dropdown-menu pull-right" role="menu"> | ||||
|                             <li><a href="{{route('repeated.edit',$entry->id)}}"><i class="fa fa-pencil fa-fw"></i> Edit</a></li> | ||||
|                             <li><a href="{{route('repeated.delete',$entry->id)}}"><i class="fa fa-trash fa-fw"></i> Delete</a></li> | ||||
|                         </ul> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="panel-body"> | ||||
|                 <div class="progress progress-striped"> | ||||
| @@ -40,7 +50,16 @@ | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
|     @endforeach | ||||
|  | ||||
| <div class="row"> | ||||
|     <div class="col-lg-12 col-md-12 col-sm-12"> | ||||
|         <p> | ||||
|             <a class="btn btn-success" href="{{route('repeated.create')}}">Create new repeated expense</a> | ||||
|         </p> | ||||
|  | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| @extends('layouts.default') | ||||
| @section('content') | ||||
| {{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $piggyBank) }} | ||||
| {{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $repeatedExpense) }} | ||||
| <div class="row"> | ||||
| <div class="col-lg-12 col-md-12 col-sm-12"> | ||||
|     @foreach($repetitions as $rep) | ||||
| @@ -21,7 +21,7 @@ | ||||
|         </div> | ||||
|         <div class="panel-body"> | ||||
|             <p> | ||||
|                 Target amount: {{mf($piggyBank->targetamount)}}. Currently saved: {{mf($rep->currentamount)}}. | ||||
|                 Target amount: {{mf($repeatedExpense->targetamount)}}. Currently saved: {{mf($rep->currentamount)}}. | ||||
|             </p> | ||||
|             <div class="row"> | ||||
|             @foreach($rep->bars as $bar) | ||||
|   | ||||
| @@ -42,12 +42,22 @@ | ||||
|             </tr> | ||||
|             @foreach($budgets as $id => $budget) | ||||
|             <tr> | ||||
|                 <td>{{{$budget['name']}}}</td> | ||||
|                 <td>{{{$budget['name']}}} | ||||
|                 @if($id == 0) | ||||
|                     <i class="fa fa-fw fa-question-circle" data-toggle="tooltip" data-placement="top" title="The calculation used here is slightly different from the row below. The numbers should match."></i> | ||||
|                 @endif | ||||
|                 </td> | ||||
|                 <td>{{mf($budget['amount'])}}</td> | ||||
|                 <?php $spent = 0;?> | ||||
|                 @foreach($accounts as $account) | ||||
|                     @if(isset($account->budgetInformation[$id])) | ||||
|                         <td>{{mf($account->budgetInformation[$id]['amount'])}}</td> | ||||
|                         <td> | ||||
|                             @if($id == 0) | ||||
|                             <a href="#">{{mf($account->budgetInformation[$id]['amount'])}}</a> | ||||
|                             @else | ||||
|                                 {{mf($account->budgetInformation[$id]['amount'])}} | ||||
|                             @endif | ||||
|                         </td> | ||||
|                         <?php | ||||
|                         $spent += floatval($account->budgetInformation[$id]['amount']); | ||||
|                         $accountSums[$account->id] += floatval($account->budgetInformation[$id]['amount']); | ||||
| @@ -61,10 +71,14 @@ | ||||
|             </tr> | ||||
|             @endforeach | ||||
|             <tr> | ||||
|                 <td colspan="2">Without budget</td> | ||||
|                 <td colspan="2">Without budget | ||||
|                     <i class="fa fa-fw fa-question-circle" data-toggle="tooltip" data-placement="top" title="The calculation used here is slightly different from the row above. The numbers should match."></i> | ||||
|                 </td> | ||||
|                 @foreach($accounts as $account) | ||||
|                     @if(isset($account->budgetInformation[0])) | ||||
|                         <td>{{mf($account->budgetInformation[0]['amount'])}}</td> | ||||
|                         <td> | ||||
|                             <a href="#">{{mf($account->budgetInformation[0]['amount'])}}</a> | ||||
|                         </td> | ||||
|                     @else | ||||
|                         <td>{{mf(0)}}</td> | ||||
|                     @endif | ||||
| @@ -74,7 +88,9 @@ | ||||
|             <tr> | ||||
|                 <td colspan="2">Balanced by transfers</td> | ||||
|                 @foreach($accounts as $account) | ||||
|                     <td>{{mf($account->balancedAmount)}}</td> | ||||
|                     <td> | ||||
|                         <a href="#">{{mf($account->balancedAmount)}}</a> | ||||
|                     </td> | ||||
|                 @endforeach | ||||
|                 <td colspan="2"> </td> | ||||
|             </tr> | ||||
| @@ -101,7 +117,9 @@ | ||||
|                     $accountSums[$account->id] += $account->balancedAmount; | ||||
|                     ?> | ||||
|                     @if(isset($account->budgetInformation[0])) | ||||
|                         <td>{{mf($account->budgetInformation[0]['amount'] + $account->balancedAmount)}}</td> | ||||
|                         <td> | ||||
|                             <a href="#">{{mf($account->budgetInformation[0]['amount'] + $account->balancedAmount)}}</a> | ||||
|                         </td> | ||||
|                     @else | ||||
|                         <td>{{mf(0)}}</td> | ||||
|                     @endif | ||||
| @@ -126,4 +144,13 @@ | ||||
|         </table> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
| <!-- modal to show various budget information --> | ||||
| <div class="modal fade" id="budgetModal"> | ||||
|  | ||||
| </div> | ||||
|  | ||||
| @stop | ||||
| @section('scripts') | ||||
| {{HTML::script('assets/javascript/firefly/reports.js')}} | ||||
| @stop | ||||
| @@ -1,5 +1,8 @@ | ||||
| $(function () { | ||||
|     $('#help').click(showHelp); | ||||
|     $(function () { | ||||
|         $('[data-toggle="tooltip"]').tooltip() | ||||
|     }) | ||||
| }); | ||||
|  | ||||
| function showHelp(e) { | ||||
|   | ||||
| @@ -1,5 +1,8 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Class PreferencesControllerCest | ||||
|  */ | ||||
| class PreferencesControllerCest | ||||
| { | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										23
									
								
								tests/functional/RepeatedExpenseCest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tests/functional/RepeatedExpenseCest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Class RepeatedExpenseCest | ||||
|  */ | ||||
| class RepeatedExpenseCest { | ||||
|     /** | ||||
|      * @param FunctionalTester $I | ||||
|      */ | ||||
|     public function _after(FunctionalTester $I) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param FunctionalTester $I | ||||
|      */ | ||||
|     public function _before(FunctionalTester $I) | ||||
|     { | ||||
|         $I->amLoggedAs(['email' => 'thegrumpydictator@gmail.com', 'password' => 'james']); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user