mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Merge branch 'release/4.1.4'
This commit is contained in:
commit
0a6f299ae6
30
CHANGELOG.md
30
CHANGELOG.md
@ -2,7 +2,37 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [4.1.4] - 2016-10-30
|
||||
### Added
|
||||
- New Dockerfile thanks to @schoentoon
|
||||
- Added changing the destination account as rule action.
|
||||
- Added changing the source account as rule action.
|
||||
- Can convert transactions into different types.
|
||||
|
||||
### Changed
|
||||
- Changed the export routine to be more future-proof.
|
||||
- Improved help routine.
|
||||
- Integrated CrowdIn translations.
|
||||
- Simplified reports
|
||||
- Change error message to refer to solution.
|
||||
|
||||
### Fixed
|
||||
- #367 thanks to @HungryFeline
|
||||
- #366 thanks to @3mz3t
|
||||
- #362 and #341 thanks to @bnw
|
||||
- #355 thanks to @roberthorlings
|
||||
|
||||
## [4.1.3] - 2016-10-22
|
||||
### Fixed
|
||||
- Some event handlers called the wrong method.
|
||||
|
||||
## [4.1.2] - 2016-10-22
|
||||
|
||||
### Fixed
|
||||
- A bug is fixed in the journal event handler that prevented Firefly III from actually storing journals.
|
||||
|
||||
## [4.1.1] - 2016-10-22
|
||||
|
||||
### Added
|
||||
- Option to show deposit accounts on the front page.
|
||||
- Script to upgrade split transactions
|
||||
|
42
Dockerfile
Normal file
42
Dockerfile
Normal file
@ -0,0 +1,42 @@
|
||||
FROM php:7-apache
|
||||
|
||||
RUN apt-get update -y && \
|
||||
apt-get install -y --no-install-recommends libcurl4-openssl-dev \
|
||||
zlib1g-dev \
|
||||
libjpeg62-turbo-dev \
|
||||
libpng12-dev \
|
||||
libicu-dev \
|
||||
libmcrypt-dev \
|
||||
libedit-dev \
|
||||
libtidy-dev \
|
||||
libxml2-dev \
|
||||
libsqlite3-dev \
|
||||
libbz2-dev && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2
|
||||
|
||||
# Enable apache mod rewrite..
|
||||
RUN a2enmod rewrite
|
||||
|
||||
# Setup the Composer installer
|
||||
RUN curl -o /tmp/composer-setup.php https://getcomposer.org/installer && \
|
||||
curl -o /tmp/composer-setup.sig https://composer.github.io/installer.sig && \
|
||||
php -r "if (hash('SHA384', file_get_contents('/tmp/composer-setup.php')) !== trim(file_get_contents('/tmp/composer-setup.sig'))) { unlink('/tmp/composer-setup.php'); echo 'Invalid installer' . PHP_EOL; exit(1); }" && \
|
||||
chmod +x /tmp/composer-setup.php && \
|
||||
php /tmp/composer-setup.php && \
|
||||
mv composer.phar /usr/local/bin/composer && \
|
||||
rm -f /tmp/composer-setup.{php,sig}
|
||||
|
||||
ADD . /var/www/firefly-iii
|
||||
RUN chown -R www-data:www-data /var/www/
|
||||
ADD docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
|
||||
|
||||
USER www-data
|
||||
|
||||
WORKDIR /var/www/firefly-iii
|
||||
|
||||
RUN composer install --no-scripts --no-dev
|
||||
|
||||
USER root
|
@ -15,6 +15,7 @@ namespace FireflyIII\Console\Commands;
|
||||
|
||||
use Crypt;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
@ -84,6 +85,9 @@ class VerifyDatabase extends Command
|
||||
|
||||
// transfers with budgets.
|
||||
$this->reportTransfersBudgets();
|
||||
|
||||
// report on journals with the wrong types of accounts.
|
||||
$this->reportIncorrectJournals();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -202,6 +206,45 @@ class VerifyDatabase extends Command
|
||||
}
|
||||
}
|
||||
|
||||
private function reportIncorrectJournals()
|
||||
{
|
||||
$configuration = [
|
||||
// a withdrawal can not have revenue account:
|
||||
TransactionType::WITHDRAWAL => [AccountType::REVENUE],
|
||||
|
||||
// deposit cannot have an expense account:
|
||||
TransactionType::DEPOSIT => [AccountType::EXPENSE],
|
||||
|
||||
// transfer cannot have either:
|
||||
TransactionType::TRANSFER => [AccountType::EXPENSE, AccountType::REVENUE],
|
||||
];
|
||||
foreach ($configuration as $transactionType => $accountTypes) {
|
||||
$set = TransactionJournal
|
||||
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
|
||||
->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id')
|
||||
->leftJoin('users', 'users.id', '=', 'transaction_journals.user_id')
|
||||
->where('transaction_types.type', $transactionType)
|
||||
->whereIn('account_types.type', $accountTypes)
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->get(['transaction_journals.id', 'transaction_journals.user_id', 'users.email', 'account_types.type as a_type', 'transaction_types.type']);
|
||||
foreach ($set as $entry) {
|
||||
$this->error(
|
||||
sprintf(
|
||||
'Transaction journal #%d (user #%d, %s) is of type "%s" but ' .
|
||||
'is linked to a "%s". The transaction journal should be recreated.',
|
||||
$entry->id,
|
||||
$entry->user_id,
|
||||
$entry->email,
|
||||
$entry->type,
|
||||
$entry->a_type
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Any deleted transaction journals that have transactions that are NOT deleted:
|
||||
*/
|
||||
|
@ -13,11 +13,10 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Export\Collector;
|
||||
|
||||
use Amount;
|
||||
use Carbon\Carbon;
|
||||
use Crypt;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\ExportJob;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Support\Collection;
|
||||
@ -31,12 +30,14 @@ use Storage;
|
||||
*/
|
||||
class AttachmentCollector extends BasicCollector implements CollectorInterface
|
||||
{
|
||||
/** @var string */
|
||||
private $explanationString = '';
|
||||
/** @var Carbon */
|
||||
private $end;
|
||||
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
|
||||
private $exportDisk;
|
||||
/** @var AttachmentRepositoryInterface */
|
||||
private $repository;
|
||||
/** @var Carbon */
|
||||
private $start;
|
||||
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
|
||||
private $uploadDisk;
|
||||
|
||||
@ -69,34 +70,17 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
|
||||
$this->exportAttachment($attachment);
|
||||
}
|
||||
|
||||
// put the explanation string in a file and attach it as well.
|
||||
$file = $this->job->key . '-Source of all your attachments explained.txt';
|
||||
$this->exportDisk->put($file, $this->explanationString);
|
||||
$this->getFiles()->push($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*/
|
||||
private function explain(Attachment $attachment)
|
||||
public function setDates(Carbon $start, Carbon $end)
|
||||
{
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $attachment->attachable;
|
||||
$args = [
|
||||
'attachment_name' => e($attachment->filename),
|
||||
'attachment_id' => $attachment->id,
|
||||
'type' => strtolower($journal->transactionType->type),
|
||||
'description' => e($journal->description),
|
||||
'journal_id' => $journal->id,
|
||||
'date' => $journal->date->formatLocalized(strval(trans('config.month_and_day'))),
|
||||
'amount' => Amount::formatJournal($journal, false),
|
||||
];
|
||||
$string = trans('firefly.attachment_explanation', $args) . "\n";
|
||||
Log::debug('Appended explanation string', ['string' => $string]);
|
||||
$this->explanationString .= $string;
|
||||
|
||||
$this->start = $start;
|
||||
$this->end = $end;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,10 +96,8 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
|
||||
$decrypted = Crypt::decrypt($this->uploadDisk->get($file));
|
||||
$exportFile = $this->exportFileName($attachment);
|
||||
$this->exportDisk->put($exportFile, $decrypted);
|
||||
$this->getFiles()->push($exportFile);
|
||||
$this->getEntries()->push($exportFile);
|
||||
|
||||
// explain:
|
||||
$this->explain($attachment);
|
||||
} catch (DecryptException $e) {
|
||||
Log::error('Catchable error: could not decrypt attachment #' . $attachment->id . ' because: ' . $e->getMessage());
|
||||
}
|
||||
@ -143,7 +125,7 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
|
||||
*/
|
||||
private function getAttachments(): Collection
|
||||
{
|
||||
$attachments = $this->repository->get();
|
||||
$attachments = $this->repository->getBetween($this->start, $this->end);
|
||||
|
||||
return $attachments;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class BasicCollector
|
||||
/** @var ExportJob */
|
||||
protected $job;
|
||||
/** @var Collection */
|
||||
private $files;
|
||||
private $entries;
|
||||
|
||||
/**
|
||||
* BasicCollector constructor.
|
||||
@ -36,24 +36,24 @@ class BasicCollector
|
||||
*/
|
||||
public function __construct(ExportJob $job)
|
||||
{
|
||||
$this->files = new Collection;
|
||||
$this->entries = new Collection;
|
||||
$this->job = $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getFiles(): Collection
|
||||
public function getEntries(): Collection
|
||||
{
|
||||
return $this->files;
|
||||
return $this->entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $files
|
||||
* @param Collection $entries
|
||||
*/
|
||||
public function setFiles(Collection $files)
|
||||
public function setEntries(Collection $entries)
|
||||
{
|
||||
$this->files = $files;
|
||||
$this->entries = $entries;
|
||||
}
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ interface CollectorInterface
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getFiles(): Collection;
|
||||
public function getEntries(): Collection;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
@ -33,9 +33,9 @@ interface CollectorInterface
|
||||
public function run(): bool;
|
||||
|
||||
/**
|
||||
* @param Collection $files
|
||||
* @param Collection $entries
|
||||
*
|
||||
*/
|
||||
public function setFiles(Collection $files);
|
||||
public function setEntries(Collection $entries);
|
||||
|
||||
}
|
||||
|
348
app/Export/Collector/JournalCollector.php
Normal file
348
app/Export/Collector/JournalCollector.php
Normal file
@ -0,0 +1,348 @@
|
||||
<?php
|
||||
/**
|
||||
* JournalCollector.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Export\Collector;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Crypt;
|
||||
use DB;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class JournalCollector
|
||||
*
|
||||
* @package FireflyIII\Export\Collector
|
||||
*/
|
||||
class JournalCollector extends BasicCollector implements CollectorInterface
|
||||
{
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var Carbon */
|
||||
private $end;
|
||||
/** @var Carbon */
|
||||
private $start;
|
||||
|
||||
/** @var Collection */
|
||||
private $workSet;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function run(): bool
|
||||
{
|
||||
/*
|
||||
* Instead of collecting journals we collect transactions for the given accounts.
|
||||
* We left join the OPPOSING transaction AND some journal data.
|
||||
* After that we complement this info with budgets, categories, etc.
|
||||
*
|
||||
* This is way more efficient and will also work on split journals.
|
||||
*/
|
||||
$this->getWorkSet();
|
||||
|
||||
/*
|
||||
* Extract:
|
||||
* possible budget ids for journals
|
||||
* possible category ids journals
|
||||
* possible budget ids for transactions
|
||||
* possible category ids for transactions
|
||||
*
|
||||
* possible IBAN and account numbers?
|
||||
*
|
||||
*/
|
||||
$journals = $this->extractJournalIds();
|
||||
$transactions = $this->extractTransactionIds();
|
||||
|
||||
|
||||
// extend work set with category data from journals:
|
||||
$this->categoryDataForJournals($journals);
|
||||
|
||||
// extend work set with category cate from transactions (overrules journals):
|
||||
$this->categoryDataForTransactions($transactions);
|
||||
|
||||
// same for budgets:
|
||||
$this->budgetDataForJournals($journals);
|
||||
$this->budgetDataForTransactions($transactions);
|
||||
|
||||
$this->setEntries($this->workSet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*/
|
||||
public function setAccounts(Collection $accounts)
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*/
|
||||
public function setDates(Carbon $start, Carbon $end)
|
||||
{
|
||||
$this->start = $start;
|
||||
$this->end = $end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $journals
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function budgetDataForJournals(array $journals): bool
|
||||
{
|
||||
$set = DB::table('budget_transaction_journal')
|
||||
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
|
||||
->whereIn('budget_transaction_journal.transaction_journal_id', $journals)
|
||||
->get(
|
||||
[
|
||||
'budget_transaction_journal.budget_id',
|
||||
'budget_transaction_journal.transaction_journal_id',
|
||||
'budgets.name',
|
||||
'budgets.encrypted',
|
||||
]
|
||||
);
|
||||
$set->each(
|
||||
function ($obj) {
|
||||
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
|
||||
}
|
||||
);
|
||||
$array = [];
|
||||
foreach ($set as $obj) {
|
||||
$array[$obj->transaction_journal_id] = ['id' => $obj->budget_id, 'name' => $obj->name];
|
||||
}
|
||||
|
||||
$this->workSet->each(
|
||||
function ($obj) use ($array) {
|
||||
if (isset($array[$obj->transaction_journal_id])) {
|
||||
$obj->budget_id = $array[$obj->transaction_journal_id]['id'];
|
||||
$obj->budget_name = $array[$obj->transaction_journal_id]['name'];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $transactions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function budgetDataForTransactions(array $transactions): bool
|
||||
{
|
||||
$set = DB::table('budget_transaction')
|
||||
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction.budget_id')
|
||||
->whereIn('budget_transaction.transaction_id', $transactions)
|
||||
->get(
|
||||
[
|
||||
'budget_transaction.budget_id',
|
||||
'budget_transaction.transaction_id',
|
||||
'budgets.name',
|
||||
'budgets.encrypted',
|
||||
]
|
||||
);
|
||||
$set->each(
|
||||
function ($obj) {
|
||||
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
|
||||
}
|
||||
);
|
||||
$array = [];
|
||||
foreach ($set as $obj) {
|
||||
$array[$obj->transaction_id] = ['id' => $obj->budget_id, 'name' => $obj->name];
|
||||
}
|
||||
|
||||
$this->workSet->each(
|
||||
function ($obj) use ($array) {
|
||||
|
||||
// first transaction
|
||||
if (isset($array[$obj->id])) {
|
||||
$obj->budget_id = $array[$obj->id]['id'];
|
||||
$obj->budget_name = $array[$obj->id]['name'];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $journals
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function categoryDataForJournals(array $journals): bool
|
||||
{
|
||||
$set = DB::table('category_transaction_journal')
|
||||
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
|
||||
->whereIn('category_transaction_journal.transaction_journal_id', $journals)
|
||||
->get(
|
||||
[
|
||||
'category_transaction_journal.category_id',
|
||||
'category_transaction_journal.transaction_journal_id',
|
||||
'categories.name',
|
||||
'categories.encrypted',
|
||||
]
|
||||
);
|
||||
$set->each(
|
||||
function ($obj) {
|
||||
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
|
||||
}
|
||||
);
|
||||
$array = [];
|
||||
foreach ($set as $obj) {
|
||||
$array[$obj->transaction_journal_id] = ['id' => $obj->category_id, 'name' => $obj->name];
|
||||
}
|
||||
|
||||
$this->workSet->each(
|
||||
function ($obj) use ($array) {
|
||||
if (isset($array[$obj->transaction_journal_id])) {
|
||||
$obj->category_id = $array[$obj->transaction_journal_id]['id'];
|
||||
$obj->category_name = $array[$obj->transaction_journal_id]['name'];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $transactions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function categoryDataForTransactions(array $transactions): bool
|
||||
{
|
||||
$set = DB::table('category_transaction')
|
||||
->leftJoin('categories', 'categories.id', '=', 'category_transaction.category_id')
|
||||
->whereIn('category_transaction.transaction_id', $transactions)
|
||||
->get(
|
||||
[
|
||||
'category_transaction.category_id',
|
||||
'category_transaction.transaction_id',
|
||||
'categories.name',
|
||||
'categories.encrypted',
|
||||
]
|
||||
);
|
||||
$set->each(
|
||||
function ($obj) {
|
||||
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
|
||||
}
|
||||
);
|
||||
$array = [];
|
||||
foreach ($set as $obj) {
|
||||
$array[$obj->transaction_id] = ['id' => $obj->category_id, 'name' => $obj->name];
|
||||
}
|
||||
|
||||
$this->workSet->each(
|
||||
function ($obj) use ($array) {
|
||||
|
||||
// first transaction
|
||||
if (isset($array[$obj->id])) {
|
||||
$obj->category_id = $array[$obj->id]['id'];
|
||||
$obj->category_name = $array[$obj->id]['name'];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function extractJournalIds(): array
|
||||
{
|
||||
return $this->workSet->pluck('transaction_journal_id')->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function extractTransactionIds()
|
||||
{
|
||||
$set = $this->workSet->pluck('id')->toArray();
|
||||
$opposing = $this->workSet->pluck('opposing_id')->toArray();
|
||||
$complete = $set + $opposing;
|
||||
|
||||
return array_unique($complete);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function getWorkSet()
|
||||
{
|
||||
$accountIds = $this->accounts->pluck('id')->toArray();
|
||||
$this->workSet = Transaction
|
||||
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin(
|
||||
'transactions AS opposing', function (JoinClause $join) {
|
||||
$join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id')
|
||||
->where('opposing.amount', '=', DB::raw('transactions.amount * -1'))
|
||||
->where('transactions.identifier', '=', 'opposing.identifier');
|
||||
}
|
||||
)
|
||||
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
|
||||
->leftJoin('accounts AS opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id')
|
||||
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', 'transaction_types.id')
|
||||
->leftJoin('transaction_currencies', 'transaction_journals.transaction_currency_id', '=', 'transaction_currencies.id')
|
||||
->whereIn('transactions.account_id', $accountIds)
|
||||
->where('transaction_journals.user_id', $this->job->user_id)
|
||||
->where('transaction_journals.date', '>=', $this->start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $this->end->format('Y-m-d'))
|
||||
->where('transaction_journals.completed', 1)
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->whereNull('opposing.deleted_at')
|
||||
->orderBy('transaction_journals.date', 'DESC')
|
||||
->orderBy('transactions.identifier', 'ASC')
|
||||
->get(
|
||||
[
|
||||
'transactions.id',
|
||||
'transactions.amount',
|
||||
'transactions.description',
|
||||
'transactions.account_id',
|
||||
'accounts.name as account_name',
|
||||
'accounts.encrypted as account_name_encrypted',
|
||||
'transactions.identifier',
|
||||
|
||||
'opposing.id as opposing_id',
|
||||
'opposing.amount AS opposing_amount',
|
||||
'opposing.description as opposing_description',
|
||||
'opposing.account_id as opposing_account_id',
|
||||
'opposing_accounts.name as opposing_account_name',
|
||||
'opposing_accounts.encrypted as opposing_account_encrypted',
|
||||
'opposing.identifier as opposing_identifier',
|
||||
|
||||
'transaction_journals.id as transaction_journal_id',
|
||||
'transaction_journals.date',
|
||||
'transaction_journals.description as journal_description',
|
||||
'transaction_journals.encrypted as journal_encrypted',
|
||||
'transaction_journals.transaction_type_id',
|
||||
'transaction_types.type as transaction_type',
|
||||
'transaction_journals.transaction_currency_id',
|
||||
'transaction_currencies.code AS transaction_currency_code',
|
||||
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
@ -26,16 +26,14 @@ use Storage;
|
||||
*/
|
||||
class UploadCollector extends BasicCollector implements CollectorInterface
|
||||
{
|
||||
/** @var string */
|
||||
private $expected;
|
||||
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
|
||||
private $exportDisk;
|
||||
private $importKeys = [];
|
||||
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
|
||||
private $uploadDisk;
|
||||
/** @var string */
|
||||
private $vintageFormat;
|
||||
|
||||
/**
|
||||
*
|
||||
* AttachmentCollector constructor.
|
||||
*
|
||||
* @param ExportJob $job
|
||||
@ -51,50 +49,74 @@ class UploadCollector extends BasicCollector implements CollectorInterface
|
||||
$this->exportDisk = Storage::disk('export');
|
||||
|
||||
// file names associated with the old import routine.
|
||||
$this->expected = 'csv-upload-' . auth()->user()->id . '-';
|
||||
$this->vintageFormat = sprintf('csv-upload-%d-', auth()->user()->id);
|
||||
|
||||
// for the new import routine:
|
||||
$this->getImportKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is called from the outside to actually start the export.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function run(): bool
|
||||
{
|
||||
// grab upload directory.
|
||||
$files = $this->uploadDisk->files();
|
||||
// collect old upload files (names beginning with "csv-upload".
|
||||
$this->collectVintageUploads();
|
||||
|
||||
foreach ($files as $entry) {
|
||||
$this->processUpload($entry);
|
||||
// then collect current upload files:
|
||||
$this->collectModernUploads();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method collects all the uploads that are uploaded using the new importer. So after the summer of 2016.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function collectModernUploads(): bool
|
||||
{
|
||||
$set = $this->job->user->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']);
|
||||
$keys = [];
|
||||
if ($set->count() > 0) {
|
||||
$keys = $set->pluck('key')->toArray();
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$this->processModernUpload($key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method collects all the uploads that are uploaded using the "old" importer. So from before the summer of 2016.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function getImportKeys()
|
||||
private function collectVintageUploads():bool
|
||||
{
|
||||
$set = auth()->user()->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']);
|
||||
if ($set->count() > 0) {
|
||||
$keys = $set->pluck('key')->toArray();
|
||||
$this->importKeys = $keys;
|
||||
// grab upload directory.
|
||||
$files = $this->uploadDisk->files();
|
||||
|
||||
foreach ($files as $entry) {
|
||||
$this->processVintageUpload($entry);
|
||||
}
|
||||
Log::debug('Valid import keys are ', $this->importKeys);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method tells you when the vintage upload file was actually uploaded.
|
||||
*
|
||||
* @param string $entry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getOriginalUploadDate(string $entry): string
|
||||
private function getVintageUploadDate(string $entry): string
|
||||
{
|
||||
// this is an original upload.
|
||||
$parts = explode('-', str_replace(['.csv.encrypted', $this->expected], '', $entry));
|
||||
$parts = explode('-', str_replace(['.csv.encrypted', $this->vintageFormat], '', $entry));
|
||||
$originalUpload = intval($parts[1]);
|
||||
$date = date('Y-m-d \a\t H-i-s', $originalUpload);
|
||||
|
||||
@ -102,33 +124,17 @@ class UploadCollector extends BasicCollector implements CollectorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells you if a file name is a vintage upload.
|
||||
*
|
||||
* @param string $entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isImportFile(string $entry): bool
|
||||
private function isVintageImport(string $entry): bool
|
||||
{
|
||||
$name = str_replace('.upload', '', $entry);
|
||||
if (in_array($name, $this->importKeys)) {
|
||||
Log::debug(sprintf('Import file "%s" is in array', $name), $this->importKeys);
|
||||
|
||||
return true;
|
||||
}
|
||||
Log::debug(sprintf('Import file "%s" is NOT in array', $name), $this->importKeys);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isOldImport(string $entry): bool
|
||||
{
|
||||
$len = strlen($this->expected);
|
||||
$len = strlen($this->vintageFormat);
|
||||
// file is part of the old import routine:
|
||||
if (substr($entry, 0, $len) === $this->expected) {
|
||||
if (substr($entry, 0, $len) === $this->vintageFormat) {
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -137,49 +143,62 @@ class UploadCollector extends BasicCollector implements CollectorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entry
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function processUpload(string $entry)
|
||||
{
|
||||
// file is old import:
|
||||
if ($this->isOldImport($entry)) {
|
||||
$this->saveOldImportFile($entry);
|
||||
}
|
||||
|
||||
// file is current import.
|
||||
if ($this->isImportFile($entry)) {
|
||||
$this->saveImportFile($entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entry
|
||||
*/
|
||||
private function saveImportFile(string $entry)
|
||||
private function processModernUpload(string $key): bool
|
||||
{
|
||||
// find job associated with import file:
|
||||
$name = str_replace('.upload', '', $entry);
|
||||
$job = auth()->user()->importJobs()->where('key', $name)->first();
|
||||
$content = '';
|
||||
try {
|
||||
$content = Crypt::decrypt($this->uploadDisk->get($entry));
|
||||
} catch (DecryptException $e) {
|
||||
Log::error('Could not decrypt old import file ' . $entry . '. Skipped because ' . $e->getMessage());
|
||||
$job = $this->job->user->importJobs()->where('key', $key)->first();
|
||||
if (is_null($job)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_null($job) && strlen($content) > 0) {
|
||||
// find the file for this import:
|
||||
$content = '';
|
||||
try {
|
||||
$content = Crypt::decrypt($this->uploadDisk->get(sprintf('%s.upload', $key)));
|
||||
} catch (DecryptException $e) {
|
||||
Log::error(sprintf('Could not decrypt old import file "%s". Skipped because: %s', $key, $e->getMessage()));
|
||||
}
|
||||
|
||||
if (strlen($content) > 0) {
|
||||
// add to export disk.
|
||||
$date = $job->created_at->format('Y-m-d');
|
||||
$file = sprintf('%s-Old %s import dated %s.%s', $this->job->key, strtoupper($job->file_type), $date, $job->file_type);
|
||||
$this->exportDisk->put($file, $content);
|
||||
$this->getFiles()->push($file);
|
||||
$this->getEntries()->push($file);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the file is a vintage upload, process it.
|
||||
*
|
||||
* @param string $entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function processVintageUpload(string $entry): bool
|
||||
{
|
||||
if ($this->isVintageImport($entry)) {
|
||||
$this->saveVintageImportFile($entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This will store the content of the old vintage upload somewhere.
|
||||
*
|
||||
* @param string $entry
|
||||
*/
|
||||
private function saveOldImportFile(string $entry)
|
||||
private function saveVintageImportFile(string $entry)
|
||||
{
|
||||
$content = '';
|
||||
try {
|
||||
@ -190,10 +209,10 @@ class UploadCollector extends BasicCollector implements CollectorInterface
|
||||
|
||||
if (strlen($content) > 0) {
|
||||
// add to export disk.
|
||||
$date = $this->getOriginalUploadDate($entry);
|
||||
$date = $this->getVintageUploadDate($entry);
|
||||
$file = $this->job->key . '-Old import dated ' . $date . '.csv';
|
||||
$this->exportDisk->put($file, $content);
|
||||
$this->getFiles()->push($file);
|
||||
$this->getEntries()->push($file);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,68 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ConfigurationFile.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Export;
|
||||
|
||||
use FireflyIII\Export\Entry\Entry;
|
||||
use FireflyIII\Models\ExportJob;
|
||||
use Storage;
|
||||
|
||||
/**
|
||||
* Class ConfigurationFile
|
||||
*
|
||||
* @package FireflyIII\Export
|
||||
*/
|
||||
class ConfigurationFile
|
||||
{
|
||||
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
|
||||
private $exportDisk;
|
||||
/** @var ExportJob */
|
||||
private $job;
|
||||
|
||||
/**
|
||||
* ConfigurationFile constructor.
|
||||
*
|
||||
* @param ExportJob $job
|
||||
*/
|
||||
public function __construct(ExportJob $job)
|
||||
{
|
||||
$this->job = $job;
|
||||
$this->exportDisk = Storage::disk('export');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function make(): string
|
||||
{
|
||||
$fields = array_keys(Entry::getFieldsAndTypes());
|
||||
$types = Entry::getFieldsAndTypes();
|
||||
|
||||
$configuration = [
|
||||
'date-format' => 'Y-m-d', // unfortunately, this is hard-coded.
|
||||
'has-headers' => true,
|
||||
'map' => [], // we could build a map if necessary for easy re-import.
|
||||
'roles' => [],
|
||||
'mapped' => [],
|
||||
'specifix' => [],
|
||||
];
|
||||
foreach ($fields as $field) {
|
||||
$configuration['roles'][] = $types[$field];
|
||||
}
|
||||
$file = $this->job->key . '-configuration.json';
|
||||
$this->exportDisk->put($file, json_encode($configuration, JSON_PRETTY_PRINT));
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
}
|
@ -13,8 +13,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Export\Entry;
|
||||
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Support\Collection;
|
||||
use Crypt;
|
||||
|
||||
/**
|
||||
* To extend the exported object, in case of new features in Firefly III for example,
|
||||
@ -35,98 +34,77 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
final class Entry
|
||||
{
|
||||
/** @var string */
|
||||
public $amount;
|
||||
/** @var EntryBill */
|
||||
public $bill;
|
||||
/** @var EntryBudget */
|
||||
public $budget;
|
||||
/** @var EntryCategory */
|
||||
public $category;
|
||||
/** @var string */
|
||||
// @formatter:off
|
||||
public $journal_id;
|
||||
public $date;
|
||||
/** @var string */
|
||||
public $description;
|
||||
/** @var EntryAccount */
|
||||
public $destinationAccount;
|
||||
/** @var Collection */
|
||||
public $destinationAccounts;
|
||||
/** @var EntryAccount */
|
||||
public $sourceAccount;
|
||||
/** @var Collection */
|
||||
public $sourceAccounts;
|
||||
|
||||
public $currency_code;
|
||||
public $amount;
|
||||
|
||||
public $transaction_type;
|
||||
|
||||
public $source_account_id;
|
||||
public $source_account_name;
|
||||
|
||||
public $destination_account_id;
|
||||
public $destination_account_name;
|
||||
|
||||
|
||||
public $budget_id;
|
||||
public $budget_name;
|
||||
public $category_id;
|
||||
public $category_name;
|
||||
// @formatter:on
|
||||
|
||||
/**
|
||||
* Entry constructor.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
$this->sourceAccounts = new Collection;
|
||||
$this->destinationAccounts = new Collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param $object
|
||||
*
|
||||
* @return Entry
|
||||
*/
|
||||
public static function fromJournal(TransactionJournal $journal)
|
||||
public static function fromObject($object): Entry
|
||||
{
|
||||
|
||||
$entry = new self;
|
||||
$entry->description = $journal->description;
|
||||
$entry->date = $journal->date->format('Y-m-d');
|
||||
$entry->amount = TransactionJournal::amount($journal);
|
||||
|
||||
$entry->budget = new EntryBudget($journal->budgets->first());
|
||||
$entry->category = new EntryCategory($journal->categories->first());
|
||||
$entry->bill = new EntryBill($journal->bill);
|
||||
// journal information:
|
||||
$entry->journal_id = $object->transaction_journal_id;
|
||||
$entry->description = $object->journal_encrypted === 1 ? Crypt::decrypt($object->journal_description) : $object->journal_description;
|
||||
$entry->amount = round($object->amount, 2); // always positive
|
||||
$entry->date = $object->date;
|
||||
$entry->transaction_type = $object->transaction_type;
|
||||
$entry->currency_code = $object->transaction_currency_code;
|
||||
|
||||
$sources = TransactionJournal::sourceAccountList($journal);
|
||||
$destinations = TransactionJournal::destinationAccountList($journal);
|
||||
$entry->sourceAccount = new EntryAccount($sources->first());
|
||||
$entry->destinationAccount = new EntryAccount($destinations->first());
|
||||
// source information:
|
||||
$entry->source_account_id = $object->account_id;
|
||||
$entry->source_account_name = $object->account_name_encrypted === 1 ? Crypt::decrypt($object->account_name) : $object->account_name;
|
||||
|
||||
foreach ($sources as $source) {
|
||||
$entry->sourceAccounts->push(new EntryAccount($source));
|
||||
}
|
||||
|
||||
foreach ($destinations as $destination) {
|
||||
$entry->destinationAccounts->push(new EntryAccount($destination));
|
||||
// destination information
|
||||
$entry->destination_account_id = $object->opposing_account_id;
|
||||
$entry->destination_account_name = $object->opposing_account_encrypted === 1 ? Crypt::decrypt($object->opposing_account_name)
|
||||
: $object->opposing_account_name;
|
||||
|
||||
|
||||
// category and budget
|
||||
$entry->category_id = $object->category_id ?? '';
|
||||
$entry->category_name = $object->category_name ?? '';
|
||||
$entry->budget_id = $object->budget_id ?? '';
|
||||
$entry->budget_name = $object->budget_name ?? '';
|
||||
|
||||
|
||||
// update description when transaction description is different:
|
||||
if (!is_null($object->description) && $object->description != $entry->description) {
|
||||
$entry->description = $entry->description . ' (' . $object->description . ')';
|
||||
}
|
||||
|
||||
return $entry;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getFieldsAndTypes(): array
|
||||
{
|
||||
// key = field name (see top of class)
|
||||
// value = field type (see csv.php under 'roles')
|
||||
return [
|
||||
'description' => 'description',
|
||||
'amount' => 'amount',
|
||||
'date' => 'date-transaction',
|
||||
'source_account_id' => 'account-id',
|
||||
'source_account_name' => 'account-name',
|
||||
'source_account_iban' => 'account-iban',
|
||||
'source_account_type' => '_ignore',
|
||||
'source_account_number' => 'account-number',
|
||||
'destination_account_id' => 'opposing-id',
|
||||
'destination_account_name' => 'opposing-name',
|
||||
'destination_account_iban' => 'opposing-iban',
|
||||
'destination_account_type' => '_ignore',
|
||||
'destination_account_number' => 'account-number',
|
||||
'budget_id' => 'budget-id',
|
||||
'budget_name' => 'budget-name',
|
||||
'category_id' => 'category-id',
|
||||
'category_name' => 'category-name',
|
||||
'bill_id' => 'bill-id',
|
||||
'bill_name' => 'bill-name',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* EntryAccount.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Export\Entry;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
/**
|
||||
* Class EntryAccount
|
||||
*
|
||||
* @package FireflyIII\Export\Entry
|
||||
*/
|
||||
class EntryAccount
|
||||
{
|
||||
/** @var int */
|
||||
public $accountId;
|
||||
/** @var string */
|
||||
public $iban;
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $number;
|
||||
/** @var string */
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* EntryAccount constructor.
|
||||
*
|
||||
* @param Account $account
|
||||
*/
|
||||
public function __construct(Account $account)
|
||||
{
|
||||
$this->accountId = $account->id;
|
||||
$this->name = $account->name;
|
||||
$this->iban = $account->iban;
|
||||
$this->type = $account->accountType->type;
|
||||
$this->number = $account->getMeta('accountNumber');
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* EntryBill.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Export\Entry;
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
|
||||
/**
|
||||
* Class EntryBill
|
||||
*
|
||||
* @package FireflyIII\Export\Entry
|
||||
*/
|
||||
class EntryBill
|
||||
{
|
||||
/** @var int */
|
||||
public $billId = '';
|
||||
/** @var string */
|
||||
public $name = '';
|
||||
|
||||
/**
|
||||
* EntryBill constructor.
|
||||
*
|
||||
* @param Bill $bill
|
||||
*/
|
||||
public function __construct(Bill $bill = null)
|
||||
{
|
||||
if (!is_null($bill)) {
|
||||
$this->billId = $bill->id;
|
||||
$this->name = $bill->name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* EntryBudget.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Export\Entry;
|
||||
|
||||
use FireflyIII\Models\Budget;
|
||||
|
||||
/**
|
||||
* Class EntryBudget
|
||||
*
|
||||
* @package FireflyIII\Export\Entry
|
||||
*/
|
||||
class EntryBudget
|
||||
{
|
||||
/** @var int */
|
||||
public $budgetId = '';
|
||||
/** @var string */
|
||||
public $name = '';
|
||||
|
||||
/**
|
||||
* EntryBudget constructor.
|
||||
*
|
||||
* @param Budget $budget
|
||||
*/
|
||||
public function __construct(Budget $budget = null)
|
||||
{
|
||||
if (!is_null($budget)) {
|
||||
$this->budgetId = $budget->id;
|
||||
$this->name = $budget->name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* EntryCategory.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Export\Entry;
|
||||
|
||||
use FireflyIII\Models\Category;
|
||||
|
||||
/**
|
||||
* Class EntryCategory
|
||||
*
|
||||
* @package FireflyIII\Export\Entry
|
||||
*/
|
||||
class EntryCategory
|
||||
{
|
||||
/** @var int */
|
||||
public $categoryId = '';
|
||||
/** @var string */
|
||||
public $name = '';
|
||||
|
||||
/**
|
||||
* EntryCategory constructor.
|
||||
*
|
||||
* @param Category $category
|
||||
*/
|
||||
public function __construct(Category $category = null)
|
||||
{
|
||||
if (!is_null($category)) {
|
||||
$this->categoryId = $category->id;
|
||||
$this->name = $category->name;
|
||||
}
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ namespace FireflyIII\Export\Exporter;
|
||||
use FireflyIII\Export\Entry\Entry;
|
||||
use FireflyIII\Export\Entry\EntryAccount;
|
||||
use FireflyIII\Models\ExportJob;
|
||||
use Illuminate\Support\Collection;
|
||||
use League\Csv\Writer;
|
||||
use SplFileObject;
|
||||
|
||||
@ -62,110 +61,24 @@ class CsvExporter extends BasicExporter implements ExporterInterface
|
||||
$writer = Writer::createFromPath(new SplFileObject($fullPath, 'a+'), 'w');
|
||||
$rows = [];
|
||||
|
||||
// Count the maximum number of sources and destinations each entry has. May need to expand the number of export fields:
|
||||
$maxSourceAccounts = 1;
|
||||
$maxDestAccounts = 1;
|
||||
/** @var Entry $entry */
|
||||
foreach ($this->getEntries() as $entry) {
|
||||
$sources = $entry->sourceAccounts->count();
|
||||
$destinations = $entry->destinationAccounts->count();
|
||||
$maxSourceAccounts = max($maxSourceAccounts, $sources);
|
||||
$maxDestAccounts = max($maxDestAccounts, $destinations);
|
||||
}
|
||||
$rows[] = array_keys($this->getFieldsAndTypes($maxSourceAccounts, $maxDestAccounts));
|
||||
// get field names for header row:
|
||||
$first = $this->getEntries()->first();
|
||||
$headers = array_keys(get_object_vars($first));
|
||||
$rows[] = $headers;
|
||||
|
||||
/** @var Entry $entry */
|
||||
foreach ($this->getEntries() as $entry) {
|
||||
// order is defined in Entry::getFieldsAndTypes.
|
||||
$current = [$entry->description, $entry->amount, $entry->date];
|
||||
$sourceData = $this->getAccountData($maxSourceAccounts, $entry->sourceAccounts);
|
||||
$current = array_merge($current, $sourceData);
|
||||
$destData = $this->getAccountData($maxDestAccounts, $entry->destinationAccounts);
|
||||
$current = array_merge($current, $destData);
|
||||
$rest = [$entry->budget->budgetId, $entry->budget->name, $entry->category->categoryId, $entry->category->name, $entry->bill->billId,
|
||||
$entry->bill->name];
|
||||
$current = array_merge($current, $rest);
|
||||
$rows[] = $current;
|
||||
$line = [];
|
||||
foreach ($headers as $header) {
|
||||
$line[] = $entry->$header;
|
||||
}
|
||||
$rows[] = $line;
|
||||
}
|
||||
$writer->insertAll($rows);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $max
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAccountData(int $max, Collection $accounts): array
|
||||
{
|
||||
$current = [];
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
/** @var EntryAccount $source */
|
||||
$source = $accounts->get($i);
|
||||
$currentId = '';
|
||||
$currentName = '';
|
||||
$currentIban = '';
|
||||
$currentType = '';
|
||||
$currentNumber = '';
|
||||
if ($source) {
|
||||
$currentId = $source->accountId;
|
||||
$currentName = $source->name;
|
||||
$currentIban = $source->iban;
|
||||
$currentType = $source->type;
|
||||
$currentNumber = $source->number;
|
||||
}
|
||||
$current[] = $currentId;
|
||||
$current[] = $currentName;
|
||||
$current[] = $currentIban;
|
||||
$current[] = $currentType;
|
||||
$current[] = $currentNumber;
|
||||
}
|
||||
unset($source);
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $sources
|
||||
* @param int $destinations
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getFieldsAndTypes(int $sources, int $destinations): array
|
||||
{
|
||||
// key = field name (see top of class)
|
||||
// value = field type (see csv.php under 'roles')
|
||||
$array = [
|
||||
'description' => 'description',
|
||||
'amount' => 'amount',
|
||||
'date' => 'date-transaction',
|
||||
];
|
||||
for ($i = 0; $i < $sources; $i++) {
|
||||
$array['source_account_' . $i . '_id'] = 'account-id';
|
||||
$array['source_account_' . $i . '_name'] = 'account-name';
|
||||
$array['source_account_' . $i . '_iban'] = 'account-iban';
|
||||
$array['source_account_' . $i . '_type'] = '_ignore';
|
||||
$array['source_account_' . $i . '_number'] = 'account-number';
|
||||
}
|
||||
for ($i = 0; $i < $destinations; $i++) {
|
||||
$array['destination_account_' . $i . '_id'] = 'account-id';
|
||||
$array['destination_account_' . $i . '_name'] = 'account-name';
|
||||
$array['destination_account_' . $i . '_iban'] = 'account-iban';
|
||||
$array['destination_account_' . $i . '_type'] = '_ignore';
|
||||
$array['destination_account_' . $i . '_number'] = 'account-number';
|
||||
}
|
||||
|
||||
$array['budget_id'] = 'budget-id';
|
||||
$array['budget_name'] = 'budget-name';
|
||||
$array['category_id'] = 'category-id';
|
||||
$array['category_name'] = 'category-name';
|
||||
$array['bill_id'] = 'bill-id';
|
||||
$array['bill_name'] = 'bill-name';
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
private function tempFile()
|
||||
{
|
||||
|
@ -15,13 +15,13 @@ namespace FireflyIII\Export;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Export\Collector\AttachmentCollector;
|
||||
use FireflyIII\Export\Collector\JournalCollector;
|
||||
use FireflyIII\Export\Collector\UploadCollector;
|
||||
use FireflyIII\Export\Entry\Entry;
|
||||
use FireflyIII\Models\ExportJob;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Storage;
|
||||
use ZipArchive;
|
||||
|
||||
@ -40,8 +40,6 @@ class Processor
|
||||
/** @var bool */
|
||||
public $includeAttachments;
|
||||
/** @var bool */
|
||||
public $includeConfig;
|
||||
/** @var bool */
|
||||
public $includeOldUploads;
|
||||
/** @var ExportJob */
|
||||
public $job;
|
||||
@ -68,7 +66,6 @@ class Processor
|
||||
$this->accounts = $settings['accounts'];
|
||||
$this->exportFormat = $settings['exportFormat'];
|
||||
$this->includeAttachments = $settings['includeAttachments'];
|
||||
$this->includeConfig = $settings['includeConfig'];
|
||||
$this->includeOldUploads = $settings['includeOldUploads'];
|
||||
$this->job = $settings['job'];
|
||||
$this->journals = new Collection;
|
||||
@ -84,8 +81,9 @@ class Processor
|
||||
{
|
||||
/** @var AttachmentCollector $attachmentCollector */
|
||||
$attachmentCollector = app(AttachmentCollector::class, [$this->job]);
|
||||
$attachmentCollector->setDates($this->settings['startDate'], $this->settings['endDate']);
|
||||
$attachmentCollector->run();
|
||||
$this->files = $this->files->merge($attachmentCollector->getFiles());
|
||||
$this->files = $this->files->merge($attachmentCollector->getEntries());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -95,9 +93,13 @@ class Processor
|
||||
*/
|
||||
public function collectJournals(): bool
|
||||
{
|
||||
/** @var JournalTaskerInterface $tasker */
|
||||
$tasker = app(JournalTaskerInterface::class);
|
||||
$this->journals = $tasker->getJournalsInRange($this->accounts, $this->settings['startDate'], $this->settings['endDate']);
|
||||
/** @var JournalCollector $collector */
|
||||
$collector = app(JournalCollector::class, [$this->job]);
|
||||
$collector->setDates($this->settings['startDate'], $this->settings['endDate']);
|
||||
$collector->setAccounts($this->settings['accounts']);
|
||||
$collector->run();
|
||||
$this->journals = $collector->getEntries();
|
||||
Log::debug(sprintf('Count %d journals in collectJournals() ', $this->journals->count()));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -111,7 +113,7 @@ class Processor
|
||||
$uploadCollector = app(UploadCollector::class, [$this->job]);
|
||||
$uploadCollector->run();
|
||||
|
||||
$this->files = $this->files->merge($uploadCollector->getFiles());
|
||||
$this->files = $this->files->merge($uploadCollector->getEntries());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -122,22 +124,11 @@ class Processor
|
||||
public function convertJournals(): bool
|
||||
{
|
||||
$count = 0;
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($this->journals as $journal) {
|
||||
$this->exportEntries->push(Entry::fromJournal($journal));
|
||||
foreach ($this->journals as $object) {
|
||||
$this->exportEntries->push(Entry::fromObject($object));
|
||||
$count++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function createConfigFile(): bool
|
||||
{
|
||||
$this->configurationMaker = app(ConfigurationFile::class, [$this->job]);
|
||||
$this->files->push($this->configurationMaker->make());
|
||||
Log::debug(sprintf('Count %d entries in exportEntries (convertJournals)', $this->exportEntries->count()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -26,47 +26,39 @@ class Help implements HelpInterface
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $route
|
||||
* @param string $language
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFromCache(string $key): string
|
||||
public function getFromCache(string $route, string $language): string
|
||||
{
|
||||
return Cache::get($key);
|
||||
return Cache::get('help.' . $route . '.' . $language);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $language
|
||||
* @param string $route
|
||||
*
|
||||
* @return array
|
||||
* @return string
|
||||
*/
|
||||
public function getFromGithub(string $language, string $route): array
|
||||
public function getFromGithub(string $language, string $route): string
|
||||
{
|
||||
|
||||
$uri = sprintf('https://raw.githubusercontent.com/JC5/firefly-iii-help/master/%s/%s.md', $language, $route);
|
||||
$routeIndex = str_replace('.', '-', $route);
|
||||
$title = trans('help.' . $routeIndex);
|
||||
$content = [
|
||||
'text' => '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>',
|
||||
'title' => $title,
|
||||
];
|
||||
|
||||
|
||||
$uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route);
|
||||
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
$result = Requests::get($uri);
|
||||
|
||||
|
||||
if ($result->status_code === 200) {
|
||||
$content['text'] = $result->body;
|
||||
$content = $result->body;
|
||||
}
|
||||
|
||||
|
||||
if (strlen(trim($content['text'])) == 0) {
|
||||
$content['text'] = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
if (strlen(trim($content)) == 0) {
|
||||
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
}
|
||||
$converter = new CommonMarkConverter();
|
||||
$content['text'] = $converter->convertToHtml($content['text']);
|
||||
$content = $converter->convertToHtml($content);
|
||||
|
||||
return $content;
|
||||
|
||||
@ -84,27 +76,26 @@ class Help implements HelpInterface
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $route
|
||||
* @param string $language
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function inCache(string $route):bool
|
||||
public function inCache(string $route, string $language):bool
|
||||
{
|
||||
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
|
||||
return Cache::has('help.' . $route . '.' . $language);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $route
|
||||
* @param string $language
|
||||
* @param array $content
|
||||
* @param string $content
|
||||
*
|
||||
* @internal param $title
|
||||
*/
|
||||
public function putInCache(string $route, string $language, array $content)
|
||||
public function putInCache(string $route, string $language, string $content)
|
||||
{
|
||||
Cache::put('help.' . $route . '.text.' . $language, $content['text'], 10080); // a week.
|
||||
Cache::put('help.' . $route . '.title.' . $language, $content['title'], 10080);
|
||||
Cache::put('help.' . $route . '.' . $language, $content, 10080); // a week.
|
||||
}
|
||||
}
|
||||
|
@ -21,19 +21,20 @@ interface HelpInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string $route
|
||||
* @param string $language
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFromCache(string $key): string;
|
||||
public function getFromCache(string $route, string $language): string;
|
||||
|
||||
/**
|
||||
* @param string $language
|
||||
* @param string $route
|
||||
*
|
||||
* @return array
|
||||
* @return string
|
||||
*/
|
||||
public function getFromGithub(string $language, string $route):array;
|
||||
public function getFromGithub(string $language, string $route):string;
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
@ -44,15 +45,16 @@ interface HelpInterface
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
* @param string $language
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function inCache(string $route): bool;
|
||||
public function inCache(string $route, string $language ): bool;
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
* @param string $language
|
||||
* @param array $content
|
||||
* @param string $content
|
||||
*/
|
||||
public function putInCache(string $route, string $language, array $content);
|
||||
public function putInCache(string $route, string $language, string $content);
|
||||
}
|
||||
|
@ -102,7 +102,8 @@ class ReportHelper implements ReportHelperInterface
|
||||
$billLine->setHit(true);
|
||||
}
|
||||
|
||||
if ($billLine->isActive()) {
|
||||
// non active AND non hit? do not add:
|
||||
if ($billLine->isActive() || $billLine->isHit()) {
|
||||
$collection->addBill($billLine);
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,11 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use ExpandedForm;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Requests\AccountFormRequest;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
@ -44,8 +46,16 @@ class AccountController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// translations:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('mainTitleIcon', 'fa-credit-card');
|
||||
View::share('title', trans('firefly.accounts'));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,7 +156,7 @@ class AccountController extends Controller
|
||||
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
|
||||
'openingBalanceDate' => $openingBalanceDate,
|
||||
'openingBalance' => $openingBalanceAmount,
|
||||
'virtualBalance' => round($account->virtual_balance, 2),
|
||||
'virtualBalance' => $account->virtual_balance,
|
||||
];
|
||||
Session::flash('preFilled', $preFilled);
|
||||
Session::flash('gaEventCategory', 'accounts');
|
||||
@ -200,6 +210,9 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function show(AccountTaskerInterface $tasker, ARI $repository, Account $account)
|
||||
{
|
||||
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
|
||||
return $this->redirectToOriginalAccount($account);
|
||||
}
|
||||
// show journals from current period only:
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
||||
$subTitle = $account->name;
|
||||
@ -363,4 +376,29 @@ class AccountController extends Controller
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function redirectToOriginalAccount(Account $account)
|
||||
{
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $account->transactions()->first();
|
||||
if (is_null($transaction)) {
|
||||
throw new FireflyException('Expected a transaction. This account has none. BEEP, error.');
|
||||
}
|
||||
|
||||
$journal = $transaction->transactionJournal;
|
||||
/** @var Transaction $opposingTransaction */
|
||||
$opposingTransaction = $journal->transactions()->where('transactions.id', '!=', $transaction->id)->first();
|
||||
|
||||
if (is_null($opposingTransaction)) {
|
||||
throw new FireflyException('Expected an opposing transaction. This account has none. BEEP, error.');
|
||||
}
|
||||
|
||||
return redirect(route('accounts.show', [$opposingTransaction->account_id]));
|
||||
}
|
||||
}
|
||||
|
@ -37,9 +37,16 @@ class ConfigurationController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', strval(trans('firefly.administration')));
|
||||
View::share('mainTitleIcon', 'fa-hand-spock-o');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,10 +73,10 @@ class ConfigurationController extends Controller
|
||||
public function store(ConfigurationRequest $request)
|
||||
{
|
||||
// get config values:
|
||||
$singleUserMode = intval($request->get('single_user_mode')) === 1 ? true : false;
|
||||
$data = $request->getConfigurationData();
|
||||
|
||||
// store config values
|
||||
FireflyConfig::set('single_user_mode', $singleUserMode);
|
||||
FireflyConfig::set('single_user_mode', $data['single_user_mode']);
|
||||
|
||||
// flash message
|
||||
Session::flash('success', strval(trans('firefly.configuration_updated')));
|
||||
|
@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers\Admin;
|
||||
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\Request;
|
||||
@ -83,6 +84,7 @@ class DomainController extends Controller
|
||||
*/
|
||||
public function toggleDomain(string $domain)
|
||||
{
|
||||
$domain = strtolower($domain);
|
||||
$blocked = FireflyConfig::get('blocked-domains', [])->data;
|
||||
|
||||
if (in_array($domain, $blocked)) {
|
||||
@ -111,15 +113,16 @@ class DomainController extends Controller
|
||||
*/
|
||||
private function getKnownDomains(): array
|
||||
{
|
||||
$users = User::get();
|
||||
/** @var UserRepositoryInterface $repository */
|
||||
$repository = app(UserRepositoryInterface::class);
|
||||
$users = $repository->all();
|
||||
$set = [];
|
||||
$filtered = [];
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$email = $user->email;
|
||||
$parts = explode('@', $email);
|
||||
$domain = $parts[1];
|
||||
$set[] = $domain;
|
||||
$set[] = strtolower($parts[1]);
|
||||
}
|
||||
$set = array_unique($set);
|
||||
// filter for already banned domains:
|
||||
@ -131,7 +134,6 @@ class DomainController extends Controller
|
||||
$filtered[] = $domain;
|
||||
}
|
||||
}
|
||||
asort($filtered);
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
@ -42,8 +42,16 @@ class AttachmentController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// translations:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('mainTitleIcon', 'fa-paperclip');
|
||||
View::share('title', trans('firefly.attachments'));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,8 +38,16 @@ class BillController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.bills'));
|
||||
View::share('mainTitleIcon', 'fa-calendar-o');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,23 +145,15 @@ class BillController extends Controller
|
||||
$bills = $repository->getBills();
|
||||
$bills->each(
|
||||
function (Bill $bill) use ($repository, $start, $end) {
|
||||
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, new Carbon);
|
||||
$bill->lastFoundMatch = $repository->lastFoundMatch($bill);
|
||||
$journals = $repository->getJournalsInRange($bill, $start, $end);
|
||||
// loop journals, find average:
|
||||
$average = '0';
|
||||
$count = $journals->count();
|
||||
if ($count > 0) {
|
||||
$sum = '0';
|
||||
foreach ($journals as $journal) {
|
||||
$sum = bcadd($sum, TransactionJournal::amountPositive($journal));
|
||||
}
|
||||
$average = bcdiv($sum, strval($count));
|
||||
}
|
||||
|
||||
$bill->lastPaidAmount = $average;
|
||||
$bill->paidInPeriod = ($start <= $bill->lastFoundMatch) && ($end >= $bill->lastFoundMatch);
|
||||
|
||||
// paid in this period?
|
||||
$bill->paidDates = $repository->getPaidDatesInRange($bill, $start, $end);
|
||||
$bill->payDates = $repository->getPayDatesInRange($bill, $start, $end);
|
||||
$lastDate = clone $start;
|
||||
if ($bill->paidDates->count() >= $bill->payDates->count()) {
|
||||
$lastDate = $end;
|
||||
}
|
||||
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, $lastDate);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -26,6 +26,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Log;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Response;
|
||||
@ -47,9 +48,17 @@ class BudgetController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
View::share('hideBudgets', true);
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.budgets'));
|
||||
View::share('mainTitleIcon', 'fa-tasks');
|
||||
View::share('hideBudgets', true);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,10 +200,12 @@ class BudgetController extends Controller
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
$startAsString = $start->format('Y-m-d');
|
||||
$endAsString = $end->format('Y-m-d');
|
||||
Log::debug('Now at /budgets');
|
||||
|
||||
// loop the budgets:
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
Log::debug(sprintf('Now at budget #%d ("%s")', $budget->id, $budget->name));
|
||||
$budget->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);
|
||||
$allRepetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
|
||||
$otherRepetitions = new Collection;
|
||||
@ -283,12 +294,12 @@ class BudgetController extends Controller
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param AccountRepositoryInterface $accountRepository
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return View
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function show(BudgetRepositoryInterface $repository, Budget $budget)
|
||||
public function show(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget)
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = session('first', Carbon::create()->startOfYear());
|
||||
@ -300,6 +311,7 @@ class BudgetController extends Controller
|
||||
$count = $journals->count();
|
||||
$journals = $journals->slice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
|
||||
$journals->setPath('/budgets/show/' . $budget->id);
|
||||
|
||||
@ -310,7 +322,7 @@ class BudgetController extends Controller
|
||||
|
||||
/** @var LimitRepetition $entry */
|
||||
foreach ($set as $entry) {
|
||||
$entry->spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $entry->startdate, $entry->enddate);
|
||||
$entry->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->startdate, $entry->enddate);
|
||||
$limits->push($entry);
|
||||
}
|
||||
|
||||
@ -319,14 +331,16 @@ class BudgetController extends Controller
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param AccountRepositoryInterface $accountRepository
|
||||
* @param Budget $budget
|
||||
* @param LimitRepetition $repetition
|
||||
*
|
||||
* @return View
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function showWithRepetition(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
|
||||
{
|
||||
public function showWithRepetition(
|
||||
BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget, LimitRepetition $repetition
|
||||
) {
|
||||
if ($repetition->budgetLimit->budget->id != $budget->id) {
|
||||
throw new FireflyException('This budget limit is not part of this budget.');
|
||||
}
|
||||
@ -340,11 +354,13 @@ class BudgetController extends Controller
|
||||
$journals = $journals->slice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
|
||||
$subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
|
||||
|
||||
$journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id);
|
||||
|
||||
|
||||
$repetition->spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate);
|
||||
$repetition->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $repetition->startdate, $repetition->enddate);
|
||||
$limits = new Collection([$repetition]);
|
||||
|
||||
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
|
||||
|
@ -43,8 +43,16 @@ class CategoryController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.categories'));
|
||||
View::share('mainTitleIcon', 'fa-bar-chart');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -264,11 +272,8 @@ class CategoryController extends Controller
|
||||
*/
|
||||
public function store(CategoryFormRequest $request, CRI $repository)
|
||||
{
|
||||
$categoryData = [
|
||||
'name' => trim($request->input('name')),
|
||||
'user' => auth()->user()->id,
|
||||
];
|
||||
$category = $repository->store($categoryData);
|
||||
$data = $request->getCategoryData();
|
||||
$category = $repository->store($data);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.stored_category', ['name' => e($category->name)])));
|
||||
Preferences::mark();
|
||||
@ -292,11 +297,8 @@ class CategoryController extends Controller
|
||||
*/
|
||||
public function update(CategoryFormRequest $request, CRI $repository, Category $category)
|
||||
{
|
||||
$categoryData = [
|
||||
'name' => $request->input('name'),
|
||||
];
|
||||
|
||||
$repository->update($category, $categoryData);
|
||||
$data = $request->getCategoryData();
|
||||
$repository->update($category, $data);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.updated_category', ['name' => e($category->name)])));
|
||||
Preferences::mark();
|
||||
|
@ -46,11 +46,19 @@ class Controller extends BaseController
|
||||
View::share('hideBills', false);
|
||||
View::share('hideTags', false);
|
||||
|
||||
// save some formats:
|
||||
|
||||
// translations:
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->monthFormat = (string)trans('config.month');
|
||||
$this->monthAndDayFormat = (string)trans('config.month_and_day');
|
||||
$this->dateTimeFormat = (string)trans('config.date_time');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,8 +39,16 @@ class CurrencyController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.currencies'));
|
||||
View::share('mainTitleIcon', 'fa-usd');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,8 +41,16 @@ class ExportController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('mainTitleIcon', 'fa-file-archive-o');
|
||||
View::share('title', trans('firefly.export_data'));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,7 +141,6 @@ class ExportController extends Controller
|
||||
'endDate' => new Carbon($request->get('export_end_range')),
|
||||
'exportFormat' => $request->get('exportFormat'),
|
||||
'includeAttachments' => intval($request->get('include_attachments')) === 1,
|
||||
'includeConfig' => intval($request->get('include_config')) === 1,
|
||||
'includeOldUploads' => intval($request->get('include_old_uploads')) === 1,
|
||||
'job' => $job,
|
||||
];
|
||||
@ -177,15 +184,6 @@ class ExportController extends Controller
|
||||
$job->change('export_status_collected_old_uploads');
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate / collect config file.
|
||||
*/
|
||||
if ($settings['includeConfig']) {
|
||||
$job->change('export_status_creating_config_file');
|
||||
$processor->createConfigFile();
|
||||
$job->change('export_status_created_config_file');
|
||||
}
|
||||
|
||||
/*
|
||||
* Create ZIP file:
|
||||
*/
|
||||
|
@ -41,11 +41,9 @@ class HelpController extends Controller
|
||||
*/
|
||||
public function show(HelpInterface $help, string $route)
|
||||
{
|
||||
|
||||
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
|
||||
$content = [
|
||||
'text' => '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>',
|
||||
'title' => 'Help',
|
||||
];
|
||||
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
|
||||
if (!$help->hasRoute($route)) {
|
||||
Log::error('No such route: ' . $route);
|
||||
@ -53,11 +51,9 @@ class HelpController extends Controller
|
||||
return Response::json($content);
|
||||
}
|
||||
|
||||
if ($help->inCache($route)) {
|
||||
$content = [
|
||||
'text' => $help->getFromCache('help.' . $route . '.text.' . $language),
|
||||
'title' => $help->getFromCache('help.' . $route . '.title.' . $language),
|
||||
];
|
||||
if ($help->inCache($route, $language)) {
|
||||
$content = $help->getFromCache($route, $language);
|
||||
Log::debug('Help text was in cache.');
|
||||
|
||||
return Response::json($content);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ use Log;
|
||||
use Preferences;
|
||||
use Route;
|
||||
use Session;
|
||||
|
||||
use View;
|
||||
|
||||
/**
|
||||
* Class HomeController
|
||||
@ -41,6 +41,8 @@ class HomeController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
View::share('title', 'Firefly III');
|
||||
View::share('mainTitleIcon', 'fa-fire');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,9 +130,7 @@ class HomeController extends Controller
|
||||
return redirect(route('new-user.index'));
|
||||
}
|
||||
|
||||
$title = 'Firefly';
|
||||
$subTitle = trans('firefly.welcomeBack');
|
||||
$mainTitleIcon = 'fa-fire';
|
||||
$transactions = [];
|
||||
$frontPage = Preferences::get(
|
||||
'frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray()
|
||||
@ -164,36 +164,29 @@ class HomeController extends Controller
|
||||
public function routes()
|
||||
{
|
||||
// these routes are not relevant for the help pages:
|
||||
$ignore = [
|
||||
$ignore = ['login', 'registe', 'logout', 'two-fac', 'lost-two', 'confirm', 'resend', 'do_confirm', 'testFla', 'json.', 'piggy-banks.add',
|
||||
'piggy-banks.remove', 'preferences.', 'rules.rule.up', 'rules.rule.down', 'rules.rule-group.up', 'rules.rule-group.down', 'popup.report',
|
||||
'admin.users.domains.block-', 'import.json', 'help.',
|
||||
];
|
||||
$routes = Route::getRoutes();
|
||||
|
||||
echo '<pre>';
|
||||
|
||||
/** @var \Illuminate\Routing\Route $route */
|
||||
foreach ($routes as $route) {
|
||||
|
||||
$name = $route->getName();
|
||||
$methods = $route->getMethods();
|
||||
$search = [
|
||||
'{account}', '{what}', '{rule}', '{tj}', '{category}', '{budget}', '{code}', '{date}', '{attachment}', '{bill}', '{limitrepetition}',
|
||||
'{currency}', '{jobKey}', '{piggyBank}', '{ruleGroup}', '{rule}', '{route}', '{unfinishedJournal}',
|
||||
'{reportType}', '{start_date}', '{end_date}', '{accountList}', '{tag}', '{journalList}',
|
||||
|
||||
];
|
||||
$replace = [1, 'asset', 1, 1, 1, 1, 'abc', '2016-01-01', 1, 1, 1, 1, 1, 1, 1, 1, 'index', 1,
|
||||
'default', '20160101', '20160131', '1,2', 1, '1,2',
|
||||
];
|
||||
if (count($search) != count($replace)) {
|
||||
echo 'count';
|
||||
exit;
|
||||
}
|
||||
$url = str_replace($search, $replace, $route->getUri());
|
||||
|
||||
if (!is_null($name) && in_array('GET', $methods) && !$this->startsWithAny($ignore, $name)) {
|
||||
echo '<a href="/' . $url . '" title="' . $name . '">' . $name . '</a><br>' . "\n";
|
||||
if (!is_null($name) && strlen($name) > 0 && in_array('GET', $methods) && !$this->startsWithAny($ignore, $name)) {
|
||||
echo sprintf('touch %s.md', $name) . "\n";
|
||||
|
||||
}
|
||||
}
|
||||
echo '</pre>';
|
||||
|
||||
return '<hr>';
|
||||
echo '<hr />';
|
||||
|
||||
return ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,8 +41,15 @@ class ImportController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('mainTitleIcon', 'fa-archive');
|
||||
View::share('title', trans('firefly.import_data_full'));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,6 +32,13 @@ class NewUserController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -107,7 +114,6 @@ class NewUserController extends Controller
|
||||
'accountType' => 'asset',
|
||||
'virtualBalance' => 0,
|
||||
'active' => true,
|
||||
'user' => auth()->user()->id,
|
||||
'accountRole' => 'defaultAsset',
|
||||
'openingBalance' => round($request->input('bank_balance'), 2),
|
||||
'openingBalanceDate' => new Carbon,
|
||||
@ -133,7 +139,6 @@ class NewUserController extends Controller
|
||||
'accountType' => 'asset',
|
||||
'virtualBalance' => 0,
|
||||
'active' => true,
|
||||
'user' => auth()->user()->id,
|
||||
'accountRole' => 'savingAsset',
|
||||
'openingBalance' => round($request->input('savings_balance'), 2),
|
||||
'openingBalanceDate' => new Carbon,
|
||||
@ -158,7 +163,6 @@ class NewUserController extends Controller
|
||||
'accountType' => 'asset',
|
||||
'virtualBalance' => round($request->get('credit_card_limit'), 2),
|
||||
'active' => true,
|
||||
'user' => auth()->user()->id,
|
||||
'accountRole' => 'ccAsset',
|
||||
'openingBalance' => null,
|
||||
'openingBalanceDate' => null,
|
||||
|
@ -45,8 +45,16 @@ class PiggyBankController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.piggyBanks'));
|
||||
View::share('mainTitleIcon', 'fa-sort-amount-asc');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -364,18 +372,8 @@ class PiggyBankController extends Controller
|
||||
*/
|
||||
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
|
||||
{
|
||||
|
||||
$piggyBankData = [
|
||||
'name' => $request->get('name'),
|
||||
'startdate' => new Carbon,
|
||||
'account_id' => intval($request->get('account_id')),
|
||||
'targetamount' => round($request->get('targetamount'), 2),
|
||||
'order' => $repository->getMaxOrder() + 1,
|
||||
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
|
||||
'note' => $request->get('note'),
|
||||
];
|
||||
|
||||
$piggyBank = $repository->store($piggyBankData);
|
||||
$data = $request->getPiggyBankData();
|
||||
$piggyBank = $repository->store($data);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.stored_piggy_bank', ['name' => e($piggyBank->name)])));
|
||||
Preferences::mark();
|
||||
@ -400,16 +398,8 @@ class PiggyBankController extends Controller
|
||||
*/
|
||||
public function update(PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request, PiggyBank $piggyBank)
|
||||
{
|
||||
$piggyBankData = [
|
||||
'name' => $request->get('name'),
|
||||
'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate,
|
||||
'account_id' => intval($request->get('account_id')),
|
||||
'targetamount' => round($request->get('targetamount'), 2),
|
||||
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
|
||||
'note' => $request->get('note'),
|
||||
];
|
||||
|
||||
$piggyBank = $repository->update($piggyBank, $piggyBankData);
|
||||
$data = $request->getPiggyBankData();
|
||||
$piggyBank = $repository->update($piggyBank, $data);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.updated_piggy_bank', ['name' => e($piggyBank->name)])));
|
||||
Preferences::mark();
|
||||
|
@ -173,7 +173,10 @@ class ReportController extends Controller
|
||||
/** @var CategoryRepositoryInterface $repository */
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$category = $repository->find(intval($attributes['categoryId']));
|
||||
$journals = $repository->journalsInPeriod(new Collection([$category]), $attributes['accounts'], [], $attributes['startDate'], $attributes['endDate']);
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
$journals = $repository->journalsInPeriod(
|
||||
new Collection([$category]), $attributes['accounts'], $types, $attributes['startDate'], $attributes['endDate']
|
||||
);
|
||||
$view = view('popup.report.category-entry', compact('journals', 'category'))->render();
|
||||
|
||||
return $view;
|
||||
|
@ -35,8 +35,16 @@ class PreferencesController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.preferences'));
|
||||
View::share('mainTitleIcon', 'fa-gear');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,8 +36,15 @@ class ProfileController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.profile'));
|
||||
View::share('mainTitleIcon', 'fa-user');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,9 +52,11 @@ class ProfileController extends Controller
|
||||
*/
|
||||
public function changePassword()
|
||||
{
|
||||
return view('profile.change-password')->with('title', auth()->user()->email)->with('subTitle', trans('firefly.change_your_password'))->with(
|
||||
'mainTitleIcon', 'fa-user'
|
||||
);
|
||||
$title = auth()->user()->email;
|
||||
$subTitle = strval(trans('firefly.change_your_password'));
|
||||
$subTitleIcon = 'fa-key';
|
||||
|
||||
return view('profile.change-password', compact('title', 'subTitle', 'subTitleIcon'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,9 +64,11 @@ class ProfileController extends Controller
|
||||
*/
|
||||
public function deleteAccount()
|
||||
{
|
||||
return view('profile.delete-account')->with('title', auth()->user()->email)->with('subTitle', trans('firefly.delete_account'))->with(
|
||||
'mainTitleIcon', 'fa-user'
|
||||
);
|
||||
$title = auth()->user()->email;
|
||||
$subTitle = strval(trans('firefly.delete_account'));
|
||||
$subTitleIcon = 'fa-trash';
|
||||
|
||||
return view('profile.delete-account', compact('title', 'subTitle', 'subTitleIcon'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,6 +17,7 @@ namespace FireflyIII\Http\Controllers\Report;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@ -36,9 +37,23 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function accountReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('account-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
|
||||
$accountTasker = app(AccountTaskerInterface::class);
|
||||
$accountReport = $accountTasker->getAccountReport($start, $end, $accounts);
|
||||
|
||||
return view('reports.partials.accounts', compact('accountReport'));
|
||||
$result = view('reports.partials.accounts', compact('accountReport'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
60
app/Http/Controllers/Report/BalanceController.php
Normal file
60
app/Http/Controllers/Report/BalanceController.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* BalanceController.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Report;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Report\BalanceReportHelperInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class BalanceController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Report
|
||||
*/
|
||||
class BalanceController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @param BalanceReportHelperInterface $helper
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function balanceReport(BalanceReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('balance-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
$balance = $helper->getBalanceReport($start, $end, $accounts);
|
||||
|
||||
$result = view('reports.partials.balance', compact('balance'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
59
app/Http/Controllers/Report/CategoryController.php
Normal file
59
app/Http/Controllers/Report/CategoryController.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* CategoryController.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Report;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Report\ReportHelperInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class CategoryController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Report
|
||||
*/
|
||||
class CategoryController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ReportHelperInterface $helper
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function categoryReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('category-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
$categories = $helper->getCategoryReport($start, $end, $accounts);
|
||||
|
||||
$result = view('reports.partials.categories', compact('categories'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
66
app/Http/Controllers/Report/InOutController.php
Normal file
66
app/Http/Controllers/Report/InOutController.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* InOutController.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Report;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Report\ReportHelperInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
* Class InOutController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Report
|
||||
*/
|
||||
class InOutController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ReportHelperInterface $helper
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function inOutReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('in-out-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
$incomes = $helper->getIncomeReport($start, $end, $accounts);
|
||||
$expenses = $helper->getExpenseReport($start, $end, $accounts);
|
||||
|
||||
$result = [
|
||||
'income' => view('reports.partials.income', compact('incomes'))->render(),
|
||||
'expenses' => view('reports.partials.expenses', compact('expenses'))->render(),
|
||||
'incomes_expenses' => view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render(),
|
||||
];
|
||||
$cache->store($result);
|
||||
|
||||
return Response::json($result);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -15,14 +15,12 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Report\BalanceReportHelperInterface;
|
||||
use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
|
||||
use FireflyIII\Helpers\Report\ReportHelperInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
@ -39,10 +37,6 @@ use View;
|
||||
*/
|
||||
class ReportController extends Controller
|
||||
{
|
||||
|
||||
/** @var BalanceReportHelperInterface */
|
||||
protected $balanceHelper;
|
||||
|
||||
/** @var BudgetReportHelperInterface */
|
||||
protected $budgetHelper;
|
||||
/** @var ReportHelperInterface */
|
||||
@ -55,9 +49,19 @@ class ReportController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.reports'));
|
||||
View::share('mainTitleIcon', 'fa-line-chart');
|
||||
|
||||
$this->helper = app(ReportHelperInterface::class);
|
||||
$this->budgetHelper = app(BudgetReportHelperInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,7 +71,7 @@ class ReportController extends Controller
|
||||
*/
|
||||
public function index(AccountRepositoryInterface $repository)
|
||||
{
|
||||
$this->createRepositories();
|
||||
|
||||
/** @var Carbon $start */
|
||||
$start = clone session('first');
|
||||
$months = $this->helper->listOfMonths($start);
|
||||
@ -98,7 +102,6 @@ class ReportController extends Controller
|
||||
*/
|
||||
public function report(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$this->createRepositories();
|
||||
// throw an error if necessary.
|
||||
if ($end < $start) {
|
||||
throw new FireflyException('End date cannot be before start date, silly!');
|
||||
@ -206,16 +209,6 @@ class ReportController extends Controller
|
||||
return view('reports.audit.report', compact('start', 'end', 'reportType', 'accountIds', 'accounts', 'auditData', 'hideable', 'defaultShow'));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function createRepositories()
|
||||
{
|
||||
$this->helper = app(ReportHelperInterface::class);
|
||||
$this->budgetHelper = app(BudgetReportHelperInterface::class);
|
||||
$this->balanceHelper = app(BalanceReportHelperInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $reportType
|
||||
* @param Carbon $start
|
||||
@ -226,15 +219,8 @@ class ReportController extends Controller
|
||||
*/
|
||||
private function defaultMonth(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$incomeTopLength = 8;
|
||||
$expenseTopLength = 8;
|
||||
|
||||
// get report stuff!
|
||||
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
|
||||
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
|
||||
$budgets = $this->budgetHelper->getBudgetReport($start, $end, $accounts);
|
||||
$categories = $this->helper->getCategoryReport($start, $end, $accounts);
|
||||
$balance = $this->balanceHelper->getBalanceReport($start, $end, $accounts);
|
||||
$bills = $this->helper->getBillReport($start, $end, $accounts);
|
||||
$tags = $this->helper->tagReport($start, $end, $accounts);
|
||||
|
||||
@ -245,12 +231,9 @@ class ReportController extends Controller
|
||||
return view(
|
||||
'reports.default.month',
|
||||
compact(
|
||||
'start', 'end', 'reportType',
|
||||
'start', 'end',
|
||||
'tags',
|
||||
'incomes', 'incomeTopLength',
|
||||
'expenses', 'expenseTopLength',
|
||||
'budgets', 'balance',
|
||||
'categories',
|
||||
'budgets',
|
||||
'bills',
|
||||
'accountIds', 'reportType'
|
||||
)
|
||||
@ -268,13 +251,8 @@ class ReportController extends Controller
|
||||
private function defaultMultiYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
|
||||
$incomeTopLength = 8;
|
||||
$expenseTopLength = 8;
|
||||
// list of users stuff:
|
||||
$budgets = app(BudgetRepositoryInterface::class)->getActiveBudgets();
|
||||
$categories = app(CategoryRepositoryInterface::class)->getCategories();
|
||||
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
|
||||
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
|
||||
$tags = $this->helper->tagReport($start, $end, $accounts);
|
||||
|
||||
// and some id's, joined:
|
||||
@ -288,9 +266,7 @@ class ReportController extends Controller
|
||||
return view(
|
||||
'reports.default.multi-year',
|
||||
compact(
|
||||
'budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'reportType',
|
||||
'incomes', 'expenses',
|
||||
'incomeTopLength', 'expenseTopLength', 'tags'
|
||||
'budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'reportType', 'tags'
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -305,11 +281,6 @@ class ReportController extends Controller
|
||||
*/
|
||||
private function defaultYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$incomeTopLength = 8;
|
||||
$expenseTopLength = 8;
|
||||
|
||||
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
|
||||
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
|
||||
$tags = $this->helper->tagReport($start, $end, $accounts);
|
||||
$budgets = $this->budgetHelper->budgetYearOverview($start, $end, $accounts);
|
||||
|
||||
@ -328,8 +299,7 @@ class ReportController extends Controller
|
||||
return view(
|
||||
'reports.default.year',
|
||||
compact(
|
||||
'start', 'incomes', 'reportType', 'accountIds', 'end',
|
||||
'expenses', 'incomeTopLength', 'expenseTopLength', 'tags', 'budgets'
|
||||
'start', 'reportType', 'accountIds', 'end', 'tags', 'budgets'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -42,8 +42,16 @@ class RuleController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.rules'));
|
||||
View::share('mainTitleIcon', 'fa-random');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -238,23 +246,8 @@ class RuleController extends Controller
|
||||
*/
|
||||
public function store(RuleFormRequest $request, RuleRepositoryInterface $repository, RuleGroup $ruleGroup)
|
||||
{
|
||||
|
||||
|
||||
// process the rule itself:
|
||||
$data = [
|
||||
'rule_group_id' => $ruleGroup->id,
|
||||
'title' => $request->get('title'),
|
||||
'user_id' => auth()->user()->id,
|
||||
'trigger' => $request->get('trigger'),
|
||||
'description' => $request->get('description'),
|
||||
'rule-triggers' => $request->get('rule-trigger'),
|
||||
'rule-trigger-values' => $request->get('rule-trigger-value'),
|
||||
'rule-trigger-stop' => $request->get('rule-trigger-stop'),
|
||||
'rule-actions' => $request->get('rule-action'),
|
||||
'rule-action-values' => $request->get('rule-action-value'),
|
||||
'rule-action-stop' => $request->get('rule-action-stop'),
|
||||
'stop_processing' => $request->get('stop_processing'),
|
||||
];
|
||||
$data = $request->getRuleData();
|
||||
$data['rule_group_id'] = $ruleGroup->id;
|
||||
|
||||
$rule = $repository->store($data);
|
||||
Session::flash('success', trans('firefly.stored_new_rule', ['title' => $rule->title]));
|
||||
@ -341,21 +334,7 @@ class RuleController extends Controller
|
||||
*/
|
||||
public function update(RuleRepositoryInterface $repository, RuleFormRequest $request, Rule $rule)
|
||||
{
|
||||
|
||||
// process the rule itself:
|
||||
$data = [
|
||||
'title' => $request->get('title'),
|
||||
'active' => intval($request->get('active')) == 1,
|
||||
'trigger' => $request->get('trigger'),
|
||||
'description' => $request->get('description'),
|
||||
'rule-triggers' => $request->get('rule-trigger'),
|
||||
'rule-trigger-values' => $request->get('rule-trigger-value'),
|
||||
'rule-trigger-stop' => $request->get('rule-trigger-stop'),
|
||||
'rule-actions' => $request->get('rule-action'),
|
||||
'rule-action-values' => $request->get('rule-action-value'),
|
||||
'rule-action-stop' => $request->get('rule-action-stop'),
|
||||
'stop_processing' => intval($request->get('stop_processing')) == 1,
|
||||
];
|
||||
$data = $request->getRuleData();
|
||||
$repository->update($rule, $data);
|
||||
|
||||
Session::flash('success', trans('firefly.updated_rule', ['title' => $rule->title]));
|
||||
@ -381,7 +360,6 @@ class RuleController extends Controller
|
||||
$data = [
|
||||
'rule_group_id' => $repository->getFirstRuleGroup()->id,
|
||||
'stop_processing' => 0,
|
||||
'user_id' => auth()->user()->id,
|
||||
'title' => trans('firefly.default_rule_name'),
|
||||
'description' => trans('firefly.default_rule_description'),
|
||||
'trigger' => 'store-journal',
|
||||
@ -410,11 +388,10 @@ class RuleController extends Controller
|
||||
{
|
||||
|
||||
/** @var RuleGroupRepositoryInterface $repository */
|
||||
$repository = app('FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface');
|
||||
$repository = app(RuleGroupRepositoryInterface::class);
|
||||
|
||||
if ($repository->count() === 0) {
|
||||
$data = [
|
||||
'user_id' => auth()->user()->id,
|
||||
'title' => trans('firefly.default_rule_group_name'),
|
||||
'description' => trans('firefly.default_rule_group_description'),
|
||||
];
|
||||
|
@ -41,8 +41,16 @@ class RuleGroupController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.rules'));
|
||||
View::share('mainTitleIcon', 'fa-random');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,12 +212,7 @@ class RuleGroupController extends Controller
|
||||
*/
|
||||
public function store(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository)
|
||||
{
|
||||
$data = [
|
||||
'title' => $request->input('title'),
|
||||
'description' => $request->input('description'),
|
||||
'user_id' => auth()->user()->id,
|
||||
];
|
||||
|
||||
$data = $request->getRuleGroupData();
|
||||
$ruleGroup = $repository->store($data);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.created_new_rule_group', ['title' => $ruleGroup->title])));
|
||||
|
@ -29,6 +29,13 @@ class SearchController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,15 +50,22 @@ class TagController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
View::share('hideTags', true);
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', 'Tags');
|
||||
View::share('mainTitleIcon', 'fa-tags');
|
||||
View::share('hideTags', true);
|
||||
$this->tagOptions = [
|
||||
'nothing' => trans('firefly.regular_tag'),
|
||||
'balancingAct' => trans('firefly.balancing_act'),
|
||||
'advancePayment' => trans('firefly.advance_payment'),
|
||||
];
|
||||
View::share('tagOptions', $this->tagOptions);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
241
app/Http/Controllers/Transaction/ConvertController.php
Normal file
241
app/Http/Controllers/Transaction/ConvertController.php
Normal file
@ -0,0 +1,241 @@
|
||||
<?php
|
||||
/**
|
||||
* ConvertController.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Transaction;
|
||||
|
||||
use ExpandedForm;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Session;
|
||||
use View;
|
||||
|
||||
/**
|
||||
* Class ConvertController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Transaction
|
||||
*/
|
||||
class ConvertController extends Controller
|
||||
{
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accounts;
|
||||
|
||||
/**
|
||||
* ConvertController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// some useful repositories:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->accounts = app(AccountRepositoryInterface::class);
|
||||
|
||||
View::share('title', trans('firefly.transactions'));
|
||||
View::share('mainTitleIcon', 'fa-exchange');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionType $destinationType
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*/
|
||||
public function convert(TransactionType $destinationType, TransactionJournal $journal)
|
||||
{
|
||||
$positiveAmount = TransactionJournal::amountPositive($journal);
|
||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
|
||||
$sourceType = $journal->transactionType;
|
||||
$subTitle = trans('firefly.convert_to_' . $destinationType->type, ['description' => $journal->description]);
|
||||
$subTitleIcon = 'fa-exchange';
|
||||
|
||||
// cannot convert to its own type.
|
||||
if ($sourceType->type === $destinationType->type) {
|
||||
Session::flash('info', trans('firefly.convert_is_already_type_' . $destinationType->type));
|
||||
|
||||
return redirect(route('transactions.show', [$journal->id]));
|
||||
}
|
||||
|
||||
// cannot convert split.
|
||||
if ($journal->transactions()->count() > 2) {
|
||||
Session::flash('error', trans('firefly.cannot_convert_split_journl'));
|
||||
|
||||
return redirect(route('transactions.show', [$journal->id]));
|
||||
}
|
||||
|
||||
// get source and destination account:
|
||||
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
|
||||
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
|
||||
|
||||
return view(
|
||||
'transactions.convert',
|
||||
compact(
|
||||
'sourceType', 'destinationType', 'journal', 'assetAccounts',
|
||||
'positiveAmount', 'sourceAccount', 'destinationAccount', 'sourceType',
|
||||
'subTitle', 'subTitleIcon'
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// convert withdrawal to deposit requires a new source account ()
|
||||
// or to transfer requires
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param TransactionType $destinationType
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function submit(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal)
|
||||
{
|
||||
$data = $request->all();
|
||||
|
||||
// cannot convert to its own type.
|
||||
if ($journal->transactionType->type === $destinationType->type) {
|
||||
Session::flash('error', trans('firefly.convert_is_already_type_' . $destinationType->type));
|
||||
|
||||
return redirect(route('transactions.show', [$journal->id]));
|
||||
}
|
||||
|
||||
// cannot convert split.
|
||||
if ($journal->transactions()->count() > 2) {
|
||||
Session::flash('error', trans('firefly.cannot_convert_split_journl'));
|
||||
|
||||
return redirect(route('transactions.show', [$journal->id]));
|
||||
}
|
||||
|
||||
// get the new source and destination account:
|
||||
$source = $this->getSourceAccount($journal, $destinationType, $data);
|
||||
$destination = $this->getDestinationAccount($journal, $destinationType, $data);
|
||||
|
||||
// update the journal:
|
||||
$errors = $repository->convert($journal, $destinationType, $source, $destination);
|
||||
|
||||
if ($errors->count() > 0) {
|
||||
return redirect(route('transactions.convert', [strtolower($destinationType->type), $journal->id]))->withErrors($errors)->withInput();
|
||||
}
|
||||
|
||||
Session::flash('success', trans('firefly.converted_to_' . $destinationType->type));
|
||||
|
||||
return redirect(route('transactions.show', [$journal->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param TransactionType $destinationType
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function getDestinationAccount(TransactionJournal $journal, TransactionType $destinationType, array $data): Account
|
||||
{
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
|
||||
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
|
||||
$sourceType = $journal->transactionType;
|
||||
$destination = null;
|
||||
$joined = $sourceType->type . '-' . $destinationType->type;
|
||||
switch ($joined) {
|
||||
default:
|
||||
throw new FireflyException('Cannot handle ' . $joined);
|
||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one
|
||||
$destination = $sourceAccount;
|
||||
break;
|
||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: # two
|
||||
$destination = $accountRepository->find(intval($data['destination_account_asset']));
|
||||
break;
|
||||
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: # three
|
||||
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: #five
|
||||
$data = [
|
||||
'name' => $data['destination_account_expense'],
|
||||
'accountType' => 'expense',
|
||||
'virtualBalance' => 0,
|
||||
'active' => true,
|
||||
'iban' => null,
|
||||
];
|
||||
$destination = $accountRepository->store($data);
|
||||
break;
|
||||
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four
|
||||
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six
|
||||
$destination = $destinationAccount;
|
||||
break;
|
||||
}
|
||||
|
||||
return $destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param TransactionType $destinationType
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function getSourceAccount(TransactionJournal $journal, TransactionType $destinationType, array $data): Account
|
||||
{
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
|
||||
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
|
||||
$sourceType = $journal->transactionType;
|
||||
$source = new Account;
|
||||
$joined = $sourceType->type . '-' . $destinationType->type;
|
||||
switch ($joined) {
|
||||
default:
|
||||
throw new FireflyException('Cannot handle ' . $joined);
|
||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one
|
||||
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six
|
||||
$data = [
|
||||
'name' => $data['source_account_revenue'],
|
||||
'accountType' => 'revenue',
|
||||
'virtualBalance' => 0,
|
||||
'active' => true,
|
||||
'iban' => null,
|
||||
];
|
||||
$source = $accountRepository->store($data);
|
||||
break;
|
||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: # two
|
||||
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: #five
|
||||
$source = $sourceAccount;
|
||||
break;
|
||||
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: # three
|
||||
$source = $destinationAccount;
|
||||
break;
|
||||
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four
|
||||
$source = $accountRepository->find(intval($data['source_account_asset']));
|
||||
break;
|
||||
}
|
||||
|
||||
return $source;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -41,8 +41,16 @@ class MassController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.transactions'));
|
||||
View::share('mainTitleIcon', 'fa-repeat');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,7 +217,6 @@ class MassController extends Controller
|
||||
'destination_account_id' => intval($destAccountId),
|
||||
'destination_account_name' => $destAccountName,
|
||||
'amount' => round($request->get('amount')[$journal->id], 4),
|
||||
'user' => auth()->user()->id,
|
||||
'amount_currency_id_amount' => intval($request->get('amount_currency_id_amount_' . $journal->id)),
|
||||
'date' => new Carbon($request->get('date')[$journal->id]),
|
||||
'interest_date' => $journal->interest_date,
|
||||
|
@ -59,8 +59,6 @@ class SingleController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
View::share('title', trans('firefly.transactions'));
|
||||
View::share('mainTitleIcon', 'fa-repeat');
|
||||
|
||||
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
|
||||
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
|
||||
@ -75,11 +73,13 @@ class SingleController extends Controller
|
||||
$this->piggyBanks = app(PiggyBankRepositoryInterface::class);
|
||||
$this->attachments = app(AttachmentHelperInterface::class);
|
||||
|
||||
View::share('title', trans('firefly.transactions'));
|
||||
View::share('mainTitleIcon', 'fa-repeat');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Session;
|
||||
use Steam;
|
||||
@ -62,8 +63,7 @@ class SplitController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
View::share('mainTitleIcon', 'fa-share-alt');
|
||||
View::share('title', trans('firefly.split-transactions'));
|
||||
|
||||
|
||||
// some useful repositories:
|
||||
$this->middleware(
|
||||
@ -73,6 +73,8 @@ class SplitController extends Controller
|
||||
$this->tasker = app(JournalTaskerInterface::class);
|
||||
$this->attachments = app(AttachmentHelperInterface::class);
|
||||
$this->currencies = app(CurrencyRepositoryInterface::class);
|
||||
View::share('mainTitleIcon', 'fa-share-alt');
|
||||
View::share('title', trans('firefly.split-transactions'));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@ -264,6 +266,7 @@ class SplitController extends Controller
|
||||
$return = [];
|
||||
$transactions = $request->get('transactions');
|
||||
foreach ($transactions as $transaction) {
|
||||
|
||||
$return[] = [
|
||||
'description' => $transaction['description'],
|
||||
'source_account_id' => $transaction['source_account_id'] ?? 0,
|
||||
@ -273,9 +276,9 @@ class SplitController extends Controller
|
||||
'amount' => round($transaction['amount'] ?? 0, 2),
|
||||
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
|
||||
'category' => $transaction['category'] ?? '',
|
||||
'user' => auth()->user()->id, // needed for accounts.
|
||||
];
|
||||
}
|
||||
Log::debug(sprintf('Found %d splits in request data.', count($return)));
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
@ -35,9 +35,17 @@ class TransactionController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.transactions'));
|
||||
View::share('mainTitleIcon', 'fa-repeat');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,17 +70,29 @@ class Range
|
||||
// set start, end and finish:
|
||||
$this->setRange();
|
||||
|
||||
// set view variables.
|
||||
$this->configureView();
|
||||
|
||||
// get variables for date range:
|
||||
$this->datePicker();
|
||||
|
||||
// set view variables.
|
||||
$this->configureView();
|
||||
// set more view variables:
|
||||
$this->configureList();
|
||||
}
|
||||
|
||||
return $theNext($request);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function configureList()
|
||||
{
|
||||
$pref = Preferences::get('list-length', config('firefly.list_length', 10))->data;
|
||||
View::share('listLength', $pref);
|
||||
}
|
||||
|
||||
private function configureView()
|
||||
{
|
||||
$pref = Preferences::get('language', config('firefly.default_language', 'en_US'));
|
||||
|
@ -14,8 +14,7 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use Input;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Class AccountFormRequest
|
||||
@ -45,7 +44,6 @@ class AccountFormRequest extends Request
|
||||
'accountType' => $this->input('what'),
|
||||
'virtualBalance' => round($this->input('virtualBalance'), 2),
|
||||
'virtualBalanceCurrency' => intval($this->input('amount_currency_id_virtualBalance')),
|
||||
'user' => auth()->user()->id,
|
||||
'iban' => trim($this->input('iban')),
|
||||
'accountNumber' => trim($this->input('accountNumber')),
|
||||
'accountRole' => $this->input('accountRole'),
|
||||
@ -62,15 +60,17 @@ class AccountFormRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$accountRoles = join(',', array_keys(config('firefly.accountRoles')));
|
||||
$types = join(',', array_keys(config('firefly.subTitlesByIdentifier')));
|
||||
$ccPaymentTypes = join(',', array_keys(config('firefly.ccTypes')));
|
||||
|
||||
$nameRule = 'required|min:1|uniqueAccountForUser';
|
||||
$idRule = '';
|
||||
if (Account::find(Input::get('id'))) {
|
||||
if (!is_null($repository->find(intval($this->get('id')))->id)) {
|
||||
$idRule = 'belongsToUser:accounts';
|
||||
$nameRule = 'required|min:1|uniqueAccountForUser:' . Input::get('id');
|
||||
$nameRule = 'required|min:1|uniqueAccountForUser:' . $this->get('id');
|
||||
}
|
||||
|
||||
return [
|
||||
|
@ -14,7 +14,6 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Input;
|
||||
|
||||
/**
|
||||
* Class BillFormRequest
|
||||
@ -46,7 +45,6 @@ class BillFormRequest extends Request
|
||||
'amount_currency_id_amount_max' => intval($this->get('amount_currency_id_amount_max')),
|
||||
'amount_max' => round($this->get('amount_max'), 2),
|
||||
'date' => new Carbon($this->get('date')),
|
||||
'user' => auth()->user()->id,
|
||||
'repeat_freq' => $this->get('repeat_freq'),
|
||||
'skip' => intval($this->get('skip')),
|
||||
'automatch' => intval($this->get('automatch')) === 1,
|
||||
@ -61,9 +59,9 @@ class BillFormRequest extends Request
|
||||
{
|
||||
$nameRule = 'required|between:1,255|uniqueObjectForUser:bills,name';
|
||||
$matchRule = 'required|between:1,255|uniqueObjectForUser:bills,match';
|
||||
if (intval(Input::get('id')) > 0) {
|
||||
$nameRule .= ',' . intval(Input::get('id'));
|
||||
$matchRule .= ',' . intval(Input::get('id'));
|
||||
if (intval($this->get('id')) > 0) {
|
||||
$nameRule .= ',' . intval($this->get('id'));
|
||||
$matchRule .= ',' . intval($this->get('id'));
|
||||
}
|
||||
|
||||
$rules = [
|
||||
|
@ -13,8 +13,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use FireflyIII\Models\Budget;
|
||||
use Input;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Class BudgetFormRequest
|
||||
@ -39,7 +38,6 @@ class BudgetFormRequest extends Request
|
||||
{
|
||||
return [
|
||||
'name' => trim($this->input('name')),
|
||||
'user' => auth()->user()->id,
|
||||
'active' => intval($this->input('active')) == 1,
|
||||
];
|
||||
}
|
||||
@ -49,10 +47,11 @@ class BudgetFormRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name';
|
||||
if (Budget::find(Input::get('id'))) {
|
||||
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,' . intval(Input::get('id'));
|
||||
if (!is_null($repository->find(intval($this->get('id')))->id)) {
|
||||
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,' . intval($this->get('id'));
|
||||
}
|
||||
|
||||
return [
|
||||
|
@ -13,8 +13,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use FireflyIII\Models\Category;
|
||||
use Input;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Class CategoryFormRequest
|
||||
@ -33,15 +32,26 @@ class CategoryFormRequest extends Request
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCategoryData(): array
|
||||
{
|
||||
return [
|
||||
'name' => trim($this->input('name')),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
|
||||
/** @var CategoryRepositoryInterface $repository */
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name';
|
||||
if (Category::find(Input::get('id'))) {
|
||||
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,' . intval(Input::get('id'));
|
||||
if (!is_null($repository->find(intval($this->get('id')))->id)) {
|
||||
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,' . intval($this->get('id'));
|
||||
}
|
||||
|
||||
return [
|
||||
|
@ -30,6 +30,16 @@ class ConfigurationRequest extends Request
|
||||
return auth()->check() && auth()->user()->hasRole('owner');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getConfigurationData(): array
|
||||
{
|
||||
return [
|
||||
'single_user_mode' => intval($this->get('single_user_mode')) === 1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
|
@ -13,8 +13,6 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use Input;
|
||||
|
||||
/**
|
||||
* Class BillFormRequest
|
||||
*
|
||||
@ -55,7 +53,7 @@ class CurrencyFormRequest extends Request
|
||||
'name' => 'required|max:48|min:1|unique:transaction_currencies,name',
|
||||
'symbol' => 'required|min:1|max:8|unique:transaction_currencies,symbol',
|
||||
];
|
||||
if (intval(Input::get('id')) > 0) {
|
||||
if (intval($this->get('id')) > 0) {
|
||||
$rules = [
|
||||
'code' => 'required|min:3|max:3',
|
||||
'name' => 'required|max:48|min:1',
|
||||
|
@ -16,7 +16,6 @@ namespace FireflyIII\Http\Requests;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Input;
|
||||
|
||||
/**
|
||||
* Class JournalFormRequest
|
||||
@ -44,7 +43,6 @@ class JournalFormRequest extends Request
|
||||
{
|
||||
$data = [
|
||||
'what' => $this->get('what'), // type. can be 'deposit', 'withdrawal' or 'transfer'
|
||||
'user' => auth()->user()->id,
|
||||
'date' => new Carbon($this->get('date')),
|
||||
'tags' => explode(',', $this->getFieldOrEmptyString('tags')),
|
||||
'currency_id' => intval($this->get('amount_currency_id_amount')),
|
||||
@ -80,7 +78,7 @@ class JournalFormRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$what = Input::get('what');
|
||||
$what = $this->get('what');
|
||||
$rules = [
|
||||
'what' => 'required|in:withdrawal,deposit,transfer',
|
||||
'date' => 'required|date',
|
||||
|
@ -13,7 +13,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use Input;
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Class PiggyBankFormRequest
|
||||
@ -32,6 +32,21 @@ class PiggyBankFormRequest extends Request
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getPiggyBankData(): array
|
||||
{
|
||||
return [
|
||||
'name' => trim($this->get('name')),
|
||||
'startdate' => new Carbon,
|
||||
'account_id' => intval($this->get('account_id')),
|
||||
'targetamount' => round($this->get('targetamount'), 2),
|
||||
'targetdate' => strlen($this->get('targetdate')) > 0 ? new Carbon($this->get('targetdate')) : null,
|
||||
'note' => trim($this->get('note')),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
@ -40,8 +55,8 @@ class PiggyBankFormRequest extends Request
|
||||
|
||||
$nameRule = 'required|between:1,255|uniquePiggyBankForUser';
|
||||
$targetDateRule = 'date';
|
||||
if (intval(Input::get('id'))) {
|
||||
$nameRule = 'required|between:1,255|uniquePiggyBankForUser:' . intval(Input::get('id'));
|
||||
if (intval($this->get('id'))) {
|
||||
$nameRule = 'required|between:1,255|uniquePiggyBankForUser:' . intval($this->get('id'));
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,8 +13,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use Input;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Class RuleFormRequest
|
||||
@ -33,12 +32,33 @@ class RuleFormRequest extends Request
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRuleData(): array
|
||||
{
|
||||
return [
|
||||
'title' => trim($this->get('title')),
|
||||
'active' => intval($this->get('active')) == 1,
|
||||
'trigger' => trim($this->get('trigger')),
|
||||
'description' => trim($this->get('description')),
|
||||
'rule-triggers' => $this->get('rule-trigger'),
|
||||
'rule-trigger-values' => $this->get('rule-trigger-value'),
|
||||
'rule-trigger-stop' => $this->get('rule-trigger-stop'),
|
||||
'rule-actions' => $this->get('rule-action'),
|
||||
'rule-action-values' => $this->get('rule-action-value'),
|
||||
'rule-action-stop' => $this->get('rule-action-stop'),
|
||||
'stop_processing' => intval($this->get('stop_processing')) === 1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
|
||||
/** @var RuleGroupRepositoryInterface $repository */
|
||||
$repository = app(RuleGroupRepositoryInterface::class);
|
||||
$validTriggers = array_keys(config('firefly.rule-triggers'));
|
||||
$validActions = array_keys(config('firefly.rule-actions'));
|
||||
|
||||
@ -46,8 +66,8 @@ class RuleFormRequest extends Request
|
||||
$contextActions = join(',', config('firefly.rule-actions-text'));
|
||||
|
||||
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title';
|
||||
if (RuleGroup::find(Input::get('id'))) {
|
||||
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval(Input::get('id'));
|
||||
if (!is_null($repository->find(intval($this->get('id')))->id)) {
|
||||
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval($this->get('id'));
|
||||
}
|
||||
|
||||
$rules = [
|
||||
|
@ -20,8 +20,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use Input;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Class RuleGroupFormRequest
|
||||
@ -40,15 +39,27 @@ class RuleGroupFormRequest extends Request
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRuleGroupData(): array
|
||||
{
|
||||
return [
|
||||
'title' => trim($this->input('title')),
|
||||
'description' => trim($this->input('description')),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
|
||||
/** @var RuleGroupRepositoryInterface $repository */
|
||||
$repository = app(RuleGroupRepositoryInterface::class, [auth()->user()]);
|
||||
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title';
|
||||
if (RuleGroup::find(Input::get('id'))) {
|
||||
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval(Input::get('id'));
|
||||
if (!is_null($repository->find(intval($this->get('id')))->id)) {
|
||||
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval($this->get('id'));
|
||||
}
|
||||
|
||||
return [
|
||||
|
@ -13,8 +13,7 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Tag;
|
||||
use Input;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
|
||||
/**
|
||||
* Class TagFormRequest
|
||||
@ -38,7 +37,7 @@ class TagFormRequest extends Request
|
||||
*/
|
||||
public function collectTagData() :array
|
||||
{
|
||||
if (Input::get('setTag') == 'true') {
|
||||
if ($this->get('setTag') == 'true') {
|
||||
$latitude = $this->get('latitude');
|
||||
$longitude = $this->get('longitude');
|
||||
$zoomLevel = $this->get('zoomLevel');
|
||||
@ -69,11 +68,13 @@ class TagFormRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
/** @var TagRepositoryInterface $repository */
|
||||
$repository = app(TagRepositoryInterface::class);
|
||||
$idRule = '';
|
||||
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag';
|
||||
if (Tag::find(Input::get('id'))) {
|
||||
if (!is_null($repository->find(intval($this->get('id')))->id)) {
|
||||
$idRule = 'belongsToUser:tags';
|
||||
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag,' . Input::get('id');
|
||||
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag,' . $this->get('id');
|
||||
}
|
||||
|
||||
return [
|
||||
|
@ -25,6 +25,7 @@ use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\User;
|
||||
|
||||
/**
|
||||
@ -595,13 +596,24 @@ Breadcrumbs::register(
|
||||
Breadcrumbs::register(
|
||||
'transactions.show', function (BreadCrumbGenerator $breadcrumbs, TransactionJournal $journal) {
|
||||
|
||||
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
||||
$what = strtolower($journal->transactionType->type);
|
||||
$breadcrumbs->parent('transactions.index', $what);
|
||||
$breadcrumbs->push($journal->description, route('transactions.show', [$journal->id]));
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'transactions.convert', function (BreadCrumbGenerator $breadcrumbs, TransactionType $destinationType, TransactionJournal $journal) {
|
||||
|
||||
$breadcrumbs->parent('transactions.show', $journal);
|
||||
$breadcrumbs->push(
|
||||
trans('firefly.convert_to_' . $destinationType->type, ['description' => $journal->description]),
|
||||
route('transactions.convert', [strtolower($destinationType->type), $journal->id])
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* SPLIT
|
||||
*/
|
||||
|
@ -369,6 +369,11 @@ class CsvSetup implements SetupInterface
|
||||
|
||||
foreach ($results as $rowIndex => $row) {
|
||||
|
||||
// skip first row?
|
||||
if ($rowIndex === 0 && $config['has-headers']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// run specifics here:
|
||||
// and this is the point where the specifix go to work.
|
||||
foreach ($config['specifics'] as $name => $enabled) {
|
||||
|
@ -58,9 +58,12 @@ class Preference extends Model
|
||||
$data = Crypt::decrypt($value);
|
||||
} catch (DecryptException $e) {
|
||||
Log::error('Could not decrypt preference.', ['id' => $this->id, 'name' => $this->name, 'data' => $value]);
|
||||
throw new FireflyException('Could not decrypt preference #' . $this->id . '.');
|
||||
throw new FireflyException(
|
||||
sprintf('Could not decrypt preference #%d. If this error persists, please run "php artisan cache:clear" on the command line.', $this->id)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return json_decode($data);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ namespace FireflyIII\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* FireflyIII\Models\TransactionType
|
||||
@ -43,6 +44,25 @@ class TransactionType extends Model
|
||||
|
||||
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*
|
||||
* @return Model|null|static
|
||||
*/
|
||||
public static function routeBinder(string $type)
|
||||
{
|
||||
if (!auth()->check()) {
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
$transactionType = self::where('type', $type)->first();
|
||||
if (!is_null($transactionType)) {
|
||||
return $transactionType;
|
||||
}
|
||||
throw new NotFoundHttpException;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -388,7 +388,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
// create it:
|
||||
$newAccount = new Account(
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
'user_id' => $this->user->id,
|
||||
'account_type_id' => $accountType->id,
|
||||
'name' => $data['name'],
|
||||
'virtual_balance' => $data['virtualBalance'],
|
||||
@ -417,13 +417,12 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
protected function storeInitialBalance(Account $account, array $data): TransactionJournal
|
||||
{
|
||||
$amount = $data['openingBalance'];
|
||||
$user = $data['user'];
|
||||
$name = $data['name'];
|
||||
$opposing = $this->storeOpposingAccount($amount, $user, $name);
|
||||
$opposing = $this->storeOpposingAccount($amount, $name);
|
||||
$transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
'user_id' => $this->user->id,
|
||||
'transaction_type_id' => $transactionType->id,
|
||||
'transaction_currency_id' => $data['openingBalanceCurrency'],
|
||||
'description' => 'Initial balance for "' . $account->name . '"',
|
||||
@ -458,16 +457,14 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param float $amount
|
||||
* @param int $user
|
||||
* @param string $name
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
protected function storeOpposingAccount(float $amount, int $user, string $name):Account
|
||||
protected function storeOpposingAccount(float $amount, string $name):Account
|
||||
{
|
||||
$type = $amount < 0 ? 'expense' : 'revenue';
|
||||
$opposingData = [
|
||||
'user' => $user,
|
||||
'accountType' => $type,
|
||||
'name' => $name . ' initial balance',
|
||||
'active' => false,
|
||||
|
@ -19,6 +19,7 @@ use DB;
|
||||
use FireflyIII\Helpers\Collection\Account as AccountCollection;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
@ -305,6 +306,8 @@ class AccountTasker implements AccountTaskerInterface
|
||||
* - Expense accounts (where money is spent) should only return earnings (the account gets money).
|
||||
* - Revenue accounts (where money comes from) should only return expenses (they spend money).
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param array $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
@ -325,6 +328,7 @@ class AccountTasker implements AccountTaskerInterface
|
||||
$join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where('other_side.amount', $joinModifier, 0);
|
||||
}
|
||||
)
|
||||
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
@ -361,6 +365,8 @@ class AccountTasker implements AccountTaskerInterface
|
||||
* @param Carbon $end
|
||||
* @param bool $incoming
|
||||
*
|
||||
* Opening balances are ignored.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
protected function financialReport(array $accounts, Carbon $start, Carbon $end, bool $incoming): Collection
|
||||
@ -371,12 +377,14 @@ class AccountTasker implements AccountTaskerInterface
|
||||
$query = Transaction
|
||||
::distinct()
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
|
||||
->leftJoin(
|
||||
'transactions as other_side', function (JoinClause $join) use ($joinModifier) {
|
||||
$join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where('other_side.amount', $joinModifier, 0);
|
||||
}
|
||||
)
|
||||
->leftJoin('accounts as other_account', 'other_account.id', '=', 'other_side.account_id')
|
||||
->where('transaction_types.type','!=', TransactionType::OPENING_BALANCE)
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
|
@ -13,6 +13,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Repositories\Attachment;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\User;
|
||||
@ -63,6 +64,24 @@ class AttachmentRepository implements AttachmentRepositoryInterface
|
||||
return $this->user->attachments()->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getBetween(Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
$query = $this->user
|
||||
->attachments()
|
||||
->leftJoin('transaction_journals', 'attachments.attachable_id', '=', 'transaction_journals.id')
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->get(['attachments.*']);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
* @param array $data
|
||||
|
@ -13,6 +13,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Repositories\Attachment;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@ -36,6 +37,14 @@ interface AttachmentRepositoryInterface
|
||||
*/
|
||||
public function get(): Collection;
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getBetween(Carbon $start, Carbon $end): Collection;
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
* @param array $attachmentData
|
||||
|
@ -312,25 +312,11 @@ class BillRepository implements BillRepositoryInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all journals that were recorded on this bill between these dates.
|
||||
*
|
||||
* @param Bill $bill
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournalsInRange(Bill $bill, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
return $bill->transactionJournals()->before($end)->after($start)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $bill
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOverallAverage($bill): string
|
||||
public function getOverallAverage(Bill $bill): string
|
||||
{
|
||||
$journals = $bill->transactionJournals()->get();
|
||||
$sum = '0';
|
||||
@ -347,6 +333,21 @@ class BillRepository implements BillRepositoryInterface
|
||||
return $avg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getPaidDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
$dates = $bill->transactionJournals()->before($end)->after($start)->get(['transaction_journals.date'])->pluck('date');
|
||||
|
||||
return $dates;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Between start and end, tells you on which date(s) the bill is expected to hit.
|
||||
*
|
||||
@ -451,21 +452,6 @@ class BillRepository implements BillRepositoryInterface
|
||||
return $avg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return \Carbon\Carbon
|
||||
*/
|
||||
public function lastFoundMatch(Bill $bill): Carbon
|
||||
{
|
||||
$last = $bill->transactionJournals()->orderBy('date', 'DESC')->first();
|
||||
if ($last) {
|
||||
return $last->date;
|
||||
}
|
||||
|
||||
return Carbon::now()->addDays(2); // in the future!
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a bill and a date, this method will tell you at which moment this bill expects its next
|
||||
* transaction. Whether or not it is there already, is not relevant.
|
||||
@ -610,7 +596,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
'name' => $data['name'],
|
||||
'match' => $data['match'],
|
||||
'amount_min' => $data['amount_min'],
|
||||
'user_id' => $data['user'],
|
||||
'user_id' => $this->user->id,
|
||||
'amount_max' => $data['amount_max'],
|
||||
'date' => $data['date'],
|
||||
'repeat_freq' => $data['repeat_freq'],
|
||||
|
@ -114,22 +114,20 @@ interface BillRepositoryInterface
|
||||
public function getJournals(Bill $bill, int $page, int $pageSize = 50): LengthAwarePaginator;
|
||||
|
||||
/**
|
||||
* Get all journals that were recorded on this bill between these dates.
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOverallAverage(Bill $bill): string;
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournalsInRange(Bill $bill, Carbon $start, Carbon $end): Collection;
|
||||
|
||||
/**
|
||||
* @param $bill
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOverallAverage($bill): string;
|
||||
public function getPaidDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection;
|
||||
|
||||
/**
|
||||
* Between start and end, tells you on which date(s) the bill is expected to hit.
|
||||
@ -157,14 +155,6 @@ interface BillRepositoryInterface
|
||||
*/
|
||||
public function getYearAverage(Bill $bill, Carbon $date): string;
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return \Carbon\Carbon
|
||||
*/
|
||||
public function lastFoundMatch(Bill $bill): Carbon;
|
||||
|
||||
|
||||
/**
|
||||
* Given a bill and a date, this method will tell you at which moment this bill expects its next
|
||||
* transaction. Whether or not it is there already, is not relevant.
|
||||
|
@ -19,6 +19,7 @@ use FireflyIII\Events\UpdatedBudgetLimit;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\User;
|
||||
@ -26,6 +27,7 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class BudgetRepository
|
||||
@ -347,79 +349,75 @@ class BudgetRepository implements BudgetRepositoryInterface
|
||||
*/
|
||||
public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) : string
|
||||
{
|
||||
// first collect actual transaction journals (fairly easy)
|
||||
$query = $this->user
|
||||
->transactionJournals()
|
||||
// collect amount of transaction journals, which is easy:
|
||||
$budgetIds = $budgets->pluck('id')->toArray();
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
|
||||
Log::debug('spentInPeriod: Now in spentInPeriod for these budgets: ', $budgetIds);
|
||||
Log::debug('spentInPeriod: and these accounts: ', $accountIds);
|
||||
Log::debug(sprintf('spentInPeriod: Start date is "%s", end date is "%s"', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
|
||||
$fromJournalsQuery = TransactionJournal
|
||||
::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->leftJoin(
|
||||
'transactions as source', function (JoinClause $join) {
|
||||
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0);
|
||||
'transactions', function (JoinClause $join) {
|
||||
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', '0');
|
||||
}
|
||||
)
|
||||
->leftJoin(
|
||||
'transactions as destination', function (JoinClause $join) {
|
||||
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0);
|
||||
}
|
||||
);
|
||||
$query->whereNull('source.deleted_at');
|
||||
$query->whereNull('destination.deleted_at');
|
||||
$query->where('transaction_journals.completed', 1);
|
||||
|
||||
if ($end >= $start) {
|
||||
$query->before($end)->after($start);
|
||||
}
|
||||
if ($accounts->count() > 0) {
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$query->where(
|
||||
// source.account_id in accountIds XOR destination.account_id in accountIds
|
||||
function (Builder $query) use ($accountIds) {
|
||||
$query->where(
|
||||
function (Builder $q1) use ($accountIds) {
|
||||
$q1->whereIn('source.account_id', $accountIds)
|
||||
->whereNotIn('destination.account_id', $accountIds);
|
||||
}
|
||||
)->orWhere(
|
||||
function (Builder $q2) use ($accountIds) {
|
||||
$q2->whereIn('destination.account_id', $accountIds)
|
||||
->whereNotIn('source.account_id', $accountIds);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
if ($budgets->count() > 0) {
|
||||
$budgetIds = $budgets->pluck('id')->toArray();
|
||||
$query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
$query->whereIn('budget_transaction_journal.budget_id', $budgetIds);
|
||||
|
||||
}
|
||||
|
||||
// that should do it:
|
||||
$ids = $query->distinct()->get(['transaction_journals.id'])->pluck('id')->toArray();
|
||||
$first = '0';
|
||||
if (count($ids) > 0) {
|
||||
$first = strval(
|
||||
$this->user->transactions()
|
||||
->whereIn('transaction_journal_id', $ids)
|
||||
->where('amount', '<', '0')
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->sum('amount')
|
||||
);
|
||||
}
|
||||
// then collection transactions (harder)
|
||||
$query = $this->user->transactions()
|
||||
->where('transactions.amount', '<', 0)
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'));
|
||||
if ($accounts->count() > 0) {
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$query->whereIn('transactions.account_id', $accountIds);
|
||||
}
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
->where('transaction_types.type', 'Withdrawal');
|
||||
|
||||
// add budgets:
|
||||
if ($budgets->count() > 0) {
|
||||
$budgetIds = $budgets->pluck('id')->toArray();
|
||||
$query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id');
|
||||
$query->whereIn('budget_transaction.budget_id', $budgetIds);
|
||||
$fromJournalsQuery->whereIn('budget_transaction_journal.budget_id', $budgetIds);
|
||||
}
|
||||
$second = strval($query->sum('transactions.amount'));
|
||||
|
||||
// add accounts:
|
||||
if ($accounts->count() > 0) {
|
||||
$fromJournalsQuery->whereIn('transactions.account_id', $accountIds);
|
||||
}
|
||||
$first = strval($fromJournalsQuery->sum('transactions.amount'));
|
||||
Log::debug(sprintf('spentInPeriod: Result from first query: %s', $first));
|
||||
unset($fromJournalsQuery);
|
||||
|
||||
// collect amount from transactions:
|
||||
/**
|
||||
* select transactions.id, budget_transaction.budget_id , transactions.amount
|
||||
*
|
||||
*
|
||||
* and budget_transaction.budget_id in (1,61)
|
||||
* and transactions.account_id in (2)
|
||||
*/
|
||||
$fromTransactionsQuery = Transaction
|
||||
::leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id')
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->where('transactions.amount', '<', 0)
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
->where('transaction_types.type', 'Withdrawal');
|
||||
|
||||
// add budgets:
|
||||
if ($budgets->count() > 0) {
|
||||
$fromTransactionsQuery->whereIn('budget_transaction.budget_id', $budgetIds);
|
||||
}
|
||||
|
||||
// add accounts:
|
||||
if ($accounts->count() > 0) {
|
||||
$fromTransactionsQuery->whereIn('transactions.account_id', $accountIds);
|
||||
}
|
||||
$second = strval($fromTransactionsQuery->sum('transactions.amount'));
|
||||
Log::debug(sprintf('spentInPeriod: Result from second query: %s', $second));
|
||||
|
||||
Log::debug(sprintf('spentInPeriod: FINAL: %s', bcadd($first, $second)));
|
||||
|
||||
return bcadd($first, $second);
|
||||
}
|
||||
@ -500,7 +498,7 @@ class BudgetRepository implements BudgetRepositoryInterface
|
||||
{
|
||||
$newBudget = new Budget(
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
'user_id' => $this->user->id,
|
||||
'name' => $data['name'],
|
||||
]
|
||||
);
|
||||
|
@ -241,6 +241,7 @@ class CategoryRepository implements CategoryRepositoryInterface
|
||||
if (count($types) > 0) {
|
||||
$query->transactionTypes($types);
|
||||
}
|
||||
|
||||
if ($accounts->count() > 0) {
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$query->leftJoin('transactions as t', 't.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
@ -275,7 +276,7 @@ class CategoryRepository implements CategoryRepositoryInterface
|
||||
}
|
||||
|
||||
|
||||
$second = $query->get(['transaction_journals.*']);
|
||||
$second = $query->get(['transaction_journals.*','transaction_types.type as transaction_type_type']);
|
||||
|
||||
$complete = $complete->merge($first);
|
||||
$complete = $complete->merge($second);
|
||||
@ -433,7 +434,7 @@ class CategoryRepository implements CategoryRepositoryInterface
|
||||
{
|
||||
$newCategory = Category::firstOrCreateEncrypted(
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
'user_id' => $this->user->id,
|
||||
'name' => $data['name'],
|
||||
]
|
||||
);
|
||||
|
@ -26,7 +26,9 @@ use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Log;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class JournalRepository
|
||||
@ -51,6 +53,41 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param TransactionType $type
|
||||
* @param Account $source
|
||||
* @param Account $destination
|
||||
*
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function convert(TransactionJournal $journal, TransactionType $type, Account $source, Account $destination): MessageBag
|
||||
{
|
||||
// default message bag that shows errors for everything.
|
||||
$messages = new MessageBag;
|
||||
$messages->add('source_account_revenue', trans('firefly.invalid_convert_selection'));
|
||||
$messages->add('destination_account_asset', trans('firefly.invalid_convert_selection'));
|
||||
$messages->add('destination_account_expense', trans('firefly.invalid_convert_selection'));
|
||||
$messages->add('source_account_asset', trans('firefly.invalid_convert_selection'));
|
||||
|
||||
if ($source->id === $destination->id || is_null($source->id) || is_null($destination->id)) {
|
||||
return $messages;
|
||||
}
|
||||
|
||||
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
|
||||
$destinationTransaction = $journal->transactions()->where('amount', '>', 0)->first();
|
||||
$sourceTransaction->account_id = $source->id;
|
||||
$sourceTransaction->save();
|
||||
$destinationTransaction->account_id = $destination->id;
|
||||
$destinationTransaction->save();
|
||||
$journal->transaction_type_id = $type->id;
|
||||
$journal->save();
|
||||
Preferences::mark();
|
||||
$messages = new MessageBag;
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
@ -95,7 +132,6 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
return $entry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
@ -107,7 +143,7 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
$transactionType = TransactionType::where('type', ucfirst($data['what']))->first();
|
||||
$journal = new TransactionJournal(
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
'user_id' => $this->user->id,
|
||||
'transaction_type_id' => $transactionType->id,
|
||||
'transaction_currency_id' => $data['currency_id'],
|
||||
'description' => $data['description'],
|
||||
@ -182,7 +218,7 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
// store actual journal.
|
||||
$journal = new TransactionJournal(
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
'user_id' => $this->user->id,
|
||||
'transaction_type_id' => $transactionType->id,
|
||||
'transaction_currency_id' => $data['amount_currency_id_amount'],
|
||||
'description' => $data['description'],
|
||||
@ -267,6 +303,7 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
$journal->description = $data['journal_description'];
|
||||
$journal->date = $data['date'];
|
||||
$journal->save();
|
||||
Log::debug(sprintf('Updated split journal #%d', $journal->id));
|
||||
|
||||
// unlink all categories:
|
||||
$journal->categories()->detach();
|
||||
@ -282,8 +319,6 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
}
|
||||
Log::debug(sprintf('Could not store meta field "%s" with value "%s" for journal #%d', json_encode($key), json_encode($value), $journal->id));
|
||||
}
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
|
||||
@ -297,6 +332,7 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
|
||||
// store each transaction.
|
||||
$identifier = 0;
|
||||
Log::debug(sprintf('Count %d transactions in updateSplitJournal()', count($data['transactions'])));
|
||||
foreach ($data['transactions'] as $transaction) {
|
||||
Log::debug(sprintf('Split journal update split transaction %d', $identifier));
|
||||
$transaction = $this->appendTransactionData($transaction, $data);
|
||||
@ -478,7 +514,7 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
if (strlen($data['source_account_name']) > 0) {
|
||||
$sourceType = AccountType::where('type', 'Revenue account')->first();
|
||||
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $data['user'], 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name'], 'active' => 1]
|
||||
['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name'], 'active' => 1]
|
||||
);
|
||||
|
||||
return [
|
||||
@ -488,7 +524,7 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
}
|
||||
$sourceType = AccountType::where('type', 'Cash account')->first();
|
||||
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $data['user'], 'account_type_id' => $sourceType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
);
|
||||
|
||||
return [
|
||||
@ -587,7 +623,7 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
$destinationType = AccountType::where('type', AccountType::EXPENSE)->first();
|
||||
$destinationAccount = Account::firstOrCreateEncrypted(
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
'user_id' => $this->user->id,
|
||||
'account_type_id' => $destinationType->id,
|
||||
'name' => $data['destination_account_name'],
|
||||
'active' => 1,
|
||||
@ -601,7 +637,7 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
}
|
||||
$destinationType = AccountType::where('type', 'Cash account')->first();
|
||||
$destinationAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $data['user'], 'account_type_id' => $destinationType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
['user_id' => $this->user->id, 'account_type_id' => $destinationType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
);
|
||||
|
||||
return [
|
||||
|
@ -13,7 +13,10 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Repositories\Journal;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Support\MessageBag;
|
||||
|
||||
/**
|
||||
* Interface JournalRepositoryInterface
|
||||
@ -32,6 +35,15 @@ interface JournalRepositoryInterface
|
||||
*/
|
||||
public function delete(TransactionJournal $journal): bool;
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param TransactionType $type
|
||||
* @param array $data
|
||||
*
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function convert(TransactionJournal $journal, TransactionType $type, Account $source, Account $destination): MessageBag;
|
||||
|
||||
/**
|
||||
* Find a specific journal
|
||||
*
|
||||
|
@ -16,6 +16,7 @@ namespace FireflyIII\Repositories\Journal;
|
||||
use Carbon\Carbon;
|
||||
use Crypt;
|
||||
use DB;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\User;
|
||||
|
@ -175,6 +175,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
|
||||
*/
|
||||
public function store(array $data): PiggyBank
|
||||
{
|
||||
$data['order'] = $this->getMaxOrder() + 1;
|
||||
$piggyBank = PiggyBank::create($data);
|
||||
|
||||
$this->updateNote($piggyBank, $data['note']);
|
||||
|
@ -233,7 +233,7 @@ class RuleRepository implements RuleRepositoryInterface
|
||||
|
||||
// start by creating a new rule:
|
||||
$rule = new Rule;
|
||||
$rule->user()->associate($data['user_id']);
|
||||
$rule->user()->associate($this->user->id);
|
||||
|
||||
$rule->rule_group_id = $data['rule_group_id'];
|
||||
$rule->order = ($order + 1);
|
||||
|
@ -79,6 +79,21 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $ruleGroupId
|
||||
*
|
||||
* @return RuleGroup
|
||||
*/
|
||||
public function find(int $ruleGroupId): RuleGroup
|
||||
{
|
||||
$group = $this->user->ruleGroups()->find($ruleGroupId);
|
||||
if (is_null($group)) {
|
||||
return new RuleGroup;
|
||||
}
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
@ -226,7 +241,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
|
||||
|
||||
$newRuleGroup = new RuleGroup(
|
||||
[
|
||||
'user_id' => $data['user_id'],
|
||||
'user_id' => $this->user->id,
|
||||
'title' => $data['title'],
|
||||
'description' => $data['description'],
|
||||
'order' => ($order + 1),
|
||||
|
@ -42,6 +42,13 @@ interface RuleGroupRepositoryInterface
|
||||
*/
|
||||
public function destroy(RuleGroup $ruleGroup, RuleGroup $moveTo = null): bool;
|
||||
|
||||
/**
|
||||
* @param int $ruleGroupId
|
||||
*
|
||||
* @return RuleGroup
|
||||
*/
|
||||
public function find(int $ruleGroupId): RuleGroup;
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
|
141
app/Rules/Actions/SetDestinationAccount.php
Normal file
141
app/Rules/Actions/SetDestinationAccount.php
Normal file
@ -0,0 +1,141 @@
|
||||
<?php
|
||||
/**
|
||||
* SetDestinationAccount.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Rules\Actions;
|
||||
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\RuleAction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class SetDestinationAccount
|
||||
*
|
||||
* @package FireflyIII\Rules\Action
|
||||
*/
|
||||
class SetDestinationAccount implements ActionInterface
|
||||
{
|
||||
|
||||
private $action;
|
||||
|
||||
/** @var TransactionJournal */
|
||||
private $journal;
|
||||
|
||||
/** @var Account */
|
||||
private $newDestinationAccount;
|
||||
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
|
||||
/**
|
||||
* TriggerInterface constructor.
|
||||
*
|
||||
* @param RuleAction $action
|
||||
*/
|
||||
public function __construct(RuleAction $action)
|
||||
{
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function act(TransactionJournal $journal): bool
|
||||
{
|
||||
$this->journal = $journal;
|
||||
$this->repository = app(AccountRepositoryInterface::class, [$journal->user]);
|
||||
$count = $journal->transactions()->count();
|
||||
if ($count > 2) {
|
||||
Log::error(sprintf('Cannot change destination account of journal #%d because it is a split journal.', $journal->id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// journal type:
|
||||
$type = $journal->transactionType->type;
|
||||
|
||||
// if this is a deposit or a transfer, the destination account must be an asset account or a default account, and it MUST exist:
|
||||
if (($type === TransactionType::DEPOSIT || $type === TransactionType::TRANSFER) && !$this->findAssetAccount()) {
|
||||
Log::error(
|
||||
sprintf(
|
||||
'Cannot change destination account of journal #%d because no asset account with name "%s" exists.',
|
||||
$journal->id, $this->action->action_value
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// if this is a withdrawal, the new destination account must be a expense account and may be created:
|
||||
if ($type === TransactionType::WITHDRAWAL) {
|
||||
$this->findExpenseAccount();
|
||||
}
|
||||
|
||||
Log::debug(sprintf('New destination account is #%d ("%s").', $this->newDestinationAccount->id, $this->newDestinationAccount->name));
|
||||
|
||||
// update destination transaction with new destination account:
|
||||
// get destination transaction:
|
||||
$transaction = $journal->transactions()->where('amount', '>', 0)->first();
|
||||
$transaction->account_id = $this->newDestinationAccount->id;
|
||||
$transaction->save();
|
||||
Log::debug(sprintf('Updated transaction #%d and gave it new account ID.', $transaction->id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function findAssetAccount(): bool
|
||||
{
|
||||
$account = $this->repository->findByName($this->action->action_value, [AccountType::DEFAULT, AccountType::ASSET]);
|
||||
|
||||
if (is_null($account->id)) {
|
||||
Log::debug(sprintf('There is NO asset account called "%s".', $this->action->action_value));
|
||||
|
||||
return false;
|
||||
}
|
||||
Log::debug(sprintf('There exists an asset account called "%s". ID is #%d', $this->action->action_value, $account->id));
|
||||
$this->newDestinationAccount = $account;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function findExpenseAccount()
|
||||
{
|
||||
$account = $this->repository->findByName($this->action->action_value, [AccountType::REVENUE]);
|
||||
if (is_null($account->id)) {
|
||||
// create new revenue account with this name:
|
||||
$data = [
|
||||
'name' => $this->action->action_value,
|
||||
'accountType' => 'expense',
|
||||
'virtualBalance' => 0,
|
||||
'active' => true,
|
||||
'iban' => null,
|
||||
];
|
||||
$account = $this->repository->store($data);
|
||||
}
|
||||
Log::debug(sprintf('Found or created expense account #%d ("%s")', $account->id, $account->name));
|
||||
$this->newDestinationAccount = $account;
|
||||
}
|
||||
}
|
140
app/Rules/Actions/SetSourceAccount.php
Normal file
140
app/Rules/Actions/SetSourceAccount.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* SetSourceAccount.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Rules\Actions;
|
||||
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\RuleAction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class SetSourceAccount
|
||||
*
|
||||
* @package FireflyIII\Rules\Action
|
||||
*/
|
||||
class SetSourceAccount implements ActionInterface
|
||||
{
|
||||
|
||||
private $action;
|
||||
|
||||
/** @var TransactionJournal */
|
||||
private $journal;
|
||||
|
||||
/** @var Account */
|
||||
private $newSourceAccount;
|
||||
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
|
||||
/**
|
||||
* TriggerInterface constructor.
|
||||
*
|
||||
* @param RuleAction $action
|
||||
*/
|
||||
public function __construct(RuleAction $action)
|
||||
{
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function act(TransactionJournal $journal): bool
|
||||
{
|
||||
$this->journal = $journal;
|
||||
$this->repository = app(AccountRepositoryInterface::class, [$journal->user]);
|
||||
$count = $journal->transactions()->count();
|
||||
if ($count > 2) {
|
||||
Log::error(sprintf('Cannot change source account of journal #%d because it is a split journal.', $journal->id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// journal type:
|
||||
$type = $journal->transactionType->type;
|
||||
// if this is a transfer or a withdrawal, the new source account must be an asset account or a default account, and it MUST exist:
|
||||
if (($type === TransactionType::WITHDRAWAL || $type === TransactionType::TRANSFER) && !$this->findAssetAccount()) {
|
||||
Log::error(
|
||||
sprintf(
|
||||
'Cannot change source account of journal #%d because no asset account with name "%s" exists.',
|
||||
$journal->id, $this->action->action_value
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// if this is a deposit, the new source account must be a revenue account and may be created:
|
||||
if ($type === TransactionType::DEPOSIT) {
|
||||
$this->findRevenueAccount();
|
||||
}
|
||||
|
||||
Log::debug(sprintf('New source account is #%d ("%s").', $this->newSourceAccount->id, $this->newSourceAccount->name));
|
||||
|
||||
// update source transaction with new source account:
|
||||
// get source transaction:
|
||||
$transaction = $journal->transactions()->where('amount', '<', 0)->first();
|
||||
$transaction->account_id = $this->newSourceAccount->id;
|
||||
$transaction->save();
|
||||
Log::debug(sprintf('Updated transaction #%d and gave it new account ID.', $transaction->id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function findAssetAccount(): bool
|
||||
{
|
||||
$account = $this->repository->findByName($this->action->action_value, [AccountType::DEFAULT, AccountType::ASSET]);
|
||||
|
||||
if (is_null($account->id)) {
|
||||
Log::debug(sprintf('There is NO asset account called "%s".', $this->action->action_value));
|
||||
|
||||
return false;
|
||||
}
|
||||
Log::debug(sprintf('There exists an asset account called "%s". ID is #%d', $this->action->action_value, $account->id));
|
||||
$this->newSourceAccount = $account;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function findRevenueAccount()
|
||||
{
|
||||
$account = $this->repository->findByName($this->action->action_value, [AccountType::REVENUE]);
|
||||
if (is_null($account->id)) {
|
||||
// create new revenue account with this name:
|
||||
$data = [
|
||||
'name' => $this->action->action_value,
|
||||
'accountType' => 'revenue',
|
||||
'virtualBalance' => 0,
|
||||
'active' => true,
|
||||
'iban' => null,
|
||||
];
|
||||
$account = $this->repository->store($data);
|
||||
}
|
||||
Log::debug(sprintf('Found or created revenue account #%d ("%s")', $account->id, $account->name));
|
||||
$this->newSourceAccount = $account;
|
||||
}
|
||||
}
|
@ -93,7 +93,7 @@ class ExpandedForm
|
||||
$label = $this->label($name, $options);
|
||||
$options = $this->expandOptionArray($name, $label, $options);
|
||||
$classes = $this->getHolderClasses($name);
|
||||
$value = $this->fillFieldValue($name, $value);
|
||||
$value = round($this->fillFieldValue($name, $value), 2);
|
||||
$options['step'] = 'any';
|
||||
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
|
||||
$currencies = Amt::getAllCurrencies();
|
||||
|
158
composer.lock
generated
158
composer.lock
generated
@ -410,35 +410,35 @@
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
"version": "v1.2.7",
|
||||
"version": "v1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/annotations.git",
|
||||
"reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535"
|
||||
"reference": "30e07cf03edc3cd3ef579d0dd4dd8c58250799a5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535",
|
||||
"reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535",
|
||||
"url": "https://api.github.com/repos/doctrine/annotations/zipball/30e07cf03edc3cd3ef579d0dd4dd8c58250799a5",
|
||||
"reference": "30e07cf03edc3cd3ef579d0dd4dd8c58250799a5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/lexer": "1.*",
|
||||
"php": ">=5.3.2"
|
||||
"php": "^5.6 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/cache": "1.*",
|
||||
"phpunit/phpunit": "4.*"
|
||||
"phpunit/phpunit": "^5.6.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.3.x-dev"
|
||||
"dev-master": "1.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Doctrine\\Common\\Annotations\\": "lib/"
|
||||
"psr-4": {
|
||||
"Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@ -474,20 +474,20 @@
|
||||
"docblock",
|
||||
"parser"
|
||||
],
|
||||
"time": "2015-08-31 12:32:49"
|
||||
"time": "2016-10-24 11:45:47"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/cache",
|
||||
"version": "v1.6.0",
|
||||
"version": "v1.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/cache.git",
|
||||
"reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6"
|
||||
"reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6",
|
||||
"reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6",
|
||||
"url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3",
|
||||
"reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -544,7 +544,7 @@
|
||||
"cache",
|
||||
"caching"
|
||||
],
|
||||
"time": "2015-12-31 16:37:02"
|
||||
"time": "2016-10-29 11:16:17"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/collections",
|
||||
@ -1275,16 +1275,16 @@
|
||||
},
|
||||
{
|
||||
"name": "league/csv",
|
||||
"version": "8.1.1",
|
||||
"version": "8.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/csv.git",
|
||||
"reference": "3b22a40804aa0bc5224ffb2f5e8248edf0a9a38c"
|
||||
"reference": "33447984f7a7038fefaa5a6177e8407b66bc85b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/csv/zipball/3b22a40804aa0bc5224ffb2f5e8248edf0a9a38c",
|
||||
"reference": "3b22a40804aa0bc5224ffb2f5e8248edf0a9a38c",
|
||||
"url": "https://api.github.com/repos/thephpleague/csv/zipball/33447984f7a7038fefaa5a6177e8407b66bc85b4",
|
||||
"reference": "33447984f7a7038fefaa5a6177e8407b66bc85b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1298,7 +1298,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "8.0-dev"
|
||||
"dev-master": "8.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -1328,7 +1328,7 @@
|
||||
"read",
|
||||
"write"
|
||||
],
|
||||
"time": "2016-09-05 08:16:07"
|
||||
"time": "2016-10-27 11:21:24"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
@ -2170,7 +2170,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/class-loader",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/class-loader.git",
|
||||
@ -2226,16 +2226,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "6cb0872fb57b38b3b09ff213c21ed693956b9eb0"
|
||||
"reference": "c99da1119ae61e15de0e4829196b9fba6f73d065"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/6cb0872fb57b38b3b09ff213c21ed693956b9eb0",
|
||||
"reference": "6cb0872fb57b38b3b09ff213c21ed693956b9eb0",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/c99da1119ae61e15de0e4829196b9fba6f73d065",
|
||||
"reference": "c99da1119ae61e15de0e4829196b9fba6f73d065",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2283,11 +2283,11 @@
|
||||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-09-28 00:11:12"
|
||||
"time": "2016-10-06 01:44:51"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/debug.git",
|
||||
@ -2344,16 +2344,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "c0c00c80b3a69132c4e55c3e7db32b4a387615e5"
|
||||
"reference": "28b0832b2553ffb80cabef6a7a812ff1e670c0bc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c0c00c80b3a69132c4e55c3e7db32b4a387615e5",
|
||||
"reference": "c0c00c80b3a69132c4e55c3e7db32b4a387615e5",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/28b0832b2553ffb80cabef6a7a812ff1e670c0bc",
|
||||
"reference": "28b0832b2553ffb80cabef6a7a812ff1e670c0bc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2400,11 +2400,11 @@
|
||||
],
|
||||
"description": "Symfony EventDispatcher Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-07-19 10:45:57"
|
||||
"time": "2016-10-13 06:28:43"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
@ -2453,16 +2453,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "5114f1becca9f29e3af94374f1689c83c1aa3d97"
|
||||
"reference": "f21e5a8b88274b7720779aa88f9c02c6d6ec08d7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/5114f1becca9f29e3af94374f1689c83c1aa3d97",
|
||||
"reference": "5114f1becca9f29e3af94374f1689c83c1aa3d97",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/f21e5a8b88274b7720779aa88f9c02c6d6ec08d7",
|
||||
"reference": "f21e5a8b88274b7720779aa88f9c02c6d6ec08d7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2502,20 +2502,20 @@
|
||||
],
|
||||
"description": "Symfony HttpFoundation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-09-21 20:55:10"
|
||||
"time": "2016-10-24 15:52:44"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "dc339d6eebadfa6e39c52868b4d4a715eff13c69"
|
||||
"reference": "c235f1b13ba67012e283996a5427f22e2e04be14"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/dc339d6eebadfa6e39c52868b4d4a715eff13c69",
|
||||
"reference": "dc339d6eebadfa6e39c52868b4d4a715eff13c69",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/c235f1b13ba67012e283996a5427f22e2e04be14",
|
||||
"reference": "c235f1b13ba67012e283996a5427f22e2e04be14",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2523,7 +2523,7 @@
|
||||
"psr/log": "~1.0",
|
||||
"symfony/debug": "~2.8|~3.0",
|
||||
"symfony/event-dispatcher": "~2.8|~3.0",
|
||||
"symfony/http-foundation": "~2.8.8|~3.0.8|~3.1.2|~3.2"
|
||||
"symfony/http-foundation": "~2.8.13|~3.1.6|~3.2"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/config": "<2.8"
|
||||
@ -2584,7 +2584,7 @@
|
||||
],
|
||||
"description": "Symfony HttpKernel Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-10-03 19:01:06"
|
||||
"time": "2016-10-27 02:38:31"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
@ -2755,7 +2755,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
@ -2804,7 +2804,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/routing.git",
|
||||
@ -2879,16 +2879,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "93013a18d272e59dab8e67f583155b78c68947eb"
|
||||
"reference": "ff1285087397d2f64041b35e591f3025881c90cd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/93013a18d272e59dab8e67f583155b78c68947eb",
|
||||
"reference": "93013a18d272e59dab8e67f583155b78c68947eb",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/ff1285087397d2f64041b35e591f3025881c90cd",
|
||||
"reference": "ff1285087397d2f64041b35e591f3025881c90cd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2939,20 +2939,20 @@
|
||||
],
|
||||
"description": "Symfony Translation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-09-06 11:02:40"
|
||||
"time": "2016-10-18 04:30:12"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "70bfe927b86ba9999aeebd829715b0bb2cd39a10"
|
||||
"reference": "4dc2f03b480c43f1665d3317d827a04ed6ffd11e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/70bfe927b86ba9999aeebd829715b0bb2cd39a10",
|
||||
"reference": "70bfe927b86ba9999aeebd829715b0bb2cd39a10",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/4dc2f03b480c43f1665d3317d827a04ed6ffd11e",
|
||||
"reference": "4dc2f03b480c43f1665d3317d827a04ed6ffd11e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3002,20 +3002,20 @@
|
||||
"debug",
|
||||
"dump"
|
||||
],
|
||||
"time": "2016-09-29 14:13:09"
|
||||
"time": "2016-10-18 15:46:07"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v1.26.1",
|
||||
"version": "v1.27.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "a09d8ee17ac1cfea29ed60c83960ad685c6a898d"
|
||||
"reference": "3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a09d8ee17ac1cfea29ed60c83960ad685c6a898d",
|
||||
"reference": "a09d8ee17ac1cfea29ed60c83960ad685c6a898d",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97",
|
||||
"reference": "3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3028,7 +3028,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.26-dev"
|
||||
"dev-master": "1.27-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -3063,7 +3063,7 @@
|
||||
"keywords": [
|
||||
"templating"
|
||||
],
|
||||
"time": "2016-10-05 18:57:41"
|
||||
"time": "2016-10-25 19:17:17"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
@ -3880,16 +3880,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "5.6.1",
|
||||
"version": "5.6.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "60c32c5b5e79c2248001efa2560f831da11cc2d7"
|
||||
"reference": "cd13b23ac5a519a4708e00736c26ee0bb28b2e01"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/60c32c5b5e79c2248001efa2560f831da11cc2d7",
|
||||
"reference": "60c32c5b5e79c2248001efa2560f831da11cc2d7",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cd13b23ac5a519a4708e00736c26ee0bb28b2e01",
|
||||
"reference": "cd13b23ac5a519a4708e00736c26ee0bb28b2e01",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3958,7 +3958,7 @@
|
||||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2016-10-07 13:03:26"
|
||||
"time": "2016-10-25 07:40:25"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit-mock-objects",
|
||||
@ -4534,7 +4534,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
@ -4587,16 +4587,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/dom-crawler",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dom-crawler.git",
|
||||
"reference": "bb7395e8b1db3654de82b9f35d019958276de4d7"
|
||||
"reference": "59eee3c76eb89f21857798620ebdad7a05ad14f4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/bb7395e8b1db3654de82b9f35d019958276de4d7",
|
||||
"reference": "bb7395e8b1db3654de82b9f35d019958276de4d7",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/59eee3c76eb89f21857798620ebdad7a05ad14f4",
|
||||
"reference": "59eee3c76eb89f21857798620ebdad7a05ad14f4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -4639,20 +4639,20 @@
|
||||
],
|
||||
"description": "Symfony DomCrawler Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-08-05 08:37:39"
|
||||
"time": "2016-10-18 15:46:07"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v3.1.5",
|
||||
"version": "v3.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "368b9738d4033c8b93454cb0dbd45d305135a6d3"
|
||||
"reference": "7ff51b06c6c3d5cc6686df69004a42c69df09e27"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/368b9738d4033c8b93454cb0dbd45d305135a6d3",
|
||||
"reference": "368b9738d4033c8b93454cb0dbd45d305135a6d3",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/7ff51b06c6c3d5cc6686df69004a42c69df09e27",
|
||||
"reference": "7ff51b06c6c3d5cc6686df69004a42c69df09e27",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -4688,7 +4688,7 @@
|
||||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-09-25 08:27:07"
|
||||
"time": "2016-10-24 18:41:13"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
|
@ -22,12 +22,13 @@ return [
|
||||
'single_user_mode' => true,
|
||||
],
|
||||
'chart' => 'chartjs',
|
||||
'version' => '4.1.1',
|
||||
'version' => '4.1.4',
|
||||
'csv_import_enabled' => true,
|
||||
'maxUploadSize' => 5242880,
|
||||
'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'],
|
||||
'resend_confirmation' => 3600,
|
||||
'confirmation_age' => 14400, // four hours
|
||||
'list_length' => 10,
|
||||
|
||||
'export_formats' => [
|
||||
'csv' => 'FireflyIII\Export\Exporter\CsvExporter',
|
||||
@ -136,6 +137,7 @@ return [
|
||||
'bill' => 'FireflyIII\Models\Bill',
|
||||
'budget' => 'FireflyIII\Models\Budget',
|
||||
'category' => 'FireflyIII\Models\Category',
|
||||
'transaction_type' => 'FireflyIII\Models\TransactionType',
|
||||
'currency' => 'FireflyIII\Models\TransactionCurrency',
|
||||
'limitrepetition' => 'FireflyIII\Models\LimitRepetition',
|
||||
'piggyBank' => 'FireflyIII\Models\PiggyBank',
|
||||
@ -183,6 +185,9 @@ return [
|
||||
'set_description' => 'FireflyIII\Rules\Actions\SetDescription',
|
||||
'append_description' => 'FireflyIII\Rules\Actions\AppendDescription',
|
||||
'prepend_description' => 'FireflyIII\Rules\Actions\PrependDescription',
|
||||
|
||||
'set_source_account' => 'FireflyIII\Rules\Actions\SetSourceAccount',
|
||||
'set_destination_account' => 'FireflyIII\Rules\Actions\SetDestinationAccount',
|
||||
],
|
||||
'rule-actions-text' => [
|
||||
'set_category',
|
||||
|
4
crowdin.yaml
Normal file
4
crowdin.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
files:
|
||||
-
|
||||
source: /resources/lang/en_US/*.php
|
||||
translation: /resources/lang/%locale_with_underscore%/%original_file_name%
|
24
docker/apache-firefly.conf
Normal file
24
docker/apache-firefly.conf
Normal file
@ -0,0 +1,24 @@
|
||||
<VirtualHost *:80>
|
||||
|
||||
ServerAdmin webmaster@localhost
|
||||
DocumentRoot /var/www/firefly-iii/public
|
||||
|
||||
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
||||
# error, crit, alert, emerg.
|
||||
# It is also possible to configure the loglevel for particular
|
||||
# modules, e.g.
|
||||
#LogLevel info ssl:warn
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
<Directory /var/www/firefly-iii/public>
|
||||
Options -Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Order allow,deny
|
||||
allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
|
||||
|
@ -16,12 +16,12 @@ function showHelp(e) {
|
||||
$('#helpTitle').html('Please hold...');
|
||||
|
||||
$('#helpModal').modal('show');
|
||||
$('#helpTitle').html('Help for this page');
|
||||
$.getJSON('help/' + encodeURI(route)).done(function (data) {
|
||||
$('#helpBody').html(data.text);
|
||||
$('#helpTitle').html(data.title);
|
||||
$('#helpBody').html(data);
|
||||
}).fail(function () {
|
||||
$('#helpBody').html('<p class="text-danger">No help text could be found.</p>');
|
||||
$('#helpTitle').html('Sorry...');
|
||||
$('#helpTitle').html('Apologies');
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
@ -8,11 +8,6 @@
|
||||
|
||||
/* globals hideable */
|
||||
|
||||
|
||||
/**
|
||||
* Created by sander on 01/04/16.
|
||||
*/
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* globals startDate, endDate, reportType, accountIds */
|
||||
/* globals startDate, showOnlyTop, showFullList, endDate, reportType, accountIds, inOutReportUrl, accountReportUrl */
|
||||
/*
|
||||
* all.js
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
@ -7,21 +7,80 @@
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Created by sander on 01/04/16.
|
||||
*/
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
|
||||
// find the little info buttons and respond to them.
|
||||
$('.firefly-info-button').click(clickInfoButton);
|
||||
|
||||
|
||||
// load the account report, which this report shows:
|
||||
loadAccountReport();
|
||||
|
||||
// load income / expense / difference:
|
||||
loadInOutReport();
|
||||
|
||||
// trigger info click
|
||||
triggerInfoClick();
|
||||
|
||||
// trigger list length things:
|
||||
listLengthInitial();
|
||||
|
||||
});
|
||||
|
||||
function triggerInfoClick() {
|
||||
"use strict";
|
||||
// find the little info buttons and respond to them.
|
||||
$('.firefly-info-button').unbind('clicl').click(clickInfoButton);
|
||||
}
|
||||
|
||||
function listLengthInitial() {
|
||||
"use strict";
|
||||
$('.overListLength').hide();
|
||||
$('.listLengthTrigger').unbind('click').click(triggerList)
|
||||
}
|
||||
|
||||
function triggerList(e) {
|
||||
"use strict";
|
||||
var link = $(e.target);
|
||||
var table = link.parent().parent().parent().parent();
|
||||
console.log('data-hidden = ' + table.attr('data-hidden'));
|
||||
if (table.attr('data-hidden') === 'no') {
|
||||
// hide all elements, return false.
|
||||
table.find('.overListLength').hide();
|
||||
table.attr('data-hidden', 'yes');
|
||||
link.text(showFullList);
|
||||
return false;
|
||||
}
|
||||
// show all, return false
|
||||
table.find('.overListLength').show();
|
||||
table.attr('data-hidden', 'no');
|
||||
link.text(showOnlyTop);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function loadInOutReport() {
|
||||
"use strict";
|
||||
console.log('Going to grab ' + inOutReportUrl);
|
||||
$.get(inOutReportUrl).done(placeInOutReport).fail(failInOutReport);
|
||||
}
|
||||
|
||||
function placeInOutReport(data) {
|
||||
"use strict";
|
||||
$('#incomeReport').removeClass('loading').html(data.income);
|
||||
$('#expenseReport').removeClass('loading').html(data.expenses);
|
||||
$('#incomeVsExpenseReport').removeClass('loading').html(data.incomes_expenses);
|
||||
listLengthInitial();
|
||||
triggerInfoClick();
|
||||
}
|
||||
|
||||
function failInOutReport() {
|
||||
"use strict";
|
||||
console.log('Fail in/out report data!');
|
||||
$('#incomeReport').removeClass('loading').addClass('general-chart-error');
|
||||
$('#expenseReport').removeClass('loading').addClass('general-chart-error');
|
||||
$('#incomeVsExpenseReport').removeClass('loading').addClass('general-chart-error');
|
||||
}
|
||||
|
||||
function loadAccountReport() {
|
||||
"use strict";
|
||||
$.get(accountReportUrl).done(placeAccountReport).fail(failAccountReport);
|
||||
|
@ -1,16 +1,52 @@
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds, lineChart, categoryReportUrl, balanceReportUrl */
|
||||
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
drawChart();
|
||||
|
||||
// click open the top X income list:
|
||||
$('#showIncomes').click(showIncomes);
|
||||
// click open the top X expense list:
|
||||
$('#showExpenses').click(showExpenses);
|
||||
loadCategoryReport();
|
||||
loadBalanceReport();
|
||||
});
|
||||
|
||||
function loadCategoryReport() {
|
||||
"use strict";
|
||||
console.log('Going to grab ' + categoryReportUrl);
|
||||
$.get(categoryReportUrl).done(placeCategoryReport).fail(failCategoryReport);
|
||||
}
|
||||
|
||||
function loadBalanceReport() {
|
||||
"use strict";
|
||||
console.log('Going to grab ' + categoryReportUrl);
|
||||
$.get(balanceReportUrl).done(placeBalanceReport).fail(failBalanceReport);
|
||||
}
|
||||
|
||||
function placeBalanceReport(data) {
|
||||
"use strict";
|
||||
$('#balanceReport').removeClass('loading').html(data);
|
||||
listLengthInitial();
|
||||
triggerInfoClick();
|
||||
}
|
||||
|
||||
function placeCategoryReport(data) {
|
||||
"use strict";
|
||||
$('#categoryReport').removeClass('loading').html(data);
|
||||
listLengthInitial();
|
||||
triggerInfoClick();
|
||||
}
|
||||
|
||||
function failBalanceReport() {
|
||||
"use strict";
|
||||
console.log('Fail balance report data!');
|
||||
$('#balanceReport').removeClass('loading').addClass('general-chart-error');
|
||||
}
|
||||
|
||||
function failCategoryReport() {
|
||||
"use strict";
|
||||
console.log('Fail category report data!');
|
||||
$('#categoryReport').removeClass('loading').addClass('general-chart-error');
|
||||
}
|
||||
|
||||
|
||||
function drawChart() {
|
||||
"use strict";
|
||||
@ -19,46 +55,3 @@ function drawChart() {
|
||||
// draw account chart
|
||||
lineChart('chart/account/report/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'account-balances-chart');
|
||||
}
|
||||
|
||||
|
||||
function showIncomes() {
|
||||
"use strict";
|
||||
if (incomeRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showIncomes').text(showTheRest);
|
||||
$('.incomesCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showIncomes').text(hideTheRest);
|
||||
$('.incomesCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function showExpenses() {
|
||||
"use strict";
|
||||
if (expenseRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showExpenses').text(showTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showExpenses').text(hideTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -1,15 +1,10 @@
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, year, month, columnChart, lineChart, stackedColumnChart */
|
||||
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
drawChart();
|
||||
|
||||
// click open the top X income list:
|
||||
$('#showIncomes').click(showIncomes);
|
||||
// click open the top X expense list:
|
||||
$('#showExpenses').click(showExpenses);
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -159,47 +154,3 @@ function readCookie(name) {
|
||||
function eraseCookie(name) {
|
||||
createCookie(name, "", -1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function showIncomes() {
|
||||
"use strict";
|
||||
if (incomeRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showIncomes').text(showTheRest);
|
||||
$('.incomesCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showIncomes').text(hideTheRest);
|
||||
$('.incomesCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function showExpenses() {
|
||||
"use strict";
|
||||
if (expenseRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showExpenses').text(showTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showExpenses').text(hideTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, year, month, columnChart, lineChart, stackedColumnChart */
|
||||
|
||||
var chartDrawn;
|
||||
var budgetChart;
|
||||
@ -7,10 +7,6 @@ $(function () {
|
||||
chartDrawn = false;
|
||||
drawChart();
|
||||
|
||||
// click open the top X income list:
|
||||
$('#showIncomes').click(showIncomes);
|
||||
// click open the top X expense list:
|
||||
$('#showExpenses').click(showExpenses);
|
||||
});
|
||||
|
||||
|
||||
@ -86,52 +82,5 @@ function clickBudgetChart(e) {
|
||||
|
||||
}
|
||||
|
||||
// if chart drawn is true, add new data to existing chart.
|
||||
// console.log('Budget id is ' + budgetId);
|
||||
// $('#budget_chart').empty();
|
||||
// columnChart('chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budget_chart');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function showIncomes() {
|
||||
"use strict";
|
||||
if (incomeRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showIncomes').text(showTheRest);
|
||||
$('.incomesCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showIncomes').text(hideTheRest);
|
||||
$('.incomesCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function showExpenses() {
|
||||
"use strict";
|
||||
if (expenseRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showExpenses').text(showTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showExpenses').text(hideTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, year, month, columnChart, lineChart, stackedColumnChart */
|
||||
|
||||
|
||||
$(function () {
|
||||
|
@ -58,11 +58,11 @@ function sortStop(event, ui) {
|
||||
|
||||
});
|
||||
if (parent.hasClass('rule-triggers')) {
|
||||
$.post('rules/rules/trigger/reorder/' + ruleId, {_token: token, triggers: entries}).fail(function () {
|
||||
$.post('rules/trigger/order/' + ruleId, {_token: token, triggers: entries}).fail(function () {
|
||||
alert('Could not re-order rule triggers. Please refresh the page.');
|
||||
});
|
||||
} else {
|
||||
$.post('rules/rules/action/reorder/' + ruleId, {_token: token, actions: entries}).fail(function () {
|
||||
$.post('rules/action/order/' + ruleId, {_token: token, actions: entries}).fail(function () {
|
||||
alert('Could not re-order rule actions. Please refresh the page.');
|
||||
});
|
||||
|
||||
|
@ -136,13 +136,13 @@ function resetSplits() {
|
||||
// ends with ][description]
|
||||
$.each($('input[name$="][description]"]'), function (i, v) {
|
||||
var input = $(v);
|
||||
input.attr('name', 'transaction[' + i + '][description]');
|
||||
input.attr('name', 'transactions[' + i + '][description]');
|
||||
console.log('description is now ' + input.attr('name'));
|
||||
});
|
||||
// ends with ][destination_account_name]
|
||||
$.each($('input[name$="][destination_account_name]"]'), function (i, v) {
|
||||
var input = $(v);
|
||||
input.attr('name', 'transaction[' + i + '][destination_account_name]');
|
||||
input.attr('name', 'transactions[' + i + '][destination_account_name]');
|
||||
console.log('destination_account_name is now ' + input.attr('name'));
|
||||
});
|
||||
// ends with ][source_account_name]
|
||||
@ -154,19 +154,19 @@ function resetSplits() {
|
||||
// ends with ][amount]
|
||||
$.each($('input[name$="][amount]"]'), function (i, v) {
|
||||
var input = $(v);
|
||||
input.attr('name', 'transaction[' + i + '][amount]');
|
||||
input.attr('name', 'transactions[' + i + '][amount]');
|
||||
console.log('amount is now ' + input.attr('name'));
|
||||
});
|
||||
// ends with ][budget_id]
|
||||
$.each($('input[name$="][budget_id]"]'), function (i, v) {
|
||||
var input = $(v);
|
||||
input.attr('name', 'transaction[' + i + '][budget_id]');
|
||||
input.attr('name', 'transactions[' + i + '][budget_id]');
|
||||
console.log('budget_id is now ' + input.attr('name'));
|
||||
});
|
||||
// ends with ][category]
|
||||
$.each($('input[name$="][category]"]'), function (i, v) {
|
||||
var input = $(v);
|
||||
input.attr('name', 'transaction[' + i + '][category]');
|
||||
input.attr('name', 'transactions[' + i + '][category]');
|
||||
console.log('category is now ' + input.attr('name'));
|
||||
});
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => 'These credentials do not match our records.',
|
||||
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
|
||||
'failed' => 'Falscher Benutzername und/oder falsches Passwort.',
|
||||
'throttle' => 'Zu viele Login-Versuche. Bitte versuchen Sie es in :seconds Sekunde(n) erneut.',
|
||||
|
||||
];
|
@ -73,6 +73,7 @@ return [
|
||||
'field_supports_markdown' => 'This field supports <a href="https://en.support.wordpress.com/markdown-quick-reference/">Markdown</a>.',
|
||||
|
||||
// repeat frequencies:
|
||||
'repeat_freq_yearly' => 'yearly',
|
||||
'repeat_freq_monthly' => 'monatlich',
|
||||
'weekly' => 'wöchentlich',
|
||||
'quarterly' => 'vierteljährlich',
|
||||
@ -99,7 +100,6 @@ return [
|
||||
'export_format_csv' => 'Durch Komma getrennte Werte (CSV-Datei)',
|
||||
'export_format_mt940' => 'MT940 compatible format',
|
||||
'export_included_accounts' => 'Exportiere die Überweisungen von diesem Konto',
|
||||
'include_config_help' => 'Zum einfachen re-importieren in Firefly III',
|
||||
'include_old_uploads_help' => 'Firefly III löscht nicht die originalen CSV-Dateien, welche zuvor importiert wurden. Sie können dem Export hinzugefügt werden.',
|
||||
'do_export' => 'Export',
|
||||
'export_status_never_started' => 'Der Export hat noch nicht begonnen',
|
||||
@ -238,6 +238,10 @@ return [
|
||||
'rule_action_set_description_choice' => 'Set description to..',
|
||||
'rule_action_append_description_choice' => 'Append description with..',
|
||||
'rule_action_prepend_description_choice' => 'Prepend description with..',
|
||||
'rule_action_set_source_account_choice' => 'Set source account to...',
|
||||
'rule_action_set_source_account' => 'Set source account to :action_value',
|
||||
'rule_action_set_destination_account_choice' => 'Set destination account to...',
|
||||
'rule_action_set_destination_account' => 'Set destination account to :action_value',
|
||||
|
||||
// tags
|
||||
'store_new_tag' => 'Neuen Tag speichern',
|
||||
@ -349,6 +353,41 @@ return [
|
||||
'title_transfer' => 'Überweisungen',
|
||||
'title_transfers' => 'Überweisungen',
|
||||
|
||||
// convert stuff:
|
||||
'convert_is_already_type_Withdrawal' => 'This transaction is already a withdrawal',
|
||||
'convert_is_already_type_Deposit' => 'This transaction is already a deposit',
|
||||
'convert_is_already_type_Transfer' => 'This transaction is already a transfer',
|
||||
'convert_to_Withdrawal' => 'Convert ":description" to a withdrawal',
|
||||
'convert_to_Deposit' => 'Convert ":description" to a deposit',
|
||||
'convert_to_Transfer' => 'Convert ":description" to a transfer',
|
||||
'convert_options_WithdrawalDeposit' => 'Convert a withdrawal into a deposit',
|
||||
'convert_options_WithdrawalTransfer' => 'Convert a withdrawal into a transfer',
|
||||
'convert_options_DepositTransfer' => 'Convert a deposit into a transfer',
|
||||
'convert_options_DepositWithdrawal' => 'Convert a deposit into a withdrawal',
|
||||
'convert_options_TransferWithdrawal' => 'Convert a transfer into a withdrawal',
|
||||
'convert_options_TransferDeposit' => 'Convert a transfer into a deposit',
|
||||
'transaction_journal_convert_options' => 'Convert this transaction',
|
||||
'convert_Withdrawal_to_deposit' => 'Convert this withdrawal to a deposit',
|
||||
'convert_Withdrawal_to_transfer' => 'Convert this withdrawal to a transfer',
|
||||
'convert_Deposit_to_withdrawal' => 'Convert this deposit to a withdrawal',
|
||||
'convert_Deposit_to_transfer' => 'Convert this deposit to a transfer',
|
||||
'convert_Transfer_to_deposit' => 'Convert this transfer to a deposit',
|
||||
'convert_Transfer_to_withdrawal' => 'Convert this transfer to a withdrawal',
|
||||
'convert_please_set_revenue_source' => 'Please pick the revenue account where the money will come from.',
|
||||
'convert_please_set_asset_destination' => 'Please pick the asset account where the money will go to.',
|
||||
'convert_please_set_expense_destination' => 'Please pick the expense account where the money will go to.',
|
||||
'convert_please_set_asset_source' => 'Please pick the asset account where the money will come from.',
|
||||
'convert_explanation_withdrawal_deposit' => 'If you convert this withdrawal into a deposit, :amount will be deposited into <a href=":sourceRoute">:sourceName</a> instead of taken from it.',
|
||||
'convert_explanation_withdrawal_transfer' => 'If you convert this withdrawal into a transfer, :amount will be transferred from <a href=":sourceRoute">:sourceName</a> to a new asset account, instead of being paid to <a href=":destinationRoute">:destinationName</a>.',
|
||||
'convert_explanation_deposit_withdrawal' => 'If you convert this deposit into a withdrawal, :amount will be removed from <a href=":destinationRoute">:destinationName</a> instead of added to it.',
|
||||
'convert_explanation_deposit_transfer' => 'If you convert this deposit into a transfer, :amount will be transferred from an asset account of your choice into <a href=":destinationRoute">:destinationName</a>.',
|
||||
'convert_explanation_transfer_withdrawal' => 'If you convert this transfer into a withdrawal, :amount will go from <a href=":sourceRoute">:sourceName</a> to a new destination as an expense, instead of to <a href=":destinationRoute">:destinationName</a> as a transfer.',
|
||||
'convert_explanation_transfer_deposit' => 'If you convert this transfer into a deposit, :amount will be deposited into account <a href=":destinationRoute">:destinationName</a> instead of being transferred there.',
|
||||
'converted_to_Withdrawal' => 'The transaction has been converted to a withdrawal',
|
||||
'converted_to_Deposit' => 'The transaction has been converted to a deposit',
|
||||
'converted_to_Transfer' => 'The transaction has been converted to a transfer',
|
||||
|
||||
|
||||
// create new stuff:
|
||||
'create_new_withdrawal' => 'Erstelle eine neue Ausgabe',
|
||||
'create_new_deposit' => 'Erstelle ein neues Einkommen',
|
||||
@ -426,10 +465,10 @@ return [
|
||||
'stored_new_bill' => 'Neue Rechung ":name" gespeichert',
|
||||
'cannot_scan_inactive_bill' => 'Inactive bills cannot be scanned.',
|
||||
'rescanned_bill' => 'Rescanned everything.',
|
||||
'bill_date_little_relevance' => 'Der einzige Teil dieses Datum, der von Firefly verwendet wird, ist der Tag. Er ist nur sinnvoll, wenn ihre Rechnung immer am selben Tag im Monat anfallen. Wenn der Zahlungstag der Rechnung variiert geben sie einfach den Ersten des Monats an.',
|
||||
'average_bill_amount_year' => 'Durchschnittliche Rechnungssumme (:year)',
|
||||
'average_bill_amount_overall' => 'Durchschnittliche Rechnungssumme (gesamt)',
|
||||
|
||||
'not_or_not_yet' => 'Not (yet)',
|
||||
'not_expected_period' => 'Not expected this period',
|
||||
// accounts:
|
||||
'details_for_asset' => 'Details für Girokonto ":name"',
|
||||
'details_for_expense' => 'Details for expense account ":name"',
|
||||
@ -604,8 +643,8 @@ return [
|
||||
'in' => 'Rein',
|
||||
'out' => 'Raus',
|
||||
'topX' => 'top :number',
|
||||
'showTheRest' => 'Alles anzeigen',
|
||||
'hideTheRest' => 'Nur die Top :number anzeigen',
|
||||
'show_full_list' => 'Show entire list',
|
||||
'show_only_top' => 'Show only top :number',
|
||||
'sum_of_year' => 'Summe des Jahres',
|
||||
'sum_of_years' => 'Summe der Jahre',
|
||||
'average_of_year' => 'Durchschnitt des Jahres',
|
||||
@ -619,7 +658,6 @@ return [
|
||||
'more_info_help' => 'Weitere Informationen über diese Art von Berichten finden Sie in der Hilfe. Drücken Sie hierfür das (?)-Symbol in der oberen rechten Ecke.',
|
||||
'report_included_accounts' => 'Eingezogene Konten',
|
||||
'report_date_range' => 'Zeitraum',
|
||||
'report_include_help' => 'In allen Fällen gilt: Überweisungen zu gemeinsamen Konten gelten als Ausgaben und Überweisungen von gemeinsamen Konten gelten als Einnahmen.',
|
||||
'report_preset_ranges' => 'Pre-set ranges',
|
||||
'shared' => 'Shared',
|
||||
'fiscal_year' => 'Geschäftsjahr',
|
||||
@ -630,6 +668,9 @@ return [
|
||||
'balance_amount' => 'Expenses in budget ":budget" paid from account ":account" between :start and :end',
|
||||
'no_audit_activity' => 'No activity was recorded on account <a href=":url" title=":account_name">:account_name</a> between :start and :end.',
|
||||
'audit_end_balance' => 'Account balance of <a href=":url" title=":account_name">:account_name</a> at the end of :end was: :balance',
|
||||
'reports_extra_options' => 'Extra options',
|
||||
'report_has_no_extra_options' => 'This report has no extra options',
|
||||
'reports_submit' => 'View report',
|
||||
|
||||
// charts:
|
||||
'chart' => 'Chart',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user