First attempt at job to create transactions for recurring transactions.

This commit is contained in:
James Cole 2018-06-22 18:42:23 +02:00
parent 636cd84f4f
commit db1c27d833
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
16 changed files with 646 additions and 133 deletions

View File

@ -24,8 +24,13 @@ declare(strict_types=1);
namespace FireflyIII\Console;
use Carbon\Carbon;
use FireflyIII\Jobs\CreateRecurringTransactions;
use FireflyIII\Models\RecurrenceRepetition;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Log;
/**
* File to make sure commnds work.
@ -58,7 +63,43 @@ class Kernel extends ConsoleKernel
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function schedule(Schedule $schedule)
protected function schedule(Schedule $schedule): void
{
$schedule->call(
function () {
// run for the entirety of 2018, just to see what happens
$start = new Carbon('2018-01-01');
$end = new Carbon('2018-12-31');
while ($start <= $end) {
Log::info(sprintf('Now at %s', $start->format('D Y-m-d')));
$job = new CreateRecurringTransactions(clone $start);
$job->handle();
$start->addDay();
}
}
)->everyMinute();
//$schedule->job(new CreateRecurringTransactions(new Carbon))->everyMinute();
//$schedule->job(new CreateRecurringTransactions(new Carbon))->everyMinute();
// $schedule->call(
// function () {
// // command to do something
// Log::debug('Schedule creation of transactions yaay!');
// }
// )->daily();
//
// $schedule->call(
// function () {
// // command to do something
// Log::debug('Every minute!');
// }
// )->everyMinute()
// ->emailOutputTo('thege');
}
}

View File

@ -26,7 +26,6 @@ namespace FireflyIII\Factory;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Services\Internal\Support\JournalServiceTrait;
use FireflyIII\Services\Internal\Support\TransactionTypeTrait;
use FireflyIII\User;
@ -100,7 +99,8 @@ class TransactionJournalFactory
// store date meta fields (if present):
$fields = ['sepa-cc', 'sepa-ct-op', 'sepa-ct-id', 'sepa-db', 'sepa-country', 'sepa-ep', 'sepa-ci', 'interest_date', 'book_date', 'process_date',
'due_date', 'payment_date', 'invoice_date', 'internal_reference', 'bunq_payment_id', 'importHash', 'importHashV2', 'external_id'];
'due_date', 'recurrence_id', 'payment_date', 'invoice_date', 'internal_reference', 'bunq_payment_id', 'importHash', 'importHashV2',
'external_id'];
foreach ($fields as $field) {
$this->storeMeta($journal, $data, $field);

View File

@ -76,6 +76,8 @@ class EditController extends Controller
*/
public function edit(Request $request, Recurrence $recurrence)
{
// use transformer:
$transformer = new RecurrenceTransformer(new ParameterBag);
$array = $transformer->transform($recurrence);
@ -86,6 +88,7 @@ class EditController extends Controller
// todo handle old repetition type as well.
/** @var RecurrenceRepetition $repetition */
$repetition = $recurrence->recurrenceRepetitions()->first();
$currentRepetitionType = $repetition->repetition_type;
@ -116,13 +119,12 @@ class EditController extends Controller
// code to handle active-checkboxes
$hasOldInput = null !== $request->old('_token');
// $hasOldInput = false;
$preFilled = [
'transaction_type' => strtolower($recurrence->transactionType->type),
'active' => $hasOldInput ? (bool)$request->old('active') : $recurrence->active,
'apply_rules' => $hasOldInput ? (bool)$request->old('apply_rules') : $recurrence->apply_rules,
];
$request->flash('preFilled', $preFilled);
return view('recurring.edit', compact('recurrence', 'array', 'budgets', 'preFilled', 'currentRepetitionType', 'repetitionEnd', 'repetitionEnds'));
}

View File

@ -159,7 +159,7 @@ class IndexController extends Controller
{
$page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page');
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
$collection = $this->recurring->getActive();
$collection = $this->recurring->get();
// TODO: split collection into pages

View File

@ -0,0 +1,321 @@
<?php
namespace FireflyIII\Jobs;
use Carbon\Carbon;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceRepetition;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Log;
/**
* Class CreateRecurringTransactions
*/
class CreateRecurringTransactions implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** @var Carbon */
private $date;
/** @var JournalRepositoryInterface */
private $journalRepository;
/** @var RecurringRepositoryInterface */
private $repository;
/**
* Create a new job instance.
*
* @param Carbon $date
*/
public function __construct(Carbon $date)
{
$date->startOfDay();
$this->date = $date;
$this->repository = app(RecurringRepositoryInterface::class);
$this->journalRepository = app(JournalRepositoryInterface::class);
}
/**
* Execute the job.
*
* TODO check number of repetitions.
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function handle(): void
{
Log::debug('Now at start of CreateRecurringTransactions() job.');
$recurrences = $this->repository->getAll();
Log::debug(sprintf('Count of collection is %d', $recurrences->count()));
/** @var Collection $filtered */
$filtered = $recurrences->filter(
function (Recurrence $recurrence) {
return $this->validRecurrence($recurrence);
}
);
Log::debug(sprintf('Left after filtering is %d', $filtered->count()));
/** @var Recurrence $recurrence */
foreach ($filtered as $recurrence) {
$this->repository->setUser($recurrence->user);
$this->journalRepository->setUser($recurrence->user);
Log::debug(sprintf('Now at recurrence #%d', $recurrence->id));
$this->handleRepetitions($recurrence);
Log::debug(sprintf('Done with recurrence #%c', $recurrence->id));
}
Log::debug('Done with handle()');
}
/**
* Return recurring transaction is active.
*
* @param Recurrence $recurrence
*
* @return bool
*/
private function active(Recurrence $recurrence): bool
{
return $recurrence->active;
}
/**
* @param array $occurrences
*
* @return array
*/
private function debugArray(array $occurrences): array
{
$return = [];
foreach ($occurrences as $entry) {
$return[] = $entry->format('Y-m-d');
}
return $return;
}
/**
* @param Recurrence $recurrence
*
* @return Carbon
*/
private function getStartDate(Recurrence $recurrence): Carbon
{
$startDate = clone $recurrence->first_date;
if (null !== $recurrence->latest_date && $recurrence->latest_date->gte($startDate)) {
$startDate = clone $recurrence->latest_date;
// jump to a day later.
$startDate->addDay();
}
return $startDate;
}
/**
* @param Recurrence $recurrence
*
* @return array
*/
private function getTransactionData(Recurrence $recurrence): array
{
$transactions = $recurrence->recurrenceTransactions()->get();
$return = [];
/** @var RecurrenceTransaction $transaction */
foreach ($transactions as $index => $transaction) {
$single = [
'currency_id' => $transaction->transaction_currency_id,
'currency_code' => null,
'description' => null,
'amount' => $transaction->amount,
'budget_id' => $this->repository->getBudget($transaction),
'budget_name' => null,
'category_id' => null,
'category_name' => $this->repository->getCategory($transaction),
'source_id' => $transaction->source_account_id,
'source_name' => null,
'destination_id' => $transaction->destination_account_id,
'destination_name' => null,
'foreign_currency_id' => $transaction->foreign_currency_id,
'foreign_currency_code' => null,
'foreign_amount' => $transaction->foreign_amount,
'reconciled' => false,
'identifier' => $index,
];
$return[] = $single;
}
return $return;
}
/**
* @param Recurrence $recurrence
* @param array $occurrences
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function handleOccurrences(Recurrence $recurrence, array $occurrences): void
{
/** @var Carbon $date */
foreach ($occurrences as $date) {
Log::debug(sprintf('Now at date %s.', $date->format('Y-m-d')));
if ($date->ne($this->date)) {
Log::debug(sprintf('%s is not not today (%s)', $date->format('Y-m-d'), $this->date->format('Y-m-d')));
continue;
}
Log::debug(sprintf('%s IS today (%s)', $date->format('Y-m-d'), $this->date->format('Y-m-d')));
// create transaction array and send to factory.
$array = [
'type' => $recurrence->transactionType->type,
'date' => $date,
'tags' => $this->repository->getTags($recurrence),
'user' => $recurrence->user_id,
'notes' => trans('firefly.created_from_recurrence', ['id' => $recurrence->id, 'title' => $recurrence->title]),
// journal data:
'description' => $recurrence->recurrenceTransactions()->first()->description,
'piggy_bank_id' => null,
'piggy_bank_name' => null,
'bill_id' => null,
'bill_name' => null,
'recurrence_id' => $recurrence->id,
// transaction data:
'transactions' => $this->getTransactionData($recurrence),
];
$journal = $this->journalRepository->store($array);
Log::info(sprintf('Created new journal #%d', $journal->id));
// update recurring thing:
$recurrence->latest_date = $date;
$recurrence->save();
}
}
/**
* Separate method that will loop all repetitions and do something with it:
*
* @param Recurrence $recurrence
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function handleRepetitions(Recurrence $recurrence): void
{
/** @var RecurrenceRepetition $repetition */
foreach ($recurrence->recurrenceRepetitions as $repetition) {
Log::debug(
sprintf(
'Now repeating %s with value "%s", skips every %d time(s)', $repetition->repetition_type, $repetition->repetition_moment,
$repetition->repetition_skip
)
);
// start looping from $startDate to today perhaps we have a hit?
$occurrences = $this->repository->getOccurrencesInRange($repetition, $recurrence->first_date, $this->date);
Log::debug(
sprintf(
'Calculated %d occurrences between %s and %s', \count($occurrences), $recurrence->first_date->format('Y-m-d'), $this->date->format('Y-m-d')
), $this->debugArray($occurrences)
);
$this->handleOccurrences($recurrence, $occurrences);
}
}
/**
* @param Recurrence $recurrence
*
* @return bool
*/
private function hasFiredToday(Recurrence $recurrence): bool
{
return null !== $recurrence->latest_date && $recurrence->latest_date->eq($this->date);
}
/**
* @param $recurrence
*
* @return bool
*/
private function hasNotStartedYet(Recurrence $recurrence): bool
{
$startDate = $this->getStartDate($recurrence);
return $startDate->gt($this->date);
}
/**
* Return true if the $repeat_until date is in the past.
*
* @param Recurrence $recurrence
*
* @return bool
*/
private function repeatUntilHasPassed(Recurrence $recurrence): bool
{
// date has passed
return null !== $recurrence->repeat_until && $recurrence->repeat_until->lt($this->date);
}
/**
* @param Recurrence $recurrence
*
* @return bool
*/
private function validRecurrence(Recurrence $recurrence): bool
{
// is not active.
if (!$this->active($recurrence)) {
Log::info(sprintf('Recurrence #%d is not active. Skipped.', $recurrence->id));
return false;
}
// is no longer running
if ($this->repeatUntilHasPassed($recurrence)) {
Log::info(
sprintf(
'Recurrence #%d was set to run until %s, and today\'s date is %s. Skipped.',
$recurrence->id,
$recurrence->repeat_until->format('Y-m-d'),
$this->date->format('Y-m-d')
)
);
return false;
}
// first_date is in the future
if ($this->hasNotStartedYet($recurrence)) {
Log::info(
sprintf(
'Recurrence #%d is set to run on %s, and today\'s date is %s. Skipped.',
$recurrence->id,
$recurrence->first_date->format('Y-m-d'),
$this->date->format('Y-m-d')
)
);
return false;
}
// already fired today (with success):
if ($this->hasFiredToday($recurrence)) {
Log::info(sprintf('Recurrence #%d has already fired today. Skipped.', $recurrence->id));
return false;
}
return true;
}
}

View File

@ -29,11 +29,14 @@ use FireflyIII\Factory\RecurrenceFactory;
use FireflyIII\Models\Note;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceMeta;
use FireflyIII\Models\RecurrenceRepetition;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\RecurrenceTransactionMeta;
use FireflyIII\Services\Internal\Update\RecurrenceUpdateService;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Log;
/**
*
@ -49,11 +52,68 @@ class RecurringRepository implements RecurringRepositoryInterface
*
* @return Collection
*/
public function getActive(): Collection
public function get(): Collection
{
return $this->user->recurrences()->with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions'])->where(
'active', 1
)->get();
return $this->user->recurrences()
->with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions'])
->orderBy('active', 'DESC')
->orderBy('title', 'ASC')
->get();
}
/**
* Get ALL recurring transactions.
*
* @return Collection
*/
public function getAll(): Collection
{
// grab ALL recurring transactions:
return Recurrence
::with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions'])
->orderBy('active', 'DESC')
->orderBy('title', 'ASC')
->get();
}
/**
* Get the budget ID from a recurring transaction transaction.
*
* @param RecurrenceTransaction $recurrenceTransaction
*
* @return null|int
*/
public function getBudget(RecurrenceTransaction $recurrenceTransaction): ?int
{
$return = 0;
/** @var RecurrenceTransactionMeta $meta */
foreach ($recurrenceTransaction->recurrenceTransactionMeta as $meta) {
if ($meta->name === 'budget_id') {
$return = (int)$meta->value;
}
}
return $return === 0 ? null : $return;
}
/**
* Get the category from a recurring transaction transaction.
*
* @param RecurrenceTransaction $recurrenceTransaction
*
* @return null|string
*/
public function getCategory(RecurrenceTransaction $recurrenceTransaction): ?string
{
$return = '';
/** @var RecurrenceTransactionMeta $meta */
foreach ($recurrenceTransaction->recurrenceTransactionMeta as $meta) {
if ($meta->name === 'category_name') {
$return = (string)$meta->value;
}
}
return $return === '' ? null : $return;
}
/**
@ -92,6 +152,7 @@ class RecurringRepository implements RecurringRepositoryInterface
$mutator->startOfDay();
$skipMod = $repetition->repetition_skip + 1;
$attempts = 0;
Log::debug(sprintf('Calculating occurrences for rep type "%s"', $repetition->repetition_type));
switch ($repetition->repetition_type) {
default:
throw new FireflyException(
@ -120,7 +181,7 @@ class RecurringRepository implements RecurringRepositoryInterface
$dayDifference = $dayOfWeek - $mutator->dayOfWeekIso;
$mutator->addDays($dayDifference);
while ($mutator <= $end) {
if ($attempts % $skipMod === 0) {
if ($attempts % $skipMod === 0 && $start->lte($mutator) && $end->gte($mutator)) {
$return[] = clone $mutator;
}
$attempts++;
@ -129,19 +190,31 @@ class RecurringRepository implements RecurringRepositoryInterface
break;
case 'monthly':
$dayOfMonth = (int)$repetition->repetition_moment;
Log::debug(sprintf('Day of month in repetition is %d', $dayOfMonth));
Log::debug(sprintf('Start is %s.', $start->format('Y-m-d')));
Log::debug(sprintf('End is %s.', $end->format('Y-m-d')));
if ($mutator->day > $dayOfMonth) {
Log::debug('Add a month.');
// day has passed already, add a month.
$mutator->addMonth();
}
Log::debug(sprintf('Start is now %s.', $mutator->format('Y-m-d')));
Log::debug('Start loop.');
while ($mutator < $end) {
Log::debug(sprintf('Mutator is now %s.', $mutator->format('Y-m-d')));
$domCorrected = min($dayOfMonth, $mutator->daysInMonth);
Log::debug(sprintf('DoM corrected is %d', $domCorrected));
$mutator->day = $domCorrected;
if ($attempts % $skipMod === 0) {
Log::debug(sprintf('Mutator is now %s.', $mutator->format('Y-m-d')));
Log::debug(sprintf('$attempts %% $skipMod === 0 is %s', var_export($attempts % $skipMod === 0, true)));
Log::debug(sprintf('$start->lte($mutator) is %s', var_export($start->lte($mutator), true)));
Log::debug(sprintf('$end->gte($mutator) is %s', var_export($end->gte($mutator), true)));
if ($attempts % $skipMod === 0 && $start->lte($mutator) && $end->gte($mutator)) {
Log::debug(sprintf('ADD %s to return!', $mutator->format('Y-m-d')));
$return[] = clone $mutator;
}
$attempts++;
$mutator->endOfMonth()->addDay();
$mutator->endOfMonth()->startOfDay()->addDay();
}
break;
case 'ndom':
@ -180,6 +253,26 @@ class RecurringRepository implements RecurringRepositoryInterface
return $return;
}
/**
* Get the tags from the recurring transaction.
*
* @param Recurrence $recurrence
*
* @return array
*/
public function getTags(Recurrence $recurrence): array
{
$tags = [];
/** @var RecurrenceMeta $meta */
foreach ($recurrence->recurrenceMeta as $meta) {
if ($meta->name === 'tags' && '' !== $meta->value) {
$tags = explode(',', $meta->value);
}
}
return $tags;
}
/**
* Calculate the next X iterations starting on the date given in $date.
*
@ -377,6 +470,7 @@ class RecurringRepository implements RecurringRepositoryInterface
* @param array $data
*
* @return Recurrence
* @throws FireflyException
*/
public function update(Recurrence $recurrence, array $data): Recurrence
{

View File

@ -27,6 +27,7 @@ use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceRepetition;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\User;
use Illuminate\Support\Collection;
@ -43,7 +44,31 @@ interface RecurringRepositoryInterface
*
* @return Collection
*/
public function getActive(): Collection;
public function get(): Collection;
/**
* Get ALL recurring transactions.
* @return Collection
*/
public function getAll(): Collection;
/**
* Get the category from a recurring transaction transaction.
*
* @param RecurrenceTransaction $recurrenceTransaction
*
* @return null|string
*/
public function getCategory(RecurrenceTransaction $recurrenceTransaction): ?string;
/**
* Get the budget ID from a recurring transaction transaction.
*
* @param RecurrenceTransaction $recurrenceTransaction
*
* @return null|int
*/
public function getBudget(RecurrenceTransaction $recurrenceTransaction): ?int;
/**
* Get the notes.
@ -67,6 +92,15 @@ interface RecurringRepositoryInterface
*/
public function getOccurrencesInRange(RecurrenceRepetition $repetition, Carbon $start, Carbon $end): array;
/**
* Get the tags from the recurring transaction.
*
* @param Recurrence $recurrence
*
* @return array
*/
public function getTags(Recurrence $recurrence): array;
/**
* Calculate the next X iterations starting on the date given in $date.
* Returns an array of Carbon objects.
@ -98,6 +132,7 @@ interface RecurringRepositoryInterface
/**
* Store a new recurring transaction.
*\
*
* @param array $data
*

View File

@ -515,16 +515,7 @@ class ExpandedForm
*/
public function optionsList(string $type, string $name): string
{
$previousValue = null;
try {
$previousValue = request()->old('post_submit_action');
} catch (RuntimeException $e) {
// don't care
}
$previousValue = $previousValue ?? 'store';
$html = view('form.options', compact('type', 'name', 'previousValue'))->render();
$html = view('form.options', compact('type', 'name'))->render();
return $html;
}

50
composer.lock generated
View File

@ -1150,16 +1150,16 @@
},
{
"name": "laravel/framework",
"version": "v5.6.24",
"version": "v5.6.25",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "56290edeb0d8051826d40b4cbd8ed3c30348b2b5"
"reference": "a15efe4fbcd6b38ea76edc5c8939ffde4f4c7b7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/56290edeb0d8051826d40b4cbd8ed3c30348b2b5",
"reference": "56290edeb0d8051826d40b4cbd8ed3c30348b2b5",
"url": "https://api.github.com/repos/laravel/framework/zipball/a15efe4fbcd6b38ea76edc5c8939ffde4f4c7b7f",
"reference": "a15efe4fbcd6b38ea76edc5c8939ffde4f4c7b7f",
"shasum": ""
},
"require": {
@ -1285,7 +1285,7 @@
"framework",
"laravel"
],
"time": "2018-06-04T14:51:03+00:00"
"time": "2018-06-12T14:39:24+00:00"
},
{
"name": "laravel/passport",
@ -4457,16 +4457,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.8.0",
"version": "1.8.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6"
"reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/478465659fd987669df0bd8a9bf22a8710e5f1b6",
"reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
"reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
"shasum": ""
},
"require": {
@ -4501,7 +4501,7 @@
"object",
"object graph"
],
"time": "2018-05-29T17:25:09+00:00"
"time": "2018-06-11T23:09:50+00:00"
},
{
"name": "phar-io/manifest",
@ -4968,16 +4968,16 @@
},
{
"name": "phpunit/php-file-iterator",
"version": "2.0.0",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "e20525b0c2945c7c317fff95660698cb3d2a53bc"
"reference": "cecbc684605bb0cc288828eb5d65d93d5c676d3c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/e20525b0c2945c7c317fff95660698cb3d2a53bc",
"reference": "e20525b0c2945c7c317fff95660698cb3d2a53bc",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cecbc684605bb0cc288828eb5d65d93d5c676d3c",
"reference": "cecbc684605bb0cc288828eb5d65d93d5c676d3c",
"shasum": ""
},
"require": {
@ -5011,7 +5011,7 @@
"filesystem",
"iterator"
],
"time": "2018-05-28T12:13:49+00:00"
"time": "2018-06-11T11:44:00+00:00"
},
{
"name": "phpunit/php-text-template",
@ -5450,16 +5450,16 @@
},
{
"name": "sebastian/comparator",
"version": "3.0.0",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "ed5fd2281113729f1ebcc64d101ad66028aeb3d5"
"reference": "591a30922f54656695e59b1f39501aec513403da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/ed5fd2281113729f1ebcc64d101ad66028aeb3d5",
"reference": "ed5fd2281113729f1ebcc64d101ad66028aeb3d5",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/591a30922f54656695e59b1f39501aec513403da",
"reference": "591a30922f54656695e59b1f39501aec513403da",
"shasum": ""
},
"require": {
@ -5510,20 +5510,20 @@
"compare",
"equality"
],
"time": "2018-04-18T13:33:00+00:00"
"time": "2018-06-14T15:05:28+00:00"
},
{
"name": "sebastian/diff",
"version": "3.0.0",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "e09160918c66281713f1c324c1f4c4c3037ba1e8"
"reference": "366541b989927187c4ca70490a35615d3fef2dce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/e09160918c66281713f1c324c1f4c4c3037ba1e8",
"reference": "e09160918c66281713f1c324c1f4c4c3037ba1e8",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce",
"reference": "366541b989927187c4ca70490a35615d3fef2dce",
"shasum": ""
},
"require": {
@ -5566,7 +5566,7 @@
"unidiff",
"unified diff"
],
"time": "2018-02-01T13:45:15+00:00"
"time": "2018-06-10T07:54:39+00:00"
},
{
"name": "sebastian/environment",

View File

@ -100,11 +100,19 @@ function respondToRepetitionEnd() {
}
function respondToFirstDateChange() {
//
var obj = $('#ffInput_first_date');
var select = $('#ffInput_repetition_type');
var date = obj.val();
select.prop('disabled', true);
$.getJSON(suggestUri, {date: date,past:true}).fail(function () {
// preselected value:
var preSelected = currentRepetitionType;
if(preSelected === '') {
preSelected = select.val();
}
$.getJSON(suggestUri, {date: date,pre_select: preSelected,past:true}).fail(function () {
console.error('Could not load repetition suggestions');
alert('Could not load repetition suggestions');
}).done(parseRepetitionSuggestions);
@ -117,8 +125,9 @@ function parseRepetitionSuggestions(data) {
var opt;
for (var k in data) {
if (data.hasOwnProperty(k)) {
opt = $('<option>').val(k).attr('label', data[k]).text(data[k]);
if(k === currentRepetitionType) {
console.log('label: ' + data[k].label + ', selected: ' + data[k].selected);
opt = $('<option>').val(k).attr('label', data[k].label).text(data[k].label);
if(data[k].selected) {
opt.attr('selected','selected');
}
select.append(opt);

View File

@ -100,11 +100,7 @@ There are many ways to run Firefly III
5. You can [deploy to Sandstorm.io](https://apps.sandstorm.io/app/uws252ya9mep4t77tevn85333xzsgrpgth8q4y1rhknn1hammw70)
6. You can [install it using Softaculous](https://softaculous.com/). These guys even have made [another demo site](http://www.softaculous.com/softaculous/apps/others/Firefly_III)!
7. You can [install it using AMPPS](https://www.ampps.com/)
8. Install it on [YunoHost](https://yunohost.org).
[![Install Firefly-III with YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=firefly-iii)
See [here.](https://github.com/YunoHost-Apps/Firefly-III_ynh).
8. You can [install it with YunoHost](https://install-app.yunohost.org/?app=firefly-iii).
9. *Even more options are on the way!*
### Update your instance

View File

@ -1223,33 +1223,41 @@ return [
'overview_for_recurrence' => 'Overview for recurring transaction ":title"',
'warning_duplicates_repetitions' => 'In rare instances, dates appear twice in this list. This can happen when multiple repetitions collide. Firefly III will always generate one transaction per day.',
'created_transactions' => 'Related transactions',
'expected_transactions' => 'Expected transactions',
'recurring_meta_field_tags' => 'Tags',
'recurring_meta_field_notes' => 'Notes',
'recurring_meta_field_bill_id' => 'Bill',
'recurring_meta_field_piggy_bank_id' => 'Piggy bank',
'create_new_recurrence' => 'Create new recurring transaction',
'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.',
'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.',
'no_currency' => '(no currency)',
'mandatory_for_recurring' => 'Mandatory recurrence information',
'mandatory_for_transaction' => 'Mandatory transaction information',
'optional_for_recurring' => 'Optional recurrence information',
'optional_for_transaction' => 'Optional transaction information',
'change_date_other_options' => 'Change the "first date" to see more options.',
'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created',
'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.',
'repeat_forever' => 'Repeat forever',
'repeat_until_date' => 'Repeat until date',
'repeat_times' => 'Repeat a number of times',
'recurring_skips_one' => 'Every other',
'recurring_skips_more' => 'Skips :count occurrences',
'store_new_recurrence' => 'Store recurring transaction',
'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.',
'edit_recurrence' => 'Edit recurring transaction ":title"',
'recurring_repeats_until' => 'Repeats until :date',
'recurring_repeats_forever' => 'Repeats forever',
'recurring_repeats_x_times' => 'Repeats :count time(s)',
'update_recurrence' => 'Update recurring transaction',
'updated_recurrence' => 'Updated recurring transaction ":title"',
'expected_Withdrawals' => 'Expected withdrawals',
'expected_Deposits' => 'Expected deposits',
'expected_Transfers' => 'Expected transfers',
'created_Withdrawals' => 'Created withdrawals',
'created_Deposits' => 'Created deposits',
'created_Transfers' => 'Created transfers',
'created_from_recurrence' => 'Created from recurring transaction ":title" (#:id)',
'recurring_meta_field_tags' => 'Tags',
'recurring_meta_field_notes' => 'Notes',
'recurring_meta_field_bill_id' => 'Bill',
'recurring_meta_field_piggy_bank_id' => 'Piggy bank',
'create_new_recurrence' => 'Create new recurring transaction',
'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.',
'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.',
'no_currency' => '(no currency)',
'mandatory_for_recurring' => 'Mandatory recurrence information',
'mandatory_for_transaction' => 'Mandatory transaction information',
'optional_for_recurring' => 'Optional recurrence information',
'optional_for_transaction' => 'Optional transaction information',
'change_date_other_options' => 'Change the "first date" to see more options.',
'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created',
'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.',
'repeat_forever' => 'Repeat forever',
'repeat_until_date' => 'Repeat until date',
'repeat_times' => 'Repeat a number of times',
'recurring_skips_one' => 'Every other',
'recurring_skips_more' => 'Skips :count occurrences',
'store_new_recurrence' => 'Store recurring transaction',
'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.',
'edit_recurrence' => 'Edit recurring transaction ":title"',
'recurring_repeats_until' => 'Repeats until :date',
'recurring_repeats_forever' => 'Repeats forever',
'recurring_repeats_x_times' => 'Repeats :count time(s)',
'update_recurrence' => 'Update recurring transaction',
'updated_recurrence' => 'Updated recurring transaction ":title"',
'recurrence_is_inactive' => 'This recurring transaction is not active and will not generate new transactions.',
];

View File

@ -24,7 +24,7 @@
<div class="col-sm-8">
<div class="checkbox"><label>
{{ Form.checkbox('return_to_edit', '1', old('return_to_edit') == '1', {'id': name ~ '_return_to_edit'}) }}
{{ Form.checkbox('return_to_edit', '1', null, {'id': name ~ '_return_to_edit'}) }}
{{ trans('form.returnHereUpdateExplanation') }}
</label>
</div>

View File

@ -222,7 +222,7 @@
var transactionType = "{{ preFilled.transaction_type }}";
var suggestUri = "{{ route('recurring.suggest') }}";
var eventsUri = "{{ route('recurring.events') }}";
var currentRepetitionType= "{{ currentRepetitionType }}";
var currentRepetitionType = "{{ currentRepetitionType }}";
</script>
<script type="text/javascript" src="js/ff/recurring/edit.js?v={{ FF_VERSION }}"></script>
{% endblock %}

View File

@ -54,12 +54,15 @@
</div>
</td>
<td data-value="{{ rt.title }}">
{% if rt.active == false %}<s>{% endif %}
{{ rt.transaction_type|_ }}:
<a href="{{ route('recurring.show',rt.id) }}">{{ rt.title }}</a>
{% if rt.active == false %}</s> ({{ 'inactive'|_|lower }}){% endif %}
{% if rt.description|length > 0 %}
<small><br>{{ rt.description }}</small>
{% endif %}
</td>
<td data-value="0">
<ol>

View File

@ -12,10 +12,23 @@
<div class="box-header with-border">
<h3 class="box-title">
{{ array.title }}
({{ array.transaction_type }})
{% if array.active == false %}
({{ 'inactive'|_|lower }})
{% endif %}
</h3>
</div>
<div class="box-body">
<p><em>{{ array.description }}</em></p>
{% if array.active == false %}
<p>
{{ 'recurrence_is_inactive'|_ }}
</p>
{% endif %}
<ul>
{% for rep in array.repetitions %}
<li>{{ rep.description }}</li>
@ -35,13 +48,13 @@
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">
{{ 'expected_transactions'|_ }}
{{ ('expected_'~array.transaction_type~'s')|_ }}
</h3>
</div>
<div class="box-body">
<ul>
{% for rep in array.repetitions %}
{% for rep in array.recurrence_repetitions %}
<li>
{{ rep.description }}
{% if rep.repetition_skip == 1 %}
@ -128,47 +141,47 @@
<!-- meta data -->
{% if array.meta|length > 0 %}
<div class="col-lg-4 col-md-12 col-sm-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">
{{ 'meta_data'|_ }}
</h3>
</div>
<div class="box-body no-padding">
<table class="table table-hover sortable">
<thead>
<th style="width:30%;" data-defaultsign="az">{{ trans('list.field') }}</th>
<th data-defaultsign="az">{{ trans('list.value') }}</th>
</thead>
<tbody>
{% for meta in array.meta %}
<tr>
<td>{{ trans('firefly.recurring_meta_field_'~meta.name) }}</td>
<td>
{% if meta.name == 'tags' %}
{% for tag in meta.tags %}
<span class="label label-info">{{ tag }}</span>
{% endfor %}
{% endif %}
{% if meta.name == 'notes' %}
{{ meta.value|markdown }}
{% endif %}
{% if meta.name == 'bill_id' %}
<a href="{{ route('bills.show', [meta.bill_id]) }}">{{ meta.bill_name }}</a>
{% endif %}
{% if meta.name == 'piggy_bank_id' %}
<a href="{{ route('piggy-banks.show', [meta.piggy_bank_id]) }}">{{ meta.piggy_bank_name }}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
<div class="col-lg-4 col-md-12 col-sm-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">
{{ 'meta_data'|_ }}
</h3>
</div>
<div class="box-body no-padding">
<table class="table table-hover sortable">
<thead>
<th style="width:30%;" data-defaultsign="az">{{ trans('list.field') }}</th>
<th data-defaultsign="az">{{ trans('list.value') }}</th>
</thead>
<tbody>
{% for meta in array.meta %}
<tr>
<td>{{ trans('firefly.recurring_meta_field_'~meta.name) }}</td>
<td>
{% if meta.name == 'tags' %}
{% for tag in meta.tags %}
<span class="label label-info">{{ tag }}</span>
{% endfor %}
{% endif %}
{% if meta.name == 'notes' %}
{{ meta.value|markdown }}
{% endif %}
{% if meta.name == 'bill_id' %}
<a href="{{ route('bills.show', [meta.bill_id]) }}">{{ meta.bill_name }}</a>
{% endif %}
{% if meta.name == 'piggy_bank_id' %}
<a href="{{ route('piggy-banks.show', [meta.piggy_bank_id]) }}">{{ meta.piggy_bank_name }}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</table>
</div>
</div>
</div>
</div>
{% endif %}
</div>
<div class="row">
@ -177,11 +190,11 @@
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">
{{ 'transactions'|_ }}
{{ ('created_'~array.transaction_type~'s')|_ }}
</h3>
</div>
<div class="box-body">
Bla bla
List be here.
</div>
</div>
</div>