mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Merge branch 'release/4.6.5'
This commit is contained in:
commit
81bef28607
37
CHANGELOG.md
37
CHANGELOG.md
@ -2,6 +2,43 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [4.6.5] - 2017-09-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- #616, The ability to link transactions
|
||||||
|
- #763, as suggested by @tannie
|
||||||
|
- #770, as suggested by @skibbipl
|
||||||
|
- #780, as suggested by @skibbipl
|
||||||
|
- #784, as suggested by @SmilingWorlock
|
||||||
|
- Lots of code for future support of automated Bunq imports
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Rewrote the export routine
|
||||||
|
- #782, as suggested by @NiceGuyIT
|
||||||
|
- #800, as suggested by @jleeong
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- #724, reported by @skibbipl
|
||||||
|
- #738, reported by @skibbipl
|
||||||
|
- #760, reported by @leander091
|
||||||
|
- #764, reported by @tannie
|
||||||
|
- #792, reported by @jleeong
|
||||||
|
- #793, reported by @nicoschreiner
|
||||||
|
- #797, reported by @leander091
|
||||||
|
- #801, reported by @pkoziol
|
||||||
|
- #803, reported by @pkoziol
|
||||||
|
- #805, reported by @pkoziol
|
||||||
|
- #806, reported by @pkoziol
|
||||||
|
- #807, reported by @pkoziol
|
||||||
|
- #808, reported by @pkoziol
|
||||||
|
- #809, reported by @pkoziol
|
||||||
|
- #814, reported by @nicoschreiner
|
||||||
|
- #818, reported by @gavu
|
||||||
|
- #819, reported by @DieBauer
|
||||||
|
- #820, reported by @simonsmiley
|
||||||
|
- Various other fixes
|
||||||
|
|
||||||
|
|
||||||
## [4.6.4] - 2017-08-13
|
## [4.6.4] - 2017-08-13
|
||||||
### Added
|
### Added
|
||||||
- PHP7.1 support
|
- PHP7.1 support
|
||||||
|
22
Dockerfile
22
Dockerfile
@ -23,28 +23,18 @@ RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip
|
|||||||
# Generate locales supported by firefly
|
# 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
|
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
|
||||||
|
|
||||||
|
COPY docker/apache2.conf /etc/apache2/apache2.conf
|
||||||
# Enable apache mod rewrite..
|
# Enable apache mod rewrite..
|
||||||
RUN a2enmod rewrite
|
RUN a2enmod rewrite
|
||||||
|
|
||||||
# Setup the Composer installer
|
# Setup the Composer installer
|
||||||
RUN curl -o /tmp/composer-setup.php https://getcomposer.org/installer && \
|
run curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||||
curl -o /tmp/composer-setup.sig https://composer.github.io/installer.sig && \
|
|
||||||
php -r "if (hash('SHA384', file_get_contents('/tmp/composer-setup.php')) !== trim(file_get_contents('/tmp/composer-setup.sig'))) { unlink('/tmp/composer-setup.php'); echo 'Invalid installer' . PHP_EOL; exit(1); }" && \
|
|
||||||
chmod +x /tmp/composer-setup.php && \
|
|
||||||
php /tmp/composer-setup.php && \
|
|
||||||
mv composer.phar /usr/local/bin/composer && \
|
|
||||||
rm -f /tmp/composer-setup.{php,sig}
|
|
||||||
|
|
||||||
ADD . /var/www/firefly-iii
|
RUN cd /var/www && composer create-project grumpydictator/firefly-iii --no-dev --prefer-dist firefly-iii 4.6.4
|
||||||
RUN chown -R www-data:www-data /var/www/
|
COPY docker/entrypoint.sh /var/www/firefly-iii/docker/entrypoint.sh
|
||||||
ADD docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
|
ADD docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
|
||||||
|
RUN chown -R www-data:www-data /var/www && chmod -R 775 /var/www/firefly-iii/storage
|
||||||
USER www-data
|
|
||||||
|
|
||||||
WORKDIR /var/www/firefly-iii
|
WORKDIR /var/www/firefly-iii
|
||||||
|
EXPOSE 80
|
||||||
RUN composer install --no-scripts --no-dev
|
|
||||||
|
|
||||||
USER root
|
|
||||||
|
|
||||||
ENTRYPOINT ["/var/www/firefly-iii/docker/entrypoint.sh"]
|
ENTRYPOINT ["/var/www/firefly-iii/docker/entrypoint.sh"]
|
||||||
|
@ -54,6 +54,8 @@ class CreateImport extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Run the command.
|
||||||
|
*
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped
|
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly.
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly.
|
||||||
*/
|
*/
|
||||||
@ -133,6 +135,8 @@ class CreateImport extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Verify user inserts correct arguments.
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly.
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly.
|
||||||
*/
|
*/
|
||||||
|
@ -51,7 +51,7 @@ class Import extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Run the import routine.
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
@ -91,6 +91,8 @@ class Import extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Check if job is valid to be imported.
|
||||||
|
*
|
||||||
* @param ImportJob $job
|
* @param ImportJob $job
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
@ -20,22 +20,21 @@ use FireflyIII\Models\AccountMeta;
|
|||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\LimitRepetition;
|
use FireflyIII\Models\LimitRepetition;
|
||||||
use FireflyIII\Models\PiggyBankEvent;
|
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Log;
|
use Log;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Schema;
|
use Schema;
|
||||||
use Steam;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class UpgradeDatabase
|
* Class UpgradeDatabase
|
||||||
|
*
|
||||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it just touches a lot of things.
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it just touches a lot of things.
|
||||||
*
|
*
|
||||||
* @package FireflyIII\Console\Commands
|
* @package FireflyIII\Console\Commands
|
||||||
@ -71,76 +70,26 @@ class UpgradeDatabase extends Command
|
|||||||
{
|
{
|
||||||
$this->setTransactionIdentifier();
|
$this->setTransactionIdentifier();
|
||||||
$this->migrateRepetitions();
|
$this->migrateRepetitions();
|
||||||
$this->repairPiggyBanks();
|
|
||||||
$this->updateAccountCurrencies();
|
$this->updateAccountCurrencies();
|
||||||
$this->updateJournalCurrencies();
|
$this->line('Updating currency information..');
|
||||||
$this->currencyInfoToTransactions();
|
$this->updateTransferCurrencies();
|
||||||
$this->verifyCurrencyInfo();
|
$this->updateOtherCurrencies();
|
||||||
$this->info('Firefly III database is up to date.');
|
$this->info('Firefly III database is up to date.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the currency id info to the transaction instead of the journal.
|
* Migrate budget repetitions to new format where the end date is in the budget limit as well,
|
||||||
*
|
* making the limit_repetition table obsolete.
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // cannot be helped.
|
|
||||||
*/
|
*/
|
||||||
private function currencyInfoToTransactions()
|
public function migrateRepetitions(): void
|
||||||
{
|
{
|
||||||
$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.
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 5.
|
|
||||||
*/
|
|
||||||
private function migrateRepetitions()
|
|
||||||
{
|
|
||||||
if (!Schema::hasTable('budget_limits')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// get all budget limits with end_date NULL
|
|
||||||
$set = BudgetLimit::whereNull('end_date')->get();
|
$set = BudgetLimit::whereNull('end_date')->get();
|
||||||
if ($set->count() > 0) {
|
|
||||||
$this->line(sprintf('Found %d budget limit(s) to update', $set->count()));
|
|
||||||
}
|
|
||||||
/** @var BudgetLimit $budgetLimit */
|
/** @var BudgetLimit $budgetLimit */
|
||||||
foreach ($set as $budgetLimit) {
|
foreach ($set as $budgetLimit) {
|
||||||
// get limit repetition (should be just one):
|
|
||||||
/** @var LimitRepetition $repetition */
|
/** @var LimitRepetition $repetition */
|
||||||
$repetition = $budgetLimit->limitrepetitions()->first();
|
$repetition = $budgetLimit->limitrepetitions()->first();
|
||||||
if (!is_null($repetition)) {
|
if (!is_null($repetition)) {
|
||||||
@ -150,59 +99,30 @@ class UpgradeDatabase extends Command
|
|||||||
$repetition->delete();
|
$repetition->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make sure there are only transfers linked to piggy bank events.
|
* This method gives all transactions which are part of a split journal (so more than 2) a sort of "order" so they are easier
|
||||||
|
* to easier to match to their counterpart. When a journal is split, it has two or three transactions: -3, -4 and -5 for example.
|
||||||
*
|
*
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // cannot be helped.
|
* In the database this is reflected as 6 transactions: -3/+3, -4/+4, -5/+5.
|
||||||
|
*
|
||||||
|
* When either of these are the same amount, FF3 can't keep them apart: +3/-3, +3/-3, +3/-3. This happens more often than you would
|
||||||
|
* think. So each set gets a number (1,2,3) to keep them apart.
|
||||||
*/
|
*/
|
||||||
private function repairPiggyBanks()
|
public function setTransactionIdentifier(): void
|
||||||
{
|
|
||||||
// if table does not exist, return false
|
|
||||||
if (!Schema::hasTable('piggy_bank_events')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
|
|
||||||
/** @var PiggyBankEvent $event */
|
|
||||||
foreach ($set as $event) {
|
|
||||||
|
|
||||||
if (is_null($event->transaction_journal_id)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/** @var TransactionJournal $journal */
|
|
||||||
$journal = $event->transactionJournal()->first();
|
|
||||||
if (is_null($journal)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$type = $journal->transactionType->type;
|
|
||||||
if ($type !== TransactionType::TRANSFER) {
|
|
||||||
$event->transaction_journal_id = null;
|
|
||||||
$event->save();
|
|
||||||
$this->line(sprintf('Piggy bank #%d was referenced by an invalid event. This has been fixed.', $event->piggy_bank_id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is strangely complex, because the HAVING modifier is a no-no. And subqueries in Laravel are weird.
|
|
||||||
*/
|
|
||||||
private function setTransactionIdentifier()
|
|
||||||
{
|
{
|
||||||
// if table does not exist, return false
|
// if table does not exist, return false
|
||||||
if (!Schema::hasTable('transaction_journals')) {
|
if (!Schema::hasTable('transaction_journals')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$subQuery = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||||
|
->whereNull('transaction_journals.deleted_at')
|
||||||
$subQuery = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
->whereNull('transactions.deleted_at')
|
||||||
->whereNull('transaction_journals.deleted_at')
|
->groupBy(['transaction_journals.id'])
|
||||||
->whereNull('transactions.deleted_at')
|
->select(['transaction_journals.id', DB::raw('COUNT(transactions.id) AS t_count')]);
|
||||||
->groupBy(['transaction_journals.id'])
|
|
||||||
->select(['transaction_journals.id', DB::raw('COUNT(transactions.id) AS t_count')]);
|
|
||||||
|
|
||||||
$result = DB::table(DB::raw('(' . $subQuery->toSql() . ') AS derived'))
|
$result = DB::table(DB::raw('(' . $subQuery->toSql() . ') AS derived'))
|
||||||
->mergeBindings($subQuery->getQuery())
|
->mergeBindings($subQuery->getQuery())
|
||||||
->where('t_count', '>', 2)
|
->where('t_count', '>', 2)
|
||||||
@ -210,55 +130,172 @@ class UpgradeDatabase extends Command
|
|||||||
$journalIds = array_unique($result->pluck('id')->toArray());
|
$journalIds = array_unique($result->pluck('id')->toArray());
|
||||||
|
|
||||||
foreach ($journalIds as $journalId) {
|
foreach ($journalIds as $journalId) {
|
||||||
$this->updateJournal(intval($journalId));
|
$this->updateJournalidentifiers(intval($journalId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make sure all accounts have proper currency info.
|
* Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's forced upon the account.
|
||||||
*/
|
*/
|
||||||
private function updateAccountCurrencies()
|
public function updateAccountCurrencies(): void
|
||||||
{
|
{
|
||||||
$accounts = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
$accounts = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||||
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->get(['accounts.*']);
|
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->get(['accounts.*']);
|
||||||
|
|
||||||
/** @var Account $account */
|
$accounts->each(
|
||||||
foreach ($accounts as $account) {
|
function (Account $account) {
|
||||||
// get users preference, fall back to system pref.
|
// get users preference, fall back to system pref.
|
||||||
$defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
|
$defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
|
||||||
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
|
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
|
||||||
$accountCurrency = intval($account->getMeta('currency_id'));
|
$accountCurrency = intval($account->getMeta('currency_id'));
|
||||||
$openingBalance = $account->getOpeningBalance();
|
$openingBalance = $account->getOpeningBalance();
|
||||||
$obCurrency = intval($openingBalance->transaction_currency_id);
|
$obCurrency = intval($openingBalance->transaction_currency_id);
|
||||||
|
|
||||||
// both 0? set to default currency:
|
// both 0? set to default currency:
|
||||||
if ($accountCurrency === 0 && $obCurrency === 0) {
|
if ($accountCurrency === 0 && $obCurrency === 0) {
|
||||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $defaultCurrency->id]);
|
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $defaultCurrency->id]);
|
||||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
||||||
continue;
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// account is set to 0, opening balance is not?
|
||||||
|
if ($accountCurrency === 0 && $obCurrency > 0) {
|
||||||
|
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]);
|
||||||
|
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not match and opening balance id is not null.
|
||||||
|
if ($accountCurrency !== $obCurrency && $openingBalance->id > 0) {
|
||||||
|
// update opening balance:
|
||||||
|
$openingBalance->transaction_currency_id = $accountCurrency;
|
||||||
|
$openingBalance->save();
|
||||||
|
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// account is set to 0, opening balance is not?
|
return;
|
||||||
if ($accountCurrency === 0 && $obCurrency > 0) {
|
}
|
||||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]);
|
|
||||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
/**
|
||||||
continue;
|
* This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for
|
||||||
|
* the accounts they are linked to.
|
||||||
|
*
|
||||||
|
* Both source and destination must match the respective currency preference of the related asset account.
|
||||||
|
* So FF3 must verify all transactions.
|
||||||
|
*/
|
||||||
|
public function updateOtherCurrencies(): void
|
||||||
|
{
|
||||||
|
/** @var CurrencyRepositoryInterface $repository */
|
||||||
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
|
$set = TransactionJournal
|
||||||
|
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||||
|
->whereIn('transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE])
|
||||||
|
->get(['transaction_journals.*']);
|
||||||
|
|
||||||
|
$set->each(
|
||||||
|
function (TransactionJournal $journal) use ($repository) {
|
||||||
|
// get the transaction with the asset account in it:
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
$transaction = $journal->transactions()
|
||||||
|
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
|
||||||
|
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||||
|
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->first(['transactions.*']);
|
||||||
|
/** @var Account $account */
|
||||||
|
$account = $transaction->account;
|
||||||
|
$currency = $repository->find(intval($account->getMeta('currency_id')));
|
||||||
|
$transactions = $journal->transactions()->get();
|
||||||
|
$transactions->each(
|
||||||
|
function (Transaction $transaction) use ($currency) {
|
||||||
|
if (is_null($transaction->transaction_currency_id)) {
|
||||||
|
$transaction->transaction_currency_id = $currency->id;
|
||||||
|
$transaction->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// when mismatch in transaction:
|
||||||
|
if ($transaction->transaction_currency_id !== $currency->id) {
|
||||||
|
$transaction->foreign_currency_id = $transaction->transaction_currency_id;
|
||||||
|
$transaction->foreign_amount = $transaction->amount;
|
||||||
|
$transaction->transaction_currency_id = $currency->id;
|
||||||
|
$transaction->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// also update the journal, of course:
|
||||||
|
$journal->transaction_currency_id = $currency->id;
|
||||||
|
$journal->save();
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// do not match:
|
return;
|
||||||
if ($accountCurrency !== $obCurrency) {
|
}
|
||||||
// update opening balance:
|
|
||||||
$openingBalance->transaction_currency_id = $accountCurrency;
|
/**
|
||||||
$openingBalance->save();
|
* This routine verifies that transfers have the correct currency settings for the accounts they are linked to.
|
||||||
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
* For transfers, this is can be a destructive routine since we FORCE them into a currency setting whether they
|
||||||
continue;
|
* like it or not. Previous routines MUST have set the currency setting for both accounts for this to work.
|
||||||
|
*
|
||||||
|
* A transfer always has the
|
||||||
|
*
|
||||||
|
* Both source and destination must match the respective currency preference. So FF3 must verify ALL
|
||||||
|
* transactions.
|
||||||
|
*/
|
||||||
|
public function updateTransferCurrencies()
|
||||||
|
{
|
||||||
|
$set = TransactionJournal
|
||||||
|
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||||
|
->where('transaction_types.type', TransactionType::TRANSFER)
|
||||||
|
->get(['transaction_journals.*']);
|
||||||
|
|
||||||
|
$set->each(
|
||||||
|
function (TransactionJournal $transfer) {
|
||||||
|
// select all "source" transactions:
|
||||||
|
/** @var Collection $transactions */
|
||||||
|
$transactions = $transfer->transactions()->where('amount', '<', 0)->get();
|
||||||
|
$transactions->each(
|
||||||
|
function (Transaction $transaction) {
|
||||||
|
$this->updateTransactionCurrency($transaction);
|
||||||
|
$this->updateJournalCurrency($transaction);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// opening balance 0, account not zero? just continue:
|
|
||||||
// both are equal, just continue:
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method makes sure that the transaction journal uses the currency given in the transaction.
|
||||||
|
*
|
||||||
|
* @param Transaction $transaction
|
||||||
|
*/
|
||||||
|
private function updateJournalCurrency(Transaction $transaction): void
|
||||||
|
{
|
||||||
|
/** @var CurrencyRepositoryInterface $repository */
|
||||||
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
|
$currency = $repository->find(intval($transaction->account->getMeta('currency_id')));
|
||||||
|
$journal = $transaction->transactionJournal;
|
||||||
|
|
||||||
|
if (!(intval($currency->id) === intval($journal->transaction_currency_id))) {
|
||||||
|
$this->line(
|
||||||
|
sprintf(
|
||||||
|
'Transfer #%d ("%s") has been updated to use %s instead of %s.', $journal->id, $journal->description, $currency->code,
|
||||||
|
$journal->transactionCurrency->code
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$journal->transaction_currency_id = $currency->id;
|
||||||
|
$journal->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -267,7 +304,7 @@ class UpgradeDatabase extends Command
|
|||||||
*
|
*
|
||||||
* @param int $journalId
|
* @param int $journalId
|
||||||
*/
|
*/
|
||||||
private function updateJournal(int $journalId)
|
private function updateJournalidentifiers(int $journalId): void
|
||||||
{
|
{
|
||||||
$identifier = 0;
|
$identifier = 0;
|
||||||
$processed = [];
|
$processed = [];
|
||||||
@ -295,121 +332,128 @@ class UpgradeDatabase extends Command
|
|||||||
if (!is_null($opposing)) {
|
if (!is_null($opposing)) {
|
||||||
// give both a new identifier:
|
// give both a new identifier:
|
||||||
$transaction->identifier = $identifier;
|
$transaction->identifier = $identifier;
|
||||||
|
$opposing->identifier = $identifier;
|
||||||
$transaction->save();
|
$transaction->save();
|
||||||
$opposing->identifier = $identifier;
|
|
||||||
$opposing->save();
|
$opposing->save();
|
||||||
$processed[] = $transaction->id;
|
$processed[] = $transaction->id;
|
||||||
$processed[] = $opposing->id;
|
$processed[] = $opposing->id;
|
||||||
}
|
}
|
||||||
$identifier++;
|
$identifier++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes sure that withdrawals, deposits and transfers have
|
* This method makes sure that the tranaction uses the same currency as the source account does.
|
||||||
* a currency setting matching their respective accounts
|
* If not, the currency is updated to include a reference to its original currency as the "foreign" currency.
|
||||||
*/
|
|
||||||
private function updateJournalCurrencies()
|
|
||||||
{
|
|
||||||
$types = [
|
|
||||||
TransactionType::WITHDRAWAL => '<',
|
|
||||||
TransactionType::DEPOSIT => '>',
|
|
||||||
];
|
|
||||||
$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();
|
|
||||||
$pgsql = ['pgsql', 'postgresql'];
|
|
||||||
|
|
||||||
foreach ($types as $type => $operator) {
|
|
||||||
$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');
|
|
||||||
}
|
|
||||||
)
|
|
||||||
->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');
|
|
||||||
if (in_array($driver, $pgsql)) {
|
|
||||||
$query->where('transaction_journals.transaction_currency_id', '!=', DB::raw('cast(account_meta.data as int)'));
|
|
||||||
}
|
|
||||||
if (!in_array($driver, $pgsql)) {
|
|
||||||
$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));
|
|
||||||
$line = sprintf($notification, $type, $journal->id, $journal->transactionCurrency->code, $expectedCurrency->code);
|
|
||||||
|
|
||||||
$journal->setMeta('foreign_amount', $journal->transaction_amount);
|
|
||||||
$journal->setMeta('foreign_currency_id', $journal->transaction_currency_id);
|
|
||||||
$journal->transaction_currency_id = $expectedCurrency->id;
|
|
||||||
$journal->save();
|
|
||||||
$this->line($line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* For transfers it's slightly different. Both source and destination
|
|
||||||
* must match the respective currency preference. So we must verify ALL
|
|
||||||
* transactions.
|
|
||||||
*/
|
|
||||||
$set = TransactionJournal
|
|
||||||
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
|
||||||
->where('transaction_types.type', TransactionType::TRANSFER)
|
|
||||||
->get(['transaction_journals.*']);
|
|
||||||
/** @var TransactionJournal $journal */
|
|
||||||
foreach ($set as $journal) {
|
|
||||||
$updated = false;
|
|
||||||
/** @var Transaction $sourceTransaction */
|
|
||||||
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
|
|
||||||
$sourceCurrency = $repository->find(intval($sourceTransaction->account->getMeta('currency_id')));
|
|
||||||
|
|
||||||
if ($sourceCurrency->id !== $journal->transaction_currency_id) {
|
|
||||||
$updated = true;
|
|
||||||
$journal->transaction_currency_id = $sourceCurrency->id;
|
|
||||||
$journal->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
// destination
|
|
||||||
$destinationTransaction = $journal->transactions()->where('amount', '>', 0)->first();
|
|
||||||
$destinationCurrency = $repository->find(intval($destinationTransaction->account->getMeta('currency_id')));
|
|
||||||
|
|
||||||
if ($destinationCurrency->id !== $journal->transaction_currency_id) {
|
|
||||||
$updated = true;
|
|
||||||
$journal->deleteMeta('foreign_amount');
|
|
||||||
$journal->deleteMeta('foreign_currency_id');
|
|
||||||
$journal->setMeta('foreign_amount', $destinationTransaction->amount);
|
|
||||||
$journal->setMeta('foreign_currency_id', $destinationCurrency->id);
|
|
||||||
}
|
|
||||||
if ($updated) {
|
|
||||||
$line = sprintf($transfer, $journal->id);
|
|
||||||
$this->line($line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
*
|
||||||
|
* The transaction that is sent to this function MUST be the source transaction (amount negative).
|
||||||
|
*
|
||||||
|
* @param Transaction $transaction
|
||||||
*/
|
*/
|
||||||
private function verifyCurrencyInfo()
|
private function updateTransactionCurrency(Transaction $transaction): void
|
||||||
{
|
{
|
||||||
$count = 0;
|
/** @var CurrencyRepositoryInterface $repository */
|
||||||
$transactions = Transaction::get();
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
/** @var Transaction $transaction */
|
$currency = $repository->find(intval($transaction->account->getMeta('currency_id')));
|
||||||
foreach ($transactions as $transaction) {
|
|
||||||
$currencyId = intval($transaction->transaction_currency_id);
|
// has no currency ID? Must have, so fill in using account preference:
|
||||||
$foreignId = intval($transaction->foreign_currency_id);
|
if (is_null($transaction->transaction_currency_id)) {
|
||||||
if ($currencyId === $foreignId) {
|
$transaction->transaction_currency_id = $currency->id;
|
||||||
$transaction->foreign_currency_id = null;
|
Log::debug(sprintf('Transaction #%d has no currency setting, now set to %s', $transaction->id, $currency->code));
|
||||||
$transaction->foreign_amount = null;
|
$transaction->save();
|
||||||
$transaction->save();
|
|
||||||
$count++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$this->line(sprintf('Updated currency information for %d transactions', $count));
|
|
||||||
|
// does not match the source account (see above)? Can be fixed
|
||||||
|
// when mismatch in transaction and NO foreign amount is set:
|
||||||
|
if ($transaction->transaction_currency_id !== $currency->id && is_null($transaction->foreign_amount)) {
|
||||||
|
Log::debug(
|
||||||
|
sprintf(
|
||||||
|
'Transaction #%d has a currency setting (#%d) that should be #%d. Amount remains %s, currency is changed.', $transaction->id,
|
||||||
|
$transaction->transaction_currency_id, $currency->id, $transaction->amount
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$transaction->transaction_currency_id = $currency->id;
|
||||||
|
$transaction->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab opposing transaction:
|
||||||
|
/** @var TransactionJournal $journal */
|
||||||
|
$journal = $transaction->transactionJournal;
|
||||||
|
/** @var Transaction $opposing */
|
||||||
|
$opposing = $journal->transactions()->where('amount', '>', 0)->where('identifier', $transaction->identifier)->first();
|
||||||
|
$opposingCurrency = $repository->find(intval($opposing->account->getMeta('currency_id')));
|
||||||
|
|
||||||
|
if (is_null($opposingCurrency->id)) {
|
||||||
|
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $opposing->account->id, $opposing->account->name));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the destination account currency is the same, both foreign_amount and foreign_currency_id must be NULL for both transactions:
|
||||||
|
if ($opposingCurrency->id === $currency->id) {
|
||||||
|
// update both transactions to match:
|
||||||
|
$transaction->foreign_amount = null;
|
||||||
|
$transaction->foreign_currency_id = null;
|
||||||
|
$opposing->foreign_amount = null;
|
||||||
|
$opposing->foreign_currency_id = null;
|
||||||
|
$opposing->transaction_currency_id = $currency->id;
|
||||||
|
$transaction->save();
|
||||||
|
$opposing->save();
|
||||||
|
Log::debug(sprintf('Cleaned up transaction #%d and #%d', $transaction->id, $opposing->id));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// if destination account currency is different, both transactions must have this currency as foreign currency id.
|
||||||
|
if ($opposingCurrency->id !== $currency->id) {
|
||||||
|
$transaction->foreign_currency_id = $opposingCurrency->id;
|
||||||
|
$opposing->foreign_currency_id = $opposingCurrency->id;
|
||||||
|
$transaction->save();
|
||||||
|
$opposing->save();
|
||||||
|
Log::debug(sprintf('Verified foreign currency ID of transaction #%d and #%d', $transaction->id, $opposing->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if foreign amount of one is null and the other is not, use this to restore:
|
||||||
|
if (is_null($transaction->foreign_amount) && !is_null($opposing->foreign_amount)) {
|
||||||
|
$transaction->foreign_amount = bcmul(strval($opposing->foreign_amount), '-1');
|
||||||
|
$transaction->save();
|
||||||
|
Log::debug(sprintf('Restored foreign amount of transaction (1) #%d to %s', $transaction->id, $transaction->foreign_amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if foreign amount of one is null and the other is not, use this to restore (other way around)
|
||||||
|
if (is_null($opposing->foreign_amount) && !is_null($transaction->foreign_amount)) {
|
||||||
|
$opposing->foreign_amount = bcmul(strval($transaction->foreign_amount), '-1');
|
||||||
|
$opposing->save();
|
||||||
|
Log::debug(sprintf('Restored foreign amount of transaction (2) #%d to %s', $opposing->id, $opposing->foreign_amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
// when both are zero, try to grab it from journal:
|
||||||
|
if (is_null($opposing->foreign_amount) && is_null($transaction->foreign_amount)) {
|
||||||
|
|
||||||
|
$foreignAmount = $journal->getMeta('foreign_amount');
|
||||||
|
if (is_null($foreignAmount)) {
|
||||||
|
Log::debug(sprintf('Journal #%d has missing foreign currency data, forced to do 1:1 conversion :(.', $transaction->transaction_journal_id));
|
||||||
|
$transaction->foreign_amount = bcmul(strval($transaction->amount), '-1');
|
||||||
|
$opposing->foreign_amount = bcmul(strval($opposing->amount), '-1');
|
||||||
|
$transaction->save();
|
||||||
|
$opposing->save();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$foreignPositive = app('steam')->positive(strval($foreignAmount));
|
||||||
|
Log::debug(
|
||||||
|
sprintf(
|
||||||
|
'Journal #%d has missing foreign currency info, try to restore from meta-data ("%s").', $transaction->transaction_journal_id, $foreignAmount
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$transaction->foreign_amount = bcmul($foreignPositive, '-1');
|
||||||
|
$opposing->foreign_amount = $foreignPositive;
|
||||||
|
$transaction->save();
|
||||||
|
$opposing->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,9 @@ class UpgradeFireflyInstructions extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render instructions.
|
||||||
|
*/
|
||||||
private function installInstructions()
|
private function installInstructions()
|
||||||
{
|
{
|
||||||
/** @var string $version */
|
/** @var string $version */
|
||||||
@ -102,7 +105,7 @@ class UpgradeFireflyInstructions extends Command
|
|||||||
$this->boxed('');
|
$this->boxed('');
|
||||||
if (is_null($text)) {
|
if (is_null($text)) {
|
||||||
|
|
||||||
$this->boxed(sprintf('Thank you for installin Firefly III, v%s!', $version));
|
$this->boxed(sprintf('Thank you for installing Firefly III, v%s!', $version));
|
||||||
$this->boxedInfo('There are no extra installation instructions.');
|
$this->boxedInfo('There are no extra installation instructions.');
|
||||||
$this->boxed('Firefly III should be ready for use.');
|
$this->boxed('Firefly III should be ready for use.');
|
||||||
$this->boxed('');
|
$this->boxed('');
|
||||||
@ -131,6 +134,9 @@ class UpgradeFireflyInstructions extends Command
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render upgrade instructions.
|
||||||
|
*/
|
||||||
private function updateInstructions()
|
private function updateInstructions()
|
||||||
{
|
{
|
||||||
/** @var string $version */
|
/** @var string $version */
|
||||||
|
@ -1,4 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UseEncryption.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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,6 +26,8 @@ use Illuminate\Support\Str;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Class UseEncryption
|
* Class UseEncryption
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Console\Commands
|
||||||
*/
|
*/
|
||||||
class UseEncryption extends Command
|
class UseEncryption extends Command
|
||||||
{
|
{
|
||||||
@ -46,7 +58,6 @@ class UseEncryption extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
//
|
|
||||||
$this->handleObjects('Account', 'name', 'encrypted');
|
$this->handleObjects('Account', 'name', 'encrypted');
|
||||||
$this->handleObjects('Bill', 'name', 'name_encrypted');
|
$this->handleObjects('Bill', 'name', 'name_encrypted');
|
||||||
$this->handleObjects('Bill', 'match', 'match_encrypted');
|
$this->handleObjects('Bill', 'match', 'match_encrypted');
|
||||||
@ -57,6 +68,8 @@ class UseEncryption extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Run each object and encrypt them (or not).
|
||||||
|
*
|
||||||
* @param string $class
|
* @param string $class
|
||||||
* @param string $field
|
* @param string $field
|
||||||
* @param string $indicator
|
* @param string $indicator
|
||||||
|
@ -17,6 +17,7 @@ use Crypt;
|
|||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
|
use FireflyIII\Models\PiggyBankEvent;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
@ -94,6 +95,40 @@ class VerifyDatabase extends Command
|
|||||||
// report on journals with the wrong types of accounts.
|
// report on journals with the wrong types of accounts.
|
||||||
$this->reportIncorrectJournals();
|
$this->reportIncorrectJournals();
|
||||||
|
|
||||||
|
// report (and fix) piggy banks
|
||||||
|
$this->repairPiggyBanks();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure there are only transfers linked to piggy bank events.
|
||||||
|
*/
|
||||||
|
private function repairPiggyBanks(): void
|
||||||
|
{
|
||||||
|
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
|
||||||
|
$set->each(
|
||||||
|
function (PiggyBankEvent $event) {
|
||||||
|
if (is_null($event->transaction_journal_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/** @var TransactionJournal $journal */
|
||||||
|
$journal = $event->transactionJournal()->first();
|
||||||
|
if (is_null($journal)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $journal->transactionType->type;
|
||||||
|
if ($type !== TransactionType::TRANSFER) {
|
||||||
|
$event->transaction_journal_id = null;
|
||||||
|
$event->save();
|
||||||
|
$this->line(sprintf('Piggy bank #%d was referenced by an invalid event. This has been fixed.', $event->piggy_bank_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,6 +204,9 @@ class VerifyDatabase extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report on journals with bad account types linked to them.
|
||||||
|
*/
|
||||||
private function reportIncorrectJournals()
|
private function reportIncorrectJournals()
|
||||||
{
|
{
|
||||||
$configuration = [
|
$configuration = [
|
||||||
@ -235,7 +273,7 @@ class VerifyDatabase extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Report on journals without transactions.
|
||||||
*/
|
*/
|
||||||
private function reportNoTransactions()
|
private function reportNoTransactions()
|
||||||
{
|
{
|
||||||
@ -253,6 +291,8 @@ class VerifyDatabase extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Report on things with no linked journals.
|
||||||
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
*/
|
*/
|
||||||
private function reportObject(string $name)
|
private function reportObject(string $name)
|
||||||
@ -324,7 +364,7 @@ class VerifyDatabase extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Report on transfers that have budgets.
|
||||||
*/
|
*/
|
||||||
private function reportTransfersBudgets()
|
private function reportTransfersBudgets()
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,7 @@ namespace FireflyIII\Export\Collector;
|
|||||||
|
|
||||||
use Crypt;
|
use Crypt;
|
||||||
use Illuminate\Contracts\Encryption\DecryptException;
|
use Illuminate\Contracts\Encryption\DecryptException;
|
||||||
|
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||||
use Log;
|
use Log;
|
||||||
use Storage;
|
use Storage;
|
||||||
|
|
||||||
@ -50,14 +51,6 @@ class UploadCollector extends BasicCollector implements CollectorInterface
|
|||||||
public function run(): bool
|
public function run(): bool
|
||||||
{
|
{
|
||||||
Log::debug('Going to collect attachments', ['key' => $this->job->key]);
|
Log::debug('Going to collect attachments', ['key' => $this->job->key]);
|
||||||
|
|
||||||
// file names associated with the old import routine.
|
|
||||||
$this->vintageFormat = sprintf('csv-upload-%d-', $this->job->user->id);
|
|
||||||
|
|
||||||
// collect old upload files (names beginning with "csv-upload".
|
|
||||||
$this->collectVintageUploads();
|
|
||||||
|
|
||||||
// then collect current upload files:
|
|
||||||
$this->collectModernUploads();
|
$this->collectModernUploads();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -70,7 +63,8 @@ class UploadCollector extends BasicCollector implements CollectorInterface
|
|||||||
*/
|
*/
|
||||||
private function collectModernUploads(): bool
|
private function collectModernUploads(): bool
|
||||||
{
|
{
|
||||||
$set = $this->job->user->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']);
|
$set = $this->job->user->importJobs()->whereIn('status', ['import_complete', 'finished'])->get(['import_jobs.*']);
|
||||||
|
Log::debug(sprintf('Found %d import jobs', $set->count()));
|
||||||
$keys = [];
|
$keys = [];
|
||||||
if ($set->count() > 0) {
|
if ($set->count() > 0) {
|
||||||
$keys = $set->pluck('key')->toArray();
|
$keys = $set->pluck('key')->toArray();
|
||||||
@ -83,59 +77,6 @@ class UploadCollector extends BasicCollector implements CollectorInterface
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method collects all the uploads that are uploaded using the "old" importer. So from before the summer of 2016.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function collectVintageUploads(): bool
|
|
||||||
{
|
|
||||||
// grab upload directory.
|
|
||||||
$files = $this->uploadDisk->files();
|
|
||||||
|
|
||||||
foreach ($files as $entry) {
|
|
||||||
$this->processVintageUpload($entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method tells you when the vintage upload file was actually uploaded.
|
|
||||||
*
|
|
||||||
* @param string $entry
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function getVintageUploadDate(string $entry): string
|
|
||||||
{
|
|
||||||
// this is an original upload.
|
|
||||||
$parts = explode('-', str_replace(['.csv.encrypted', $this->vintageFormat], '', $entry));
|
|
||||||
$originalUpload = intval($parts[1]);
|
|
||||||
$date = date('Y-m-d \a\t H-i-s', $originalUpload);
|
|
||||||
|
|
||||||
return $date;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells you if a file name is a vintage upload.
|
|
||||||
*
|
|
||||||
* @param string $entry
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function isVintageImport(string $entry): bool
|
|
||||||
{
|
|
||||||
$len = strlen($this->vintageFormat);
|
|
||||||
// file is part of the old import routine:
|
|
||||||
if (substr($entry, 0, $len) === $this->vintageFormat) {
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $key
|
* @param string $key
|
||||||
*
|
*
|
||||||
@ -153,7 +94,7 @@ class UploadCollector extends BasicCollector implements CollectorInterface
|
|||||||
$content = '';
|
$content = '';
|
||||||
try {
|
try {
|
||||||
$content = Crypt::decrypt($this->uploadDisk->get(sprintf('%s.upload', $key)));
|
$content = Crypt::decrypt($this->uploadDisk->get(sprintf('%s.upload', $key)));
|
||||||
} catch (DecryptException $e) {
|
} catch (FileNotFoundException | DecryptException $e) {
|
||||||
Log::error(sprintf('Could not decrypt old import file "%s". Skipped because: %s', $key, $e->getMessage()));
|
Log::error(sprintf('Could not decrypt old import file "%s". Skipped because: %s', $key, $e->getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,47 +109,4 @@ class UploadCollector extends BasicCollector implements CollectorInterface
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If the file is a vintage upload, process it.
|
|
||||||
*
|
|
||||||
* @param string $entry
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function processVintageUpload(string $entry): bool
|
|
||||||
{
|
|
||||||
if ($this->isVintageImport($entry)) {
|
|
||||||
$this->saveVintageImportFile($entry);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This will store the content of the old vintage upload somewhere.
|
|
||||||
*
|
|
||||||
* @param string $entry
|
|
||||||
*/
|
|
||||||
private function saveVintageImportFile(string $entry)
|
|
||||||
{
|
|
||||||
$content = '';
|
|
||||||
try {
|
|
||||||
$content = Crypt::decrypt($this->uploadDisk->get($entry));
|
|
||||||
} catch (DecryptException $e) {
|
|
||||||
Log::error('Could not decrypt old CSV import file ' . $entry . '. Skipped because ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen($content) > 0) {
|
|
||||||
// add to export disk.
|
|
||||||
$date = $this->getVintageUploadDate($entry);
|
|
||||||
$file = $this->job->key . '-Old import dated ' . $date . '.csv';
|
|
||||||
$this->exportDisk->put($file, $content);
|
|
||||||
$this->getEntries()->push($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Export\Entry;
|
namespace FireflyIII\Export\Entry;
|
||||||
|
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
use Steam;
|
use Steam;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,24 +38,43 @@ final class Entry
|
|||||||
{
|
{
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
public $journal_id;
|
public $journal_id;
|
||||||
|
public $transaction_id = 0;
|
||||||
|
|
||||||
public $date;
|
public $date;
|
||||||
public $description;
|
public $description;
|
||||||
|
|
||||||
public $currency_code;
|
public $currency_code;
|
||||||
public $amount;
|
public $amount;
|
||||||
|
public $foreign_currency_code = '';
|
||||||
|
public $foreign_amount = '0';
|
||||||
|
|
||||||
public $transaction_type;
|
public $transaction_type;
|
||||||
|
|
||||||
public $asset_account_id;
|
public $asset_account_id;
|
||||||
public $asset_account_name;
|
public $asset_account_name;
|
||||||
|
public $asset_account_iban;
|
||||||
|
public $asset_account_bic;
|
||||||
|
public $asset_account_number;
|
||||||
|
public $asset_currency_code;
|
||||||
|
|
||||||
public $opposing_account_id;
|
public $opposing_account_id;
|
||||||
public $opposing_account_name;
|
public $opposing_account_name;
|
||||||
|
public $opposing_account_iban;
|
||||||
|
public $opposing_account_bic;
|
||||||
|
public $opposing_account_number;
|
||||||
|
public $opposing_currency_code;
|
||||||
|
|
||||||
public $budget_id;
|
public $budget_id;
|
||||||
public $budget_name;
|
public $budget_name;
|
||||||
|
|
||||||
public $category_id;
|
public $category_id;
|
||||||
public $category_name;
|
public $category_name;
|
||||||
|
|
||||||
|
public $bill_id;
|
||||||
|
public $bill_name;
|
||||||
|
|
||||||
|
public $notes;
|
||||||
|
public $tags;
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,5 +115,75 @@ final class Entry
|
|||||||
return $entry;
|
return $entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a given transaction (as collected by the collector) into an export entry.
|
||||||
|
*
|
||||||
|
* @param Transaction $transaction
|
||||||
|
*
|
||||||
|
* @return Entry
|
||||||
|
*/
|
||||||
|
public static function fromTransaction(Transaction $transaction): Entry
|
||||||
|
{
|
||||||
|
$entry = new self;
|
||||||
|
$entry->journal_id = $transaction->journal_id;
|
||||||
|
$entry->transaction_id = $transaction->id;
|
||||||
|
$entry->date = $transaction->date->format('Ymd');
|
||||||
|
$entry->description = $transaction->description;
|
||||||
|
if (strlen(strval($transaction->transaction_description)) > 0) {
|
||||||
|
$entry->description = $transaction->transaction_description . '(' . $transaction->description . ')';
|
||||||
|
}
|
||||||
|
$entry->currency_code = $transaction->transactionCurrency->code;
|
||||||
|
$entry->amount = round($transaction->transaction_amount, $transaction->transactionCurrency->decimal_places);
|
||||||
|
|
||||||
|
$entry->foreign_currency_code = is_null($transaction->foreign_currency_id) ? null : $transaction->foreignCurrency->code;
|
||||||
|
$entry->foreign_amount = is_null($transaction->foreign_currency_id)
|
||||||
|
? null
|
||||||
|
: strval(
|
||||||
|
round(
|
||||||
|
$transaction->transaction_foreign_amount, $transaction->foreignCurrency->decimal_places
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$entry->transaction_type = $transaction->transaction_type_type;
|
||||||
|
$entry->asset_account_id = $transaction->account_id;
|
||||||
|
$entry->asset_account_name = app('steam')->tryDecrypt($transaction->account_name);
|
||||||
|
$entry->asset_account_iban = $transaction->account_iban;
|
||||||
|
$entry->asset_account_number = $transaction->account_number;
|
||||||
|
$entry->asset_account_bic = $transaction->account_bic;
|
||||||
|
$entry->asset_currency_code = $transaction->account_currency_code;
|
||||||
|
|
||||||
|
$entry->opposing_account_id = $transaction->opposing_account_id;
|
||||||
|
$entry->opposing_account_name = app('steam')->tryDecrypt($transaction->opposing_account_name);
|
||||||
|
$entry->opposing_account_iban = $transaction->opposing_account_iban;
|
||||||
|
$entry->opposing_account_number = $transaction->opposing_account_number;
|
||||||
|
$entry->opposing_account_bic = $transaction->opposing_account_bic;
|
||||||
|
$entry->opposing_currency_code = $transaction->opposing_currency_code;
|
||||||
|
|
||||||
|
/** budget */
|
||||||
|
$entry->budget_id = $transaction->transaction_budget_id;
|
||||||
|
$entry->budget_name = app('steam')->tryDecrypt($transaction->transaction_budget_name);
|
||||||
|
if (is_null($transaction->transaction_budget_id)) {
|
||||||
|
$entry->budget_id = $transaction->transaction_journal_budget_id;
|
||||||
|
$entry->budget_name = app('steam')->tryDecrypt($transaction->transaction_journal_budget_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** category */
|
||||||
|
$entry->category_id = $transaction->transaction_category_id;
|
||||||
|
$entry->category_name = app('steam')->tryDecrypt($transaction->transaction_category_name);
|
||||||
|
if (is_null($transaction->transaction_category_id)) {
|
||||||
|
$entry->category_id = $transaction->transaction_journal_category_id;
|
||||||
|
$entry->category_name = app('steam')->tryDecrypt($transaction->transaction_journal_category_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** budget */
|
||||||
|
$entry->bill_id = $transaction->bill_id;
|
||||||
|
$entry->bill_name = app('steam')->tryDecrypt($transaction->bill_name);
|
||||||
|
|
||||||
|
$entry->tags = $transaction->tags;
|
||||||
|
$entry->notes = $transaction->notes;
|
||||||
|
|
||||||
|
return $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
339
app/Export/ExpandedProcessor.php
Normal file
339
app/Export/ExpandedProcessor.php
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ExpandedProcessor.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\Export;
|
||||||
|
|
||||||
|
|
||||||
|
use Crypt;
|
||||||
|
use DB;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Export\Collector\AttachmentCollector;
|
||||||
|
use FireflyIII\Export\Collector\UploadCollector;
|
||||||
|
use FireflyIII\Export\Entry\Entry;
|
||||||
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
||||||
|
use FireflyIII\Models\AccountMeta;
|
||||||
|
use FireflyIII\Models\ExportJob;
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use FireflyIII\Models\TransactionJournalMeta;
|
||||||
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
use Storage;
|
||||||
|
use ZipArchive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpandedProcessor
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Export
|
||||||
|
*/
|
||||||
|
class ExpandedProcessor implements ProcessorInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var Collection */
|
||||||
|
public $accounts;
|
||||||
|
/** @var string */
|
||||||
|
public $exportFormat;
|
||||||
|
/** @var bool */
|
||||||
|
public $includeAttachments;
|
||||||
|
/** @var bool */
|
||||||
|
public $includeOldUploads;
|
||||||
|
/** @var ExportJob */
|
||||||
|
public $job;
|
||||||
|
/** @var array */
|
||||||
|
public $settings;
|
||||||
|
/** @var Collection */
|
||||||
|
private $exportEntries;
|
||||||
|
/** @var Collection */
|
||||||
|
private $files;
|
||||||
|
/** @var Collection */
|
||||||
|
private $journals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processor constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->journals = new Collection;
|
||||||
|
$this->exportEntries = new Collection;
|
||||||
|
$this->files = new Collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function collectAttachments(): bool
|
||||||
|
{
|
||||||
|
/** @var AttachmentCollector $attachmentCollector */
|
||||||
|
$attachmentCollector = app(AttachmentCollector::class);
|
||||||
|
$attachmentCollector->setJob($this->job);
|
||||||
|
$attachmentCollector->setDates($this->settings['startDate'], $this->settings['endDate']);
|
||||||
|
$attachmentCollector->run();
|
||||||
|
$this->files = $this->files->merge($attachmentCollector->getEntries());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function collectJournals(): bool
|
||||||
|
{
|
||||||
|
// use journal collector thing.
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAccounts($this->accounts)->setRange($this->settings['startDate'], $this->settings['endDate'])
|
||||||
|
->withOpposingAccount()->withBudgetInformation()->withCategoryInformation()
|
||||||
|
->removeFilter(InternalTransferFilter::class);
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
// get some more meta data for each entry:
|
||||||
|
$ids = $transactions->pluck('journal_id')->toArray();
|
||||||
|
$assetIds = $transactions->pluck('account_id')->toArray();
|
||||||
|
$opposingIds = $transactions->pluck('opposing_account_id')->toArray();
|
||||||
|
$notes = $this->getNotes($ids);
|
||||||
|
$tags = $this->getTags($ids);
|
||||||
|
$ibans = $this->getIbans($assetIds) + $this->getIbans($opposingIds);
|
||||||
|
$currencies = $this->getAccountCurrencies($ibans);
|
||||||
|
$transactions->each(
|
||||||
|
function (Transaction $transaction) use ($notes, $tags, $ibans, $currencies) {
|
||||||
|
$journalId = intval($transaction->journal_id);
|
||||||
|
$accountId = intval($transaction->account_id);
|
||||||
|
$opposingId = intval($transaction->opposing_account_id);
|
||||||
|
$currencyId = $ibans[$accountId]['currency_id'] ?? 0;
|
||||||
|
$opposingCurrencyId = $ibans[$opposingId]['currency_id'] ?? 0;
|
||||||
|
$transaction->notes = $notes[$journalId] ?? '';
|
||||||
|
$transaction->tags = join(',', $tags[$journalId] ?? []);
|
||||||
|
$transaction->account_number = $ibans[$accountId]['accountNumber'] ?? '';
|
||||||
|
$transaction->account_bic = $ibans[$accountId]['BIC'] ?? '';
|
||||||
|
$transaction->account_currency_code = $currencies[$currencyId] ?? '';
|
||||||
|
$transaction->opposing_account_number = $ibans[$opposingId]['accountNumber'] ?? '';
|
||||||
|
$transaction->opposing_account_bic = $ibans[$opposingId]['BIC'] ?? '';
|
||||||
|
$transaction->opposing_currency_code = $currencies[$opposingCurrencyId] ?? '';
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->journals = $transactions;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function collectOldUploads(): bool
|
||||||
|
{
|
||||||
|
/** @var UploadCollector $uploadCollector */
|
||||||
|
$uploadCollector = app(UploadCollector::class);
|
||||||
|
$uploadCollector->setJob($this->job);
|
||||||
|
$uploadCollector->run();
|
||||||
|
|
||||||
|
$this->files = $this->files->merge($uploadCollector->getEntries());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function convertJournals(): bool
|
||||||
|
{
|
||||||
|
$this->journals->each(
|
||||||
|
function (Transaction $transaction) {
|
||||||
|
$this->exportEntries->push(Entry::fromTransaction($transaction));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Log::debug(sprintf('Count %d entries in exportEntries (convertJournals)', $this->exportEntries->count()));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function createZipFile(): bool
|
||||||
|
{
|
||||||
|
$zip = new ZipArchive;
|
||||||
|
$file = $this->job->key . '.zip';
|
||||||
|
$fullPath = storage_path('export') . '/' . $file;
|
||||||
|
|
||||||
|
if ($zip->open($fullPath, ZipArchive::CREATE) !== true) {
|
||||||
|
throw new FireflyException('Cannot store zip file.');
|
||||||
|
}
|
||||||
|
// for each file in the collection, add it to the zip file.
|
||||||
|
$disk = Storage::disk('export');
|
||||||
|
foreach ($this->getFiles() as $entry) {
|
||||||
|
// is part of this job?
|
||||||
|
$zipFileName = str_replace($this->job->key . '-', '', $entry);
|
||||||
|
$zip->addFromString($zipFileName, $disk->get($entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
$zip->close();
|
||||||
|
|
||||||
|
// delete the files:
|
||||||
|
$this->deleteFiles();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function exportJournals(): bool
|
||||||
|
{
|
||||||
|
$exporterClass = config('firefly.export_formats.' . $this->exportFormat);
|
||||||
|
$exporter = app($exporterClass);
|
||||||
|
$exporter->setJob($this->job);
|
||||||
|
$exporter->setEntries($this->exportEntries);
|
||||||
|
$exporter->run();
|
||||||
|
$this->files->push($exporter->getFileName());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getFiles(): Collection
|
||||||
|
{
|
||||||
|
return $this->files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save export job settings to class.
|
||||||
|
*
|
||||||
|
* @param array $settings
|
||||||
|
*/
|
||||||
|
public function setSettings(array $settings)
|
||||||
|
{
|
||||||
|
// save settings
|
||||||
|
$this->settings = $settings;
|
||||||
|
$this->accounts = $settings['accounts'];
|
||||||
|
$this->exportFormat = $settings['exportFormat'];
|
||||||
|
$this->includeAttachments = $settings['includeAttachments'];
|
||||||
|
$this->includeOldUploads = $settings['includeOldUploads'];
|
||||||
|
$this->job = $settings['job'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function deleteFiles()
|
||||||
|
{
|
||||||
|
$disk = Storage::disk('export');
|
||||||
|
foreach ($this->getFiles() as $file) {
|
||||||
|
$disk->delete($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getAccountCurrencies(array $array): array
|
||||||
|
{
|
||||||
|
/** @var CurrencyRepositoryInterface $repository */
|
||||||
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
|
$return = [];
|
||||||
|
$ids = [];
|
||||||
|
$repository->setUser($this->job->user);
|
||||||
|
foreach ($array as $value) {
|
||||||
|
$ids[] = $value['currency_id'] ?? 0;
|
||||||
|
}
|
||||||
|
$ids = array_unique($ids);
|
||||||
|
$result = $repository->getByIds($ids);
|
||||||
|
|
||||||
|
foreach ($result as $currency) {
|
||||||
|
$return[$currency->id] = $currency->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all IBAN / SWIFT / account numbers
|
||||||
|
*
|
||||||
|
* @param array $array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getIbans(array $array): array
|
||||||
|
{
|
||||||
|
$array = array_unique($array);
|
||||||
|
$return = [];
|
||||||
|
$set = AccountMeta::whereIn('account_id', $array)
|
||||||
|
->leftJoin('accounts', 'accounts.id', 'account_meta.account_id')
|
||||||
|
->where('accounts.user_id', $this->job->user_id)
|
||||||
|
->whereIn('account_meta.name', ['accountNumber', 'BIC', 'currency_id'])
|
||||||
|
->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']);
|
||||||
|
/** @var AccountMeta $meta */
|
||||||
|
foreach ($set as $meta) {
|
||||||
|
$id = intval($meta->account_id);
|
||||||
|
$return[$id][$meta->name] = $meta->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns, if present, for the given journal ID's the notes.
|
||||||
|
*
|
||||||
|
* @param array $array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getNotes(array $array): array
|
||||||
|
{
|
||||||
|
$array = array_unique($array);
|
||||||
|
$set = TransactionJournalMeta::whereIn('journal_meta.transaction_journal_id', $array)
|
||||||
|
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
|
||||||
|
->where('transaction_journals.user_id', $this->job->user_id)
|
||||||
|
->where('journal_meta.name', 'notes')->get(
|
||||||
|
['journal_meta.transaction_journal_id', 'journal_meta.data', 'journal_meta.id']
|
||||||
|
);
|
||||||
|
$return = [];
|
||||||
|
/** @var TransactionJournalMeta $meta */
|
||||||
|
foreach ($set as $meta) {
|
||||||
|
$id = intval($meta->transaction_journal_id);
|
||||||
|
$return[$id] = $meta->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a comma joined list of all the users tags linked to these journals.
|
||||||
|
*
|
||||||
|
* @param array $array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getTags(array $array): array
|
||||||
|
{
|
||||||
|
$set = DB::table('tag_transaction_journal')
|
||||||
|
->whereIn('tag_transaction_journal.transaction_journal_id', $array)
|
||||||
|
->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id')
|
||||||
|
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'tag_transaction_journal.transaction_journal_id')
|
||||||
|
->where('transaction_journals.user_id', $this->job->user_id)
|
||||||
|
->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag']);
|
||||||
|
$result = [];
|
||||||
|
foreach ($set as $entry) {
|
||||||
|
$id = intval($entry->transaction_journal_id);
|
||||||
|
$result[$id] = isset($result[$id]) ? $result[$id] : [];
|
||||||
|
$result[$id][] = Crypt::decrypt($entry->tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
@ -58,8 +58,12 @@ class CsvExporter extends BasicExporter implements ExporterInterface
|
|||||||
|
|
||||||
// get field names for header row:
|
// get field names for header row:
|
||||||
$first = $this->getEntries()->first();
|
$first = $this->getEntries()->first();
|
||||||
$headers = array_keys(get_object_vars($first));
|
$headers = [];
|
||||||
$rows[] = $headers;
|
if (!is_null($first)) {
|
||||||
|
$headers = array_keys(get_object_vars($first));
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows[] = $headers;
|
||||||
|
|
||||||
/** @var Entry $entry */
|
/** @var Entry $entry */
|
||||||
foreach ($this->getEntries() as $entry) {
|
foreach ($this->getEntries() as $entry) {
|
||||||
|
@ -15,7 +15,6 @@ namespace FireflyIII\Helpers\Collector;
|
|||||||
|
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Crypt;
|
|
||||||
use DB;
|
use DB;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Filter\FilterInterface;
|
use FireflyIII\Helpers\Filter\FilterInterface;
|
||||||
@ -31,7 +30,6 @@ use FireflyIII\Models\Tag;
|
|||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Contracts\Encryption\DecryptException;
|
|
||||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
use Illuminate\Database\Query\JoinClause;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
@ -86,7 +84,9 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
|
|
||||||
'accounts.name as account_name',
|
'accounts.name as account_name',
|
||||||
'accounts.encrypted as account_encrypted',
|
'accounts.encrypted as account_encrypted',
|
||||||
|
'accounts.iban as account_iban',
|
||||||
'account_types.type as account_type',
|
'account_types.type as account_type',
|
||||||
|
|
||||||
];
|
];
|
||||||
/** @var bool */
|
/** @var bool */
|
||||||
private $filterTransfers = false;
|
private $filterTransfers = false;
|
||||||
@ -175,12 +175,10 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
if (!is_null($transaction->bill_name)) {
|
if (!is_null($transaction->bill_name)) {
|
||||||
$transaction->bill_name = Steam::decrypt(intval($transaction->bill_name_encrypted), $transaction->bill_name);
|
$transaction->bill_name = Steam::decrypt(intval($transaction->bill_name_encrypted), $transaction->bill_name);
|
||||||
}
|
}
|
||||||
|
$transaction->opposing_account_name = app('steam')->tryDecrypt($transaction->opposing_account_name);
|
||||||
|
$transaction->account_iban = app('steam')->tryDecrypt($transaction->account_iban);
|
||||||
|
$transaction->opposing_account_iban = app('steam')->tryDecrypt($transaction->opposing_account_iban);
|
||||||
|
|
||||||
try {
|
|
||||||
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
|
|
||||||
} catch (DecryptException $e) {
|
|
||||||
// if this fails its already decrypted.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -621,6 +619,8 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
$this->query->leftJoin('budgets as transaction_journal_budgets', 'transaction_journal_budgets.id', '=', 'budget_transaction_journal.budget_id');
|
$this->query->leftJoin('budgets as transaction_journal_budgets', 'transaction_journal_budgets.id', '=', 'budget_transaction_journal.budget_id');
|
||||||
$this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id');
|
$this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id');
|
||||||
$this->query->leftJoin('budgets as transaction_budgets', 'transaction_budgets.id', '=', 'budget_transaction.budget_id');
|
$this->query->leftJoin('budgets as transaction_budgets', 'transaction_budgets.id', '=', 'budget_transaction.budget_id');
|
||||||
|
$this->query->whereNull('transaction_journal_budgets.deleted_at');
|
||||||
|
$this->query->whereNull('transaction_budgets.deleted_at');
|
||||||
|
|
||||||
$this->fields[] = 'budget_transaction_journal.budget_id as transaction_journal_budget_id';
|
$this->fields[] = 'budget_transaction_journal.budget_id as transaction_journal_budget_id';
|
||||||
$this->fields[] = 'transaction_journal_budgets.encrypted as transaction_journal_budget_encrypted';
|
$this->fields[] = 'transaction_journal_budgets.encrypted as transaction_journal_budget_encrypted';
|
||||||
@ -677,10 +677,12 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
$this->query->leftJoin('account_types as opposing_account_types', 'opposing_accounts.account_type_id', '=', 'opposing_account_types.id');
|
$this->query->leftJoin('account_types as opposing_account_types', 'opposing_accounts.account_type_id', '=', 'opposing_account_types.id');
|
||||||
$this->query->whereNull('opposing.deleted_at');
|
$this->query->whereNull('opposing.deleted_at');
|
||||||
|
|
||||||
$this->fields[] = 'opposing.id as opposing_id';
|
$this->fields[] = 'opposing.id as opposing_id';
|
||||||
$this->fields[] = 'opposing.account_id as opposing_account_id';
|
$this->fields[] = 'opposing.account_id as opposing_account_id';
|
||||||
$this->fields[] = 'opposing_accounts.name as opposing_account_name';
|
$this->fields[] = 'opposing_accounts.name as opposing_account_name';
|
||||||
$this->fields[] = 'opposing_accounts.encrypted as opposing_account_encrypted';
|
$this->fields[] = 'opposing_accounts.encrypted as opposing_account_encrypted';
|
||||||
|
$this->fields[] = 'opposing_accounts.iban as opposing_account_iban';
|
||||||
|
|
||||||
$this->fields[] = 'opposing_account_types.type as opposing_account_type';
|
$this->fields[] = 'opposing_account_types.type as opposing_account_type';
|
||||||
$this->joinedOpposing = true;
|
$this->joinedOpposing = true;
|
||||||
Log::debug('joinedOpposing is now true!');
|
Log::debug('joinedOpposing is now true!');
|
||||||
|
@ -14,16 +14,12 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Helpers\Report;
|
namespace FireflyIII\Helpers\Report;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use DB;
|
|
||||||
use FireflyIII\Helpers\Collection\Balance;
|
use FireflyIII\Helpers\Collection\Balance;
|
||||||
use FireflyIII\Helpers\Collection\BalanceEntry;
|
use FireflyIII\Helpers\Collection\BalanceEntry;
|
||||||
use FireflyIII\Helpers\Collection\BalanceHeader;
|
use FireflyIII\Helpers\Collection\BalanceHeader;
|
||||||
use FireflyIII\Helpers\Collection\BalanceLine;
|
use FireflyIII\Helpers\Collection\BalanceLine;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\Tag;
|
|
||||||
use FireflyIII\Models\TransactionType;
|
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
@ -74,14 +70,9 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
|||||||
$line = $this->createBalanceLine($budgetLimit, $accounts);
|
$line = $this->createBalanceLine($budgetLimit, $accounts);
|
||||||
$balance->addBalanceLine($line);
|
$balance->addBalanceLine($line);
|
||||||
}
|
}
|
||||||
Log::debug('Create rest of the things.');
|
$noBudgetLine = $this->createNoBudgetLine($accounts, $start, $end);
|
||||||
$noBudgetLine = $this->createNoBudgetLine($accounts, $start, $end);
|
|
||||||
$coveredByTagLine = $this->createTagsBalanceLine($accounts, $start, $end);
|
|
||||||
$leftUnbalancedLine = $this->createLeftUnbalancedLine($noBudgetLine, $coveredByTagLine);
|
|
||||||
|
|
||||||
$balance->addBalanceLine($noBudgetLine);
|
$balance->addBalanceLine($noBudgetLine);
|
||||||
$balance->addBalanceLine($coveredByTagLine);
|
|
||||||
$balance->addBalanceLine($leftUnbalancedLine);
|
|
||||||
$balance->setBalanceHeader($header);
|
$balance->setBalanceHeader($header);
|
||||||
|
|
||||||
Log::debug('Clear unused budgets.');
|
Log::debug('Clear unused budgets.');
|
||||||
@ -93,54 +84,6 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
|||||||
return $balance;
|
return $balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method collects all transfers that are part of a "balancing act" tag
|
|
||||||
* and groups the amounts of those transfers by their destination account.
|
|
||||||
*
|
|
||||||
* This is used to indicate which expenses, usually outside of budgets, have been
|
|
||||||
* corrected by transfers from a savings account.
|
|
||||||
*
|
|
||||||
* @param Collection $accounts
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
private function allCoveredByBalancingActs(Collection $accounts, Carbon $start, Carbon $end): Collection
|
|
||||||
{
|
|
||||||
$ids = $accounts->pluck('id')->toArray();
|
|
||||||
$set = auth()->user()->tags()
|
|
||||||
->leftJoin('tag_transaction_journal', 'tag_transaction_journal.tag_id', '=', 'tags.id')
|
|
||||||
->leftJoin('transaction_journals', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
|
||||||
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
|
|
||||||
->leftJoin(
|
|
||||||
'transactions AS t_source', function (JoinClause $join) {
|
|
||||||
$join->on('transaction_journals.id', '=', 't_source.transaction_journal_id')->where('t_source.amount', '<', 0);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
->leftJoin(
|
|
||||||
'transactions AS t_destination', function (JoinClause $join) {
|
|
||||||
$join->on('transaction_journals.id', '=', 't_destination.transaction_journal_id')->where('t_destination.amount', '>', 0);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
->where('tags.tagMode', 'balancingAct')
|
|
||||||
->where('transaction_types.type', TransactionType::TRANSFER)
|
|
||||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
|
||||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
|
||||||
->whereNull('transaction_journals.deleted_at')
|
|
||||||
->whereIn('t_source.account_id', $ids)
|
|
||||||
->whereIn('t_destination.account_id', $ids)
|
|
||||||
->groupBy('t_destination.account_id')
|
|
||||||
->get(
|
|
||||||
[
|
|
||||||
't_destination.account_id',
|
|
||||||
DB::raw('SUM(t_destination.amount) AS sum'),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
return $set;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BudgetLimit $budgetLimit
|
* @param BudgetLimit $budgetLimit
|
||||||
@ -168,40 +111,6 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
|||||||
return $line;
|
return $line;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param BalanceLine $noBudgetLine
|
|
||||||
* @param BalanceLine $coveredByTagLine
|
|
||||||
*
|
|
||||||
* @return BalanceLine
|
|
||||||
*/
|
|
||||||
private function createLeftUnbalancedLine(BalanceLine $noBudgetLine, BalanceLine $coveredByTagLine): BalanceLine
|
|
||||||
{
|
|
||||||
$line = new BalanceLine;
|
|
||||||
$line->setRole(BalanceLine::ROLE_DIFFROLE);
|
|
||||||
$noBudgetEntries = $noBudgetLine->getBalanceEntries();
|
|
||||||
$tagEntries = $coveredByTagLine->getBalanceEntries();
|
|
||||||
|
|
||||||
foreach ($noBudgetEntries as $entry) {
|
|
||||||
$account = $entry->getAccount();
|
|
||||||
$tagEntry = $tagEntries->filter(
|
|
||||||
function (BalanceEntry $current) use ($account) {
|
|
||||||
return $current->getAccount()->id === $account->id;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if ($tagEntry->first()) {
|
|
||||||
// found corresponding entry. As we should:
|
|
||||||
$newEntry = new BalanceEntry;
|
|
||||||
$newEntry->setAccount($account);
|
|
||||||
$spent = bcadd($tagEntry->first()->getLeft(), $entry->getSpent());
|
|
||||||
$newEntry->setSpent($spent);
|
|
||||||
$line->addBalanceEntry($newEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $line;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
@ -227,41 +136,6 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
|||||||
return $empty;
|
return $empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection $accounts
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return BalanceLine
|
|
||||||
*/
|
|
||||||
private function createTagsBalanceLine(Collection $accounts, Carbon $start, Carbon $end): BalanceLine
|
|
||||||
{
|
|
||||||
$tags = new BalanceLine;
|
|
||||||
$tagsLeft = $this->allCoveredByBalancingActs($accounts, $start, $end);
|
|
||||||
|
|
||||||
$tags->setRole(BalanceLine::ROLE_TAGROLE);
|
|
||||||
|
|
||||||
foreach ($accounts as $account) {
|
|
||||||
$leftEntry = $tagsLeft->filter(
|
|
||||||
function (Tag $tag) use ($account) {
|
|
||||||
return $tag->account_id === $account->id;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
$left = '0';
|
|
||||||
if (!is_null($leftEntry->first())) {
|
|
||||||
$left = $leftEntry->first()->sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// balanced by tags
|
|
||||||
$tagEntry = new BalanceEntry;
|
|
||||||
$tagEntry->setAccount($account);
|
|
||||||
$tagEntry->setLeft($left);
|
|
||||||
$tags->addBalanceEntry($tagEntry);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Balance $balance
|
* @param Balance $balance
|
||||||
|
@ -194,7 +194,7 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
return view(
|
return view(
|
||||||
'accounts.edit', compact(
|
'accounts.edit', compact(
|
||||||
'allCurrencies', 'currencySelectList', 'account', 'currency', 'subTitle', 'subTitleIcon', 'what', 'roles'
|
'allCurrencies', 'currencySelectList', 'account', 'currency', 'subTitle', 'subTitleIcon', 'what', 'roles', 'preFilled'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
229
app/Http/Controllers/Admin/LinkController.php
Normal file
229
app/Http/Controllers/Admin/LinkController.php
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* LinkController.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\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Http\Requests\LinkTypeFormRequest;
|
||||||
|
use FireflyIII\Models\LinkType;
|
||||||
|
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Preferences;
|
||||||
|
use View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LinkController
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Http\Controllers\Admin
|
||||||
|
*/
|
||||||
|
class LinkController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
View::share('title', strval(trans('firefly.administration')));
|
||||||
|
View::share('mainTitleIcon', 'fa-hand-spock-o');
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
$subTitle = trans('firefly.create_new_link_type');
|
||||||
|
$subTitleIcon = 'fa-link';
|
||||||
|
|
||||||
|
// put previous url in session if not redirect from store (not "create another").
|
||||||
|
if (session('link_types.create.fromStore') !== true) {
|
||||||
|
$this->rememberPreviousUri('link_types.create.uri');
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin.link.create', compact('subTitle', 'subTitleIcon'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param LinkTypeRepositoryInterface $repository
|
||||||
|
* @param LinkType $linkType
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||||
|
*/
|
||||||
|
public function delete(Request $request, LinkTypeRepositoryInterface $repository, LinkType $linkType)
|
||||||
|
{
|
||||||
|
if (!$linkType->editable) {
|
||||||
|
$request->session()->flash('error', strval(trans('firefly.cannot_edit_link_type', ['name' => $linkType->name])));
|
||||||
|
|
||||||
|
return redirect(route('admin.links.index'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$subTitle = trans('firefly.delete_link_type', ['name' => $linkType->name]);
|
||||||
|
$otherTypes = $repository->get();
|
||||||
|
$count = $repository->countJournals($linkType);
|
||||||
|
$moveTo = [];
|
||||||
|
$moveTo[0] = trans('firefly.do_not_save_connection');
|
||||||
|
/** @var LinkType $otherType */
|
||||||
|
foreach ($otherTypes as $otherType) {
|
||||||
|
if ($otherType->id !== $linkType->id) {
|
||||||
|
$moveTo[$otherType->id] = sprintf('%s (%s / %s)', $otherType->name, $otherType->inward, $otherType->outward);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// put previous url in session
|
||||||
|
$this->rememberPreviousUri('link_types.delete.uri');
|
||||||
|
|
||||||
|
return view('admin.link.delete', compact('linkType', 'subTitle', 'moveTo', 'count'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param LinkTypeRepositoryInterface $repository
|
||||||
|
* @param LinkType $linkType
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
|
*/
|
||||||
|
public function destroy(Request $request, LinkTypeRepositoryInterface $repository, LinkType $linkType)
|
||||||
|
{
|
||||||
|
$name = $linkType->name;
|
||||||
|
$moveTo = $repository->find(intval($request->get('move_link_type_before_delete')));
|
||||||
|
$repository->destroy($linkType, $moveTo);
|
||||||
|
|
||||||
|
$request->session()->flash('success', strval(trans('firefly.deleted_link_type', ['name' => $name])));
|
||||||
|
Preferences::mark();
|
||||||
|
|
||||||
|
return redirect($this->getPreviousUri('link_types.delete.uri'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param LinkType $linkType
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function edit(Request $request, LinkType $linkType)
|
||||||
|
{
|
||||||
|
if (!$linkType->editable) {
|
||||||
|
$request->session()->flash('error', strval(trans('firefly.cannot_edit_link_type', ['name' => $linkType->name])));
|
||||||
|
|
||||||
|
return redirect(route('admin.links.index'));
|
||||||
|
}
|
||||||
|
$subTitle = trans('firefly.edit_link_type', ['name' => $linkType->name]);
|
||||||
|
$subTitleIcon = 'fa-link';
|
||||||
|
|
||||||
|
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||||
|
if (session('link_types.edit.fromUpdate') !== true) {
|
||||||
|
$this->rememberPreviousUri('link_types.edit.uri');
|
||||||
|
}
|
||||||
|
$request->session()->forget('link_types.edit.fromUpdate');
|
||||||
|
|
||||||
|
return view('admin.link.edit', compact('subTitle', 'subTitleIcon', 'linkType'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkTypeRepositoryInterface $repository
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function index(LinkTypeRepositoryInterface $repository)
|
||||||
|
{
|
||||||
|
$subTitle = trans('firefly.journal_link_configuration');
|
||||||
|
$subTitleIcon = 'fa-link';
|
||||||
|
$linkTypes = $repository->get();
|
||||||
|
$linkTypes->each(
|
||||||
|
function (LinkType $linkType) use ($repository) {
|
||||||
|
$linkType->journalCount = $repository->countJournals($linkType);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return view('admin.link.index', compact('subTitle', 'subTitleIcon', 'linkTypes'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkType $linkType
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function show(LinkType $linkType)
|
||||||
|
{
|
||||||
|
$subTitle = trans('firefly.overview_for_link', ['name' => $linkType->name]);
|
||||||
|
$subTitleIcon = 'fa-link';
|
||||||
|
$links = $linkType->transactionJournalLinks()->get();
|
||||||
|
|
||||||
|
return view('admin.link.show', compact('subTitle', 'subTitleIcon', 'linkType', 'links'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkTypeFormRequest $request
|
||||||
|
* @param LinkTypeRepositoryInterface $repository
|
||||||
|
*
|
||||||
|
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
|
*/
|
||||||
|
public function store(LinkTypeFormRequest $request, LinkTypeRepositoryInterface $repository)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'name' => $request->string('name'),
|
||||||
|
'inward' => $request->string('inward'),
|
||||||
|
'outward' => $request->string('outward'),
|
||||||
|
];
|
||||||
|
$linkType = $repository->store($data);
|
||||||
|
$request->session()->flash('success', strval(trans('firefly.stored_new_link_type', ['name' => $linkType->name])));
|
||||||
|
|
||||||
|
if (intval($request->get('create_another')) === 1) {
|
||||||
|
// set value so create routine will not overwrite URL:
|
||||||
|
$request->session()->put('link_types.create.fromStore', true);
|
||||||
|
|
||||||
|
return redirect(route('link_types.create', [$request->input('what')]))->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirect to previous URL.
|
||||||
|
return redirect($this->getPreviousUri('link_types.create.uri'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(LinkTypeFormRequest $request, LinkTypeRepositoryInterface $repository, LinkType $linkType)
|
||||||
|
{
|
||||||
|
if (!$linkType->editable) {
|
||||||
|
$request->session()->flash('error', strval(trans('firefly.cannot_edit_link_type', ['name' => $linkType->name])));
|
||||||
|
|
||||||
|
return redirect(route('admin.links.index'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'name' => $request->string('name'),
|
||||||
|
'inward' => $request->string('inward'),
|
||||||
|
'outward' => $request->string('outward'),
|
||||||
|
];
|
||||||
|
$repository->update($linkType, $data);
|
||||||
|
|
||||||
|
$request->session()->flash('success', strval(trans('firefly.updated_link_type', ['name' => $linkType->name])));
|
||||||
|
Preferences::mark();
|
||||||
|
|
||||||
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
|
// set value so edit routine will not overwrite URL:
|
||||||
|
$request->session()->put('link_types.edit.fromUpdate', true);
|
||||||
|
|
||||||
|
return redirect(route('admin.links.edit', [$linkType->id]))->withInput(['return_to_edit' => 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirect to previous URL.
|
||||||
|
return redirect($this->getPreviousUri('link_types.edit.uri'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,6 +13,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
|
use Amount;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Http\Requests\BillFormRequest;
|
use FireflyIII\Http\Requests\BillFormRequest;
|
||||||
@ -129,6 +130,11 @@ class BillController extends Controller
|
|||||||
if (session('bills.edit.fromUpdate') !== true) {
|
if (session('bills.edit.fromUpdate') !== true) {
|
||||||
$this->rememberPreviousUri('bills.edit.uri');
|
$this->rememberPreviousUri('bills.edit.uri');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$currency = Amount::getDefaultCurrency();
|
||||||
|
$bill->amount_min = round($bill->amount_min, $currency->decimal_places);
|
||||||
|
$bill->amount_max = round($bill->amount_max, $currency->decimal_places);
|
||||||
|
|
||||||
$request->session()->forget('bills.edit.fromUpdate');
|
$request->session()->forget('bills.edit.fromUpdate');
|
||||||
$request->session()->flash('gaEventCategory', 'bills');
|
$request->session()->flash('gaEventCategory', 'bills');
|
||||||
$request->session()->flash('gaEventAction', 'edit');
|
$request->session()->flash('gaEventAction', 'edit');
|
||||||
|
@ -75,11 +75,9 @@ class BudgetController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function amount(Request $request, Budget $budget)
|
public function amount(Request $request, Budget $budget)
|
||||||
{
|
{
|
||||||
$amount = intval($request->get('amount'));
|
$amount = intval($request->get('amount'));
|
||||||
/** @var Carbon $start */
|
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
|
||||||
/** @var Carbon $end */
|
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
|
||||||
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
|
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
|
||||||
if ($amount === 0) {
|
if ($amount === 0) {
|
||||||
$budgetLimit = null;
|
$budgetLimit = null;
|
||||||
@ -243,7 +241,7 @@ class BudgetController extends Controller
|
|||||||
compact(
|
compact(
|
||||||
'available', 'currentMonth', 'next', 'nextText', 'prev', 'prevText',
|
'available', 'currentMonth', 'next', 'nextText', 'prev', 'prevText',
|
||||||
'periodStart', 'periodEnd', 'budgetInformation', 'inactive', 'budgets',
|
'periodStart', 'periodEnd', 'budgetInformation', 'inactive', 'budgets',
|
||||||
'spent', 'budgeted', 'previousLoop', 'nextLoop', 'start'
|
'spent', 'budgeted', 'previousLoop', 'nextLoop', 'start', 'end'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -313,15 +311,15 @@ class BudgetController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function postUpdateIncome(BudgetIncomeRequest $request)
|
public function postUpdateIncome(BudgetIncomeRequest $request)
|
||||||
{
|
{
|
||||||
$start = session('start', new Carbon);
|
$start = Carbon::createFromFormat('Y-m-d', $request->string('start'));
|
||||||
$end = session('end', new Carbon);
|
$end = Carbon::createFromFormat('Y-m-d', $request->string('end'));
|
||||||
$defaultCurrency = Amount::getDefaultCurrency();
|
$defaultCurrency = Amount::getDefaultCurrency();
|
||||||
$amount = $request->get('amount');
|
$amount = $request->get('amount');
|
||||||
|
|
||||||
$this->repository->setAvailableBudget($defaultCurrency, $start, $end, $amount);
|
$this->repository->setAvailableBudget($defaultCurrency, $start, $end, $amount);
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
return redirect(route('budgets.index'));
|
return redirect(route('budgets.index', [$start->format('Y-m-d')]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -443,15 +441,16 @@ class BudgetController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return View
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
*/
|
*/
|
||||||
public function updateIncome()
|
public function updateIncome(Carbon $start, Carbon $end)
|
||||||
{
|
{
|
||||||
$start = session('start', new Carbon);
|
|
||||||
$end = session('end', new Carbon);
|
|
||||||
$defaultCurrency = Amount::getDefaultCurrency();
|
$defaultCurrency = Amount::getDefaultCurrency();
|
||||||
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
|
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
|
||||||
|
$available = round($available, $defaultCurrency->decimal_places);
|
||||||
|
|
||||||
return view('budgets.income', compact('available', 'start', 'end'));
|
return view('budgets.income', compact('available', 'start', 'end'));
|
||||||
}
|
}
|
||||||
|
@ -137,13 +137,14 @@ class ExportController extends Controller
|
|||||||
public function postIndex(ExportFormRequest $request, AccountRepositoryInterface $repository, ExportJobRepositoryInterface $jobs)
|
public function postIndex(ExportFormRequest $request, AccountRepositoryInterface $repository, ExportJobRepositoryInterface $jobs)
|
||||||
{
|
{
|
||||||
$job = $jobs->findByKey($request->get('job'));
|
$job = $jobs->findByKey($request->get('job'));
|
||||||
|
$accounts = $request->get('accounts') ?? [];
|
||||||
$settings = [
|
$settings = [
|
||||||
'accounts' => $repository->getAccountsById($request->get('accounts')),
|
'accounts' => $repository->getAccountsById($accounts),
|
||||||
'startDate' => new Carbon($request->get('export_start_range')),
|
'startDate' => new Carbon($request->get('export_start_range')),
|
||||||
'endDate' => new Carbon($request->get('export_end_range')),
|
'endDate' => new Carbon($request->get('export_end_range')),
|
||||||
'exportFormat' => $request->get('exportFormat'),
|
'exportFormat' => $request->get('exportFormat'),
|
||||||
'includeAttachments' => intval($request->get('include_attachments')) === 1,
|
'includeAttachments' => $request->boolean('include_attachments'),
|
||||||
'includeOldUploads' => intval($request->get('include_old_uploads')) === 1,
|
'includeOldUploads' => $request->boolean('include_old_uploads'),
|
||||||
'job' => $job,
|
'job' => $job,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -159,12 +160,14 @@ class ExportController extends Controller
|
|||||||
$jobs->changeStatus($job, 'export_status_collecting_journals');
|
$jobs->changeStatus($job, 'export_status_collecting_journals');
|
||||||
$processor->collectJournals();
|
$processor->collectJournals();
|
||||||
$jobs->changeStatus($job, 'export_status_collected_journals');
|
$jobs->changeStatus($job, 'export_status_collected_journals');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Transform to exportable entries:
|
* Transform to exportable entries:
|
||||||
*/
|
*/
|
||||||
$jobs->changeStatus($job, 'export_status_converting_to_export_format');
|
$jobs->changeStatus($job, 'export_status_converting_to_export_format');
|
||||||
$processor->convertJournals();
|
$processor->convertJournals();
|
||||||
$jobs->changeStatus($job, 'export_status_converted_to_export_format');
|
$jobs->changeStatus($job, 'export_status_converted_to_export_format');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Transform to (temporary) file:
|
* Transform to (temporary) file:
|
||||||
*/
|
*/
|
||||||
@ -180,6 +183,7 @@ class ExportController extends Controller
|
|||||||
$jobs->changeStatus($job, 'export_status_collected_attachments');
|
$jobs->changeStatus($job, 'export_status_collected_attachments');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Collect old uploads
|
* Collect old uploads
|
||||||
*/
|
*/
|
||||||
|
@ -82,6 +82,14 @@ class HomeController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function displayError()
|
public function displayError()
|
||||||
{
|
{
|
||||||
|
Log::debug('This is a test message at the DEBUG level.');
|
||||||
|
Log::info('This is a test message at the INFO level.');
|
||||||
|
Log::notice('This is a test message at the NOTICE level.');
|
||||||
|
Log::warning('This is a test message at the WARNING level.');
|
||||||
|
Log::error('This is a test message at the ERROR level.');
|
||||||
|
Log::critical('This is a test message at the CRITICAL level.');
|
||||||
|
Log::alert('This is a test message at the ALERT level.');
|
||||||
|
Log::emergency('This is a test message at the EMERGENCY level.');
|
||||||
throw new FireflyException('A very simple test error.');
|
throw new FireflyException('A very simple test error.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,18 +13,126 @@ namespace FireflyIII\Http\Controllers\Import;
|
|||||||
|
|
||||||
|
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Support\Import\Information\InformationInterface;
|
||||||
use FireflyIII\Support\Import\Prerequisites\PrerequisitesInterface;
|
use FireflyIII\Support\Import\Prerequisites\PrerequisitesInterface;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Log;
|
||||||
|
use Session;
|
||||||
|
|
||||||
class BankController extends Controller
|
class BankController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
public function postPrerequisites()
|
/**
|
||||||
|
* This method must ask the user all parameters necessary to start importing data. This may not be enough
|
||||||
|
* to finish the import itself (ie. mapping) but it should be enough to begin: accounts to import from,
|
||||||
|
* accounts to import into, data ranges, etc.
|
||||||
|
*
|
||||||
|
* @param string $bank
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function form(string $bank)
|
||||||
{
|
{
|
||||||
|
$class = config(sprintf('firefly.import_pre.%s', $bank));
|
||||||
|
/** @var PrerequisitesInterface $object */
|
||||||
|
$object = app($class);
|
||||||
|
$object->setUser(auth()->user());
|
||||||
|
|
||||||
|
if ($object->hasPrerequisites()) {
|
||||||
|
return redirect(route('import.bank.prerequisites', [$bank]));
|
||||||
|
}
|
||||||
|
$class = config(sprintf('firefly.import_info.%s', $bank));
|
||||||
|
/** @var InformationInterface $object */
|
||||||
|
$object = app($class);
|
||||||
|
$object->setUser(auth()->user());
|
||||||
|
$remoteAccounts = $object->getAccounts();
|
||||||
|
|
||||||
|
return view('import.bank.form', compact('remoteAccounts', 'bank'));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* With the information given in the submitted form Firefly III will call upon the bank's classes to return transaction
|
||||||
|
* information as requested. The user will be able to map unknown data and continue. Or maybe, it's put into some kind of
|
||||||
|
* fake CSV file and forwarded to the import routine.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param string $bank
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|null
|
||||||
|
*/
|
||||||
|
public function postForm(Request $request, string $bank)
|
||||||
|
{
|
||||||
|
|
||||||
|
$class = config(sprintf('firefly.import_pre.%s', $bank));
|
||||||
|
/** @var PrerequisitesInterface $object */
|
||||||
|
$object = app($class);
|
||||||
|
$object->setUser(auth()->user());
|
||||||
|
|
||||||
|
if ($object->hasPrerequisites()) {
|
||||||
|
return redirect(route('import.bank.prerequisites', [$bank]));
|
||||||
|
}
|
||||||
|
$remoteAccounts = $request->get('do_import');
|
||||||
|
if (!is_array($remoteAccounts) || count($remoteAccounts) === 0) {
|
||||||
|
Session::flash('error', 'Must select accounts');
|
||||||
|
|
||||||
|
return redirect(route('import.bank.form', [$bank]));
|
||||||
|
}
|
||||||
|
$remoteAccounts = array_keys($remoteAccounts);
|
||||||
|
|
||||||
|
$class = config(sprintf('firefly.import_pre.%s', $bank));
|
||||||
|
// get import file
|
||||||
|
|
||||||
|
// get import config
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method processes the prerequisites the user has entered in the previous step.
|
||||||
|
*
|
||||||
|
* Whatever storePrerequisites does, it should make sure that the system is ready to continue immediately. So
|
||||||
|
* no extra calls or stuff, except maybe to open a session
|
||||||
|
*
|
||||||
|
* @see PrerequisitesInterface::storePrerequisites
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param string $bank
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
|
*/
|
||||||
|
public function postPrerequisites(Request $request, string $bank)
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('Now in postPrerequisites for %s', $bank));
|
||||||
|
$class = config(sprintf('firefly.import_pre.%s', $bank));
|
||||||
|
/** @var PrerequisitesInterface $object */
|
||||||
|
$object = app($class);
|
||||||
|
$object->setUser(auth()->user());
|
||||||
|
if (!$object->hasPrerequisites()) {
|
||||||
|
Log::debug(sprintf('No more prerequisites for %s, move to form.', $bank));
|
||||||
|
|
||||||
|
return redirect(route('import.bank.form', [$bank]));
|
||||||
|
}
|
||||||
|
Log::debug('Going to store entered preprerequisites.');
|
||||||
|
// store post data
|
||||||
|
$result = $object->storePrerequisites($request);
|
||||||
|
|
||||||
|
if ($result->count() > 0) {
|
||||||
|
Session::flash('error', $result->first());
|
||||||
|
|
||||||
|
return redirect(route('import.bank.prerequisites', [$bank]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect(route('import.bank.form', [$bank]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method shows you, if necessary, a form that allows you to enter any required values, such as API keys,
|
||||||
|
* login passwords or other values.
|
||||||
|
*
|
||||||
* @param string $bank
|
* @param string $bank
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
|
||||||
*/
|
*/
|
||||||
public function prerequisites(string $bank)
|
public function prerequisites(string $bank)
|
||||||
{
|
{
|
||||||
@ -40,10 +148,7 @@ class BankController extends Controller
|
|||||||
return view($view, $parameters);
|
return view($view, $parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$object->hasPrerequisites()) {
|
return redirect(route('import.bank.form', [$bank]));
|
||||||
echo 'redirect to import form.';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
171
app/Http/Controllers/Json/AutoCompleteController.php
Normal file
171
app/Http/Controllers/Json/AutoCompleteController.php
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* AutoCompleteController.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\Http\Controllers\Json;
|
||||||
|
|
||||||
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Support\CacheProperties;
|
||||||
|
use Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AutoCompleteController
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Http\Controllers\Json
|
||||||
|
*/
|
||||||
|
class AutoCompleteController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a JSON list of all accounts.
|
||||||
|
*
|
||||||
|
* @param AccountRepositoryInterface $repository
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function allAccounts(AccountRepositoryInterface $repository)
|
||||||
|
{
|
||||||
|
$return = array_unique(
|
||||||
|
$repository->getAccountsByType(
|
||||||
|
[AccountType::REVENUE, AccountType::EXPENSE, AccountType::BENEFICIARY, AccountType::DEFAULT, AccountType::ASSET]
|
||||||
|
)->pluck('name')->toArray()
|
||||||
|
);
|
||||||
|
sort($return);
|
||||||
|
|
||||||
|
return Response::json($return);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param JournalCollectorInterface $collector
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function allTransactionJournals(JournalCollectorInterface $collector)
|
||||||
|
{
|
||||||
|
$collector->setLimit(250)->setPage(1);
|
||||||
|
$return = array_unique($collector->getJournals()->pluck('description')->toArray());
|
||||||
|
sort($return);
|
||||||
|
|
||||||
|
return Response::json($return);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a JSON list of all beneficiaries.
|
||||||
|
*
|
||||||
|
* @param AccountRepositoryInterface $repository
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function expenseAccounts(AccountRepositoryInterface $repository)
|
||||||
|
{
|
||||||
|
$set = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
|
||||||
|
$filtered = $set->filter(
|
||||||
|
function (Account $account) {
|
||||||
|
if ($account->active) {
|
||||||
|
return $account;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$return = array_unique($filtered->pluck('name')->toArray());
|
||||||
|
|
||||||
|
sort($return);
|
||||||
|
|
||||||
|
return Response::json($return);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param JournalCollectorInterface $collector
|
||||||
|
*
|
||||||
|
* @param TransactionJournal $except
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse|mixed
|
||||||
|
*/
|
||||||
|
public function journalsWithId(JournalCollectorInterface $collector, TransactionJournal $except)
|
||||||
|
{
|
||||||
|
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty('recent-journals-id');
|
||||||
|
|
||||||
|
if ($cache->has()) {
|
||||||
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
$collector->setLimit(400)->setPage(1);
|
||||||
|
$set = $collector->getJournals()->pluck('description', 'journal_id')->toArray();
|
||||||
|
$return = [];
|
||||||
|
foreach ($set as $id => $description) {
|
||||||
|
$id = intval($id);
|
||||||
|
if ($id !== $except->id) {
|
||||||
|
$return[] = [
|
||||||
|
'id' => $id,
|
||||||
|
'name' => $id . ': ' . $description,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache->store($return);
|
||||||
|
|
||||||
|
return Response::json($return);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AccountRepositoryInterface $repository
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function revenueAccounts(AccountRepositoryInterface $repository)
|
||||||
|
{
|
||||||
|
$set = $repository->getAccountsByType([AccountType::REVENUE]);
|
||||||
|
$filtered = $set->filter(
|
||||||
|
function (Account $account) {
|
||||||
|
if ($account->active) {
|
||||||
|
return $account;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$return = array_unique($filtered->pluck('name')->toArray());
|
||||||
|
sort($return);
|
||||||
|
|
||||||
|
return Response::json($return);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param JournalCollectorInterface $collector
|
||||||
|
* @param string $what
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function transactionJournals(JournalCollectorInterface $collector, string $what)
|
||||||
|
{
|
||||||
|
$type = config('firefly.transactionTypesByWhat.' . $what);
|
||||||
|
$types = [$type];
|
||||||
|
|
||||||
|
$collector->setTypes($types)->setLimit(250)->setPage(1);
|
||||||
|
$return = array_unique($collector->getJournals()->pluck('description')->toArray());
|
||||||
|
sort($return);
|
||||||
|
|
||||||
|
return Response::json($return);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,9 +16,7 @@ namespace FireflyIII\Http\Controllers;
|
|||||||
use Amount;
|
use Amount;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Models\AccountType;
|
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||||
@ -62,43 +60,6 @@ class JsonController extends Controller
|
|||||||
return Response::json(['html' => $view]);
|
return Response::json(['html' => $view]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JSON list of all accounts.
|
|
||||||
*
|
|
||||||
* @param AccountRepositoryInterface $repository
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\JsonResponse
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function allAccounts(AccountRepositoryInterface $repository)
|
|
||||||
{
|
|
||||||
$return = array_unique(
|
|
||||||
$repository->getAccountsByType(
|
|
||||||
[AccountType::REVENUE, AccountType::EXPENSE, AccountType::BENEFICIARY, AccountType::DEFAULT, AccountType::ASSET]
|
|
||||||
)->pluck('name')->toArray()
|
|
||||||
);
|
|
||||||
sort($return);
|
|
||||||
|
|
||||||
return Response::json($return);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param JournalCollectorInterface $collector
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\JsonResponse
|
|
||||||
*/
|
|
||||||
public function allTransactionJournals(JournalCollectorInterface $collector)
|
|
||||||
{
|
|
||||||
$collector->setLimit(100)->setPage(1);
|
|
||||||
$return = array_unique($collector->getJournals()->pluck('description')->toArray());
|
|
||||||
sort($return);
|
|
||||||
|
|
||||||
return Response::json($return);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BillRepositoryInterface $repository
|
* @param BillRepositoryInterface $repository
|
||||||
*
|
*
|
||||||
@ -235,36 +196,6 @@ class JsonController extends Controller
|
|||||||
return Response::json($return);
|
return Response::json($return);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JSON list of all beneficiaries.
|
|
||||||
*
|
|
||||||
* @param AccountRepositoryInterface $repository
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\JsonResponse
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function expenseAccounts(AccountRepositoryInterface $repository)
|
|
||||||
{
|
|
||||||
$return = array_unique($repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY])->pluck('name')->toArray());
|
|
||||||
sort($return);
|
|
||||||
|
|
||||||
return Response::json($return);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param AccountRepositoryInterface $repository
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\JsonResponse
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function revenueAccounts(AccountRepositoryInterface $repository)
|
|
||||||
{
|
|
||||||
$return = array_unique($repository->getAccountsByType([AccountType::REVENUE])->pluck('name')->toArray());
|
|
||||||
sort($return);
|
|
||||||
|
|
||||||
return Response::json($return);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a JSON list of all beneficiaries.
|
* Returns a JSON list of all beneficiaries.
|
||||||
*
|
*
|
||||||
@ -281,26 +212,6 @@ class JsonController extends Controller
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param JournalCollectorInterface $collector
|
|
||||||
* @param string $what
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\JsonResponse
|
|
||||||
*/
|
|
||||||
public function transactionJournals(JournalCollectorInterface $collector, string $what)
|
|
||||||
{
|
|
||||||
$type = config('firefly.transactionTypesByWhat.' . $what);
|
|
||||||
$types = [$type];
|
|
||||||
|
|
||||||
$collector->setTypes($types)->setLimit(100)->setPage(1);
|
|
||||||
$return = array_unique($collector->getJournals()->pluck('description')->toArray());
|
|
||||||
sort($return);
|
|
||||||
|
|
||||||
return Response::json($return);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param JournalRepositoryInterface $repository
|
* @param JournalRepositoryInterface $repository
|
||||||
*
|
*
|
||||||
@ -329,6 +240,7 @@ class JsonController extends Controller
|
|||||||
$triggers[$key] = trans('firefly.rule_trigger_' . $key . '_choice');
|
$triggers[$key] = trans('firefly.rule_trigger_' . $key . '_choice');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
asort($triggers);
|
||||||
|
|
||||||
$view = view('rules.partials.trigger', compact('triggers', 'count'))->render();
|
$view = view('rules.partials.trigger', compact('triggers', 'count'))->render();
|
||||||
|
|
||||||
|
@ -166,10 +166,13 @@ class RuleController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function edit(Request $request, RuleRepositoryInterface $repository, Rule $rule)
|
public function edit(Request $request, RuleRepositoryInterface $repository, Rule $rule)
|
||||||
{
|
{
|
||||||
$oldTriggers = $this->getCurrentTriggers($rule);
|
/** @var RuleGroupRepositoryInterface $ruleGroupRepository */
|
||||||
$triggerCount = count($oldTriggers);
|
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||||
$oldActions = $this->getCurrentActions($rule);
|
$oldTriggers = $this->getCurrentTriggers($rule);
|
||||||
$actionCount = count($oldActions);
|
$triggerCount = count($oldTriggers);
|
||||||
|
$oldActions = $this->getCurrentActions($rule);
|
||||||
|
$actionCount = count($oldActions);
|
||||||
|
$ruleGroups = ExpandedForm::makeSelectList($ruleGroupRepository->get());
|
||||||
|
|
||||||
// has old input?
|
// has old input?
|
||||||
if ($request->old()) {
|
if ($request->old()) {
|
||||||
@ -191,7 +194,12 @@ class RuleController extends Controller
|
|||||||
Session::flash('gaEventCategory', 'rules');
|
Session::flash('gaEventCategory', 'rules');
|
||||||
Session::flash('gaEventAction', 'edit-rule');
|
Session::flash('gaEventAction', 'edit-rule');
|
||||||
|
|
||||||
return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount'));
|
return view(
|
||||||
|
'rules.rule.edit', compact(
|
||||||
|
'rule', 'subTitle',
|
||||||
|
'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount', 'ruleGroups'
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -523,7 +531,7 @@ class RuleController extends Controller
|
|||||||
$actions[] = view(
|
$actions[] = view(
|
||||||
'rules.partials.action',
|
'rules.partials.action',
|
||||||
[
|
[
|
||||||
'oldTrigger' => $entry->action_type,
|
'oldAction' => $entry->action_type,
|
||||||
'oldValue' => $entry->action_value,
|
'oldValue' => $entry->action_value,
|
||||||
'oldChecked' => $entry->stop_processing,
|
'oldChecked' => $entry->stop_processing,
|
||||||
'count' => $count,
|
'count' => $count,
|
||||||
|
@ -43,9 +43,6 @@ use View;
|
|||||||
class TagController extends Controller
|
class TagController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
/** @var array */
|
|
||||||
public $tagOptions = [];
|
|
||||||
|
|
||||||
/** @var TagRepositoryInterface */
|
/** @var TagRepositoryInterface */
|
||||||
protected $repository;
|
protected $repository;
|
||||||
|
|
||||||
@ -60,17 +57,8 @@ class TagController extends Controller
|
|||||||
$this->middleware(
|
$this->middleware(
|
||||||
function ($request, $next) {
|
function ($request, $next) {
|
||||||
$this->repository = app(TagRepositoryInterface::class);
|
$this->repository = app(TagRepositoryInterface::class);
|
||||||
$this->tagOptions = [
|
|
||||||
'nothing' => trans('firefly.regular_tag'),
|
|
||||||
'balancingAct' => trans('firefly.balancing_act'),
|
|
||||||
'advancePayment' => trans('firefly.advance_payment'),
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
View::share('title', strval(trans('firefly.tags')));
|
View::share('title', strval(trans('firefly.tags')));
|
||||||
View::share('mainTitleIcon', 'fa-tags');
|
View::share('mainTitleIcon', 'fa-tags');
|
||||||
View::share('tagOptions', $this->tagOptions);
|
|
||||||
|
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
@ -168,41 +156,22 @@ class TagController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(TagRepositoryInterface $repository)
|
public function index(TagRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
$title = 'Tags';
|
|
||||||
$mainTitleIcon = 'fa-tags';
|
|
||||||
$types = ['nothing', 'balancingAct', 'advancePayment'];
|
|
||||||
$hasTypes = 0; // which types of tag the user actually has.
|
|
||||||
$counts = []; // how many of each type?
|
|
||||||
$count = $repository->count();
|
|
||||||
|
|
||||||
// loop each types and get the tags, group them by year.
|
// collect tags by year:
|
||||||
$collection = [];
|
/** @var Carbon $start */
|
||||||
foreach ($types as $type) {
|
$start = clone(session('first'));
|
||||||
|
$now = new Carbon;
|
||||||
|
$clouds = [];
|
||||||
|
$clouds['no-date'] = $repository->tagCloud(null);
|
||||||
|
while ($now > $start) {
|
||||||
|
$year = $now->year;
|
||||||
|
$clouds[$year] = $repository->tagCloud($year);
|
||||||
|
|
||||||
/** @var Collection $tags */
|
$now->subYear();
|
||||||
$tags = $repository->getByType($type);
|
|
||||||
$tags = $tags->sortBy(
|
|
||||||
function (Tag $tag) {
|
|
||||||
$date = !is_null($tag->date) ? $tag->date->format('Ymd') : '000000';
|
|
||||||
|
|
||||||
return strtolower($date . $tag->tag);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if ($tags->count() > 0) {
|
|
||||||
$hasTypes++;
|
|
||||||
}
|
|
||||||
$counts[$type] = $tags->count();
|
|
||||||
|
|
||||||
/** @var Tag $tag */
|
|
||||||
foreach ($tags as $tag) {
|
|
||||||
$year = is_null($tag->date) ? trans('firefly.no_year') : $tag->date->year;
|
|
||||||
$monthFormatted = is_null($tag->date) ? trans('firefly.no_month') : $tag->date->formatLocalized($this->monthFormat);
|
|
||||||
|
|
||||||
$collection[$type][$year][$monthFormatted][] = $tag;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$count = $repository->count();
|
||||||
|
|
||||||
return view('tags.index', compact('title', 'mainTitleIcon', 'counts', 'hasTypes', 'types', 'collection', 'count'));
|
return view('tags.index', compact('clouds', 'count'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -235,6 +204,7 @@ class TagController extends Controller
|
|||||||
$start = $repository->firstUseDate($tag);
|
$start = $repository->firstUseDate($tag);
|
||||||
$end = new Carbon;
|
$end = new Carbon;
|
||||||
$sum = $repository->sumOfTag($tag, null, null);
|
$sum = $repository->sumOfTag($tag, null, null);
|
||||||
|
$result = $repository->resultOfTag($tag, null, null);
|
||||||
$path = route('tags.show', [$tag->id, 'all']);
|
$path = route('tags.show', [$tag->id, 'all']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,6 +219,7 @@ class TagController extends Controller
|
|||||||
);
|
);
|
||||||
$periods = $this->getPeriodOverview($tag);
|
$periods = $this->getPeriodOverview($tag);
|
||||||
$sum = $repository->sumOfTag($tag, $start, $end);
|
$sum = $repository->sumOfTag($tag, $start, $end);
|
||||||
|
$result = $repository->resultOfTag($tag, $start, $end);
|
||||||
$path = route('tags.show', [$tag->id, $moment]);
|
$path = route('tags.show', [$tag->id, $moment]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,6 +228,8 @@ class TagController extends Controller
|
|||||||
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
|
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||||
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
|
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||||
$periods = $this->getPeriodOverview($tag);
|
$periods = $this->getPeriodOverview($tag);
|
||||||
|
$sum = $repository->sumOfTag($tag, $start, $end);
|
||||||
|
$result = $repository->resultOfTag($tag, $start, $end);
|
||||||
$subTitle = trans(
|
$subTitle = trans(
|
||||||
'firefly.journals_in_period_for_tag',
|
'firefly.journals_in_period_for_tag',
|
||||||
['tag' => $tag->tag, 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
['tag' => $tag->tag, 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
@ -271,10 +244,9 @@ class TagController extends Controller
|
|||||||
$journals->setPath($path);
|
$journals->setPath($path);
|
||||||
|
|
||||||
|
|
||||||
return view('tags.show', compact('apiKey', 'tag', 'periods', 'subTitle', 'subTitleIcon', 'journals', 'sum', 'start', 'end', 'moment'));
|
return view('tags.show', compact('apiKey', 'tag', 'result', 'periods', 'subTitle', 'subTitleIcon', 'journals', 'sum', 'start', 'end', 'moment'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TagFormRequest $request
|
* @param TagFormRequest $request
|
||||||
*
|
*
|
||||||
@ -371,4 +343,6 @@ class TagController extends Controller
|
|||||||
return $collection;
|
return $collection;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
143
app/Http/Controllers/Transaction/LinkController.php
Normal file
143
app/Http/Controllers/Transaction/LinkController.php
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* LinkController.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\Http\Controllers\Transaction;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Http\Requests\JournalLinkRequest;
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use FireflyIII\Models\TransactionJournalLink;
|
||||||
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
|
||||||
|
use Log;
|
||||||
|
use Preferences;
|
||||||
|
use Session;
|
||||||
|
use URL;
|
||||||
|
use View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LinkController
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Http\Controllers\Transaction
|
||||||
|
*/
|
||||||
|
class LinkController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
// some useful repositories:
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
View::share('title', trans('firefly.transactions'));
|
||||||
|
View::share('mainTitleIcon', 'fa-repeat');
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournalLink $link
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function delete(TransactionJournalLink $link)
|
||||||
|
{
|
||||||
|
$subTitleIcon = 'fa-link';
|
||||||
|
$subTitle = trans('breadcrumbs.delete_journal_link');
|
||||||
|
$this->rememberPreviousUri('journal_links.delete.uri');
|
||||||
|
|
||||||
|
return view('transactions.links.delete', compact('link', 'subTitle', 'subTitleIcon'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkTypeRepositoryInterface $repository
|
||||||
|
* @param TransactionJournalLink $link
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
|
*/
|
||||||
|
public function destroy(LinkTypeRepositoryInterface $repository, TransactionJournalLink $link)
|
||||||
|
{
|
||||||
|
$repository->destroyLink($link);
|
||||||
|
|
||||||
|
Session::flash('success', strval(trans('firefly.deleted_link')));
|
||||||
|
Preferences::mark();
|
||||||
|
|
||||||
|
return redirect(strval(session('journal_links.delete.uri')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param JournalLinkRequest $request
|
||||||
|
* @param LinkTypeRepositoryInterface $repository
|
||||||
|
* @param JournalRepositoryInterface $journalRepository
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
|
*/
|
||||||
|
public function store(
|
||||||
|
JournalLinkRequest $request, LinkTypeRepositoryInterface $repository, JournalRepositoryInterface $journalRepository, TransactionJournal $journal
|
||||||
|
) {
|
||||||
|
$linkInfo = $request->getLinkInfo();
|
||||||
|
$linkType = $repository->find($linkInfo['link_type_id']);
|
||||||
|
$other = $journalRepository->find($linkInfo['transaction_journal_id']);
|
||||||
|
$alreadyLinked = $repository->findLink($journal, $other);
|
||||||
|
if ($alreadyLinked) {
|
||||||
|
Session::flash('error', trans('firefly.journals_error_linked'));
|
||||||
|
|
||||||
|
return redirect(route('transactions.show', [$journal->id]));
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('Journal is %d, opposing is %d', $journal->id, $other->id));
|
||||||
|
|
||||||
|
$journalLink = new TransactionJournalLink;
|
||||||
|
$journalLink->linkType()->associate($linkType);
|
||||||
|
if ($linkInfo['direction'] === 'inward') {
|
||||||
|
Log::debug(sprintf('Link type is inwards ("%s"), so %d is source and %d is destination.', $linkType->inward, $other->id, $journal->id));
|
||||||
|
$journalLink->source()->associate($other);
|
||||||
|
$journalLink->destination()->associate($journal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($linkInfo['direction'] === 'outward') {
|
||||||
|
Log::debug(sprintf('Link type is inwards ("%s"), so %d is source and %d is destination.', $linkType->outward, $journal->id, $other->id));
|
||||||
|
$journalLink->source()->associate($journal);
|
||||||
|
$journalLink->destination()->associate($other);
|
||||||
|
}
|
||||||
|
|
||||||
|
$journalLink->comment = $linkInfo['comments'];
|
||||||
|
$journalLink->save();
|
||||||
|
Session::flash('success', trans('firefly.journals_linked'));
|
||||||
|
|
||||||
|
return redirect(route('transactions.show', [$journal->id]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkTypeRepositoryInterface $repository
|
||||||
|
* @param TransactionJournalLink $link
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
|
*/
|
||||||
|
public function switch(LinkTypeRepositoryInterface $repository, TransactionJournalLink $link)
|
||||||
|
{
|
||||||
|
|
||||||
|
$repository->switchLink($link);
|
||||||
|
|
||||||
|
return redirect(URL::previous());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,12 +14,14 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Http\Controllers\Transaction;
|
namespace FireflyIII\Http\Controllers\Transaction;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use ExpandedForm;
|
use ExpandedForm;
|
||||||
use FireflyIII\Events\StoredTransactionJournal;
|
use FireflyIII\Events\StoredTransactionJournal;
|
||||||
use FireflyIII\Events\UpdatedTransactionJournal;
|
use FireflyIII\Events\UpdatedTransactionJournal;
|
||||||
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Http\Requests\JournalFormRequest;
|
use FireflyIII\Http\Requests\JournalFormRequest;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
@ -115,7 +117,7 @@ class SingleController extends Controller
|
|||||||
'foreign_amount' => $foreignAmount,
|
'foreign_amount' => $foreignAmount,
|
||||||
'native_amount' => $foreignAmount,
|
'native_amount' => $foreignAmount,
|
||||||
'amount_currency_id_amount' => $transaction->foreign_currency_id ?? 0,
|
'amount_currency_id_amount' => $transaction->foreign_currency_id ?? 0,
|
||||||
'date' => $journal->date->format('Y-m-d'),
|
'date' => (new Carbon())->format('Y-m-d'),
|
||||||
'budget_id' => $budgetId,
|
'budget_id' => $budgetId,
|
||||||
'category' => $categoryName,
|
'category' => $categoryName,
|
||||||
'tags' => $tags,
|
'tags' => $tags,
|
||||||
@ -142,7 +144,7 @@ class SingleController extends Controller
|
|||||||
{
|
{
|
||||||
$what = strtolower($what);
|
$what = strtolower($what);
|
||||||
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
|
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
|
||||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
|
$assetAccounts = $this->groupedActiveAccountList();
|
||||||
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
||||||
$piggyBanks = $this->piggyBanks->getPiggyBanksWithAmount();
|
$piggyBanks = $this->piggyBanks->getPiggyBanksWithAmount();
|
||||||
$piggies = ExpandedForm::makeSelectListWithEmpty($piggyBanks);
|
$piggies = ExpandedForm::makeSelectListWithEmpty($piggyBanks);
|
||||||
@ -238,7 +240,7 @@ class SingleController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$what = strtolower($journal->transactionTypeStr());
|
$what = strtolower($journal->transactionTypeStr());
|
||||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
|
$assetAccounts = $this->groupedAccountList();
|
||||||
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getBudgets());
|
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getBudgets());
|
||||||
|
|
||||||
// view related code
|
// view related code
|
||||||
@ -408,6 +410,46 @@ class SingleController extends Controller
|
|||||||
return redirect($this->getPreviousUri('transactions.edit.uri'));
|
return redirect($this->getPreviousUri('transactions.edit.uri'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function groupedAccountList(): array
|
||||||
|
{
|
||||||
|
$accounts = $this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||||
|
$return = [];
|
||||||
|
/** @var Account $account */
|
||||||
|
foreach ($accounts as $account) {
|
||||||
|
$type = $account->getMeta('accountRole');
|
||||||
|
if (strlen($type) === 0) {
|
||||||
|
$type = 'no_account_type';
|
||||||
|
}
|
||||||
|
$key = strval(trans('firefly.opt_group_' . $type));
|
||||||
|
$return[$key][$account->id] = $account->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function groupedActiveAccountList(): array
|
||||||
|
{
|
||||||
|
$accounts = $this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||||
|
$return = [];
|
||||||
|
/** @var Account $account */
|
||||||
|
foreach ($accounts as $account) {
|
||||||
|
$type = $account->getMeta('accountRole');
|
||||||
|
if (strlen($type) === 0) {
|
||||||
|
$type = 'no_account_type';
|
||||||
|
}
|
||||||
|
$key = strval(trans('firefly.opt_group_' . $type));
|
||||||
|
$return[$key][$account->id] = $account->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TransactionJournal $journal
|
* @param TransactionJournal $journal
|
||||||
*
|
*
|
||||||
|
@ -18,6 +18,8 @@ use ExpandedForm;
|
|||||||
use FireflyIII\Events\UpdatedTransactionJournal;
|
use FireflyIII\Events\UpdatedTransactionJournal;
|
||||||
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Http\Requests\SplitJournalFormRequest;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
@ -94,13 +96,23 @@ class SplitController extends Controller
|
|||||||
|
|
||||||
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
|
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
|
||||||
$currencies = $this->currencies->get();
|
$currencies = $this->currencies->get();
|
||||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
|
$accountList = $this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||||
|
$assetAccounts = ExpandedForm::makeSelectList($accountList);
|
||||||
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
|
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
|
||||||
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
||||||
$preFilled = $this->arrayFromJournal($request, $journal);
|
$preFilled = $this->arrayFromJournal($request, $journal);
|
||||||
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
|
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
|
||||||
$subTitleIcon = 'fa-pencil';
|
$subTitleIcon = 'fa-pencil';
|
||||||
|
|
||||||
|
$accountArray = [];
|
||||||
|
// account array to display currency info:
|
||||||
|
/** @var Account $account */
|
||||||
|
foreach ($accountList as $account) {
|
||||||
|
$accountArray[$account->id] = $account;
|
||||||
|
$accountArray[$account->id]['currency_id'] = intval($account->getMeta('currency_id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Session::flash('gaEventCategory', 'transactions');
|
Session::flash('gaEventCategory', 'transactions');
|
||||||
Session::flash('gaEventAction', 'edit-split-' . $preFilled['what']);
|
Session::flash('gaEventAction', 'edit-split-' . $preFilled['what']);
|
||||||
|
|
||||||
@ -115,25 +127,24 @@ class SplitController extends Controller
|
|||||||
compact(
|
compact(
|
||||||
'subTitleIcon', 'currencies', 'optionalFields',
|
'subTitleIcon', 'currencies', 'optionalFields',
|
||||||
'preFilled', 'subTitle', 'uploadSize', 'assetAccounts',
|
'preFilled', 'subTitle', 'uploadSize', 'assetAccounts',
|
||||||
'budgets', 'journal'
|
'budgets', 'journal', 'accountArray', 'previous'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param SplitJournalFormRequest $request
|
||||||
* @param JournalRepositoryInterface $repository
|
* @param JournalRepositoryInterface $repository
|
||||||
* @param TransactionJournal $journal
|
* @param TransactionJournal $journal
|
||||||
*
|
*
|
||||||
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
*/
|
*/
|
||||||
public function update(Request $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
|
public function update(SplitJournalFormRequest $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
|
||||||
{
|
{
|
||||||
if ($this->isOpeningBalance($journal)) {
|
if ($this->isOpeningBalance($journal)) {
|
||||||
return $this->redirectToAccount($journal);
|
return $this->redirectToAccount($journal);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->arrayFromInput($request);
|
$data = $this->arrayFromInput($request);
|
||||||
$journal = $repository->updateSplitJournal($journal, $data);
|
$journal = $repository->updateSplitJournal($journal, $data);
|
||||||
/** @var array $files */
|
/** @var array $files */
|
||||||
@ -167,11 +178,11 @@ class SplitController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param SplitJournalFormRequest $request
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function arrayFromInput(Request $request): array
|
private function arrayFromInput(SplitJournalFormRequest $request): array
|
||||||
{
|
{
|
||||||
$array = [
|
$array = [
|
||||||
'journal_description' => $request->get('journal_description'),
|
'journal_description' => $request->get('journal_description'),
|
||||||
@ -200,8 +211,8 @@ class SplitController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param SplitJournalFormRequest|Request $request
|
||||||
* @param TransactionJournal $journal
|
* @param TransactionJournal $journal
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
@ -234,6 +245,8 @@ class SplitController extends Controller
|
|||||||
// transactions.
|
// transactions.
|
||||||
'transactions' => $this->getTransactionDataFromJournal($journal),
|
'transactions' => $this->getTransactionDataFromJournal($journal),
|
||||||
];
|
];
|
||||||
|
// update transactions array with old request data.
|
||||||
|
$array['transactions'] = $this->updateWithPrevious($array['transactions'], $request->old());
|
||||||
|
|
||||||
return $array;
|
return $array;
|
||||||
}
|
}
|
||||||
@ -282,11 +295,11 @@ class SplitController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param SplitJournalFormRequest|Request $request
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function getTransactionDataFromRequest(Request $request): array
|
private function getTransactionDataFromRequest(SplitJournalFormRequest $request): array
|
||||||
{
|
{
|
||||||
$return = [];
|
$return = [];
|
||||||
$transactions = $request->get('transactions');
|
$transactions = $request->get('transactions');
|
||||||
@ -312,5 +325,36 @@ class SplitController extends Controller
|
|||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $array
|
||||||
|
* @param $old
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function updateWithPrevious($array, $old): array
|
||||||
|
{
|
||||||
|
if (count($old) === 0 || !isset($old['transactions'])) {
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
$old = $old['transactions'];
|
||||||
|
foreach ($old as $index => $row) {
|
||||||
|
if (isset($array[$index])) {
|
||||||
|
$array[$index] = array_merge($array[$index], $row);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// take some info from first transaction, that should at least exist.
|
||||||
|
$array[$index] = $row;
|
||||||
|
$array[$index]['transaction_currency_id'] = $array[0]['transaction_currency_id'];
|
||||||
|
$array[$index]['transaction_currency_code'] = $array[0]['transaction_currency_code'];
|
||||||
|
$array[$index]['transaction_currency_symbol'] = $array[0]['transaction_currency_symbol'];
|
||||||
|
$array[$index]['foreign_amount'] = round($array[0]['foreign_destination_amount'] ?? '0', 12);
|
||||||
|
$array[$index]['foreign_currency_id'] = $array[0]['foreign_currency_id'];
|
||||||
|
$array[$index]['foreign_currency_code'] = $array[0]['foreign_currency_code'];
|
||||||
|
$array[$index]['foreign_currency_symbol'] = $array[0]['foreign_currency_symbol'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,11 @@ use Carbon\Carbon;
|
|||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||||
|
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -27,7 +29,6 @@ use Log;
|
|||||||
use Navigation;
|
use Navigation;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Response;
|
use Response;
|
||||||
use Steam;
|
|
||||||
use View;
|
use View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,23 +151,26 @@ class TransactionController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TransactionJournal $journal
|
* @param TransactionJournal $journal
|
||||||
* @param JournalTaskerInterface $tasker
|
* @param JournalTaskerInterface $tasker
|
||||||
|
*
|
||||||
|
* @param LinkTypeRepositoryInterface $linkTypeRepository
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||||
*/
|
*/
|
||||||
public function show(TransactionJournal $journal, JournalTaskerInterface $tasker)
|
public function show(TransactionJournal $journal, JournalTaskerInterface $tasker, LinkTypeRepositoryInterface $linkTypeRepository)
|
||||||
{
|
{
|
||||||
if ($this->isOpeningBalance($journal)) {
|
if ($this->isOpeningBalance($journal)) {
|
||||||
return $this->redirectToAccount($journal);
|
return $this->redirectToAccount($journal);
|
||||||
}
|
}
|
||||||
|
$linkTypes = $linkTypeRepository->get();
|
||||||
|
$links = $linkTypeRepository->getLinks($journal);
|
||||||
$events = $tasker->getPiggyBankEvents($journal);
|
$events = $tasker->getPiggyBankEvents($journal);
|
||||||
$transactions = $tasker->getTransactionsOverview($journal);
|
$transactions = $tasker->getTransactionsOverview($journal);
|
||||||
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
||||||
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
|
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
|
||||||
|
|
||||||
return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions'));
|
return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions', 'linkTypes', 'links'));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -210,35 +214,24 @@ class TransactionController extends Controller
|
|||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withOpposingAccount()->setTypes($types);
|
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withOpposingAccount()->setTypes($types);
|
||||||
$collector->removeFilter(InternalTransferFilter::class);
|
$collector->removeFilter(InternalTransferFilter::class);
|
||||||
$set = $collector->getJournals();
|
$journals = $collector->getJournals();
|
||||||
$sum = $set->sum('transaction_amount');
|
$sum = $journals->sum('transaction_amount');
|
||||||
$journals = $set->count();
|
|
||||||
|
// count per currency:
|
||||||
|
$sums = $this->sumPerCurrency($journals);
|
||||||
$dateStr = $end->format('Y-m-d');
|
$dateStr = $end->format('Y-m-d');
|
||||||
$dateName = Navigation::periodShow($end, $range);
|
$dateName = Navigation::periodShow($end, $range);
|
||||||
$array = [
|
$array = [
|
||||||
'string' => $dateStr,
|
'string' => $dateStr,
|
||||||
'name' => $dateName,
|
'name' => $dateName,
|
||||||
'count' => $journals,
|
'sum' => $sum,
|
||||||
'spent' => 0,
|
'sums' => $sums,
|
||||||
'earned' => 0,
|
'date' => clone $end,
|
||||||
'transferred' => 0,
|
|
||||||
'date' => clone $end,
|
|
||||||
];
|
];
|
||||||
Log::debug(sprintf('What is %s', $what));
|
Log::debug(sprintf('What is %s', $what));
|
||||||
switch ($what) {
|
if ($journals->count() > 0) {
|
||||||
case 'withdrawal':
|
$entries->push($array);
|
||||||
$array['spent'] = $sum;
|
|
||||||
break;
|
|
||||||
case 'deposit':
|
|
||||||
$array['earned'] = $sum;
|
|
||||||
break;
|
|
||||||
case 'transfers':
|
|
||||||
case 'transfer':
|
|
||||||
$array['transferred'] = Steam::positive($sum);
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
$entries->push($array);
|
|
||||||
$end = Navigation::subtractPeriod($end, $range, 1);
|
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||||
}
|
}
|
||||||
Log::debug('End of loop');
|
Log::debug('End of loop');
|
||||||
@ -247,4 +240,41 @@ class TransactionController extends Controller
|
|||||||
return $entries;
|
return $entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $collection
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function sumPerCurrency(Collection $collection): array
|
||||||
|
{
|
||||||
|
$return = [];
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($collection as $transaction) {
|
||||||
|
$currencyId = $transaction->transaction_currency_id;
|
||||||
|
|
||||||
|
// save currency information:
|
||||||
|
if (!isset($return[$currencyId])) {
|
||||||
|
$currencySymbol = $transaction->transaction_currency_symbol;
|
||||||
|
$decimalPlaces = $transaction->transaction_currency_dp;
|
||||||
|
$currencyCode = $transaction->transaction_currency_code;
|
||||||
|
$return[$currencyId] = [
|
||||||
|
'currency' => [
|
||||||
|
'id' => $currencyId,
|
||||||
|
'code' => $currencyCode,
|
||||||
|
'symbol' => $currencySymbol,
|
||||||
|
'dp' => $decimalPlaces,
|
||||||
|
],
|
||||||
|
'sum' => '0',
|
||||||
|
'count' => 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// save amount:
|
||||||
|
$return[$currencyId]['sum'] = bcadd($return[$currencyId]['sum'], $transaction->transaction_amount);
|
||||||
|
$return[$currencyId]['count']++;
|
||||||
|
}
|
||||||
|
asort($return);
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,8 @@ class BudgetIncomeRequest extends Request
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'amount' => 'numeric|required|min:0',
|
'amount' => 'numeric|required|min:0',
|
||||||
|
'start' => 'required|date|before:end',
|
||||||
|
'end' => 'required|date|after:start',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,9 @@ class ExportFormRequest extends Request
|
|||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
$sessionFirst = clone session('first');
|
$sessionFirst = clone session('first');
|
||||||
|
$first = $sessionFirst->subDay()->format('Y-m-d');
|
||||||
$first = $sessionFirst->subDay()->format('Y-m-d');
|
$today = Carbon::create()->addDay()->format('Y-m-d');
|
||||||
$today = Carbon::create()->addDay()->format('Y-m-d');
|
$formats = join(',', array_keys(config('firefly.export_formats')));
|
||||||
$formats = join(',', array_keys(config('firefly.export_formats')));
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'export_start_range' => 'required|date|after:' . $first,
|
'export_start_range' => 'required|date|after:' . $first,
|
||||||
|
@ -110,7 +110,7 @@ class JournalFormRequest extends Request
|
|||||||
// foreign currency amounts
|
// foreign currency amounts
|
||||||
'native_amount' => 'numeric|more:0',
|
'native_amount' => 'numeric|more:0',
|
||||||
'source_amount' => 'numeric|more:0',
|
'source_amount' => 'numeric|more:0',
|
||||||
'destination_amount' => 'numeric|more:0',
|
'destination_amount' => 'numeric',
|
||||||
];
|
];
|
||||||
|
|
||||||
// some rules get an upgrade depending on the type of data:
|
// some rules get an upgrade depending on the type of data:
|
||||||
|
74
app/Http/Requests/JournalLinkRequest.php
Normal file
74
app/Http/Requests/JournalLinkRequest.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* JournalLinkRequest.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\Http\Requests;
|
||||||
|
|
||||||
|
use FireflyIII\Models\LinkType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class JournalLink
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Http\Requests
|
||||||
|
*/
|
||||||
|
class JournalLinkRequest extends Request
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
// Only allow logged in users
|
||||||
|
return auth()->check();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getLinkInfo(): array
|
||||||
|
{
|
||||||
|
$return = [];
|
||||||
|
$linkType = $this->get('link_type');
|
||||||
|
$parts = explode('_', $linkType);
|
||||||
|
$return['link_type_id'] = intval($parts[0]);
|
||||||
|
$return['transaction_journal_id'] = $this->integer('link_journal_id');
|
||||||
|
$return['comments'] = strlen($this->string('comments')) > 0 ? $this->string('comments') : null;
|
||||||
|
$return['direction'] = $parts[1];
|
||||||
|
if ($return['transaction_journal_id'] === 0 && ctype_digit($this->string('link_other'))) {
|
||||||
|
$return['transaction_journal_id'] = $this->integer('link_other');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
// all possible combinations of link types and inward / outward:
|
||||||
|
$combinations = [];
|
||||||
|
$linkTypes = LinkType::get(['id']);
|
||||||
|
/** @var LinkType $type */
|
||||||
|
foreach ($linkTypes as $type) {
|
||||||
|
$combinations[] = sprintf('%d_inward', $type->id);
|
||||||
|
$combinations[] = sprintf('%d_outward', $type->id);
|
||||||
|
}
|
||||||
|
$string = join(',', $combinations);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'link_type' => sprintf('required|in:%s', $string),
|
||||||
|
'link_other' => 'belongsToUser:transaction_journals',
|
||||||
|
'link_journal_id' => 'belongsToUser:transaction_journals',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
57
app/Http/Requests/LinkTypeFormRequest.php
Normal file
57
app/Http/Requests/LinkTypeFormRequest.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* LinkTypeFormRequest.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\Http\Requests;
|
||||||
|
|
||||||
|
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BillFormRequest
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Http\Requests
|
||||||
|
*/
|
||||||
|
class LinkTypeFormRequest extends Request
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
// Only allow logged and admins
|
||||||
|
return auth()->check() && auth()->user()->hasRole('owner');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
/** @var LinkTypeRepositoryInterface $repository */
|
||||||
|
$repository = app(LinkTypeRepositoryInterface::class);
|
||||||
|
$nameRule = 'required|min:1|unique:link_types,name';
|
||||||
|
$idRule = '';
|
||||||
|
if (!is_null($repository->find($this->integer('id'))->id)) {
|
||||||
|
$idRule = 'exists:link_types,id';
|
||||||
|
$nameRule = 'required|min:1';
|
||||||
|
}
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'id' => $idRule,
|
||||||
|
'name' => $nameRule,
|
||||||
|
'inward' => 'required|min:1|different:outward',
|
||||||
|
'outward' => 'required|min:1|different:inward',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $rules;
|
||||||
|
}
|
||||||
|
}
|
@ -28,64 +28,17 @@ class Request extends FormRequest
|
|||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function boolean(string $field): bool
|
public function boolean(string $field): bool
|
||||||
{
|
{
|
||||||
return intval($this->input($field)) === 1;
|
return intval($this->input($field)) === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $field
|
|
||||||
*
|
|
||||||
* @return Carbon|null
|
|
||||||
*/
|
|
||||||
protected function date(string $field)
|
|
||||||
{
|
|
||||||
return $this->get($field) ? new Carbon($this->get($field)) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $field
|
|
||||||
*
|
|
||||||
* @return float
|
|
||||||
*/
|
|
||||||
protected function float(string $field): float
|
|
||||||
{
|
|
||||||
return round($this->input($field), 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $field
|
|
||||||
* @param string $type
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function getArray(string $field, string $type): array
|
|
||||||
{
|
|
||||||
$original = $this->get($field);
|
|
||||||
$return = [];
|
|
||||||
foreach ($original as $index => $value) {
|
|
||||||
$return[$index] = $this->$type($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $field
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
protected function integer(string $field): int
|
|
||||||
{
|
|
||||||
return intval($this->get($field));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $field
|
* @param string $field
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function string(string $field): string
|
public function string(string $field): string
|
||||||
{
|
{
|
||||||
$string = $this->get($field) ?? '';
|
$string = $this->get($field) ?? '';
|
||||||
$search = [
|
$search = [
|
||||||
@ -140,4 +93,51 @@ class Request extends FormRequest
|
|||||||
|
|
||||||
return trim($string);
|
return trim($string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $field
|
||||||
|
*
|
||||||
|
* @return Carbon|null
|
||||||
|
*/
|
||||||
|
protected function date(string $field)
|
||||||
|
{
|
||||||
|
return $this->get($field) ? new Carbon($this->get($field)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $field
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
protected function float(string $field): float
|
||||||
|
{
|
||||||
|
return round($this->input($field), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $field
|
||||||
|
* @param string $type
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getArray(string $field, string $type): array
|
||||||
|
{
|
||||||
|
$original = $this->get($field);
|
||||||
|
$return = [];
|
||||||
|
foreach ($original as $index => $value) {
|
||||||
|
$return[$index] = $this->$type($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $field
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
protected function integer(string $field): int
|
||||||
|
{
|
||||||
|
return intval($this->get($field));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Http\Requests;
|
namespace FireflyIII\Http\Requests;
|
||||||
|
|
||||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class RuleFormRequest
|
* Class RuleFormRequest
|
||||||
@ -39,6 +39,7 @@ class RuleFormRequest extends Request
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'title' => $this->string('title'),
|
'title' => $this->string('title'),
|
||||||
|
'rule_group_id' => $this->integer('rule_group_id'),
|
||||||
'active' => $this->boolean('active'),
|
'active' => $this->boolean('active'),
|
||||||
'trigger' => $this->string('trigger'),
|
'trigger' => $this->string('trigger'),
|
||||||
'description' => $this->string('description'),
|
'description' => $this->string('description'),
|
||||||
@ -57,19 +58,18 @@ class RuleFormRequest extends Request
|
|||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
/** @var RuleGroupRepositoryInterface $repository */
|
/** @var RuleRepositoryInterface $repository */
|
||||||
$repository = app(RuleGroupRepositoryInterface::class);
|
$repository = app(RuleRepositoryInterface::class);
|
||||||
$validTriggers = array_keys(config('firefly.rule-triggers'));
|
$validTriggers = array_keys(config('firefly.rule-triggers'));
|
||||||
$validActions = array_keys(config('firefly.rule-actions'));
|
$validActions = array_keys(config('firefly.rule-actions'));
|
||||||
|
|
||||||
// some actions require text:
|
// some actions require text:
|
||||||
$contextActions = join(',', config('firefly.rule-actions-text'));
|
$contextActions = join(',', config('firefly.rule-actions-text'));
|
||||||
|
|
||||||
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title';
|
$titleRule = 'required|between:1,100|uniqueObjectForUser:rules,title';
|
||||||
if (!is_null($repository->find(intval($this->get('id')))->id)) {
|
if (!is_null($repository->find(intval($this->get('id')))->id)) {
|
||||||
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval($this->get('id'));
|
$titleRule = 'required|between:1,100|uniqueObjectForUser:rules,title,' . intval($this->get('id'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$rules = [
|
$rules = [
|
||||||
'title' => $titleRule,
|
'title' => $titleRule,
|
||||||
'description' => 'between:1,5000',
|
'description' => 'between:1,5000',
|
||||||
|
@ -61,23 +61,23 @@ class SplitJournalFormRequest extends Request
|
|||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'what' => 'required|in:withdrawal,deposit,transfer',
|
'what' => 'required|in:withdrawal,deposit,transfer',
|
||||||
'journal_description' => 'required|between:1,255',
|
'journal_description' => 'required|between:1,255',
|
||||||
'id' => 'numeric|belongsToUser:transaction_journals,id',
|
'id' => 'numeric|belongsToUser:transaction_journals,id',
|
||||||
'journal_source_account_id' => 'numeric|belongsToUser:accounts,id',
|
'journal_source_account_id' => 'numeric|belongsToUser:accounts,id',
|
||||||
'journal_source_account_name.*' => 'between:1,255',
|
'journal_source_account_name.*' => 'between:1,255',
|
||||||
'journal_currency_id' => 'required|exists:transaction_currencies,id',
|
'journal_currency_id' => 'required|exists:transaction_currencies,id',
|
||||||
'date' => 'required|date',
|
'date' => 'required|date',
|
||||||
'interest_date' => 'date',
|
'interest_date' => 'date',
|
||||||
'book_date' => 'date',
|
'book_date' => 'date',
|
||||||
'process_date' => 'date',
|
'process_date' => 'date',
|
||||||
'description.*' => 'required|between:1,255',
|
'transactions.*.description' => 'required|between:1,255',
|
||||||
'destination_account_id.*' => 'numeric|belongsToUser:accounts,id',
|
'transactions.*.destination_account_id' => 'numeric|belongsToUser:accounts,id',
|
||||||
'destination_account_name.*' => 'between:1,255',
|
'transactions.*.destination_account_name' => 'between:1,255',
|
||||||
'amount.*' => 'required|numeric',
|
'transactions.*.amount' => 'required|numeric',
|
||||||
'budget_id.*' => 'belongsToUser:budgets,id',
|
'transactions.*.budget_id' => 'belongsToUser:budgets,id',
|
||||||
'category.*' => 'between:1,255',
|
'transactions.*.category' => 'between:1,255',
|
||||||
'piggy_bank_id.*' => 'between:1,255',
|
'transactions.*.piggy_bank_id' => 'between:1,255',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,14 +37,13 @@ class TagFormRequest extends Request
|
|||||||
*/
|
*/
|
||||||
public function collectTagData(): array
|
public function collectTagData(): array
|
||||||
{
|
{
|
||||||
|
$latitude = null;
|
||||||
|
$longitude = null;
|
||||||
|
$zoomLevel = null;
|
||||||
if ($this->get('setTag') === 'true') {
|
if ($this->get('setTag') === 'true') {
|
||||||
$latitude = $this->string('latitude');
|
$latitude = $this->string('latitude');
|
||||||
$longitude = $this->string('longitude');
|
$longitude = $this->string('longitude');
|
||||||
$zoomLevel = $this->integer('zoomLevel');
|
$zoomLevel = $this->integer('zoomLevel');
|
||||||
} else {
|
|
||||||
$latitude = null;
|
|
||||||
$longitude = null;
|
|
||||||
$zoomLevel = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
|
@ -21,12 +21,14 @@ use FireflyIII\Models\Budget;
|
|||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\Category;
|
use FireflyIII\Models\Category;
|
||||||
use FireflyIII\Models\ImportJob;
|
use FireflyIII\Models\ImportJob;
|
||||||
|
use FireflyIII\Models\LinkType;
|
||||||
use FireflyIII\Models\PiggyBank;
|
use FireflyIII\Models\PiggyBank;
|
||||||
use FireflyIII\Models\Rule;
|
use FireflyIII\Models\Rule;
|
||||||
use FireflyIII\Models\RuleGroup;
|
use FireflyIII\Models\RuleGroup;
|
||||||
use FireflyIII\Models\Tag;
|
use FireflyIII\Models\Tag;
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use FireflyIII\Models\TransactionJournalLink;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -152,6 +154,49 @@ Breadcrumbs::register(
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Breadcrumbs::register(
|
||||||
|
'admin.links.index', function (BreadCrumbGenerator $breadcrumbs) {
|
||||||
|
$breadcrumbs->parent('admin.index');
|
||||||
|
$breadcrumbs->push(trans('firefly.journal_link_configuration'), route('admin.links.index'));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Breadcrumbs::register(
|
||||||
|
'admin.links.create', function (BreadCrumbGenerator $breadcrumbs) {
|
||||||
|
$breadcrumbs->parent('admin.links.index');
|
||||||
|
$breadcrumbs->push(trans('firefly.create_new_link_type'), route('admin.links.create'));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Breadcrumbs::register(
|
||||||
|
'admin.links.show', function (BreadCrumbGenerator $breadcrumbs, LinkType $linkType) {
|
||||||
|
$breadcrumbs->parent('admin.links.index');
|
||||||
|
$breadcrumbs->push(trans('firefly.overview_for_link', [$linkType->name]), route('admin.links.show', [$linkType->id]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Breadcrumbs::register(
|
||||||
|
'admin.links.edit', function (BreadCrumbGenerator $breadcrumbs, LinkType $linkType) {
|
||||||
|
$breadcrumbs->parent('admin.links.index');
|
||||||
|
$breadcrumbs->push(trans('firefly.edit_link_type', ['name' => $linkType->name]), route('admin.links.edit', [$linkType->id]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Breadcrumbs::register(
|
||||||
|
'admin.links.delete', function (BreadCrumbGenerator $breadcrumbs, LinkType $linkType) {
|
||||||
|
$breadcrumbs->parent('admin.links.index');
|
||||||
|
$breadcrumbs->push(trans('firefly.delete_link_type', ['name' => $linkType->name]), route('admin.links.delete', [$linkType->id]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Breadcrumbs::register(
|
||||||
|
'transactions.link.delete', function (BreadCrumbGenerator $breadcrumbs, TransactionJournalLink $link) {
|
||||||
|
$breadcrumbs->parent('home');
|
||||||
|
$breadcrumbs->push(trans('breadcrumbs.delete_journal_link'), route('transactions.link.delete', $link->id));
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ATTACHMENTS
|
* ATTACHMENTS
|
||||||
*/
|
*/
|
||||||
|
@ -13,6 +13,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Import\Converter;
|
namespace FireflyIII\Import\Converter;
|
||||||
|
|
||||||
|
use Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class RabobankDebetCredit
|
* Class RabobankDebetCredit
|
||||||
*
|
*
|
||||||
@ -34,31 +36,40 @@ class Amount implements ConverterInterface
|
|||||||
*/
|
*/
|
||||||
public function convert($value): string
|
public function convert($value): string
|
||||||
{
|
{
|
||||||
|
Log::debug(sprintf('Start with amount "%s"', $value));
|
||||||
$len = strlen($value);
|
$len = strlen($value);
|
||||||
$decimalPosition = $len - 3;
|
$decimalPosition = $len - 3;
|
||||||
$decimal = null;
|
$decimal = null;
|
||||||
|
|
||||||
if (($len > 2 && $value{$decimalPosition} === '.') || ($len > 2 && strpos($value, '.') > $decimalPosition)) {
|
if (($len > 2 && $value{$decimalPosition} === '.') || ($len > 2 && strpos($value, '.') > $decimalPosition)) {
|
||||||
$decimal = '.';
|
$decimal = '.';
|
||||||
|
Log::debug(sprintf('Decimal character in "%s" seems to be a dot.', $value));
|
||||||
}
|
}
|
||||||
if ($len > 2 && $value{$decimalPosition} === ',') {
|
if ($len > 2 && $value{$decimalPosition} === ',') {
|
||||||
$decimal = ',';
|
$decimal = ',';
|
||||||
|
Log::debug(sprintf('Decimal character in "%s" seems to be a comma.', $value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// if decimal is dot, replace all comma's and spaces with nothing. then parse as float (round to 4 pos)
|
// if decimal is dot, replace all comma's and spaces with nothing. then parse as float (round to 4 pos)
|
||||||
if ($decimal === '.') {
|
if ($decimal === '.') {
|
||||||
$search = [',', ' '];
|
$search = [',', ' '];
|
||||||
$value = str_replace($search, '', $value);
|
$oldValue = $value;
|
||||||
|
$value = str_replace($search, '', $value);
|
||||||
|
Log::debug(sprintf('Converted amount from "%s" to "%s".', $oldValue, $value));
|
||||||
}
|
}
|
||||||
if ($decimal === ',') {
|
if ($decimal === ',') {
|
||||||
$search = ['.', ' '];
|
$search = ['.', ' '];
|
||||||
$value = str_replace($search, '', $value);
|
$oldValue = $value;
|
||||||
$value = str_replace(',', '.', $value);
|
$value = str_replace($search, '', $value);
|
||||||
|
$value = str_replace(',', '.', $value);
|
||||||
|
Log::debug(sprintf('Converted amount from "%s" to "%s".', $oldValue, $value));
|
||||||
}
|
}
|
||||||
if (is_null($decimal)) {
|
if (is_null($decimal)) {
|
||||||
// replace all:
|
// replace all:
|
||||||
$search = ['.', ' ', ','];
|
$search = ['.', ' ', ','];
|
||||||
$value = str_replace($search, '', $value);
|
$oldValue = $value;
|
||||||
|
$value = str_replace($search, '', $value);
|
||||||
|
Log::debug(sprintf('No decimal character found. Converted amount from "%s" to "%s".', $oldValue, $value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return strval(round(floatval($value), 12));
|
return strval(round(floatval($value), 12));
|
||||||
|
@ -151,10 +151,14 @@ class CsvProcessor implements FileProcessorInterface
|
|||||||
*/
|
*/
|
||||||
private function getImportArray(): Iterator
|
private function getImportArray(): Iterator
|
||||||
{
|
{
|
||||||
$content = $this->job->uploadFileContents();
|
$content = $this->job->uploadFileContents();
|
||||||
$config = $this->job->configuration;
|
$config = $this->job->configuration;
|
||||||
$reader = Reader::createFromString($content);
|
$reader = Reader::createFromString($content);
|
||||||
$reader->setDelimiter($config['delimiter']);
|
$delimiter = $config['delimiter'];
|
||||||
|
if ($delimiter === 'tab') {
|
||||||
|
$delimiter = "\t";
|
||||||
|
}
|
||||||
|
$reader->setDelimiter($delimiter);
|
||||||
$start = $config['has-headers'] ? 1 : 0;
|
$start = $config['has-headers'] ? 1 : 0;
|
||||||
$results = $reader->setOffset($start)->fetch();
|
$results = $reader->setOffset($start)->fetch();
|
||||||
Log::debug(sprintf('Created a CSV reader starting at offset %d', $start));
|
Log::debug(sprintf('Created a CSV reader starting at offset %d', $start));
|
||||||
|
@ -37,6 +37,8 @@ class ImportJournal
|
|||||||
public $budget;
|
public $budget;
|
||||||
/** @var ImportCategory */
|
/** @var ImportCategory */
|
||||||
public $category;
|
public $category;
|
||||||
|
/** @var ImportCurrency */
|
||||||
|
public $currency;
|
||||||
/** @var string */
|
/** @var string */
|
||||||
public $description = '';
|
public $description = '';
|
||||||
/** @var string */
|
/** @var string */
|
||||||
@ -51,8 +53,8 @@ class ImportJournal
|
|||||||
public $tags = [];
|
public $tags = [];
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $amount;
|
private $amount;
|
||||||
/** @var ImportCurrency */
|
/** @var string */
|
||||||
public $currency;
|
private $convertedAmount = null;
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $date = '';
|
private $date = '';
|
||||||
/** @var string */
|
/** @var string */
|
||||||
@ -85,25 +87,37 @@ class ImportJournal
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
public function getAmount(): string
|
public function getAmount(): string
|
||||||
{
|
{
|
||||||
if (is_null($this->amount)) {
|
Log::debug('Now in getAmount()');
|
||||||
|
if (is_null($this->convertedAmount)) {
|
||||||
|
Log::debug('convertedAmount is NULL');
|
||||||
/** @var ConverterInterface $amountConverter */
|
/** @var ConverterInterface $amountConverter */
|
||||||
$amountConverter = app(Amount::class);
|
$amountConverter = app(Amount::class);
|
||||||
$this->amount = $amountConverter->convert($this->amount);
|
$this->convertedAmount = $amountConverter->convert($this->amount);
|
||||||
|
Log::debug(sprintf('First attempt to convert gives "%s"', $this->convertedAmount));
|
||||||
// modify
|
// modify
|
||||||
foreach ($this->modifiers as $modifier) {
|
foreach ($this->modifiers as $modifier) {
|
||||||
$class = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $modifier['role'])));
|
$class = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $modifier['role'])));
|
||||||
/** @var ConverterInterface $converter */
|
/** @var ConverterInterface $converter */
|
||||||
$converter = app($class);
|
$converter = app($class);
|
||||||
|
Log::debug(sprintf('Now launching converter %s', $class));
|
||||||
if ($converter->convert($modifier['value']) === -1) {
|
if ($converter->convert($modifier['value']) === -1) {
|
||||||
$this->amount = Steam::negative($this->amount);
|
$this->convertedAmount = Steam::negative($this->convertedAmount);
|
||||||
}
|
}
|
||||||
|
Log::debug(sprintf('convertedAmount after conversion is %s', $this->convertedAmount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('After modifiers the result is: "%s"', $this->convertedAmount));
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('convertedAmount is: "%s"', $this->convertedAmount));
|
||||||
|
if (bccomp($this->convertedAmount, '0') === 0) {
|
||||||
|
throw new FireflyException('Amount is zero.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->amount;
|
return $this->convertedAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,7 +59,8 @@ class RabobankDescription implements SpecificInterface
|
|||||||
);
|
);
|
||||||
$row[6] = $alternateName;
|
$row[6] = $alternateName;
|
||||||
$row[10] = '';
|
$row[10] = '';
|
||||||
} else {
|
}
|
||||||
|
if (!(strlen($oppositeAccount) < 1 && strlen($oppositeName) < 1)) {
|
||||||
Log::debug('Rabobank specific: either opposite account or name are filled.');
|
Log::debug('Rabobank specific: either opposite account or name are filled.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Import\Storage;
|
namespace FireflyIII\Import\Storage;
|
||||||
|
|
||||||
|
use ErrorException;
|
||||||
|
use Exception;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Import\Object\ImportJournal;
|
use FireflyIII\Import\Object\ImportJournal;
|
||||||
use FireflyIII\Models\ImportJob;
|
use FireflyIII\Models\ImportJob;
|
||||||
@ -95,7 +97,7 @@ class ImportStorage
|
|||||||
function (ImportJournal $importJournal, int $index) {
|
function (ImportJournal $importJournal, int $index) {
|
||||||
try {
|
try {
|
||||||
$this->storeImportJournal($index, $importJournal);
|
$this->storeImportJournal($index, $importJournal);
|
||||||
} catch (FireflyException $e) {
|
} catch (FireflyException | ErrorException | Exception $e) {
|
||||||
$this->errors->push($e->getMessage());
|
$this->errors->push($e->getMessage());
|
||||||
Log::error(sprintf('Cannot import row #%d because: %s', $index, $e->getMessage()));
|
Log::error(sprintf('Cannot import row #%d because: %s', $index, $e->getMessage()));
|
||||||
}
|
}
|
||||||
@ -142,7 +144,10 @@ class ImportStorage
|
|||||||
if ($this->isDoubleTransfer($parameters) || $this->hashAlreadyImported($importJournal->hash)) {
|
if ($this->isDoubleTransfer($parameters) || $this->hashAlreadyImported($importJournal->hash)) {
|
||||||
$this->job->addStepsDone(3);
|
$this->job->addStepsDone(3);
|
||||||
// throw error
|
// throw error
|
||||||
throw new FireflyException('Detected a possible duplicate, skip this one.');
|
$message = sprintf('Detected a possible duplicate, skip this one (hash: %s).', $importJournal->hash);
|
||||||
|
Log::error($message, $parameters);
|
||||||
|
throw new FireflyException($message);
|
||||||
|
|
||||||
}
|
}
|
||||||
unset($parameters);
|
unset($parameters);
|
||||||
|
|
||||||
@ -202,25 +207,32 @@ class ImportStorage
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$amount = app('steam')->positive($parameters['amount']);
|
$amount = app('steam')->positive($parameters['amount']);
|
||||||
$names = [$parameters['asset'], $parameters['opposing']];
|
$names = [$parameters['asset'], $parameters['opposing']];
|
||||||
|
$transfer = [];
|
||||||
|
$hit = false;
|
||||||
sort($names);
|
sort($names);
|
||||||
|
|
||||||
foreach ($this->transfers as $transfer) {
|
foreach ($this->transfers as $transfer) {
|
||||||
if ($parameters['description'] !== $transfer['description']) {
|
if ($parameters['description'] === $transfer['description']) {
|
||||||
return false;
|
$hit = true;
|
||||||
}
|
}
|
||||||
if ($names !== $transfer['names']) {
|
if ($names === $transfer['names']) {
|
||||||
return false;
|
$hit = true;
|
||||||
}
|
}
|
||||||
if (bccomp($amount, $transfer['amount']) !== 0) {
|
if (bccomp($amount, $transfer['amount']) === 0) {
|
||||||
return false;
|
$hit = true;
|
||||||
}
|
}
|
||||||
if ($parameters['date'] !== $transfer['date']) {
|
if ($parameters['date'] === $transfer['date']) {
|
||||||
return false;
|
$hit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($hit === true) {
|
||||||
|
Log::error(
|
||||||
|
'There already is a transfer imported with these properties. Compare existing with new. ', ['existing' => $transfer, 'new' => $parameters]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return $hit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ trait ImportSupport
|
|||||||
*/
|
*/
|
||||||
private function getTransactionType(string $amount, Account $account): string
|
private function getTransactionType(string $amount, Account $account): string
|
||||||
{
|
{
|
||||||
$transactionType = '';
|
$transactionType = TransactionType::WITHDRAWAL;
|
||||||
// amount is negative, it's a withdrawal, opposing is an expense:
|
// amount is negative, it's a withdrawal, opposing is an expense:
|
||||||
if (bccomp($amount, '0') === -1) {
|
if (bccomp($amount, '0') === -1) {
|
||||||
$transactionType = TransactionType::WITHDRAWAL;
|
$transactionType = TransactionType::WITHDRAWAL;
|
||||||
@ -296,12 +296,15 @@ trait ImportSupport
|
|||||||
*/
|
*/
|
||||||
private function hashAlreadyImported(string $hash): bool
|
private function hashAlreadyImported(string $hash): bool
|
||||||
{
|
{
|
||||||
$json = json_encode($hash);
|
$json = json_encode($hash);
|
||||||
|
/** @var TransactionJournalMeta $entry */
|
||||||
$entry = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
|
$entry = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
|
||||||
->where('data', $json)
|
->where('data', $json)
|
||||||
->where('name', 'importHash')
|
->where('name', 'importHash')
|
||||||
->first();
|
->first();
|
||||||
if (!is_null($entry)) {
|
if (!is_null($entry)) {
|
||||||
|
Log::error(sprintf('A journal with hash %s has already been imported (spoiler: it\'s journal #%d)', $hash, $entry->transaction_journal_id));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,4 +444,4 @@ trait ImportSupport
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegisteredUser.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);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RequestedNewPassword.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);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,6 +17,7 @@ use Crypt;
|
|||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
use Watson\Validating\ValidatingTrait;
|
use Watson\Validating\ValidatingTrait;
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ use Watson\Validating\ValidatingTrait;
|
|||||||
class Bill extends Model
|
class Bill extends Model
|
||||||
{
|
{
|
||||||
|
|
||||||
use ValidatingTrait;
|
use SoftDeletes, ValidatingTrait;
|
||||||
/**
|
/**
|
||||||
* The attributes that should be casted to native types.
|
* The attributes that should be casted to native types.
|
||||||
*
|
*
|
||||||
|
@ -13,12 +13,15 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Models;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ExportJob
|
* Class ExportJob
|
||||||
*
|
*
|
||||||
|
* @property User $user
|
||||||
|
*
|
||||||
* @package FireflyIII\Models
|
* @package FireflyIII\Models
|
||||||
*/
|
*/
|
||||||
class ExportJob extends Model
|
class ExportJob extends Model
|
||||||
|
62
app/Models/LinkType.php
Normal file
62
app/Models/LinkType.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* LinkType.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\Models;
|
||||||
|
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $journalCount
|
||||||
|
* Class LinkType
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Models
|
||||||
|
*/
|
||||||
|
class LinkType extends Model
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The attributes that should be casted to native types.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $casts
|
||||||
|
= [
|
||||||
|
'created_at' => 'date',
|
||||||
|
'updated_at' => 'date',
|
||||||
|
'deleted_at' => 'date',
|
||||||
|
'editable' => 'boolean',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
* @throws NotFoundHttpException
|
||||||
|
*/
|
||||||
|
public static function routeBinder($value)
|
||||||
|
{
|
||||||
|
if (auth()->check()) {
|
||||||
|
$model = self::where('id', $value)->first();
|
||||||
|
if (!is_null($model)) {
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NotFoundHttpException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transactionJournalLinks()
|
||||||
|
{
|
||||||
|
return $this->hasMany(TransactionJournalLink::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -84,6 +84,7 @@ class PiggyBank extends Model
|
|||||||
return $this->currentRep;
|
return $this->currentRep;
|
||||||
}
|
}
|
||||||
// repeating piggy banks are no longer supported.
|
// repeating piggy banks are no longer supported.
|
||||||
|
/** @var PiggyBankRepetition $rep */
|
||||||
$rep = $this->piggyBankRepetitions()->first(['piggy_bank_repetitions.*']);
|
$rep = $this->piggyBankRepetitions()->first(['piggy_bank_repetitions.*']);
|
||||||
if (is_null($rep)) {
|
if (is_null($rep)) {
|
||||||
return new PiggyBankRepetition();
|
return new PiggyBankRepetition();
|
||||||
|
@ -14,6 +14,7 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Models;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use Crypt;
|
use Crypt;
|
||||||
|
use Exception;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use Illuminate\Contracts\Encryption\DecryptException;
|
use Illuminate\Contracts\Encryption\DecryptException;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@ -56,7 +57,15 @@ class Preference extends Model
|
|||||||
sprintf('Could not decrypt preference #%d. If this error persists, please run "php artisan cache:clear" on the command line.', $this->id)
|
sprintf('Could not decrypt preference #%d. If this error persists, please run "php artisan cache:clear" on the command line.', $this->id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
$unserialized = false;
|
||||||
|
try {
|
||||||
|
$unserialized = unserialize($data);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// don't care, assume is false.
|
||||||
|
}
|
||||||
|
if (!($unserialized === false)) {
|
||||||
|
return $unserialized;
|
||||||
|
}
|
||||||
|
|
||||||
return json_decode($data, true);
|
return json_decode($data, true);
|
||||||
}
|
}
|
||||||
@ -66,7 +75,7 @@ class Preference extends Model
|
|||||||
*/
|
*/
|
||||||
public function setDataAttribute($value)
|
public function setDataAttribute($value)
|
||||||
{
|
{
|
||||||
$this->attributes['data'] = Crypt::encrypt(json_encode($value));
|
$this->attributes['data'] = Crypt::encrypt(serialize($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,6 +22,48 @@ use Watson\Validating\ValidatingTrait;
|
|||||||
/**
|
/**
|
||||||
* Class Transaction
|
* Class Transaction
|
||||||
*
|
*
|
||||||
|
* @property-read int $journal_id
|
||||||
|
* @property-read Carbon $date
|
||||||
|
* @property-read string $transaction_description
|
||||||
|
* @property-read string $transaction_amount
|
||||||
|
* @property-read string $transaction_foreign_amount
|
||||||
|
* @property-read string $transaction_type_type
|
||||||
|
*
|
||||||
|
* @property-read int $account_id
|
||||||
|
* @property-read string $account_name
|
||||||
|
* @property string $account_iban
|
||||||
|
* @property string $account_number
|
||||||
|
* @property string $account_bic
|
||||||
|
* @property string $account_currency_code
|
||||||
|
*
|
||||||
|
* @property-read int $opposing_account_id
|
||||||
|
* @property string $opposing_account_name
|
||||||
|
* @property string $opposing_account_iban
|
||||||
|
* @property string $opposing_account_number
|
||||||
|
* @property string $opposing_account_bic
|
||||||
|
* @property string $opposing_currency_code
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @property-read int $transaction_budget_id
|
||||||
|
* @property-read string $transaction_budget_name
|
||||||
|
* @property-read int $transaction_journal_budget_id
|
||||||
|
* @property-read string $transaction_journal_budget_name
|
||||||
|
*
|
||||||
|
* @property-read int $transaction_category_id
|
||||||
|
* @property-read string $transaction_category_name
|
||||||
|
* @property-read int $transaction_journal_category_id
|
||||||
|
* @property-read string $transaction_journal_category_name
|
||||||
|
*
|
||||||
|
* @property-read int $bill_id
|
||||||
|
* @property string $bill_name
|
||||||
|
*
|
||||||
|
* @property string $notes
|
||||||
|
* @property string $tags
|
||||||
|
*
|
||||||
|
* @property string $transaction_currency_symbol
|
||||||
|
* @property int $transaction_currency_dp
|
||||||
|
* @property string $transaction_currency_code
|
||||||
|
*
|
||||||
* @package FireflyIII\Models
|
* @package FireflyIII\Models
|
||||||
*/
|
*/
|
||||||
class Transaction extends Model
|
class Transaction extends Model
|
||||||
|
@ -140,6 +140,14 @@ class TransactionJournal extends Model
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return HasMany
|
||||||
|
*/
|
||||||
|
public function destinationJournalLinks(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(TransactionJournalLink::class, 'destination_id');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param $value
|
* @param $value
|
||||||
@ -372,6 +380,14 @@ class TransactionJournal extends Model
|
|||||||
return $entry;
|
return $entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return HasMany
|
||||||
|
*/
|
||||||
|
public function sourceJournalLinks(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(TransactionJournalLink::class, 'source_id');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||||
*/
|
*/
|
||||||
|
105
app/Models/TransactionJournalLink.php
Normal file
105
app/Models/TransactionJournalLink.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* TransactionJournalLink.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\Models;
|
||||||
|
|
||||||
|
|
||||||
|
use Crypt;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TransactionJournalLink
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Models
|
||||||
|
*/
|
||||||
|
class TransactionJournalLink extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'journal_links';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
* @throws NotFoundHttpException
|
||||||
|
*/
|
||||||
|
public static function routeBinder($value)
|
||||||
|
{
|
||||||
|
if (auth()->check()) {
|
||||||
|
$model = self::where('journal_links.id', $value)
|
||||||
|
->leftJoin('transaction_journals as t_a', 't_a.id', '=', 'source_id')
|
||||||
|
->leftJoin('transaction_journals as t_b', 't_b.id', '=', 'destination_id')
|
||||||
|
->where('t_a.user_id', auth()->user()->id)
|
||||||
|
->where('t_b.user_id', auth()->user()->id)
|
||||||
|
->first(['journal_links.*']);
|
||||||
|
if (!is_null($model)) {
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NotFoundHttpException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function destination()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(TransactionJournal::class, 'destination_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $value
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
public function getCommentAttribute($value): ?string
|
||||||
|
{
|
||||||
|
if (!is_null($value)) {
|
||||||
|
return Crypt::decrypt($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function linkType(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(LinkType::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param $value
|
||||||
|
*/
|
||||||
|
public function setCommentAttribute($value): void
|
||||||
|
{
|
||||||
|
if (!is_null($value) && strlen($value) > 0) {
|
||||||
|
$this->attributes['comment'] = Crypt::encrypt($value);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->attributes['comment'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function source()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(TransactionJournal::class, 'source_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
63
app/Providers/AdminServiceProvider.php
Normal file
63
app/Providers/AdminServiceProvider.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* AdminServiceProvider.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\Providers;
|
||||||
|
|
||||||
|
use FireflyIII\Repositories\LinkType\LinkTypeRepository;
|
||||||
|
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
|
||||||
|
use Illuminate\Foundation\Application;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
|
||||||
|
class AdminServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Bootstrap the application services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the application services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
$this->linkType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function linkType()
|
||||||
|
{
|
||||||
|
$this->app->bind(
|
||||||
|
LinkTypeRepositoryInterface::class,
|
||||||
|
function (Application $app) {
|
||||||
|
/** @var LinkTypeRepository $repository */
|
||||||
|
$repository = app(LinkTypeRepository::class);
|
||||||
|
if ($app->auth->check()) {
|
||||||
|
$repository->setUser(auth()->user());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $repository;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,7 +13,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Providers;
|
namespace FireflyIII\Providers;
|
||||||
|
|
||||||
use FireflyIII\Export\Processor;
|
use FireflyIII\Export\ExpandedProcessor;
|
||||||
use FireflyIII\Export\ProcessorInterface;
|
use FireflyIII\Export\ProcessorInterface;
|
||||||
use FireflyIII\Generator\Chart\Basic\ChartJsGenerator;
|
use FireflyIII\Generator\Chart\Basic\ChartJsGenerator;
|
||||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||||
@ -138,7 +138,8 @@ class FireflyServiceProvider extends ServiceProvider
|
|||||||
);
|
);
|
||||||
|
|
||||||
// other generators
|
// other generators
|
||||||
$this->app->bind(ProcessorInterface::class, Processor::class);
|
// export:
|
||||||
|
$this->app->bind(ProcessorInterface::class, ExpandedProcessor::class);
|
||||||
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
|
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
|
||||||
$this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class);
|
$this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class);
|
||||||
|
|
||||||
|
@ -205,10 +205,12 @@ trait FindAccountsTrait
|
|||||||
*/
|
*/
|
||||||
public function getCashAccount(): Account
|
public function getCashAccount(): Account
|
||||||
{
|
{
|
||||||
$type = AccountType::where('type', AccountType::CASH)->first();
|
$type = AccountType::where('type', AccountType::CASH)->first();
|
||||||
$account = Account::firstOrCreateEncrypted(
|
$account = Account::firstOrCreateEncrypted(
|
||||||
['user_id' => $this->user->id, 'account_type_id' => $type->id, 'name' => 'Cash account', 'active' => 1]
|
['user_id' => $this->user->id, 'account_type_id' => $type->id, 'name' => 'Cash account']
|
||||||
);
|
);
|
||||||
|
$account->active = true;
|
||||||
|
$account->save();
|
||||||
|
|
||||||
return $account;
|
return $account;
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ class BillRepository implements BillRepositoryInterface
|
|||||||
$set = $set->sortBy(
|
$set = $set->sortBy(
|
||||||
function (Bill $bill) {
|
function (Bill $bill) {
|
||||||
|
|
||||||
$int = $bill->active === 1 ? 0 : 1;
|
$int = $bill->active ? 0 : 1;
|
||||||
|
|
||||||
return $int . strtolower($bill->name);
|
return $int . strtolower($bill->name);
|
||||||
}
|
}
|
||||||
|
@ -166,6 +166,16 @@ class CurrencyRepository implements CurrencyRepositoryInterface
|
|||||||
return TransactionCurrency::get();
|
return TransactionCurrency::get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $ids
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getByIds(array $ids): Collection
|
||||||
|
{
|
||||||
|
return TransactionCurrency::whereIn('id', $ids)->get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Preference $preference
|
* @param Preference $preference
|
||||||
*
|
*
|
||||||
|
@ -90,6 +90,13 @@ interface CurrencyRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function get(): Collection;
|
public function get(): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $ids
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getByIds(array $ids): Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Preference $preference
|
* @param Preference $preference
|
||||||
*
|
*
|
||||||
|
@ -117,8 +117,11 @@ trait SupportJournalsTrait
|
|||||||
if (strlen($data['source_account_name']) > 0) {
|
if (strlen($data['source_account_name']) > 0) {
|
||||||
$sourceType = AccountType::where('type', 'Revenue account')->first();
|
$sourceType = AccountType::where('type', 'Revenue account')->first();
|
||||||
$sourceAccount = Account::firstOrCreateEncrypted(
|
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||||
['user_id' => $user->id, 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name'], 'active' => 1]
|
['user_id' => $user->id, 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name']]
|
||||||
);
|
);
|
||||||
|
// always make account active
|
||||||
|
$sourceAccount->active = true;
|
||||||
|
$sourceAccount->save();
|
||||||
|
|
||||||
Log::debug(sprintf('source account name is "%s", account is %d', $data['source_account_name'], $sourceAccount->id));
|
Log::debug(sprintf('source account name is "%s", account is %d', $data['source_account_name'], $sourceAccount->id));
|
||||||
|
|
||||||
@ -132,8 +135,11 @@ trait SupportJournalsTrait
|
|||||||
|
|
||||||
$sourceType = AccountType::where('type', AccountType::CASH)->first();
|
$sourceType = AccountType::where('type', AccountType::CASH)->first();
|
||||||
$sourceAccount = Account::firstOrCreateEncrypted(
|
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||||
['user_id' => $user->id, 'account_type_id' => $sourceType->id, 'name' => 'Cash account', 'active' => 1]
|
['user_id' => $user->id, 'account_type_id' => $sourceType->id, 'name' => 'Cash account']
|
||||||
);
|
);
|
||||||
|
// always make account active
|
||||||
|
$sourceAccount->active = true;
|
||||||
|
$sourceAccount->save();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'source' => $sourceAccount,
|
'source' => $sourceAccount,
|
||||||
@ -161,10 +167,13 @@ trait SupportJournalsTrait
|
|||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'account_type_id' => $destinationType->id,
|
'account_type_id' => $destinationType->id,
|
||||||
'name' => $data['destination_account_name'],
|
'name' => $data['destination_account_name'],
|
||||||
'active' => 1,
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// always make account active
|
||||||
|
$destinationAccount->active = true;
|
||||||
|
$destinationAccount->save();
|
||||||
|
|
||||||
Log::debug(sprintf('destination account name is "%s", account is %d', $data['destination_account_name'], $destinationAccount->id));
|
Log::debug(sprintf('destination account name is "%s", account is %d', $data['destination_account_name'], $destinationAccount->id));
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -175,8 +184,11 @@ trait SupportJournalsTrait
|
|||||||
Log::debug('destination_account_name is empty, so default to cash account!');
|
Log::debug('destination_account_name is empty, so default to cash account!');
|
||||||
$destinationType = AccountType::where('type', AccountType::CASH)->first();
|
$destinationType = AccountType::where('type', AccountType::CASH)->first();
|
||||||
$destinationAccount = Account::firstOrCreateEncrypted(
|
$destinationAccount = Account::firstOrCreateEncrypted(
|
||||||
['user_id' => $user->id, 'account_type_id' => $destinationType->id, 'name' => 'Cash account', 'active' => 1]
|
['user_id' => $user->id, 'account_type_id' => $destinationType->id, 'name' => 'Cash account']
|
||||||
);
|
);
|
||||||
|
// always make account active
|
||||||
|
$destinationAccount->active = true;
|
||||||
|
$destinationAccount->save();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'source' => $sourceAccount,
|
'source' => $sourceAccount,
|
||||||
|
181
app/Repositories/LinkType/LinkTypeRepository.php
Normal file
181
app/Repositories/LinkType/LinkTypeRepository.php
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* LinkTypeRepository.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\LinkType;
|
||||||
|
|
||||||
|
use FireflyIII\Models\LinkType;
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use FireflyIII\Models\TransactionJournalLink;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LinkTypeRepository
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Repositories\LinkType
|
||||||
|
*/
|
||||||
|
class LinkTypeRepository implements LinkTypeRepositoryInterface
|
||||||
|
{
|
||||||
|
/** @var User */
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkType $linkType
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function countJournals(LinkType $linkType): int
|
||||||
|
{
|
||||||
|
return $linkType->transactionJournalLinks()->count() * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkType $linkType
|
||||||
|
* @param LinkType $moveTo
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function destroy(LinkType $linkType, LinkType $moveTo): bool
|
||||||
|
{
|
||||||
|
if (!is_null($moveTo->id)) {
|
||||||
|
TransactionJournalLink::where('link_type_id', $linkType->id)->update(['link_type_id' => $moveTo->id]);
|
||||||
|
}
|
||||||
|
$linkType->delete();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournalLink $link
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function destroyLink(TransactionJournalLink $link): bool
|
||||||
|
{
|
||||||
|
$link->delete();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
*
|
||||||
|
* @return LinkType
|
||||||
|
*/
|
||||||
|
public function find(int $id): LinkType
|
||||||
|
{
|
||||||
|
$linkType = LinkType::find($id);
|
||||||
|
if (is_null($linkType)) {
|
||||||
|
return new LinkType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $linkType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if link exists between journals.
|
||||||
|
*
|
||||||
|
* @param TransactionJournal $one
|
||||||
|
* @param TransactionJournal $two
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function findLink(TransactionJournal $one, TransactionJournal $two): bool
|
||||||
|
{
|
||||||
|
$count = TransactionJournalLink::whereDestinationId($one->id)->whereSourceId($two->id)->count();
|
||||||
|
$opposingCount = TransactionJournalLink::whereDestinationId($two->id)->whereSourceId($one->id)->count();
|
||||||
|
|
||||||
|
return ($count + $opposingCount > 0);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function get(): Collection
|
||||||
|
{
|
||||||
|
return LinkType::orderBy('name', 'ASC')->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return list of existing connections.
|
||||||
|
*
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getLinks(TransactionJournal $journal): Collection
|
||||||
|
{
|
||||||
|
$outward = TransactionJournalLink::whereSourceId($journal->id)->get();
|
||||||
|
$inward = TransactionJournalLink::whereDestinationId($journal->id)->get();
|
||||||
|
|
||||||
|
return $outward->merge($inward);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
*/
|
||||||
|
public function setUser(User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $data
|
||||||
|
*
|
||||||
|
* @return LinkType
|
||||||
|
*/
|
||||||
|
public function store(array $data): LinkType
|
||||||
|
{
|
||||||
|
$linkType = new LinkType;
|
||||||
|
$linkType->name = $data['name'];
|
||||||
|
$linkType->inward = $data['inward'];
|
||||||
|
$linkType->outward = $data['outward'];
|
||||||
|
$linkType->editable = true;
|
||||||
|
$linkType->save();
|
||||||
|
|
||||||
|
return $linkType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournalLink $link
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function switchLink(TransactionJournalLink $link): bool
|
||||||
|
{
|
||||||
|
$source = $link->source_id;
|
||||||
|
$link->source_id = $link->destination_id;
|
||||||
|
$link->destination_id = $source;
|
||||||
|
$link->save();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkType $linkType
|
||||||
|
* @param array $data
|
||||||
|
*
|
||||||
|
* @return LinkType
|
||||||
|
*/
|
||||||
|
public function update(LinkType $linkType, array $data): LinkType
|
||||||
|
{
|
||||||
|
$linkType->name = $data['name'];
|
||||||
|
$linkType->inward = $data['inward'];
|
||||||
|
$linkType->outward = $data['outward'];
|
||||||
|
$linkType->save();
|
||||||
|
|
||||||
|
return $linkType;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
102
app/Repositories/LinkType/LinkTypeRepositoryInterface.php
Normal file
102
app/Repositories/LinkType/LinkTypeRepositoryInterface.php
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* LinkTypeRepositoryInterface.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\LinkType;
|
||||||
|
|
||||||
|
use FireflyIII\Models\LinkType;
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use FireflyIII\Models\TransactionJournalLink;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface LinkTypeRepositoryInterface
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Repositories\LinkType
|
||||||
|
*/
|
||||||
|
interface LinkTypeRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param LinkType $linkType
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function countJournals(LinkType $linkType): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkType $linkType
|
||||||
|
* @param LinkType $moveTo
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function destroy(LinkType $linkType, LinkType $moveTo): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournalLink $link
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function destroyLink(TransactionJournalLink $link): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
*
|
||||||
|
* @return LinkType
|
||||||
|
*/
|
||||||
|
public function find(int $id): LinkType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if link exists between journals.
|
||||||
|
*
|
||||||
|
* @param TransactionJournal $one
|
||||||
|
* @param TransactionJournal $two
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function findLink(TransactionJournal $one, TransactionJournal $two): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function get(): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return list of existing connections.
|
||||||
|
*
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getLinks(TransactionJournal $journal): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $data
|
||||||
|
*
|
||||||
|
* @return LinkType
|
||||||
|
*/
|
||||||
|
public function store(array $data): LinkType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournalLink $link
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function switchLink(TransactionJournalLink $link): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LinkType $linkType
|
||||||
|
* @param array $data
|
||||||
|
*
|
||||||
|
* @return LinkType
|
||||||
|
*/
|
||||||
|
public function update(LinkType $linkType, array $data): LinkType;
|
||||||
|
|
||||||
|
}
|
@ -56,6 +56,21 @@ class RuleRepository implements RuleRepositoryInterface
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $ruleId
|
||||||
|
*
|
||||||
|
* @return Rule
|
||||||
|
*/
|
||||||
|
public function find(int $ruleId): Rule
|
||||||
|
{
|
||||||
|
$rule = $this->user->rules()->find($ruleId);
|
||||||
|
if (is_null($rule)) {
|
||||||
|
return new Rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rule;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FIxXME can return null
|
* FIxXME can return null
|
||||||
*
|
*
|
||||||
@ -286,7 +301,7 @@ class RuleRepository implements RuleRepositoryInterface
|
|||||||
$ruleTrigger->active = 1;
|
$ruleTrigger->active = 1;
|
||||||
$ruleTrigger->stop_processing = $values['stopProcessing'];
|
$ruleTrigger->stop_processing = $values['stopProcessing'];
|
||||||
$ruleTrigger->trigger_type = $values['action'];
|
$ruleTrigger->trigger_type = $values['action'];
|
||||||
$ruleTrigger->trigger_value = $values['value'];
|
$ruleTrigger->trigger_value = is_null($values['value']) ? '' : $values['value'];
|
||||||
$ruleTrigger->save();
|
$ruleTrigger->save();
|
||||||
|
|
||||||
return $ruleTrigger;
|
return $ruleTrigger;
|
||||||
@ -301,6 +316,7 @@ class RuleRepository implements RuleRepositoryInterface
|
|||||||
public function update(Rule $rule, array $data): Rule
|
public function update(Rule $rule, array $data): Rule
|
||||||
{
|
{
|
||||||
// update rule:
|
// update rule:
|
||||||
|
$rule->rule_group_id = $data['rule_group_id'];
|
||||||
$rule->active = $data['active'];
|
$rule->active = $data['active'];
|
||||||
$rule->stop_processing = $data['stop_processing'];
|
$rule->stop_processing = $data['stop_processing'];
|
||||||
$rule->title = $data['title'];
|
$rule->title = $data['title'];
|
||||||
|
@ -38,6 +38,14 @@ interface RuleRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function destroy(Rule $rule): bool;
|
public function destroy(Rule $rule): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $ruleId
|
||||||
|
*
|
||||||
|
* @return Rule
|
||||||
|
*/
|
||||||
|
public function find(int $ruleId): Rule;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return RuleGroup
|
* @return RuleGroup
|
||||||
*/
|
*/
|
||||||
|
@ -186,6 +186,34 @@ class TagRepository implements TagRepositoryInterface
|
|||||||
return new Carbon;
|
return new Carbon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as sum of tag but substracts income instead of adding it as well.
|
||||||
|
*
|
||||||
|
* @param Tag $tag
|
||||||
|
* @param Carbon|null $start
|
||||||
|
* @param Carbon|null $end
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function resultOfTag(Tag $tag, ?Carbon $start, ?Carbon $end): string
|
||||||
|
{
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
|
||||||
|
if (!is_null($start) && !is_null($end)) {
|
||||||
|
$collector->setRange($start, $end);
|
||||||
|
}
|
||||||
|
|
||||||
|
$collector->setAllAssetAccounts()->setTag($tag);
|
||||||
|
$journals = $collector->getJournals();
|
||||||
|
$sum = '0';
|
||||||
|
foreach ($journals as $journal) {
|
||||||
|
$sum = bcadd($sum, strval($journal->transaction_amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
return strval($sum);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param User $user
|
* @param User $user
|
||||||
*/
|
*/
|
||||||
@ -253,11 +281,71 @@ class TagRepository implements TagRepositoryInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$collector->setAllAssetAccounts()->setTag($tag);
|
$collector->setAllAssetAccounts()->setTag($tag);
|
||||||
$sum = $collector->getJournals()->sum('transaction_amount');
|
$journals = $collector->getJournals();
|
||||||
|
$sum = '0';
|
||||||
|
foreach ($journals as $journal) {
|
||||||
|
$sum = bcadd($sum, app('steam')->positive(strval($journal->transaction_amount)));
|
||||||
|
}
|
||||||
|
|
||||||
return strval($sum);
|
return strval($sum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a tag cloud.
|
||||||
|
*
|
||||||
|
* @param int|null $year
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function tagCloud(?int $year): array
|
||||||
|
{
|
||||||
|
$min = null;
|
||||||
|
$max = 0;
|
||||||
|
$query = $this->user->tags();
|
||||||
|
$return = [];
|
||||||
|
Log::debug('Going to build tag-cloud');
|
||||||
|
if (!is_null($year)) {
|
||||||
|
Log::debug(sprintf('Year is not null: %d', $year));
|
||||||
|
$start = $year . '-01-01';
|
||||||
|
$end = $year . '-12-31';
|
||||||
|
$query->where('date', '>=', $start)->where('date', '<=', $end);
|
||||||
|
}
|
||||||
|
if (is_null($year)) {
|
||||||
|
$query->whereNull('date');
|
||||||
|
Log::debug('Year is NULL');
|
||||||
|
}
|
||||||
|
$tags = $query->orderBy('id', 'desc')->get();
|
||||||
|
$temporary = [];
|
||||||
|
Log::debug(sprintf('Found %d tags', $tags->count()));
|
||||||
|
/** @var Tag $tag */
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
|
||||||
|
$amount = floatval($this->sumOfTag($tag, null, null));
|
||||||
|
$min = $amount < $min || is_null($min) ? $amount : $min;
|
||||||
|
$max = $amount > $max ? $amount : $max;
|
||||||
|
$temporary[] = [
|
||||||
|
'amount' => $amount,
|
||||||
|
'tag' => $tag,
|
||||||
|
];
|
||||||
|
Log::debug(sprintf('Now working on tag %s with total amount %s', $tag->tag, $amount));
|
||||||
|
Log::debug(sprintf('Minimum is now %f, maximum is %f', $min, $max));
|
||||||
|
}
|
||||||
|
/** @var array $entry */
|
||||||
|
foreach ($temporary as $entry) {
|
||||||
|
$scale = $this->cloudScale([12, 20], $entry['amount'], $min, $max);
|
||||||
|
$tagId = $entry['tag']->id;
|
||||||
|
$return[$tagId] = [
|
||||||
|
'scale' => $scale,
|
||||||
|
'tag' => $entry['tag'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug('DONE with tagcloud');
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Tag $tag
|
* @param Tag $tag
|
||||||
* @param array $data
|
* @param array $data
|
||||||
@ -277,4 +365,38 @@ class TagRepository implements TagRepositoryInterface
|
|||||||
return $tag;
|
return $tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $range
|
||||||
|
* @param float $amount
|
||||||
|
* @param float $min
|
||||||
|
* @param float $max
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function cloudScale(array $range, float $amount, float $min, float $max): int
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('Now in cloudScale with %s as amount and %f min, %f max', $amount, $min, $max));
|
||||||
|
$amountDiff = $max - $min;
|
||||||
|
Log::debug(sprintf('AmountDiff is %f', $amountDiff));
|
||||||
|
|
||||||
|
// no difference? Every tag same range:
|
||||||
|
if ($amountDiff === 0.0) {
|
||||||
|
Log::debug(sprintf('AmountDiff is zero, return %d', $range[0]));
|
||||||
|
|
||||||
|
return $range[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$diff = $range[1] - $range[0];
|
||||||
|
$step = 1;
|
||||||
|
if ($diff != 0) {
|
||||||
|
$step = $amountDiff / $diff;
|
||||||
|
}
|
||||||
|
if ($step == 0) {
|
||||||
|
$step = 1;
|
||||||
|
}
|
||||||
|
$extra = round($amount / $step);
|
||||||
|
|
||||||
|
|
||||||
|
return intval($range[0] + $extra);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,15 @@ interface TagRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function lastUseDate(Tag $tag): Carbon;
|
public function lastUseDate(Tag $tag): Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Tag $tag
|
||||||
|
* @param Carbon|null $start
|
||||||
|
* @param Carbon|null $end
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function resultOfTag(Tag $tag, ?Carbon $start, ?Carbon $end): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param User $user
|
* @param User $user
|
||||||
*/
|
*/
|
||||||
@ -135,6 +144,15 @@ interface TagRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function sumOfTag(Tag $tag, ?Carbon $start, ?Carbon $end): string;
|
public function sumOfTag(Tag $tag, ?Carbon $start, ?Carbon $end): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a tag cloud.
|
||||||
|
*
|
||||||
|
* @param int|null $year
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function tagCloud(?int $year): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a tag.
|
* Update a tag.
|
||||||
*
|
*
|
||||||
|
@ -118,7 +118,8 @@ final class Processor
|
|||||||
{
|
{
|
||||||
$self = new self;
|
$self = new self;
|
||||||
foreach ($triggers as $entry) {
|
foreach ($triggers as $entry) {
|
||||||
$trigger = TriggerFactory::makeTriggerFromStrings($entry['type'], $entry['value'], $entry['stopProcessing']);
|
$entry['value'] = is_null($entry['value']) ? '' : $entry['value'];
|
||||||
|
$trigger = TriggerFactory::makeTriggerFromStrings($entry['type'], $entry['value'], $entry['stopProcessing']);
|
||||||
$self->triggers->push($trigger);
|
$self->triggers->push($trigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
65
app/Rules/Triggers/HasAnyBudget.php
Normal file
65
app/Rules/Triggers/HasAnyBudget.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HasAnyBudget.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\Rules\Triggers;
|
||||||
|
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class HasAnyBudget
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Rules\Triggers
|
||||||
|
*/
|
||||||
|
final class HasAnyBudget extends AbstractTrigger implements TriggerInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A trigger is said to "match anything", or match any given transaction,
|
||||||
|
* when the trigger value is very vague or has no restrictions. Easy examples
|
||||||
|
* are the "AmountMore"-trigger combined with an amount of 0: any given transaction
|
||||||
|
* has an amount of more than zero! Other examples are all the "Description"-triggers
|
||||||
|
* which have hard time handling empty trigger values such as "" or "*" (wild cards).
|
||||||
|
*
|
||||||
|
* If the user tries to create such a trigger, this method MUST return true so Firefly III
|
||||||
|
* can stop the storing / updating the trigger. If the trigger is in any way restrictive
|
||||||
|
* (even if it will still include 99.9% of the users transactions), this method MUST return
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* @param null $value
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function willMatchEverything($value = null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function triggered(TransactionJournal $journal): bool
|
||||||
|
{
|
||||||
|
$count = $journal->budgets()->count();
|
||||||
|
if ($count > 0) {
|
||||||
|
Log::debug(sprintf('RuleTrigger HasAnyBudget for journal #%d: count is %d, return true.', $journal->id, $count));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('RuleTrigger HasAnyBudget for journal #%d: count is %d, return false.', $journal->id, $count));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
65
app/Rules/Triggers/HasAnyCategory.php
Normal file
65
app/Rules/Triggers/HasAnyCategory.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HasAnyCategory.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\Rules\Triggers;
|
||||||
|
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class HasAnyCategory
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Rules\Triggers
|
||||||
|
*/
|
||||||
|
final class HasAnyCategory extends AbstractTrigger implements TriggerInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A trigger is said to "match anything", or match any given transaction,
|
||||||
|
* when the trigger value is very vague or has no restrictions. Easy examples
|
||||||
|
* are the "AmountMore"-trigger combined with an amount of 0: any given transaction
|
||||||
|
* has an amount of more than zero! Other examples are all the "Description"-triggers
|
||||||
|
* which have hard time handling empty trigger values such as "" or "*" (wild cards).
|
||||||
|
*
|
||||||
|
* If the user tries to create such a trigger, this method MUST return true so Firefly III
|
||||||
|
* can stop the storing / updating the trigger. If the trigger is in any way restrictive
|
||||||
|
* (even if it will still include 99.9% of the users transactions), this method MUST return
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* @param null $value
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function willMatchEverything($value = null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function triggered(TransactionJournal $journal): bool
|
||||||
|
{
|
||||||
|
$count = $journal->categories()->count();
|
||||||
|
if ($count > 0) {
|
||||||
|
Log::debug(sprintf('RuleTrigger HasAnyCategory for journal #%d: count is %d, return true.', $journal->id, $count));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('RuleTrigger HasAnyCategory for journal #%d: count is %d, return false.', $journal->id, $count));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
65
app/Rules/Triggers/HasAnyTag.php
Normal file
65
app/Rules/Triggers/HasAnyTag.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HasAnyTag.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\Rules\Triggers;
|
||||||
|
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class HasAnyTag
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Rules\Triggers
|
||||||
|
*/
|
||||||
|
final class HasAnyTag extends AbstractTrigger implements TriggerInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A trigger is said to "match anything", or match any given transaction,
|
||||||
|
* when the trigger value is very vague or has no restrictions. Easy examples
|
||||||
|
* are the "AmountMore"-trigger combined with an amount of 0: any given transaction
|
||||||
|
* has an amount of more than zero! Other examples are all the "Description"-triggers
|
||||||
|
* which have hard time handling empty trigger values such as "" or "*" (wild cards).
|
||||||
|
*
|
||||||
|
* If the user tries to create such a trigger, this method MUST return true so Firefly III
|
||||||
|
* can stop the storing / updating the trigger. If the trigger is in any way restrictive
|
||||||
|
* (even if it will still include 99.9% of the users transactions), this method MUST return
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* @param null $value
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function willMatchEverything($value = null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function triggered(TransactionJournal $journal): bool
|
||||||
|
{
|
||||||
|
$count = $journal->tags()->count();
|
||||||
|
if ($count > 0) {
|
||||||
|
Log::debug(sprintf('RuleTrigger HasAnyTag for journal #%d: count is %d, return true.', $journal->id, $count));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('RuleTrigger HasAnyTag for journal #%d: count is %d, return false.', $journal->id, $count));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
65
app/Rules/Triggers/HasNoBudget.php
Normal file
65
app/Rules/Triggers/HasNoBudget.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HasNoBudget.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\Rules\Triggers;
|
||||||
|
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class HasNoBudget
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Rules\Triggers
|
||||||
|
*/
|
||||||
|
final class HasNoBudget extends AbstractTrigger implements TriggerInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A trigger is said to "match anything", or match any given transaction,
|
||||||
|
* when the trigger value is very vague or has no restrictions. Easy examples
|
||||||
|
* are the "AmountMore"-trigger combined with an amount of 0: any given transaction
|
||||||
|
* has an amount of more than zero! Other examples are all the "Description"-triggers
|
||||||
|
* which have hard time handling empty trigger values such as "" or "*" (wild cards).
|
||||||
|
*
|
||||||
|
* If the user tries to create such a trigger, this method MUST return true so Firefly III
|
||||||
|
* can stop the storing / updating the trigger. If the trigger is in any way restrictive
|
||||||
|
* (even if it will still include 99.9% of the users transactions), this method MUST return
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* @param null $value
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function willMatchEverything($value = null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function triggered(TransactionJournal $journal): bool
|
||||||
|
{
|
||||||
|
$count = $journal->budgets()->count();
|
||||||
|
if ($count === 0) {
|
||||||
|
Log::debug(sprintf('RuleTrigger HasNoBudget for journal #%d: count is %d, return true.', $journal->id, $count));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('RuleTrigger HasNoBudget for journal #%d: count is %d, return false.', $journal->id, $count));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
65
app/Rules/Triggers/HasNoCategory.php
Normal file
65
app/Rules/Triggers/HasNoCategory.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HasNoCategory.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\Rules\Triggers;
|
||||||
|
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class HasNoCategory
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Rules\Triggers
|
||||||
|
*/
|
||||||
|
final class HasNoCategory extends AbstractTrigger implements TriggerInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A trigger is said to "match anything", or match any given transaction,
|
||||||
|
* when the trigger value is very vague or has no restrictions. Easy examples
|
||||||
|
* are the "AmountMore"-trigger combined with an amount of 0: any given transaction
|
||||||
|
* has an amount of more than zero! Other examples are all the "Description"-triggers
|
||||||
|
* which have hard time handling empty trigger values such as "" or "*" (wild cards).
|
||||||
|
*
|
||||||
|
* If the user tries to create such a trigger, this method MUST return true so Firefly III
|
||||||
|
* can stop the storing / updating the trigger. If the trigger is in any way restrictive
|
||||||
|
* (even if it will still include 99.9% of the users transactions), this method MUST return
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* @param null $value
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function willMatchEverything($value = null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function triggered(TransactionJournal $journal): bool
|
||||||
|
{
|
||||||
|
$count = $journal->categories()->count();
|
||||||
|
if ($count === 0) {
|
||||||
|
Log::debug(sprintf('RuleTrigger HasNoCategory for journal #%d: count is %d, return true.', $journal->id, $count));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('RuleTrigger HasNoCategory for journal #%d: count is %d, return false.', $journal->id, $count));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
65
app/Rules/Triggers/HasNoTag.php
Normal file
65
app/Rules/Triggers/HasNoTag.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HasNoTag.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\Rules\Triggers;
|
||||||
|
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class HasNoTag
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Rules\Triggers
|
||||||
|
*/
|
||||||
|
final class HasNoTag extends AbstractTrigger implements TriggerInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A trigger is said to "match anything", or match any given transaction,
|
||||||
|
* when the trigger value is very vague or has no restrictions. Easy examples
|
||||||
|
* are the "AmountMore"-trigger combined with an amount of 0: any given transaction
|
||||||
|
* has an amount of more than zero! Other examples are all the "Description"-triggers
|
||||||
|
* which have hard time handling empty trigger values such as "" or "*" (wild cards).
|
||||||
|
*
|
||||||
|
* If the user tries to create such a trigger, this method MUST return true so Firefly III
|
||||||
|
* can stop the storing / updating the trigger. If the trigger is in any way restrictive
|
||||||
|
* (even if it will still include 99.9% of the users transactions), this method MUST return
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* @param null $value
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function willMatchEverything($value = null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function triggered(TransactionJournal $journal): bool
|
||||||
|
{
|
||||||
|
$count = $journal->tags()->count();
|
||||||
|
if ($count === 0) {
|
||||||
|
Log::debug(sprintf('RuleTrigger HasNoTag for journal #%d: count is %d, return true.', $journal->id, $count));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('RuleTrigger HasNoTag for journal #%d: count is %d, return false.', $journal->id, $count));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
43
app/Services/Bunq/Id/BunqId.php
Normal file
43
app/Services/Bunq/Id/BunqId.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BunqId.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\Services\Bunq\Id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BunqId
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Id
|
||||||
|
*/
|
||||||
|
class BunqId
|
||||||
|
{
|
||||||
|
/** @var int */
|
||||||
|
private $id = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
*/
|
||||||
|
public function setId(int $id)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
25
app/Services/Bunq/Id/DeviceServerId.php
Normal file
25
app/Services/Bunq/Id/DeviceServerId.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeviceServerId.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\Services\Bunq\Id;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DeviceServerId
|
||||||
|
*
|
||||||
|
* @package Bunq\Id
|
||||||
|
*/
|
||||||
|
class DeviceServerId extends BunqId
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
24
app/Services/Bunq/Id/DeviceSessionId.php
Normal file
24
app/Services/Bunq/Id/DeviceSessionId.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* DeviceSessionId.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\Services\Bunq\Id;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DeviceSessionId
|
||||||
|
*
|
||||||
|
* @package Bunq\Id
|
||||||
|
*/
|
||||||
|
class DeviceSessionId extends BunqId
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
24
app/Services/Bunq/Id/InstallationId.php
Normal file
24
app/Services/Bunq/Id/InstallationId.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* InstallationId.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\Services\Bunq\Id;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class InstallationId
|
||||||
|
*
|
||||||
|
* @package Bunq\Id
|
||||||
|
*/
|
||||||
|
class InstallationId extends BunqId
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
68
app/Services/Bunq/Object/Alias.php
Normal file
68
app/Services/Bunq/Object/Alias.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Alias.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Alias
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Object
|
||||||
|
*/
|
||||||
|
class Alias extends BunqObject
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $name = '';
|
||||||
|
/** @var string */
|
||||||
|
private $type = '';
|
||||||
|
/** @var string */
|
||||||
|
private $value = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias constructor.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->type = $data['type'];
|
||||||
|
$this->name = $data['name'];
|
||||||
|
$this->value = $data['value'];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getValue(): string
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
57
app/Services/Bunq/Object/Amount.php
Normal file
57
app/Services/Bunq/Object/Amount.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Currency.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Amount
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Object
|
||||||
|
*/
|
||||||
|
class Amount extends BunqObject
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $currency = '';
|
||||||
|
/** @var string */
|
||||||
|
private $value = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount constructor.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->currency = $data['currency'];
|
||||||
|
$this->value = $data['value'];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCurrency(): string
|
||||||
|
{
|
||||||
|
return $this->currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getValue(): string
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
23
app/Services/Bunq/Object/Avatar.php
Normal file
23
app/Services/Bunq/Object/Avatar.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Avatar.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Avatar
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Object
|
||||||
|
*/
|
||||||
|
class Avatar extends BunqObject
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
23
app/Services/Bunq/Object/BunqObject.php
Normal file
23
app/Services/Bunq/Object/BunqObject.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* BunqObject.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BunqObject
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Object
|
||||||
|
*/
|
||||||
|
class BunqObject
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
63
app/Services/Bunq/Object/DeviceServer.php
Normal file
63
app/Services/Bunq/Object/DeviceServer.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* DeviceServer.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Services\Bunq\Id\DeviceServerId;
|
||||||
|
|
||||||
|
class DeviceServer extends BunqObject
|
||||||
|
{
|
||||||
|
/** @var Carbon */
|
||||||
|
private $created;
|
||||||
|
/** @var string */
|
||||||
|
private $description;
|
||||||
|
/** @var DeviceServerId */
|
||||||
|
private $id;
|
||||||
|
/** @var string */
|
||||||
|
private $ip;
|
||||||
|
/** @var string */
|
||||||
|
private $status;
|
||||||
|
/** @var Carbon */
|
||||||
|
private $updated;
|
||||||
|
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$id = new DeviceServerId();
|
||||||
|
$id->setId($data['id']);
|
||||||
|
$this->id = $id;
|
||||||
|
$this->created = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['created']);
|
||||||
|
$this->updated = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['updated']);
|
||||||
|
$this->ip = $data['ip'];
|
||||||
|
$this->description = $data['description'];
|
||||||
|
$this->status = $data['status'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return DeviceServerId
|
||||||
|
*/
|
||||||
|
public function getId(): DeviceServerId
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIp(): string
|
||||||
|
{
|
||||||
|
return $this->ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
151
app/Services/Bunq/Object/MonetaryAccountBank.php
Normal file
151
app/Services/Bunq/Object/MonetaryAccountBank.php
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* MonetaryAccountBank.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class MonetaryAccountBank
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Object
|
||||||
|
*/
|
||||||
|
class MonetaryAccountBank extends BunqObject
|
||||||
|
{
|
||||||
|
/** @var array */
|
||||||
|
private $aliases = [];
|
||||||
|
/** @var Avatar */
|
||||||
|
private $avatar;
|
||||||
|
/** @var Amount */
|
||||||
|
private $balance;
|
||||||
|
/** @var Carbon */
|
||||||
|
private $created;
|
||||||
|
/** @var string */
|
||||||
|
private $currency = '';
|
||||||
|
/** @var Amount */
|
||||||
|
private $dailyLimit;
|
||||||
|
/** @var Amount */
|
||||||
|
private $dailySpent;
|
||||||
|
/** @var string */
|
||||||
|
private $description = '';
|
||||||
|
/** @var int */
|
||||||
|
private $id = 0;
|
||||||
|
/** @var MonetaryAccountProfile */
|
||||||
|
private $monetaryAccountProfile;
|
||||||
|
/** @var array */
|
||||||
|
private $notificationFilters = [];
|
||||||
|
/** @var Amount */
|
||||||
|
private $overdraftLimit;
|
||||||
|
/** @var string */
|
||||||
|
private $publicUuid = '';
|
||||||
|
/** @var string */
|
||||||
|
private $reason = '';
|
||||||
|
/** @var string */
|
||||||
|
private $reasonDescription = '';
|
||||||
|
/** @var MonetaryAccountSetting */
|
||||||
|
private $setting;
|
||||||
|
/** @var string */
|
||||||
|
private $status = '';
|
||||||
|
/** @var string */
|
||||||
|
private $subStatus = '';
|
||||||
|
/** @var Carbon */
|
||||||
|
private $updated;
|
||||||
|
/** @var int */
|
||||||
|
private $userId = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MonetaryAccountBank constructor.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->id = $data['id'];
|
||||||
|
$this->created = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['created']);
|
||||||
|
$this->updated = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['updated']);
|
||||||
|
$this->balance = new Amount($data['balance']);
|
||||||
|
$this->currency = $data['currency'];
|
||||||
|
$this->dailyLimit = new Amount($data['daily_limit']);
|
||||||
|
$this->dailySpent = new Amount($data['daily_spent']);
|
||||||
|
$this->description = $data['description'];
|
||||||
|
$this->publicUuid = $data['public_uuid'];
|
||||||
|
$this->status = $data['status'];
|
||||||
|
$this->subStatus = $data['sub_status'];
|
||||||
|
$this->userId = $data['user_id'];
|
||||||
|
$this->status = $data['status'];
|
||||||
|
$this->subStatus = $data['sub_status'];
|
||||||
|
$this->monetaryAccountProfile = new MonetaryAccountProfile($data['monetary_account_profile']);
|
||||||
|
$this->setting = new MonetaryAccountSetting($data['setting']);
|
||||||
|
$this->overdraftLimit = new Amount($data['overdraft_limit']);
|
||||||
|
|
||||||
|
$this->publicUuid = $data['public_uuid'];
|
||||||
|
|
||||||
|
// create aliases:
|
||||||
|
foreach ($data['alias'] as $alias) {
|
||||||
|
$this->aliases[] = new Alias($alias);
|
||||||
|
}
|
||||||
|
foreach ($data['notification_filters'] as $filter) {
|
||||||
|
$this->notificationFilters = new NotificationFilter($filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAliases(): array
|
||||||
|
{
|
||||||
|
return $this->aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Amount
|
||||||
|
*/
|
||||||
|
public function getBalance(): Amount
|
||||||
|
{
|
||||||
|
return $this->balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCurrency(): string
|
||||||
|
{
|
||||||
|
return $this->currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return MonetaryAccountSetting
|
||||||
|
*/
|
||||||
|
public function getSetting(): MonetaryAccountSetting
|
||||||
|
{
|
||||||
|
return $this->setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
44
app/Services/Bunq/Object/MonetaryAccountProfile.php
Normal file
44
app/Services/Bunq/Object/MonetaryAccountProfile.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* MonetaryAccountProfile.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class MonetaryAccountProfile
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Object
|
||||||
|
*/
|
||||||
|
class MonetaryAccountProfile extends BunqObject
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $profileActionRequired = '';
|
||||||
|
/** @var Amount */
|
||||||
|
private $profileAmountRequired;
|
||||||
|
private $profileDrain;
|
||||||
|
private $profileFill;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MonetaryAccountProfile constructor.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->profileDrain = null;
|
||||||
|
$this->profileFill = null;
|
||||||
|
$this->profileActionRequired = $data['profile_action_required'];
|
||||||
|
$this->profileAmountRequired = new Amount($data['profile_amount_required']);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
68
app/Services/Bunq/Object/MonetaryAccountSetting.php
Normal file
68
app/Services/Bunq/Object/MonetaryAccountSetting.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* MonetaryAccountSetting.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class MonetaryAccountSetting
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Object
|
||||||
|
*/
|
||||||
|
class MonetaryAccountSetting extends BunqObject
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $color = '';
|
||||||
|
/** @var string */
|
||||||
|
private $defaultAvatarStatus = '';
|
||||||
|
/** @var string */
|
||||||
|
private $restrictionChat = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MonetaryAccountSetting constructor.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->color = $data['color'];
|
||||||
|
$this->defaultAvatarStatus = $data['default_avatar_status'];
|
||||||
|
$this->restrictionChat = $data['restriction_chat'];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getColor(): string
|
||||||
|
{
|
||||||
|
return $this->color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDefaultAvatarStatus(): string
|
||||||
|
{
|
||||||
|
return $this->defaultAvatarStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRestrictionChat(): string
|
||||||
|
{
|
||||||
|
return $this->restrictionChat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
31
app/Services/Bunq/Object/NotificationFilter.php
Normal file
31
app/Services/Bunq/Object/NotificationFilter.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* NotificationFilter.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class NotificationFilter
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Object
|
||||||
|
*/
|
||||||
|
class NotificationFilter extends BunqObject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* NotificationFilter constructor.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
52
app/Services/Bunq/Object/ServerPublicKey.php
Normal file
52
app/Services/Bunq/Object/ServerPublicKey.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ServerPublicKey.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ServerPublicKey
|
||||||
|
*
|
||||||
|
* @package Bunq\Object
|
||||||
|
*/
|
||||||
|
class ServerPublicKey extends BunqObject
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $publicKey = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServerPublicKey constructor.
|
||||||
|
*
|
||||||
|
* @param array $response
|
||||||
|
*/
|
||||||
|
public function __construct(array $response)
|
||||||
|
{
|
||||||
|
$this->publicKey = $response['server_public_key'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPublicKey(): string
|
||||||
|
{
|
||||||
|
return $this->publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $publicKey
|
||||||
|
*/
|
||||||
|
public function setPublicKey(string $publicKey)
|
||||||
|
{
|
||||||
|
$this->publicKey = $publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
106
app/Services/Bunq/Object/UserCompany.php
Normal file
106
app/Services/Bunq/Object/UserCompany.php
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* UserCompany.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UserCompany
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Object
|
||||||
|
*/
|
||||||
|
class UserCompany extends BunqObject
|
||||||
|
{
|
||||||
|
private $addressMain;
|
||||||
|
private $addressPostal;
|
||||||
|
/** @var array */
|
||||||
|
private $aliases = [];
|
||||||
|
private $avatar;
|
||||||
|
/** @var string */
|
||||||
|
private $cocNumber = '';
|
||||||
|
/** @var string */
|
||||||
|
private $counterBankIban = '';
|
||||||
|
/** @var Carbon */
|
||||||
|
private $created;
|
||||||
|
private $dailyLimit;
|
||||||
|
private $directorAlias;
|
||||||
|
/** @var string */
|
||||||
|
private $displayName = '';
|
||||||
|
/** @var int */
|
||||||
|
private $id = 0;
|
||||||
|
/** @var string */
|
||||||
|
private $language = '';
|
||||||
|
/** @var string */
|
||||||
|
private $name = '';
|
||||||
|
/** @var array */
|
||||||
|
private $notificationFilters = [];
|
||||||
|
/** @var string */
|
||||||
|
private $publicNickName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $publicUuid = '';
|
||||||
|
/** @var string */
|
||||||
|
private $region = '';
|
||||||
|
/** @var string */
|
||||||
|
private $sectorOfIndustry = '';
|
||||||
|
/** @var int */
|
||||||
|
private $sessionTimeout = 0;
|
||||||
|
/** @var string */
|
||||||
|
private $status = '';
|
||||||
|
/** @var string */
|
||||||
|
private $subStatus = '';
|
||||||
|
/** @var string */
|
||||||
|
private $typeOfBusinessEntity = '';
|
||||||
|
/** @var array */
|
||||||
|
private $ubos = [];
|
||||||
|
/** @var Carbon */
|
||||||
|
private $updated;
|
||||||
|
/** @var int */
|
||||||
|
private $versionTos = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserCompany constructor.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->id = intval($data['id']);
|
||||||
|
$this->created = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['created']);
|
||||||
|
$this->updated = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['updated']);
|
||||||
|
$this->status = $data['status'];
|
||||||
|
$this->subStatus = $data['sub_status'];
|
||||||
|
$this->publicUuid = $data['public_uuid'];
|
||||||
|
$this->displayName = $data['display_name'];
|
||||||
|
$this->publicNickName = $data['public_nick_name'];
|
||||||
|
$this->language = $data['language'];
|
||||||
|
$this->region = $data['region'];
|
||||||
|
$this->sessionTimeout = intval($data['session_timeout']);
|
||||||
|
$this->versionTos = intval($data['version_terms_of_service']);
|
||||||
|
$this->cocNumber = $data['chamber_of_commerce_number'];
|
||||||
|
$this->typeOfBusinessEntity = $data['type_of_business_entity'] ?? '';
|
||||||
|
$this->sectorOfIndustry = $data['sector_of_industry'] ?? '';
|
||||||
|
$this->counterBankIban = $data['counter_bank_iban'];
|
||||||
|
$this->name = $data['name'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
70
app/Services/Bunq/Object/UserLight.php
Normal file
70
app/Services/Bunq/Object/UserLight.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* UserLight.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UserLight
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Object
|
||||||
|
*/
|
||||||
|
class UserLight extends BunqObject
|
||||||
|
{
|
||||||
|
/** @var array */
|
||||||
|
private $aliases = [];
|
||||||
|
/** @var Carbon */
|
||||||
|
private $created;
|
||||||
|
/** @var string */
|
||||||
|
private $displayName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $firstName = '';
|
||||||
|
/** @var int */
|
||||||
|
private $id = 0;
|
||||||
|
/** @var string */
|
||||||
|
private $lastName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $legalName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $middleName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $publicNickName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $publicUuid = '';
|
||||||
|
/** @var Carbon */
|
||||||
|
private $updated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserLight constructor.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
if (count($data) === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->id = intval($data['id']);
|
||||||
|
$this->created = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['created']);
|
||||||
|
$this->updated = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['updated']);
|
||||||
|
$this->publicUuid = $data['public_uuid'];
|
||||||
|
$this->displayName = $data['display_name'];
|
||||||
|
$this->publicNickName = $data['public_nick_name'];
|
||||||
|
$this->firstName = $data['first_name'];
|
||||||
|
$this->middleName = $data['middle_name'];
|
||||||
|
$this->lastName = $data['last_name'];
|
||||||
|
$this->legalName = $data['legal_name'];
|
||||||
|
// aliases
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
141
app/Services/Bunq/Object/UserPerson.php
Normal file
141
app/Services/Bunq/Object/UserPerson.php
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* UserPerson.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\Services\Bunq\Object;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UserPerson
|
||||||
|
*
|
||||||
|
* @package Bunq\Object
|
||||||
|
*/
|
||||||
|
class UserPerson extends BunqObject
|
||||||
|
{
|
||||||
|
private $addressMain;
|
||||||
|
private $addressPostal;
|
||||||
|
/** @var array */
|
||||||
|
private $aliases = [];
|
||||||
|
private $avatar;
|
||||||
|
/** @var array */
|
||||||
|
private $billingContracts = [];
|
||||||
|
/** @var string */
|
||||||
|
private $countryOfBirth = '';
|
||||||
|
/** @var Carbon */
|
||||||
|
private $created;
|
||||||
|
private $customer;
|
||||||
|
private $customerLimit;
|
||||||
|
private $dailyLimit;
|
||||||
|
/** @var Carbon */
|
||||||
|
private $dateOfBirth;
|
||||||
|
/** @var string */
|
||||||
|
private $displayName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $documentCountry = '';
|
||||||
|
/** @var string */
|
||||||
|
private $documentNumber = '';
|
||||||
|
/** @var string */
|
||||||
|
private $documentType = '';
|
||||||
|
/** @var string */
|
||||||
|
private $firstName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $gender = '';
|
||||||
|
/** @var int */
|
||||||
|
private $id = 0;
|
||||||
|
/** @var string */
|
||||||
|
private $language = '';
|
||||||
|
/** @var string */
|
||||||
|
private $lastName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $legalName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $middleName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $nationality = '';
|
||||||
|
/** @var array */
|
||||||
|
private $notificationFilters = [];
|
||||||
|
/** @var string */
|
||||||
|
private $placeOfBirth = '';
|
||||||
|
/** @var string */
|
||||||
|
private $publicNickName = '';
|
||||||
|
/** @var string */
|
||||||
|
private $publicUuid = '';
|
||||||
|
private $region;
|
||||||
|
/** @var int */
|
||||||
|
private $sessionTimeout = 0;
|
||||||
|
/** @var string */
|
||||||
|
private $status = '';
|
||||||
|
/** @var string */
|
||||||
|
private $subStatus = '';
|
||||||
|
/** @var string */
|
||||||
|
private $taxResident = '';
|
||||||
|
/** @var Carbon */
|
||||||
|
private $updated;
|
||||||
|
/** @var int */
|
||||||
|
private $versionTos = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserPerson constructor.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
if (count($data) === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->id = intval($data['id']);
|
||||||
|
$this->created = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['created']);
|
||||||
|
$this->updated = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['updated']);
|
||||||
|
$this->status = $data['status'];
|
||||||
|
$this->subStatus = $data['sub_status'];
|
||||||
|
$this->publicUuid = $data['public_uuid'];
|
||||||
|
$this->displayName = $data['display_name'];
|
||||||
|
$this->publicNickName = $data['public_nick_name'];
|
||||||
|
$this->language = $data['language'];
|
||||||
|
$this->region = $data['region'];
|
||||||
|
$this->sessionTimeout = intval($data['session_timeout']);
|
||||||
|
$this->firstName = $data['first_name'];
|
||||||
|
$this->middleName = $data['middle_name'];
|
||||||
|
$this->lastName = $data['last_name'];
|
||||||
|
$this->legalName = $data['legal_name'];
|
||||||
|
$this->taxResident = $data['tax_resident'];
|
||||||
|
$this->dateOfBirth = Carbon::createFromFormat('Y-m-d', $data['date_of_birth']);
|
||||||
|
$this->placeOfBirth = $data['place_of_birth'];
|
||||||
|
$this->countryOfBirth = $data['country_of_birth'];
|
||||||
|
$this->nationality = $data['nationality'];
|
||||||
|
$this->gender = $data['gender'];
|
||||||
|
$this->versionTos = intval($data['version_terms_of_service']);
|
||||||
|
$this->documentNumber = $data['document_number'];
|
||||||
|
$this->documentType = $data['document_type'];
|
||||||
|
$this->documentCountry = $data['document_country_of_issuance'];
|
||||||
|
|
||||||
|
// create aliases
|
||||||
|
// create avatar
|
||||||
|
// create daily limit
|
||||||
|
// create notification filters
|
||||||
|
// create address main, postal
|
||||||
|
// document front, back attachment
|
||||||
|
// customer, customer_limit
|
||||||
|
// billing contracts
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
451
app/Services/Bunq/Request/BunqRequest.php
Normal file
451
app/Services/Bunq/Request/BunqRequest.php
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* BunqRequest.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\Services\Bunq\Request;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Services\Bunq\Object\ServerPublicKey;
|
||||||
|
use Log;
|
||||||
|
use Requests;
|
||||||
|
use Requests_Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BunqRequest
|
||||||
|
*
|
||||||
|
* @package Bunq\Request
|
||||||
|
*/
|
||||||
|
abstract class BunqRequest
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
protected $secret = '';
|
||||||
|
/** @var string */
|
||||||
|
private $privateKey = '';
|
||||||
|
/** @var string */
|
||||||
|
private $server = '';
|
||||||
|
/** @var ServerPublicKey */
|
||||||
|
private $serverPublicKey;
|
||||||
|
private $upperCaseHeaders
|
||||||
|
= [
|
||||||
|
'x-bunq-client-response-id' => 'X-Bunq-Client-Response-Id',
|
||||||
|
'x-bunq-client-request-id' => 'X-Bunq-Client-Request-Id',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BunqRequest constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->server = config('firefly.bunq.server');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract public function call(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getServer(): string
|
||||||
|
{
|
||||||
|
return $this->server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $privateKey
|
||||||
|
*/
|
||||||
|
public function setPrivateKey(string $privateKey)
|
||||||
|
{
|
||||||
|
$this->privateKey = $privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $secret
|
||||||
|
*/
|
||||||
|
public function setSecret(string $secret)
|
||||||
|
{
|
||||||
|
$this->secret = $secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerPublicKey $serverPublicKey
|
||||||
|
*/
|
||||||
|
public function setServerPublicKey(ServerPublicKey $serverPublicKey)
|
||||||
|
{
|
||||||
|
$this->serverPublicKey = $serverPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $method
|
||||||
|
* @param string $uri
|
||||||
|
* @param array $headers
|
||||||
|
* @param string $data
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
protected function generateSignature(string $method, string $uri, array $headers, string $data): string
|
||||||
|
{
|
||||||
|
if (strlen($this->privateKey) === 0) {
|
||||||
|
throw new FireflyException('No private key present.');
|
||||||
|
}
|
||||||
|
if (strtolower($method) === 'get' || strtolower($method) === 'delete') {
|
||||||
|
$data = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = str_replace(['https://api.bunq.com', 'https://sandbox.public.api.bunq.com'], '', $uri);
|
||||||
|
$toSign = sprintf("%s %s\n", strtoupper($method), $uri);
|
||||||
|
$headersToSign = ['Cache-Control', 'User-Agent'];
|
||||||
|
ksort($headers);
|
||||||
|
foreach ($headers as $name => $value) {
|
||||||
|
if (in_array($name, $headersToSign) || substr($name, 0, 7) === 'X-Bunq-') {
|
||||||
|
$toSign .= sprintf("%s: %s\n", $name, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$toSign .= "\n" . $data;
|
||||||
|
$signature = '';
|
||||||
|
|
||||||
|
openssl_sign($toSign, $signature, $this->privateKey, OPENSSL_ALGO_SHA256);
|
||||||
|
$signature = base64_encode($signature);
|
||||||
|
|
||||||
|
return $signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @param array $response
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getArrayFromResponse(string $key, array $response): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
if (isset($response['Response'])) {
|
||||||
|
foreach ($response['Response'] as $entry) {
|
||||||
|
$currentKey = key($entry);
|
||||||
|
$data = current($entry);
|
||||||
|
if ($currentKey === $key) {
|
||||||
|
$result[] = $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getDefaultHeaders(): array
|
||||||
|
{
|
||||||
|
$userAgent = sprintf('FireflyIII v%s', config('firefly.version'));
|
||||||
|
|
||||||
|
return [
|
||||||
|
'X-Bunq-Client-Request-Id' => uniqid('FFIII'),
|
||||||
|
'Cache-Control' => 'no-cache',
|
||||||
|
'User-Agent' => $userAgent,
|
||||||
|
'X-Bunq-Language' => 'en_US',
|
||||||
|
'X-Bunq-Region' => 'nl_NL',
|
||||||
|
'X-Bunq-Geolocation' => '0 0 0 0 NL',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @param array $response
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getKeyFromResponse(string $key, array $response): array
|
||||||
|
{
|
||||||
|
if (isset($response['Response'])) {
|
||||||
|
foreach ($response['Response'] as $entry) {
|
||||||
|
$currentKey = key($entry);
|
||||||
|
$data = current($entry);
|
||||||
|
if ($currentKey === $key) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $uri
|
||||||
|
* @param array $headers
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function sendSignedBunqDelete(string $uri, array $headers): array
|
||||||
|
{
|
||||||
|
if (strlen($this->server) === 0) {
|
||||||
|
throw new FireflyException('No bunq server defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
$fullUri = $this->server . $uri;
|
||||||
|
$signature = $this->generateSignature('delete', $uri, $headers, '');
|
||||||
|
$headers['X-Bunq-Client-Signature'] = $signature;
|
||||||
|
try {
|
||||||
|
$response = Requests::delete($fullUri, $headers);
|
||||||
|
} catch (Requests_Exception $e) {
|
||||||
|
return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()],]];
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $response->body;
|
||||||
|
$array = json_decode($body, true);
|
||||||
|
$responseHeaders = $response->headers->getAll();
|
||||||
|
$statusCode = $response->status_code;
|
||||||
|
$array['ResponseHeaders'] = $responseHeaders;
|
||||||
|
$array['ResponseStatusCode'] = $statusCode;
|
||||||
|
|
||||||
|
Log::debug(sprintf('Response to DELETE %s is %s', $fullUri, $body));
|
||||||
|
if ($this->isErrorResponse($array)) {
|
||||||
|
$this->throwResponseError($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->verifyServerSignature($body, $responseHeaders, $statusCode)) {
|
||||||
|
throw new FireflyException(sprintf('Could not verify signature for request to "%s"', $uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $uri
|
||||||
|
* @param array $data
|
||||||
|
* @param array $headers
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function sendSignedBunqGet(string $uri, array $data, array $headers): array
|
||||||
|
{
|
||||||
|
if (strlen($this->server) === 0) {
|
||||||
|
throw new FireflyException('No bunq server defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = json_encode($data);
|
||||||
|
$fullUri = $this->server . $uri;
|
||||||
|
$signature = $this->generateSignature('get', $uri, $headers, $body);
|
||||||
|
$headers['X-Bunq-Client-Signature'] = $signature;
|
||||||
|
try {
|
||||||
|
$response = Requests::get($fullUri, $headers);
|
||||||
|
} catch (Requests_Exception $e) {
|
||||||
|
return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()],]];
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $response->body;
|
||||||
|
$array = json_decode($body, true);
|
||||||
|
$responseHeaders = $response->headers->getAll();
|
||||||
|
$statusCode = $response->status_code;
|
||||||
|
$array['ResponseHeaders'] = $responseHeaders;
|
||||||
|
$array['ResponseStatusCode'] = $statusCode;
|
||||||
|
|
||||||
|
if ($this->isErrorResponse($array)) {
|
||||||
|
$this->throwResponseError($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->verifyServerSignature($body, $responseHeaders, $statusCode)) {
|
||||||
|
throw new FireflyException(sprintf('Could not verify signature for request to "%s"', $uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $uri
|
||||||
|
* @param array $data
|
||||||
|
* @param array $headers
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function sendSignedBunqPost(string $uri, array $data, array $headers): array
|
||||||
|
{
|
||||||
|
$body = json_encode($data);
|
||||||
|
$fullUri = $this->server . $uri;
|
||||||
|
$signature = $this->generateSignature('post', $uri, $headers, $body);
|
||||||
|
$headers['X-Bunq-Client-Signature'] = $signature;
|
||||||
|
try {
|
||||||
|
$response = Requests::post($fullUri, $headers, $body);
|
||||||
|
} catch (Requests_Exception $e) {
|
||||||
|
return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()],]];
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $response->body;
|
||||||
|
$array = json_decode($body, true);
|
||||||
|
$responseHeaders = $response->headers->getAll();
|
||||||
|
$statusCode = $response->status_code;
|
||||||
|
$array['ResponseHeaders'] = $responseHeaders;
|
||||||
|
$array['ResponseStatusCode'] = $statusCode;
|
||||||
|
|
||||||
|
if ($this->isErrorResponse($array)) {
|
||||||
|
$this->throwResponseError($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->verifyServerSignature($body, $responseHeaders, $statusCode)) {
|
||||||
|
throw new FireflyException(sprintf('Could not verify signature for request to "%s"', $uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $uri
|
||||||
|
* @param array $headers
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function sendUnsignedBunqDelete(string $uri, array $headers): array
|
||||||
|
{
|
||||||
|
$fullUri = $this->server . $uri;
|
||||||
|
try {
|
||||||
|
$response = Requests::delete($fullUri, $headers);
|
||||||
|
} catch (Requests_Exception $e) {
|
||||||
|
return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()],]];
|
||||||
|
}
|
||||||
|
$body = $response->body;
|
||||||
|
$array = json_decode($body, true);
|
||||||
|
$responseHeaders = $response->headers->getAll();
|
||||||
|
$statusCode = $response->status_code;
|
||||||
|
$array['ResponseHeaders'] = $responseHeaders;
|
||||||
|
$array['ResponseStatusCode'] = $statusCode;
|
||||||
|
|
||||||
|
if ($this->isErrorResponse($array)) {
|
||||||
|
$this->throwResponseError($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $uri
|
||||||
|
* @param array $data
|
||||||
|
* @param array $headers
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function sendUnsignedBunqPost(string $uri, array $data, array $headers): array
|
||||||
|
{
|
||||||
|
$body = json_encode($data);
|
||||||
|
$fullUri = $this->server . $uri;
|
||||||
|
try {
|
||||||
|
$response = Requests::post($fullUri, $headers, $body);
|
||||||
|
} catch (Requests_Exception $e) {
|
||||||
|
return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()],]];
|
||||||
|
}
|
||||||
|
$body = $response->body;
|
||||||
|
$array = json_decode($body, true);
|
||||||
|
$responseHeaders = $response->headers->getAll();
|
||||||
|
$statusCode = $response->status_code;
|
||||||
|
$array['ResponseHeaders'] = $responseHeaders;
|
||||||
|
$array['ResponseStatusCode'] = $statusCode;
|
||||||
|
|
||||||
|
if ($this->isErrorResponse($array)) {
|
||||||
|
$this->throwResponseError($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $response
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function isErrorResponse(array $response): bool
|
||||||
|
{
|
||||||
|
$key = key($response);
|
||||||
|
if ($key === 'Error') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $response
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function throwResponseError(array $response)
|
||||||
|
{
|
||||||
|
$message = [];
|
||||||
|
if (isset($response['Error'])) {
|
||||||
|
foreach ($response['Error'] as $error) {
|
||||||
|
$message[] = $error['error_description'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new FireflyException('Bunq ERROR ' . $response['ResponseStatusCode'] . ': ' . join(', ', $message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $body
|
||||||
|
* @param array $headers
|
||||||
|
* @param int $statusCode
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function verifyServerSignature(string $body, array $headers, int $statusCode): bool
|
||||||
|
{
|
||||||
|
Log::debug('Going to verify signature for body+headers+status');
|
||||||
|
$dataToVerify = $statusCode . "\n";
|
||||||
|
$verifyHeaders = [];
|
||||||
|
|
||||||
|
// false when no public key is present
|
||||||
|
if (is_null($this->serverPublicKey)) {
|
||||||
|
Log::error('No public key present in class, so return FALSE.');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach ($headers as $header => $value) {
|
||||||
|
|
||||||
|
// skip non-bunq headers or signature
|
||||||
|
if (substr($header, 0, 7) !== 'x-bunq-' || $header === 'x-bunq-server-signature') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// need to have upper case variant of header:
|
||||||
|
if (!isset($this->upperCaseHeaders[$header])) {
|
||||||
|
throw new FireflyException(sprintf('No upper case variant for header "%s"', $header));
|
||||||
|
}
|
||||||
|
$header = $this->upperCaseHeaders[$header];
|
||||||
|
$verifyHeaders[$header] = $value[0];
|
||||||
|
}
|
||||||
|
// sort verification headers:
|
||||||
|
ksort($verifyHeaders);
|
||||||
|
|
||||||
|
// add them to data to sign:
|
||||||
|
foreach ($verifyHeaders as $header => $value) {
|
||||||
|
$dataToVerify .= $header . ': ' . trim($value) . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$signature = $headers['x-bunq-server-signature'][0];
|
||||||
|
$dataToVerify .= "\n" . $body;
|
||||||
|
$result = openssl_verify($dataToVerify, base64_decode($signature), $this->serverPublicKey->getPublicKey(), OPENSSL_ALGO_SHA256);
|
||||||
|
|
||||||
|
if (is_int($result) && $result < 1) {
|
||||||
|
Log::error(sprintf('Result of verification is %d, return false.', $result));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!is_int($result)) {
|
||||||
|
Log::error(sprintf('Result of verification is a boolean (%d), return false.', $result));
|
||||||
|
}
|
||||||
|
Log::info('Signature is a match, return true.');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
49
app/Services/Bunq/Request/DeleteDeviceSessionRequest.php
Normal file
49
app/Services/Bunq/Request/DeleteDeviceSessionRequest.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* DeleteDeviceSessionRequest.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\Services\Bunq\Request;
|
||||||
|
|
||||||
|
use FireflyIII\Services\Bunq\Token\SessionToken;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DeleteDeviceSessionRequest
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Request
|
||||||
|
*/
|
||||||
|
class DeleteDeviceSessionRequest extends BunqRequest
|
||||||
|
{
|
||||||
|
/** @var SessionToken */
|
||||||
|
private $sessionToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function call(): void
|
||||||
|
{
|
||||||
|
Log::debug('Going to send bunq delete session request.');
|
||||||
|
$uri = sprintf('/v1/session/%d', $this->sessionToken->getId());
|
||||||
|
$headers = $this->getDefaultHeaders();
|
||||||
|
$headers['X-Bunq-Client-Authentication'] = $this->sessionToken->getToken();
|
||||||
|
$this->sendSignedBunqDelete($uri, $headers);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SessionToken $sessionToken
|
||||||
|
*/
|
||||||
|
public function setSessionToken(SessionToken $sessionToken)
|
||||||
|
{
|
||||||
|
$this->sessionToken = $sessionToken;
|
||||||
|
}
|
||||||
|
}
|
82
app/Services/Bunq/Request/DeviceServerRequest.php
Normal file
82
app/Services/Bunq/Request/DeviceServerRequest.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* DeviceServerRequest.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\Services\Bunq\Request;
|
||||||
|
|
||||||
|
use FireflyIII\Services\Bunq\Id\DeviceServerId;
|
||||||
|
use FireflyIII\Services\Bunq\Token\InstallationToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DeviceServerRequest
|
||||||
|
*
|
||||||
|
* @package Bunq\Request
|
||||||
|
*/
|
||||||
|
class DeviceServerRequest extends BunqRequest
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $description = '';
|
||||||
|
/** @var DeviceServerId */
|
||||||
|
private $deviceServerId;
|
||||||
|
/** @var InstallationToken */
|
||||||
|
private $installationToken;
|
||||||
|
/** @var array */
|
||||||
|
private $permittedIps = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function call(): void
|
||||||
|
{
|
||||||
|
$uri = '/v1/device-server';
|
||||||
|
$data = ['description' => $this->description, 'secret' => $this->secret, 'permitted_ips' => $this->permittedIps];
|
||||||
|
$headers = $this->getDefaultHeaders();
|
||||||
|
$headers['X-Bunq-Client-Authentication'] = $this->installationToken->getToken();
|
||||||
|
$response = $this->sendSignedBunqPost($uri, $data, $headers);
|
||||||
|
$deviceServerId = new DeviceServerId;
|
||||||
|
$deviceServerId->setId(intval($response['Response'][0]['Id']['id']));
|
||||||
|
$this->deviceServerId = $deviceServerId;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return DeviceServerId
|
||||||
|
*/
|
||||||
|
public function getDeviceServerId(): DeviceServerId
|
||||||
|
{
|
||||||
|
return $this->deviceServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $description
|
||||||
|
*/
|
||||||
|
public function setDescription(string $description)
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InstallationToken $installationToken
|
||||||
|
*/
|
||||||
|
public function setInstallationToken(InstallationToken $installationToken)
|
||||||
|
{
|
||||||
|
$this->installationToken = $installationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $permittedIps
|
||||||
|
*/
|
||||||
|
public function setPermittedIps(array $permittedIps)
|
||||||
|
{
|
||||||
|
$this->permittedIps = $permittedIps;
|
||||||
|
}
|
||||||
|
}
|
149
app/Services/Bunq/Request/DeviceSessionRequest.php
Normal file
149
app/Services/Bunq/Request/DeviceSessionRequest.php
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* DeviceSessionRequest.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\Services\Bunq\Request;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Services\Bunq\Id\DeviceSessionId;
|
||||||
|
use FireflyIII\Services\Bunq\Object\UserCompany;
|
||||||
|
use FireflyIII\Services\Bunq\Object\UserPerson;
|
||||||
|
use FireflyIII\Services\Bunq\Token\InstallationToken;
|
||||||
|
use FireflyIII\Services\Bunq\Token\SessionToken;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DeviceSessionRequest
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Request
|
||||||
|
*/
|
||||||
|
class DeviceSessionRequest extends BunqRequest
|
||||||
|
{
|
||||||
|
/** @var DeviceSessionId */
|
||||||
|
private $deviceSessionId;
|
||||||
|
/** @var InstallationToken */
|
||||||
|
private $installationToken;
|
||||||
|
/** @var SessionToken */
|
||||||
|
private $sessionToken;
|
||||||
|
/** @var UserCompany */
|
||||||
|
private $userCompany;
|
||||||
|
/** @var UserPerson */
|
||||||
|
private $userPerson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function call(): void
|
||||||
|
{
|
||||||
|
$uri = '/v1/session-server';
|
||||||
|
$data = ['secret' => $this->secret];
|
||||||
|
$headers = $this->getDefaultHeaders();
|
||||||
|
$headers['X-Bunq-Client-Authentication'] = $this->installationToken->getToken();
|
||||||
|
$response = $this->sendSignedBunqPost($uri, $data, $headers);
|
||||||
|
|
||||||
|
|
||||||
|
$this->deviceSessionId = $this->extractDeviceSessionId($response);
|
||||||
|
$this->sessionToken = $this->extractSessionToken($response);
|
||||||
|
$this->userPerson = $this->extractUserPerson($response);
|
||||||
|
$this->userCompany = $this->extractUserCompany($response);
|
||||||
|
|
||||||
|
Log::debug(sprintf('Session ID: %s', serialize($this->deviceSessionId)));
|
||||||
|
Log::debug(sprintf('Session token: %s', serialize($this->sessionToken)));
|
||||||
|
Log::debug(sprintf('Session user person: %s', serialize($this->userPerson)));
|
||||||
|
Log::debug(sprintf('Session user company: %s', serialize($this->userCompany)));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return DeviceSessionId
|
||||||
|
*/
|
||||||
|
public function getDeviceSessionId(): DeviceSessionId
|
||||||
|
{
|
||||||
|
return $this->deviceSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return SessionToken
|
||||||
|
*/
|
||||||
|
public function getSessionToken(): SessionToken
|
||||||
|
{
|
||||||
|
return $this->sessionToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return UserPerson
|
||||||
|
*/
|
||||||
|
public function getUserPerson(): UserPerson
|
||||||
|
{
|
||||||
|
return $this->userPerson;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InstallationToken $installationToken
|
||||||
|
*/
|
||||||
|
public function setInstallationToken(InstallationToken $installationToken)
|
||||||
|
{
|
||||||
|
$this->installationToken = $installationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $response
|
||||||
|
*
|
||||||
|
* @return DeviceSessionId
|
||||||
|
*/
|
||||||
|
private function extractDeviceSessionId(array $response): DeviceSessionId
|
||||||
|
{
|
||||||
|
$data = $this->getKeyFromResponse('Id', $response);
|
||||||
|
$deviceSessionId = new DeviceSessionId;
|
||||||
|
$deviceSessionId->setId(intval($data['id']));
|
||||||
|
|
||||||
|
return $deviceSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function extractSessionToken(array $response): SessionToken
|
||||||
|
{
|
||||||
|
$data = $this->getKeyFromResponse('Token', $response);
|
||||||
|
$sessionToken = new SessionToken($data);
|
||||||
|
|
||||||
|
return $sessionToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $response
|
||||||
|
*
|
||||||
|
* @return UserCompany
|
||||||
|
*/
|
||||||
|
private function extractUserCompany($response): UserCompany
|
||||||
|
{
|
||||||
|
$data = $this->getKeyFromResponse('UserCompany', $response);
|
||||||
|
$userCompany = new UserCompany($data);
|
||||||
|
|
||||||
|
|
||||||
|
return $userCompany;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $response
|
||||||
|
*
|
||||||
|
* @return UserPerson
|
||||||
|
*/
|
||||||
|
private function extractUserPerson($response): UserPerson
|
||||||
|
{
|
||||||
|
$data = $this->getKeyFromResponse('UserPerson', $response);
|
||||||
|
$userPerson = new UserPerson($data);
|
||||||
|
|
||||||
|
|
||||||
|
return $userPerson;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
147
app/Services/Bunq/Request/InstallationTokenRequest.php
Normal file
147
app/Services/Bunq/Request/InstallationTokenRequest.php
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* InstallationTokenRequest.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\Services\Bunq\Request;
|
||||||
|
|
||||||
|
use FireflyIII\Services\Bunq\Id\InstallationId;
|
||||||
|
use FireflyIII\Services\Bunq\Object\ServerPublicKey;
|
||||||
|
use FireflyIII\Services\Bunq\Token\InstallationToken;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class InstallationTokenRequest
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Request
|
||||||
|
*/
|
||||||
|
class InstallationTokenRequest extends BunqRequest
|
||||||
|
{
|
||||||
|
/** @var InstallationId */
|
||||||
|
private $installationId;
|
||||||
|
/** @var InstallationToken */
|
||||||
|
private $installationToken;
|
||||||
|
/** @var string */
|
||||||
|
private $publicKey = '';
|
||||||
|
/** @var ServerPublicKey */
|
||||||
|
private $serverPublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function call(): void
|
||||||
|
{
|
||||||
|
$uri = '/v1/installation';
|
||||||
|
$data = ['client_public_key' => $this->publicKey,];
|
||||||
|
$headers = $this->getDefaultHeaders();
|
||||||
|
$response = $this->sendUnsignedBunqPost($uri, $data, $headers);
|
||||||
|
Log::debug('Installation request response', $response);
|
||||||
|
|
||||||
|
$this->installationId = $this->extractInstallationId($response);
|
||||||
|
$this->serverPublicKey = $this->extractServerPublicKey($response);
|
||||||
|
$this->installationToken = $this->extractInstallationToken($response);
|
||||||
|
|
||||||
|
Log::debug(sprintf('Installation ID: %s', serialize($this->installationId)));
|
||||||
|
Log::debug(sprintf('Installation token: %s', serialize($this->installationToken)));
|
||||||
|
Log::debug(sprintf('server public key: %s', serialize($this->serverPublicKey)));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return InstallationId
|
||||||
|
*/
|
||||||
|
public function getInstallationId(): InstallationId
|
||||||
|
{
|
||||||
|
return $this->installationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return InstallationToken
|
||||||
|
*/
|
||||||
|
public function getInstallationToken(): InstallationToken
|
||||||
|
{
|
||||||
|
return $this->installationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPublicKey(): string
|
||||||
|
{
|
||||||
|
return $this->publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $publicKey
|
||||||
|
*/
|
||||||
|
public function setPublicKey(string $publicKey)
|
||||||
|
{
|
||||||
|
$this->publicKey = $publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ServerPublicKey
|
||||||
|
*/
|
||||||
|
public function getServerPublicKey(): ServerPublicKey
|
||||||
|
{
|
||||||
|
return $this->serverPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $fake
|
||||||
|
*/
|
||||||
|
public function setFake(bool $fake)
|
||||||
|
{
|
||||||
|
$this->fake = $fake;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $response
|
||||||
|
*
|
||||||
|
* @return InstallationId
|
||||||
|
*/
|
||||||
|
private function extractInstallationId(array $response): InstallationId
|
||||||
|
{
|
||||||
|
$installationId = new InstallationId;
|
||||||
|
$data = $this->getKeyFromResponse('Id', $response);
|
||||||
|
$installationId->setId(intval($data['id']));
|
||||||
|
|
||||||
|
return $installationId;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $response
|
||||||
|
*
|
||||||
|
* @return InstallationToken
|
||||||
|
*/
|
||||||
|
private function extractInstallationToken(array $response): InstallationToken
|
||||||
|
{
|
||||||
|
|
||||||
|
$data = $this->getKeyFromResponse('Token', $response);
|
||||||
|
$installationToken = new InstallationToken($data);
|
||||||
|
|
||||||
|
return $installationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $response
|
||||||
|
*
|
||||||
|
* @return ServerPublicKey
|
||||||
|
*/
|
||||||
|
private function extractServerPublicKey(array $response): ServerPublicKey
|
||||||
|
{
|
||||||
|
$data = $this->getKeyFromResponse('ServerPublicKey', $response);
|
||||||
|
$serverPublicKey = new ServerPublicKey($data);
|
||||||
|
|
||||||
|
return $serverPublicKey;
|
||||||
|
}
|
||||||
|
}
|
73
app/Services/Bunq/Request/ListDeviceServerRequest.php
Normal file
73
app/Services/Bunq/Request/ListDeviceServerRequest.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ListDeviceServerRequest.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\Services\Bunq\Request;
|
||||||
|
|
||||||
|
use FireflyIII\Services\Bunq\Object\DeviceServer;
|
||||||
|
use FireflyIII\Services\Bunq\Token\InstallationToken;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ListDeviceServerRequest
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Request
|
||||||
|
*/
|
||||||
|
class ListDeviceServerRequest extends BunqRequest
|
||||||
|
{
|
||||||
|
/** @var Collection */
|
||||||
|
private $devices;
|
||||||
|
/** @var InstallationToken */
|
||||||
|
private $installationToken;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->devices = new Collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function call(): void
|
||||||
|
{
|
||||||
|
$uri = '/v1/device-server';
|
||||||
|
$data = [];
|
||||||
|
$headers = $this->getDefaultHeaders();
|
||||||
|
$headers['X-Bunq-Client-Authentication'] = $this->installationToken->getToken();
|
||||||
|
$response = $this->sendSignedBunqGet($uri, $data, $headers);
|
||||||
|
|
||||||
|
// create device server objects:
|
||||||
|
$raw = $this->getArrayFromResponse('DeviceServer', $response);
|
||||||
|
/** @var array $entry */
|
||||||
|
foreach ($raw as $entry) {
|
||||||
|
$this->devices->push(new DeviceServer($entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getDevices(): Collection
|
||||||
|
{
|
||||||
|
return $this->devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InstallationToken $installationToken
|
||||||
|
*/
|
||||||
|
public function setInstallationToken(InstallationToken $installationToken)
|
||||||
|
{
|
||||||
|
$this->installationToken = $installationToken;
|
||||||
|
}
|
||||||
|
}
|
79
app/Services/Bunq/Request/ListMonetaryAccountRequest.php
Normal file
79
app/Services/Bunq/Request/ListMonetaryAccountRequest.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ListMonetaryAccountRequest.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\Services\Bunq\Request;
|
||||||
|
|
||||||
|
use FireflyIII\Services\Bunq\Object\MonetaryAccountBank;
|
||||||
|
use FireflyIII\Services\Bunq\Token\SessionToken;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ListMonetaryAccountRequest
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Request
|
||||||
|
*/
|
||||||
|
class ListMonetaryAccountRequest extends BunqRequest
|
||||||
|
{
|
||||||
|
/** @var Collection */
|
||||||
|
private $monetaryAccounts;
|
||||||
|
/** @var SessionToken */
|
||||||
|
private $sessionToken;
|
||||||
|
/** @var int */
|
||||||
|
private $userId = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function call(): void
|
||||||
|
{
|
||||||
|
$this->monetaryAccounts = new Collection;
|
||||||
|
$uri = sprintf('/v1/user/%d/monetary-account', $this->userId);
|
||||||
|
$data = [];
|
||||||
|
$headers = $this->getDefaultHeaders();
|
||||||
|
$headers['X-Bunq-Client-Authentication'] = $this->sessionToken->getToken();
|
||||||
|
$response = $this->sendSignedBunqGet($uri, $data, $headers);
|
||||||
|
|
||||||
|
// create device server objects:
|
||||||
|
$raw = $this->getArrayFromResponse('MonetaryAccountBank', $response);
|
||||||
|
foreach ($raw as $entry) {
|
||||||
|
$account = new MonetaryAccountBank($entry);
|
||||||
|
$this->monetaryAccounts->push($account);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getMonetaryAccounts(): Collection
|
||||||
|
{
|
||||||
|
return $this->monetaryAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SessionToken $sessionToken
|
||||||
|
*/
|
||||||
|
public function setSessionToken(SessionToken $sessionToken)
|
||||||
|
{
|
||||||
|
$this->sessionToken = $sessionToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $userId
|
||||||
|
*/
|
||||||
|
public function setUserId(int $userId)
|
||||||
|
{
|
||||||
|
$this->userId = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
90
app/Services/Bunq/Request/ListUserRequest.php
Normal file
90
app/Services/Bunq/Request/ListUserRequest.php
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ListUserRequest.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\Services\Bunq\Request;
|
||||||
|
|
||||||
|
use FireflyIII\Services\Bunq\Object\UserCompany;
|
||||||
|
use FireflyIII\Services\Bunq\Object\UserLight;
|
||||||
|
use FireflyIII\Services\Bunq\Object\UserPerson;
|
||||||
|
use FireflyIII\Services\Bunq\Token\SessionToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ListUserRequest
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Services\Bunq\Request
|
||||||
|
*/
|
||||||
|
class ListUserRequest extends BunqRequest
|
||||||
|
{
|
||||||
|
/** @var SessionToken */
|
||||||
|
private $sessionToken;
|
||||||
|
/** @var UserCompany */
|
||||||
|
private $userCompany;
|
||||||
|
/** @var UserLight */
|
||||||
|
private $userLight;
|
||||||
|
/** @var UserPerson */
|
||||||
|
private $userPerson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function call(): void
|
||||||
|
{
|
||||||
|
$uri = '/v1/user';
|
||||||
|
$data = [];
|
||||||
|
$headers = $this->getDefaultHeaders();
|
||||||
|
$headers['X-Bunq-Client-Authentication'] = $this->sessionToken->getToken();
|
||||||
|
$response = $this->sendSignedBunqGet($uri, $data, $headers);
|
||||||
|
|
||||||
|
// create user objects:
|
||||||
|
$light = $this->getKeyFromResponse('UserLight', $response);
|
||||||
|
$company = $this->getKeyFromResponse('UserCompany', $response);
|
||||||
|
$person = $this->getKeyFromResponse('UserPerson', $response);
|
||||||
|
$this->userLight = new UserLight($light);
|
||||||
|
$this->userCompany = new UserCompany($company);
|
||||||
|
$this->userPerson = new UserPerson($person);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return UserCompany
|
||||||
|
*/
|
||||||
|
public function getUserCompany(): UserCompany
|
||||||
|
{
|
||||||
|
return $this->userCompany;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return UserLight
|
||||||
|
*/
|
||||||
|
public function getUserLight(): UserLight
|
||||||
|
{
|
||||||
|
return $this->userLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return UserPerson
|
||||||
|
*/
|
||||||
|
public function getUserPerson(): UserPerson
|
||||||
|
{
|
||||||
|
return $this->userPerson;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SessionToken $sessionToken
|
||||||
|
*/
|
||||||
|
public function setSessionToken(SessionToken $sessionToken)
|
||||||
|
{
|
||||||
|
$this->sessionToken = $sessionToken;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user