Compare commits

...

20 Commits

Author SHA1 Message Date
github-actions[bot]
5b0be91f93 Merge pull request #10885 from firefly-iii/release-1757479782
🤖 Automatically merge the PR into the develop branch.
2025-09-10 06:49:50 +02:00
JC5
01e7b604da 🤖 Auto commit for release 'develop' on 2025-09-10 2025-09-10 06:49:42 +02:00
James Cole
58d175444b Fix #10883 2025-09-10 06:43:24 +02:00
James Cole
134770644a Fix amounts in transaction. 2025-09-09 17:14:53 +02:00
James Cole
a9f21c9371 Extra text in the PR. 2025-09-09 15:53:25 +02:00
github-actions[bot]
781947beeb Merge pull request #10879 from firefly-iii/release-1757425805
🤖 Automatically merge the PR into the develop branch.
2025-09-09 15:50:18 +02:00
JC5
9760cd2f97 🤖 Auto commit for release 'develop' on 2025-09-09 2025-09-09 15:50:06 +02:00
James Cole
d317e9ec32 Update changelog. 2025-09-09 15:41:19 +02:00
James Cole
534f7fcadb Merge branch 'main' into develop 2025-09-09 15:36:55 +02:00
James Cole
fb3f7a1d4b Merge pull request #10874 from firefly-iii/dependabot/github_actions/actions/stale-10
Bump actions/stale from 9 to 10
2025-09-08 11:41:37 +02:00
James Cole
bf2c3e3561 Merge pull request #10873 from firefly-iii/dependabot/github_actions/actions/github-script-8
Bump actions/github-script from 7 to 8
2025-09-08 11:41:12 +02:00
github-actions[bot]
b670f81dcd Merge pull request #10875 from firefly-iii/release-1757313349
🤖 Automatically merge the PR into the develop branch.
2025-09-08 08:36:01 +02:00
JC5
7aac1cdf67 🤖 Auto commit for release 'develop' on 2025-09-08 2025-09-08 08:35:49 +02:00
Sander Dorigo
fa0ac8a16c Fix php files 2025-09-08 08:31:09 +02:00
dependabot[bot]
0990b1f0b4 Bump actions/stale from 9 to 10
Bumps [actions/stale](https://github.com/actions/stale) from 9 to 10.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v9...v10)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: '10'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 03:08:14 +00:00
dependabot[bot]
c1922670c8 Bump actions/github-script from 7 to 8
Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 03:08:10 +00:00
James Cole
81cd89d66f Final nestor and phpstan fixes. 2025-09-07 17:42:16 +02:00
James Cole
f5c202543c Clean up code. 2025-09-07 17:31:08 +02:00
James Cole
034f437c6b Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-07 17:30:43 +02:00
James Cole
8550ba6138 Remove unused code. 2025-09-07 17:30:34 +02:00
83 changed files with 180 additions and 2468 deletions

View File

@@ -15,7 +15,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Prune cancelled/skipped runs
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -45,7 +45,7 @@ jobs:
}
- name: Prune runs older than 3 days
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |

View File

@@ -250,7 +250,7 @@ jobs:
fi
echo "Merge all changes from $BRANCH_NAME back into '$MERGE_INTO' using a PR"
PR_URL=$(gh pr create -B $MERGE_INTO -H $BRANCH_NAME --title "🤖 Automatic PR to merge all changes into the '$MERGE_INTO' branch." --body '🤖 Created by GitHub action')
PR_URL=$(gh pr create -B $MERGE_INTO -H $BRANCH_NAME --title "🤖 Automatic PR to merge all changes into the '$MERGE_INTO' branch." --body '🤖 This PR was created automatically by a GitHub action to merge the changed files into this branch. It will be merged automatically. `Share and enjoy`')
echo "PR URL is '$PR_URL'"
IFS='/' read -ra parts <<< "$PR_URL"
PR_NR=$(printf %s\\n "${parts[@]:(-1)}")
@@ -272,7 +272,7 @@ jobs:
echo "Also merge everything into main since this is a release."
echo 'create PR'
PR_URL=$(gh pr create -B main -H develop --title "🤖 Automatic PR to merge all changes into the main branch." --body "🤖 Created by GitHub action")
PR_URL=$(gh pr create -B main -H develop --title "🤖 Automatic PR to merge all changes into the main branch." --body "🤖 This PR was created automatically by a GitHub action to merge the changed files into this branch. It will be merged automatically. `Share and enjoy`")
echo "PR URL is '$PR_URL'"
IFS='/' read -ra parts <<< "$PR_URL"

View File

@@ -15,7 +15,7 @@ jobs:
actions: write
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
- uses: actions/stale@v10
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: |

View File

@@ -133,9 +133,9 @@ class BudgetController extends Controller
$row['pc_left'] = '0';
$row['pc_overspent'] = '0';
if (null !== $limit) {
if ($limit instanceof BudgetLimit) {
$row['budgeted'] = $limit->amount;
$row['left'] = bcsub($row['budgeted'], bcmul($row['spent'], '-1'));
$row['left'] = bcsub((string) $row['budgeted'], bcmul((string) $row['spent'], '-1'));
$row['overspent'] = bcmul($row['left'], '-1');
$row['left'] = 1 === bccomp($row['left'], '0') ? $row['left'] : '0';
$row['overspent'] = 1 === bccomp($row['overspent'], '0') ? $row['overspent'] : '0';

View File

@@ -70,7 +70,7 @@ class StoreController extends Controller
foreach ($data as $date => $rate) {
$date = Carbon::createFromFormat('Y-m-d', $date);
$existing = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null !== $existing) {
if ($existing instanceof CurrencyExchangeRate) {
// update existing rate.
$existing = $this->repository->updateExchangeRate($existing, $rate);
$collection->push($existing);
@@ -101,7 +101,7 @@ class StoreController extends Controller
foreach ($data['rates'] as $key => $rate) {
$to = Amount::getTransactionCurrencyByCode($key);
$existing = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null !== $existing) {
if ($existing instanceof CurrencyExchangeRate) {
// update existing rate.
$existing = $this->repository->updateExchangeRate($existing, $rate);
$collection->push($existing);

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Account;
use Illuminate\Validation\Validator;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Rules\IsValidSortInstruction;
@@ -30,7 +31,6 @@ use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\User;
use Illuminate\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
class ShowRequest extends FormRequest

View File

@@ -24,13 +24,13 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate;
use Illuminate\Validation\Validator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Validator;
class StoreByDateRequest extends FormRequest
{

View File

@@ -24,8 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\PiggyBank;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Validation\Validator;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\IsValidZeroOrMoreAmount;
@@ -97,7 +97,7 @@ class StoreRequest extends FormRequest
// validate start before end only if both are there.
$data = $validator->getData();
$currency = $this->getCurrencyFromData($validator, $data);
if (null === $currency) {
if (!$currency instanceof TransactionCurrency) {
return;
}
$targetAmount = (string) ($data['target_amount'] ?? '0');

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Factory;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Log;
@@ -42,7 +43,7 @@ class TransactionCurrencyFactory
$data['code'] = e($data['code']);
$data['symbol'] = e($data['symbol']);
$data['name'] = e($data['name']);
$data['decimal_places'] = (int) $data['decimal_places'];
$data['decimal_places'] = (int)$data['decimal_places'];
// if the code already exists (deleted)
// force delete it and then create the transaction:
$count = TransactionCurrency::withTrashed()->whereCode($data['code'])->count();
@@ -77,7 +78,8 @@ class TransactionCurrencyFactory
public function find(?int $currencyId, ?string $currencyCode): ?TransactionCurrency
{
$currencyCode = e($currencyCode);
$currencyId = (int) $currencyId;
$currencyId = (int)$currencyId;
$currency = null;
if ('' === $currencyCode && 0 === $currencyId) {
Log::debug('Cannot find anything on empty currency code and empty currency ID!');
@@ -87,22 +89,22 @@ class TransactionCurrencyFactory
// first by ID:
if ($currencyId > 0) {
$currency = TransactionCurrency::find($currencyId);
if (null !== $currency) {
return $currency;
try {
$currency = Amount::getTransactionCurrencyById($currencyId);
} catch (FireflyException) {
Log::warning(sprintf('Currency ID is #%d but found nothing!', $currencyId));
}
Log::warning(sprintf('Currency ID is %d but found nothing!', $currencyId));
}
// then by code:
if ('' !== $currencyCode) {
$currency = TransactionCurrency::whereCode($currencyCode)->first();
if (null !== $currency) {
return $currency;
if ('' !== $currencyCode && null === $currency) {
try {
$currency = Amount::getTransactionCurrencyByCode($currencyCode);
} catch (FireflyException) {
Log::warning(sprintf('Currency code is "%s" but found nothing!', $currencyCode));
}
Log::warning(sprintf('Currency code is %d but found nothing!', $currencyCode));
}
Log::warning('Found nothing for currency.');
Log::info(sprintf('Found currency #%d based on ID %d and code "%s".', $currency->id, $currencyId, $currencyCode));
return null;
return $currency;
}
}

View File

@@ -32,7 +32,6 @@ use FireflyIII\Notifications\Admin\UserInvitation;
use FireflyIII\Notifications\Admin\VersionCheckResult;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\Notifications\Test\OwnerTestNotificationEmail;
use FireflyIII\Notifications\Test\OwnerTestNotificationNtfy;
use FireflyIII\Notifications\Test\OwnerTestNotificationPushover;
use FireflyIII\Notifications\Test\OwnerTestNotificationSlack;
use Illuminate\Support\Facades\Log;

View File

@@ -44,7 +44,6 @@ use FireflyIII\Models\UserRole;
use FireflyIII\Notifications\Admin\UserRegistration as AdminRegistrationNotification;
use FireflyIII\Notifications\Security\UserFailedLoginAttempt;
use FireflyIII\Notifications\Test\UserTestNotificationEmail;
use FireflyIII\Notifications\Test\UserTestNotificationNtfy;
use FireflyIII\Notifications\Test\UserTestNotificationPushover;
use FireflyIII\Notifications\Test\UserTestNotificationSlack;
use FireflyIII\Notifications\User\UserLogin;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Helpers\Collector\Extensions;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
@@ -919,6 +920,8 @@ trait MetaCollection
{
$this->withCategoryInformation();
$this->query->whereNull('category_transaction_journal.category_id');
// better fix for #10507
$this->query->whereNotIn('transaction_types.type', [TransactionTypeEnum::OPENING_BALANCE->value]);
return $this;
}

View File

@@ -1095,9 +1095,6 @@ class GroupCollector implements GroupCollectorInterface
->whereNull('transaction_groups.deleted_at')
->whereNull('transaction_journals.deleted_at')
->whereNull('source.deleted_at')
// #10507 ignore opening balance.
->where('transaction_types.type', '!=', TransactionTypeEnum::OPENING_BALANCE->value)
->whereNotNull('transaction_groups.id')
->whereNull('destination.deleted_at')
->orderBy('transaction_journals.date', 'DESC')
@@ -1177,7 +1174,8 @@ class GroupCollector implements GroupCollectorInterface
// include budget ID + name (if any)
->withBudgetInformation()
// include bill ID + name (if any)
->withBillInformation();
->withBillInformation()
;
return $this;
}

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use Illuminate\Validation\Validator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -32,7 +33,6 @@ use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
/**
* Class PiggyBankStoreRequest.

View File

@@ -23,8 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Exceptions\FireflyException;
use Illuminate\Validation\Validator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Notifications\Admin;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Bus\Queueable;
@@ -35,7 +34,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
class UnknownUserLoginAttempt extends Notification
{

View File

@@ -27,7 +27,6 @@ namespace FireflyIII\Notifications\Admin;
use FireflyIII\Models\InvitedUser;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
@@ -36,7 +35,6 @@ use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
/**
* Class UserInvitation

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Notifications\Admin;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
@@ -36,7 +35,6 @@ use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
/**
* Class UserRegistration

View File

@@ -26,14 +26,12 @@ namespace FireflyIII\Notifications\Admin;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
/**
* Class VersionCheckResult

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Security;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
@@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
class DisabledMFANotification extends Notification
{

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Security;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
@@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
class EnabledMFANotification extends Notification
{

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Security;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
@@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
class MFABackupFewLeftNotification extends Notification
{

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Security;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
@@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
class MFABackupNoLeftNotification extends Notification
{

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Security;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
@@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
class MFAManyFailedAttemptsNotification extends Notification
{

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Security;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
@@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
class MFAUsedBackupCodeNotification extends Notification
{

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Security;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
@@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
class NewBackupCodesNotification extends Notification
{

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Security;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
@@ -35,7 +34,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
class UserFailedLoginAttempt extends Notification
{

View File

@@ -25,11 +25,8 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Test;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\Notifications\ReturnsSettings;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Ntfy\Message;
use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel;
// use Illuminate\Notifications\Slack\SlackMessage;

View File

@@ -24,12 +24,9 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Test;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Ntfy\Message;
use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel;
// use Illuminate\Notifications\Slack\SlackMessage;

View File

@@ -26,14 +26,12 @@ namespace FireflyIII\Notifications\User;
use FireflyIII\Models\Bill;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
/**
* Class BillReminder

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\User;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
@@ -35,7 +34,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
/**
* Class NewAccessToken

View File

@@ -25,13 +25,11 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\User;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
/**
* Class RuleActionFailed

View File

@@ -27,7 +27,6 @@ namespace FireflyIII\Notifications\User;
use Carbon\Carbon;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
@@ -63,9 +62,7 @@ class SubscriptionsOverdueReminder extends Notification
'bill' => $item['bill'],
];
$current['pay_dates'] = array_map(
static function (string $date): string {
return new Carbon($date)->isoFormat((string)trans('config.month_and_day_moment_js'));
},
static fn (string $date): string => new Carbon($date)->isoFormat((string)trans('config.month_and_day_moment_js')),
$item['dates']['pay_dates']
);
$info[] = $current;

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\User;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
@@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
/**
* Class UserLogin

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\User;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
@@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Request;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
/**
* Class UserNewPassword

View File

@@ -294,9 +294,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$summarizer->setConvertToPrimary($convertToPrimary);
// filter $journals by range AND currency if it is present.
$expenses = array_filter($expenses, static function (array $expense) use ($start, $end, $transactionCurrency): bool {
return $expense['date']->between($start, $end) && $expense['currency_id'] === $transactionCurrency->id;
});
$expenses = array_filter($expenses, static fn (array $expense): bool => $expense['date']->between($start, $end) && $expense['currency_id'] === $transactionCurrency->id);
return $summarizer->groupByCurrencyId($expenses, 'negative', false);
}
@@ -308,9 +306,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$summarizer->setConvertToPrimary($convertToPrimary);
// filter $journals by range AND currency if it is present.
$expenses = array_filter($expenses, static function (array $expense) use ($budget): bool {
return $expense['budget_id'] === $budget->id;
});
$expenses = array_filter($expenses, static fn (array $expense): bool => $expense['budget_id'] === $budget->id);
return $summarizer->groupByCurrencyId($expenses, 'negative', false);
}

View File

@@ -38,6 +38,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Services\Internal\Destroy\CurrencyDestroyService;
use FireflyIII\Services\Internal\Update\CurrencyUpdateService;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
@@ -84,7 +85,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
}
// is being used in accounts:
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((string) $currency->id))->count();
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((string)$currency->id))->count();
if ($meta > 0) {
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
@@ -92,7 +93,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
}
// second search using integer check.
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((int) $currency->id))->count();
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((int)$currency->id))->count();
if ($meta > 0) {
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
@@ -183,7 +184,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
return $all->map(static function (TransactionCurrency $current) use ($local) {
$hasId = $local->contains(static fn (TransactionCurrency $entry) => $entry->id === $current->id);
$isPrimary = $local->contains(static fn (TransactionCurrency $entry) => 1 === (int) $entry->pivot->group_default && $entry->id === $current->id);
$isPrimary = $local->contains(static fn (TransactionCurrency $entry) => 1 === (int)$entry->pivot->group_default && $entry->id === $current->id);
$current->userGroupEnabled = $hasId;
$current->userGroupNative = $isPrimary;
@@ -196,7 +197,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
$all = $this->userGroup->currencies()->orderBy('code', 'ASC')->withPivot(['group_default'])->get();
$all->map(static function (TransactionCurrency $current) { // @phpstan-ignore-line
$current->userGroupEnabled = true;
$current->userGroupNative = 1 === (int) $current->pivot->group_default;
$current->userGroupNative = 1 === (int)$current->pivot->group_default;
return $current;
});
@@ -261,14 +262,14 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
public function findCurrencyNull(?int $currencyId, ?string $currencyCode): ?TransactionCurrency
{
Log::debug(sprintf('Now in findCurrencyNull(%s, "%s")', var_export($currencyId, true), $currencyCode));
$result = $this->find((int) $currencyId);
$result = $this->find((int)$currencyId);
if ($result instanceof TransactionCurrency) {
Log::debug(sprintf('Found currency by ID: %s', $result->code));
return $result;
}
Log::debug(sprintf('Searching for currency with code "%s"...', $currencyCode));
$result = $this->findByCode((string) $currencyCode);
$result = $this->findByCode((string)$currencyCode);
if ($result instanceof TransactionCurrency && false === $result->enabled) {
Log::debug(sprintf('Also enabled currency %s', $result->code));
@@ -282,7 +283,13 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
#[Override]
public function find(int $currencyId): ?TransactionCurrency
{
return TransactionCurrency::find($currencyId);
try {
$result = Amount::getTransactionCurrencyById($currencyId);
} catch (FireflyException) {
return null;
}
return $result;
}
/**
@@ -290,7 +297,13 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
*/
public function findByCode(string $currencyCode): ?TransactionCurrency
{
return TransactionCurrency::where('code', $currencyCode)->first();
try {
$result = Amount::getTransactionCurrencyByCode($currencyCode);
} catch (FireflyException) {
return null;
}
return $result;
}
public function enable(TransactionCurrency $currency): void

View File

@@ -1,415 +0,0 @@
<?php
/*
* AccountRepository.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Account;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Services\Internal\Update\AccountUpdateService;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Override;
use stdClass;
use function Safe\json_encode;
/**
* Class AccountRepository
*
* @deprecated
*/
class AccountRepository implements AccountRepositoryInterface
{
use UserGroupTrait;
#[Override]
public function countAccounts(array $types): int
{
$query = $this->userGroup->accounts();
if (0 !== count($types)) {
$query->accountTypeIn($types);
}
return $query->count();
}
public function findByAccountNumber(string $number, array $types): ?Account
{
$dbQuery = $this->userGroup
->accounts()
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
->where('accounts.active', true)
->where(
static function (EloquentBuilder $q1) use ($number): void {
$json = json_encode($number);
$q1->where('account_meta.name', '=', 'account_number');
$q1->where('account_meta.data', '=', $json);
}
)
;
if (0 !== count($types)) {
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$dbQuery->whereIn('account_types.type', $types);
}
/** @var null|Account */
return $dbQuery->first(['accounts.*']);
}
public function findByIbanNull(string $iban, array $types): ?Account
{
$iban = Steam::filterSpaces($iban);
$query = $this->userGroup->accounts()->where('iban', '!=', '')->whereNotNull('iban');
if (0 !== count($types)) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$query->whereIn('account_types.type', $types);
}
/** @var null|Account */
return $query->where('iban', $iban)->first(['accounts.*']);
}
public function findByName(string $name, array $types): ?Account
{
$query = $this->userGroup->accounts();
if (0 !== count($types)) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$query->whereIn('account_types.type', $types);
}
app('log')->debug(sprintf('Searching for account named "%s" (of user #%d) of the following type(s)', $name, $this->user->id), ['types' => $types]);
$query->where('accounts.name', $name);
/** @var null|Account $account */
$account = $query->first(['accounts.*']);
if (null === $account) {
app('log')->debug(sprintf('There is no account with name "%s" of types', $name), $types);
return null;
}
app('log')->debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id));
return $account;
}
#[Override]
public function getAccountBalances(Account $account): Collection
{
return $account->accountBalances;
}
public function getAccountCurrency(Account $account): ?TransactionCurrency
{
$type = $account->accountType->type;
$list = config('firefly.valid_currency_account_types');
// return null if not in this list.
if (!in_array($type, $list, true)) {
return null;
}
$currencyId = (int) $this->getMetaValue($account, 'currency_id');
if ($currencyId > 0) {
return Amount::getTransactionCurrencyById($currencyId);
}
return null;
}
/**
* Return meta value for account. Null if not found.
*/
public function getMetaValue(Account $account, string $field): ?string
{
$result = $account->accountMeta->filter(
static fn (AccountMeta $meta) => strtolower($meta->name) === strtolower($field)
);
if (0 === $result->count()) {
return null;
}
if (1 === $result->count()) {
return (string) $result->first()->data;
}
return null;
}
public function find(int $accountId): ?Account
{
$account = $this->user->accounts()->find($accountId);
if (null === $account) {
/** @var null|Account */
return $this->userGroup->accounts()->find($accountId);
}
/** @var null|Account */
return $account;
}
#[Override]
public function getAccountTypes(Collection $accounts): Collection
{
return AccountType::leftJoin('accounts', 'accounts.account_type_id', '=', 'account_types.id')
->whereIn('accounts.id', $accounts->pluck('id')->toArray())
->get(['accounts.id', 'account_types.type'])
;
}
public function getAccountsById(array $accountIds): Collection
{
$query = $this->userGroup->accounts();
if (0 !== count($accountIds)) {
$query->whereIn('accounts.id', $accountIds);
}
$query->orderBy('accounts.order', 'ASC');
$query->orderBy('accounts.active', 'DESC');
$query->orderBy('accounts.name', 'ASC');
return $query->get(['accounts.*']);
}
#[Override]
public function getAccountsInOrder(array $types, array $sort, int $startRow, int $endRow): Collection
{
$query = $this->userGroup->accounts();
if (0 !== count($types)) {
$query->accountTypeIn($types);
}
$query->skip($startRow);
$query->take($endRow - $startRow);
// add sort parameters. At this point they're filtered to allowed fields to sort by:
if (0 !== count($sort)) {
foreach ($sort as $label => $direction) {
$query->orderBy(sprintf('accounts.%s', $label), $direction);
}
}
if (0 === count($sort)) {
$query->orderBy('accounts.order', 'ASC');
$query->orderBy('accounts.active', 'DESC');
$query->orderBy('accounts.name', 'ASC');
}
return $query->get(['accounts.*']);
}
public function getActiveAccountsByType(array $types): Collection
{
$query = $this->userGroup->accounts();
if (0 !== count($types)) {
$query->accountTypeIn($types);
}
$query->where('active', true);
$query->orderBy('accounts.account_type_id', 'ASC');
$query->orderBy('accounts.order', 'ASC');
$query->orderBy('accounts.name', 'ASC');
return $query->get(['accounts.*']);
}
#[Override]
public function getLastActivity(Collection $accounts): array
{
return Transaction::whereIn('account_id', $accounts->pluck('id')->toArray())->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id')->groupBy('transactions.account_id')->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) as date_max')])->toArray();
}
#[Override]
public function getMetaValues(Collection $accounts, array $fields): Collection
{
$query = AccountMeta::whereIn('account_id', $accounts->pluck('id')->toArray());
if (count($fields) > 0) {
$query->whereIn('name', $fields);
}
return $query->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']);
}
#[Override]
public function getObjectGroups(Collection $accounts): array
{
$groupIds = [];
$return = [];
$set = DB::table('object_groupables')->where('object_groupable_type', Account::class)
->whereIn('object_groupable_id', $accounts->pluck('id')->toArray())->get()
;
/** @var stdClass $row */
foreach ($set as $row) {
$groupIds[] = $row->object_group_id;
}
$groupIds = array_unique($groupIds);
$groups = ObjectGroup::whereIn('id', $groupIds)->get();
/** @var stdClass $row */
foreach ($set as $row) {
if (!array_key_exists($row->object_groupable_id, $return)) {
/** @var null|ObjectGroup $group */
$group = $groups->firstWhere('id', '=', $row->object_group_id);
if (null !== $group) {
$return[$row->object_groupable_id] = ['title' => $group->title, 'order' => $group->order, 'id' => $group->id];
}
}
}
return $return;
}
public function resetAccountOrder(): void
{
$sets = [
[AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value],
[AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::MORTGAGE->value],
];
foreach ($sets as $set) {
$list = $this->getAccountsByType($set);
$index = 1;
foreach ($list as $account) {
if (false === $account->active) {
$account->order = 0;
continue;
}
if ($index !== (int) $account->order) {
app('log')->debug(sprintf('Account #%d ("%s"): order should %d be but is %d.', $account->id, $account->name, $index, $account->order));
$account->order = $index;
$account->save();
}
++$index;
}
}
// reset the rest to zero.
$all = [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::MORTGAGE->value];
$this->user->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereNotIn('account_types.type', $all)
->update(['order' => 0])
;
}
public function getAccountsByType(array $types, ?array $sort = [], ?array $filters = []): Collection
{
$sortable = ['name', 'active']; // TODO yes this is a duplicate array.
$res = array_intersect([AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value], $types);
$query = $this->userGroup->accounts();
if (0 !== count($types)) {
$query->accountTypeIn($types);
}
// process filters
// TODO this should be repeatable, it feels like a hack when you do it here.
// TODO some fields cannot be filtered using the query, and a second filter must be applied on the collection.
foreach ($filters as $column => $value) {
// filter on NULL values
if (null === $value) {
continue;
}
if ('active' === $column) {
$query->where('accounts.active', $value);
}
if ('name' === $column) {
$query->whereLike('accounts.name', sprintf('%%%s%%', $value));
}
}
// add sort parameters. At this point they're filtered to allowed fields to sort by:
$hasActiveColumn = array_key_exists('active', $sort);
if (count($sort) > 0) {
if (false === $hasActiveColumn) {
$query->orderBy('accounts.active', 'DESC');
}
foreach ($sort as $column => $direction) {
if (in_array($column, $sortable, true)) {
$query->orderBy(sprintf('accounts.%s', $column), $direction);
}
}
}
if (0 === count($sort)) {
if (0 !== count($res)) {
$query->orderBy('accounts.active', 'DESC');
}
$query->orderBy('accounts.order', 'ASC');
$query->orderBy('accounts.name', 'ASC');
$query->orderBy('accounts.account_type_id', 'ASC');
$query->orderBy('accounts.id', 'ASC');
}
return $query->get(['accounts.*']);
}
#[Override]
public function update(Account $account, array $data): Account
{
/** @var AccountUpdateService $service */
$service = app(AccountUpdateService::class);
return $service->update($account, $data);
}
public function searchAccount(string $query, array $types, int $page, int $limit): Collection
{
// search by group, not by user
$dbQuery = $this->userGroup->accounts()
->where('active', true)
->orderBy('accounts.updated_at', 'ASC')
->orderBy('accounts.order', 'ASC')
->orderBy('accounts.account_type_id', 'ASC')
->orderBy('accounts.name', 'ASC')
->with(['accountType'])
;
// split query on spaces just in case:
if ('' !== trim($query)) {
$dbQuery->where(function (EloquentBuilder $q) use ($query): void {
$parts = explode(' ', $query);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$q->orWhereLike('name', $search);
}
});
}
if (0 !== count($types)) {
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$dbQuery->whereIn('account_types.type', $types);
}
$dbQuery->skip(($page - 1) * $limit)->take($limit);
return $dbQuery->get(['accounts.*']);
}
}

View File

@@ -1,84 +0,0 @@
<?php
/*
* AccountRepositoryInterface.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Account;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionCurrency;
use Illuminate\Support\Collection;
/**
* Interface AccountRepositoryInterface
*
* @deprecated
*/
interface AccountRepositoryInterface
{
public function countAccounts(array $types): int;
public function find(int $accountId): ?Account;
public function findByAccountNumber(string $number, array $types): ?Account;
public function findByIbanNull(string $iban, array $types): ?Account;
public function findByName(string $name, array $types): ?Account;
public function getAccountBalances(Account $account): Collection;
public function getAccountCurrency(Account $account): ?TransactionCurrency;
public function getAccountTypes(Collection $accounts): Collection;
public function getAccountsById(array $accountIds): Collection;
public function getAccountsByType(array $types, ?array $sort = [], ?array $filters = []): Collection;
/**
* Used in the infinite accounts list.
*/
public function getAccountsInOrder(array $types, array $sort, int $startRow, int $endRow): Collection;
public function getActiveAccountsByType(array $types): Collection;
public function getLastActivity(Collection $accounts): array;
/**
* Return meta value for account. Null if not found.
*/
public function getMetaValue(Account $account, string $field): ?string;
public function getMetaValues(Collection $accounts, array $fields): Collection;
public function getObjectGroups(Collection $accounts): array;
/**
* Reset order types of the mentioned accounts.
*/
public function resetAccountOrder(): void;
public function searchAccount(string $query, array $types, int $page, int $limit): Collection;
public function update(Account $account, array $data): Account;
}

View File

@@ -1,236 +0,0 @@
<?php
/*
* BillRepository.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Bill;
use Carbon\Carbon;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class BillRepository
*
* @deprecated
*/
class BillRepository implements BillRepositoryInterface
{
use UserGroupTrait;
/**
* Correct order of piggies in case of issues.
*/
public function correctOrder(): void
{
$set = $this->userGroup->bills()->orderBy('order', 'ASC')->get();
$current = 1;
foreach ($set as $bill) {
if ($bill->order !== $current) {
$bill->order = $current;
$bill->save();
}
++$current;
}
}
public function getBills(): Collection
{
return $this->userGroup->bills()
->orderBy('bills.name', 'ASC')
->get(['bills.*'])
;
}
public function sumPaidInRange(Carbon $start, Carbon $end): array
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$bills = $this->getActiveBills();
$primary = app('amount')->getPrimaryCurrency();
$return = [];
$converter = new ExchangeRateConverter();
/** @var Bill $bill */
foreach ($bills as $bill) {
/** @var Collection $set */
$set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']);
$currency = $bill->transactionCurrency;
$currencyId = $bill->transaction_currency_id;
$return[$currencyId] ??= [
'currency_id' => (string) $currency->id,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
'currency_decimal_places' => $currency->decimal_places,
'primary_currency_id' => (string) $primary->id,
'primary_currency_name' => $primary->name,
'primary_currency_symbol' => $primary->symbol,
'primary_currency_code' => $primary->code,
'primary_currency_decimal_places' => $primary->decimal_places,
'sum' => '0',
'pc_sum' => '0',
];
/** @var TransactionJournal $transactionJournal */
foreach ($set as $transactionJournal) {
/** @var null|Transaction $sourceTransaction */
$sourceTransaction = $transactionJournal->transactions()->where('amount', '<', 0)->first();
if (null !== $sourceTransaction) {
$amount = $sourceTransaction->amount;
if ((int) $sourceTransaction->foreign_currency_id === $currency->id) {
// use foreign amount instead!
$amount = (string) $sourceTransaction->foreign_amount;
}
// convert to primary currency
$pcAmount = $amount;
if ($currencyId !== $primary->id) {
// get rate and convert.
$pcAmount = $converter->convert($currency, $primary, $transactionJournal->date, $amount);
}
if ((int) $sourceTransaction->foreign_currency_id === $primary->id) {
// ignore conversion, use foreign amount
$pcAmount = (string) $sourceTransaction->foreign_amount;
}
$return[$currencyId]['sum'] = bcadd($return[$currencyId]['sum'], (string) $amount);
$return[$currencyId]['pc_sum'] = bcadd($return[$currencyId]['pc_sum'], (string) $pcAmount);
}
}
}
$converter->summarize();
return $return;
}
public function getActiveBills(): Collection
{
return $this->userGroup->bills()
->where('active', true)
->orderBy('bills.name', 'ASC')
->get(['bills.*'])
;
}
public function sumUnpaidInRange(Carbon $start, Carbon $end): array
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$bills = $this->getActiveBills();
$return = [];
$primary = app('amount')->getPrimaryCurrency();
$converter = new ExchangeRateConverter();
/** @var Bill $bill */
foreach ($bills as $bill) {
$dates = $this->getPayDatesInRange($bill, $start, $end);
$count = $bill->transactionJournals()->after($start)->before($end)->count();
$total = $dates->count() - $count;
if ($total > 0) {
$currency = $bill->transactionCurrency;
$currencyId = $bill->transaction_currency_id;
$average = bcdiv(bcadd((string) $bill->amount_max, (string) $bill->amount_min), '2');
$pcAverage = $converter->convert($currency, $primary, $start, $average);
$return[$currencyId] ??= [
'currency_id' => (string) $currency->id,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
'currency_decimal_places' => $currency->decimal_places,
'primary_currency_id' => (string) $primary->id,
'primary_currency_name' => $primary->name,
'primary_currency_symbol' => $primary->symbol,
'primary_currency_code' => $primary->code,
'primary_currency_decimal_places' => $primary->decimal_places,
'sum' => '0',
'pc_sum' => '0',
];
$return[$currencyId]['sum'] = bcadd($return[$currencyId]['sum'], bcmul($average, (string) $total));
$return[$currencyId]['pc_sum'] = bcadd($return[$currencyId]['pc_sum'], bcmul($pcAverage, (string) $total));
}
}
$converter->summarize();
return $return;
}
/**
* Between start and end, tells you on which date(s) the bill is expected to hit.
* TODO duplicate of function in other billrepositoryinterface
*/
public function getPayDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection
{
$set = new Collection();
$currentStart = clone $start;
// app('log')->debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq));
// app('log')->debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d')));
while ($currentStart <= $end) {
// app('log')->debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d')));
$nextExpectedMatch = $this->nextDateMatch($bill, $currentStart);
// app('log')->debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d')));
if ($nextExpectedMatch > $end) {// If nextExpectedMatch is after end, we continue
break;
}
$set->push(clone $nextExpectedMatch);
// app('log')->debug(sprintf('Now %d dates in set.', $set->count()));
$nextExpectedMatch->addDay();
// app('log')->debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d')));
$currentStart = clone $nextExpectedMatch;
}
return $set;
}
/**
* Given a bill and a date, this method will tell you at which moment this bill expects its next
* transaction. Whether it is there already, is not relevant.
*
* TODO duplicate of other repos
*/
public function nextDateMatch(Bill $bill, Carbon $date): Carbon
{
$cache = new CacheProperties();
$cache->addProperty($bill->id);
$cache->addProperty('nextDateMatch');
$cache->addProperty($date);
if ($cache->has()) {
return $cache->get();
}
// find the most recent date for this bill NOT in the future. Cache this date:
$start = clone $bill->date;
while ($start < $date) {
$start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip);
}
$cache->store($start);
return $start;
}
}

View File

@@ -1,72 +0,0 @@
<?php
/*
* BillRepositoryInterface.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Bill;
use Carbon\Carbon;
use FireflyIII\Models\Bill;
use Illuminate\Support\Collection;
/**
* Interface BillRepositoryInterface
*
* @deprecated
*/
interface BillRepositoryInterface
{
/**
* TODO duplicate of other repos
* Add correct order to bills.
*/
public function correctOrder(): void;
public function getActiveBills(): Collection;
public function getBills(): Collection;
/**
* Between start and end, tells you on which date(s) the bill is expected to hit.
*
* TODO duplicate of method in other billrepositoryinterface
*/
public function getPayDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection;
/**
* Given a bill and a date, this method will tell you at which moment this bill expects its next
* transaction. Whether it is there already, is not relevant.
*
* TODO duplicate of method in other bill repos
*/
public function nextDateMatch(Bill $bill, Carbon $date): Carbon;
/**
* Collect multi-currency of sum of bills already paid.
*/
public function sumPaidInRange(Carbon $start, Carbon $end): array;
/**
* Collect multi-currency of sum of bills yet to pay.
*/
public function sumUnpaidInRange(Carbon $start, Carbon $end): array;
}

View File

@@ -1,78 +0,0 @@
<?php
/*
* AvailableBudgetRepository.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Budget;
use Carbon\Carbon;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Facades\Log;
/**
* Class AvailableBudgetRepository
*
* @deprecated
*/
class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
{
use UserGroupTrait;
public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$return = [];
$converter = new ExchangeRateConverter();
$primary = app('amount')->getPrimaryCurrency();
$availableBudgets = $this->userGroup->availableBudgets()
->where('start_date', $start->format('Y-m-d'))
->where('end_date', $end->format('Y-m-d'))->get()
;
/** @var AvailableBudget $availableBudget */
foreach ($availableBudgets as $availableBudget) {
$currencyId = $availableBudget->transaction_currency_id;
$return[$currencyId] ??= [
'currency_id' => $currencyId,
'currency_code' => $availableBudget->transactionCurrency->code,
'currency_symbol' => $availableBudget->transactionCurrency->symbol,
'currency_name' => $availableBudget->transactionCurrency->name,
'currency_decimal_places' => $availableBudget->transactionCurrency->decimal_places,
'primary_currency_id' => $primary->id,
'primary_currency_code' => $primary->code,
'primary_currency_symbol' => $primary->symbol,
'primary_currency_name' => $primary->name,
'primary_currency_decimal_places' => $primary->decimal_places,
'amount' => '0',
'pc_amount' => '0',
];
$pcAmount = $converter->convert($availableBudget->transactionCurrency, $primary, $availableBudget->start_date, $availableBudget->amount);
$return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], (string) $availableBudget->amount);
$return[$currencyId]['pc_amount'] = bcadd($return[$currencyId]['pc_amount'], $pcAmount);
}
$converter->summarize();
return $return;
}
}

View File

@@ -1,37 +0,0 @@
<?php
/*
* AvailableBudgetRepositoryInterface.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Budget;
use Carbon\Carbon;
/**
* Interface AvailableBudgetRepositoryInterface
*
* @deprecated
*/
interface AvailableBudgetRepositoryInterface
{
public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array;
}

View File

@@ -1,56 +0,0 @@
<?php
/*
* BudgetRepository.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Budget;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
/**
* Class BudgetRepository
*
* @deprecated
*/
class BudgetRepository implements BudgetRepositoryInterface
{
use UserGroupTrait;
public function getActiveBudgets(): Collection
{
return $this->userGroup->budgets()->where('active', true)
->orderBy('order', 'ASC')
->orderBy('name', 'ASC')
->get()
;
}
public function getBudgets(): Collection
{
return $this->userGroup->budgets()
->orderBy('order', 'ASC')
->orderBy('name', 'ASC')
->get()
;
}
}

View File

@@ -1,39 +0,0 @@
<?php
/*
* BudgetRepositoryInterface.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Budget;
use Illuminate\Support\Collection;
/**
* Interface BudgetRepositoryInterface
*
* @deprecated
*/
interface BudgetRepositoryInterface
{
public function getActiveBudgets(): Collection;
public function getBudgets(): Collection;
}

View File

@@ -1,136 +0,0 @@
<?php
/*
* OperationsRepository.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Budget;
use Carbon\Carbon;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
/**
* Class OperationsRepository
*
* @deprecated
*/
class OperationsRepository implements OperationsRepositoryInterface
{
use UserGroupTrait;
/**
* @throws FireflyException
*/
public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUserGroup($this->userGroup)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
if ($accounts instanceof Collection && $accounts->count() > 0) {
$collector->setAccounts($accounts);
}
if ($budgets instanceof Collection && $budgets->count() > 0) {
$collector->setBudgets($budgets);
}
if (!$budgets instanceof Collection || (0 === $budgets->count())) {
$collector->setBudgets($this->getBudgets());
}
$collector->withBudgetInformation()->withAccountInformation()->withCategoryInformation();
$journals = $collector->getExtractedJournals();
$array = [];
foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id'];
$budgetId = (int) $journal['budget_id'];
$budgetName = (string) $journal['budget_name'];
// catch "no budget" entries.
if (0 === $budgetId) {
continue;
}
// info about the currency:
$array[$currencyId] ??= [
'budgets' => [],
'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'],
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_decimal_places' => $journal['currency_decimal_places'],
];
// info about the budgets:
$array[$currencyId]['budgets'][$budgetId] ??= [
'id' => $budgetId,
'name' => $budgetName,
'transaction_journals' => [],
];
// add journal to array:
// only a subset of the fields.
$journalId = (int) $journal['transaction_journal_id'];
$final = [
'amount' => app('steam')->negative($journal['amount']),
'currency_id' => $journal['currency_id'],
'foreign_amount' => null,
'foreign_currency_id' => null,
'foreign_currency_code' => null,
'foreign_currency_symbol' => null,
'foreign_currency_name' => null,
'foreign_currency_decimal_places' => null,
'destination_account_id' => $journal['destination_account_id'],
'destination_account_name' => $journal['destination_account_name'],
'source_account_id' => $journal['source_account_id'],
'source_account_name' => $journal['source_account_name'],
'category_name' => $journal['category_name'],
'description' => $journal['description'],
'transaction_group_id' => $journal['transaction_group_id'],
'date' => $journal['date'],
];
if (null !== $journal['foreign_amount']) {
$final['foreign_amount'] = app('steam')->negative($journal['foreign_amount']);
$final['foreign_currency_id'] = $journal['foreign_currency_id'];
$final['foreign_currency_code'] = $journal['foreign_currency_code'];
$final['foreign_currency_symbol'] = $journal['foreign_currency_symbol'];
$final['foreign_currency_name'] = $journal['foreign_currency_name'];
$final['foreign_currency_decimal_places'] = $journal['foreign_currency_decimal_places'];
}
$array[$currencyId]['budgets'][$budgetId]['transaction_journals'][$journalId] = $final;
}
return $array;
}
private function getBudgets(): Collection
{
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$repository->setUserGroup($this->getUserGroup());
return $repository->getActiveBudgets();
}
}

View File

@@ -1,43 +0,0 @@
<?php
/*
* OperationsRepositoryInterface.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Budget;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Interface OperationsRepositoryInterface
*
* @deprecated
*/
interface OperationsRepositoryInterface
{
/**
* This method returns a list of all the withdrawal transaction journals (as arrays) set in that period
* which have the specified budget set to them. It's grouped per currency, with as few details in the array
* as possible. Amounts are always negative.
*/
public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array;
}

View File

@@ -1,58 +0,0 @@
<?php
/*
* CategoryRepository.php
* Copyright (c) 2024 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Category;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
/**
* Class CategoryRepository
*
* @deprecated
*/
class CategoryRepository implements CategoryRepositoryInterface
{
use UserGroupTrait;
public function searchCategory(array $query, int $limit): Collection
{
$search = $this->userGroup->categories();
if (count($query) > 0) {
// split query on spaces just in case:
$search->where(function (EloquentBuilder $q) use ($query): void {
foreach ($query as $line) {
$parts = explode(' ', $line);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$q->orWhereLike('name', $search);
}
}
});
}
return $search->take($limit)->get();
}
}

View File

@@ -1,40 +0,0 @@
<?php
/*
* CategoryRepositoryInterface.php
* Copyright (c) 2024 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Category;
use Illuminate\Support\Collection;
/**
* Interface CategoryRepositoryInterface
*
* @deprecated
*/
interface CategoryRepositoryInterface
{
/**
* Search for a category using wild cards. Uses the database, so case sensitive.
*/
public function searchCategory(array $query, int $limit): Collection;
}

View File

@@ -1,392 +0,0 @@
<?php
/*
* CurrencyRepository.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Currency;
use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Bill;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Services\Internal\Destroy\CurrencyDestroyService;
use FireflyIII\Services\Internal\Update\CurrencyUpdateService;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use function Safe\json_encode;
/**
* Class CurrencyRepository
*
* @deprecated
*/
class CurrencyRepository implements CurrencyRepositoryInterface
{
use UserGroupTrait;
/**
* @throws FireflyException
*/
public function currencyInUse(TransactionCurrency $currency): bool
{
$result = $this->currencyInUseAt($currency);
return null !== $result;
}
/**
* @throws FireflyException
*/
public function currencyInUseAt(TransactionCurrency $currency): ?string
{
Log::debug(sprintf('Now in currencyInUse() for #%d ("%s")', $currency->id, $currency->code));
$countJournals = $this->countJournals($currency);
if ($countJournals > 0) {
Log::info(sprintf('Count journals is %d, return true.', $countJournals));
return 'journals';
}
// is the only currency left
if (1 === $this->getAll()->count()) {
Log::info('Is the last currency in the system, return true. ');
return 'last_left';
}
// is being used in accounts:
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((string) $currency->id))->count();
if ($meta > 0) {
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
return 'account_meta';
}
// second search using integer check.
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((int) $currency->id))->count();
if ($meta > 0) {
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
return 'account_meta';
}
// is being used in bills:
$bills = Bill::where('transaction_currency_id', $currency->id)->count();
if ($bills > 0) {
Log::info(sprintf('Used in %d bills as currency, return true. ', $bills));
return 'bills';
}
// is being used in recurring transactions
$recurringAmount = RecurrenceTransaction::where('transaction_currency_id', $currency->id)->count();
$recurringForeign = RecurrenceTransaction::where('foreign_currency_id', $currency->id)->count();
if ($recurringAmount > 0 || $recurringForeign > 0) {
Log::info(sprintf('Used in %d recurring transactions as (foreign) currency id, return true. ', $recurringAmount + $recurringForeign));
return 'recurring';
}
// is being used in accounts (as integer)
$meta = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id')
->whereNull('accounts.deleted_at')
->where('account_meta.name', 'currency_id')->where('account_meta.data', json_encode($currency->id))->count()
;
if ($meta > 0) {
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
return 'account_meta';
}
// is being used in available budgets
$availableBudgets = AvailableBudget::where('transaction_currency_id', $currency->id)->count();
if ($availableBudgets > 0) {
Log::info(sprintf('Used in %d available budgets as currency, return true. ', $availableBudgets));
return 'available_budgets';
}
// is being used in budget limits
$budgetLimit = BudgetLimit::where('transaction_currency_id', $currency->id)->count();
if ($budgetLimit > 0) {
Log::info(sprintf('Used in %d budget limits as currency, return true. ', $budgetLimit));
return 'budget_limits';
}
// is the default currency for the user or the system
$count = $this->userGroup->currencies()->where('transaction_currencies.id', $currency->id)->wherePivot('group_default', 1)->count();
if ($count > 0) {
Log::info('Is the default currency of the user, return true.');
return 'current_default';
}
// is the default currency for the user or the system
$count = $this->userGroup->currencies()->where('transaction_currencies.id', $currency->id)->wherePivot('group_default', 1)->count();
if ($count > 0) {
Log::info('Is the default currency of the user group, return true.');
return 'current_default';
}
Log::debug('Currency is not used, return false.');
return null;
}
private function countJournals(TransactionCurrency $currency): int
{
$count = $currency->transactions()->whereNull('deleted_at')->count() + $currency->transactionJournals()->whereNull('deleted_at')->count();
// also count foreign:
return $count + Transaction::where('foreign_currency_id', $currency->id)->count();
}
/**
* Returns ALL currencies, regardless of whether they are enabled or not.
*/
public function getAll(): Collection
{
$all = TransactionCurrency::orderBy('code', 'ASC')->get();
$local = $this->get();
return $all->map(static function (TransactionCurrency $current) use ($local) {
$hasId = $local->contains(static fn (TransactionCurrency $entry) => $entry->id === $current->id);
$isPrimary = $local->contains(static fn (TransactionCurrency $entry) => 1 === (int) $entry->pivot->group_default && $entry->id === $current->id);
$current->userGroupEnabled = $hasId;
$current->userGroupNative = $isPrimary;
return $current;
});
}
public function get(): Collection
{
$all = $this->userGroup->currencies()->orderBy('code', 'ASC')->withPivot(['group_default'])->get();
$all->map(static function (TransactionCurrency $current) { // @phpstan-ignore-line
$current->userGroupEnabled = true;
$current->userGroupNative = 1 === (int) $current->pivot->group_default;
return $current;
});
/** @var Collection */
return $all;
}
public function destroy(TransactionCurrency $currency): bool
{
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
if ($repository->hasRole($this->user, 'owner')) {
/** @var CurrencyDestroyService $service */
$service = app(CurrencyDestroyService::class);
$service->destroy($currency);
}
return true;
}
/**
* Disables a currency
*/
public function disable(TransactionCurrency $currency): void
{
$this->userGroup->currencies()->detach($currency->id);
$currency->enabled = false;
$currency->save();
}
public function findByName(string $name): ?TransactionCurrency
{
return TransactionCurrency::where('name', $name)->first();
}
/**
* Find by object, ID or code. Returns user default or system default.
*
* @throws FireflyException
*/
public function findCurrency(?int $currencyId, ?string $currencyCode): TransactionCurrency
{
$result = $this->findCurrencyNull($currencyId, $currencyCode);
if (!$result instanceof TransactionCurrency) {
Log::debug('Grabbing default currency for this user...');
/** @var null|TransactionCurrency $result */
$result = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup);
}
Log::debug(sprintf('Final result: %s', $result->code));
if (false === $result->enabled) {
Log::debug(sprintf('Also enabled currency %s', $result->code));
$this->enable($result);
}
return $result;
}
/**
* Find by object, ID or code. Returns NULL if nothing found.
*/
public function findCurrencyNull(?int $currencyId, ?string $currencyCode): ?TransactionCurrency
{
Log::debug(sprintf('Now in findCurrencyNull("%s", "%s")', $currencyId, $currencyCode));
$result = $this->find((int) $currencyId);
if (!$result instanceof TransactionCurrency) {
Log::debug(sprintf('Searching for currency with code "%s"...', $currencyCode));
$result = $this->findByCode((string) $currencyCode);
}
if ($result instanceof TransactionCurrency && false === $result->enabled) {
Log::debug(sprintf('Also enabled currency %s', $result->code));
$this->enable($result);
}
return $result;
}
/**
* Find by ID, return NULL if not found.
*/
public function find(int $currencyId): ?TransactionCurrency
{
return TransactionCurrency::find($currencyId);
}
/**
* Find by currency code, return NULL if unfound.
*/
public function findByCode(string $currencyCode): ?TransactionCurrency
{
return TransactionCurrency::where('code', $currencyCode)->first();
}
public function enable(TransactionCurrency $currency): void
{
$this->userGroup->currencies()->syncWithoutDetaching([$currency->id]);
$currency->enabled = false;
$currency->save();
}
public function getByIds(array $ids): Collection
{
return TransactionCurrency::orderBy('code', 'ASC')->whereIn('id', $ids)->get();
}
public function isFallbackCurrency(TransactionCurrency $currency): bool
{
return $currency->code === config('firefly.default_currency', 'EUR');
}
public function searchCurrency(string $search, int $limit): Collection
{
$query = TransactionCurrency::where('enabled', true);
if ('' !== $search) {
$query->whereLike('name', sprintf('%%%s%%', $search));
}
return $query->take($limit)->get();
}
/**
* @throws FireflyException
*/
public function store(array $data): TransactionCurrency
{
/** @var TransactionCurrencyFactory $factory */
$factory = app(TransactionCurrencyFactory::class);
$result = $factory->create($data);
if (true === $data['enabled']) {
$this->userGroup->currencies()->attach($result->id);
}
return $result;
}
public function update(TransactionCurrency $currency, array $data): TransactionCurrency
{
Log::debug('Now in update()');
// can be true, false, null
$enabled = array_key_exists('enabled', $data) ? $data['enabled'] : null;
// can be true, false, but method only responds to "true".
$default = array_key_exists('default', $data) ? $data['default'] : false;
// remove illegal combo's:
if (false === $enabled && true === $default) {
$enabled = true;
}
// update currency with current user specific settings
$currency->refreshForUser($this->user);
// currency is enabled, must be disabled.
if (false === $enabled) {
Log::debug(sprintf('Disabled currency %s for user #%d', $currency->code, $this->userGroup->id));
$this->userGroup->currencies()->detach($currency->id);
}
// currency must be enabled
if (true === $enabled) {
Log::debug(sprintf('Enabled currency %s for user #%d', $currency->code, $this->userGroup->id));
$this->userGroup->currencies()->detach($currency->id);
$this->userGroup->currencies()->syncWithoutDetaching([$currency->id => ['group_default' => false]]);
}
// currency must be made default.
if (true === $default) {
$this->makePrimary($currency);
}
/** @var CurrencyUpdateService $service */
$service = app(CurrencyUpdateService::class);
return $service->update($currency, $data);
}
public function makePrimary(TransactionCurrency $currency): void
{
$current = app('amount')->getPrimaryCurrencyByUserGroup($this->userGroup);
Log::debug(sprintf('Enabled + made default currency %s for user #%d', $currency->code, $this->userGroup->id));
$this->userGroup->currencies()->detach($currency->id);
foreach ($this->userGroup->currencies()->get() as $item) {
$this->userGroup->currencies()->updateExistingPivot($item->id, ['group_default' => false]);
}
$this->userGroup->currencies()->syncWithoutDetaching([$currency->id => ['group_default' => true]]);
if ($current->id !== $currency->id) {
Log::debug('Trigger on a different default currency.');
// clear all primary currency amounts through an event.
event(new UserGroupChangedPrimaryCurrency($this->userGroup));
}
}
}

View File

@@ -1,102 +0,0 @@
<?php
/*
* CurrencyRepositoryInterface.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Currency;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use Illuminate\Support\Collection;
/**
* Interface CurrencyRepositoryInterface
*
* @deprecated
*/
interface CurrencyRepositoryInterface
{
public function currencyInUse(TransactionCurrency $currency): bool;
/**
* Currency is in use where exactly.
*/
public function currencyInUseAt(TransactionCurrency $currency): ?string;
public function destroy(TransactionCurrency $currency): bool;
/**
* Disables a currency
*/
public function disable(TransactionCurrency $currency): void;
/**
* Enables a currency
*/
public function enable(TransactionCurrency $currency): void;
/**
* Find by ID, return NULL if not found.
*/
public function find(int $currencyId): ?TransactionCurrency;
public function findByCode(string $currencyCode): ?TransactionCurrency;
public function findByName(string $name): ?TransactionCurrency;
/**
* Find by object, ID or code. Returns user default or system default.
*/
public function findCurrency(?int $currencyId, ?string $currencyCode): TransactionCurrency;
/**
* Find by object, ID or code. Returns NULL if nothing found.
*/
public function findCurrencyNull(?int $currencyId, ?string $currencyCode): ?TransactionCurrency;
/**
* Get the user group's currencies.
*
* @return Collection<TransactionCurrency>
*/
public function get(): Collection;
/**
* Get ALL currencies.
*/
public function getAll(): Collection;
public function getByIds(array $ids): Collection;
public function isFallbackCurrency(TransactionCurrency $currency): bool;
public function makePrimary(TransactionCurrency $currency): void;
public function searchCurrency(string $search, int $limit): Collection;
/**
* @throws FireflyException
*/
public function store(array $data): TransactionCurrency;
public function update(TransactionCurrency $currency, array $data): TransactionCurrency;
}

View File

@@ -1,119 +0,0 @@
<?php
/*
* ExchangeRateRepository.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\ExchangeRate;
use Carbon\Carbon;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Override;
/**
* Class ExchangeRateRepository
*
* @deprecated
*/
class ExchangeRateRepository implements ExchangeRateRepositoryInterface
{
use UserGroupTrait;
#[Override]
public function deleteRate(CurrencyExchangeRate $rate): void
{
$this->userGroup->currencyExchangeRates()->where('id', $rate->id)->delete();
}
#[Override]
public function getAll(): Collection
{
return $this->userGroup->currencyExchangeRates()->orderBy('date', 'ASC')->get();
}
#[Override]
public function getRates(TransactionCurrency $from, TransactionCurrency $to): Collection
{
// orderBy('date', 'DESC')->toRawSql();
return
$this->userGroup->currencyExchangeRates()
->where(function (Builder $q1) use ($from, $to): void {
$q1->where(function (Builder $q) use ($from, $to): void {
$q->where('from_currency_id', $from->id)
->where('to_currency_id', $to->id)
;
})->orWhere(function (Builder $q) use ($from, $to): void {
$q->where('from_currency_id', $to->id)
->where('to_currency_id', $from->id)
;
});
})
->orderBy('date', 'DESC')
->get(['currency_exchange_rates.*'])
;
}
#[Override]
public function getSpecificRateOnDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): ?CurrencyExchangeRate
{
/** @var null|CurrencyExchangeRate */
return
$this->userGroup->currencyExchangeRates()
->where('from_currency_id', $from->id)
->where('to_currency_id', $to->id)
->where('date', $date->format('Y-m-d'))
->first()
;
}
#[Override]
public function storeExchangeRate(TransactionCurrency $from, TransactionCurrency $to, string $rate, Carbon $date): CurrencyExchangeRate
{
$object = new CurrencyExchangeRate();
$object->user_id = auth()->user()->id;
$object->user_group_id = $this->userGroup->id;
$object->from_currency_id = $from->id;
$object->to_currency_id = $to->id;
$object->rate = $rate;
$object->date = $date;
$object->date_tz = $date->format('e');
$object->save();
return $object;
}
#[Override]
public function updateExchangeRate(CurrencyExchangeRate $object, string $rate, ?Carbon $date = null): CurrencyExchangeRate
{
$object->rate = $rate;
if ($date instanceof Carbon) {
$object->date = $date;
}
$object->save();
return $object;
}
}

View File

@@ -1,50 +0,0 @@
<?php
/*
* ExchangeRateRepositoryInterface.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\ExchangeRate;
use Carbon\Carbon;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use Illuminate\Support\Collection;
/**
* Interface ExchangeRateRepositoryInterface
*
* @deprecated
*/
interface ExchangeRateRepositoryInterface
{
public function deleteRate(CurrencyExchangeRate $rate): void;
public function getAll(): Collection;
public function getRates(TransactionCurrency $from, TransactionCurrency $to): Collection;
public function getSpecificRateOnDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): ?CurrencyExchangeRate;
public function storeExchangeRate(TransactionCurrency $from, TransactionCurrency $to, string $rate, Carbon $date): CurrencyExchangeRate;
public function updateExchangeRate(CurrencyExchangeRate $object, string $rate, ?Carbon $date = null): CurrencyExchangeRate;
}

View File

@@ -1,60 +0,0 @@
<?php
/*
* JournalRepository.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Journal;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
/**
* Class JournalRepository
*
* @deprecated
*/
class JournalRepository implements JournalRepositoryInterface
{
use UserGroupTrait;
public function searchJournalDescriptions(array $query, int $limit): Collection
{
$search = $this->userGroup->transactionJournals()
->orderBy('date', 'DESC')
;
if (count($query) > 0) {
// split query on spaces just in case:
$search->where(function (EloquentBuilder $q) use ($query): void {
foreach ($query as $line) {
$parts = explode(' ', $line);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$q->orWhereLike('description', $search);
}
}
});
}
return $search->take($limit)->get();
}
}

View File

@@ -1,40 +0,0 @@
<?php
/*
* JournalRepositoryInterface.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Journal;
use Illuminate\Support\Collection;
/**
* Interface JournalRepositoryInterface
*
* @deprecated
*/
interface JournalRepositoryInterface
{
/**
* Search in journal descriptions.
*/
public function searchJournalDescriptions(array $query, int $limit): Collection;
}

View File

@@ -1,53 +0,0 @@
<?php
/*
* PiggyBankRepository.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\PiggyBank;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
/**
* Class PiggyBankRepository
*
* @deprecated
*/
class PiggyBankRepository implements PiggyBankRepositoryInterface
{
use UserGroupTrait;
public function getPiggyBanks(): Collection
{
return PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
->where('accounts.user_group_id', $this->userGroup->id)
->with(
[
'objectGroups',
]
)
->orderBy('piggy_banks.order', 'ASC')->distinct()->get(['piggy_banks.*'])
;
}
}

View File

@@ -1,40 +0,0 @@
<?php
/*
* PiggyBankRepositoryInterface.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\PiggyBank;
use Illuminate\Support\Collection;
/**
* Interface PiggyBankRepositoryInterface
*
* @deprecated
*/
interface PiggyBankRepositoryInterface
{
/**
* Return all piggy banks.
*/
public function getPiggyBanks(): Collection;
}

View File

@@ -1,58 +0,0 @@
<?php
/*
* TagRepository.php
* Copyright (c) 2024 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Tag;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
/**
* Class TagRepository
*
* @deprecated
*/
class TagRepository implements TagRepositoryInterface
{
use UserGroupTrait;
public function searchTag(array $query, int $limit): Collection
{
$search = $this->userGroup->tags();
if (count($query) > 0) {
// split query on spaces just in case:
$search->where(function (EloquentBuilder $q) use ($query): void {
foreach ($query as $line) {
$parts = explode(' ', $line);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$q->orWhereLike('tag', $search);
}
}
});
}
return $search->take($limit)->get(['tags.*']);
}
}

View File

@@ -1,40 +0,0 @@
<?php
/*
* TagRepositoryInterface.php
* Copyright (c) 2024 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Tag;
use Illuminate\Support\Collection;
/**
* Interface TagRepositoryInterface
*
* @deprecated
*/
interface TagRepositoryInterface
{
/**
* Find one or more tags based on the query.
*/
public function searchTag(array $query, int $limit): Collection;
}

View File

@@ -28,12 +28,7 @@ use Illuminate\Contracts\Validation\ValidationRule;
class IsValidSortInstruction implements ValidationRule
{
private string $class;
public function __construct(string $class)
{
$this->class = $class;
}
public function __construct(private readonly string $class) {}
public function validate(string $attribute, mixed $value, Closure $fail): void
{

View File

@@ -221,13 +221,7 @@ class JournalUpdateService
private function hasFields(array $fields): bool
{
foreach ($fields as $field) {
if (array_key_exists($field, $this->data)) {
return true;
}
}
return false;
return array_any($fields, fn ($field) => array_key_exists($field, $this->data));
}
private function getOriginalSourceAccount(): Account

View File

@@ -60,7 +60,7 @@ class AccountEnrichment implements EnrichmentInterface
private array $currencies = [];
private array $locations = [];
private array $meta = [];
private TransactionCurrency $primaryCurrency;
private readonly TransactionCurrency $primaryCurrency;
private array $notes = [];
private array $openingBalances = [];
private User $user;
@@ -69,7 +69,7 @@ class AccountEnrichment implements EnrichmentInterface
private ?Carbon $date = null;
private ?Carbon $start = null;
private ?Carbon $end = null;
private bool $convertToPrimary;
private readonly bool $convertToPrimary;
private array $balances = [];
private array $startBalances = [];
private array $endBalances = [];
@@ -365,7 +365,7 @@ class AccountEnrichment implements EnrichmentInterface
private function collectBalances(): void
{
$this->balances = Steam::accountsBalancesOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary);
if (null !== $this->start && null !== $this->end) {
if ($this->start instanceof Carbon && $this->end instanceof Carbon) {
$this->startBalances = Steam::accountsBalancesOptimized($this->collection, $this->start, $this->primaryCurrency, $this->convertToPrimary);
$this->endBalances = Steam::accountsBalancesOptimized($this->collection, $this->end, $this->primaryCurrency, $this->convertToPrimary);
}
@@ -395,7 +395,7 @@ class AccountEnrichment implements EnrichmentInterface
public function setDate(?Carbon $date): void
{
if (null !== $date) {
if ($date instanceof Carbon) {
$date->endOfDay();
Log::debug(sprintf('Date is now %s', $date->toW3cString()));
}
@@ -404,7 +404,7 @@ class AccountEnrichment implements EnrichmentInterface
public function getDate(): Carbon
{
if (null === $this->date) {
if (!$this->date instanceof Carbon) {
return now();
}
@@ -423,7 +423,7 @@ class AccountEnrichment implements EnrichmentInterface
private function getBalanceDifference(int $id, TransactionCurrency $currency): ?string
{
if (null === $this->start || null === $this->end) {
if (!$this->start instanceof Carbon || !$this->end instanceof Carbon) {
return null;
}
$startBalance = $this->startBalances[$id] ?? [];
@@ -458,9 +458,7 @@ class AccountEnrichment implements EnrichmentInterface
case 'current_balance':
case 'pc_current_balance':
$this->collection = $this->collection->sortBy(static function (Account $account) use ($parameter) {
return $account->meta['balances'][$parameter[0]] ?? '0';
}, SORT_NUMERIC, 'desc' === $parameter[1]);
$this->collection = $this->collection->sortBy(static fn (Account $account) => $account->meta['balances'][$parameter[0]] ?? '0', SORT_NUMERIC, 'desc' === $parameter[1]);
break;
}

View File

@@ -42,7 +42,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
{
private User $user; // @phpstan-ignore-line
private UserGroup $userGroup; // @phpstan-ignore-line
private bool $convertToPrimary;
private readonly bool $convertToPrimary;
private array $ids = [];
private array $currencyIds = [];
private array $currencies = [];

View File

@@ -161,7 +161,7 @@ class BudgetEnrichment implements EnrichmentInterface
private function collectExpenses(): void
{
if (null !== $this->start && null !== $this->end) {
if ($this->start instanceof Carbon && $this->end instanceof Carbon) {
/** @var OperationsRepositoryInterface $opsRepository */
$opsRepository = app(OperationsRepositoryInterface::class);
$opsRepository->setUser($this->user);

View File

@@ -53,7 +53,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
private array $currencyIds = [];
private array $currencies = [];
private bool $convertToPrimary = true;
private TransactionCurrency $primaryCurrency;
private readonly TransactionCurrency $primaryCurrency;
public function __construct()
{
@@ -181,27 +181,21 @@ class BudgetLimitEnrichment implements EnrichmentInterface
private function stringifyIds(): void
{
$this->expenses = array_map(function ($first) {
return array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
$this->expenses = array_map(fn ($first) => array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
return $second;
}, $first);
}, $this->expenses);
return $second;
}, $first), $this->expenses);
$this->pcExpenses = array_map(function ($first) {
return array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
$this->pcExpenses = array_map(fn ($first) => array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
return $second;
}, $first);
}, $this->expenses);
return $second;
}, $first), $this->expenses);
}
private function filterToBudget(array $expenses, int $budget): array
{
return array_filter($expenses, function (array $item) use ($budget) {
return (int)$item['budget_id'] === $budget;
});
return array_filter($expenses, fn (array $item) => (int)$item['budget_id'] === $budget);
}
}

View File

@@ -53,7 +53,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
// private array $accountCurrencies = [];
private array $notes = [];
private array $mappedObjects = [];
private TransactionCurrency $primaryCurrency;
private readonly TransactionCurrency $primaryCurrency;
private array $amounts = [];
private array $accounts = [];
private array $objectGroups = [];
@@ -271,7 +271,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
*/
private function getSuggestedMonthlyAmount(?Carbon $startDate, ?Carbon $targetDate, ?string $targetAmount, string $currentAmount): string
{
if (null === $targetAmount || null === $targetDate || null === $startDate) {
if (null === $targetAmount || !$targetDate instanceof Carbon || !$startDate instanceof Carbon) {
return '0';
}
$savePerMonth = '0';

View File

@@ -27,7 +27,6 @@ namespace FireflyIII\Support\JsonApi\Enrichments;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\UserGroup;
use FireflyIII\Support\Facades\Amount;

View File

@@ -49,7 +49,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
private User $user;
private UserGroup $userGroup; // @phpstan-ignore-line
private Collection $collection;
private bool $convertToPrimary;
private readonly bool $convertToPrimary;
private ?Carbon $start = null;
private ?Carbon $end = null;
private array $subscriptionIds = [];
@@ -58,7 +58,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
private array $paidDates = [];
private array $notes = [];
private array $payDates = [];
private TransactionCurrency $primaryCurrency;
private readonly TransactionCurrency $primaryCurrency;
private BillDateCalculator $calculator;
public function __construct()
@@ -210,7 +210,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
{
$this->paidDates = [];
Log::debug('Now in collectPaidDates for bills');
if (null === $this->start || null === $this->end) {
if (!$this->start instanceof Carbon || !$this->end instanceof Carbon) {
Log::debug('Parameters are NULL, set empty array');
return;
@@ -274,9 +274,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
// At this point the "next match" is exactly after the last time the bill was paid.
$result = [];
$filtered = $set->filter(function (TransactionJournal $journal) use ($subscription) {
return (int)$journal->bill_id === (int)$subscription->id;
});
$filtered = $set->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id);
foreach ($filtered as $entry) {
$array = [
'transaction_group_id' => (string)$entry->transaction_group_id,
@@ -346,9 +344,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
*/
protected function lastPaidDate(Bill $subscription, Collection $dates, Carbon $default): Carbon
{
$filtered = $dates->filter(function (TransactionJournal $journal) use ($subscription) {
return (int)$journal->bill_id === (int)$subscription->id;
});
$filtered = $dates->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id);
Log::debug(sprintf('Filtered down from %d to %d entries for bill #%d.', $dates->count(), $filtered->count(), $subscription->id));
if (0 === $filtered->count()) {
return $default;
@@ -392,7 +388,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
private function collectPayDates(): void
{
if (null === $this->start || null === $this->end) {
if (!$this->start instanceof Carbon || !$this->end instanceof Carbon) {
Log::debug('Parameters are NULL, set empty array');
return;
@@ -440,8 +436,8 @@ class SubscriptionEnrichment implements EnrichmentInterface
// nullify again when it's outside the current view range.
if (
(null !== $this->start && $nemDate->lt($this->start))
|| (null !== $this->end && $nemDate->gt($this->end))
($this->start instanceof Carbon && $nemDate->lt($this->start))
|| ($this->end instanceof Carbon && $nemDate->gt($this->end))
) {
$nem = null;
$nemDate = null;
@@ -454,7 +450,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
private function getNextExpectedMatchDiff(?Carbon $nem, array $payDates): string
{
if (null === $nem) {
if (!$nem instanceof Carbon) {
return trans('firefly.not_expected_period');
}
$nemDiff = trans('firefly.not_expected_period');

View File

@@ -72,7 +72,7 @@ trait UserGroupTrait
return;
}
$class = null === $user ? 'NULL' : $user::class;
$class = $user instanceof Authenticatable ? $user::class : 'NULL';
throw new FireflyException(sprintf('Object is %s, not User.', $class));
}

View File

@@ -25,10 +25,10 @@ declare(strict_types=1);
namespace FireflyIII\Support\Request;
use Illuminate\Validation\Validator;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Models\Webhook;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
trait ValidatesWebhooks
{

View File

@@ -348,9 +348,7 @@ class Steam
$currency = $currencies[$account->id];
// second array
$accountSum = array_filter($arrayOfSums, function ($entry) use ($account) {
return $entry['account_id'] === $account->id;
});
$accountSum = array_filter($arrayOfSums, fn ($entry) => $entry['account_id'] === $account->id);
if (0 === count($accountSum)) {
$result[$account->id] = $return;

View File

@@ -34,7 +34,7 @@ use FireflyIII\Support\Facades\Amount;
*/
class PiggyBankTransformer extends AbstractTransformer
{
private TransactionCurrency $primaryCurrency;
private readonly TransactionCurrency $primaryCurrency;
/**
* PiggyBankTransformer constructor.
@@ -53,8 +53,8 @@ class PiggyBankTransformer extends AbstractTransformer
{
// Amounts, depending on 0.0 state of target amount
$percentage = null;
if (null !== $piggyBank->meta['target_amount'] && 0 !== bccomp($piggyBank->meta['current_amount'], '0')) { // target amount is not 0.00
$percentage = (int)bcmul(bcdiv($piggyBank->meta['current_amount'], $piggyBank->meta['target_amount']), '100');
if (null !== $piggyBank->meta['target_amount'] && 0 !== bccomp((string) $piggyBank->meta['current_amount'], '0')) { // target amount is not 0.00
$percentage = (int)bcmul(bcdiv((string) $piggyBank->meta['current_amount'], $piggyBank->meta['target_amount']), '100');
}
$startDate = $piggyBank->start_date?->toAtomString();
$targetDate = $piggyBank->target_date?->toAtomString();

View File

@@ -3,17 +3,13 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 6.4.0 - 2025-09-01
## 6.4.0 - 2025-09-19
### Added
- [Issue 5532](https://github.com/firefly-iii/firefly-iii/issues/5532) (Asset prices and exchange rates) reported by @svozniuk
- [Discussion 10725](https://github.com/orgs/firefly-iii/discussions/10725) (New webhook triggers) started by @Billos. See the [documentation](https://docs.firefly-iii.org/how-to/firefly-iii/features/webhooks/).
### Changed
- Initial release.
### Fixed
- [Issue 10790](https://github.com/firefly-iii/firefly-iii/issues/10790) (Undefined variable $occurrences) reported by @senna1992
@@ -22,12 +18,18 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- [Issue 10799](https://github.com/firefly-iii/firefly-iii/issues/10799) (Budget - "Left (per day)" not showing the correct value) reported by @GensHaze
- [Issue 10802](https://github.com/firefly-iii/firefly-iii/issues/10802) (Crash when trying to update a budget limit) reported by @Billos
- [Issue 10803](https://github.com/firefly-iii/firefly-iii/issues/10803) (Issue in /v1/budget-limits spent attribute) reported by @Billos
- [Issue 10804](https://github.com/firefly-iii/firefly-iii/issues/10804) (No notes information included in the "List all accounts" API call) reported by @gpampuro
- [Issue 10808](https://github.com/firefly-iii/firefly-iii/issues/10808) (cron job Error: Undefined variable $preference) reported by @MexerSam
- [Issue 10813](https://github.com/firefly-iii/firefly-iii/issues/10813) (Error "Argument #2 ($symbol) must be of type string" while try open subscriptions section) reported by @mrResident
- [Issue 10814](https://github.com/firefly-iii/firefly-iii/issues/10814) (Deposit show negative amounts & red numbers in detail view) reported by @dreautall
- [Issue 10819](https://github.com/firefly-iii/firefly-iii/issues/10819) (Internal Server Error when trying to open piggy banks) reported by @noantiq
- [Issue 10820](https://github.com/firefly-iii/firefly-iii/issues/10820) (Unable to search date 1970-01-01 to apply rule.) reported by @Kage1
- [Issue 10824](https://github.com/firefly-iii/firefly-iii/issues/10824) (Converting withdrawal to transfer to account in different currency doesn't allow setting correct currencies) reported by @avee87
- [Issue 10833](https://github.com/firefly-iii/firefly-iii/issues/10833) (Can't open transaction after assigning a tag to it) reported by @zynexiz
- [Issue 10837](https://github.com/firefly-iii/firefly-iii/issues/10837) (Internal Server Erorr when viewing or editing Recurring transaction) reported by @Tommy78649
- [Discussion 10846](https://github.com/orgs/firefly-iii/discussions/10846) (Add support for WebP format in attachments) started by @Idestius
- [Issue 10853](https://github.com/firefly-iii/firefly-iii/issues/10853) (Failed to Send Discord Notification) reported by @pimonteiro
- [Issue 10854](https://github.com/firefly-iii/firefly-iii/issues/10854) (string / null in budget causes budget page to not render) reported by @4e868df3
- [Discussion 10883](https://github.com/orgs/firefly-iii/discussions/10883) (Initial balance for account is missing from exported CSV) started by @ajaskiewiczpl
### API

24
composer.lock generated
View File

@@ -1811,16 +1811,16 @@
},
{
"name": "laravel-notification-channels/pushover",
"version": "4.1.1",
"version": "4.1.2",
"source": {
"type": "git",
"url": "https://github.com/laravel-notification-channels/pushover.git",
"reference": "68697f85a01c5f10168ad0ab05b14ef3e244cec4"
"reference": "53be939273e79e832a417d5863c1d443f0b3c665"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-notification-channels/pushover/zipball/68697f85a01c5f10168ad0ab05b14ef3e244cec4",
"reference": "68697f85a01c5f10168ad0ab05b14ef3e244cec4",
"url": "https://api.github.com/repos/laravel-notification-channels/pushover/zipball/53be939273e79e832a417d5863c1d443f0b3c665",
"reference": "53be939273e79e832a417d5863c1d443f0b3c665",
"shasum": ""
},
"require": {
@@ -1872,9 +1872,9 @@
"homepage": "https://github.com/laravel-notification-channels/pushover",
"support": {
"issues": "https://github.com/laravel-notification-channels/pushover/issues",
"source": "https://github.com/laravel-notification-channels/pushover/tree/4.1.1"
"source": "https://github.com/laravel-notification-channels/pushover/tree/4.1.2"
},
"time": "2025-03-01T09:05:11+00:00"
"time": "2025-09-09T09:14:17+00:00"
},
{
"name": "laravel/framework",
@@ -10738,16 +10738,16 @@
},
{
"name": "larastan/larastan",
"version": "v3.6.1",
"version": "v3.7.0",
"source": {
"type": "git",
"url": "https://github.com/larastan/larastan.git",
"reference": "3c223047e374befd1b64959784685d6ecccf66aa"
"reference": "6b7d28a2762a4b69f0f064e1e5b7358af11f04e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/larastan/larastan/zipball/3c223047e374befd1b64959784685d6ecccf66aa",
"reference": "3c223047e374befd1b64959784685d6ecccf66aa",
"url": "https://api.github.com/repos/larastan/larastan/zipball/6b7d28a2762a4b69f0f064e1e5b7358af11f04e4",
"reference": "6b7d28a2762a4b69f0f064e1e5b7358af11f04e4",
"shasum": ""
},
"require": {
@@ -10815,7 +10815,7 @@
],
"support": {
"issues": "https://github.com/larastan/larastan/issues",
"source": "https://github.com/larastan/larastan/tree/v3.6.1"
"source": "https://github.com/larastan/larastan/tree/v3.7.0"
},
"funding": [
{
@@ -10823,7 +10823,7 @@
"type": "github"
}
],
"time": "2025-08-25T07:24:56+00:00"
"time": "2025-09-09T15:17:27+00:00"
},
{
"name": "laravel-json-api/testing",

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2025-09-07',
'build_time' => 1757249809,
'version' => 'develop/2025-09-10',
'build_time' => 1757479665,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 26,

42
package-lock.json generated
View File

@@ -5711,9 +5711,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.5.214",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.214.tgz",
"integrity": "sha512-TpvUNdha+X3ybfU78NoQatKvQEm1oq3lf2QbnmCEdw+Bd9RuIAY+hJTvq1avzHM0f7EJfnH3vbCnbzKzisc/9Q==",
"version": "1.5.215",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.215.tgz",
"integrity": "sha512-TIvGp57UpeNetj/wV/xpFNpWGb0b/ROw372lHPx5Aafx02gjTBtWnEEcaSX3W2dLM3OSdGGyHX/cHl01JQsLaQ==",
"dev": true,
"license": "ISC"
},
@@ -7969,9 +7969,9 @@
}
},
"node_modules/magic-string": {
"version": "0.30.18",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
"integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
"version": "0.30.19",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
"integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9923,9 +9923,9 @@
"license": "MIT"
},
"node_modules/regenerate-unicode-properties": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz",
"integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==",
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
"integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9936,18 +9936,18 @@
}
},
"node_modules/regexpu-core": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz",
"integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.3.0.tgz",
"integrity": "sha512-ulzJYRb0qgR4t8eTgHeL7nnKL/4ul2yjnuTBEDIpYG7cSs8CcADE1q18RFFChXLP8WwRgPrHThGbYplvASdujw==",
"dev": true,
"license": "MIT",
"dependencies": {
"regenerate": "^1.4.2",
"regenerate-unicode-properties": "^10.2.0",
"regenerate-unicode-properties": "^10.2.2",
"regjsgen": "^0.8.0",
"regjsparser": "^0.12.0",
"unicode-match-property-ecmascript": "^2.0.0",
"unicode-match-property-value-ecmascript": "^2.1.0"
"unicode-match-property-value-ecmascript": "^2.2.1"
},
"engines": {
"node": ">=4"
@@ -11377,9 +11377,9 @@
}
},
"node_modules/unicode-match-property-value-ecmascript": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz",
"integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
"integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -11543,9 +11543,9 @@
}
},
"node_modules/vite": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.4.tgz",
"integrity": "sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==",
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz",
"integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11554,7 +11554,7 @@
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
"rollup": "^4.43.0",
"tinyglobby": "^0.2.14"
"tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"

View File

@@ -116,9 +116,9 @@
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u039b\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_RELEVANT": "\u03a3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ad\u03c2 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2",
"webhook_response_ACCOUNTS": "\u03a0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd",
"webhook_response_NONE": "No details",
"webhook_response_NONE": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03bd \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2",
"webhook_delivery_JSON": "JSON",
"actions": "\u0395\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b5\u03c2",
"meta_data": "\u039c\u03b5\u03c4\u03b1-\u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1",

View File

@@ -87,11 +87,16 @@ let show = function () {
if (this.entries.hasOwnProperty(i)) {
const currencyCode = this.entries[i].currency_code;
const foreignCurrencyCode = this.entries[i].foreign_currency_code;
const primaryCurrencyCode = this.entries[i].primary_currency_code;
if (undefined === this.amounts[currencyCode]) {
this.amounts[currencyCode] = 0;
this.amounts[currencyCode] += parseFloat(this.entries[i].amount);
}
if (undefined === this.amounts[primaryCurrencyCode]) {
this.amounts[primaryCurrencyCode] = 0;
this.amounts[primaryCurrencyCode] += parseFloat(this.entries[i].pc_amount);
}
if (null !== foreignCurrencyCode && '' !== foreignCurrencyCode && undefined === this.amounts[foreignCurrencyCode]) {
this.amounts[foreignCurrencyCode] = 0;
this.amounts[foreignCurrencyCode] += parseFloat(this.entries[i].foreign_amount);

View File

@@ -17,23 +17,23 @@
<tbody>
<tr>
<th style="width:10%;">
<template x-if="'Withdrawal' === groupProperties.transactionType">
<template x-if="'withdrawal' === groupProperties.transactionType">
<em class="fa fa-solid fa-arrow-left"
:title="groupProperties.transactionTypeTranslated"></em>
</template>
<template x-if="'Deposit' === groupProperties.transactionType">
<template x-if="'deposit' === groupProperties.transactionType">
<em class="fa-solid fa-arrow-right"
:title="groupProperties.transactionTypeTranslated"></em>
</template>
<template x-if="'Transfer' === groupProperties.transactionType">
<template x-if="'transfer' === groupProperties.transactionType">
<em class="fa-solid fa-rotate"
:title="groupProperties.transactionTypeTranslated"></em>
</template>
<template
x-if="'Transfer' !== groupProperties.transactionType && 'Deposit' !== groupProperties.transactionType && 'Withdrawal' !== groupProperties.transactionType">
<span>TODO missing ICON</span>
x-if="'transfer' !== groupProperties.transactionType && 'deposit' !== groupProperties.transactionType && 'withdrawal' !== groupProperties.transactionType">
<span x-text="groupProperties.transactionType"></span>
</template>
</th>
<td><span class="group_title" :data-group="groupProperties.id" x-text="groupProperties.title"></span></td>
@@ -53,6 +53,18 @@
<a class="btn btn-danger" :href="'./transactions/delete/' + groupProperties.id">
<em class="fa-solid fa-trash"></em> {{ __('firefly.delete') }}
</a>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
{{ __('firefly.actions') }}
</button>
<ul class="dropdown-menu">
<li x-show="'withdrawal' !== groupProperties.transactionType"><a class="dropdown-item" href="{{ route('transactions.convert.index', ['withdrawal', $transactionGroup['id']]) }}">{{ __('firefly.convert_to_withdrawal') }}</a></li>
<li x-show="'deposit' !== groupProperties.transactionType"><a class="dropdown-item" href="{{ route('transactions.convert.index', ['deposit', $transactionGroup['id']]) }}">{{ __('firefly.convert_to_deposit') }}</a></li>
<li x-show="'transfer' !== groupProperties.transactionType"><a class="dropdown-item" href="{{ route('transactions.convert.index', ['transfer', $transactionGroup['id']]) }}">{{ __('firefly.convert_to_transfer') }}</a></li>
<li><a class="dropdown-item" href="#">{{ __('firefly.clone') }}</a></li>
<li><a class="dropdown-item" href="#">{{ __('firefly.clone_and_edit') }}</a></li>
</ul>
</div>
</div>
</div>
</div>
@@ -70,25 +82,25 @@
<em class="fa-solid fa-money-bill-wave" title="{{ __('firefly.amount') }}"></em>
</th>
<td>
<template x-if="'Withdrawal' === groupProperties.transactionType">
<template x-if="'withdrawal' === groupProperties.transactionType">
<template x-for="(amount, code) in amounts">
<span class="text-danger" x-text="formatMoney(amount*-1, code)"></span>
</template>
</template>
<template x-if="'Deposit' === groupProperties.transactionType">
<template x-if="'deposit' === groupProperties.transactionType">
<template x-for="(amount, code) in amounts">
<span class="text-success" x-text="formatMoney(amount, code)"></span>
</template>
</template>
<template x-if="'Transfer' === groupProperties.transactionType">
<template x-if="'transfer' === groupProperties.transactionType">
<template x-for="(amount, code) in amounts">
<span class="text-info" x-text="formatMoney(amount, code)"></span>
</template>
</template>
<template
x-if="'Transfer' !== groupProperties.transactionType && 'Deposit' !== groupProperties.transactionType && 'Withdrawal' !== groupProperties.transactionType">
x-if="'transfer' !== groupProperties.transactionType && 'deposit' !== groupProperties.transactionType && 'withdrawal' !== groupProperties.transactionType">
<span>TODO PARSE MISSING AMOUNT</span>
</template>
</td>
@@ -164,33 +176,33 @@
:title="entry.source_account.name"
x-text="entry.source_account.name"></a>
&rarr;
<template x-if="'Withdrawal' === groupProperties.transactionType">
<template x-if="'withdrawal' === groupProperties.transactionType">
<span class="text-danger"
x-text="formatMoney(entry.amount*-1, entry.currency_code)"></span>
</template>
<template x-if="'Deposit' === groupProperties.transactionType">
<template x-if="'deposit' === groupProperties.transactionType">
<span class="text-success"
x-text="formatMoney(entry.amount, entry.currency_code)"></span>
</template>
<template x-if="'Transfer' === groupProperties.transactionType">
<template x-if="'transfer' === groupProperties.transactionType">
<span class="text-info"
x-text="formatMoney(entry.amount, entry.currency_code)"></span>
</template>
<template
x-if="null !== entry.foreign_currency_code && 'Withdrawal' === groupProperties.transactionType">
x-if="null !== entry.foreign_currency_code && '' !== entry.foreign_currency_code && 'withdrawal' === groupProperties.transactionType">
<span class="text-muted"
x-text="formatMoney(entry.foreign_amount*-1, entry.foreign_currency_code)"></span>
x-text="formatMoney(entry.foreign_amount*-1, entry.foreign_currency_code) + 'y'"></span>
</template>
<template
x-if="null !== entry.foreign_currency_code && 'Withdrawal' !== groupProperties.transactionType">
x-if="null !== entry.foreign_currency_code && '' !== entry.foreign_currency_code && 'withdrawal' !== groupProperties.transactionType">
<span class="text-muted"
x-text="formatMoney(entry.foreign_amount, entry.foreign_currency_code)"></span>
x-text="formatMoney(entry.foreign_amount, entry.foreign_currency_code) + 'x'"></span>
</template>
<template
x-if="'Transfer' !== groupProperties.transactionType && 'Deposit' !== groupProperties.transactionType && 'Withdrawal' !== groupProperties.transactionType">
x-if="'transfer' !== groupProperties.transactionType && 'deposit' !== groupProperties.transactionType && 'withdrawal' !== groupProperties.transactionType">
<span>TODO PARSE MISSING AMOUNT</span>
</template>
&rarr;

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace Tests\integration\Api\Chart;
use Override;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;
@@ -39,7 +40,6 @@ final class AccountControllerTest extends TestCase
private $user;
#[Override]
#[\Override]
protected function setUp(): void
{
parent::setUp();

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace Tests\integration\Api\Chart;
use Override;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;
@@ -39,7 +40,6 @@ final class BalanceControllerTest extends TestCase
private $user;
#[Override]
#[\Override]
protected function setUp(): void
{
parent::setUp();

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace Tests\integration\Api\Chart;
use Override;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;
@@ -39,7 +40,6 @@ final class BudgetControllerTest extends TestCase
private $user;
#[Override]
#[\Override]
protected function setUp(): void
{
parent::setUp();

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace Tests\integration\Api\Chart;
use Override;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;
@@ -39,7 +40,6 @@ final class CategoryControllerTest extends TestCase
private $user;
#[Override]
#[\Override]
protected function setUp(): void
{
parent::setUp();