mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-19 02:52:44 +00:00
Merge branch 'release/4.5.0'
This commit is contained in:
13
CHANGELOG.md
13
CHANGELOG.md
@@ -2,6 +2,19 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [4.5.0] - 2017-07-07
|
||||
|
||||
### Added
|
||||
- Better support for multi-currency transactions and display of transactions, accounts and everything. This requires a database overhaul (moving the currency information to specific transactions) so be careful when upgrading.
|
||||
- Translations for Spanish and Slovenian.
|
||||
- New interface for budget page, ~~stolen from~~ inspired by YNAB.
|
||||
- Expanded Docker to work with postgresql as well, thanks to @kressh
|
||||
|
||||
### Fixed
|
||||
- PostgreSQL support in database upgrade routine (#644, reported by @)
|
||||
- Frontpage budget chart was off, fix by @nhaarman
|
||||
- Was not possible to remove opening balance.
|
||||
|
||||
## [4.4.3] - 2017-05-03
|
||||
### Added
|
||||
- Added support for Slovenian
|
||||
|
@@ -11,13 +11,14 @@ RUN apt-get update -y && \
|
||||
libtidy-dev \
|
||||
libxml2-dev \
|
||||
libsqlite3-dev \
|
||||
libpq-dev \
|
||||
libbz2-dev \
|
||||
gettext-base \
|
||||
locales && \
|
||||
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
|
||||
RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2 pdo_pgsql
|
||||
|
||||
# Generate locales supported by firefly
|
||||
RUN echo "en_US.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
|
||||
|
@@ -32,6 +32,7 @@ use Illuminate\Database\QueryException;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Schema;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
* Class UpgradeDatabase
|
||||
@@ -72,9 +73,54 @@ class UpgradeDatabase extends Command
|
||||
$this->repairPiggyBanks();
|
||||
$this->updateAccountCurrencies();
|
||||
$this->updateJournalCurrencies();
|
||||
$this->currencyInfoToTransactions();
|
||||
$this->verifyCurrencyInfo();
|
||||
$this->info('Firefly III database is up to date.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the currency id info to the transaction instead of the journal.
|
||||
*/
|
||||
private function currencyInfoToTransactions()
|
||||
{
|
||||
$count = 0;
|
||||
$set = TransactionJournal::with('transactions')->get();
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions as $transaction) {
|
||||
if (is_null($transaction->transaction_currency_id)) {
|
||||
$transaction->transaction_currency_id = $journal->transaction_currency_id;
|
||||
$transaction->save();
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// read and use the foreign amounts when present.
|
||||
if ($journal->hasMeta('foreign_amount')) {
|
||||
$amount = Steam::positive($journal->getMeta('foreign_amount'));
|
||||
|
||||
// update both transactions:
|
||||
foreach ($journal->transactions as $transaction) {
|
||||
$transaction->foreign_amount = $amount;
|
||||
if (bccomp($transaction->amount, '0') === -1) {
|
||||
// update with negative amount:
|
||||
$transaction->foreign_amount = bcmul($amount, '-1');
|
||||
}
|
||||
// set foreign currency id:
|
||||
$transaction->foreign_currency_id = intval($journal->getMeta('foreign_currency_id'));
|
||||
$transaction->save();
|
||||
}
|
||||
$journal->deleteMeta('foreign_amount');
|
||||
$journal->deleteMeta('foreign_currency_id');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->line(sprintf('Updated currency information for %d transactions', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate budget repetitions to new format.
|
||||
*/
|
||||
@@ -269,9 +315,10 @@ class UpgradeDatabase extends Command
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$notification = '%s #%d uses %s but should use %s. It has been updated. Please verify this in Firefly III.';
|
||||
$transfer = 'Transfer #%d has been updated to use the correct currencies. Please verify this in Firefly III.';
|
||||
$driver = DB::connection()->getDriverName();
|
||||
|
||||
foreach ($types as $type => $operator) {
|
||||
$set = TransactionJournal
|
||||
$query = TransactionJournal
|
||||
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')->leftJoin(
|
||||
'transactions', function (JoinClause $join) use ($operator) {
|
||||
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', $operator, '0');
|
||||
@@ -280,9 +327,15 @@ class UpgradeDatabase extends Command
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
|
||||
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
|
||||
->where('transaction_types.type', $type)
|
||||
->where('account_meta.name', 'currency_id')
|
||||
->where('transaction_journals.transaction_currency_id', '!=', DB::raw('account_meta.data'))
|
||||
->get(['transaction_journals.*', 'account_meta.data as expected_currency_id', 'transactions.amount as transaction_amount']);
|
||||
->where('account_meta.name', 'currency_id');
|
||||
if ($driver === 'postgresql') {
|
||||
$query->where('transaction_journals.transaction_currency_id', '!=', DB::raw('cast(account_meta.data as int)'));
|
||||
}
|
||||
if ($driver !== 'postgresql') {
|
||||
$query->where('transaction_journals.transaction_currency_id', '!=', DB::raw('account_meta.data'));
|
||||
}
|
||||
|
||||
$set = $query->get(['transaction_journals.*', 'account_meta.data as expected_currency_id', 'transactions.amount as transaction_amount']);
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$expectedCurrency = $repository->find(intval($journal->expected_currency_id));
|
||||
@@ -334,4 +387,25 @@ class UpgradeDatabase extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function verifyCurrencyInfo()
|
||||
{
|
||||
$count = 0;
|
||||
$transactions = Transaction::get();
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$currencyId = intval($transaction->transaction_currency_id);
|
||||
$foreignId = intval($transaction->foreign_currency_id);
|
||||
if ($currencyId === $foreignId) {
|
||||
$transaction->foreign_currency_id = null;
|
||||
$transaction->foreign_amount = null;
|
||||
$transaction->save();
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
$this->line(sprintf('Updated currency information for %d transactions', $count));
|
||||
}
|
||||
}
|
||||
|
@@ -26,9 +26,9 @@ class StoredTransactionJournal extends Event
|
||||
|
||||
use SerializesModels;
|
||||
|
||||
/** @var TransactionJournal */
|
||||
/** @var TransactionJournal */
|
||||
public $journal;
|
||||
/** @var int */
|
||||
/** @var int */
|
||||
public $piggyBankId;
|
||||
|
||||
/**
|
||||
|
@@ -26,7 +26,7 @@ class UpdatedTransactionJournal extends Event
|
||||
|
||||
use SerializesModels;
|
||||
|
||||
/** @var TransactionJournal */
|
||||
/** @var TransactionJournal */
|
||||
public $journal;
|
||||
|
||||
/**
|
||||
|
@@ -303,7 +303,7 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac
|
||||
->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')
|
||||
->leftJoin('transaction_currencies', 'transactions.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'))
|
||||
@@ -338,7 +338,7 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac
|
||||
'transaction_journals.encrypted as journal_encrypted',
|
||||
'transaction_journals.transaction_type_id',
|
||||
'transaction_types.type as transaction_type',
|
||||
'transaction_journals.transaction_currency_id',
|
||||
'transactions.transaction_currency_id',
|
||||
'transaction_currencies.code AS transaction_currency_code',
|
||||
|
||||
]
|
||||
|
@@ -112,7 +112,7 @@ class ChartJsGenerator implements GeneratorInterface
|
||||
// sort by value, keep keys.
|
||||
asort($data);
|
||||
|
||||
$index = 0;
|
||||
$index = 0;
|
||||
foreach ($data as $key => $value) {
|
||||
|
||||
// make larger than 0
|
||||
|
@@ -22,10 +22,15 @@ interface GeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Will generate a (ChartJS) compatible array from the given input. Expects this format:
|
||||
* Will generate a Chart JS compatible array from the given input. Expects this format
|
||||
*
|
||||
* Will take labels for all from first set.
|
||||
*
|
||||
* 0: [
|
||||
* 'label' => 'label of set',
|
||||
* 'type' => bar or line, optional
|
||||
* 'yAxisID' => ID of yAxis, optional, will not be included when unused.
|
||||
* 'fill' => if to fill a line? optional, will not be included when unused.
|
||||
* 'entries' =>
|
||||
* [
|
||||
* 'label-of-entry' => 'value'
|
||||
@@ -33,12 +38,16 @@ interface GeneratorInterface
|
||||
* ]
|
||||
* 1: [
|
||||
* 'label' => 'label of another set',
|
||||
* 'type' => bar or line, optional
|
||||
* 'yAxisID' => ID of yAxis, optional, will not be included when unused.
|
||||
* 'fill' => if to fill a line? optional, will not be included when unused.
|
||||
* 'entries' =>
|
||||
* [
|
||||
* 'label-of-entry' => 'value'
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
|
@@ -19,6 +19,7 @@ use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Steam;
|
||||
|
||||
@@ -147,6 +148,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
*/
|
||||
private function getAuditReport(Account $account, Carbon $date): array
|
||||
{
|
||||
/** @var CurrencyRepositoryInterface $currencyRepos */
|
||||
$currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
@@ -155,15 +158,21 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
$journals = $journals->reverse();
|
||||
$dayBeforeBalance = Steam::balance($account, $date);
|
||||
$startBalance = $dayBeforeBalance;
|
||||
|
||||
$currency = $currencyRepos->find(intval($account->getMeta('currency_id')));
|
||||
|
||||
/** @var Transaction $journal */
|
||||
foreach ($journals as $transaction) {
|
||||
$transaction->before = $startBalance;
|
||||
$transactionAmount = $transaction->transaction_amount;
|
||||
$newBalance = bcadd($startBalance, $transactionAmount);
|
||||
$transaction->after = $newBalance;
|
||||
$startBalance = $newBalance;
|
||||
$transactionAmount = $transaction->transaction_amount;
|
||||
|
||||
if ($currency->id === $transaction->foreign_currency_id) {
|
||||
$transactionAmount = $transaction->transaction_foreign_amount;
|
||||
}
|
||||
|
||||
$newBalance = bcadd($startBalance, $transactionAmount);
|
||||
$transaction->after = $newBalance;
|
||||
$startBalance = $newBalance;
|
||||
$transaction->currency = $currency;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -15,7 +15,6 @@ namespace FireflyIII\Generator\Report;
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
|
||||
/**
|
||||
|
@@ -42,6 +42,10 @@ class StoredJournalEventHandler
|
||||
|
||||
/**
|
||||
* StoredJournalEventHandler constructor.
|
||||
*
|
||||
* @param PRI $repository
|
||||
* @param JRI $journalRepository
|
||||
* @param RGRI $ruleGroupRepository
|
||||
*/
|
||||
public function __construct(PRI $repository, JRI $journalRepository, RGRI $ruleGroupRepository)
|
||||
{
|
||||
|
@@ -23,7 +23,7 @@ use FireflyIII\Support\Events\BillScanner;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
*
|
||||
* Class UpdatedJournalEventHandler
|
||||
*
|
||||
* @package FireflyIII\Handlers\Events
|
||||
@@ -35,6 +35,8 @@ class UpdatedJournalEventHandler
|
||||
|
||||
/**
|
||||
* StoredJournalEventHandler constructor.
|
||||
*
|
||||
* @param RuleGroupRepositoryInterface $ruleGroupRepository
|
||||
*/
|
||||
public function __construct(RuleGroupRepositoryInterface $ruleGroupRepository)
|
||||
{
|
||||
@@ -52,7 +54,7 @@ class UpdatedJournalEventHandler
|
||||
{
|
||||
// get all the user's rule groups, with the rules, order by 'order'.
|
||||
$journal = $updatedJournalEvent->journal;
|
||||
$groups = $this->repository->getActiveGroups($journal->user);
|
||||
$groups = $this->repository->getActiveGroups($journal->user);
|
||||
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
|
@@ -74,6 +74,7 @@ class UserEventHandler
|
||||
} catch (Swift_TransportException $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return true;
|
||||
@@ -96,16 +97,17 @@ class UserEventHandler
|
||||
}
|
||||
// get the email address
|
||||
$email = $event->user->email;
|
||||
$address = route('index');
|
||||
$uri = route('index');
|
||||
$ipAddress = $event->ipAddress;
|
||||
|
||||
// send email.
|
||||
try {
|
||||
Mail::to($email)->send(new RegisteredUserMail($address, $ipAddress));
|
||||
Mail::to($email)->send(new RegisteredUserMail($uri, $ipAddress));
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Swift_TransportException $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return true;
|
||||
|
@@ -20,7 +20,7 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Storage;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class AttachmentHelper
|
||||
*
|
||||
@@ -202,6 +202,7 @@ class AttachmentHelper implements AttachmentHelperInterface
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param UploadedFile $file
|
||||
*
|
||||
* @return bool
|
||||
|
@@ -60,17 +60,30 @@ class JournalCollector implements JournalCollectorInterface
|
||||
'transaction_journals.description',
|
||||
'transaction_journals.date',
|
||||
'transaction_journals.encrypted',
|
||||
'transaction_currencies.code as transaction_currency_code',
|
||||
'transaction_types.type as transaction_type_type',
|
||||
'transaction_journals.bill_id',
|
||||
'bills.name as bill_name',
|
||||
'bills.name_encrypted as bill_name_encrypted',
|
||||
'transactions.id as id',
|
||||
'transactions.amount as transaction_amount',
|
||||
|
||||
'transactions.description as transaction_description',
|
||||
'transactions.account_id',
|
||||
'transactions.identifier',
|
||||
'transactions.transaction_journal_id',
|
||||
|
||||
'transactions.amount as transaction_amount',
|
||||
|
||||
'transactions.transaction_currency_id as transaction_currency_id',
|
||||
'transaction_currencies.code as transaction_currency_code',
|
||||
'transaction_currencies.symbol as transaction_currency_symbol',
|
||||
'transaction_currencies.decimal_places as transaction_currency_dp',
|
||||
|
||||
'transactions.foreign_amount as transaction_foreign_amount',
|
||||
'transactions.foreign_currency_id as foreign_currency_id',
|
||||
'foreign_currencies.code as foreign_currency_code',
|
||||
'foreign_currencies.symbol as foreign_currency_symbol',
|
||||
'foreign_currencies.decimal_places as foreign_currency_dp',
|
||||
|
||||
'accounts.name as account_name',
|
||||
'accounts.encrypted as account_encrypted',
|
||||
'account_types.type as account_type',
|
||||
@@ -484,11 +497,12 @@ class JournalCollector implements JournalCollectorInterface
|
||||
Log::debug('journalCollector::startQuery');
|
||||
/** @var EloquentBuilder $query */
|
||||
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
|
||||
->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id')
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
|
||||
->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
|
||||
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transactions.transaction_currency_id')
|
||||
->leftJoin('transaction_currencies as foreign_currencies', 'foreign_currencies.id', 'transactions.foreign_currency_id')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
|
@@ -26,7 +26,7 @@ use Log;
|
||||
*/
|
||||
class InternalTransferFilter implements FilterInterface
|
||||
{
|
||||
/** @var array */
|
||||
/** @var array */
|
||||
private $accounts = [];
|
||||
|
||||
/**
|
||||
|
@@ -187,7 +187,7 @@ class PopupReport implements PopupReportInterface
|
||||
$journals = $journals->filter(
|
||||
function (Transaction $transaction) use ($report) {
|
||||
// get the destinations:
|
||||
$destinations = $transaction->destinationAccountList($transaction->transactionJournal)->pluck('id')->toArray();
|
||||
$destinations = $transaction->transactionJournal->destinationAccountList()->pluck('id')->toArray();
|
||||
|
||||
// do these intersect with the current list?
|
||||
return !empty(array_intersect($report, $destinations));
|
||||
|
@@ -219,8 +219,8 @@ class AccountController extends Controller
|
||||
$start->subDay();
|
||||
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
$startBalances = Steam::balancesById($ids, $start);
|
||||
$endBalances = Steam::balancesById($ids, $end);
|
||||
$startBalances = Steam::balancesByAccounts($accounts, $start);
|
||||
$endBalances = Steam::balancesByAccounts($accounts, $end);
|
||||
$activities = Steam::getLastActivities($ids);
|
||||
|
||||
$accounts->each(
|
||||
@@ -293,8 +293,8 @@ class AccountController extends Controller
|
||||
$periods = $this->getPeriodOverview($account);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$loop = 0;
|
||||
$count = 0;
|
||||
$loop = 0;
|
||||
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||
Log::info('Now at loop start.');
|
||||
while ($count === 0 && $loop < 3) {
|
||||
|
@@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Amount;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Requests\BudgetFormRequest;
|
||||
@@ -166,16 +167,38 @@ class BudgetController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $moment
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function index()
|
||||
public function index(string $moment = null)
|
||||
{
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = session('start', new Carbon);
|
||||
$end = session('end', new Carbon);
|
||||
|
||||
// make date if present:
|
||||
if (!is_null($moment) || strlen(strval($moment)) !== 0) {
|
||||
try {
|
||||
$start = new Carbon($moment);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
} catch (Exception $e) {
|
||||
// start and end are already defined.
|
||||
|
||||
}
|
||||
}
|
||||
$next = clone $end;
|
||||
$next->addDay();
|
||||
$prev = clone $start;
|
||||
$prev->subDay();
|
||||
$prev = Navigation::startOfPeriod($prev, $range);
|
||||
|
||||
|
||||
$this->repository->cleanupBudgets();
|
||||
|
||||
|
||||
$budgets = $this->repository->getActiveBudgets();
|
||||
$inactive = $this->repository->getInactiveBudgets();
|
||||
$start = session('start', new Carbon);
|
||||
$end = session('end', new Carbon);
|
||||
$periodStart = $start->formatLocalized($this->monthAndDayFormat);
|
||||
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
|
||||
$budgetInformation = $this->collectBudgetInformation($budgets, $start, $end);
|
||||
@@ -184,9 +207,44 @@ class BudgetController extends Controller
|
||||
$spent = array_sum(array_column($budgetInformation, 'spent'));
|
||||
$budgeted = array_sum(array_column($budgetInformation, 'budgeted'));
|
||||
|
||||
// select thing for last 12 periods:
|
||||
$previousLoop = [];
|
||||
$previousDate = clone $start;
|
||||
$count = 0;
|
||||
while ($count < 12) {
|
||||
$previousDate->subDay();
|
||||
$previousDate = Navigation::startOfPeriod($previousDate, $range);
|
||||
$format = $previousDate->format('Y-m-d');
|
||||
$previousLoop[$format] = Navigation::periodShow($previousDate, $range);
|
||||
$count++;
|
||||
}
|
||||
|
||||
// select thing for next 12 periods:
|
||||
$nextLoop = [];
|
||||
$nextDate = clone $end;
|
||||
$nextDate->addDay();
|
||||
$count = 0;
|
||||
|
||||
while ($count < 12) {
|
||||
$format = $nextDate->format('Y-m-d');
|
||||
$nextLoop[$format] = Navigation::periodShow($nextDate, $range);
|
||||
$nextDate = Navigation::endOfPeriod($nextDate, $range);
|
||||
$count++;
|
||||
$nextDate->addDay();
|
||||
}
|
||||
|
||||
// display info
|
||||
$currentMonth = Navigation::periodShow($start, $range);
|
||||
$nextText = Navigation::periodShow($next, $range);
|
||||
$prevText = Navigation::periodShow($prev, $range);
|
||||
|
||||
return view(
|
||||
'budgets.index',
|
||||
compact('available', 'periodStart', 'periodEnd', 'budgetInformation', 'inactive', 'budgets', 'spent', 'budgeted')
|
||||
compact(
|
||||
'available', 'currentMonth', 'next', 'nextText', 'prev', 'prevText',
|
||||
'periodStart', 'periodEnd', 'budgetInformation', 'inactive', 'budgets',
|
||||
'spent', 'budgeted', 'previousLoop', 'nextLoop', 'start'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
@@ -115,9 +114,8 @@ class AccountController extends Controller
|
||||
$start->subDay();
|
||||
|
||||
$accounts = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
$startBalances = Steam::balancesById($ids, $start);
|
||||
$endBalances = Steam::balancesById($ids, $end);
|
||||
$startBalances = Steam::balancesByAccounts($accounts, $start);
|
||||
$endBalances = Steam::balancesByAccounts($accounts, $end);
|
||||
$chartData = [];
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
@@ -336,7 +334,7 @@ class AccountController extends Controller
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $start
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws FireflyException
|
||||
@@ -411,9 +409,8 @@ class AccountController extends Controller
|
||||
$accounts = $repository->getAccountsByType([AccountType::REVENUE]);
|
||||
|
||||
$start->subDay();
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
$startBalances = Steam::balancesById($ids, $start);
|
||||
$endBalances = Steam::balancesById($ids, $end);
|
||||
$startBalances = Steam::balancesByAccounts($accounts, $start);
|
||||
$endBalances = Steam::balancesByAccounts($accounts, $end);
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$id = $account->id;
|
||||
|
@@ -31,6 +31,7 @@ use Illuminate\Support\Collection;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
* Class BudgetController
|
||||
@@ -320,12 +321,12 @@ class BudgetController extends Controller
|
||||
['label' => strval(trans('firefly.overspent')), 'entries' => [], 'type' => 'bar',],
|
||||
];
|
||||
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
// get relevant repetitions:
|
||||
$limits = $this->repository->getBudgetLimits($budget, $start, $end);
|
||||
$expenses = $this->getExpensesForBudget($limits, $budget, $start, $end);
|
||||
|
||||
foreach ($expenses as $name => $row) {
|
||||
$chartData[0]['entries'][$name] = $row['spent'];
|
||||
$chartData[1]['entries'][$name] = $row['left'];
|
||||
@@ -529,9 +530,7 @@ class BudgetController extends Controller
|
||||
$rows = $this->spentInPeriodMulti($budget, $limits);
|
||||
foreach ($rows as $name => $row) {
|
||||
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['left'], '0') !== 0) {
|
||||
$return[$name]['spent'] = bcmul($row['spent'], '-1');
|
||||
$return[$name]['left'] = $row['left'];
|
||||
$return[$name]['overspent'] = bcmul($row['overspent'], '-1');
|
||||
$return[$name] = $row;
|
||||
}
|
||||
}
|
||||
unset($rows, $row);
|
||||
@@ -563,6 +562,7 @@ class BudgetController extends Controller
|
||||
/** @var BudgetLimit $budgetLimit */
|
||||
foreach ($limits as $budgetLimit) {
|
||||
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date);
|
||||
$expenses = Steam::positive($expenses);
|
||||
|
||||
if ($limits->count() > 1) {
|
||||
$name = $budget->name . ' ' . trans(
|
||||
@@ -578,10 +578,14 @@ class BudgetController extends Controller
|
||||
* left: amount of budget limit min spent, or 0 when < 0.
|
||||
* spent: spent, or amount of budget limit when > amount
|
||||
*/
|
||||
$amount = $budgetLimit->amount;
|
||||
$left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses);
|
||||
$spent = bccomp($expenses, $amount) === 1 ? $expenses : bcmul($amount, '-1');
|
||||
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
|
||||
$amount = $budgetLimit->amount;
|
||||
$leftInLimit = bcsub($amount, $expenses);
|
||||
$hasOverspent = bccomp($leftInLimit, '0') === -1;
|
||||
|
||||
$left = $hasOverspent ? '0' : bcsub($amount, $expenses);
|
||||
$spent = $hasOverspent ? $amount : $expenses;
|
||||
$overspent = $hasOverspent ? Steam::positive($leftInLimit) : '0';
|
||||
|
||||
$return[$name] = [
|
||||
'left' => $left,
|
||||
'overspent' => $overspent,
|
||||
|
@@ -277,10 +277,10 @@ class CategoryController extends Controller
|
||||
*/
|
||||
public function specificPeriod(CategoryRepositoryInterface $repository, Category $category, Carbon $date)
|
||||
{
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($date, $range);
|
||||
$end = Navigation::endOfPeriod($date, $range);
|
||||
$data = $this->makePeriodChart($repository, $category, $start, $end);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($date, $range);
|
||||
$end = Navigation::endOfPeriod($date, $range);
|
||||
$data = $this->makePeriodChart($repository, $category, $start, $end);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
@@ -336,9 +336,9 @@ class CategoryController extends Controller
|
||||
$sum = bcadd($spent, $earned);
|
||||
$label = trim(Navigation::periodShow($start, '1D'));
|
||||
|
||||
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'),12);
|
||||
$chartData[1]['entries'][$label] = round($earned,12);
|
||||
$chartData[2]['entries'][$label] = round($sum,12);
|
||||
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12);
|
||||
$chartData[1]['entries'][$label] = round($earned, 12);
|
||||
$chartData[2]['entries'][$label] = round($sum, 12);
|
||||
|
||||
|
||||
$start->addDay();
|
||||
|
@@ -16,7 +16,6 @@ namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
|
||||
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
|
||||
|
@@ -67,11 +67,10 @@ class ReportController extends Controller
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
$current = clone $start;
|
||||
$chartData = [];
|
||||
while ($current < $end) {
|
||||
$balances = Steam::balancesById($ids, $current);
|
||||
$balances = Steam::balancesByAccounts($accounts, $current);
|
||||
$sum = $this->arraySum($balances);
|
||||
$label = $current->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$chartData[$label] = $sum;
|
||||
@@ -104,7 +103,7 @@ class ReportController extends Controller
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
//return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
Log::debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray());
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
@@ -250,7 +249,7 @@ class ReportController extends Controller
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
// return $cache->get(); // @codeCoverageIgnore
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$currentStart = clone $start;
|
||||
|
@@ -34,7 +34,7 @@ class JavascriptController extends Controller
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param CurrencyRepositoryInterface $currencyRepository
|
||||
*
|
||||
* @return $this
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function accounts(AccountRepositoryInterface $repository, CurrencyRepositoryInterface $currencyRepository)
|
||||
{
|
||||
@@ -63,7 +63,7 @@ class JavascriptController extends Controller
|
||||
/**
|
||||
* @param CurrencyRepositoryInterface $repository
|
||||
*
|
||||
* @return $this
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function currencies(CurrencyRepositoryInterface $repository)
|
||||
{
|
||||
@@ -71,8 +71,8 @@ class JavascriptController extends Controller
|
||||
$data = ['currencies' => [],];
|
||||
/** @var TransactionCurrency $currency */
|
||||
foreach ($currencies as $currency) {
|
||||
$currencyId = $currency->id;
|
||||
$entry = ['name' => $currency->name, 'code' => $currency->code, 'symbol' => $currency->symbol];
|
||||
$currencyId = $currency->id;
|
||||
$entry = ['name' => $currency->name, 'code' => $currency->code, 'symbol' => $currency->symbol];
|
||||
$data['currencies'][$currencyId] = $entry;
|
||||
}
|
||||
|
||||
|
@@ -41,7 +41,6 @@ class ExchangeController extends Controller
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$rate = $repository->getExchangeRate($fromCurrency, $toCurrency, $date);
|
||||
$amount = null;
|
||||
if (is_null($rate->id)) {
|
||||
Log::debug(sprintf('No cached exchange rate in database for %s to %s on %s', $fromCurrency->code, $toCurrency->code, $date->format('Y-m-d')));
|
||||
$preferred = env('EXCHANGE_RATE_SERVICE', config('firefly.preferred_exchange_service'));
|
||||
|
@@ -20,7 +20,6 @@ use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
@@ -116,10 +115,11 @@ class JsonController extends Controller
|
||||
* Since both this method and the chart use the exact same data, we can suffice
|
||||
* with calling the one method in the bill repository that will get this amount.
|
||||
*/
|
||||
$amount = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
|
||||
$amount = bcmul($amount, '-1');
|
||||
$amount = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
|
||||
$amount = bcmul($amount, '-1');
|
||||
$currency = Amount::getDefaultCurrency();
|
||||
|
||||
$data = ['box' => 'bills-paid', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
|
||||
$data = ['box' => 'bills-paid', 'amount' => Amount::formatAnything($currency, $amount, false), 'amount_raw' => $amount];
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
@@ -131,19 +131,19 @@ class JsonController extends Controller
|
||||
*/
|
||||
public function boxBillsUnpaid(BillRepositoryInterface $repository)
|
||||
{
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
$amount = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount.
|
||||
$data = ['box' => 'bills-unpaid', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
$amount = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount.
|
||||
$currency = Amount::getDefaultCurrency();
|
||||
$data = ['box' => 'bills-unpaid', 'amount' => Amount::formatAnything($currency, $amount, false), 'amount_raw' => $amount];
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountTaskerInterface $accountTasker
|
||||
* @param AccountRepositoryInterface $repository
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @internal param AccountTaskerInterface $accountTasker
|
||||
* @internal param AccountRepositoryInterface $repository
|
||||
*
|
||||
*/
|
||||
public function boxIn()
|
||||
@@ -167,18 +167,19 @@ class JsonController extends Controller
|
||||
->setTypes([TransactionType::DEPOSIT])
|
||||
->withOpposingAccount();
|
||||
|
||||
$amount = strval($collector->getJournals()->sum('transaction_amount'));
|
||||
$data = ['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
|
||||
$amount = strval($collector->getJournals()->sum('transaction_amount'));
|
||||
$currency = Amount::getDefaultCurrency();
|
||||
$data = ['box' => 'in', 'amount' => Amount::formatAnything($currency, $amount, false), 'amount_raw' => $amount];
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountTaskerInterface $accountTasker
|
||||
* @param AccountRepositoryInterface $repository
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @internal param AccountTaskerInterface $accountTasker
|
||||
* @internal param AccountRepositoryInterface $repository
|
||||
*
|
||||
*/
|
||||
public function boxOut()
|
||||
{
|
||||
@@ -200,9 +201,9 @@ class JsonController extends Controller
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)
|
||||
->setTypes([TransactionType::WITHDRAWAL])
|
||||
->withOpposingAccount();
|
||||
$amount = strval($collector->getJournals()->sum('transaction_amount'));
|
||||
|
||||
$data = ['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
|
||||
$amount = strval($collector->getJournals()->sum('transaction_amount'));
|
||||
$currency = Amount::getDefaultCurrency();
|
||||
$data = ['box' => 'out', 'amount' => Amount::formatAnything($currency, $amount, false), 'amount_raw' => $amount];
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
@@ -276,18 +276,29 @@ class PiggyBankController extends Controller
|
||||
*/
|
||||
public function postAdd(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||
{
|
||||
$amount = $request->get('amount');
|
||||
|
||||
$amount = $request->get('amount');
|
||||
$currency = Amount::getDefaultCurrency();
|
||||
if ($repository->canAddAmount($piggyBank, $amount)) {
|
||||
$repository->addAmount($piggyBank, $amount);
|
||||
Session::flash('success', strval(trans('firefly.added_amount_to_piggy', ['amount' => Amount::format($amount, false), 'name' => $piggyBank->name])));
|
||||
Session::flash(
|
||||
'success', strval(
|
||||
trans(
|
||||
'firefly.added_amount_to_piggy',
|
||||
['amount' => Amount::formatAnything($currency, $amount, false), 'name' => $piggyBank->name]
|
||||
)
|
||||
)
|
||||
);
|
||||
Preferences::mark();
|
||||
|
||||
return redirect(route('piggy-banks.index'));
|
||||
}
|
||||
|
||||
Log::error('Cannot add ' . $amount . ' because canAddAmount returned false.');
|
||||
Session::flash('error', strval(trans('firefly.cannot_add_amount_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])));
|
||||
Session::flash(
|
||||
'error', strval(
|
||||
trans('firefly.cannot_add_amount_piggy', ['amount' => Amount::formatAnything($currency, $amount, false), 'name' => e($piggyBank->name)])
|
||||
)
|
||||
);
|
||||
|
||||
return redirect(route('piggy-banks.index'));
|
||||
}
|
||||
@@ -301,11 +312,13 @@ class PiggyBankController extends Controller
|
||||
*/
|
||||
public function postRemove(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||
{
|
||||
$amount = $request->get('amount');
|
||||
$amount = $request->get('amount');
|
||||
$currency = Amount::getDefaultCurrency();
|
||||
if ($repository->canRemoveAmount($piggyBank, $amount)) {
|
||||
$repository->removeAmount($piggyBank, $amount);
|
||||
Session::flash(
|
||||
'success', strval(trans('firefly.removed_amount_from_piggy', ['amount' => Amount::format($amount, false), 'name' => $piggyBank->name]))
|
||||
'success',
|
||||
strval(trans('firefly.removed_amount_from_piggy', ['amount' => Amount::formatAnything($currency, $amount, false), 'name' => $piggyBank->name]))
|
||||
);
|
||||
Preferences::mark();
|
||||
|
||||
@@ -314,7 +327,11 @@ class PiggyBankController extends Controller
|
||||
|
||||
$amount = strval(round($request->get('amount'), 12));
|
||||
|
||||
Session::flash('error', strval(trans('firefly.cannot_remove_from_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])));
|
||||
Session::flash(
|
||||
'error', strval(
|
||||
trans('firefly.cannot_remove_from_piggy', ['amount' => Amount::formatAnything($currency, $amount, false), 'name' => e($piggyBank->name)])
|
||||
)
|
||||
);
|
||||
|
||||
return redirect(route('piggy-banks.index'));
|
||||
}
|
||||
|
@@ -15,10 +15,7 @@ namespace FireflyIII\Http\Controllers\Report;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
|
@@ -306,7 +306,7 @@ class RuleController extends Controller
|
||||
}
|
||||
|
||||
// Return json response
|
||||
$view = view('list.journals-tiny-tasker', ['transactions' => $matchingTransactions])->render();
|
||||
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render();
|
||||
|
||||
return Response::json(['html' => $view, 'warning' => $warning]);
|
||||
}
|
||||
|
@@ -174,14 +174,23 @@ class ConvertController extends Controller
|
||||
switch ($joined) {
|
||||
default:
|
||||
throw new FireflyException('Cannot handle ' . $joined); // @codeCoverageIgnore
|
||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: // one
|
||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT:
|
||||
// one
|
||||
$destination = $sourceAccount;
|
||||
break;
|
||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: // two
|
||||
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
|
||||
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL:
|
||||
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL:
|
||||
// three and five
|
||||
if ($data['destination_account_expense'] === '') {
|
||||
// destination is a cash account.
|
||||
$destination = $accountRepository->getCashAccount();
|
||||
|
||||
return $destination;
|
||||
}
|
||||
$data = [
|
||||
'name' => $data['destination_account_expense'],
|
||||
'accountType' => 'expense',
|
||||
@@ -191,8 +200,9 @@ class ConvertController extends Controller
|
||||
];
|
||||
$destination = $accountRepository->store($data);
|
||||
break;
|
||||
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: // four
|
||||
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: // six
|
||||
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER:
|
||||
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT:
|
||||
// four and six
|
||||
$destination = $destinationAccount;
|
||||
break;
|
||||
}
|
||||
@@ -221,6 +231,14 @@ class ConvertController extends Controller
|
||||
throw new FireflyException('Cannot handle ' . $joined); // @codeCoverageIgnore
|
||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: // one
|
||||
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: // six
|
||||
|
||||
if ($data['source_account_revenue'] === '') {
|
||||
// destination is a cash account.
|
||||
$destination = $accountRepository->getCashAccount();
|
||||
|
||||
return $destination;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'name' => $data['source_account_revenue'],
|
||||
'accountType' => 'revenue',
|
||||
|
@@ -19,6 +19,7 @@ use FireflyIII\Http\Requests\MassDeleteJournalRequest;
|
||||
use FireflyIII\Http\Requests\MassEditJournalRequest;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
@@ -126,8 +127,7 @@ class MassController extends Controller
|
||||
$budgetRepository = app(BudgetRepositoryInterface::class);
|
||||
$budgets = $budgetRepository->getBudgets();
|
||||
|
||||
// skip transactions that have multiple destinations
|
||||
// or multiple sources:
|
||||
// skip transactions that have multiple destinations, multiple sources or are an opening balance.
|
||||
$filtered = new Collection;
|
||||
$messages = [];
|
||||
/**
|
||||
@@ -146,6 +146,10 @@ class MassController extends Controller
|
||||
$messages[] = trans('firefly.cannot_edit_multiple_dest', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
}
|
||||
if ($journal->transactionType->type === TransactionType::OPENING_BALANCE) {
|
||||
$messages[] = trans('firefly.cannot_edit_opening_balance');
|
||||
continue;
|
||||
}
|
||||
$filtered->push($journal);
|
||||
}
|
||||
|
||||
@@ -158,13 +162,21 @@ class MassController extends Controller
|
||||
Session::flash('gaEventCategory', 'transactions');
|
||||
Session::flash('gaEventAction', 'mass-edit');
|
||||
|
||||
// set some values to be used in the edit routine:
|
||||
// collect some useful meta data for the mass edit:
|
||||
$filtered->each(
|
||||
function (TransactionJournal $journal) {
|
||||
$journal->amount = $journal->amountPositive();
|
||||
$sources = $journal->sourceAccountList();
|
||||
$destinations = $journal->destinationAccountList();
|
||||
$journal->transaction_count = $journal->transactions()->count();
|
||||
$transaction = $journal->positiveTransaction();
|
||||
$currency = $transaction->transactionCurrency;
|
||||
$journal->amount = floatval($transaction->amount);
|
||||
$sources = $journal->sourceAccountList();
|
||||
$destinations = $journal->destinationAccountList();
|
||||
$journal->transaction_count = $journal->transactions()->count();
|
||||
$journal->currency_symbol = $currency->symbol;
|
||||
$journal->transaction_type_type = $journal->transactionType->type;
|
||||
|
||||
$journal->foreign_amount = floatval($transaction->foreign_amount);
|
||||
$journal->foreign_currency = $transaction->foreignCurrency;
|
||||
|
||||
if (!is_null($sources->first())) {
|
||||
$journal->source_account_id = $sources->first()->id;
|
||||
$journal->source_account_name = $sources->first()->editname;
|
||||
@@ -208,6 +220,10 @@ class MassController extends Controller
|
||||
$budgetId = $request->get('budget_id')[$journal->id] ?? 0;
|
||||
$category = $request->get('category')[$journal->id];
|
||||
$tags = $journal->tags->pluck('tag')->toArray();
|
||||
$amount = round($request->get('amount')[$journal->id], 12);
|
||||
$foreignAmount = isset($request->get('foreign_amount')[$journal->id]) ? round($request->get('foreign_amount')[$journal->id], 12) : null;
|
||||
$foreignCurrencyId = isset($request->get('foreign_currency_id')[$journal->id]) ?
|
||||
intval($request->get('foreign_currency_id')[$journal->id]) : null;
|
||||
|
||||
// build data array
|
||||
$data = [
|
||||
@@ -218,16 +234,19 @@ class MassController extends Controller
|
||||
'source_account_name' => $sourceAccountName,
|
||||
'destination_account_id' => intval($destAccountId),
|
||||
'destination_account_name' => $destAccountName,
|
||||
'amount' => round($request->get('amount')[$journal->id], 12),
|
||||
'currency_id' => $journal->transaction_currency_id,
|
||||
'amount' => $foreignAmount,
|
||||
'native_amount' => $amount,
|
||||
'source_amount' => $amount,
|
||||
'date' => new Carbon($request->get('date')[$journal->id]),
|
||||
'interest_date' => $journal->interest_date,
|
||||
'book_date' => $journal->book_date,
|
||||
'process_date' => $journal->process_date,
|
||||
'budget_id' => intval($budgetId),
|
||||
'currency_id' => $foreignCurrencyId,
|
||||
'foreign_amount' => $foreignAmount,
|
||||
'destination_amount' => $foreignAmount,
|
||||
'category' => $category,
|
||||
'tags' => $tags,
|
||||
|
||||
];
|
||||
// call repository update function.
|
||||
$repository->update($journal, $data);
|
||||
@@ -235,6 +254,7 @@ class MassController extends Controller
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Preferences::mark();
|
||||
Session::flash('success', trans('firefly.mass_edited_transactions_success', ['amount' => $count]));
|
||||
|
@@ -238,6 +238,7 @@ class SingleController extends Controller
|
||||
$sourceAccounts = $journal->sourceAccountList();
|
||||
$destinationAccounts = $journal->destinationAccountList();
|
||||
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
|
||||
$pTransaction = $journal->positiveTransaction();
|
||||
$preFilled = [
|
||||
'date' => $journal->dateAsString(),
|
||||
'interest_date' => $journal->dateAsString('interest_date'),
|
||||
@@ -250,8 +251,6 @@ class SingleController extends Controller
|
||||
'source_account_name' => $sourceAccounts->first()->edit_name,
|
||||
'destination_account_id' => $destinationAccounts->first()->id,
|
||||
'destination_account_name' => $destinationAccounts->first()->edit_name,
|
||||
'amount' => $journal->amountPositive(),
|
||||
'currency' => $journal->transactionCurrency,
|
||||
|
||||
// new custom fields:
|
||||
'due_date' => $journal->dateAsString('due_date'),
|
||||
@@ -260,26 +259,36 @@ class SingleController extends Controller
|
||||
'interal_reference' => $journal->getMeta('internal_reference'),
|
||||
'notes' => $journal->getMeta('notes'),
|
||||
|
||||
// exchange rate fields
|
||||
'native_amount' => $journal->amountPositive(),
|
||||
'native_currency' => $journal->transactionCurrency,
|
||||
// amount fields
|
||||
'amount' => $pTransaction->amount,
|
||||
'source_amount' => $pTransaction->amount,
|
||||
'native_amount' => $pTransaction->amount,
|
||||
'destination_amount' => $pTransaction->foreign_amount,
|
||||
'currency' => $pTransaction->transactionCurrency,
|
||||
'source_currency' => $pTransaction->transactionCurrency,
|
||||
'native_currency' => $pTransaction->transactionCurrency,
|
||||
'foreign_currency' => !is_null($pTransaction->foreignCurrency) ? $pTransaction->foreignCurrency : $pTransaction->transactionCurrency,
|
||||
'destination_currency' => !is_null($pTransaction->foreignCurrency) ? $pTransaction->foreignCurrency : $pTransaction->transactionCurrency,
|
||||
];
|
||||
|
||||
// if user has entered a foreign currency, update some fields
|
||||
$foreignCurrencyId = intval($journal->getMeta('foreign_currency_id'));
|
||||
if ($foreignCurrencyId > 0) {
|
||||
// update some fields in pre-filled.
|
||||
// @codeCoverageIgnoreStart
|
||||
$preFilled['amount'] = $journal->getMeta('foreign_amount');
|
||||
$preFilled['currency'] = $this->currency->find(intval($journal->getMeta('foreign_currency_id')));
|
||||
// @codeCoverageIgnoreEnd
|
||||
// amounts for withdrawals and deposits:
|
||||
// amount, native_amount, source_amount, destination_amount
|
||||
if (($journal->isWithdrawal() || $journal->isDeposit()) && !is_null($pTransaction->foreign_amount)) {
|
||||
$preFilled['amount'] = $pTransaction->foreign_amount;
|
||||
$preFilled['currency'] = $pTransaction->foreignCurrency;
|
||||
}
|
||||
|
||||
if ($journal->isWithdrawal() && $destinationAccounts->first()->accountType->type == AccountType::CASH) {
|
||||
if ($journal->isTransfer() && !is_null($pTransaction->foreign_amount)) {
|
||||
$preFilled['destination_amount'] = $pTransaction->foreign_amount;
|
||||
$preFilled['destination_currency'] = $pTransaction->foreignCurrency;
|
||||
}
|
||||
|
||||
// fixes for cash accounts:
|
||||
if ($journal->isWithdrawal() && $destinationAccounts->first()->accountType->type === AccountType::CASH) {
|
||||
$preFilled['destination_account_name'] = '';
|
||||
}
|
||||
|
||||
if ($journal->isDeposit() && $sourceAccounts->first()->accountType->type == AccountType::CASH) {
|
||||
if ($journal->isDeposit() && $sourceAccounts->first()->accountType->type === AccountType::CASH) {
|
||||
$preFilled['source_account_name'] = '';
|
||||
}
|
||||
|
||||
@@ -319,6 +328,7 @@ class SingleController extends Controller
|
||||
|
||||
return redirect(route('transactions.create', [$request->input('what')]))->withInput();
|
||||
}
|
||||
|
||||
/** @var array $files */
|
||||
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
|
||||
$this->attachments->saveAttachmentsForModel($journal, $files);
|
||||
|
@@ -93,7 +93,7 @@ class SplitController extends Controller
|
||||
}
|
||||
|
||||
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
|
||||
$currencies = ExpandedForm::makeSelectList($this->currencies->get());
|
||||
$currencies = $this->currencies->get();
|
||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
|
||||
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
|
||||
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
||||
@@ -130,7 +130,6 @@ class SplitController extends Controller
|
||||
*/
|
||||
public function update(Request $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
|
||||
{
|
||||
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
@@ -179,7 +178,6 @@ class SplitController extends Controller
|
||||
'journal_source_account_id' => $request->get('journal_source_account_id'),
|
||||
'journal_source_account_name' => $request->get('journal_source_account_name'),
|
||||
'journal_destination_account_id' => $request->get('journal_destination_account_id'),
|
||||
'currency_id' => $request->get('currency_id'),
|
||||
'what' => $request->get('what'),
|
||||
'date' => $request->get('date'),
|
||||
// all custom fields:
|
||||
@@ -218,7 +216,6 @@ class SplitController extends Controller
|
||||
'journal_source_account_id' => $request->old('journal_source_account_id', $sourceAccounts->first()->id),
|
||||
'journal_source_account_name' => $request->old('journal_source_account_name', $sourceAccounts->first()->name),
|
||||
'journal_destination_account_id' => $request->old('journal_destination_account_id', $destinationAccounts->first()->id),
|
||||
'currency_id' => $request->old('currency_id', $journal->transaction_currency_id),
|
||||
'destinationAccounts' => $destinationAccounts,
|
||||
'what' => strtolower($journal->transactionTypeStr()),
|
||||
'date' => $request->old('date', $journal->date),
|
||||
@@ -253,14 +250,22 @@ class SplitController extends Controller
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
$set = [
|
||||
'description' => $transaction['description'],
|
||||
'source_account_id' => $transaction['source_account_id'],
|
||||
'source_account_name' => $transaction['source_account_name'],
|
||||
'destination_account_id' => $transaction['destination_account_id'],
|
||||
'destination_account_name' => $transaction['destination_account_name'],
|
||||
'amount' => round($transaction['destination_amount'], 12),
|
||||
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
|
||||
'category' => $transaction['category'],
|
||||
'description' => $transaction['description'],
|
||||
'source_account_id' => $transaction['source_account_id'],
|
||||
'source_account_name' => $transaction['source_account_name'],
|
||||
'destination_account_id' => $transaction['destination_account_id'],
|
||||
'destination_account_name' => $transaction['destination_account_name'],
|
||||
'amount' => round($transaction['destination_amount'], 12),
|
||||
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
|
||||
'category' => $transaction['category'],
|
||||
'transaction_currency_id' => $transaction['transaction_currency_id'],
|
||||
'transaction_currency_code' => $transaction['transaction_currency_code'],
|
||||
'transaction_currency_symbol' => $transaction['transaction_currency_symbol'],
|
||||
'foreign_amount' => round($transaction['foreign_destination_amount'], 12),
|
||||
'foreign_currency_id' => $transaction['foreign_currency_id'],
|
||||
'foreign_currency_code' => $transaction['foreign_currency_code'],
|
||||
'foreign_currency_symbol' => $transaction['foreign_currency_symbol'],
|
||||
|
||||
];
|
||||
|
||||
// set initial category and/or budget:
|
||||
@@ -294,8 +299,12 @@ class SplitController extends Controller
|
||||
'destination_account_id' => $transaction['destination_account_id'] ?? 0,
|
||||
'destination_account_name' => $transaction['destination_account_name'] ?? '',
|
||||
'amount' => round($transaction['amount'] ?? 0, 12),
|
||||
'foreign_amount' => !isset($transaction['foreign_amount']) ? null : round($transaction['foreign_amount'] ?? 0, 12),
|
||||
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
|
||||
'category' => $transaction['category'] ?? '',
|
||||
'transaction_currency_id' => intval($transaction['transaction_currency_id']),
|
||||
'foreign_currency_id' => $transaction['foreign_currency_id'] ?? null,
|
||||
|
||||
];
|
||||
}
|
||||
Log::debug(sprintf('Found %d splits in request data.', count($return)));
|
||||
|
@@ -18,7 +18,6 @@ use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
@@ -119,7 +118,7 @@ class TransactionController extends Controller
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setTypes($types)->setLimit($pageSize)->setPage($page)->withOpposingAccount();
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/list/no-budget');
|
||||
$journals->setPath('/transactions/' . $what);
|
||||
$count = $journals->getCollection()->count();
|
||||
if ($count === 0) {
|
||||
$start->subDay();
|
||||
@@ -179,21 +178,12 @@ class TransactionController extends Controller
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
|
||||
$events = $tasker->getPiggyBankEvents($journal);
|
||||
$transactions = $tasker->getTransactionsOverview($journal);
|
||||
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
||||
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
|
||||
$foreignCurrency = null;
|
||||
$events = $tasker->getPiggyBankEvents($journal);
|
||||
$transactions = $tasker->getTransactionsOverview($journal);
|
||||
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
||||
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
|
||||
|
||||
if ($journal->hasMeta('foreign_currency_id')) {
|
||||
// @codeCoverageIgnoreStart
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$foreignCurrency = $repository->find(intval($journal->getMeta('foreign_currency_id')));
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions', 'foreignCurrency'));
|
||||
return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions'));
|
||||
|
||||
|
||||
}
|
||||
|
@@ -38,19 +38,19 @@ class AccountFormRequest extends Request
|
||||
public function getAccountData(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->string('name'),
|
||||
'active' => $this->boolean('active'),
|
||||
'accountType' => $this->string('what'),
|
||||
'currency_id' => $this->integer('currency_id'),
|
||||
'virtualBalance' => $this->float('virtualBalance'),
|
||||
'iban' => $this->string('iban'),
|
||||
'BIC' => $this->string('BIC'),
|
||||
'accountNumber' => $this->string('accountNumber'),
|
||||
'accountRole' => $this->string('accountRole'),
|
||||
'openingBalance' => $this->float('openingBalance'),
|
||||
'openingBalanceDate' => $this->date('openingBalanceDate'),
|
||||
'ccType' => $this->string('ccType'),
|
||||
'ccMonthlyPaymentDate' => $this->string('ccMonthlyPaymentDate'),
|
||||
'name' => $this->string('name'),
|
||||
'active' => $this->boolean('active'),
|
||||
'accountType' => $this->string('what'),
|
||||
'currency_id' => $this->integer('currency_id'),
|
||||
'virtualBalance' => $this->float('virtualBalance'),
|
||||
'iban' => $this->string('iban'),
|
||||
'BIC' => $this->string('BIC'),
|
||||
'accountNumber' => $this->string('accountNumber'),
|
||||
'accountRole' => $this->string('accountRole'),
|
||||
'openingBalance' => $this->float('openingBalance'),
|
||||
'openingBalanceDate' => $this->date('openingBalanceDate'),
|
||||
'ccType' => $this->string('ccType'),
|
||||
'ccMonthlyPaymentDate' => $this->string('ccMonthlyPaymentDate'),
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -81,7 +81,7 @@ Breadcrumbs::register(
|
||||
if ($moment !== 'all') {
|
||||
$title = trans(
|
||||
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
|
||||
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
|
||||
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
|
||||
);
|
||||
$breadcrumbs->push($title, route('accounts.show', [$account->id, $moment, $start, $end]));
|
||||
}
|
||||
@@ -726,16 +726,16 @@ Breadcrumbs::register(
|
||||
Breadcrumbs::register(
|
||||
'tags.show', function (BreadCrumbGenerator $breadcrumbs, Tag $tag, string $moment, Carbon $start, Carbon $end) {
|
||||
$breadcrumbs->parent('tags.index');
|
||||
$breadcrumbs->push(e($tag->tag), route('tags.show', [$tag->id], $moment));
|
||||
$breadcrumbs->push(e($tag->tag), route('tags.show', [$tag->id, $moment]));
|
||||
if ($moment === 'all') {
|
||||
$breadcrumbs->push(trans('firefly.everything'), route('tags.show', [$tag->id], $moment));
|
||||
$breadcrumbs->push(trans('firefly.everything'), route('tags.show', [$tag->id, $moment]));
|
||||
}
|
||||
if ($moment !== 'all') {
|
||||
$title = trans(
|
||||
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
|
||||
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
|
||||
);
|
||||
$breadcrumbs->push($title, route('tags.show', [$tag->id], $moment));
|
||||
$breadcrumbs->push($title, route('tags.show', [$tag->id, $moment]));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@@ -16,15 +16,12 @@ namespace FireflyIII\Import\Setup;
|
||||
|
||||
use ExpandedForm;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Import\Mapper\MapperInterface;
|
||||
use FireflyIII\Import\MapperPreProcess\PreProcessorInterface;
|
||||
use FireflyIII\Import\Specifics\SpecificInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Import\CsvImportSupportTrait;
|
||||
use Illuminate\Http\Request;
|
||||
use League\Csv\Reader;
|
||||
use Log;
|
||||
use Symfony\Component\HttpFoundation\FileBag;
|
||||
|
||||
@@ -35,6 +32,7 @@ use Symfony\Component\HttpFoundation\FileBag;
|
||||
*/
|
||||
class CsvSetup implements SetupInterface
|
||||
{
|
||||
use CsvImportSupportTrait;
|
||||
/** @var Account */
|
||||
public $defaultImportAccount;
|
||||
/** @var ImportJob */
|
||||
@@ -286,220 +284,4 @@ class CsvSetup implements SetupInterface
|
||||
$this->job->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function doColumnMapping(): bool
|
||||
{
|
||||
$mapArray = $this->job->configuration['column-do-mapping'] ?? [];
|
||||
$doMap = false;
|
||||
foreach ($mapArray as $value) {
|
||||
if ($value === true) {
|
||||
$doMap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->job->configuration['column-mapping-complete'] === false && $doMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function doColumnRoles(): bool
|
||||
{
|
||||
return $this->job->configuration['column-roles-complete'] === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function getDataForColumnMapping(): array
|
||||
{
|
||||
$config = $this->job->configuration;
|
||||
$data = [];
|
||||
$indexes = [];
|
||||
|
||||
foreach ($config['column-do-mapping'] as $index => $mustBeMapped) {
|
||||
if ($mustBeMapped) {
|
||||
|
||||
$column = $config['column-roles'][$index] ?? '_ignore';
|
||||
|
||||
// is valid column?
|
||||
$validColumns = array_keys(config('csv.import_roles'));
|
||||
if (!in_array($column, $validColumns)) {
|
||||
throw new FireflyException(sprintf('"%s" is not a valid column.', $column));
|
||||
}
|
||||
|
||||
$canBeMapped = config('csv.import_roles.' . $column . '.mappable');
|
||||
$preProcessMap = config('csv.import_roles.' . $column . '.pre-process-map');
|
||||
if ($canBeMapped) {
|
||||
$mapperClass = config('csv.import_roles.' . $column . '.mapper');
|
||||
$mapperName = sprintf('\\FireflyIII\\Import\Mapper\\%s', $mapperClass);
|
||||
/** @var MapperInterface $mapper */
|
||||
$mapper = new $mapperName;
|
||||
$indexes[] = $index;
|
||||
$data[$index] = [
|
||||
'name' => $column,
|
||||
'mapper' => $mapperName,
|
||||
'index' => $index,
|
||||
'options' => $mapper->getMap(),
|
||||
'preProcessMap' => null,
|
||||
'values' => [],
|
||||
];
|
||||
if ($preProcessMap) {
|
||||
$preClass = sprintf(
|
||||
'\\FireflyIII\\Import\\MapperPreProcess\\%s',
|
||||
config('csv.import_roles.' . $column . '.pre-process-mapper')
|
||||
);
|
||||
$data[$index]['preProcessMap'] = $preClass;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// in order to actually map we also need all possible values from the CSV file.
|
||||
$content = $this->job->uploadFileContents();
|
||||
/** @var Reader $reader */
|
||||
$reader = Reader::createFromString($content);
|
||||
$reader->setDelimiter($config['delimiter']);
|
||||
$results = $reader->fetch();
|
||||
$validSpecifics = array_keys(config('csv.import_specifics'));
|
||||
|
||||
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) {
|
||||
|
||||
if (!in_array($name, $validSpecifics)) {
|
||||
throw new FireflyException(sprintf('"%s" is not a valid class name', $name));
|
||||
}
|
||||
$class = config('csv.import_specifics.' . $name);
|
||||
/** @var SpecificInterface $specific */
|
||||
$specific = app($class);
|
||||
|
||||
// it returns the row, possibly modified:
|
||||
$row = $specific->run($row);
|
||||
}
|
||||
|
||||
//do something here
|
||||
foreach ($indexes as $index) { // this is simply 1, 2, 3, etc.
|
||||
if (!isset($row[$index])) {
|
||||
// don't really know how to handle this. Just skip, for now.
|
||||
continue;
|
||||
}
|
||||
$value = $row[$index];
|
||||
if (strlen($value) > 0) {
|
||||
|
||||
// we can do some preprocessing here,
|
||||
// which is exclusively to fix the tags:
|
||||
if (!is_null($data[$index]['preProcessMap'])) {
|
||||
/** @var PreProcessorInterface $preProcessor */
|
||||
$preProcessor = app($data[$index]['preProcessMap']);
|
||||
$result = $preProcessor->run($value);
|
||||
$data[$index]['values'] = array_merge($data[$index]['values'], $result);
|
||||
|
||||
Log::debug($rowIndex . ':' . $index . 'Value before preprocessor', ['value' => $value]);
|
||||
Log::debug($rowIndex . ':' . $index . 'Value after preprocessor', ['value-new' => $result]);
|
||||
Log::debug($rowIndex . ':' . $index . 'Value after joining', ['value-complete' => $data[$index]['values']]);
|
||||
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[$index]['values'][] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($data as $index => $entry) {
|
||||
$data[$index]['values'] = array_unique($data[$index]['values']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method collects the data that will enable a user to choose column content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getDataForColumnRoles(): array
|
||||
{
|
||||
Log::debug('Now in getDataForColumnRoles()');
|
||||
$config = $this->job->configuration;
|
||||
$data = [
|
||||
'columns' => [],
|
||||
'columnCount' => 0,
|
||||
'columnHeaders' => [],
|
||||
];
|
||||
|
||||
// show user column role configuration.
|
||||
$content = $this->job->uploadFileContents();
|
||||
|
||||
// create CSV reader.
|
||||
$reader = Reader::createFromString($content);
|
||||
$reader->setDelimiter($config['delimiter']);
|
||||
$start = $config['has-headers'] ? 1 : 0;
|
||||
$end = $start + config('csv.example_rows');
|
||||
$header = [];
|
||||
if ($config['has-headers']) {
|
||||
$header = $reader->fetchOne(0);
|
||||
}
|
||||
|
||||
|
||||
// collect example data in $data['columns']
|
||||
Log::debug(sprintf('While %s is smaller than %d', $start, $end));
|
||||
while ($start < $end) {
|
||||
$row = $reader->fetchOne($start);
|
||||
Log::debug(sprintf('Row %d has %d columns', $start, count($row)));
|
||||
// run specifics here:
|
||||
// and this is the point where the specifix go to work.
|
||||
foreach ($config['specifics'] as $name => $enabled) {
|
||||
/** @var SpecificInterface $specific */
|
||||
$specific = app('FireflyIII\Import\Specifics\\' . $name);
|
||||
Log::debug(sprintf('Will now apply specific "%s" to row %d.', $name, $start));
|
||||
// it returns the row, possibly modified:
|
||||
$row = $specific->run($row);
|
||||
}
|
||||
|
||||
foreach ($row as $index => $value) {
|
||||
$value = trim($value);
|
||||
$data['columnHeaders'][$index] = $header[$index] ?? '';
|
||||
if (strlen($value) > 0) {
|
||||
$data['columns'][$index][] = $value;
|
||||
}
|
||||
}
|
||||
$start++;
|
||||
$data['columnCount'] = count($row) > $data['columnCount'] ? count($row) : $data['columnCount'];
|
||||
}
|
||||
|
||||
// make unique example data
|
||||
foreach ($data['columns'] as $index => $values) {
|
||||
$data['columns'][$index] = array_unique($values);
|
||||
}
|
||||
|
||||
$data['set_roles'] = [];
|
||||
// collect possible column roles:
|
||||
$data['available_roles'] = [];
|
||||
foreach (array_keys(config('csv.import_roles')) as $role) {
|
||||
$data['available_roles'][$role] = trans('csv.column_' . $role);
|
||||
}
|
||||
|
||||
$config['column-count'] = $data['columnCount'];
|
||||
$this->job->configuration = $config;
|
||||
$this->job->save();
|
||||
|
||||
return $data;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -12,17 +12,18 @@ class RegisteredUser extends Mailable
|
||||
/** @var string */
|
||||
public $address;
|
||||
/** @var string */
|
||||
public $ip;
|
||||
public $ipAddress;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
* @param string $address
|
||||
* @param string $ipAddress
|
||||
*/
|
||||
public function __construct(string $address, string $ip)
|
||||
public function __construct(string $address, string $ipAddress)
|
||||
{
|
||||
$this->address = $address;
|
||||
$this->ip = $ip;
|
||||
$this->ipAddress = $ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -10,7 +10,7 @@ class RequestedNewPassword extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
/** @var string */
|
||||
public $ip;
|
||||
public $ipAddress;
|
||||
/** @var string */
|
||||
public $url;
|
||||
|
||||
@@ -18,12 +18,12 @@ class RequestedNewPassword extends Mailable
|
||||
* RequestedNewPassword constructor.
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $ip
|
||||
* @param string $ipAddress
|
||||
*/
|
||||
public function __construct(string $url, string $ip)
|
||||
public function __construct(string $url, string $ipAddress)
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->ip = $ip;
|
||||
$this->url = $url;
|
||||
$this->ipAddress = $ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -26,14 +26,13 @@ use Watson\Validating\ValidatingTrait;
|
||||
*/
|
||||
class Transaction extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The attributes that should be casted to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts
|
||||
= [
|
||||
= [
|
||||
'created_at' => 'date',
|
||||
'updated_at' => 'date',
|
||||
'deleted_at' => 'date',
|
||||
@@ -41,17 +40,19 @@ class Transaction extends Model
|
||||
'encrypted' => 'boolean', // model does not have these fields though
|
||||
'bill_name_encrypted' => 'boolean',
|
||||
];
|
||||
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
|
||||
protected $fillable = ['account_id', 'transaction_journal_id', 'description', 'amount', 'identifier'];
|
||||
protected $hidden = ['encrypted'];
|
||||
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
|
||||
protected $fillable
|
||||
= ['account_id', 'transaction_journal_id', 'description', 'amount', 'identifier', 'transaction_currency_id', 'foreign_currency_id',
|
||||
'foreign_amount'];
|
||||
protected $hidden = ['encrypted'];
|
||||
protected $rules
|
||||
= [
|
||||
'account_id' => 'required|exists:accounts,id',
|
||||
'transaction_journal_id' => 'required|exists:transaction_journals,id',
|
||||
'description' => 'between:0,1024',
|
||||
'amount' => 'required|numeric',
|
||||
= [
|
||||
'account_id' => 'required|exists:accounts,id',
|
||||
'transaction_journal_id' => 'required|exists:transaction_journals,id',
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'description' => 'between:0,1024',
|
||||
'amount' => 'required|numeric',
|
||||
];
|
||||
use SoftDeletes, ValidatingTrait;
|
||||
|
||||
/**
|
||||
* @param Builder $query
|
||||
@@ -74,6 +75,8 @@ class Transaction extends Model
|
||||
return false;
|
||||
}
|
||||
|
||||
use SoftDeletes, ValidatingTrait;
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
@@ -98,6 +101,14 @@ class Transaction extends Model
|
||||
return $this->belongsToMany('FireflyIII\Models\Category');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function foreignCurrency()
|
||||
{
|
||||
return $this->belongsTo('FireflyIII\Models\TransactionCurrency', 'foreign_currency_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*
|
||||
@@ -160,6 +171,14 @@ class Transaction extends Model
|
||||
$this->attributes['amount'] = strval(round($value, 12));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function transactionCurrency()
|
||||
{
|
||||
return $this->belongsTo('FireflyIII\Models\TransactionCurrency');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
|
@@ -19,6 +19,7 @@ use FireflyIII\Support\CacheProperties;
|
||||
use FireflyIII\Support\Models\TransactionJournalTrait;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Log;
|
||||
@@ -66,13 +67,12 @@ class TransactionJournal extends Model
|
||||
/** @var array */
|
||||
protected $rules
|
||||
= [
|
||||
'user_id' => 'required|exists:users,id',
|
||||
'transaction_type_id' => 'required|exists:transaction_types,id',
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'description' => 'required|between:1,1024',
|
||||
'completed' => 'required|boolean',
|
||||
'date' => 'required|date',
|
||||
'encrypted' => 'required|boolean',
|
||||
'user_id' => 'required|exists:users,id',
|
||||
'transaction_type_id' => 'required|exists:transaction_types,id',
|
||||
'description' => 'required|between:1,1024',
|
||||
'completed' => 'required|boolean',
|
||||
'date' => 'required|date',
|
||||
'encrypted' => 'required|boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -115,7 +115,7 @@ class TransactionJournal extends Model
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function budgets()
|
||||
public function budgets(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany('FireflyIII\Models\Budget');
|
||||
}
|
||||
@@ -123,7 +123,7 @@ class TransactionJournal extends Model
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function categories()
|
||||
public function categories(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany('FireflyIII\Models\Category');
|
||||
}
|
||||
@@ -204,7 +204,7 @@ class TransactionJournal extends Model
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDeposit()
|
||||
public function isDeposit(): bool
|
||||
{
|
||||
if (!is_null($this->transaction_type_type)) {
|
||||
return $this->transaction_type_type == TransactionType::DEPOSIT;
|
||||
@@ -217,7 +217,7 @@ class TransactionJournal extends Model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isOpeningBalance()
|
||||
public function isOpeningBalance(): bool
|
||||
{
|
||||
if (!is_null($this->transaction_type_type)) {
|
||||
return $this->transaction_type_type == TransactionType::OPENING_BALANCE;
|
||||
@@ -230,7 +230,7 @@ class TransactionJournal extends Model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isTransfer()
|
||||
public function isTransfer(): bool
|
||||
{
|
||||
if (!is_null($this->transaction_type_type)) {
|
||||
return $this->transaction_type_type == TransactionType::TRANSFER;
|
||||
@@ -243,7 +243,7 @@ class TransactionJournal extends Model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWithdrawal()
|
||||
public function isWithdrawal(): bool
|
||||
{
|
||||
if (!is_null($this->transaction_type_type)) {
|
||||
return $this->transaction_type_type == TransactionType::WITHDRAWAL;
|
||||
@@ -255,7 +255,7 @@ class TransactionJournal extends Model
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function piggyBankEvents()
|
||||
public function piggyBankEvents(): HasMany
|
||||
{
|
||||
return $this->hasMany('FireflyIII\Models\PiggyBankEvent');
|
||||
}
|
||||
@@ -267,7 +267,7 @@ class TransactionJournal extends Model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save(array $options = [])
|
||||
public function save(array $options = []): bool
|
||||
{
|
||||
$count = $this->tags()->count();
|
||||
$this->tag_count = $count;
|
||||
@@ -299,46 +299,6 @@ class TransactionJournal extends Model
|
||||
return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 00:00:00'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EloquentBuilder $query
|
||||
*/
|
||||
public function scopeExpanded(EloquentBuilder $query)
|
||||
{
|
||||
// left join transaction type:
|
||||
if (!self::isJoined($query, 'transaction_types')) {
|
||||
$query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
|
||||
}
|
||||
|
||||
// left join transaction currency:
|
||||
$query->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transaction_journals.transaction_currency_id');
|
||||
|
||||
// extend group by:
|
||||
$query->groupBy(
|
||||
[
|
||||
'transaction_journals.id',
|
||||
'transaction_journals.created_at',
|
||||
'transaction_journals.updated_at',
|
||||
'transaction_journals.deleted_at',
|
||||
'transaction_journals.user_id',
|
||||
'transaction_journals.transaction_type_id',
|
||||
'transaction_journals.bill_id',
|
||||
'transaction_journals.transaction_currency_id',
|
||||
'transaction_journals.description',
|
||||
'transaction_journals.date',
|
||||
'transaction_journals.interest_date',
|
||||
'transaction_journals.book_date',
|
||||
'transaction_journals.process_date',
|
||||
'transaction_journals.order',
|
||||
'transaction_journals.tag_count',
|
||||
'transaction_journals.encrypted',
|
||||
'transaction_journals.completed',
|
||||
'transaction_types.type',
|
||||
'transaction_currencies.code',
|
||||
]
|
||||
);
|
||||
$query->with(['categories', 'budgets', 'attachments', 'bill', 'transactions']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EloquentBuilder $query
|
||||
*/
|
||||
@@ -445,9 +405,9 @@ class TransactionJournal extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
* @return HasMany
|
||||
*/
|
||||
public function transactions()
|
||||
public function transactions(): HasMany
|
||||
{
|
||||
return $this->hasMany('FireflyIII\Models\Transaction');
|
||||
}
|
||||
|
@@ -44,6 +44,7 @@ use FireflyIII\Support\Navigation;
|
||||
use FireflyIII\Support\Preferences;
|
||||
use FireflyIII\Support\Steam;
|
||||
use FireflyIII\Support\Twig\Account;
|
||||
use FireflyIII\Support\Twig\AmountFormat;
|
||||
use FireflyIII\Support\Twig\General;
|
||||
use FireflyIII\Support\Twig\Journal;
|
||||
use FireflyIII\Support\Twig\PiggyBank;
|
||||
@@ -51,11 +52,11 @@ use FireflyIII\Support\Twig\Rule;
|
||||
use FireflyIII\Support\Twig\Transaction;
|
||||
use FireflyIII\Support\Twig\Translation;
|
||||
use FireflyIII\Validation\FireflyValidator;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Twig;
|
||||
use TwigBridge\Extension\Loader\Functions;
|
||||
use Validator;
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
/**
|
||||
* Class FireflyServiceProvider
|
||||
@@ -79,7 +80,7 @@ class FireflyServiceProvider extends ServiceProvider
|
||||
Twig::addExtension(new Translation);
|
||||
Twig::addExtension(new Transaction);
|
||||
Twig::addExtension(new Rule);
|
||||
Twig::addExtension(new Account);
|
||||
Twig::addExtension(new AmountFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -52,6 +52,9 @@ class JournalServiceProvider extends ServiceProvider
|
||||
$this->registerCollector();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function registerCollector()
|
||||
{
|
||||
$this->app->bind(
|
||||
@@ -69,6 +72,9 @@ class JournalServiceProvider extends ServiceProvider
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function registerRepository()
|
||||
{
|
||||
$this->app->bind(
|
||||
@@ -86,6 +92,9 @@ class JournalServiceProvider extends ServiceProvider
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function registerTasker()
|
||||
{
|
||||
$this->app->bind(
|
||||
@@ -102,4 +111,5 @@ class JournalServiceProvider extends ServiceProvider
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -24,8 +24,6 @@ use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
|
||||
@@ -37,6 +35,7 @@ use Log;
|
||||
*/
|
||||
class AccountRepository implements AccountRepositoryInterface
|
||||
{
|
||||
use FindAccountsTrait;
|
||||
|
||||
/** @var User */
|
||||
private $user;
|
||||
@@ -77,179 +76,6 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accountId
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function find(int $accountId): Account
|
||||
{
|
||||
$account = $this->user->accounts()->find($accountId);
|
||||
if (is_null($account)) {
|
||||
return new Account;
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $number
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function findByAccountNumber(string $number, array $types): Account
|
||||
{
|
||||
$query = $this->user->accounts()
|
||||
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
|
||||
->where('account_meta.name', 'accountNumber')
|
||||
->where('account_meta.data', json_encode($number));
|
||||
|
||||
if (count($types) > 0) {
|
||||
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||
$query->whereIn('account_types.type', $types);
|
||||
}
|
||||
|
||||
/** @var Collection $accounts */
|
||||
$accounts = $query->get(['accounts.*']);
|
||||
if ($accounts->count() > 0) {
|
||||
return $accounts->first();
|
||||
}
|
||||
|
||||
return new Account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $iban
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function findByIban(string $iban, array $types): Account
|
||||
{
|
||||
$query = $this->user->accounts()->where('iban', '!=', '')->whereNotNull('iban');
|
||||
|
||||
if (count($types) > 0) {
|
||||
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||
$query->whereIn('account_types.type', $types);
|
||||
}
|
||||
|
||||
$accounts = $query->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->iban === $iban) {
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
|
||||
return new Account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function findByName(string $name, array $types): Account
|
||||
{
|
||||
$query = $this->user->accounts();
|
||||
|
||||
if (count($types) > 0) {
|
||||
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||
$query->whereIn('account_types.type', $types);
|
||||
|
||||
}
|
||||
Log::debug(sprintf('Searching for account named %s of the following type(s)', $name), ['types' => $types]);
|
||||
|
||||
$accounts = $query->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->name === $name) {
|
||||
Log::debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id));
|
||||
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
Log::debug('Found nothing.');
|
||||
|
||||
return new Account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $accountIds
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAccountsById(array $accountIds): Collection
|
||||
{
|
||||
/** @var Collection $result */
|
||||
$query = $this->user->accounts();
|
||||
|
||||
if (count($accountIds) > 0) {
|
||||
$query->whereIn('accounts.id', $accountIds);
|
||||
}
|
||||
|
||||
$result = $query->get(['accounts.*']);
|
||||
$result = $result->sortBy(
|
||||
function (Account $account) {
|
||||
return strtolower($account->name);
|
||||
}
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAccountsByType(array $types): Collection
|
||||
{
|
||||
/** @var Collection $result */
|
||||
$query = $this->user->accounts();
|
||||
if (count($types) > 0) {
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
|
||||
$result = $query->get(['accounts.*']);
|
||||
$result = $result->sortBy(
|
||||
function (Account $account) {
|
||||
return strtolower($account->name);
|
||||
}
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getActiveAccountsByType(array $types): Collection
|
||||
{
|
||||
/** @var Collection $result */
|
||||
$query = $this->user->accounts()->with(
|
||||
['accountmeta' => function (HasMany $query) {
|
||||
$query->where('name', 'accountRole');
|
||||
}]
|
||||
);
|
||||
if (count($types) > 0) {
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
$query->where('active', 1);
|
||||
$result = $query->get(['accounts.*']);
|
||||
$result = $result->sortBy(
|
||||
function (Account $account) {
|
||||
return strtolower($account->name);
|
||||
}
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date of the very last transaction in this account.
|
||||
*
|
||||
@@ -453,7 +279,12 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
*/
|
||||
protected function storeInitialBalance(Account $account, array $data): TransactionJournal
|
||||
{
|
||||
$amount = $data['openingBalance'];
|
||||
$amount = strval($data['openingBalance']);
|
||||
|
||||
if (bccomp($amount, '0') === 0) {
|
||||
return new TransactionJournal;
|
||||
}
|
||||
|
||||
$name = $data['name'];
|
||||
$currencyId = $data['currency_id'];
|
||||
$opposing = $this->storeOpposingAccount($name);
|
||||
@@ -474,18 +305,32 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
$firstAccount = $account;
|
||||
$secondAccount = $opposing;
|
||||
$firstAmount = $amount;
|
||||
$secondAmount = $amount * -1;
|
||||
$secondAmount = bcmul($amount, '-1');
|
||||
|
||||
if ($data['openingBalance'] < 0) {
|
||||
$firstAccount = $opposing;
|
||||
$secondAccount = $account;
|
||||
$firstAmount = $amount * -1;
|
||||
$firstAmount = bcmul($amount, '-1');
|
||||
$secondAmount = $amount;
|
||||
}
|
||||
|
||||
$one = new Transaction(['account_id' => $firstAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $firstAmount]);
|
||||
$one = new Transaction(
|
||||
[
|
||||
'account_id' => $firstAccount->id,
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'amount' => $firstAmount,
|
||||
'transaction_currency_id' => $currencyId,
|
||||
]
|
||||
);
|
||||
$one->save();// first transaction: from
|
||||
$two = new Transaction(['account_id' => $secondAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $secondAmount]);
|
||||
|
||||
$two = new Transaction(
|
||||
[
|
||||
'account_id' => $secondAccount->id,
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'amount' => $secondAmount,
|
||||
'transaction_currency_id' => $currencyId,]
|
||||
);
|
||||
$two->save(); // second transaction: to
|
||||
|
||||
Log::debug(sprintf('Stored two transactions, #%d and #%d', $one->id, $two->id));
|
||||
@@ -593,9 +438,15 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
protected function updateOpeningBalanceJournal(Account $account, TransactionJournal $journal, array $data): bool
|
||||
{
|
||||
$date = $data['openingBalanceDate'];
|
||||
$amount = $data['openingBalance'];
|
||||
$amount = strval($data['openingBalance']);
|
||||
$currencyId = intval($data['currency_id']);
|
||||
|
||||
if (bccomp($amount, '0') === 0) {
|
||||
$journal->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// update date:
|
||||
$journal->date = $date;
|
||||
$journal->transaction_currency_id = $currencyId;
|
||||
@@ -604,11 +455,13 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions()->get() as $transaction) {
|
||||
if ($account->id == $transaction->account_id) {
|
||||
$transaction->amount = $amount;
|
||||
$transaction->amount = $amount;
|
||||
$transaction->transaction_currency_id = $currencyId;
|
||||
$transaction->save();
|
||||
}
|
||||
if ($account->id != $transaction->account_id) {
|
||||
$transaction->amount = $amount * -1;
|
||||
$transaction->amount = bcmul($amount, '-1');
|
||||
$transaction->transaction_currency_id = $currencyId;
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
@@ -618,6 +471,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
@@ -625,9 +479,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
*/
|
||||
protected function validOpeningBalanceData(array $data): bool
|
||||
{
|
||||
if (isset($data['openingBalance']) && isset($data['openingBalanceDate'])
|
||||
&& bccomp(strval($data['openingBalance']), '0') !== 0
|
||||
) {
|
||||
if (isset($data['openingBalance']) && isset($data['openingBalanceDate'])) {
|
||||
Log::debug('Array has valid opening balance data.');
|
||||
|
||||
return true;
|
||||
|
@@ -98,6 +98,11 @@ interface AccountRepositoryInterface
|
||||
*/
|
||||
public function getActiveAccountsByType(array $types): Collection;
|
||||
|
||||
/**
|
||||
* @return Account
|
||||
*/
|
||||
public function getCashAccount(): Account;
|
||||
|
||||
/**
|
||||
* Returns the date of the very last transaction in this account.
|
||||
*
|
||||
|
@@ -41,11 +41,10 @@ class AccountTasker implements AccountTaskerInterface
|
||||
*/
|
||||
public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): array
|
||||
{
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
$yesterday = clone $start;
|
||||
$yesterday->subDay();
|
||||
$startSet = Steam::balancesById($ids, $yesterday);
|
||||
$endSet = Steam::balancesById($ids, $end);
|
||||
$startSet = Steam::balancesByAccounts($accounts, $yesterday);
|
||||
$endSet = Steam::balancesByAccounts($accounts, $end);
|
||||
|
||||
Log::debug('Start of accountreport');
|
||||
|
||||
|
212
app/Repositories/Account/FindAccountsTrait.php
Normal file
212
app/Repositories/Account/FindAccountsTrait.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
/**
|
||||
* FindAccountsTrait.php
|
||||
* Copyright (c) 2017 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\Repositories\Account;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Trait FindAccountsTrait
|
||||
*
|
||||
* @package FireflyIII\Repositories\Account
|
||||
*/
|
||||
trait FindAccountsTrait
|
||||
{
|
||||
/**
|
||||
* @param $accountId
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function find(int $accountId): Account
|
||||
{
|
||||
$account = $this->user->accounts()->find($accountId);
|
||||
if (is_null($account)) {
|
||||
return new Account;
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $number
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function findByAccountNumber(string $number, array $types): Account
|
||||
{
|
||||
$query = $this->user->accounts()
|
||||
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
|
||||
->where('account_meta.name', 'accountNumber')
|
||||
->where('account_meta.data', json_encode($number));
|
||||
|
||||
if (count($types) > 0) {
|
||||
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||
$query->whereIn('account_types.type', $types);
|
||||
}
|
||||
|
||||
/** @var Collection $accounts */
|
||||
$accounts = $query->get(['accounts.*']);
|
||||
if ($accounts->count() > 0) {
|
||||
return $accounts->first();
|
||||
}
|
||||
|
||||
return new Account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $iban
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function findByIban(string $iban, array $types): Account
|
||||
{
|
||||
$query = $this->user->accounts()->where('iban', '!=', '')->whereNotNull('iban');
|
||||
|
||||
if (count($types) > 0) {
|
||||
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||
$query->whereIn('account_types.type', $types);
|
||||
}
|
||||
|
||||
$accounts = $query->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->iban === $iban) {
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
|
||||
return new Account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function findByName(string $name, array $types): Account
|
||||
{
|
||||
$query = $this->user->accounts();
|
||||
|
||||
if (count($types) > 0) {
|
||||
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||
$query->whereIn('account_types.type', $types);
|
||||
|
||||
}
|
||||
Log::debug(sprintf('Searching for account named %s of the following type(s)', $name), ['types' => $types]);
|
||||
|
||||
$accounts = $query->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->name === $name) {
|
||||
Log::debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id));
|
||||
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
Log::debug('Found nothing.');
|
||||
|
||||
return new Account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $accountIds
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAccountsById(array $accountIds): Collection
|
||||
{
|
||||
/** @var Collection $result */
|
||||
$query = $this->user->accounts();
|
||||
|
||||
if (count($accountIds) > 0) {
|
||||
$query->whereIn('accounts.id', $accountIds);
|
||||
}
|
||||
|
||||
$result = $query->get(['accounts.*']);
|
||||
$result = $result->sortBy(
|
||||
function (Account $account) {
|
||||
return strtolower($account->name);
|
||||
}
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAccountsByType(array $types): Collection
|
||||
{
|
||||
/** @var Collection $result */
|
||||
$query = $this->user->accounts();
|
||||
if (count($types) > 0) {
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
|
||||
$result = $query->get(['accounts.*']);
|
||||
$result = $result->sortBy(
|
||||
function (Account $account) {
|
||||
return strtolower($account->name);
|
||||
}
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getActiveAccountsByType(array $types): Collection
|
||||
{
|
||||
/** @var Collection $result */
|
||||
$query = $this->user->accounts()->with(
|
||||
['accountmeta' => function (HasMany $query) {
|
||||
$query->where('name', 'accountRole');
|
||||
}]
|
||||
);
|
||||
if (count($types) > 0) {
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
$query->where('active', 1);
|
||||
$result = $query->get(['accounts.*']);
|
||||
$result = $result->sortBy(
|
||||
function (Account $account) {
|
||||
return strtolower($account->name);
|
||||
}
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account
|
||||
*/
|
||||
public function getCashAccount(): Account
|
||||
{
|
||||
$type = AccountType::where('type', AccountType::CASH)->first();
|
||||
$account = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $this->user->id, 'account_type_id' => $type->id, 'name' => 'Cash account', 'active' => 1]
|
||||
);
|
||||
|
||||
return $account;
|
||||
}
|
||||
}
|
186
app/Repositories/Journal/CreateJournalsTrait.php
Normal file
186
app/Repositories/Journal/CreateJournalsTrait.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
/**
|
||||
* CreateJournalsTrait.php
|
||||
* Copyright (c) 2017 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\Repositories\Journal;
|
||||
|
||||
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Trait CreateJournalsTrait
|
||||
*
|
||||
* @package FireflyIII\Repositories\Journal
|
||||
*/
|
||||
trait CreateJournalsTrait
|
||||
{
|
||||
/**
|
||||
*
|
||||
* * Remember: a balancingAct takes at most one expense and one transfer.
|
||||
* an advancePayment takes at most one expense, infinite deposits and NO transfers.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function saveTags(TransactionJournal $journal, array $array): bool
|
||||
{
|
||||
/** @var TagRepositoryInterface $tagRepository */
|
||||
$tagRepository = app(TagRepositoryInterface::class);
|
||||
|
||||
foreach ($array as $name) {
|
||||
if (strlen(trim($name)) > 0) {
|
||||
$tag = Tag::firstOrCreateEncrypted(['tag' => $name, 'user_id' => $journal->user_id]);
|
||||
if (!is_null($tag)) {
|
||||
Log::debug(sprintf('Will try to connect tag #%d to journal #%d.', $tag->id, $journal->id));
|
||||
$tagRepository->connect($journal, $tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param int $budgetId
|
||||
*/
|
||||
protected function storeBudgetWithTransaction(Transaction $transaction, int $budgetId)
|
||||
{
|
||||
if (intval($budgetId) > 0 && $transaction->transactionJournal->transactionType->type !== TransactionType::TRANSFER) {
|
||||
/** @var \FireflyIII\Models\Budget $budget */
|
||||
$budget = Budget::find($budgetId);
|
||||
$transaction->budgets()->save($budget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param string $category
|
||||
*/
|
||||
protected function storeCategoryWithTransaction(Transaction $transaction, string $category)
|
||||
{
|
||||
if (strlen($category) > 0) {
|
||||
$category = Category::firstOrCreateEncrypted(['name' => $category, 'user_id' => $transaction->transactionJournal->user_id]);
|
||||
$transaction->categories()->save($category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The reference to storeAccounts() in this function is an indication of spagetti code but alas,
|
||||
* I leave it as it is.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $transaction
|
||||
* @param int $identifier
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
protected function storeSplitTransaction(TransactionJournal $journal, array $transaction, int $identifier): Collection
|
||||
{
|
||||
// store source and destination accounts (depends on type)
|
||||
$accounts = $this->storeAccounts($this->user, $journal->transactionType, $transaction);
|
||||
|
||||
// store transaction one way:
|
||||
$amount = bcmul(strval($transaction['amount']), '-1');
|
||||
$foreignAmount = is_null($transaction['foreign_amount']) ? null : bcmul(strval($transaction['foreign_amount']), '-1');
|
||||
$one = $this->storeTransaction(
|
||||
[
|
||||
'journal' => $journal,
|
||||
'account' => $accounts['source'],
|
||||
'amount' => $amount,
|
||||
'transaction_currency_id' => $transaction['transaction_currency_id'],
|
||||
'foreign_amount' => $foreignAmount,
|
||||
'foreign_currency_id' => $transaction['foreign_currency_id'],
|
||||
'description' => $transaction['description'],
|
||||
'category' => null,
|
||||
'budget' => null,
|
||||
'identifier' => $identifier,
|
||||
]
|
||||
);
|
||||
$this->storeCategoryWithTransaction($one, $transaction['category']);
|
||||
$this->storeBudgetWithTransaction($one, $transaction['budget_id']);
|
||||
|
||||
// and the other way:
|
||||
$amount = strval($transaction['amount']);
|
||||
$foreignAmount = is_null($transaction['foreign_amount']) ? null : strval($transaction['foreign_amount']);
|
||||
$two = $this->storeTransaction(
|
||||
[
|
||||
'journal' => $journal,
|
||||
'account' => $accounts['destination'],
|
||||
'amount' => $amount,
|
||||
'transaction_currency_id' => $transaction['transaction_currency_id'],
|
||||
'foreign_amount' => $foreignAmount,
|
||||
'foreign_currency_id' => $transaction['foreign_currency_id'],
|
||||
'description' => $transaction['description'],
|
||||
'category' => null,
|
||||
'budget' => null,
|
||||
'identifier' => $identifier,
|
||||
]
|
||||
);
|
||||
$this->storeCategoryWithTransaction($two, $transaction['category']);
|
||||
$this->storeBudgetWithTransaction($two, $transaction['budget_id']);
|
||||
|
||||
return new Collection([$one, $two]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
protected function storeTransaction(array $data): Transaction
|
||||
{
|
||||
$fields = [
|
||||
'transaction_journal_id' => $data['journal']->id,
|
||||
'account_id' => $data['account']->id,
|
||||
'amount' => $data['amount'],
|
||||
'foreign_amount' => $data['foreign_amount'],
|
||||
'transaction_currency_id' => $data['transaction_currency_id'],
|
||||
'foreign_currency_id' => $data['foreign_currency_id'],
|
||||
'description' => $data['description'],
|
||||
'identifier' => $data['identifier'],
|
||||
];
|
||||
|
||||
|
||||
if (is_null($data['foreign_currency_id'])) {
|
||||
unset($fields['foreign_currency_id']);
|
||||
}
|
||||
if (is_null($data['foreign_amount'])) {
|
||||
unset($fields['foreign_amount']);
|
||||
}
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = Transaction::create($fields);
|
||||
|
||||
Log::debug(sprintf('Transaction stored with ID: %s', $transaction->id));
|
||||
|
||||
if (!is_null($data['category'])) {
|
||||
$transaction->categories()->save($data['category']);
|
||||
}
|
||||
|
||||
if (!is_null($data['budget'])) {
|
||||
$transaction->categories()->save($data['budget']);
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -13,17 +13,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Repositories\Journal;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\MessageBag;
|
||||
@@ -37,14 +29,12 @@ use Preferences;
|
||||
*/
|
||||
class JournalRepository implements JournalRepositoryInterface
|
||||
{
|
||||
use CreateJournalsTrait, UpdateJournalsTrait, SupportJournalsTrait;
|
||||
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/** @var array */
|
||||
private $validMetaFields
|
||||
= ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date', 'internal_reference', 'notes', 'foreign_amount',
|
||||
'foreign_currency_id',
|
||||
];
|
||||
private $validMetaFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date', 'internal_reference', 'notes'];
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
@@ -180,15 +170,14 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
// find transaction type.
|
||||
/** @var TransactionType $transactionType */
|
||||
$transactionType = TransactionType::where('type', ucfirst($data['what']))->first();
|
||||
$accounts = $this->storeAccounts($transactionType, $data);
|
||||
$accounts = $this->storeAccounts($this->user, $transactionType, $data);
|
||||
$data = $this->verifyNativeAmount($data, $accounts);
|
||||
$currencyId = $data['currency_id'];
|
||||
$amount = strval($data['amount']);
|
||||
$journal = new TransactionJournal(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'transaction_type_id' => $transactionType->id,
|
||||
'transaction_currency_id' => $currencyId,
|
||||
'transaction_currency_id' => $data['currency_id'], // no longer used.
|
||||
'description' => $data['description'],
|
||||
'completed' => 0,
|
||||
'date' => $data['date'],
|
||||
@@ -200,27 +189,32 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
$this->storeCategoryWithJournal($journal, $data['category']);
|
||||
$this->storeBudgetWithJournal($journal, $data['budget_id']);
|
||||
|
||||
|
||||
// store two transactions:
|
||||
$one = [
|
||||
'journal' => $journal,
|
||||
'account' => $accounts['source'],
|
||||
'amount' => bcmul($amount, '-1'),
|
||||
'description' => null,
|
||||
'category' => null,
|
||||
'budget' => null,
|
||||
'identifier' => 0,
|
||||
'journal' => $journal,
|
||||
'account' => $accounts['source'],
|
||||
'amount' => bcmul($amount, '-1'),
|
||||
'transaction_currency_id' => $data['currency_id'],
|
||||
'foreign_amount' => is_null($data['foreign_amount']) ? null : bcmul(strval($data['foreign_amount']), '-1'),
|
||||
'foreign_currency_id' => $data['foreign_currency_id'],
|
||||
'description' => null,
|
||||
'category' => null,
|
||||
'budget' => null,
|
||||
'identifier' => 0,
|
||||
];
|
||||
$this->storeTransaction($one);
|
||||
|
||||
$two = [
|
||||
'journal' => $journal,
|
||||
'account' => $accounts['destination'],
|
||||
'amount' => $amount,
|
||||
'description' => null,
|
||||
'category' => null,
|
||||
'budget' => null,
|
||||
'identifier' => 0,
|
||||
'journal' => $journal,
|
||||
'account' => $accounts['destination'],
|
||||
'amount' => $amount,
|
||||
'transaction_currency_id' => $data['currency_id'],
|
||||
'foreign_amount' => $data['foreign_amount'],
|
||||
'foreign_currency_id' => $data['foreign_currency_id'],
|
||||
'description' => null,
|
||||
'category' => null,
|
||||
'budget' => null,
|
||||
'identifier' => 0,
|
||||
];
|
||||
|
||||
$this->storeTransaction($two);
|
||||
@@ -256,20 +250,12 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
{
|
||||
|
||||
// update actual journal:
|
||||
$journal->description = $data['description'];
|
||||
$journal->date = $data['date'];
|
||||
$accounts = $this->storeAccounts($journal->transactionType, $data);
|
||||
$amount = strval($data['amount']);
|
||||
|
||||
if ($data['currency_id'] !== $journal->transaction_currency_id) {
|
||||
// user has entered amount in foreign currency.
|
||||
// amount in "our" currency is $data['exchanged_amount']:
|
||||
$amount = strval($data['exchanged_amount']);
|
||||
// other values must be stored as well:
|
||||
$data['original_amount'] = $data['amount'];
|
||||
$data['original_currency_id'] = $data['currency_id'];
|
||||
|
||||
}
|
||||
$journal->description = $data['description'];
|
||||
$journal->date = $data['date'];
|
||||
$accounts = $this->storeAccounts($this->user, $journal->transactionType, $data);
|
||||
$data = $this->verifyNativeAmount($data, $accounts);
|
||||
$data['amount'] = strval($data['amount']);
|
||||
$data['foreign_amount'] = is_null($data['foreign_amount']) ? null : strval($data['foreign_amount']);
|
||||
|
||||
// unlink all categories, recreate them:
|
||||
$journal->categories()->detach();
|
||||
@@ -278,9 +264,11 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
$this->storeCategoryWithJournal($journal, $data['category']);
|
||||
$this->storeBudgetWithJournal($journal, $data['budget_id']);
|
||||
|
||||
// negative because source loses money.
|
||||
$this->updateSourceTransaction($journal, $accounts['source'], $data);
|
||||
|
||||
$this->updateSourceTransaction($journal, $accounts['source'], bcmul($amount, '-1')); // negative because source loses money.
|
||||
$this->updateDestinationTransaction($journal, $accounts['destination'], $amount); // positive because destination gets money.
|
||||
// positive because destination gets money.
|
||||
$this->updateDestinationTransaction($journal, $accounts['destination'], $data);
|
||||
|
||||
$journal->save();
|
||||
|
||||
@@ -317,9 +305,8 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
public function updateSplitJournal(TransactionJournal $journal, array $data): TransactionJournal
|
||||
{
|
||||
// update actual journal:
|
||||
$journal->transaction_currency_id = $data['currency_id'];
|
||||
$journal->description = $data['journal_description'];
|
||||
$journal->date = $data['date'];
|
||||
$journal->description = $data['journal_description'];
|
||||
$journal->date = $data['date'];
|
||||
$journal->save();
|
||||
Log::debug(sprintf('Updated split journal #%d', $journal->id));
|
||||
|
||||
@@ -339,7 +326,6 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update tags:
|
||||
if (isset($data['tags']) && is_array($data['tags'])) {
|
||||
$this->updateTags($journal, $data['tags']);
|
||||
@@ -351,6 +337,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);
|
||||
@@ -362,466 +349,4 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the user edits a split journal, each line is missing crucial data:
|
||||
*
|
||||
* - Withdrawal lines are missing the source account ID
|
||||
* - Deposit lines are missing the destination account ID
|
||||
* - Transfers are missing both.
|
||||
*
|
||||
* We need to append the array.
|
||||
*
|
||||
* @param array $transaction
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function appendTransactionData(array $transaction, array $data): array
|
||||
{
|
||||
switch ($data['what']) {
|
||||
case strtolower(TransactionType::TRANSFER):
|
||||
case strtolower(TransactionType::WITHDRAWAL):
|
||||
$transaction['source_account_id'] = intval($data['journal_source_account_id']);
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($data['what']) {
|
||||
case strtolower(TransactionType::TRANSFER):
|
||||
case strtolower(TransactionType::DEPOSIT):
|
||||
$transaction['destination_account_id'] = intval($data['journal_destination_account_id']);
|
||||
break;
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* * Remember: a balancingAct takes at most one expense and one transfer.
|
||||
* an advancePayment takes at most one expense, infinite deposits and NO transfers.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function saveTags(TransactionJournal $journal, array $array): bool
|
||||
{
|
||||
/** @var TagRepositoryInterface $tagRepository */
|
||||
$tagRepository = app(TagRepositoryInterface::class);
|
||||
|
||||
foreach ($array as $name) {
|
||||
if (strlen(trim($name)) > 0) {
|
||||
$tag = Tag::firstOrCreateEncrypted(['tag' => $name, 'user_id' => $journal->user_id]);
|
||||
if (!is_null($tag)) {
|
||||
Log::debug(sprintf('Will try to connect tag #%d to journal #%d.', $tag->id, $journal->id));
|
||||
$tagRepository->connect($journal, $tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionType $type
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function storeAccounts(TransactionType $type, array $data): array
|
||||
{
|
||||
$accounts = [
|
||||
'source' => null,
|
||||
'destination' => null,
|
||||
];
|
||||
|
||||
Log::debug(sprintf('Going to store accounts for type %s', $type->type));
|
||||
switch ($type->type) {
|
||||
case TransactionType::WITHDRAWAL:
|
||||
$accounts = $this->storeWithdrawalAccounts($data);
|
||||
break;
|
||||
|
||||
case TransactionType::DEPOSIT:
|
||||
$accounts = $this->storeDepositAccounts($data);
|
||||
|
||||
break;
|
||||
case TransactionType::TRANSFER:
|
||||
$accounts['source'] = Account::where('user_id', $this->user->id)->where('id', $data['source_account_id'])->first();
|
||||
$accounts['destination'] = Account::where('user_id', $this->user->id)->where('id', $data['destination_account_id'])->first();
|
||||
break;
|
||||
default:
|
||||
throw new FireflyException(sprintf('Did not recognise transaction type "%s".', $type->type));
|
||||
}
|
||||
|
||||
if (is_null($accounts['source'])) {
|
||||
Log::error('"source"-account is null, so we cannot continue!', ['data' => $data]);
|
||||
throw new FireflyException('"source"-account is null, so we cannot continue!');
|
||||
}
|
||||
|
||||
if (is_null($accounts['destination'])) {
|
||||
Log::error('"destination"-account is null, so we cannot continue!', ['data' => $data]);
|
||||
throw new FireflyException('"destination"-account is null, so we cannot continue!');
|
||||
|
||||
}
|
||||
|
||||
|
||||
return $accounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param int $budgetId
|
||||
*/
|
||||
private function storeBudgetWithJournal(TransactionJournal $journal, int $budgetId)
|
||||
{
|
||||
if (intval($budgetId) > 0 && $journal->transactionType->type === TransactionType::WITHDRAWAL) {
|
||||
/** @var \FireflyIII\Models\Budget $budget */
|
||||
$budget = Budget::find($budgetId);
|
||||
$journal->budgets()->save($budget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param int $budgetId
|
||||
*/
|
||||
private function storeBudgetWithTransaction(Transaction $transaction, int $budgetId)
|
||||
{
|
||||
if (intval($budgetId) > 0 && $transaction->transactionJournal->transactionType->type !== TransactionType::TRANSFER) {
|
||||
/** @var \FireflyIII\Models\Budget $budget */
|
||||
$budget = Budget::find($budgetId);
|
||||
$transaction->budgets()->save($budget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $category
|
||||
*/
|
||||
private function storeCategoryWithJournal(TransactionJournal $journal, string $category)
|
||||
{
|
||||
if (strlen($category) > 0) {
|
||||
$category = Category::firstOrCreateEncrypted(['name' => $category, 'user_id' => $journal->user_id]);
|
||||
$journal->categories()->save($category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param string $category
|
||||
*/
|
||||
private function storeCategoryWithTransaction(Transaction $transaction, string $category)
|
||||
{
|
||||
if (strlen($category) > 0) {
|
||||
$category = Category::firstOrCreateEncrypted(['name' => $category, 'user_id' => $transaction->transactionJournal->user_id]);
|
||||
$transaction->categories()->save($category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function storeDepositAccounts(array $data): array
|
||||
{
|
||||
Log::debug('Now in storeDepositAccounts().');
|
||||
$destinationAccount = Account::where('user_id', $this->user->id)->where('id', $data['destination_account_id'])->first(['accounts.*']);
|
||||
|
||||
Log::debug(sprintf('Destination account is #%d ("%s")', $destinationAccount->id, $destinationAccount->name));
|
||||
|
||||
if (strlen($data['source_account_name']) > 0) {
|
||||
$sourceType = AccountType::where('type', 'Revenue account')->first();
|
||||
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name'], 'active' => 1]
|
||||
);
|
||||
|
||||
Log::debug(sprintf('source account name is "%s", account is %d', $data['source_account_name'], $sourceAccount->id));
|
||||
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
}
|
||||
|
||||
Log::debug('source_account_name is empty, so default to cash account!');
|
||||
|
||||
$sourceType = AccountType::where('type', AccountType::CASH)->first();
|
||||
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
);
|
||||
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $transaction
|
||||
* @param int $identifier
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function storeSplitTransaction(TransactionJournal $journal, array $transaction, int $identifier): Collection
|
||||
{
|
||||
// store source and destination accounts (depends on type)
|
||||
$accounts = $this->storeAccounts($journal->transactionType, $transaction);
|
||||
|
||||
// store transaction one way:
|
||||
$one = $this->storeTransaction(
|
||||
[
|
||||
'journal' => $journal,
|
||||
'account' => $accounts['source'],
|
||||
'amount' => bcmul(strval($transaction['amount']), '-1'),
|
||||
'description' => $transaction['description'],
|
||||
'category' => null,
|
||||
'budget' => null,
|
||||
'identifier' => $identifier,
|
||||
]
|
||||
);
|
||||
$this->storeCategoryWithTransaction($one, $transaction['category']);
|
||||
$this->storeBudgetWithTransaction($one, $transaction['budget_id']);
|
||||
|
||||
// and the other way:
|
||||
$two = $this->storeTransaction(
|
||||
[
|
||||
'journal' => $journal,
|
||||
'account' => $accounts['destination'],
|
||||
'amount' => strval($transaction['amount']),
|
||||
'description' => $transaction['description'],
|
||||
'category' => null,
|
||||
'budget' => null,
|
||||
'identifier' => $identifier,
|
||||
]
|
||||
);
|
||||
$this->storeCategoryWithTransaction($two, $transaction['category']);
|
||||
$this->storeBudgetWithTransaction($two, $transaction['budget_id']);
|
||||
|
||||
return new Collection([$one, $two]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
private function storeTransaction(array $data): Transaction
|
||||
{
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $data['journal']->id,
|
||||
'account_id' => $data['account']->id,
|
||||
'amount' => $data['amount'],
|
||||
'description' => $data['description'],
|
||||
'identifier' => $data['identifier'],
|
||||
]
|
||||
);
|
||||
|
||||
Log::debug(sprintf('Transaction stored with ID: %s', $transaction->id));
|
||||
|
||||
if (!is_null($data['category'])) {
|
||||
$transaction->categories()->save($data['category']);
|
||||
}
|
||||
|
||||
if (!is_null($data['budget'])) {
|
||||
$transaction->categories()->save($data['budget']);
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function storeWithdrawalAccounts(array $data): array
|
||||
{
|
||||
Log::debug('Now in storeWithdrawalAccounts().');
|
||||
$sourceAccount = Account::where('user_id', $this->user->id)->where('id', $data['source_account_id'])->first(['accounts.*']);
|
||||
|
||||
Log::debug(sprintf('Source account is #%d ("%s")', $sourceAccount->id, $sourceAccount->name));
|
||||
|
||||
if (strlen($data['destination_account_name']) > 0) {
|
||||
$destinationType = AccountType::where('type', AccountType::EXPENSE)->first();
|
||||
$destinationAccount = Account::firstOrCreateEncrypted(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'account_type_id' => $destinationType->id,
|
||||
'name' => $data['destination_account_name'],
|
||||
'active' => 1,
|
||||
]
|
||||
);
|
||||
|
||||
Log::debug(sprintf('destination account name is "%s", account is %d', $data['destination_account_name'], $destinationAccount->id));
|
||||
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
}
|
||||
Log::debug('destination_account_name is empty, so default to cash account!');
|
||||
$destinationType = AccountType::where('type', AccountType::CASH)->first();
|
||||
$destinationAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $this->user->id, 'account_type_id' => $destinationType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
);
|
||||
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Account $account
|
||||
* @param string $amount
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function updateDestinationTransaction(TransactionJournal $journal, Account $account, string $amount)
|
||||
{
|
||||
// should be one:
|
||||
$set = $journal->transactions()->where('amount', '>', 0)->get();
|
||||
if ($set->count() != 1) {
|
||||
throw new FireflyException(
|
||||
sprintf('Journal #%d has an unexpected (%d) amount of transactions with an amount more than zero.', $journal->id, $set->count())
|
||||
);
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $set->first();
|
||||
$transaction->amount = $amount;
|
||||
$transaction->account_id = $account->id;
|
||||
$transaction->save();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Account $account
|
||||
* @param string $amount
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function updateSourceTransaction(TransactionJournal $journal, Account $account, string $amount)
|
||||
{
|
||||
// should be one:
|
||||
$set = $journal->transactions()->where('amount', '<', 0)->get();
|
||||
if ($set->count() != 1) {
|
||||
throw new FireflyException(
|
||||
sprintf('Journal #%d has an unexpected (%d) amount of transactions with an amount less than zero.', $journal->id, $set->count())
|
||||
);
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $set->first();
|
||||
$transaction->amount = $amount;
|
||||
$transaction->account_id = $account->id;
|
||||
$transaction->save();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function updateTags(TransactionJournal $journal, array $array): bool
|
||||
{
|
||||
// create tag repository
|
||||
/** @var TagRepositoryInterface $tagRepository */
|
||||
$tagRepository = app(TagRepositoryInterface::class);
|
||||
|
||||
|
||||
// find or create all tags:
|
||||
$tags = [];
|
||||
$ids = [];
|
||||
foreach ($array as $name) {
|
||||
if (strlen(trim($name)) > 0) {
|
||||
$tag = Tag::firstOrCreateEncrypted(['tag' => $name, 'user_id' => $journal->user_id]);
|
||||
$tags[] = $tag;
|
||||
$ids[] = $tag->id;
|
||||
}
|
||||
}
|
||||
|
||||
// delete all tags connected to journal not in this array:
|
||||
if (count($ids) > 0) {
|
||||
DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->whereNotIn('tag_id', $ids)->delete();
|
||||
}
|
||||
// if count is zero, delete them all:
|
||||
if (count($ids) == 0) {
|
||||
DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->delete();
|
||||
}
|
||||
|
||||
// connect each tag to journal (if not yet connected):
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
Log::debug(sprintf('Will try to connect tag #%d to journal #%d.', $tag->id, $journal->id));
|
||||
$tagRepository->connect($journal, $tag);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks the data array and the given accounts to verify that the native amount, currency
|
||||
* and possible the foreign currency and amount are properly saved.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $accounts
|
||||
*
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function verifyNativeAmount(array $data, array $accounts): array
|
||||
{
|
||||
/** @var TransactionType $transactionType */
|
||||
$transactionType = TransactionType::where('type', ucfirst($data['what']))->first();
|
||||
$submittedCurrencyId = $data['currency_id'];
|
||||
|
||||
// which account to check for what the native currency is?
|
||||
$check = 'source';
|
||||
if ($transactionType->type === TransactionType::DEPOSIT) {
|
||||
$check = 'destination';
|
||||
}
|
||||
switch ($transactionType->type) {
|
||||
case TransactionType::DEPOSIT:
|
||||
case TransactionType::WITHDRAWAL:
|
||||
// continue:
|
||||
$nativeCurrencyId = intval($accounts[$check]->getMeta('currency_id'));
|
||||
|
||||
// does not match? Then user has submitted amount in a foreign currency:
|
||||
if ($nativeCurrencyId !== $submittedCurrencyId) {
|
||||
// store amount and submitted currency in "foreign currency" fields:
|
||||
$data['foreign_amount'] = $data['amount'];
|
||||
$data['foreign_currency_id'] = $submittedCurrencyId;
|
||||
|
||||
// overrule the amount and currency ID fields to be the original again:
|
||||
$data['amount'] = strval($data['native_amount']);
|
||||
$data['currency_id'] = $nativeCurrencyId;
|
||||
}
|
||||
break;
|
||||
case TransactionType::TRANSFER:
|
||||
// source gets the original amount.
|
||||
$data['amount'] = strval($data['source_amount']);
|
||||
$data['currency_id'] = intval($accounts['source']->getMeta('currency_id'));
|
||||
$data['foreign_amount'] = strval($data['destination_amount']);
|
||||
$data['foreign_currency_id'] = intval($accounts['destination']->getMeta('currency_id'));
|
||||
break;
|
||||
default:
|
||||
throw new FireflyException(sprintf('Cannot handle %s in verifyNativeAmount()', $transactionType->type));
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@@ -81,6 +81,8 @@ class JournalTasker implements JournalTaskerInterface
|
||||
->leftJoin('account_types as source_account_types', 'source_accounts.account_type_id', '=', 'source_account_types.id')
|
||||
->leftJoin('accounts as destination_accounts', 'destination.account_id', '=', 'destination_accounts.id')
|
||||
->leftJoin('account_types as destination_account_types', 'destination_accounts.account_type_id', '=', 'destination_account_types.id')
|
||||
->leftJoin('transaction_currencies as native_currencies', 'transactions.transaction_currency_id', '=', 'native_currencies.id')
|
||||
->leftJoin('transaction_currencies as foreign_currencies', 'transactions.foreign_currency_id', '=', 'foreign_currencies.id')
|
||||
->where('transactions.amount', '<', 0)
|
||||
->whereNull('transactions.deleted_at')
|
||||
->get(
|
||||
@@ -91,12 +93,23 @@ class JournalTasker implements JournalTaskerInterface
|
||||
'source_accounts.encrypted as account_encrypted',
|
||||
'source_account_types.type as account_type',
|
||||
'transactions.amount',
|
||||
'transactions.foreign_amount',
|
||||
'transactions.description',
|
||||
'destination.id as destination_id',
|
||||
'destination.account_id as destination_account_id',
|
||||
'destination_accounts.name as destination_account_name',
|
||||
'destination_accounts.encrypted as destination_account_encrypted',
|
||||
'destination_account_types.type as destination_account_type',
|
||||
'native_currencies.id as transaction_currency_id',
|
||||
'native_currencies.decimal_places as transaction_currency_dp',
|
||||
'native_currencies.code as transaction_currency_code',
|
||||
'native_currencies.symbol as transaction_currency_symbol',
|
||||
|
||||
'foreign_currencies.id as foreign_currency_id',
|
||||
'foreign_currencies.decimal_places as foreign_currency_dp',
|
||||
'foreign_currencies.code as foreign_currency_code',
|
||||
'foreign_currencies.symbol as foreign_currency_symbol',
|
||||
|
||||
]
|
||||
);
|
||||
|
||||
@@ -109,23 +122,33 @@ class JournalTasker implements JournalTaskerInterface
|
||||
$budget = $entry->budgets->first();
|
||||
$category = $entry->categories->first();
|
||||
$transaction = [
|
||||
'source_id' => $entry->id,
|
||||
'source_amount' => $entry->amount,
|
||||
'description' => $entry->description,
|
||||
'source_account_id' => $entry->account_id,
|
||||
'source_account_name' => Steam::decrypt(intval($entry->account_encrypted), $entry->account_name),
|
||||
'source_account_type' => $entry->account_type,
|
||||
'source_account_before' => $sourceBalance,
|
||||
'source_account_after' => bcadd($sourceBalance, $entry->amount),
|
||||
'destination_id' => $entry->destination_id,
|
||||
'destination_amount' => bcmul($entry->amount, '-1'),
|
||||
'destination_account_id' => $entry->destination_account_id,
|
||||
'destination_account_type' => $entry->destination_account_type,
|
||||
'destination_account_name' => Steam::decrypt(intval($entry->destination_account_encrypted), $entry->destination_account_name),
|
||||
'destination_account_before' => $destinationBalance,
|
||||
'destination_account_after' => bcadd($destinationBalance, bcmul($entry->amount, '-1')),
|
||||
'budget_id' => is_null($budget) ? 0 : $budget->id,
|
||||
'category' => is_null($category) ? '' : $category->name,
|
||||
'source_id' => $entry->id,
|
||||
'source_amount' => $entry->amount,
|
||||
'foreign_source_amount' => $entry->foreign_amount,
|
||||
'description' => $entry->description,
|
||||
'source_account_id' => $entry->account_id,
|
||||
'source_account_name' => Steam::decrypt(intval($entry->account_encrypted), $entry->account_name),
|
||||
'source_account_type' => $entry->account_type,
|
||||
'source_account_before' => $sourceBalance,
|
||||
'source_account_after' => bcadd($sourceBalance, $entry->amount),
|
||||
'destination_id' => $entry->destination_id,
|
||||
'destination_amount' => bcmul($entry->amount, '-1'),
|
||||
'foreign_destination_amount' => is_null($entry->foreign_amount) ? null : bcmul($entry->foreign_amount, '-1'),
|
||||
'destination_account_id' => $entry->destination_account_id,
|
||||
'destination_account_type' => $entry->destination_account_type,
|
||||
'destination_account_name' => Steam::decrypt(intval($entry->destination_account_encrypted), $entry->destination_account_name),
|
||||
'destination_account_before' => $destinationBalance,
|
||||
'destination_account_after' => bcadd($destinationBalance, bcmul($entry->amount, '-1')),
|
||||
'budget_id' => is_null($budget) ? 0 : $budget->id,
|
||||
'category' => is_null($category) ? '' : $category->name,
|
||||
'transaction_currency_id' => $entry->transaction_currency_id,
|
||||
'transaction_currency_code' => $entry->transaction_currency_code,
|
||||
'transaction_currency_symbol' => $entry->transaction_currency_symbol,
|
||||
'transaction_currency_dp' => $entry->transaction_currency_dp,
|
||||
'foreign_currency_id' => $entry->foreign_currency_id,
|
||||
'foreign_currency_code' => $entry->foreign_currency_code,
|
||||
'foreign_currency_symbol' => $entry->foreign_currency_symbol,
|
||||
'foreign_currency_dp' => $entry->foreign_currency_dp,
|
||||
];
|
||||
if ($entry->destination_account_type === AccountType::CASH) {
|
||||
$transaction['destination_account_name'] = '';
|
||||
|
246
app/Repositories/Journal/SupportJournalsTrait.php
Normal file
246
app/Repositories/Journal/SupportJournalsTrait.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
/**
|
||||
* SupportJournalsTrait.php
|
||||
* Copyright (c) 2017 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\Repositories\Journal;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\User;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Trait SupportJournalsTrait
|
||||
*
|
||||
* @package FireflyIII\Repositories\Journal
|
||||
*/
|
||||
trait SupportJournalsTrait
|
||||
{
|
||||
/**
|
||||
* @param User $user
|
||||
* @param TransactionType $type
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function storeAccounts(User $user, TransactionType $type, array $data): array
|
||||
{
|
||||
$accounts = [
|
||||
'source' => null,
|
||||
'destination' => null,
|
||||
];
|
||||
|
||||
Log::debug(sprintf('Going to store accounts for type %s', $type->type));
|
||||
switch ($type->type) {
|
||||
case TransactionType::WITHDRAWAL:
|
||||
$accounts = $this->storeWithdrawalAccounts($user, $data);
|
||||
break;
|
||||
|
||||
case TransactionType::DEPOSIT:
|
||||
$accounts = $this->storeDepositAccounts($user, $data);
|
||||
|
||||
break;
|
||||
case TransactionType::TRANSFER:
|
||||
$accounts['source'] = Account::where('user_id', $user->id)->where('id', $data['source_account_id'])->first();
|
||||
$accounts['destination'] = Account::where('user_id', $user->id)->where('id', $data['destination_account_id'])->first();
|
||||
break;
|
||||
default:
|
||||
throw new FireflyException(sprintf('Did not recognise transaction type "%s".', $type->type));
|
||||
}
|
||||
|
||||
if (is_null($accounts['source'])) {
|
||||
Log::error('"source"-account is null, so we cannot continue!', ['data' => $data]);
|
||||
throw new FireflyException('"source"-account is null, so we cannot continue!');
|
||||
}
|
||||
|
||||
if (is_null($accounts['destination'])) {
|
||||
Log::error('"destination"-account is null, so we cannot continue!', ['data' => $data]);
|
||||
throw new FireflyException('"destination"-account is null, so we cannot continue!');
|
||||
|
||||
}
|
||||
|
||||
|
||||
return $accounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param int $budgetId
|
||||
*/
|
||||
protected function storeBudgetWithJournal(TransactionJournal $journal, int $budgetId)
|
||||
{
|
||||
if (intval($budgetId) > 0 && $journal->transactionType->type === TransactionType::WITHDRAWAL) {
|
||||
/** @var \FireflyIII\Models\Budget $budget */
|
||||
$budget = Budget::find($budgetId);
|
||||
$journal->budgets()->save($budget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $category
|
||||
*/
|
||||
protected function storeCategoryWithJournal(TransactionJournal $journal, string $category)
|
||||
{
|
||||
if (strlen($category) > 0) {
|
||||
$category = Category::firstOrCreateEncrypted(['name' => $category, 'user_id' => $journal->user_id]);
|
||||
$journal->categories()->save($category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function storeDepositAccounts(User $user, array $data): array
|
||||
{
|
||||
Log::debug('Now in storeDepositAccounts().');
|
||||
$destinationAccount = Account::where('user_id', $user->id)->where('id', $data['destination_account_id'])->first(['accounts.*']);
|
||||
|
||||
Log::debug(sprintf('Destination account is #%d ("%s")', $destinationAccount->id, $destinationAccount->name));
|
||||
|
||||
if (strlen($data['source_account_name']) > 0) {
|
||||
$sourceType = AccountType::where('type', 'Revenue account')->first();
|
||||
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $user->id, 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name'], 'active' => 1]
|
||||
);
|
||||
|
||||
Log::debug(sprintf('source account name is "%s", account is %d', $data['source_account_name'], $sourceAccount->id));
|
||||
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
}
|
||||
|
||||
Log::debug('source_account_name is empty, so default to cash account!');
|
||||
|
||||
$sourceType = AccountType::where('type', AccountType::CASH)->first();
|
||||
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $user->id, 'account_type_id' => $sourceType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
);
|
||||
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function storeWithdrawalAccounts(User $user, array $data): array
|
||||
{
|
||||
Log::debug('Now in storeWithdrawalAccounts().');
|
||||
$sourceAccount = Account::where('user_id', $user->id)->where('id', $data['source_account_id'])->first(['accounts.*']);
|
||||
|
||||
Log::debug(sprintf('Source account is #%d ("%s")', $sourceAccount->id, $sourceAccount->name));
|
||||
|
||||
if (strlen($data['destination_account_name']) > 0) {
|
||||
$destinationType = AccountType::where('type', AccountType::EXPENSE)->first();
|
||||
$destinationAccount = Account::firstOrCreateEncrypted(
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'account_type_id' => $destinationType->id,
|
||||
'name' => $data['destination_account_name'],
|
||||
'active' => 1,
|
||||
]
|
||||
);
|
||||
|
||||
Log::debug(sprintf('destination account name is "%s", account is %d', $data['destination_account_name'], $destinationAccount->id));
|
||||
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
}
|
||||
Log::debug('destination_account_name is empty, so default to cash account!');
|
||||
$destinationType = AccountType::where('type', AccountType::CASH)->first();
|
||||
$destinationAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $user->id, 'account_type_id' => $destinationType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
);
|
||||
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks the data array and the given accounts to verify that the native amount, currency
|
||||
* and possible the foreign currency and amount are properly saved.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $accounts
|
||||
*
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function verifyNativeAmount(array $data, array $accounts): array
|
||||
{
|
||||
/** @var TransactionType $transactionType */
|
||||
$transactionType = TransactionType::where('type', ucfirst($data['what']))->first();
|
||||
$submittedCurrencyId = $data['currency_id'];
|
||||
$data['foreign_amount'] = null;
|
||||
$data['foreign_currency_id'] = null;
|
||||
|
||||
// which account to check for what the native currency is?
|
||||
$check = 'source';
|
||||
if ($transactionType->type === TransactionType::DEPOSIT) {
|
||||
$check = 'destination';
|
||||
}
|
||||
switch ($transactionType->type) {
|
||||
case TransactionType::DEPOSIT:
|
||||
case TransactionType::WITHDRAWAL:
|
||||
// continue:
|
||||
$nativeCurrencyId = intval($accounts[$check]->getMeta('currency_id'));
|
||||
|
||||
// does not match? Then user has submitted amount in a foreign currency:
|
||||
if ($nativeCurrencyId !== $submittedCurrencyId) {
|
||||
// store amount and submitted currency in "foreign currency" fields:
|
||||
$data['foreign_amount'] = $data['amount'];
|
||||
$data['foreign_currency_id'] = $submittedCurrencyId;
|
||||
|
||||
// overrule the amount and currency ID fields to be the original again:
|
||||
$data['amount'] = strval($data['native_amount']);
|
||||
$data['currency_id'] = $nativeCurrencyId;
|
||||
}
|
||||
break;
|
||||
case TransactionType::TRANSFER:
|
||||
$sourceCurrencyId = intval($accounts['source']->getMeta('currency_id'));
|
||||
$destinationCurrencyId = intval($accounts['destination']->getMeta('currency_id'));
|
||||
$data['amount'] = strval($data['source_amount']);
|
||||
$data['currency_id'] = intval($accounts['source']->getMeta('currency_id'));
|
||||
|
||||
if ($sourceCurrencyId !== $destinationCurrencyId) {
|
||||
// accounts have different id's, save this info:
|
||||
$data['foreign_amount'] = strval($data['destination_amount']);
|
||||
$data['foreign_currency_id'] = $destinationCurrencyId;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new FireflyException(sprintf('Cannot handle %s in verifyNativeAmount()', $transactionType->type));
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
156
app/Repositories/Journal/UpdateJournalsTrait.php
Normal file
156
app/Repositories/Journal/UpdateJournalsTrait.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
/**
|
||||
* UpdateJournalsTrait.php
|
||||
* Copyright (c) 2017 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\Repositories\Journal;
|
||||
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Trait UpdateJournalsTrait
|
||||
*
|
||||
* @package FireflyIII\Repositories\Journal
|
||||
*/
|
||||
trait UpdateJournalsTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* When the user edits a split journal, each line is missing crucial data:
|
||||
*
|
||||
* - Withdrawal lines are missing the source account ID
|
||||
* - Deposit lines are missing the destination account ID
|
||||
* - Transfers are missing both.
|
||||
*
|
||||
* We need to append the array.
|
||||
*
|
||||
* @param array $transaction
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function appendTransactionData(array $transaction, array $data): array
|
||||
{
|
||||
switch ($data['what']) {
|
||||
case strtolower(TransactionType::TRANSFER):
|
||||
case strtolower(TransactionType::WITHDRAWAL):
|
||||
$transaction['source_account_id'] = intval($data['journal_source_account_id']);
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($data['what']) {
|
||||
case strtolower(TransactionType::TRANSFER):
|
||||
case strtolower(TransactionType::DEPOSIT):
|
||||
$transaction['destination_account_id'] = intval($data['journal_destination_account_id']);
|
||||
break;
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Account $account
|
||||
* @param array $data
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function updateDestinationTransaction(TransactionJournal $journal, Account $account, array $data)
|
||||
{
|
||||
$set = $journal->transactions()->where('amount', '>', 0)->get();
|
||||
if ($set->count() != 1) {
|
||||
throw new FireflyException(sprintf('Journal #%d has %d transactions with an amount more than zero.', $journal->id, $set->count()));
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $set->first();
|
||||
$transaction->amount = app('steam')->positive($data['amount']);
|
||||
$transaction->transaction_currency_id = $data['currency_id'];
|
||||
$transaction->foreign_amount = is_null($data['foreign_amount']) ? null : app('steam')->positive($data['foreign_amount']);
|
||||
$transaction->foreign_currency_id = $data['foreign_currency_id'];
|
||||
$transaction->account_id = $account->id;
|
||||
$transaction->save();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Account $account
|
||||
* @param array $data
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function updateSourceTransaction(TransactionJournal $journal, Account $account, array $data)
|
||||
{
|
||||
// should be one:
|
||||
$set = $journal->transactions()->where('amount', '<', 0)->get();
|
||||
if ($set->count() != 1) {
|
||||
throw new FireflyException(sprintf('Journal #%d has %d transactions with an amount more than zero.', $journal->id, $set->count()));
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $set->first();
|
||||
$transaction->amount = bcmul(app('steam')->positive($data['amount']), '-1');
|
||||
$transaction->transaction_currency_id = $data['currency_id'];
|
||||
$transaction->foreign_amount = is_null($data['foreign_amount']) ? null : bcmul(app('steam')->positive($data['foreign_amount']), '-1');
|
||||
$transaction->foreign_currency_id = $data['foreign_currency_id'];
|
||||
$transaction->account_id = $account->id;
|
||||
$transaction->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function updateTags(TransactionJournal $journal, array $array): bool
|
||||
{
|
||||
// create tag repository
|
||||
/** @var TagRepositoryInterface $tagRepository */
|
||||
$tagRepository = app(TagRepositoryInterface::class);
|
||||
|
||||
|
||||
// find or create all tags:
|
||||
$tags = [];
|
||||
$ids = [];
|
||||
foreach ($array as $name) {
|
||||
if (strlen(trim($name)) > 0) {
|
||||
$tag = Tag::firstOrCreateEncrypted(['tag' => $name, 'user_id' => $journal->user_id]);
|
||||
$tags[] = $tag;
|
||||
$ids[] = $tag->id;
|
||||
}
|
||||
}
|
||||
|
||||
// delete all tags connected to journal not in this array:
|
||||
if (count($ids) > 0) {
|
||||
DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->whereNotIn('tag_id', $ids)->delete();
|
||||
}
|
||||
// if count is zero, delete them all:
|
||||
if (count($ids) == 0) {
|
||||
DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->delete();
|
||||
}
|
||||
|
||||
// connect each tag to journal (if not yet connected):
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
Log::debug(sprintf('Will try to connect tag #%d to journal #%d.', $tag->id, $journal->id));
|
||||
$tagRepository->connect($journal, $tag);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -240,10 +240,12 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
|
||||
*/
|
||||
public function getPiggyBanksWithAmount(): Collection
|
||||
{
|
||||
$set = $this->getPiggyBanks();
|
||||
$currency = Amount::getDefaultCurrency();
|
||||
$set = $this->getPiggyBanks();
|
||||
foreach ($set as $piggy) {
|
||||
$currentAmount = $piggy->currentRelevantRep()->currentamount ?? '0';
|
||||
$piggy->name = $piggy->name . ' (' . Amount::format($currentAmount, false) . ')';
|
||||
|
||||
$piggy->name = $piggy->name . ' (' . Amount::formatAnything($currency, $currentAmount, false) . ')';
|
||||
}
|
||||
|
||||
return $set;
|
||||
|
@@ -47,7 +47,7 @@ class TagRepository implements TagRepositoryInterface
|
||||
* Already connected:
|
||||
*/
|
||||
if ($journal->tags()->find($tag->id)) {
|
||||
Log::error(sprintf('Cannot find tag #%d', $tag->id));
|
||||
Log::info(sprintf('Tag #%d is already connected to journal #%d.', $tag->id, $journal->id));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@@ -61,6 +61,8 @@ class FixerIO implements ExchangeRateInterface
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*
|
||||
* @return mixed|void
|
||||
*/
|
||||
public function setUser(User $user)
|
||||
{
|
||||
|
@@ -14,9 +14,10 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Support;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\Transaction as TransactionModel;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences as Prefs;
|
||||
|
||||
@@ -27,6 +28,7 @@ use Preferences as Prefs;
|
||||
*/
|
||||
class Amount
|
||||
{
|
||||
|
||||
/**
|
||||
* bool $sepBySpace is $localeconv['n_sep_by_space']
|
||||
* int $signPosn = $localeconv['n_sign_posn']
|
||||
@@ -101,17 +103,6 @@ class Amount
|
||||
return $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $amount
|
||||
* @param bool $coloured
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function format(string $amount, bool $coloured = true): string
|
||||
{
|
||||
return $this->formatAnything($this->getDefaultCurrency(), $amount, $coloured);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will properly format the given number, in color or "black and white",
|
||||
* as a currency, given two things: the currency required and the current locale.
|
||||
@@ -159,49 +150,6 @@ class Amount
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in many places (unfortunately).
|
||||
*
|
||||
* @param string $currencyCode
|
||||
* @param string $amount
|
||||
* @param bool $coloured
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function formatByCode(string $currencyCode, string $amount, bool $coloured = true): string
|
||||
{
|
||||
$currency = TransactionCurrency::where('code', $currencyCode)->first();
|
||||
|
||||
return $this->formatAnything($currency, $amount, $coloured);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param \FireflyIII\Models\TransactionJournal $journal
|
||||
* @param bool $coloured
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function formatJournal(TransactionJournal $journal, bool $coloured = true): string
|
||||
{
|
||||
$currency = $journal->transactionCurrency;
|
||||
|
||||
return $this->formatAnything($currency, $journal->amount(), $coloured);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param bool $coloured
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function formatTransaction(Transaction $transaction, bool $coloured = true)
|
||||
{
|
||||
$currency = $transaction->transactionJournal->transactionCurrency;
|
||||
|
||||
return $this->formatAnything($currency, strval($transaction->amount), $coloured);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
@@ -256,7 +204,7 @@ class Amount
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TransactionCurrency
|
||||
* @return \FireflyIII\Models\TransactionCurrency
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function getDefaultCurrency(): TransactionCurrency
|
||||
@@ -295,4 +243,81 @@ class Amount
|
||||
'zero' => $positive,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param bool $coloured
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function journalAmount(TransactionJournal $journal, bool $coloured = true): string
|
||||
{
|
||||
$amounts = [];
|
||||
$transactions = $journal->transactions()->where('amount', '>', 0)->get();
|
||||
/** @var TransactionModel $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
// model some fields to fit "transactionAmount()":
|
||||
$transaction->transaction_amount = $transaction->amount;
|
||||
$transaction->transaction_foreign_amount = $transaction->foreign_amount;
|
||||
$transaction->transaction_type_type = $journal->transactionType->type;
|
||||
$transaction->transaction_currency_symbol = $transaction->transactionCurrency->symbol;
|
||||
$transaction->transaction_currency_dp = $transaction->transactionCurrency->decimal_places;
|
||||
if (!is_null($transaction->foreign_currency_id)) {
|
||||
$transaction->foreign_currency_symbol = $transaction->foreignCurrency->symbol;
|
||||
$transaction->foreign_currency_dp = $transaction->foreignCurrency->decimal_places;
|
||||
}
|
||||
|
||||
$amounts[] = $this->transactionAmount($transaction, $coloured);
|
||||
}
|
||||
|
||||
return join(' / ', $amounts);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This formats a transaction, IF that transaction has been "collected" using the JournalCollector.
|
||||
*
|
||||
* @param TransactionModel $transaction
|
||||
* @param bool $coloured
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function transactionAmount(TransactionModel $transaction, bool $coloured = true): string
|
||||
{
|
||||
$amount = bcmul(app('steam')->positive(strval($transaction->transaction_amount)), '-1');
|
||||
$format = '%s';
|
||||
|
||||
if ($transaction->transaction_type_type === TransactionType::DEPOSIT) {
|
||||
$amount = bcmul($amount, '-1');
|
||||
}
|
||||
|
||||
if ($transaction->transaction_type_type === TransactionType::TRANSFER) {
|
||||
$amount = app('steam')->positive($amount);
|
||||
$coloured = false;
|
||||
$format = '<span class="text-info">%s</span>';
|
||||
}
|
||||
|
||||
$currency = new TransactionCurrency;
|
||||
$currency->symbol = $transaction->transaction_currency_symbol;
|
||||
$currency->decimal_places = $transaction->transaction_currency_dp;
|
||||
$str = sprintf($format, $this->formatAnything($currency, $amount, $coloured));
|
||||
|
||||
|
||||
if (!is_null($transaction->transaction_foreign_amount)) {
|
||||
$amount = strval($transaction->transaction_foreign_amount);
|
||||
|
||||
if ($transaction->transaction_type_type === TransactionType::TRANSFER) {
|
||||
$amount = app('steam')->positive($amount);
|
||||
$coloured = false;
|
||||
$format = '<span class="text-info">%s</span>';
|
||||
}
|
||||
|
||||
$currency = new TransactionCurrency;
|
||||
$currency->symbol = $transaction->foreign_currency_symbol;
|
||||
$currency->decimal_places = $transaction->foreign_currency_dp;
|
||||
$str .= ' (' . sprintf($format, $this->formatAnything($currency, $amount, $coloured)) . ')';
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
@@ -37,15 +37,8 @@ class JournalList implements BinderInterface
|
||||
$ids = explode(',', $value);
|
||||
/** @var \Illuminate\Support\Collection $object */
|
||||
$object = TransactionJournal::whereIn('transaction_journals.id', $ids)
|
||||
->expanded()
|
||||
->where('transaction_journals.user_id', auth()->user()->id)
|
||||
->get(
|
||||
[
|
||||
'transaction_journals.*',
|
||||
'transaction_types.type AS transaction_type_type',
|
||||
'transaction_currencies.code AS transaction_currency_code',
|
||||
]
|
||||
);
|
||||
->get(['transaction_journals.*',]);
|
||||
|
||||
if ($object->count() > 0) {
|
||||
return $object;
|
||||
|
242
app/Support/Import/CsvImportSupportTrait.php
Normal file
242
app/Support/Import/CsvImportSupportTrait.php
Normal file
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
/**
|
||||
* CsvImportSupportTrait.php
|
||||
* Copyright (c) 2017 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\Support\Import;
|
||||
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Import\Mapper\MapperInterface;
|
||||
use FireflyIII\Import\MapperPreProcess\PreProcessorInterface;
|
||||
use FireflyIII\Import\Specifics\SpecificInterface;
|
||||
use League\Csv\Reader;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Trait CsvImportSupportTrait
|
||||
*
|
||||
* @package FireflyIII\Support\Import
|
||||
*/
|
||||
trait CsvImportSupportTrait
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function doColumnMapping(): bool
|
||||
{
|
||||
$mapArray = $this->job->configuration['column-do-mapping'] ?? [];
|
||||
$doMap = false;
|
||||
foreach ($mapArray as $value) {
|
||||
if ($value === true) {
|
||||
$doMap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->job->configuration['column-mapping-complete'] === false && $doMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function doColumnRoles(): bool
|
||||
{
|
||||
return $this->job->configuration['column-roles-complete'] === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function getDataForColumnMapping(): array
|
||||
{
|
||||
$config = $this->job->configuration;
|
||||
$data = [];
|
||||
$indexes = [];
|
||||
|
||||
foreach ($config['column-do-mapping'] as $index => $mustBeMapped) {
|
||||
if ($mustBeMapped) {
|
||||
|
||||
$column = $config['column-roles'][$index] ?? '_ignore';
|
||||
|
||||
// is valid column?
|
||||
$validColumns = array_keys(config('csv.import_roles'));
|
||||
if (!in_array($column, $validColumns)) {
|
||||
throw new FireflyException(sprintf('"%s" is not a valid column.', $column));
|
||||
}
|
||||
|
||||
$canBeMapped = config('csv.import_roles.' . $column . '.mappable');
|
||||
$preProcessMap = config('csv.import_roles.' . $column . '.pre-process-map');
|
||||
if ($canBeMapped) {
|
||||
$mapperClass = config('csv.import_roles.' . $column . '.mapper');
|
||||
$mapperName = sprintf('\\FireflyIII\\Import\Mapper\\%s', $mapperClass);
|
||||
/** @var MapperInterface $mapper */
|
||||
$mapper = new $mapperName;
|
||||
$indexes[] = $index;
|
||||
$data[$index] = [
|
||||
'name' => $column,
|
||||
'mapper' => $mapperName,
|
||||
'index' => $index,
|
||||
'options' => $mapper->getMap(),
|
||||
'preProcessMap' => null,
|
||||
'values' => [],
|
||||
];
|
||||
if ($preProcessMap) {
|
||||
$preClass = sprintf(
|
||||
'\\FireflyIII\\Import\\MapperPreProcess\\%s',
|
||||
config('csv.import_roles.' . $column . '.pre-process-mapper')
|
||||
);
|
||||
$data[$index]['preProcessMap'] = $preClass;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// in order to actually map we also need all possible values from the CSV file.
|
||||
$content = $this->job->uploadFileContents();
|
||||
/** @var Reader $reader */
|
||||
$reader = Reader::createFromString($content);
|
||||
$reader->setDelimiter($config['delimiter']);
|
||||
$results = $reader->fetch();
|
||||
$validSpecifics = array_keys(config('csv.import_specifics'));
|
||||
|
||||
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) {
|
||||
|
||||
if (!in_array($name, $validSpecifics)) {
|
||||
throw new FireflyException(sprintf('"%s" is not a valid class name', $name));
|
||||
}
|
||||
$class = config('csv.import_specifics.' . $name);
|
||||
/** @var SpecificInterface $specific */
|
||||
$specific = app($class);
|
||||
|
||||
// it returns the row, possibly modified:
|
||||
$row = $specific->run($row);
|
||||
}
|
||||
|
||||
//do something here
|
||||
foreach ($indexes as $index) { // this is simply 1, 2, 3, etc.
|
||||
if (!isset($row[$index])) {
|
||||
// don't really know how to handle this. Just skip, for now.
|
||||
continue;
|
||||
}
|
||||
$value = $row[$index];
|
||||
if (strlen($value) > 0) {
|
||||
|
||||
// we can do some preprocessing here,
|
||||
// which is exclusively to fix the tags:
|
||||
if (!is_null($data[$index]['preProcessMap'])) {
|
||||
/** @var PreProcessorInterface $preProcessor */
|
||||
$preProcessor = app($data[$index]['preProcessMap']);
|
||||
$result = $preProcessor->run($value);
|
||||
$data[$index]['values'] = array_merge($data[$index]['values'], $result);
|
||||
|
||||
Log::debug($rowIndex . ':' . $index . 'Value before preprocessor', ['value' => $value]);
|
||||
Log::debug($rowIndex . ':' . $index . 'Value after preprocessor', ['value-new' => $result]);
|
||||
Log::debug($rowIndex . ':' . $index . 'Value after joining', ['value-complete' => $data[$index]['values']]);
|
||||
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[$index]['values'][] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($data as $index => $entry) {
|
||||
$data[$index]['values'] = array_unique($data[$index]['values']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method collects the data that will enable a user to choose column content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDataForColumnRoles(): array
|
||||
{
|
||||
Log::debug('Now in getDataForColumnRoles()');
|
||||
$config = $this->job->configuration;
|
||||
$data = [
|
||||
'columns' => [],
|
||||
'columnCount' => 0,
|
||||
'columnHeaders' => [],
|
||||
];
|
||||
|
||||
// show user column role configuration.
|
||||
$content = $this->job->uploadFileContents();
|
||||
|
||||
// create CSV reader.
|
||||
$reader = Reader::createFromString($content);
|
||||
$reader->setDelimiter($config['delimiter']);
|
||||
$start = $config['has-headers'] ? 1 : 0;
|
||||
$end = $start + config('csv.example_rows');
|
||||
$header = [];
|
||||
if ($config['has-headers']) {
|
||||
$header = $reader->fetchOne(0);
|
||||
}
|
||||
|
||||
|
||||
// collect example data in $data['columns']
|
||||
Log::debug(sprintf('While %s is smaller than %d', $start, $end));
|
||||
while ($start < $end) {
|
||||
$row = $reader->fetchOne($start);
|
||||
Log::debug(sprintf('Row %d has %d columns', $start, count($row)));
|
||||
// run specifics here:
|
||||
// and this is the point where the specifix go to work.
|
||||
foreach ($config['specifics'] as $name => $enabled) {
|
||||
/** @var SpecificInterface $specific */
|
||||
$specific = app('FireflyIII\Import\Specifics\\' . $name);
|
||||
Log::debug(sprintf('Will now apply specific "%s" to row %d.', $name, $start));
|
||||
// it returns the row, possibly modified:
|
||||
$row = $specific->run($row);
|
||||
}
|
||||
|
||||
foreach ($row as $index => $value) {
|
||||
$value = trim($value);
|
||||
$data['columnHeaders'][$index] = $header[$index] ?? '';
|
||||
if (strlen($value) > 0) {
|
||||
$data['columns'][$index][] = $value;
|
||||
}
|
||||
}
|
||||
$start++;
|
||||
$data['columnCount'] = count($row) > $data['columnCount'] ? count($row) : $data['columnCount'];
|
||||
}
|
||||
|
||||
// make unique example data
|
||||
foreach ($data['columns'] as $index => $values) {
|
||||
$data['columns'][$index] = array_unique($values);
|
||||
}
|
||||
|
||||
$data['set_roles'] = [];
|
||||
// collect possible column roles:
|
||||
$data['available_roles'] = [];
|
||||
foreach (array_keys(config('csv.import_roles')) as $role) {
|
||||
$data['available_roles'][$role] = trans('csv.column_' . $role);
|
||||
}
|
||||
|
||||
$config['column-count'] = $data['columnCount'];
|
||||
$this->job->configuration = $config;
|
||||
$this->job->save();
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@@ -15,16 +15,21 @@ namespace FireflyIII\Support\Models;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class TransactionJournalTrait
|
||||
*
|
||||
* @property int $id
|
||||
* @method Collection transactions()
|
||||
* @method bool isWithdrawal()
|
||||
* @property int $id
|
||||
* @property Carbon $date
|
||||
* @property string $transaction_type_type
|
||||
* @property TransactionType $transactionType
|
||||
*
|
||||
* @package FireflyIII\Support\Models
|
||||
*/
|
||||
@@ -91,6 +96,16 @@ trait TransactionJournalTrait
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
abstract public function budgets(): BelongsToMany;
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
abstract public function categories(): BelongsToMany;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -180,6 +195,19 @@ trait TransactionJournalTrait
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getMeta(string $name);
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isDeposit(): bool;
|
||||
|
||||
/**
|
||||
* @param Builder $query
|
||||
* @param string $table
|
||||
@@ -201,6 +229,29 @@ trait TransactionJournalTrait
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isOpeningBalance(): bool;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isTransfer(): bool;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isWithdrawal(): bool;
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
abstract public function piggyBankEvents(): HasMany;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
@@ -213,6 +264,31 @@ trait TransactionJournalTrait
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Transaction
|
||||
*/
|
||||
public function positiveTransaction(): Transaction
|
||||
{
|
||||
return $this->transactions()->where('amount', '>', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the model to the database.
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function save(array $options = []): bool;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param $value
|
||||
*
|
||||
* @return TransactionJournalMeta
|
||||
*/
|
||||
abstract public function setMeta(string $name, $value): TransactionJournalMeta;
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
@@ -273,4 +349,9 @@ trait TransactionJournalTrait
|
||||
|
||||
return $typeStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany
|
||||
*/
|
||||
abstract public function transactions(): HasMany;
|
||||
}
|
||||
|
@@ -53,7 +53,7 @@ class Search implements SearchInterface
|
||||
public function __construct()
|
||||
{
|
||||
$this->modifiers = new Collection;
|
||||
$this->validModifiers = config('firefly.search_modifiers');
|
||||
$this->validModifiers = (array) config('firefly.search_modifiers');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -18,6 +18,7 @@ use Crypt;
|
||||
use DB;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class Steam
|
||||
@@ -45,14 +46,27 @@ class Steam
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$balance = strval(
|
||||
$account->transactions()->leftJoin(
|
||||
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
|
||||
)->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount')
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
// first part: get all balances in own currency:
|
||||
$nativeBalance = strval(
|
||||
$account->transactions()
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.date', '<=', $date->format('Y-m-d'))
|
||||
->where('transactions.transaction_currency_id', $currencyId)
|
||||
->sum('transactions.amount')
|
||||
);
|
||||
$virtual = is_null($account->virtual_balance) ? '0' : strval($account->virtual_balance);
|
||||
$balance = bcadd($balance, $virtual);
|
||||
|
||||
// get all balances in foreign currency:
|
||||
$foreignBalance = strval(
|
||||
$account->transactions()
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.date', '<=', $date->format('Y-m-d'))
|
||||
->where('transactions.foreign_currency_id', $currencyId)
|
||||
->sum('transactions.foreign_amount')
|
||||
);
|
||||
$balance = bcadd($nativeBalance, $foreignBalance);
|
||||
$virtual = is_null($account->virtual_balance) ? '0' : strval($account->virtual_balance);
|
||||
$balance = bcadd($balance, $virtual);
|
||||
$cache->store($balance);
|
||||
|
||||
return $balance;
|
||||
@@ -76,13 +90,26 @@ class Steam
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
|
||||
$balance = strval(
|
||||
$account->transactions()->leftJoin(
|
||||
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
|
||||
)->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount')
|
||||
$nativeBalance = strval(
|
||||
$account->transactions()
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.date', '<=', $date->format('Y-m-d'))
|
||||
->where('transactions.transaction_currency_id', $currencyId)
|
||||
->sum('transactions.amount')
|
||||
);
|
||||
|
||||
// get all balances in foreign currency:
|
||||
$foreignBalance = strval(
|
||||
$account->transactions()
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.date', '<=', $date->format('Y-m-d'))
|
||||
->where('transactions.foreign_currency_id', $currencyId)
|
||||
->sum('transactions.foreign_amount')
|
||||
);
|
||||
$balance = bcadd($nativeBalance, $foreignBalance);
|
||||
|
||||
$cache->store($balance);
|
||||
|
||||
return $balance;
|
||||
@@ -111,27 +138,55 @@ class Steam
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$balances = [];
|
||||
$start->subDay();
|
||||
$end->addDay();
|
||||
$startBalance = $this->balance($account, $start);
|
||||
$balances[$start->format('Y-m-d')] = $startBalance;
|
||||
$balances = [];
|
||||
$formatted = $start->format('Y-m-d');
|
||||
$startBalance = $this->balance($account, $start);
|
||||
$balances[$formatted] = $startBalance;
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
$start->addDay();
|
||||
|
||||
// query!
|
||||
$set = $account->transactions()
|
||||
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->groupBy('transaction_journals.date')
|
||||
->orderBy('transaction_journals.date', 'ASC')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->get(['transaction_journals.date', DB::raw('SUM(transactions.amount) AS modified')]);
|
||||
$set = $account->transactions()
|
||||
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->groupBy('transaction_journals.date')
|
||||
->groupBy('transactions.transaction_currency_id')
|
||||
->groupBy('transactions.foreign_currency_id')
|
||||
->orderBy('transaction_journals.date', 'ASC')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->get(
|
||||
[
|
||||
'transaction_journals.date',
|
||||
'transactions.transaction_currency_id',
|
||||
DB::raw('SUM(transactions.amount) AS modified'),
|
||||
'transactions.foreign_currency_id',
|
||||
DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'),
|
||||
]
|
||||
);
|
||||
|
||||
$currentBalance = $startBalance;
|
||||
/** @var Transaction $entry */
|
||||
foreach ($set as $entry) {
|
||||
$modified = is_null($entry->modified) ? '0' : strval($entry->modified);
|
||||
$currentBalance = bcadd($currentBalance, $modified);
|
||||
$balances[$entry->date] = $currentBalance;
|
||||
// normal amount and foreign amount
|
||||
$modified = is_null($entry->modified) ? '0' : strval($entry->modified);
|
||||
$foreignModified = is_null($entry->modified_foreign) ? '0' : strval($entry->modified_foreign);
|
||||
$amount = '0';
|
||||
if ($currencyId === $entry->transaction_currency_id) {
|
||||
// use normal amount:
|
||||
$amount = $modified;
|
||||
}
|
||||
if ($currencyId === $entry->foreign_currency_id) {
|
||||
// use normal amount:
|
||||
$amount = $foreignModified;
|
||||
}
|
||||
|
||||
$currentBalance = bcadd($currentBalance, $amount);
|
||||
$carbon = new Carbon($entry->date);
|
||||
$date = $carbon->format('Y-m-d');
|
||||
$balances[$date] = $currentBalance;
|
||||
}
|
||||
|
||||
$cache->store($balances);
|
||||
@@ -144,14 +199,14 @@ class Steam
|
||||
/**
|
||||
* This method always ignores the virtual balance.
|
||||
*
|
||||
* @param array $ids
|
||||
* @param \Carbon\Carbon $date
|
||||
* @param \Illuminate\Support\Collection $accounts
|
||||
* @param \Carbon\Carbon $date
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function balancesById(array $ids, Carbon $date): array
|
||||
public function balancesByAccounts(Collection $accounts, Carbon $date): array
|
||||
{
|
||||
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
// cache this property.
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($ids);
|
||||
@@ -161,21 +216,13 @@ class Steam
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$balances = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.date', '<=', $date->format('Y-m-d'))
|
||||
->groupBy('transactions.account_id')
|
||||
->whereIn('transactions.account_id', $ids)
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->get(['transactions.account_id', DB::raw('sum(transactions.amount) AS aggregate')]);
|
||||
|
||||
// need to do this per account.
|
||||
$result = [];
|
||||
foreach ($balances as $entry) {
|
||||
$accountId = intval($entry->account_id);
|
||||
$balance = $entry->aggregate;
|
||||
$result[$accountId] = $balance;
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$result[$account->id] = $this->balance($account, $date);
|
||||
}
|
||||
|
||||
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
|
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Account.php
|
||||
* Copyright (c) 2017 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\Support\Twig;
|
||||
|
||||
|
||||
use FireflyIII\Models\Account as AccountModel;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Support\Facades\Amount as AmountFacade;
|
||||
use Twig_Extension;
|
||||
use Twig_SimpleFunction;
|
||||
|
||||
/**
|
||||
* Class Account
|
||||
*
|
||||
* @package FireflyIII\Support\Twig
|
||||
*/
|
||||
class Account extends Twig_Extension
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
$this->formatAmountByAccount(),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return "active" when a part of the route matches the argument.
|
||||
* ie. "accounts" will match "accounts.index".
|
||||
*
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
protected function formatAmountByAccount(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'formatAmountByAccount', function (AccountModel $account, string $amount, bool $coloured = true): string {
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
if ($currencyId === 0) {
|
||||
// Format using default currency:
|
||||
return AmountFacade::format($amount, $coloured);
|
||||
}
|
||||
$currency = TransactionCurrency::find($currencyId);
|
||||
|
||||
return AmountFacade::formatAnything($currency, $amount, $coloured);
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
268
app/Support/Twig/AmountFormat.php
Normal file
268
app/Support/Twig/AmountFormat.php
Normal file
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
/**
|
||||
* AmountFormat.php
|
||||
* Copyright (c) 2017 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\Support\Twig;
|
||||
|
||||
|
||||
use FireflyIII\Models\Account as AccountModel;
|
||||
use FireflyIII\Models\Transaction as TransactionModel;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Twig_Extension;
|
||||
use Twig_SimpleFilter;
|
||||
use Twig_SimpleFunction;
|
||||
|
||||
/**
|
||||
* Contains all amount formatting routines.
|
||||
*
|
||||
* @package FireflyIII\Support\Twig
|
||||
*/
|
||||
class AmountFormat extends Twig_Extension
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getFilters(): array
|
||||
{
|
||||
return [
|
||||
$this->formatAmount(),
|
||||
$this->formatAmountPlain(),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
$this->formatAmountByAccount(),
|
||||
$this->transactionAmount(),
|
||||
$this->journalAmount(),
|
||||
$this->formatDestinationAfter(),
|
||||
$this->formatDestinationBefore(),
|
||||
$this->formatSourceAfter(),
|
||||
$this->formatSourceBefore(),
|
||||
$this->formatAmountByCurrency(),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'FireflyIII\Support\Twig\AmountFormat';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Twig_SimpleFilter
|
||||
*/
|
||||
protected function formatAmount(): Twig_SimpleFilter
|
||||
{
|
||||
return new Twig_SimpleFilter(
|
||||
'formatAmount', function (string $string): string {
|
||||
|
||||
$currency = app('amount')->getDefaultCurrency();
|
||||
|
||||
return app('amount')->formatAnything($currency, $string, true);
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will format the amount by the currency related to the given account.
|
||||
*
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
protected function formatAmountByAccount(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'formatAmountByAccount', function (AccountModel $account, string $amount, bool $coloured = true): string {
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
|
||||
if ($currencyId !== 0) {
|
||||
$currency = TransactionCurrency::find($currencyId);
|
||||
|
||||
return app('amount')->formatAnything($currency, $amount, $coloured);
|
||||
}
|
||||
$currency = app('amount')->getDefaultCurrency();
|
||||
|
||||
return app('amount')->formatAnything($currency, $amount, $coloured);
|
||||
|
||||
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will format the amount by the currency related to the given account.
|
||||
*
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
protected function formatAmountByCurrency(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'formatAmountByCurrency', function (TransactionCurrency $currency, string $amount, bool $coloured = true): string {
|
||||
|
||||
return app('amount')->formatAnything($currency, $amount, $coloured);
|
||||
|
||||
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFilter
|
||||
*/
|
||||
protected function formatAmountPlain(): Twig_SimpleFilter
|
||||
{
|
||||
return new Twig_SimpleFilter(
|
||||
'formatAmountPlain', function (string $string): string {
|
||||
|
||||
$currency = app('amount')->getDefaultCurrency();
|
||||
|
||||
return app('amount')->formatAnything($currency, $string, false);
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
protected function formatDestinationAfter(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'formatDestinationAfter', function (array $transaction): string {
|
||||
|
||||
// build fake currency for main amount.
|
||||
$format = new TransactionCurrency;
|
||||
$format->decimal_places = $transaction['transaction_currency_dp'];
|
||||
$format->symbol = $transaction['transaction_currency_symbol'];
|
||||
$string = app('amount')->formatAnything($format, $transaction['destination_account_after'], true);
|
||||
|
||||
// also append foreign amount for clarity:
|
||||
if (!is_null($transaction['foreign_destination_amount'])) {
|
||||
// build fake currency for foreign amount
|
||||
$format = new TransactionCurrency;
|
||||
$format->decimal_places = $transaction['foreign_currency_dp'];
|
||||
$format->symbol = $transaction['foreign_currency_symbol'];
|
||||
$string .= ' (' . app('amount')->formatAnything($format, $transaction['foreign_destination_amount'], true) . ')';
|
||||
}
|
||||
|
||||
|
||||
return $string;
|
||||
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
protected function formatDestinationBefore(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'formatDestinationBefore', function (array $transaction): string {
|
||||
|
||||
// build fake currency for main amount.
|
||||
$format = new TransactionCurrency;
|
||||
$format->decimal_places = $transaction['transaction_currency_dp'];
|
||||
$format->symbol = $transaction['transaction_currency_symbol'];
|
||||
|
||||
return app('amount')->formatAnything($format, $transaction['destination_account_before'], true);
|
||||
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
protected function formatSourceAfter(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'formatSourceAfter', function (array $transaction): string {
|
||||
|
||||
// build fake currency for main amount.
|
||||
$format = new TransactionCurrency;
|
||||
$format->decimal_places = $transaction['transaction_currency_dp'];
|
||||
$format->symbol = $transaction['transaction_currency_symbol'];
|
||||
$string = app('amount')->formatAnything($format, $transaction['source_account_after'], true);
|
||||
|
||||
// also append foreign amount for clarity:
|
||||
if (!is_null($transaction['foreign_source_amount'])) {
|
||||
// build fake currency for foreign amount
|
||||
$format = new TransactionCurrency;
|
||||
$format->decimal_places = $transaction['foreign_currency_dp'];
|
||||
$format->symbol = $transaction['foreign_currency_symbol'];
|
||||
$string .= ' (' . app('amount')->formatAnything($format, $transaction['foreign_source_amount'], true) . ')';
|
||||
}
|
||||
|
||||
|
||||
return $string;
|
||||
|
||||
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
protected function formatSourceBefore(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'formatSourceBefore', function (array $transaction): string {
|
||||
|
||||
// build fake currency for main amount.
|
||||
$format = new TransactionCurrency;
|
||||
$format->decimal_places = $transaction['transaction_currency_dp'];
|
||||
$format->symbol = $transaction['transaction_currency_symbol'];
|
||||
|
||||
return app('amount')->formatAnything($format, $transaction['source_account_before'], true);
|
||||
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
protected function journalAmount(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'journalAmount', function (TransactionJournal $journal): string {
|
||||
|
||||
return app('amount')->journalAmount($journal, true);
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
protected function transactionAmount(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'transactionAmount', function (TransactionModel $transaction): string {
|
||||
|
||||
return app('amount')->transactionAmount($transaction, true);
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -38,9 +38,6 @@ class General extends Twig_Extension
|
||||
public function getFilters(): array
|
||||
{
|
||||
return [
|
||||
$this->formatAmount(),
|
||||
$this->formatAmountPlain(),
|
||||
$this->formatJournal(),
|
||||
$this->balance(),
|
||||
$this->formatFilesize(),
|
||||
$this->mimeIcon(),
|
||||
@@ -173,33 +170,6 @@ class General extends Twig_Extension
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Twig_SimpleFilter
|
||||
*/
|
||||
protected function formatAmount(): Twig_SimpleFilter
|
||||
{
|
||||
return new Twig_SimpleFilter(
|
||||
'formatAmount', function (string $string): string {
|
||||
|
||||
return app('amount')->format($string);
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFilter
|
||||
*/
|
||||
protected function formatAmountPlain(): Twig_SimpleFilter
|
||||
{
|
||||
return new Twig_SimpleFilter(
|
||||
'formatAmountPlain', function (string $string): string {
|
||||
|
||||
return app('amount')->format($string, false);
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFilter
|
||||
*/
|
||||
@@ -223,17 +193,6 @@ class General extends Twig_Extension
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFilter
|
||||
*/
|
||||
protected function formatJournal(): Twig_SimpleFilter
|
||||
{
|
||||
return new Twig_SimpleFilter(
|
||||
'formatJournal', function (TransactionJournal $journal): string {
|
||||
return app('amount')->formatJournal($journal);
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
|
@@ -13,10 +13,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Twig;
|
||||
|
||||
use Amount;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction as TransactionModel;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Steam;
|
||||
use Twig_Extension;
|
||||
@@ -30,49 +28,6 @@ use Twig_SimpleFunction;
|
||||
*/
|
||||
class Transaction extends Twig_Extension
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
public function formatAnything(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'formatAnything', function (TransactionCurrency $currency, string $amount): string {
|
||||
|
||||
return Amount::formatAnything($currency, $amount, true);
|
||||
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
public function formatAnythingPlain(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'formatAnythingPlain', function (TransactionCurrency $currency, string $amount): string {
|
||||
|
||||
return Amount::formatAnything($currency, $amount, false);
|
||||
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
public function formatByCode(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'formatByCode', function (string $currencyCode, string $amount): string {
|
||||
|
||||
return Amount::formatByCode($currencyCode, $amount, true);
|
||||
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
@@ -91,17 +46,13 @@ class Transaction extends Twig_Extension
|
||||
public function getFunctions(): array
|
||||
{
|
||||
$functions = [
|
||||
$this->formatAnything(),
|
||||
$this->formatAnythingPlain(),
|
||||
$this->transactionSourceAccount(),
|
||||
$this->transactionDestinationAccount(),
|
||||
$this->optionalJournalAmount(),
|
||||
$this->transactionBudgets(),
|
||||
$this->transactionIdBudgets(),
|
||||
$this->transactionCategories(),
|
||||
$this->transactionIdCategories(),
|
||||
$this->splitJournalIndicator(),
|
||||
$this->formatByCode(),
|
||||
];
|
||||
|
||||
return $functions;
|
||||
@@ -117,33 +68,6 @@ class Transaction extends Twig_Extension
|
||||
return 'transaction';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
public function optionalJournalAmount(): Twig_SimpleFunction
|
||||
{
|
||||
return new Twig_SimpleFunction(
|
||||
'optionalJournalAmount', function (int $journalId, string $transactionAmount, string $code, string $type): string {
|
||||
// get amount of journal:
|
||||
$amount = strval(TransactionModel::where('transaction_journal_id', $journalId)->whereNull('deleted_at')->where('amount', '<', 0)->sum('amount'));
|
||||
// display deposit and transfer positive
|
||||
if ($type === TransactionType::DEPOSIT || $type === TransactionType::TRANSFER) {
|
||||
$amount = bcmul($amount, '-1');
|
||||
}
|
||||
|
||||
// not equal to transaction amount?
|
||||
if (bccomp($amount, $transactionAmount) !== 0 && bccomp($amount, bcmul($transactionAmount, '-1')) !== 0) {
|
||||
//$currency =
|
||||
return sprintf(' (%s)', Amount::formatByCode($code, $amount, true));
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
|
||||
}, ['is_safe' => ['html']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Twig_SimpleFunction
|
||||
*/
|
||||
|
@@ -214,9 +214,9 @@ class User extends Authenticatable
|
||||
*/
|
||||
public function sendPasswordResetNotification($token)
|
||||
{
|
||||
$ip = Request::ip();
|
||||
$ipAddress = Request::ip();
|
||||
|
||||
event(new RequestedNewPassword($this, $token, $ip));
|
||||
event(new RequestedNewPassword($this, $token, $ipAddress));
|
||||
}
|
||||
|
||||
/**
|
||||
|
294
composer.lock
generated
294
composer.lock
generated
@@ -665,16 +665,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v5.4.21",
|
||||
"version": "v5.4.24",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "2ed668f96d1a6ca42f50d5c87ee9ceecfc0a6eee"
|
||||
"reference": "ec8548db26c1b147570f661128649e98f3ac0f29"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/2ed668f96d1a6ca42f50d5c87ee9ceecfc0a6eee",
|
||||
"reference": "2ed668f96d1a6ca42f50d5c87ee9ceecfc0a6eee",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/ec8548db26c1b147570f661128649e98f3ac0f29",
|
||||
"reference": "ec8548db26c1b147570f661128649e98f3ac0f29",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -790,20 +790,20 @@
|
||||
"framework",
|
||||
"laravel"
|
||||
],
|
||||
"time": "2017-04-28T15:40:01+00:00"
|
||||
"time": "2017-05-30T12:44:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravelcollective/html",
|
||||
"version": "v5.4.1",
|
||||
"version": "v5.4.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/LaravelCollective/html.git",
|
||||
"reference": "7570f25d58a00fd6909c0563808590f9cdb14d47"
|
||||
"reference": "9b8f51e7a2368911c896f5d42757886bae0717b5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/LaravelCollective/html/zipball/7570f25d58a00fd6909c0563808590f9cdb14d47",
|
||||
"reference": "7570f25d58a00fd6909c0563808590f9cdb14d47",
|
||||
"url": "https://api.github.com/repos/LaravelCollective/html/zipball/9b8f51e7a2368911c896f5d42757886bae0717b5",
|
||||
"reference": "9b8f51e7a2368911c896f5d42757886bae0717b5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -844,20 +844,20 @@
|
||||
],
|
||||
"description": "HTML and Form Builders for the Laravel Framework",
|
||||
"homepage": "http://laravelcollective.com",
|
||||
"time": "2017-01-26T19:27:05+00:00"
|
||||
"time": "2017-05-22T06:35:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/commonmark",
|
||||
"version": "0.15.3",
|
||||
"version": "0.15.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/commonmark.git",
|
||||
"reference": "c8b43ee5821362216f8e9ac684f0f59de164edcc"
|
||||
"reference": "c4c8e6bf99e62d9568875d9fc3ef473fe3e18e0c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c8b43ee5821362216f8e9ac684f0f59de164edcc",
|
||||
"reference": "c8b43ee5821362216f8e9ac684f0f59de164edcc",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c4c8e6bf99e62d9568875d9fc3ef473fe3e18e0c",
|
||||
"reference": "c4c8e6bf99e62d9568875d9fc3ef473fe3e18e0c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -913,7 +913,7 @@
|
||||
"markdown",
|
||||
"parser"
|
||||
],
|
||||
"time": "2016-12-19T00:11:43+00:00"
|
||||
"time": "2017-05-09T12:47:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/csv",
|
||||
@@ -1583,16 +1583,16 @@
|
||||
},
|
||||
{
|
||||
"name": "swiftmailer/swiftmailer",
|
||||
"version": "v5.4.7",
|
||||
"version": "v5.4.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/swiftmailer/swiftmailer.git",
|
||||
"reference": "56db4ed32a6d5c9824c3ecc1d2e538f663f47eb4"
|
||||
"reference": "9a06dc570a0367850280eefd3f1dc2da45aef517"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/56db4ed32a6d5c9824c3ecc1d2e538f663f47eb4",
|
||||
"reference": "56db4ed32a6d5c9824c3ecc1d2e538f663f47eb4",
|
||||
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/9a06dc570a0367850280eefd3f1dc2da45aef517",
|
||||
"reference": "9a06dc570a0367850280eefd3f1dc2da45aef517",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1633,20 +1633,20 @@
|
||||
"mail",
|
||||
"mailer"
|
||||
],
|
||||
"time": "2017-04-20T17:32:18+00:00"
|
||||
"time": "2017-05-01T15:54:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38"
|
||||
"reference": "70d2a29b2911cbdc91a7e268046c395278238b2e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38",
|
||||
"reference": "a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/70d2a29b2911cbdc91a7e268046c395278238b2e",
|
||||
"reference": "70d2a29b2911cbdc91a7e268046c395278238b2e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1654,10 +1654,16 @@
|
||||
"symfony/debug": "~2.8|~3.0",
|
||||
"symfony/polyfill-mbstring": "~1.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/dependency-injection": "<3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "~1.0",
|
||||
"symfony/config": "~3.3",
|
||||
"symfony/dependency-injection": "~3.3",
|
||||
"symfony/event-dispatcher": "~2.8|~3.0",
|
||||
"symfony/filesystem": "~2.8|~3.0",
|
||||
"symfony/http-kernel": "~2.8|~3.0",
|
||||
"symfony/process": "~2.8|~3.0"
|
||||
},
|
||||
"suggest": {
|
||||
@@ -1669,7 +1675,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1696,7 +1702,7 @@
|
||||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-04-26T01:39:17+00:00"
|
||||
"time": "2017-06-02T19:24:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
@@ -1753,16 +1759,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/debug.git",
|
||||
"reference": "fd6eeee656a5a7b384d56f1072243fe1c0e81686"
|
||||
"reference": "e9c50482841ef696e8fa1470d950a79c8921f45d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/fd6eeee656a5a7b384d56f1072243fe1c0e81686",
|
||||
"reference": "fd6eeee656a5a7b384d56f1072243fe1c0e81686",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/e9c50482841ef696e8fa1470d950a79c8921f45d",
|
||||
"reference": "e9c50482841ef696e8fa1470d950a79c8921f45d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1773,13 +1779,12 @@
|
||||
"symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/class-loader": "~2.8|~3.0",
|
||||
"symfony/http-kernel": "~2.8|~3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1806,11 +1811,11 @@
|
||||
],
|
||||
"description": "Symfony Debug Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-04-19T20:17:50+00:00"
|
||||
"time": "2017-06-01T21:01:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v2.8.20",
|
||||
"version": "v2.8.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
@@ -1870,16 +1875,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "9cf076f8f492f4b1ffac40aae9c2d287b4ca6930"
|
||||
"reference": "baea7f66d30854ad32988c11a09d7ffd485810c4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/9cf076f8f492f4b1ffac40aae9c2d287b4ca6930",
|
||||
"reference": "9cf076f8f492f4b1ffac40aae9c2d287b4ca6930",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/baea7f66d30854ad32988c11a09d7ffd485810c4",
|
||||
"reference": "baea7f66d30854ad32988c11a09d7ffd485810c4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1888,7 +1893,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1915,20 +1920,20 @@
|
||||
],
|
||||
"description": "Symfony Finder Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-04-12T14:13:17+00:00"
|
||||
"time": "2017-06-01T21:01:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "9de6add7f731e5af7f5b2e9c0da365e43383ebef"
|
||||
"reference": "80eb5a1f968448b77da9e8b2c0827f6e8d767846"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/9de6add7f731e5af7f5b2e9c0da365e43383ebef",
|
||||
"reference": "9de6add7f731e5af7f5b2e9c0da365e43383ebef",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/80eb5a1f968448b77da9e8b2c0827f6e8d767846",
|
||||
"reference": "80eb5a1f968448b77da9e8b2c0827f6e8d767846",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1941,7 +1946,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1968,20 +1973,20 @@
|
||||
],
|
||||
"description": "Symfony HttpFoundation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-05-01T14:55:58+00:00"
|
||||
"time": "2017-06-05T13:06:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "46e8b209abab55c072c47d72d5cd1d62c0585e05"
|
||||
"reference": "4ad34a0d20a5848c0fcbf6ff6a2ff1cd9cf4b9ed"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/46e8b209abab55c072c47d72d5cd1d62c0585e05",
|
||||
"reference": "46e8b209abab55c072c47d72d5cd1d62c0585e05",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/4ad34a0d20a5848c0fcbf6ff6a2ff1cd9cf4b9ed",
|
||||
"reference": "4ad34a0d20a5848c0fcbf6ff6a2ff1cd9cf4b9ed",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1989,18 +1994,21 @@
|
||||
"psr/log": "~1.0",
|
||||
"symfony/debug": "~2.8|~3.0",
|
||||
"symfony/event-dispatcher": "~2.8|~3.0",
|
||||
"symfony/http-foundation": "~2.8.13|~3.1.6|~3.2"
|
||||
"symfony/http-foundation": "~3.3"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/config": "<2.8"
|
||||
"symfony/config": "<2.8",
|
||||
"symfony/dependency-injection": "<3.3",
|
||||
"symfony/var-dumper": "<3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/cache": "~1.0",
|
||||
"symfony/browser-kit": "~2.8|~3.0",
|
||||
"symfony/class-loader": "~2.8|~3.0",
|
||||
"symfony/config": "~2.8|~3.0",
|
||||
"symfony/console": "~2.8|~3.0",
|
||||
"symfony/css-selector": "~2.8|~3.0",
|
||||
"symfony/dependency-injection": "~2.8|~3.0",
|
||||
"symfony/dependency-injection": "~3.3",
|
||||
"symfony/dom-crawler": "~2.8|~3.0",
|
||||
"symfony/expression-language": "~2.8|~3.0",
|
||||
"symfony/finder": "~2.8|~3.0",
|
||||
@@ -2009,7 +2017,7 @@
|
||||
"symfony/stopwatch": "~2.8|~3.0",
|
||||
"symfony/templating": "~2.8|~3.0",
|
||||
"symfony/translation": "~2.8|~3.0",
|
||||
"symfony/var-dumper": "~3.2"
|
||||
"symfony/var-dumper": "~3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/browser-kit": "",
|
||||
@@ -2023,7 +2031,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -2050,7 +2058,7 @@
|
||||
],
|
||||
"description": "Symfony HttpKernel Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-05-01T17:46:48+00:00"
|
||||
"time": "2017-05-29T21:02:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
@@ -2221,16 +2229,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0"
|
||||
"reference": "8e30690c67aafb6c7992d6d8eb0d707807dd3eaf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0",
|
||||
"reference": "999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/8e30690c67aafb6c7992d6d8eb0d707807dd3eaf",
|
||||
"reference": "8e30690c67aafb6c7992d6d8eb0d707807dd3eaf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2239,7 +2247,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -2266,36 +2274,39 @@
|
||||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-04-12T14:13:17+00:00"
|
||||
"time": "2017-05-22T12:32:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/routing.git",
|
||||
"reference": "5029745d6d463585e8b487dbc83d6333f408853a"
|
||||
"reference": "39804eeafea5cca851946e1eed122eb94459fdb4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/5029745d6d463585e8b487dbc83d6333f408853a",
|
||||
"reference": "5029745d6d463585e8b487dbc83d6333f408853a",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/39804eeafea5cca851946e1eed122eb94459fdb4",
|
||||
"reference": "39804eeafea5cca851946e1eed122eb94459fdb4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.9"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/config": "<2.8"
|
||||
"symfony/config": "<2.8",
|
||||
"symfony/dependency-injection": "<3.3",
|
||||
"symfony/yaml": "<3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "~1.0",
|
||||
"doctrine/common": "~2.2",
|
||||
"psr/log": "~1.0",
|
||||
"symfony/config": "~2.8|~3.0",
|
||||
"symfony/dependency-injection": "~3.3",
|
||||
"symfony/expression-language": "~2.8|~3.0",
|
||||
"symfony/http-foundation": "~2.8|~3.0",
|
||||
"symfony/yaml": "~2.8|~3.0"
|
||||
"symfony/yaml": "~3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"doctrine/annotations": "For using the annotation loader",
|
||||
@@ -2308,7 +2319,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -2341,20 +2352,20 @@
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"time": "2017-04-12T14:13:17+00:00"
|
||||
"time": "2017-06-02T09:51:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "f4a04d2df710f81515df576b2de06bdeee518b83"
|
||||
"reference": "dc3b2a0c6cfff60327ba1c043a82092735397543"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/f4a04d2df710f81515df576b2de06bdeee518b83",
|
||||
"reference": "f4a04d2df710f81515df576b2de06bdeee518b83",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/dc3b2a0c6cfff60327ba1c043a82092735397543",
|
||||
"reference": "dc3b2a0c6cfff60327ba1c043a82092735397543",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2362,13 +2373,14 @@
|
||||
"symfony/polyfill-mbstring": "~1.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/config": "<2.8"
|
||||
"symfony/config": "<2.8",
|
||||
"symfony/yaml": "<3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "~1.0",
|
||||
"symfony/config": "~2.8|~3.0",
|
||||
"symfony/intl": "^2.8.18|^3.2.5",
|
||||
"symfony/yaml": "~2.8|~3.0"
|
||||
"symfony/yaml": "~3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "To use logging capability in translator",
|
||||
@@ -2378,7 +2390,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -2405,20 +2417,20 @@
|
||||
],
|
||||
"description": "Symfony Translation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-04-12T14:13:17+00:00"
|
||||
"time": "2017-05-22T07:42:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "fa47963ac7979ddbd42b2d646d1b056bddbf7bb8"
|
||||
"reference": "347c4247a3e40018810b476fcd5dec36d46d08dc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/fa47963ac7979ddbd42b2d646d1b056bddbf7bb8",
|
||||
"reference": "fa47963ac7979ddbd42b2d646d1b056bddbf7bb8",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/347c4247a3e40018810b476fcd5dec36d46d08dc",
|
||||
"reference": "347c4247a3e40018810b476fcd5dec36d46d08dc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2430,7 +2442,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-iconv": "*",
|
||||
"twig/twig": "~1.20|~2.0"
|
||||
"twig/twig": "~1.34|~2.4"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
|
||||
@@ -2439,7 +2451,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -2473,7 +2485,7 @@
|
||||
"debug",
|
||||
"dump"
|
||||
],
|
||||
"time": "2017-05-01T14:55:58+00:00"
|
||||
"time": "2017-06-02T09:10:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tijsverkoyen/css-to-inline-styles",
|
||||
@@ -2687,20 +2699,20 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "barryvdh/laravel-debugbar",
|
||||
"version": "v2.3.2",
|
||||
"version": "v2.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-debugbar.git",
|
||||
"reference": "24e4f0261e352d3fd86d0447791b56ae49398674"
|
||||
"reference": "de15d00a74696db62e1b4782474c27ed0c4fc763"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/24e4f0261e352d3fd86d0447791b56ae49398674",
|
||||
"reference": "24e4f0261e352d3fd86d0447791b56ae49398674",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/de15d00a74696db62e1b4782474c27ed0c4fc763",
|
||||
"reference": "de15d00a74696db62e1b4782474c27ed0c4fc763",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*",
|
||||
"illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*",
|
||||
"maximebf/debugbar": "~1.13.0",
|
||||
"php": ">=5.5.9",
|
||||
"symfony/finder": "~2.7|~3.0"
|
||||
@@ -2708,7 +2720,15 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.3-dev"
|
||||
"dev-master": "2.4-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Barryvdh\\Debugbar\\ServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Debugbar": "Barryvdh\\Debugbar\\Facade"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -2737,7 +2757,7 @@
|
||||
"profiler",
|
||||
"webprofiler"
|
||||
],
|
||||
"time": "2017-01-19T08:19:49+00:00"
|
||||
"time": "2017-06-01T17:46:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/laravel-ide-helper",
|
||||
@@ -3725,16 +3745,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "5.7.19",
|
||||
"version": "5.7.20",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1"
|
||||
"reference": "3cb94a5f8c07a03c8b7527ed7468a2926203f58b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/69c4f49ff376af2692bad9cebd883d17ebaa98a1",
|
||||
"reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3cb94a5f8c07a03c8b7527ed7468a2926203f58b",
|
||||
"reference": "3cb94a5f8c07a03c8b7527ed7468a2926203f58b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3752,7 +3772,7 @@
|
||||
"phpunit/php-timer": "^1.0.6",
|
||||
"phpunit/phpunit-mock-objects": "^3.2",
|
||||
"sebastian/comparator": "^1.2.4",
|
||||
"sebastian/diff": "~1.2",
|
||||
"sebastian/diff": "^1.4.3",
|
||||
"sebastian/environment": "^1.3.4 || ^2.0",
|
||||
"sebastian/exporter": "~2.0",
|
||||
"sebastian/global-state": "^1.1",
|
||||
@@ -3803,7 +3823,7 @@
|
||||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2017-04-03T02:22:27+00:00"
|
||||
"time": "2017-05-22T07:42:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit-mock-objects",
|
||||
@@ -4033,23 +4053,23 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/diff.git",
|
||||
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
|
||||
"reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
|
||||
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4",
|
||||
"reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
"php": "^5.3.3 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.8"
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -4081,7 +4101,7 @@
|
||||
"keywords": [
|
||||
"diff"
|
||||
],
|
||||
"time": "2015-12-08T07:14:41+00:00"
|
||||
"time": "2017-05-22T07:24:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/environment",
|
||||
@@ -4437,16 +4457,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/class-loader",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/class-loader.git",
|
||||
"reference": "fc4c04bfd17130a9dccfded9578353f311967da7"
|
||||
"reference": "386a294d621576302e7cc36965d6ed53b8c73c4f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/class-loader/zipball/fc4c04bfd17130a9dccfded9578353f311967da7",
|
||||
"reference": "fc4c04bfd17130a9dccfded9578353f311967da7",
|
||||
"url": "https://api.github.com/repos/symfony/class-loader/zipball/386a294d621576302e7cc36965d6ed53b8c73c4f",
|
||||
"reference": "386a294d621576302e7cc36965d6ed53b8c73c4f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4462,7 +4482,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -4489,27 +4509,31 @@
|
||||
],
|
||||
"description": "Symfony ClassLoader Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-04-12T14:13:17+00:00"
|
||||
"time": "2017-06-02T09:51:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/config",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/config.git",
|
||||
"reference": "e5533fcc0b3dd377626153b2852707878f363728"
|
||||
"reference": "35716d4904e0506a7a5a9bcf23f854aeb5719bca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/e5533fcc0b3dd377626153b2852707878f363728",
|
||||
"reference": "e5533fcc0b3dd377626153b2852707878f363728",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/35716d4904e0506a7a5a9bcf23f854aeb5719bca",
|
||||
"reference": "35716d4904e0506a7a5a9bcf23f854aeb5719bca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"symfony/filesystem": "~2.8|~3.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/dependency-injection": "<3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/dependency-injection": "~3.3",
|
||||
"symfony/yaml": "~3.0"
|
||||
},
|
||||
"suggest": {
|
||||
@@ -4518,7 +4542,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -4545,7 +4569,7 @@
|
||||
],
|
||||
"description": "Symfony Config Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-04-12T14:13:17+00:00"
|
||||
"time": "2017-06-02T18:07:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/dom-crawler",
|
||||
@@ -4605,16 +4629,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "040651db13cf061827a460cc10f6e36a445c45b4"
|
||||
"reference": "c709670bf64721202ddbe4162846f250735842c0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/040651db13cf061827a460cc10f6e36a445c45b4",
|
||||
"reference": "040651db13cf061827a460cc10f6e36a445c45b4",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/c709670bf64721202ddbe4162846f250735842c0",
|
||||
"reference": "c709670bf64721202ddbe4162846f250735842c0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4623,7 +4647,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -4650,20 +4674,20 @@
|
||||
],
|
||||
"description": "Symfony Filesystem Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-04-12T14:13:17+00:00"
|
||||
"time": "2017-05-28T14:08:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/stopwatch",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/stopwatch.git",
|
||||
"reference": "5a0105afb670dbd38f521105c444de1b8e10cfe3"
|
||||
"reference": "602a15299dc01556013b07167d4f5d3a60e90d15"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a0105afb670dbd38f521105c444de1b8e10cfe3",
|
||||
"reference": "5a0105afb670dbd38f521105c444de1b8e10cfe3",
|
||||
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/602a15299dc01556013b07167d4f5d3a60e90d15",
|
||||
"reference": "602a15299dc01556013b07167d4f5d3a60e90d15",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4672,7 +4696,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -4699,20 +4723,20 @@
|
||||
],
|
||||
"description": "Symfony Stopwatch Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-04-12T14:13:17+00:00"
|
||||
"time": "2017-04-12T14:14:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v3.2.8",
|
||||
"version": "v3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6"
|
||||
"reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/acec26fcf7f3031e094e910b94b002fa53d4e4d6",
|
||||
"reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/9752a30000a8ca9f4b34b5227d15d0101b96b063",
|
||||
"reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4727,7 +4751,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -4754,7 +4778,7 @@
|
||||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2017-05-01T14:55:58+00:00"
|
||||
"time": "2017-06-02T22:05:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
|
@@ -23,7 +23,7 @@ return [
|
||||
'is_demo_site' => false,
|
||||
],
|
||||
'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true),
|
||||
'version' => '4.4.3',
|
||||
'version' => '4.5.0',
|
||||
'maxUploadSize' => 5242880,
|
||||
'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'],
|
||||
'list_length' => 10,
|
||||
|
@@ -89,6 +89,7 @@ $factory->define(
|
||||
'user_id' => 1,
|
||||
'transaction_type_id' => 1,
|
||||
'bill_id' => null,
|
||||
// TODO update this transaction currency reference.
|
||||
'transaction_currency_id' => 1,
|
||||
'description' => $faker->words(3, true),
|
||||
'date' => '2017-01-01',
|
||||
@@ -216,25 +217,31 @@ $factory->define(
|
||||
$factory->define(
|
||||
FireflyIII\Models\Transaction::class, function (Faker\Generator $faker) {
|
||||
return [
|
||||
'transaction_amount' => strval($faker->randomFloat(2, -100, 100)),
|
||||
'destination_amount' => strval($faker->randomFloat(2, -100, 100)),
|
||||
'opposing_account_id' => $faker->numberBetween(1, 10),
|
||||
'source_account_id' => $faker->numberBetween(1, 10),
|
||||
'opposing_account_name' => $faker->words(3, true),
|
||||
'description' => $faker->words(3, true),
|
||||
'source_account_name' => $faker->words(3, true),
|
||||
'destination_account_id' => $faker->numberBetween(1, 10),
|
||||
'date' => new Carbon,
|
||||
'destination_account_name' => $faker->words(3, true),
|
||||
'amount' => strval($faker->randomFloat(2, -100, 100)),
|
||||
'budget_id' => 0,
|
||||
'category' => $faker->words(3, true),
|
||||
'transaction_journal_id' => $faker->numberBetween(1, 10),
|
||||
'journal_id' => $faker->numberBetween(1, 10),
|
||||
'transaction_currency_code' => 'EUR',
|
||||
'transaction_type_type' => 'Withdrawal',
|
||||
'account_encrypted' => 0,
|
||||
'account_name' => 'Some name',
|
||||
'transaction_amount' => strval($faker->randomFloat(2, -100, 100)),
|
||||
'destination_amount' => strval($faker->randomFloat(2, -100, 100)),
|
||||
'opposing_account_id' => $faker->numberBetween(1, 10),
|
||||
'source_account_id' => $faker->numberBetween(1, 10),
|
||||
'opposing_account_name' => $faker->words(3, true),
|
||||
'description' => $faker->words(3, true),
|
||||
'source_account_name' => $faker->words(3, true),
|
||||
'destination_account_id' => $faker->numberBetween(1, 10),
|
||||
'date' => new Carbon,
|
||||
'destination_account_name' => $faker->words(3, true),
|
||||
'amount' => strval($faker->randomFloat(2, -100, 100)),
|
||||
'budget_id' => 0,
|
||||
'category' => $faker->words(3, true),
|
||||
'transaction_journal_id' => $faker->numberBetween(1, 10),
|
||||
'journal_id' => $faker->numberBetween(1, 10),
|
||||
'transaction_currency_code' => 'EUR',
|
||||
'transaction_type_type' => 'Withdrawal',
|
||||
'account_encrypted' => 0,
|
||||
'account_name' => 'Some name',
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_currency_symbol' => '€',
|
||||
'foreign_destination_amount' => null,
|
||||
'foreign_currency_id' => null,
|
||||
'foreign_currency_code' => null,
|
||||
'foreign_currency_symbol' => null,
|
||||
];
|
||||
}
|
||||
);
|
42
database/migrations/2017_06_02_105232_changes_for_v450.php
Normal file
42
database/migrations/2017_06_02_105232_changes_for_v450.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
/**
|
||||
* Class ChangesForV450
|
||||
*/
|
||||
class ChangesForV450 extends Migration
|
||||
{
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// add "foreign_amount" to transactions
|
||||
Schema::table(
|
||||
'transactions', function (Blueprint $table) {
|
||||
$table->decimal('foreign_amount', 22, 12)->nullable()->after('amount');
|
||||
}
|
||||
);
|
||||
|
||||
// add foreign transaction currency id to transactions (is nullable):
|
||||
Schema::table(
|
||||
'transactions', function (Blueprint $table) {
|
||||
$table->integer('foreign_currency_id', false, true)->default(null)->after('foreign_amount')->nullable();
|
||||
$table->foreign('foreign_currency_id')->references('id')->on('transaction_currencies')->onDelete('set null');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@@ -8,7 +8,7 @@
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
/** global: spent, budgeted, available, currencySymbol */
|
||||
/** global: spent, budgeted, available, currencySymbol, budgetIndexURI */
|
||||
|
||||
function drawSpentBar() {
|
||||
"use strict";
|
||||
@@ -59,9 +59,26 @@ function updateBudgetedAmounts(e) {
|
||||
"use strict";
|
||||
var target = $(e.target);
|
||||
var id = target.data('id');
|
||||
|
||||
var value = target.val();
|
||||
var original = target.data('original');
|
||||
var difference = value - original;
|
||||
|
||||
var spentCell = $('td[class="spent"][data-id="' + id + '"]');
|
||||
var leftCell = $('td[class="left"][data-id="' + id + '"]');
|
||||
var spentAmount = parseFloat(spentCell.data('spent'));
|
||||
var newAmountLeft = spentAmount + parseFloat(value);
|
||||
var amountLeftString = accounting.formatMoney(newAmountLeft);
|
||||
if(newAmountLeft < 0) {
|
||||
leftCell.html('<span class="text-danger">' + amountLeftString + '</span>');
|
||||
}
|
||||
if(newAmountLeft > 0) {
|
||||
leftCell.html('<span class="text-success">' + amountLeftString + '</span>');
|
||||
}
|
||||
if(newAmountLeft === 0.0) {
|
||||
leftCell.html('<span style="color:#999">' + amountLeftString + '</span>');
|
||||
}
|
||||
|
||||
if (difference !== 0) {
|
||||
// add difference to 'budgeted' var
|
||||
budgeted = budgeted + difference;
|
||||
@@ -99,6 +116,15 @@ $(function () {
|
||||
*/
|
||||
$('input[type="number"]').on('input', updateBudgetedAmounts);
|
||||
|
||||
//
|
||||
$('.selectPeriod').change(function (e) {
|
||||
var sel = $(e.target).val();
|
||||
if (sel !== "x") {
|
||||
var newURI = budgetIndexURI.replace("REPLACE", sel);
|
||||
window.location.assign(newURI);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function updateIncome() {
|
||||
|
@@ -8,7 +8,7 @@
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
/** global: budgetChartUri,budgetLimitID */
|
||||
/** global: budgetChartUri, expenseCategoryUri, expenseAssetUri, expenseExpenseUri, budgetLimitID */
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
|
@@ -13,10 +13,7 @@
|
||||
$(function () {
|
||||
"use strict";
|
||||
|
||||
console.log('Getting charts');
|
||||
columnChart(everything, 'category-everything');
|
||||
|
||||
console.log('Specific: ' + specific);
|
||||
columnChart(specific, 'specific-period');
|
||||
|
||||
});
|
@@ -75,7 +75,7 @@ function lineChart(URI, container) {
|
||||
"use strict";
|
||||
|
||||
var colorData = true;
|
||||
var options = defaultChartOptions;
|
||||
var options = $.extend(true, {}, defaultChartOptions);
|
||||
var chartType = 'line';
|
||||
|
||||
drawAChart(URI, container, chartType, options, colorData);
|
||||
@@ -186,9 +186,8 @@ function doubleYNonStackedChart(URI, container) {
|
||||
*/
|
||||
function columnChart(URI, container) {
|
||||
"use strict";
|
||||
console.log('Going to draw column chart for ' + URI + ' in ' + container);
|
||||
var colorData = true;
|
||||
var options = defaultChartOptions;
|
||||
var options = $.extend(true, {}, defaultChartOptions);
|
||||
var chartType = 'bar';
|
||||
|
||||
drawAChart(URI, container, chartType, options, colorData);
|
||||
@@ -224,7 +223,7 @@ function pieChart(URI, container) {
|
||||
"use strict";
|
||||
|
||||
var colorData = false;
|
||||
var options = defaultPieOptions;
|
||||
var options = $.extend(true, {}, defaultPieOptions);
|
||||
var chartType = 'pie';
|
||||
|
||||
drawAChart(URI, container, chartType, options, colorData);
|
||||
|
@@ -104,10 +104,22 @@ function callExport() {
|
||||
// show download
|
||||
showDownload();
|
||||
|
||||
}).fail(function () {
|
||||
}).fail(function (data) {
|
||||
// show error.
|
||||
// show form again.
|
||||
showError('The export failed. Please check the log files to find out why.');
|
||||
|
||||
var errorText = 'The export failed. Please check the log files to find out why.';
|
||||
if (typeof data.responseJSON === 'object') {
|
||||
errorText = '';
|
||||
for (var propt in data.responseJSON) {
|
||||
if (data.responseJSON.hasOwnProperty(propt)) {
|
||||
errorText += propt + ': ' + data.responseJSON[propt][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showError(errorText);
|
||||
|
||||
|
||||
// stop polling:
|
||||
window.clearTimeout(intervalId);
|
||||
|
@@ -15,19 +15,19 @@ $(function () {
|
||||
drawChart();
|
||||
|
||||
$('#categories-in-pie-chart-checked').on('change', function () {
|
||||
redrawPieChart('categories-in-pie-chart', categoryIncomeUri);
|
||||
redrawPieChart(categoryIncomeUri, 'categories-in-pie-chart');
|
||||
});
|
||||
|
||||
$('#categories-out-pie-chart-checked').on('change', function () {
|
||||
redrawPieChart('categories-out-pie-chart', categoryExpenseUri);
|
||||
redrawPieChart(categoryExpenseUri, 'categories-out-pie-chart');
|
||||
});
|
||||
|
||||
$('#accounts-in-pie-chart-checked').on('change', function () {
|
||||
redrawPieChart('accounts-in-pie-chart', accountIncomeUri);
|
||||
redrawPieChart(accountIncomeUri, 'accounts-in-pie-chart');
|
||||
});
|
||||
|
||||
$('#accounts-out-pie-chart-checked').on('change', function () {
|
||||
redrawPieChart('accounts-out-pie-chart', accountExpenseUri);
|
||||
redrawPieChart(accountExpenseUri, 'accounts-out-pie-chart');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -40,15 +40,14 @@ function drawChart() {
|
||||
doubleYChart(mainUri, 'in-out-chart');
|
||||
|
||||
// draw pie chart of income, depending on "show other transactions too":
|
||||
redrawPieChart('categories-in-pie-chart', categoryIncomeUri);
|
||||
redrawPieChart('categories-out-pie-chart', categoryExpenseUri);
|
||||
redrawPieChart('accounts-in-pie-chart', accountIncomeUri);
|
||||
redrawPieChart('accounts-out-pie-chart', accountExpenseUri);
|
||||
|
||||
redrawPieChart(categoryIncomeUri, 'categories-in-pie-chart');
|
||||
redrawPieChart(categoryExpenseUri, 'categories-out-pie-chart');
|
||||
redrawPieChart(accountIncomeUri, 'accounts-in-pie-chart');
|
||||
redrawPieChart(accountExpenseUri, 'accounts-out-pie-chart');
|
||||
|
||||
}
|
||||
|
||||
function redrawPieChart(container, uri) {
|
||||
function redrawPieChart(uri, container) {
|
||||
"use strict";
|
||||
var checkbox = $('#' + container + '-checked');
|
||||
|
||||
|
@@ -6,6 +6,8 @@
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
/** global: zoomLevel, latitude, longitude, google, doPlaceMarker */
|
||||
|
||||
/*
|
||||
Some vars as prep for the map:
|
||||
*/
|
||||
|
@@ -6,6 +6,8 @@
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
/** global: Modernizr, accountInfo, currencyInfo, accountInfo, transferInstructions, what */
|
||||
|
||||
$(document).ready(function () {
|
||||
"use strict";
|
||||
setCommonAutocomplete();
|
||||
@@ -73,7 +75,6 @@ function selectsForeignCurrency() {
|
||||
var nativeCurrencyId = parseInt(accountInfo[selectedAccountId].preferredCurrency);
|
||||
|
||||
if (foreignCurrencyId !== nativeCurrencyId) {
|
||||
console.log('User has selected currency #' + foreignCurrencyId + ' and this is different from native currency #' + nativeCurrencyId);
|
||||
|
||||
// the input where the native amount is entered gets the symbol for the native currency:
|
||||
$('.non-selectable-currency-symbol').text(currencyInfo[nativeCurrencyId].symbol);
|
||||
@@ -90,7 +91,6 @@ function selectsForeignCurrency() {
|
||||
|
||||
}
|
||||
if (foreignCurrencyId === nativeCurrencyId) {
|
||||
console.log('User has selected currency #' + foreignCurrencyId + ' and this is equal to native currency #' + nativeCurrencyId + ' (phew).');
|
||||
$('#exchange_rate_instruction_holder').hide();
|
||||
$('#native_amount_holder').hide();
|
||||
}
|
||||
@@ -110,7 +110,6 @@ function convertForeignToNative() {
|
||||
var date = $('#ffInput_date').val();
|
||||
var amount = $('#ffInput_amount').val();
|
||||
var uri = 'json/rate/' + foreignCurrencyCode + '/' + nativeCurrencyCode + '/' + date + '?amount=' + amount;
|
||||
console.log('Will grab ' + uri);
|
||||
$.get(uri).done(updateNativeAmount);
|
||||
}
|
||||
|
||||
@@ -119,8 +118,6 @@ function convertForeignToNative() {
|
||||
* @param data
|
||||
*/
|
||||
function updateNativeAmount(data) {
|
||||
console.log('Returned data:');
|
||||
console.log(data);
|
||||
$('#ffInput_native_amount').val(data.amount);
|
||||
}
|
||||
|
||||
@@ -158,12 +155,10 @@ function validateCurrencyForTransfer() {
|
||||
$('#source_amount_holder').show().find('.non-selectable-currency-symbol').text(sourceSymbol);
|
||||
|
||||
if (sourceCurrency === destinationCurrency) {
|
||||
console.log('Both accounts accept ' + sourceCurrency);
|
||||
$('#destination_amount_holder').hide();
|
||||
$('#amount_holder').hide();
|
||||
return;
|
||||
}
|
||||
console.log('Source accepts #' + sourceCurrency + ', destination #' + destinationCurrency);
|
||||
$('#ffInput_exchange_rate_instruction').text(getTransferExchangeInstructions());
|
||||
$('#exchange_rate_instruction_holder').show();
|
||||
$('input[name="source_amount"]').val($('input[name="amount"]').val());
|
||||
@@ -191,7 +186,6 @@ function convertSourceToDestination() {
|
||||
var amount = $('#ffInput_source_amount').val();
|
||||
$('#ffInput_amount').val(amount);
|
||||
var uri = 'json/rate/' + sourceCurrencyCode + '/' + destinationCurrencyCode + '/' + date + '?amount=' + amount;
|
||||
console.log('Will grab ' + uri);
|
||||
$.get(uri).done(updateDestinationAmount);
|
||||
}
|
||||
|
||||
@@ -200,7 +194,5 @@ function convertSourceToDestination() {
|
||||
* @param data
|
||||
*/
|
||||
function updateDestinationAmount(data) {
|
||||
console.log('Returned data:');
|
||||
console.log(data);
|
||||
$('#ffInput_destination_amount').val(data.amount);
|
||||
}
|
@@ -6,7 +6,7 @@
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
/** global: what,Modernizr, title, breadcrumbs, middleCrumbName, button, piggiesLength, txt, middleCrumbUrl,exchangeRateInstructions */
|
||||
/** global: currencyInfo, accountInfo, what,Modernizr, title, breadcrumbs, middleCrumbName, button, piggiesLength, txt, middleCrumbUrl,exchangeRateInstructions, convertForeignToNative, convertSourceToDestination, selectsForeignCurrency, accountInfo */
|
||||
|
||||
$(document).ready(function () {
|
||||
"use strict";
|
||||
@@ -64,11 +64,11 @@ function updateNativeCurrency() {
|
||||
var newAccountId = getAccountId();
|
||||
var nativeCurrencyId = accountInfo[newAccountId].preferredCurrency;
|
||||
|
||||
console.log('User selected account #' + newAccountId + '. Native currency is #' + nativeCurrencyId);
|
||||
|
||||
$('.currency-option[data-id="' + nativeCurrencyId + '"]').click();
|
||||
$('[data-toggle="dropdown"]').parent().removeClass('open');
|
||||
$('select[name="source_account_id"]').focus();
|
||||
if (what !== 'transfer') {
|
||||
$('select[name="source_account_id"]').focus();
|
||||
}
|
||||
|
||||
validateCurrencyForTransfer();
|
||||
}
|
||||
@@ -105,6 +105,7 @@ function updateForm() {
|
||||
var srcName = $('#ffInput_source_account_name');
|
||||
|
||||
switch (what) {
|
||||
|
||||
case 'withdrawal':
|
||||
// show source_id and dest_name
|
||||
document.getElementById('source_account_id_holder').style.display = 'block';
|
||||
@@ -176,6 +177,8 @@ function updateForm() {
|
||||
}
|
||||
document.getElementById('piggy_bank_id_holder').style.display = showPiggies;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
updateNativeCurrency();
|
||||
}
|
||||
@@ -230,4 +233,5 @@ function getAccountId() {
|
||||
if (what === "deposit" || what === "transfer") {
|
||||
return $('select[name="destination_account_id"]').val();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
/** global: what, Modernizr */
|
||||
/** global: what, Modernizr, selectsForeignCurrency, convertForeignToNative, validateCurrencyForTransfer, convertSourceToDestination, journalData, journal, accountInfo, exchangeRateInstructions, currencyInfo */
|
||||
|
||||
$(document).ready(function () {
|
||||
"use strict";
|
||||
@@ -32,17 +32,16 @@ $(document).ready(function () {
|
||||
*/
|
||||
function updateInitialPage() {
|
||||
|
||||
console.log('Native currency is #' + journalData.native_currency.id + ' and (foreign) currency id is #' + journalData.currency.id);
|
||||
|
||||
if (journal.transaction_type.type === "Transfer") {
|
||||
$('#native_amount_holder').hide();
|
||||
$('#amount_holder').hide();
|
||||
|
||||
if (journalData.native_currency.id === journalData.currency.id) {
|
||||
|
||||
if (journalData.native_currency.id === journalData.destination_currency.id) {
|
||||
$('#exchange_rate_instruction_holder').hide();
|
||||
$('#destination_amount_holder').hide();
|
||||
}
|
||||
if (journalData.native_currency.id !== journalData.currency.id) {
|
||||
if (journalData.native_currency.id !== journalData.destination_currency.id) {
|
||||
$('#exchange_rate_instruction_holder').show().find('p').text(getTransferExchangeInstructions());
|
||||
|
||||
}
|
||||
@@ -78,6 +77,7 @@ function getAccountId() {
|
||||
}
|
||||
|
||||
alert('Cannot handle ' + journal.transaction_type.type);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -166,11 +166,31 @@ function resetSplits() {
|
||||
var input = $(v);
|
||||
input.attr('name', 'transactions[' + i + '][amount]');
|
||||
});
|
||||
|
||||
// ends with ][foreign_amount]
|
||||
$.each($('input[name$="][foreign_amount]"]'), function (i, v) {
|
||||
var input = $(v);
|
||||
input.attr('name', 'transactions[' + i + '][foreign_amount]');
|
||||
});
|
||||
|
||||
// ends with ][transaction_currency_id]
|
||||
$.each($('input[name$="][transaction_currency_id]"]'), function (i, v) {
|
||||
var input = $(v);
|
||||
input.attr('name', 'transactions[' + i + '][transaction_currency_id]');
|
||||
});
|
||||
|
||||
// ends with ][foreign_currency_id]
|
||||
$.each($('input[name$="][foreign_currency_id]"]'), function (i, v) {
|
||||
var input = $(v);
|
||||
input.attr('name', 'transactions[' + i + '][foreign_currency_id]');
|
||||
});
|
||||
|
||||
// ends with ][budget_id]
|
||||
$.each($('select[name$="][budget_id]"]'), function (i, v) {
|
||||
var input = $(v);
|
||||
input.attr('name', 'transactions[' + i + '][budget_id]');
|
||||
});
|
||||
|
||||
// ends with ][category]
|
||||
$.each($('input[name$="][category]"]'), function (i, v) {
|
||||
var input = $(v);
|
||||
|
@@ -20,6 +20,7 @@ return [
|
||||
'everything' => 'Alle',
|
||||
'customRange' => 'Individueller Bereich',
|
||||
'apply' => 'Übernehmen',
|
||||
'select_date' => 'Select date..',
|
||||
'cancel' => 'Abbrechen',
|
||||
'from' => 'Von',
|
||||
'to' => 'Bis',
|
||||
@@ -112,8 +113,8 @@ return [
|
||||
'budget_in_period' => 'All transactions for budget ":name" between :start and :end',
|
||||
'chart_budget_in_period' => 'Chart for all transactions for budget ":name" between :start and :end',
|
||||
'chart_account_in_period' => 'Chart for all transactions for account ":name" between :start and :end',
|
||||
'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
|
||||
'chart_category_all' => 'Chart for all transactions for category ":name"',
|
||||
'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
|
||||
'chart_category_all' => 'Chart for all transactions for category ":name"',
|
||||
'budget_in_period_breadcrumb' => 'Zwischen :start und :end',
|
||||
'clone_withdrawal' => 'Diese Ausgabe klonen',
|
||||
'clone_deposit' => 'Diese Einnahme klonen',
|
||||
@@ -963,6 +964,7 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?',
|
||||
'split_this_transfer' => 'Diese Überweisung aufteilen',
|
||||
'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.',
|
||||
'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.',
|
||||
'cannot_edit_opening_balance' => 'You cannot edit the opening balance of an account.',
|
||||
'no_edit_multiple_left' => 'You have selected no valid transactions to edit.',
|
||||
|
||||
// import
|
||||
|
@@ -20,6 +20,7 @@ return [
|
||||
'everything' => 'Everything',
|
||||
'customRange' => 'Custom range',
|
||||
'apply' => 'Apply',
|
||||
'select_date' => 'Select date..',
|
||||
'cancel' => 'Cancel',
|
||||
'from' => 'From',
|
||||
'to' => 'To',
|
||||
@@ -112,8 +113,8 @@ return [
|
||||
'budget_in_period' => 'All transactions for budget ":name" between :start and :end',
|
||||
'chart_budget_in_period' => 'Chart for all transactions for budget ":name" between :start and :end',
|
||||
'chart_account_in_period' => 'Chart for all transactions for account ":name" between :start and :end',
|
||||
'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
|
||||
'chart_category_all' => 'Chart for all transactions for category ":name"',
|
||||
'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
|
||||
'chart_category_all' => 'Chart for all transactions for category ":name"',
|
||||
'budget_in_period_breadcrumb' => 'Between :start and :end',
|
||||
'clone_withdrawal' => 'Clone this withdrawal',
|
||||
'clone_deposit' => 'Clone this deposit',
|
||||
@@ -962,6 +963,7 @@ return [
|
||||
'split_this_transfer' => 'Split this transfer',
|
||||
'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.',
|
||||
'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.',
|
||||
'cannot_edit_opening_balance' => 'You cannot edit the opening balance of an account.',
|
||||
'no_edit_multiple_left' => 'You have selected no valid transactions to edit.',
|
||||
|
||||
// import
|
||||
|
28
resources/lang/es_ES/auth.php
Normal file
28
resources/lang/es_ES/auth.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* auth.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.
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used during authentication for various
|
||||
| messages that we need to display to the user. You are free to modify
|
||||
| these language lines according to your application's requirements.
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => 'Las credenciales no coinciden con los registros.',
|
||||
'throttle' => 'Demasiados intentos de inicio de sesión. Por favor reintente en :seconds segundos.',
|
||||
|
||||
];
|
41
resources/lang/es_ES/breadcrumbs.php
Normal file
41
resources/lang/es_ES/breadcrumbs.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* breadcrumbs.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.
|
||||
*/
|
||||
|
||||
return [
|
||||
'home' => 'Inicio',
|
||||
'edit_currency' => 'Editar moneda ":name"',
|
||||
'delete_currency' => 'Eliminar moneda ":name"',
|
||||
'newPiggyBank' => 'Crear nueva alcancía',
|
||||
'edit_piggyBank' => 'Editar alcancía ":name"',
|
||||
'preferences' => 'Preferencias',
|
||||
'profile' => 'Perfil',
|
||||
'changePassword' => 'Cambiar contraseña',
|
||||
'bills' => 'Facturas',
|
||||
'newBill' => 'Nueva factura',
|
||||
'edit_bill' => 'Editar factura ":name"',
|
||||
'delete_bill' => 'Eliminar factura ":name"',
|
||||
'reports' => 'Reportes',
|
||||
'searchResult' => 'Buscar ":query"',
|
||||
'withdrawal_list' => 'Gastos',
|
||||
'deposit_list' => 'Ganancia, ingresos y depósitos',
|
||||
'transfer_list' => 'Transferencias',
|
||||
'transfers_list' => 'Transferencias',
|
||||
'create_withdrawal' => 'Crear nuevo retiro',
|
||||
'create_deposit' => 'Crear nuevo depósito',
|
||||
'create_transfer' => 'Crear nueva transferencia',
|
||||
'edit_journal' => 'Editar transacción ":description"',
|
||||
'delete_journal' => 'Eliminar transacción ":description"',
|
||||
'tags' => 'Etiquetas',
|
||||
'createTag' => 'Crear nueva etiqueta',
|
||||
'edit_tag' => 'Editar etiqueta ":tag"',
|
||||
'delete_tag' => 'Eliminar etiqueta ":tag"',
|
||||
];
|
23
resources/lang/es_ES/config.php
Normal file
23
resources/lang/es_ES/config.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* config.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.
|
||||
*/
|
||||
|
||||
return [
|
||||
'locale' => 'es, Spanish, es_ES, es_ES.utf8, es_ES.UTF-8',
|
||||
'month' => '%B %Y',
|
||||
'month_and_day' => '%B %e, %Y',
|
||||
'date_time' => '%B %e, %Y, @ %T',
|
||||
'specific_day' => '%e %B %Y',
|
||||
'week_in_year' => 'Semana %W, %Y',
|
||||
'quarter_of_year' => '%B %Y',
|
||||
'year' => '%Y',
|
||||
'half_year' => '%B %Y',
|
||||
|
||||
];
|
80
resources/lang/es_ES/csv.php
Normal file
80
resources/lang/es_ES/csv.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* csv.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);
|
||||
|
||||
return [
|
||||
|
||||
'import_configure_title' => 'Configurar su importación',
|
||||
'import_configure_intro' => 'Hay algunas opciones para su importación desde CSV. Por facor indique si su CSV contiene encabezados en la primera fila, y cuál es el formato de fecha utilizado. ¡Puede requerir un poco de experimentación! El delimitador de campos es usualmente ",", pero también puede ser ";". Verifíquelo cuidadosamente.',
|
||||
'import_configure_form' => 'Opciones básicas de importación desde CSV',
|
||||
'header_help' => 'Marque aquí si el CSV contiene títulos de columna en la primera fila',
|
||||
'date_help' => 'Formato de fecha y hora en el CSV. Siga el formato que <a href="https://secure.php.net/manual/es/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">esta página</a> indica. El valor por defecto interpretará fechas que se vean así: :dateExample.',
|
||||
'delimiter_help' => 'Elija el delimitador de campos del archivo de entrada. Si no está seguro, la coma es la opción más segura.',
|
||||
'import_account_help' => 'Si el archivo NO contiene información sobre su(s) caja(s) de ahorros seleccion una opción para definir a qué cuenta pertenecen las transacciones del CSV.',
|
||||
'upload_not_writeable' => 'El texto en gris indica un directorio. Debe tener permiso de escritura. Por favor verifíquelo.',
|
||||
|
||||
// roles
|
||||
'column_roles_title' => 'Definir roles de las columnas',
|
||||
'column_roles_table' => 'Tabla',
|
||||
'column_name' => 'Nombre de la columna',
|
||||
'column_example' => 'Ejemplo de datos de columna',
|
||||
'column_role' => 'Significado de los datos de la columna',
|
||||
'do_map_value' => 'Mapear estos valores',
|
||||
'column' => 'Columna',
|
||||
'no_example_data' => 'No hay datos de ejemplo disponibles',
|
||||
'store_column_roles' => 'Continuar importación',
|
||||
'do_not_map' => '(no mapear)',
|
||||
'map_title' => 'Conectar datos de importación con datos de Firefly-III',
|
||||
'map_text' => 'En las siguientes tablas el valor de la izquierda muestra información encontrada en el CSV cargado. Es su tarea mapear este valor, si es posible, a un valor ya presente en su base de datos. Firefly respeterá este mapeo. Si no hay un valor hacia el cual mapear o no desea mapear un valor específico, no seleccione ninguno.',
|
||||
|
||||
'field_value' => 'Valor del campo',
|
||||
'field_mapped_to' => 'Mapeado a',
|
||||
'store_column_mapping' => 'Guardar mapeo',
|
||||
|
||||
// map things.
|
||||
|
||||
|
||||
'column__ignore' => '(ignorar esta columna)',
|
||||
'column_account-iban' => 'Caja de ahorro (CBU)',
|
||||
'column_account-id' => 'ID de la caja de ahorro (coincide con Firefly)',
|
||||
'column_account-name' => 'Caja de ahorro (nombre)',
|
||||
'column_amount' => 'Monto',
|
||||
'column_amount-comma-separated' => 'Monto (coma como separador de decimales)',
|
||||
'column_bill-id' => 'ID de factura (coincide con Firefly)',
|
||||
'column_bill-name' => 'Nombre de factura',
|
||||
'column_budget-id' => 'ID de presupuesto (coincide con Firefly)',
|
||||
'column_budget-name' => 'Nombre de presupuesto',
|
||||
'column_category-id' => 'ID de categoría (coincide con Firefly)',
|
||||
'column_category-name' => 'Nombre de categoría',
|
||||
'column_currency-code' => 'Código de moneda (ISO 4217)',
|
||||
'column_currency-id' => 'ID de moneda (coincide con Firefly)',
|
||||
'column_currency-name' => 'Nombre de moneda (coincide con Firefly)',
|
||||
'column_currency-symbol' => 'Símbolo de moneda (coincide con Firefly)',
|
||||
'column_date-interest' => 'Fecha de cálculo de intereses',
|
||||
'column_date-book' => 'Fecha de registro de transacción',
|
||||
'column_date-process' => 'Fecha de proceso de transacción',
|
||||
'column_date-transaction' => 'Fecha',
|
||||
'column_description' => 'Descripción',
|
||||
'column_opposing-iban' => 'Cuenta opuesta (CBU)',
|
||||
'column_opposing-id' => 'ID de cuenta opuesta (coincide con Firefly)',
|
||||
'column_external-id' => 'ID externo',
|
||||
'column_opposing-name' => 'Cuenta opuesta (nombre)',
|
||||
'column_rabo-debet-credit' => 'Indicador de débito/crédito específico de Rabobank',
|
||||
'column_ing-debet-credit' => 'Indicador de débito/crédito específico de ING',
|
||||
'column_sepa-ct-id' => 'ID de transferencia de crédito end-to-end de SEPA',
|
||||
'column_sepa-ct-op' => 'Transferencia de crédito a cuenta opuesta SEPA',
|
||||
'column_sepa-db' => 'Débito directo SEPA',
|
||||
'column_tags-comma' => 'Etiquetas (separadas por comas)',
|
||||
'column_tags-space' => 'Etiquetas (separadas por espacios)',
|
||||
'column_account-number' => 'Caja de ahorro (número de cuenta)',
|
||||
'column_opposing-number' => 'Cuenta opuesta (número de cuenta)',
|
||||
];
|
24
resources/lang/es_ES/demo.php
Normal file
24
resources/lang/es_ES/demo.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* demo.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.
|
||||
*/
|
||||
|
||||
return [
|
||||
'no_demo_text' => 'Lamentablemente no hay textos de ayuda para <abbr title=":route">esta página</abbr>.',
|
||||
'see_help_icon' => 'Sin embargo, el ícono <i class="fa fa-question-circle"></i> en la esquina superior-derecha puede tener más información.',
|
||||
'index' => '¡Bienvenido a <strong>Firefly III</strong>! En esta página tendrá una vista rápida de sus finanzas. Para más información, mire sus cuentas rarr; <a href=":asset">Asset Accounts</a> y, claro, las páginas de <a href=":budgets">presupuestos</a> y <a href=":reports">reportes</a>. O simplemente investigue la aplicación por su cuenta.',
|
||||
'accounts-index' => 'Las cajas de ahorro son sus cuentas de banco personales. Las cuentas de gastos contienen sus gastos habituales como compras y salidas con amigos. Las cuentas de ingresos repesentan ingresos de su trabajo u otras fuentes. En esta página puede editarlas o eliminarlas.',
|
||||
'budgets-index' => 'Esta página le muestra una visión general de sus presupuestos. La barra superior muestra la cantidad que está disponible para ser presupuestado. Esto se puede personalizar para cualquier período haciendo clic en la cantidad a la derecha. La cantidad que has gastado hasta ahora se muestra en la barra de abajo. Debajo están los gastos por presupuesto y lo que ha presupuestado para ellos.',
|
||||
'reports-index-start' => 'Firefly III admite cuatro tipos de informes. Lea sobre ellos haciendo clic en el icono <i class="fa fa-question-circle"> en la esquina superior derecha.',
|
||||
'reports-index-examples' => 'Asegúrese de revisar estos ejemplos: <a href=":one"> un resumen financiero mensual </a>, <a href=":two"> un resumen financiero anual </a> y <a href=":three"> una vista general del presupuesto</a>.',
|
||||
'currencies-index' => 'Firefly III admite múltiples monedas. A pesar de que la moneda por defecto es el Euro, se puede seleccionar el Dólar de EE.UU, y muchas otras monedas. Como se puede ver se ha incluido una pequeña selección de monedas, pero puedes agregar tu propia moneda si lo deseas. Sin embargo, cambiar la moneda predeterminada no cambiará la moneda de las transacciones existentes: Firefly III admite el uso de varias monedas al mismo tiempo.',
|
||||
'transactions-index' => 'Estos gastos, depósitos y transferencias no son particularmente imaginativos. Se han generado automáticamente.',
|
||||
'piggy-banks-index' => 'Como puede ver, hay tres alcancías. Utilice los botones más y menos para influir en la cantidad de dinero en cada alcancía. Haga clic en el nombre de la alcancía para ver la administración de cada una.',
|
||||
'import-index' => 'Por supuesto, cualquier archivo CSV puede ser importado en Firefly III',
|
||||
'import-configure-security' => 'Debido a problemas de seguridad, su subida se ha sustituido por un archivo local.',
|
||||
'import-configure-configuration' => 'La configuración que ves a continuación es correcta para el archivo local.',
|
||||
];
|
1065
resources/lang/es_ES/firefly.php
Normal file
1065
resources/lang/es_ES/firefly.php
Normal file
File diff suppressed because it is too large
Load Diff
189
resources/lang/es_ES/form.php
Normal file
189
resources/lang/es_ES/form.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
/**
|
||||
* form.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.
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
// new user:
|
||||
'bank_name' => 'Bank name',
|
||||
'bank_balance' => 'Balance',
|
||||
'savings_balance' => 'Savings balance',
|
||||
'credit_card_limit' => 'Credit card limit',
|
||||
'automatch' => 'Match automatically',
|
||||
'skip' => 'Skip',
|
||||
'name' => 'Name',
|
||||
'active' => 'Active',
|
||||
'amount_min' => 'Minimum amount',
|
||||
'amount_max' => 'Maximum amount',
|
||||
'match' => 'Matches on',
|
||||
'repeat_freq' => 'Repeats',
|
||||
'journal_currency_id' => 'Currency',
|
||||
'currency_id' => 'Currency',
|
||||
'attachments' => 'Attachments',
|
||||
'journal_amount' => 'Amount',
|
||||
'journal_asset_source_account' => 'Asset account (source)',
|
||||
'journal_source_account_name' => 'Revenue account (source)',
|
||||
'journal_source_account_id' => 'Asset account (source)',
|
||||
'BIC' => 'BIC',
|
||||
'account_from_id' => 'From account',
|
||||
'account_to_id' => 'To account',
|
||||
'source_account' => 'Source account',
|
||||
'destination_account' => 'Destination account',
|
||||
'journal_destination_account_id' => 'Asset account (destination)',
|
||||
'asset_destination_account' => 'Asset account (destination)',
|
||||
'asset_source_account' => 'Asset account (source)',
|
||||
'journal_description' => 'Description',
|
||||
'note' => 'Notes',
|
||||
'split_journal' => 'Split this transaction',
|
||||
'split_journal_explanation' => 'Split this transaction in multiple parts',
|
||||
'currency' => 'Currency',
|
||||
'account_id' => 'Asset account',
|
||||
'budget_id' => 'Budget',
|
||||
'openingBalance' => 'Opening balance',
|
||||
'tagMode' => 'Tag mode',
|
||||
'tagPosition' => 'Tag location',
|
||||
'virtualBalance' => 'Virtual balance',
|
||||
'longitude_latitude' => 'Location',
|
||||
'targetamount' => 'Target amount',
|
||||
'accountRole' => 'Account role',
|
||||
'openingBalanceDate' => 'Opening balance date',
|
||||
'ccType' => 'Credit card payment plan',
|
||||
'ccMonthlyPaymentDate' => 'Credit card monthly payment date',
|
||||
'piggy_bank_id' => 'Piggy bank',
|
||||
'returnHere' => 'Return here',
|
||||
'returnHereExplanation' => 'After storing, return here to create another one.',
|
||||
'returnHereUpdateExplanation' => 'After updating, return here.',
|
||||
'description' => 'Description',
|
||||
'expense_account' => 'Expense account',
|
||||
'revenue_account' => 'Revenue account',
|
||||
'decimal_places' => 'Decimal places',
|
||||
'exchange_rate_instruction' => 'Foreign currencies',
|
||||
'exchanged_amount' => 'Exchanged amount',
|
||||
'source_amount' => 'Amount (source)',
|
||||
'destination_amount' => 'Amount (destination)',
|
||||
'native_amount' => 'Native amount',
|
||||
|
||||
'revenue_account_source' => 'Revenue account (source)',
|
||||
'source_account_asset' => 'Source account (asset account)',
|
||||
'destination_account_expense' => 'Destination account (expense account)',
|
||||
'destination_account_asset' => 'Destination account (asset account)',
|
||||
'source_account_revenue' => 'Source account (revenue account)',
|
||||
'type' => 'Type',
|
||||
'convert_Withdrawal' => 'Convert withdrawal',
|
||||
'convert_Deposit' => 'Convert deposit',
|
||||
'convert_Transfer' => 'Convert transfer',
|
||||
|
||||
|
||||
'amount' => 'Amount',
|
||||
'date' => 'Date',
|
||||
'interest_date' => 'Interest date',
|
||||
'book_date' => 'Book date',
|
||||
'process_date' => 'Processing date',
|
||||
'category' => 'Category',
|
||||
'tags' => 'Tags',
|
||||
'deletePermanently' => 'Delete permanently',
|
||||
'cancel' => 'Cancel',
|
||||
'targetdate' => 'Target date',
|
||||
'tag' => 'Tag',
|
||||
'under' => 'Under',
|
||||
'symbol' => 'Symbol',
|
||||
'code' => 'Code',
|
||||
'iban' => 'IBAN',
|
||||
'accountNumber' => 'Account number',
|
||||
'has_headers' => 'Headers',
|
||||
'date_format' => 'Date format',
|
||||
'specifix' => 'Bank- or file specific fixes',
|
||||
'attachments[]' => 'Attachments',
|
||||
'store_new_withdrawal' => 'Store new withdrawal',
|
||||
'store_new_deposit' => 'Store new deposit',
|
||||
'store_new_transfer' => 'Store new transfer',
|
||||
'add_new_withdrawal' => 'Add a new withdrawal',
|
||||
'add_new_deposit' => 'Add a new deposit',
|
||||
'add_new_transfer' => 'Add a new transfer',
|
||||
'noPiggybank' => '(no piggy bank)',
|
||||
'title' => 'Title',
|
||||
'notes' => 'Notes',
|
||||
'filename' => 'File name',
|
||||
'mime' => 'Mime type',
|
||||
'size' => 'Size',
|
||||
'trigger' => 'Trigger',
|
||||
'stop_processing' => 'Stop processing',
|
||||
'start_date' => 'Start of range',
|
||||
'end_date' => 'End of range',
|
||||
'export_start_range' => 'Start of export range',
|
||||
'export_end_range' => 'End of export range',
|
||||
'export_format' => 'File format',
|
||||
'include_attachments' => 'Include uploaded attachments',
|
||||
'include_old_uploads' => 'Include imported data',
|
||||
'accounts' => 'Export transactions from these accounts',
|
||||
'delete_account' => 'Delete account ":name"',
|
||||
'delete_bill' => 'Delete bill ":name"',
|
||||
'delete_budget' => 'Delete budget ":name"',
|
||||
'delete_category' => 'Delete category ":name"',
|
||||
'delete_currency' => 'Delete currency ":name"',
|
||||
'delete_journal' => 'Delete transaction with description ":description"',
|
||||
'delete_attachment' => 'Delete attachment ":name"',
|
||||
'delete_rule' => 'Delete rule ":title"',
|
||||
'delete_rule_group' => 'Delete rule group ":title"',
|
||||
'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?',
|
||||
'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?',
|
||||
'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?',
|
||||
'rule_areYouSure' => 'Are you sure you want to delete the rule titled ":title"?',
|
||||
'ruleGroup_areYouSure' => 'Are you sure you want to delete the rule group titled ":title"?',
|
||||
'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?',
|
||||
'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?',
|
||||
'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?',
|
||||
'piggyBank_areYouSure' => 'Are you sure you want to delete the piggy bank named ":name"?',
|
||||
'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?',
|
||||
'mass_journal_are_you_sure' => 'Are you sure you want to delete these transactions?',
|
||||
'tag_areYouSure' => 'Are you sure you want to delete the tag ":tag"?',
|
||||
'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.',
|
||||
'mass_make_selection' => 'You can still prevent items from being deleted by removing the checkbox.',
|
||||
'delete_all_permanently' => 'Delete selected permanently',
|
||||
'update_all_journals' => 'Update these transactions',
|
||||
'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.',
|
||||
'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.',
|
||||
'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.',
|
||||
'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.',
|
||||
'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.',
|
||||
'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.',
|
||||
'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.',
|
||||
|
||||
'email' => 'Email address',
|
||||
'password' => 'Password',
|
||||
'password_confirmation' => 'Password (again)',
|
||||
'blocked' => 'Is blocked?',
|
||||
'blocked_code' => 'Reason for block',
|
||||
|
||||
|
||||
// admin
|
||||
'domain' => 'Domain',
|
||||
'single_user_mode' => 'Single user mode',
|
||||
'must_confirm_account' => 'New users must activate account',
|
||||
'is_demo_site' => 'Is demo site',
|
||||
|
||||
|
||||
// import
|
||||
'import_file' => 'Import file',
|
||||
'configuration_file' => 'Configuration file',
|
||||
'import_file_type' => 'Import file type',
|
||||
'csv_comma' => 'A comma (,)',
|
||||
'csv_semicolon' => 'A semicolon (;)',
|
||||
'csv_tab' => 'A tab (invisible)',
|
||||
'csv_delimiter' => 'CSV field delimiter',
|
||||
'csv_import_account' => 'Default import account',
|
||||
'csv_config' => 'CSV import configuration',
|
||||
|
||||
|
||||
'due_date' => 'Due date',
|
||||
'payment_date' => 'Payment date',
|
||||
'invoice_date' => 'Invoice date',
|
||||
'internal_reference' => 'Internal reference',
|
||||
];
|
33
resources/lang/es_ES/help.php
Normal file
33
resources/lang/es_ES/help.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* help.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.
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
// tour!
|
||||
'main-content-title' => 'Welcome to Firefly III',
|
||||
'main-content-text' => 'Do yourself a favor and follow this short guide to make sure you know your way around.',
|
||||
'sidebar-toggle-title' => 'Sidebar to create stuff',
|
||||
'sidebar-toggle-text' => 'Hidden under the plus icon are all the buttons to create new stuff. Accounts, transactions, everything!',
|
||||
'account-menu-title' => 'All your accounts',
|
||||
'account-menu-text' => 'Here you can find all the accounts you\'ve made.',
|
||||
'budget-menu-title' => 'Budgets',
|
||||
'budget-menu-text' => 'Use this page to organise your finances and limit spending.',
|
||||
'report-menu-title' => 'Reports',
|
||||
'report-menu-text' => 'Check this out when you want a solid overview of your finances.',
|
||||
'transaction-menu-title' => 'Transactions',
|
||||
'transaction-menu-text' => 'All transactions you\'ve created can be found here.',
|
||||
'option-menu-title' => 'Options',
|
||||
'option-menu-text' => 'This is pretty self-explanatory.',
|
||||
'main-content-end-title' => 'The end!',
|
||||
'main-content-end-text' => 'Remember that every page has a small question mark at the right top. Click it to get help about the page you\'re on.',
|
||||
'index' => 'index',
|
||||
'home' => 'home',
|
||||
];
|
89
resources/lang/es_ES/list.php
Normal file
89
resources/lang/es_ES/list.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* list.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.
|
||||
*/
|
||||
|
||||
return [
|
||||
'buttons' => 'Botones',
|
||||
'icon' => 'Icono',
|
||||
'id' => 'ID',
|
||||
'create_date' => 'Fecha de creación',
|
||||
'update_date' => 'Fecha de modificación',
|
||||
'balance_before' => 'Balance antes de ',
|
||||
'balance_after' => 'Balance después de la',
|
||||
'name' => 'Nombre',
|
||||
'role' => 'Rol',
|
||||
'currentBalance' => 'Balance actual',
|
||||
'active' => '¿Está Activo?',
|
||||
'lastActivity' => 'Actividad más reciente',
|
||||
'balanceDiff' => 'Diferencia de saldo entre :start y :end',
|
||||
'matchedOn' => 'Encontrado en',
|
||||
'matchesOn' => 'Encontrado en',
|
||||
'account_type' => 'Tipo de cuenta',
|
||||
'created_at' => 'Fecha de creación',
|
||||
'new_balance' => 'Nuevo balance',
|
||||
'account' => 'Cuenta',
|
||||
'matchingAmount' => 'Monto',
|
||||
'lastMatch' => 'Última coincidencia',
|
||||
'split_number' => 'División #',
|
||||
'destination' => 'Destino',
|
||||
'source' => 'Origen',
|
||||
'next_expected_match' => 'Próxima coincidencia esperada',
|
||||
'automatch' => '¿Buscar coincidencia automaticamente?',
|
||||
'repeat_freq' => 'Repetición:',
|
||||
'description' => 'Descripción',
|
||||
'amount' => 'Monto',
|
||||
'internal_reference' => 'Referencia interna',
|
||||
'date' => 'Fecha',
|
||||
'interest_date' => 'Tasa de interés',
|
||||
'book_date' => 'Libro fecha',
|
||||
'process_date' => 'Fecha de procesamiento',
|
||||
'due_date' => 'Fecha de vencimiento',
|
||||
'payment_date' => 'Fecha de pago',
|
||||
'invoice_date' => 'Fecha de facturación',
|
||||
'interal_reference' => 'Referencia interna',
|
||||
'notes' => 'Notas',
|
||||
'from' => 'Desde',
|
||||
'piggy_bank' => 'Alcancilla',
|
||||
'to' => 'Hasta',
|
||||
'budget' => 'Presupuesto',
|
||||
'category' => 'Categoría',
|
||||
'bill' => 'Factura',
|
||||
'withdrawal' => 'Retiro',
|
||||
'deposit' => 'Depósito',
|
||||
'transfer' => 'Trasferencia',
|
||||
'type' => 'Tipo',
|
||||
'completed' => 'Completado',
|
||||
'iban' => 'IBAN',
|
||||
'paid_current_period' => 'Pagado este período',
|
||||
'email' => 'Email',
|
||||
'registered_at' => 'Registrado el',
|
||||
'is_activated' => 'Está activado',
|
||||
'is_blocked' => 'Está bloqueado',
|
||||
'is_admin' => '¿Es el administrador?',
|
||||
'has_two_factor' => 'Tiene 2FA',
|
||||
'confirmed_from' => 'Confirmado desde',
|
||||
'registered_from' => 'Registrado desde',
|
||||
'blocked_code' => 'Bloque de código',
|
||||
'domain' => 'Dominio',
|
||||
'registration_attempts' => 'Intentos de registro',
|
||||
'source_account' => 'Cuenta origen',
|
||||
'destination_account' => 'Cuenta destino',
|
||||
|
||||
'accounts_count' => 'Número de cuentas',
|
||||
'journals_count' => 'Número de transacciones',
|
||||
'attachments_count' => 'Núm. de datos adjuntos',
|
||||
'bills_count' => 'Número de facturas',
|
||||
'categories_count' => 'Número de categorías',
|
||||
'export_jobs_count' => 'Número de operaciones de exportación',
|
||||
'import_jobs_count' => 'Número de operaciones de importación',
|
||||
'budget_count' => 'Número de presupuestos',
|
||||
'rule_and_groups_count' => 'Número de reglas y grupos de reglas',
|
||||
'tags_count' => 'Número de etiquetas',
|
||||
];
|
17
resources/lang/es_ES/pagination.php
Normal file
17
resources/lang/es_ES/pagination.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* pagination.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.
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
'previous' => '« Anterior',
|
||||
'next' => 'Siguiente »',
|
||||
|
||||
];
|
19
resources/lang/es_ES/passwords.php
Normal file
19
resources/lang/es_ES/passwords.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* passwords.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.
|
||||
*/
|
||||
|
||||
return [
|
||||
'password' => 'Las contraseñas deben tener al menos seis caracteres y coincidir entre sí.',
|
||||
'user' => 'No podemos encontrar un usuario con esa dirección de correo electrónico.',
|
||||
'token' => 'Este token para reestablecer la contraseña no es válido.',
|
||||
'sent' => 'Te enviamos un correo con el link para reestablecer tu contraseña!',
|
||||
'reset' => 'Tu contraseña fue reestablecida!',
|
||||
'blocked' => 'Buen intento.',
|
||||
];
|
91
resources/lang/es_ES/validation.php
Normal file
91
resources/lang/es_ES/validation.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* validation.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.
|
||||
*/
|
||||
|
||||
return [
|
||||
'iban' => 'This is not a valid CBU.',
|
||||
'unique_account_number_for_user' => 'Parece que este número de cuenta ya está en uso.',
|
||||
'deleted_user' => 'Debido a restricciones de seguridad, no se puede registrar utilizando esta dirección de correo electrónico.',
|
||||
'rule_trigger_value' => 'Este valor es válido para el disparador seleccionado.',
|
||||
'rule_action_value' => 'Este valor es inválido para la acción seleccionado.',
|
||||
'invalid_domain' => 'Debido a restricciones de seguridad, no se puede registrar utilizando este dominio.',
|
||||
'file_already_attached' => 'Archivo ":name" ya ha sido añadido para este objeto.',
|
||||
'file_attached' => 'Archivo subido correctamente ": nombre".',
|
||||
'file_invalid_mime' => 'Archivo ":name" es de tipo": mime" que no es aceptado como una nueva carga.',
|
||||
'file_too_large' => 'Archivo ":name" es demasiado grande.',
|
||||
'belongs_to_user' => 'El valor de :attribute es desconocido',
|
||||
'accepted' => 'El :attribute debe ser aceptado.',
|
||||
'bic' => 'Esto no es un BIC válido.',
|
||||
'more' => ':attribute debe ser mayor que cero.',
|
||||
'active_url' => 'El campo :attribute no es una URL válida.',
|
||||
'after' => 'El campo :attribute debe ser una fecha posterior a :date.',
|
||||
'alpha' => 'El campo :attribute sólo puede contener letras.',
|
||||
'alpha_dash' => 'El campo :attribute sólo puede contener letras, números y guiones.',
|
||||
'alpha_num' => 'El campo :attribute sólo puede contener letras y números.',
|
||||
'array' => 'El campo :attribute debe ser un arreglo.',
|
||||
'unique_for_user' => 'Ya hay una entrada con esto :attribute.',
|
||||
'before' => 'El campo :attribute debe contener una fecha anterior a :date.',
|
||||
'unique_object_for_user' => 'Este nombre ya está en uso',
|
||||
'unique_account_for_user' => 'Este nombre cuenta ya está en uso',
|
||||
'between.numeric' => 'El atributo :attribute debe estar entre :min y :max.',
|
||||
'between.file' => 'El atributo :attribute debe estar entre :min y :max kilobytes.',
|
||||
'between.string' => 'El atributo :attribute debe estar entre :min y :max caracteres.',
|
||||
'between.array' => 'El atributo :attribute debe estar entre :min y :max items.',
|
||||
'boolean' => 'El campo :attribute debe ser verdadero o falso.',
|
||||
'confirmed' => 'La confirmación de :attribute no coincide.',
|
||||
'date' => 'El campo :attribute no es una fecha válida.',
|
||||
'date_format' => 'El campo :attribute no corresponde con el formato :format.',
|
||||
'different' => 'Los campos :attribute y :other han de ser diferentes.',
|
||||
'digits' => 'El campo :attribute debe contener un número de :digits dígitos.',
|
||||
'digits_between' => 'El campo :attribute debe contener entre :min y :max dígitos.',
|
||||
'email' => 'El campo :attribute no corresponde con una dirección de e-mail válida.',
|
||||
'filled' => 'El campo :attribute es requerido.',
|
||||
'exists' => 'El campo :attribute seleccionado no es correcto.',
|
||||
'image' => 'El campo :attribute debe ser una imagen.',
|
||||
'in' => 'El campo :attribute seleccionado es inválido.',
|
||||
'integer' => 'El campo :attribute debe ser un entero.',
|
||||
'ip' => 'El campo :attribute debe contener una dirección IP válida.',
|
||||
'json' => 'El campo :attribute debe ser una cadena JSON válida.',
|
||||
'max.numeric' => 'El campo :attribute no puede ser mayor que :max.',
|
||||
'max.file' => 'El campo :attribute no puede ser mayor :max de kilobytes.',
|
||||
'max.string' => 'The :attribute may not be greater than :max characters.',
|
||||
'max.array' => 'The :attribute may not have more than :max items.',
|
||||
'mimes' => 'The :attribute must be a file of type: :values.',
|
||||
'min.numeric' => 'The :attribute must be at least :min.',
|
||||
'min.file' => 'The :attribute must be at least :min kilobytes.',
|
||||
'min.string' => 'The :attribute must be at least :min characters.',
|
||||
'min.array' => 'The :attribute must have at least :min items.',
|
||||
'not_in' => 'The selected :attribute is invalid.',
|
||||
'numeric' => 'The :attribute must be a number.',
|
||||
'regex' => 'The :attribute format is invalid.',
|
||||
'required' => 'The :attribute field is required.',
|
||||
'required_if' => 'The :attribute field is required when :other is :value.',
|
||||
'required_unless' => 'The :attribute field is required unless :other is in :values.',
|
||||
'required_with' => 'The :attribute field is required when :values is present.',
|
||||
'required_with_all' => 'The :attribute field is required when :values is present.',
|
||||
'required_without' => 'The :attribute field is required when :values is not present.',
|
||||
'required_without_all' => 'The :attribute field is required when none of :values are present.',
|
||||
'same' => 'The :attribute and :other must match.',
|
||||
'size.numeric' => 'The :attribute must be :size.',
|
||||
'size.file' => 'The :attribute must be :size kilobytes.',
|
||||
'size.string' => 'The :attribute must be :size characters.',
|
||||
'size.array' => 'The :attribute must contain :size items.',
|
||||
'unique' => 'The :attribute has already been taken.',
|
||||
'string' => 'The :attribute must be a string.',
|
||||
'url' => 'The :attribute format is invalid.',
|
||||
'timezone' => 'The :attribute must be a valid zone.',
|
||||
'2fa_code' => 'The :attribute field is invalid.',
|
||||
'dimensions' => 'The :attribute has invalid image dimensions.',
|
||||
'distinct' => 'The :attribute field has a duplicate value.',
|
||||
'file' => 'The :attribute must be a file.',
|
||||
'in_array' => 'The :attribute field does not exist in :other.',
|
||||
'present' => 'The :attribute field must be present.',
|
||||
'amount_zero' => 'The total amount cannot be zero',
|
||||
];
|
@@ -20,6 +20,7 @@ return [
|
||||
'everything' => 'Tout',
|
||||
'customRange' => 'Etendue personnalisée',
|
||||
'apply' => 'Appliquer',
|
||||
'select_date' => 'Select date..',
|
||||
'cancel' => 'Annuler',
|
||||
'from' => 'Depuis',
|
||||
'to' => 'A',
|
||||
@@ -112,8 +113,8 @@ return [
|
||||
'budget_in_period' => 'All transactions for budget ":name" between :start and :end',
|
||||
'chart_budget_in_period' => 'Chart for all transactions for budget ":name" between :start and :end',
|
||||
'chart_account_in_period' => 'Chart for all transactions for account ":name" between :start and :end',
|
||||
'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
|
||||
'chart_category_all' => 'Chart for all transactions for category ":name"',
|
||||
'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
|
||||
'chart_category_all' => 'Chart for all transactions for category ":name"',
|
||||
'budget_in_period_breadcrumb' => 'Between :start and :end',
|
||||
'clone_withdrawal' => 'Clone this withdrawal',
|
||||
'clone_deposit' => 'Clone this deposit',
|
||||
@@ -962,6 +963,7 @@ return [
|
||||
'split_this_transfer' => 'Split this transfer',
|
||||
'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.',
|
||||
'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.',
|
||||
'cannot_edit_opening_balance' => 'You cannot edit the opening balance of an account.',
|
||||
'no_edit_multiple_left' => 'You have selected no valid transactions to edit.',
|
||||
|
||||
// import
|
||||
|
@@ -20,6 +20,7 @@ return [
|
||||
'everything' => 'Alles',
|
||||
'customRange' => 'Zelf bereik kiezen',
|
||||
'apply' => 'Go',
|
||||
'select_date' => 'Selecteer periode..',
|
||||
'cancel' => 'Annuleren',
|
||||
'from' => 'Van',
|
||||
'to' => 'Tot',
|
||||
@@ -112,8 +113,8 @@ return [
|
||||
'budget_in_period' => 'Alle transacties voor budget ":name" tussen :start en :end',
|
||||
'chart_budget_in_period' => 'Grafiek voor alle transacties voor budget ":name" tussen :start en :end',
|
||||
'chart_account_in_period' => 'Grafiek voor alle transacties voor rekening ":name" tussen :start en :end',
|
||||
'chart_category_in_period' => 'Grafiek voor alle transacties voor categorie ":name" tussen :start en :end',
|
||||
'chart_category_all' => 'Grafiek voor alle transacties voor categorie ":name"',
|
||||
'chart_category_in_period' => 'Grafiek voor alle transacties voor categorie ":name" tussen :start en :end',
|
||||
'chart_category_all' => 'Grafiek voor alle transacties voor categorie ":name"',
|
||||
'budget_in_period_breadcrumb' => 'Tussen :start en :end',
|
||||
'clone_withdrawal' => 'Kopieer deze uitgave',
|
||||
'clone_deposit' => 'Kopieer deze inkomsten',
|
||||
@@ -962,6 +963,7 @@ return [
|
||||
'split_this_transfer' => 'Splits deze overschrijving',
|
||||
'cannot_edit_multiple_source' => 'Je kan transactie #:id met omschrijving ":description" niet splitsen, want deze bevat meerdere bronrekeningen.',
|
||||
'cannot_edit_multiple_dest' => 'Je kan transactie #:id met omschrijving ":description" niet wijzigen, want deze bevat meerdere doelrekeningen.',
|
||||
'cannot_edit_opening_balance' => 'You cannot edit the opening balance of an account.',
|
||||
'no_edit_multiple_left' => 'Je hebt geen geldige transacties geselecteerd.',
|
||||
|
||||
// import
|
||||
|
@@ -20,6 +20,7 @@ return [
|
||||
'everything' => 'Wszystko',
|
||||
'customRange' => 'Niestandardowy zakres',
|
||||
'apply' => 'Zastosuj',
|
||||
'select_date' => 'Select date..',
|
||||
'cancel' => 'Anuluj',
|
||||
'from' => 'Z',
|
||||
'to' => 'Do',
|
||||
@@ -112,8 +113,8 @@ return [
|
||||
'budget_in_period' => 'All transactions for budget ":name" between :start and :end',
|
||||
'chart_budget_in_period' => 'Chart for all transactions for budget ":name" between :start and :end',
|
||||
'chart_account_in_period' => 'Chart for all transactions for account ":name" between :start and :end',
|
||||
'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
|
||||
'chart_category_all' => 'Chart for all transactions for category ":name"',
|
||||
'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
|
||||
'chart_category_all' => 'Chart for all transactions for category ":name"',
|
||||
'budget_in_period_breadcrumb' => 'Między :start i :end',
|
||||
'clone_withdrawal' => 'Zduplikuj tę wypłatę',
|
||||
'clone_deposit' => 'Zduplikuj tą wpłatę',
|
||||
@@ -962,6 +963,7 @@ return [
|
||||
'split_this_transfer' => 'Podziel ten transfer',
|
||||
'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.',
|
||||
'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.',
|
||||
'cannot_edit_opening_balance' => 'You cannot edit the opening balance of an account.',
|
||||
'no_edit_multiple_left' => 'Nie wybrałeś żadnych poprawnych transakcji do edycji.',
|
||||
|
||||
// import
|
||||
|
@@ -20,14 +20,15 @@ return [
|
||||
'everything' => 'Tudo',
|
||||
'customRange' => 'Intervalo Personalizado',
|
||||
'apply' => 'Aplicar',
|
||||
'select_date' => 'Select date..',
|
||||
'cancel' => 'Cancelar',
|
||||
'from' => 'De',
|
||||
'to' => 'Até',
|
||||
'showEverything' => 'Mostrar tudo',
|
||||
'never' => 'Nunca',
|
||||
'search_results_for' => 'Pesquisar resultados por ":query"',
|
||||
'advanced_search' => 'Advanced search',
|
||||
'advanced_search_intro' => 'There are several modifiers that you can use in your search to narrow down the results. If you use any of these, the search will <em>only</em> return transactions. Please click the <i class="fa fa-question-circle"></i>-icon for more information.',
|
||||
'advanced_search' => 'Busca avançada',
|
||||
'advanced_search_intro' => 'Existem vários modificadores que você pode usar em sua busca para limitar os resultados. Se você usar qualquer um destes, a busca vai <em>apenas</em> transações de retorno. Por favor, clique no <i class="fa fa-question-circle"></i>-ícone para obter mais informações.',
|
||||
'bounced_error' => 'A mensagem enviado para :email ressaltado, não tem acesso para você.',
|
||||
'deleted_error' => 'Estas credenciais não correspondem aos nossos registros.',
|
||||
'general_blocked_error' => 'Sua conta foi desativada, você não pode entrar.',
|
||||
@@ -69,12 +70,12 @@ return [
|
||||
'warning_much_data' => ':days dias de dados podem demorar um pouco para carregar.',
|
||||
'registered' => 'Você se registrou com sucesso!',
|
||||
'search' => 'Pesquisa',
|
||||
'search_found_accounts' => 'Found :count account(s) for your query.',
|
||||
'search_found_categories' => 'Found :count category(ies) for your query.',
|
||||
'search_found_budgets' => 'Found :count budget(s) for your query.',
|
||||
'search_found_tags' => 'Found :count tag(s) for your query.',
|
||||
'search_found_transactions' => 'Found :count transaction(s) for your query.',
|
||||
'results_limited' => 'The results are limited to :count entries.',
|
||||
'search_found_accounts' => 'Encontrado: contar conta (s) para sua consulta.',
|
||||
'search_found_categories' => 'Encontrado: contar categoria(s) para sua consulta.',
|
||||
'search_found_budgets' => 'Encontrado: contagem despesa(s) para a sua consulta.',
|
||||
'search_found_tags' => 'Encontrado: contar Tag (s) para sua consulta.',
|
||||
'search_found_transactions' => 'Encontrado: contagem de transação para a sua consulta.',
|
||||
'results_limited' => 'Os resultados são limitados a: contar as entradas.',
|
||||
'tagbalancingAct' => 'Saldo',
|
||||
'tagadvancePayment' => 'Pagamento antecipado',
|
||||
'tagnothing' => '',
|
||||
@@ -112,8 +113,8 @@ return [
|
||||
'budget_in_period' => 'All transactions for budget ":name" between :start and :end',
|
||||
'chart_budget_in_period' => 'Chart for all transactions for budget ":name" between :start and :end',
|
||||
'chart_account_in_period' => 'Chart for all transactions for account ":name" between :start and :end',
|
||||
'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
|
||||
'chart_category_all' => 'Chart for all transactions for category ":name"',
|
||||
'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
|
||||
'chart_category_all' => 'Chart for all transactions for category ":name"',
|
||||
'budget_in_period_breadcrumb' => 'Between :start and :end',
|
||||
'clone_withdrawal' => 'Clone this withdrawal',
|
||||
'clone_deposit' => 'Clone this deposit',
|
||||
@@ -962,6 +963,7 @@ return [
|
||||
'split_this_transfer' => 'Dividir essa transferência',
|
||||
'cannot_edit_multiple_source' => 'Você não pode editar transações parceladas #:id com a descrição ":description" porque ele contém várias contas de origem.',
|
||||
'cannot_edit_multiple_dest' => 'Você não pode editar transações parceladas #:id com a descrição ":description" porque ele contém várias contas de destino.',
|
||||
'cannot_edit_opening_balance' => 'You cannot edit the opening balance of an account.',
|
||||
'no_edit_multiple_left' => 'Você não selecionou nenhuma transação válida para editar.',
|
||||
|
||||
// import
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user