Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop

This commit is contained in:
James Cole
2025-08-03 17:42:13 +02:00
19 changed files with 402 additions and 390 deletions

View File

@@ -72,12 +72,12 @@ class ShowController extends Controller
*/ */
public function index(): JsonResponse public function index(): JsonResponse
{ {
$manager = $this->getManager(); $manager = $this->getManager();
// types to get, page size: // types to get, page size:
$pageSize = $this->parameters->get('limit'); $pageSize = $this->parameters->get('limit');
$start = $this->parameters->get('start'); $start = $this->parameters->get('start');
$end = $this->parameters->get('end'); $end = $this->parameters->get('end');
// get list of available budgets. Count it and split it. // get list of available budgets. Count it and split it.
$collection = $this->abRepository->getAvailableBudgetsByDate($start, $end); $collection = $this->abRepository->getAvailableBudgetsByDate($start, $end);
@@ -86,20 +86,20 @@ class ShowController extends Controller
// enrich // enrich
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
$enrichment = new AvailableBudgetEnrichment(); $enrichment = new AvailableBudgetEnrichment();
$enrichment->setUser($admin); $enrichment->setUser($admin);
$availableBudgets = $enrichment->enrich($availableBudgets); $availableBudgets = $enrichment->enrich($availableBudgets);
// make paginator: // make paginator:
$paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.available-budgets.index') . $this->buildParams()); $paginator->setPath(route('api.v1.available-budgets.index').$this->buildParams());
/** @var AvailableBudgetTransformer $transformer */ /** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class); $transformer = app(AvailableBudgetTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($availableBudgets, $transformer, 'available_budgets'); $resource = new FractalCollection($availableBudgets, $transformer, 'available_budgets');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
@@ -113,25 +113,25 @@ class ShowController extends Controller
*/ */
public function show(AvailableBudget $availableBudget): JsonResponse public function show(AvailableBudget $availableBudget): JsonResponse
{ {
$manager = $this->getManager(); $manager = $this->getManager();
$start = $this->parameters->get('start'); $start = $this->parameters->get('start');
$end = $this->parameters->get('end'); $end = $this->parameters->get('end');
/** @var AvailableBudgetTransformer $transformer */ /** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class); $transformer = app(AvailableBudgetTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
// enrich // enrich
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
$enrichment = new AvailableBudgetEnrichment(); $enrichment = new AvailableBudgetEnrichment();
$enrichment->setUser($admin); $enrichment->setUser($admin);
$enrichment->setStart($start); $enrichment->setStart($start);
$enrichment->setEnd($end); $enrichment->setEnd($end);
$availableBudget = $enrichment->enrichSingle($availableBudget); $availableBudget = $enrichment->enrichSingle($availableBudget);
$resource = new Item($availableBudget, $transformer, 'available_budgets'); $resource = new Item($availableBudget, $transformer, 'available_budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
} }

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\PiggyBank; namespace FireflyIII\Api\V1\Requests\Models\PiggyBank;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Contracts\Validation\Validator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\IsValidZeroOrMoreAmount; use FireflyIII\Rules\IsValidZeroOrMoreAmount;
@@ -97,7 +96,7 @@ class StoreRequest extends FormRequest
// validate start before end only if both are there. // validate start before end only if both are there.
$data = $validator->getData(); $data = $validator->getData();
$currency = $this->getCurrencyFromData($validator, $data); $currency = $this->getCurrencyFromData($validator, $data);
if(null === $currency) { if (null === $currency) {
return; return;
} }
$targetAmount = (string) ($data['target_amount'] ?? '0'); $targetAmount = (string) ($data['target_amount'] ?? '0');
@@ -148,6 +147,7 @@ class StoreRequest extends FormRequest
} }
} }
$validator->errors()->add('transaction_currency_id', trans('validation.require_currency_id_code')); $validator->errors()->add('transaction_currency_id', trans('validation.require_currency_id_code'));
return null; return null;
} }
} }

View File

@@ -32,7 +32,6 @@ use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Webhook; use FireflyIII\Models\Webhook;
use FireflyIII\Models\WebhookMessage; use FireflyIII\Models\WebhookMessage;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment; use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;

View File

@@ -151,6 +151,7 @@ class ShowController extends Controller
$enrichment->setUser($admin); $enrichment->setUser($admin);
$enrichment->setStart($start); $enrichment->setStart($start);
$enrichment->setEnd($end); $enrichment->setEnd($end);
/** @var Bill $bill */ /** @var Bill $bill */
$bill = $enrichment->enrichSingle($bill); $bill = $enrichment->enrichSingle($bill);

View File

@@ -32,7 +32,7 @@ use FireflyIII\Support\Report\Summarizer\TransactionSummarizer;
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Override;
/** /**
* Class NoBudgetRepository * Class NoBudgetRepository
@@ -100,10 +100,11 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface, UserGroupInterf
return $summarizer->groupByCurrencyId($journals); return $summarizer->groupByCurrencyId($journals);
} }
#[\Override] public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array #[Override]
public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array
{ {
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
if ($accounts instanceof Collection && $accounts->count() > 0) { if ($accounts instanceof Collection && $accounts->count() > 0) {
@@ -114,7 +115,7 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface, UserGroupInterf
} }
$collector->withoutBudget(); $collector->withoutBudget();
$collector->withBudgetInformation(); $collector->withBudgetInformation();
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
} }

View File

@@ -39,6 +39,7 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Override;
/** /**
* Class OperationsRepository * Class OperationsRepository
@@ -65,7 +66,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
++$count; ++$count;
app('log')->debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total)); app('log')->debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total));
} }
$avg = $total; $avg = $total;
if ($count > 0) { if ($count > 0) {
$avg = bcdiv($total, (string) $count); $avg = bcdiv($total, (string) $count);
} }
@@ -86,21 +87,21 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
// get all transactions: // get all transactions:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end); $collector->setAccounts($accounts)->setRange($start, $end);
$collector->setBudgets($budgets); $collector->setBudgets($budgets);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
// loop transactions: // loop transactions:
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
// prep data array for currency: // prep data array for currency:
$budgetId = (int) $journal['budget_id']; $budgetId = (int) $journal['budget_id'];
$budgetName = $journal['budget_name']; $budgetName = $journal['budget_name'];
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$key = sprintf('%d-%d', $budgetId, $currencyId); $key = sprintf('%d-%d', $budgetId, $currencyId);
$data[$key] ??= [ $data[$key] ??= [
'id' => $budgetId, 'id' => $budgetId,
'name' => sprintf('%s (%s)', $budgetName, $journal['currency_name']), 'name' => sprintf('%s (%s)', $budgetName, $journal['currency_name']),
'sum' => '0', 'sum' => '0',
@@ -126,7 +127,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array
{ {
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
if ($accounts instanceof Collection && $accounts->count() > 0) { if ($accounts instanceof Collection && $accounts->count() > 0) {
$collector->setAccounts($accounts); $collector->setAccounts($accounts);
@@ -138,8 +139,8 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$collector->setBudgets($this->getBudgets()); $collector->setBudgets($this->getBudgets());
} }
$collector->withBudgetInformation()->withAccountInformation()->withCategoryInformation(); $collector->withBudgetInformation()->withAccountInformation()->withCategoryInformation();
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
// if needs conversion to primary. // if needs conversion to primary.
$convertToPrimary = Amount::convertToPrimary($this->user); $convertToPrimary = Amount::convertToPrimary($this->user);
@@ -155,8 +156,8 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
]; ];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$amount = app('steam')->negative($journal['amount']); $amount = app('steam')->negative($journal['amount']);
$journalCurrencyId = (int) $journal['currency_id']; $journalCurrencyId = (int) $journal['currency_id'];
if (false === $convertToPrimary) { if (false === $convertToPrimary) {
$currencyId = $journalCurrencyId; $currencyId = $journalCurrencyId;
$currencyName = $journal['currency_name']; $currencyName = $journal['currency_name'];
@@ -166,11 +167,11 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
} }
if (true === $convertToPrimary && $journalCurrencyId !== $currencyId) { if (true === $convertToPrimary && $journalCurrencyId !== $currencyId) {
$currencies[$journalCurrencyId] ??= TransactionCurrency::find($journalCurrencyId); $currencies[$journalCurrencyId] ??= TransactionCurrency::find($journalCurrencyId);
$amount = $converter->convert($currencies[$journalCurrencyId], $primaryCurrency, $journal['date'], $amount); $amount = $converter->convert($currencies[$journalCurrencyId], $primaryCurrency, $journal['date'], $amount);
} }
$budgetId = (int) $journal['budget_id']; $budgetId = (int) $journal['budget_id'];
$budgetName = (string) $journal['budget_name']; $budgetName = (string) $journal['budget_name'];
// catch "no budget" entries. // catch "no budget" entries.
if (0 === $budgetId) { if (0 === $budgetId) {
@@ -178,7 +179,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
} }
// info about the currency: // info about the currency:
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'budgets' => [], 'budgets' => [],
'currency_id' => $currencyId, 'currency_id' => $currencyId,
'currency_name' => $currencyName, 'currency_name' => $currencyName,
@@ -231,8 +232,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
?Collection $budgets = null, ?Collection $budgets = null,
?TransactionCurrency $currency = null, ?TransactionCurrency $currency = null,
bool $convertToPrimary = false bool $convertToPrimary = false
): array ): array {
{
Log::debug(sprintf('Start of %s(date, date, array, array, "%s", %s).', __METHOD__, $currency?->code, var_export($convertToPrimary, true))); Log::debug(sprintf('Start of %s(date, date, array, array, "%s", %s).', __METHOD__, $currency?->code, var_export($convertToPrimary, true)));
// this collector excludes all transfers TO liabilities (which are also withdrawals) // this collector excludes all transfers TO liabilities (which are also withdrawals)
// because those expenses only become expenses once they move from the liability to the friend. // because those expenses only become expenses once they move from the liability to the friend.
@@ -240,8 +240,8 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$subset = $repository->getAccountsByType(config('firefly.valid_liabilities')); $subset = $repository->getAccountsByType(config('firefly.valid_liabilities'));
$selection = new Collection(); $selection = new Collection();
/** @var Account $account */ /** @var Account $account */
foreach ($subset as $account) { foreach ($subset as $account) {
@@ -251,11 +251,12 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
} }
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user) $collector->setUser($this->user)
->setRange($start, $end) ->setRange($start, $end)
// ->excludeDestinationAccounts($selection) // ->excludeDestinationAccounts($selection)
->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); ->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
;
if ($accounts instanceof Collection) { if ($accounts instanceof Collection) {
$collector->setAccounts($accounts); $collector->setAccounts($accounts);
@@ -270,7 +271,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
if ($budgets->count() > 0) { if ($budgets->count() > 0) {
$collector->setBudgets($budgets); $collector->setBudgets($budgets);
} }
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
// same but for transactions in the foreign currency: // same but for transactions in the foreign currency:
if ($currency instanceof TransactionCurrency) { if ($currency instanceof TransactionCurrency) {
@@ -290,14 +291,15 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$summarizer->setConvertToPrimary($convertToPrimary); $summarizer->setConvertToPrimary($convertToPrimary);
// filter $journals by range. // filter $journals by range.
$expenses = array_filter($expenses, static function (array $expense) use ($start, $end, $transactionCurrency): bool { $expenses = array_filter($expenses, static function (array $expense) use ($start, $end, $transactionCurrency): bool {
return $expense['date']->between($start, $end) && $expense['currency_id'] === $transactionCurrency->id; return $expense['date']->between($start, $end) && $expense['currency_id'] === $transactionCurrency->id;
}); });
return $summarizer->groupByCurrencyId($expenses, 'negative', false); return $summarizer->groupByCurrencyId($expenses, 'negative', false);
} }
#[\Override] public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null): array #[Override]
public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null): array
{ {
Log::debug(sprintf('Start of %s(date, date, array, array, "%s").', __METHOD__, $currency?->code)); Log::debug(sprintf('Start of %s(date, date, array, array, "%s").', __METHOD__, $currency?->code));
// this collector excludes all transfers TO liabilities (which are also withdrawals) // this collector excludes all transfers TO liabilities (which are also withdrawals)
@@ -306,8 +308,8 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$subset = $repository->getAccountsByType(config('firefly.valid_liabilities')); $subset = $repository->getAccountsByType(config('firefly.valid_liabilities'));
$selection = new Collection(); $selection = new Collection();
/** @var Account $account */ /** @var Account $account */
foreach ($subset as $account) { foreach ($subset as $account) {
@@ -317,11 +319,12 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
} }
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user) $collector->setUser($this->user)
->setRange($start, $end) ->setRange($start, $end)
// ->excludeDestinationAccounts($selection) // ->excludeDestinationAccounts($selection)
->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); ->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
;
if ($accounts instanceof Collection) { if ($accounts instanceof Collection) {
$collector->setAccounts($accounts); $collector->setAccounts($accounts);
@@ -336,6 +339,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
if ($budgets->count() > 0) { if ($budgets->count() > 0) {
$collector->setBudgets($budgets); $collector->setBudgets($budgets);
} }
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
} }

View File

@@ -142,7 +142,7 @@ class Amount
$cache->addProperty('getPrimaryCurrencyByGroup'); $cache->addProperty('getPrimaryCurrencyByGroup');
$cache->addProperty($userGroup->id); $cache->addProperty($userGroup->id);
if ($cache->has()) { if ($cache->has()) {
return $cache->get(); return $cache->get();
} }
/** @var null|TransactionCurrency $primary */ /** @var null|TransactionCurrency $primary */

View File

@@ -86,7 +86,7 @@ class AccountEnrichment implements EnrichmentInterface
} }
#[Override] #[Override]
public function enrichSingle(array | Model $model): Account | array public function enrichSingle(array|Model $model): Account|array
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection([$model]); $collection = new Collection([$model]);
@@ -141,9 +141,10 @@ class AccountEnrichment implements EnrichmentInterface
private function collectMetaData(): void private function collectMetaData(): void
{ {
$set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt']) $set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt'])
->whereIn('account_id', $this->accountIds) ->whereIn('account_id', $this->accountIds)
->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray(); ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray()
;
/** @var array $entry */ /** @var array $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
@@ -152,7 +153,7 @@ class AccountEnrichment implements EnrichmentInterface
$this->currencies[(int) $entry['data']] = true; $this->currencies[(int) $entry['data']] = true;
} }
} }
$currencies = TransactionCurrency::whereIn('id', array_keys($this->currencies))->get(); $currencies = TransactionCurrency::whereIn('id', array_keys($this->currencies))->get();
foreach ($currencies as $currency) { foreach ($currencies as $currency) {
$this->currencies[(int) $currency->id] = $currency; $this->currencies[(int) $currency->id] = $currency;
} }
@@ -167,9 +168,10 @@ class AccountEnrichment implements EnrichmentInterface
private function collectNotes(): void private function collectNotes(): void
{ {
$notes = Note::query()->whereIn('noteable_id', $this->accountIds) $notes = Note::query()->whereIn('noteable_id', $this->accountIds)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); ->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int) $note['noteable_id']] = (string) $note['text']; $this->notes[(int) $note['noteable_id']] = (string) $note['text'];
} }
@@ -179,14 +181,15 @@ class AccountEnrichment implements EnrichmentInterface
private function collectLocations(): void private function collectLocations(): void
{ {
$locations = Location::query()->whereIn('locatable_id', $this->accountIds) $locations = Location::query()->whereIn('locatable_id', $this->accountIds)
->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray(); ->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray()
;
foreach ($locations as $location) { foreach ($locations as $location) {
$this->locations[(int) $location['locatable_id']] $this->locations[(int) $location['locatable_id']]
= [ = [
'latitude' => (float) $location['latitude'], 'latitude' => (float) $location['latitude'],
'longitude' => (float) $location['longitude'], 'longitude' => (float) $location['longitude'],
'zoom_level' => (int) $location['zoom_level'], 'zoom_level' => (int) $location['zoom_level'],
]; ];
} }
Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations)));
} }
@@ -201,19 +204,20 @@ class AccountEnrichment implements EnrichmentInterface
->setUserGroup($this->userGroup) ->setUserGroup($this->userGroup)
->setAccounts($this->collection) ->setAccounts($this->collection)
->withAccountInformation() ->withAccountInformation()
->setTypes([TransactionTypeEnum::OPENING_BALANCE->value]); ->setTypes([TransactionTypeEnum::OPENING_BALANCE->value])
$journals = $collector->getExtractedJournals(); ;
$journals = $collector->getExtractedJournals();
foreach ($journals as $journal) { foreach ($journals as $journal) {
$this->openingBalances[(int) $journal['source_account_id']] $this->openingBalances[(int) $journal['source_account_id']]
= [ = [
'amount' => Steam::negative($journal['amount']), 'amount' => Steam::negative($journal['amount']),
'date' => $journal['date'], 'date' => $journal['date'],
]; ];
$this->openingBalances[(int) $journal['destination_account_id']] $this->openingBalances[(int) $journal['destination_account_id']]
= [ = [
'amount' => Steam::positive($journal['amount']), 'amount' => Steam::positive($journal['amount']),
'date' => $journal['date'], 'date' => $journal['date'],
]; ];
} }
} }
@@ -268,27 +272,27 @@ class AccountEnrichment implements EnrichmentInterface
// add balances // add balances
// get currencies: // get currencies:
$currency = $this->primaryCurrency; // assume primary currency $currency = $this->primaryCurrency; // assume primary currency
if (null !== $accountMeta['currency']) { if (null !== $accountMeta['currency']) {
$currency = $accountMeta['currency']; $currency = $accountMeta['currency'];
} }
// get the current balance: // get the current balance:
$date = $this->getDate(); $date = $this->getDate();
$finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary); $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary);
Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance); Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance);
// collect current balances: // collect current balances:
$currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places); $currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places);
$openingBalance = Steam::bcround($accountMeta['opening_balance_amount'] ?? '0', $currency->decimal_places); $openingBalance = Steam::bcround($accountMeta['opening_balance_amount'] ?? '0', $currency->decimal_places);
$virtualBalance = Steam::bcround($account->virtual_balance ?? '0', $currency->decimal_places); $virtualBalance = Steam::bcround($account->virtual_balance ?? '0', $currency->decimal_places);
$debtAmount = $accountMeta['current_debt'] ?? null; $debtAmount = $accountMeta['current_debt'] ?? null;
// set some pc_ default values to NULL: // set some pc_ default values to NULL:
$pcCurrentBalance = null; $pcCurrentBalance = null;
$pcOpeningBalance = null; $pcOpeningBalance = null;
$pcVirtualBalance = null; $pcVirtualBalance = null;
$pcDebtAmount = null; $pcDebtAmount = null;
// convert to primary currency if needed: // convert to primary currency if needed:
if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) { if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) {
@@ -331,7 +335,7 @@ class AccountEnrichment implements EnrichmentInterface
if (array_key_exists($item->id, $lastActivities)) { if (array_key_exists($item->id, $lastActivities)) {
$accountMeta['last_activity'] = $lastActivities[$item->id]; $accountMeta['last_activity'] = $lastActivities[$item->id];
} }
$item->meta = $accountMeta; $item->meta = $accountMeta;
return $item; return $item;
}); });
@@ -354,8 +358,7 @@ class AccountEnrichment implements EnrichmentInterface
if (null === $this->date) { if (null === $this->date) {
return today(); return today();
} }
return $this->date; return $this->date;
} }
} }

View File

@@ -1,4 +1,5 @@
<?php <?php
/* /*
* AvailableBudgetEnrichment.php * AvailableBudgetEnrichment.php
* Copyright (c) 2025 james@firefly-iii.org. * Copyright (c) 2025 james@firefly-iii.org.
@@ -35,6 +36,7 @@ use FireflyIII\User;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Override;
class AvailableBudgetEnrichment implements EnrichmentInterface class AvailableBudgetEnrichment implements EnrichmentInterface
{ {
@@ -53,8 +55,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
private readonly BudgetRepositoryInterface $repository; private readonly BudgetRepositoryInterface $repository;
private ?Carbon $start = null; private ?Carbon $start = null;
private ?Carbon $end = null; private ?Carbon $end = null;
public function __construct() public function __construct()
{ {
@@ -65,7 +67,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
$this->repository = app(BudgetRepositoryInterface::class); $this->repository = app(BudgetRepositoryInterface::class);
} }
#[\Override] public function enrich(Collection $collection): Collection #[Override]
public function enrich(Collection $collection): Collection
{ {
$this->collection = $collection; $this->collection = $collection;
$this->collectIds(); $this->collectIds();
@@ -75,7 +78,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
return $this->collection; return $this->collection;
} }
#[\Override] public function enrichSingle(Model | array $model): array | Model #[Override]
public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection([$model]); $collection = new Collection([$model]);
@@ -84,13 +88,15 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
return $collection->first(); return $collection->first();
} }
#[\Override] public function setUser(User $user): void #[Override]
public function setUser(User $user): void
{ {
$this->user = $user; $this->user = $user;
$this->setUserGroup($user->userGroup); $this->setUserGroup($user->userGroup);
} }
#[\Override] public function setUserGroup(UserGroup $userGroup): void #[Override]
public function setUserGroup(UserGroup $userGroup): void
{ {
$this->userGroup = $userGroup; $this->userGroup = $userGroup;
$this->noBudgetRepository->setUserGroup($userGroup); $this->noBudgetRepository->setUserGroup($userGroup);
@@ -115,9 +121,9 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
$spentInBudgets = $this->opsRepository->collectExpenses($start, $end, null, $allActive, null); $spentInBudgets = $this->opsRepository->collectExpenses($start, $end, null, $allActive, null);
$spentOutsideBudgets = $this->noBudgetRepository->collectExpenses($start, $end, null, null, null); $spentOutsideBudgets = $this->noBudgetRepository->collectExpenses($start, $end, null, null, null);
foreach ($this->collection as $availableBudget) { foreach ($this->collection as $availableBudget) {
$id = (int) $availableBudget->id; $id = (int) $availableBudget->id;
$filteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, false); $filteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, false);
$filteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, false); $filteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, false);
$this->spentInBudgets[$id] = array_values($filteredSpentInBudgets); $this->spentInBudgets[$id] = array_values($filteredSpentInBudgets);
$this->spentOutsideBudgets[$id] = array_values($filteredSpentOutsideBudgets); $this->spentOutsideBudgets[$id] = array_values($filteredSpentOutsideBudgets);
@@ -153,9 +159,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
'pc_spent_outside_budgets' => $pcSpentOutsideBudgets[$id] ?? [], 'pc_spent_outside_budgets' => $pcSpentOutsideBudgets[$id] ?? [],
]; ];
$item->meta = $meta; $item->meta = $meta;
return $item; return $item;
}); });
} }
} }

View File

@@ -61,11 +61,11 @@ class SubscriptionEnrichment implements EnrichmentInterface
$paidDates = $this->paidDates; $paidDates = $this->paidDates;
$payDates = $this->payDates; $payDates = $this->payDates;
$this->collection = $this->collection->map(function (Bill $item) use ($notes, $objectGroups, $paidDates, $payDates) { $this->collection = $this->collection->map(function (Bill $item) use ($notes, $objectGroups, $paidDates, $payDates) {
$id = (int) $item->id; $id = (int) $item->id;
$currency = $item->transactionCurrency; $currency = $item->transactionCurrency;
$nem = $this->getNextExpectedMatch($payDates[$id] ?? []); $nem = $this->getNextExpectedMatch($payDates[$id] ?? []);
$meta = [ $meta = [
'notes' => null, 'notes' => null,
'object_group_id' => null, 'object_group_id' => null,
'object_group_title' => null, 'object_group_title' => null,
@@ -76,7 +76,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
'nem' => $nem, 'nem' => $nem,
'nem_diff' => $this->getNextExpectedMatchDiff($nem, $payDates[$id] ?? []), 'nem_diff' => $this->getNextExpectedMatchDiff($nem, $payDates[$id] ?? []),
]; ];
$amounts = [ $amounts = [
'amount_min' => Steam::bcround($item->amount_min, $currency->decimal_places), 'amount_min' => Steam::bcround($item->amount_min, $currency->decimal_places),
'amount_max' => Steam::bcround($item->amount_max, $currency->decimal_places), 'amount_max' => Steam::bcround($item->amount_max, $currency->decimal_places),
'average' => Steam::bcround(bcdiv(bcadd($item->amount_min, $item->amount_max), '2'), $currency->decimal_places), 'average' => Steam::bcround(bcdiv(bcadd($item->amount_min, $item->amount_max), '2'), $currency->decimal_places),
@@ -117,7 +117,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
return $collection; return $collection;
} }
public function enrichSingle(array | Model $model): array | Model public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection([$model]); $collection = new Collection([$model]);
@@ -129,9 +129,10 @@ class SubscriptionEnrichment implements EnrichmentInterface
private function collectNotes(): void private function collectNotes(): void
{ {
$notes = Note::query()->whereIn('noteable_id', $this->subscriptionIds) $notes = Note::query()->whereIn('noteable_id', $this->subscriptionIds)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', Bill::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); ->where('noteable_type', Bill::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int) $note['noteable_id']] = (string) $note['text']; $this->notes[(int) $note['noteable_id']] = (string) $note['text'];
} }
@@ -160,12 +161,13 @@ class SubscriptionEnrichment implements EnrichmentInterface
private function collectObjectGroups(): void private function collectObjectGroups(): void
{ {
$set = DB::table('object_groupables') $set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->subscriptionIds) ->whereIn('object_groupable_id', $this->subscriptionIds)
->where('object_groupable_type', Bill::class) ->where('object_groupable_type', Bill::class)
->get(['object_groupable_id', 'object_group_id']); ->get(['object_groupable_id', 'object_group_id'])
;
$ids = array_unique($set->pluck('object_group_id')->toArray()); $ids = array_unique($set->pluck('object_group_id')->toArray());
foreach ($set as $entry) { foreach ($set as $entry) {
$this->mappedObjects[(int) $entry->object_groupable_id] = (int) $entry->object_group_id; $this->mappedObjects[(int) $entry->object_groupable_id] = (int) $entry->object_group_id;
@@ -193,13 +195,13 @@ class SubscriptionEnrichment implements EnrichmentInterface
// 2023-07-18 this particular date is used to search for the last paid date. // 2023-07-18 this particular date is used to search for the last paid date.
// 2023-07-18 the cloned $searchDate is used to grab the correct transactions. // 2023-07-18 the cloned $searchDate is used to grab the correct transactions.
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone $this->start; $start = clone $this->start;
$searchStart = clone $start; $searchStart = clone $start;
$start->subDay(); $start->subDay();
/** @var Carbon $end */ /** @var Carbon $end */
$end = clone $this->end; $end = clone $this->end;
$searchEnd = clone $end; $searchEnd = clone $end;
// move the search dates to the start of the day. // move the search dates to the start of the day.
$searchStart->startOfDay(); $searchStart->startOfDay();
@@ -208,13 +210,13 @@ class SubscriptionEnrichment implements EnrichmentInterface
Log::debug(sprintf('Search parameters are: start: %s, end: %s', $searchStart->format('Y-m-d H:i:s'), $searchEnd->format('Y-m-d H:i:s'))); Log::debug(sprintf('Search parameters are: start: %s, end: %s', $searchStart->format('Y-m-d H:i:s'), $searchEnd->format('Y-m-d H:i:s')));
// Get from database when bills were paid. // Get from database when bills were paid.
$set = $this->user->transactionJournals() $set = $this->user->transactionJournals()
->whereIn('bill_id', $this->subscriptionIds) ->whereIn('bill_id', $this->subscriptionIds)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_currencies AS currency', 'currency.id', '=', 'transactions.transaction_currency_id') ->leftJoin('transaction_currencies AS currency', 'currency.id', '=', 'transactions.transaction_currency_id')
->leftJoin('transaction_currencies AS foreign_currency', 'foreign_currency.id', '=', 'transactions.foreign_currency_id') ->leftJoin('transaction_currencies AS foreign_currency', 'foreign_currency.id', '=', 'transactions.foreign_currency_id')
->where('transactions.amount', '>', 0) ->where('transactions.amount', '>', 0)
->before($searchEnd)->after($searchStart)->get( ->before($searchEnd)->after($searchStart)->get(
[ [
'transaction_journals.id', 'transaction_journals.id',
'transaction_journals.date', 'transaction_journals.date',
@@ -231,43 +233,44 @@ class SubscriptionEnrichment implements EnrichmentInterface
'transactions.amount', 'transactions.amount',
'transactions.foreign_amount', 'transactions.foreign_amount',
] ]
); )
;
Log::debug(sprintf('Count %d entries in set', $set->count())); Log::debug(sprintf('Count %d entries in set', $set->count()));
// for each bill, do a loop. // for each bill, do a loop.
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();
/** @var Bill $subscription */ /** @var Bill $subscription */
foreach ($this->collection as $subscription) { foreach ($this->collection as $subscription) {
// Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date. // Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date.
Log::debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $start->format('Y-m-d'))); Log::debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $start->format('Y-m-d')));
$lastPaidDate = $this->lastPaidDate($subscription, $set, $start); $lastPaidDate = $this->lastPaidDate($subscription, $set, $start);
Log::debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d'))); Log::debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d')));
// At this point the "next match" is exactly after the last time the bill was paid. // At this point the "next match" is exactly after the last time the bill was paid.
$result = []; $result = [];
$filtered = $set->filter(function (TransactionJournal $journal) use ($subscription) { $filtered = $set->filter(function (TransactionJournal $journal) use ($subscription) {
return (int) $journal->bill_id === (int) $subscription->id; return (int) $journal->bill_id === (int) $subscription->id;
}); });
foreach ($filtered as $entry) { foreach ($filtered as $entry) {
$array = [ $array = [
'transaction_group_id' => (string) $entry->transaction_group_id, 'transaction_group_id' => (string) $entry->transaction_group_id,
'transaction_journal_id' => (string) $entry->id, 'transaction_journal_id' => (string) $entry->id,
'date' => $entry->date->toAtomString(), 'date' => $entry->date->toAtomString(),
'date_object' => $entry->date, 'date_object' => $entry->date,
'subscription_id' => (string) $entry->bill_id, 'subscription_id' => (string) $entry->bill_id,
'currency_id' => (string) $entry->transaction_currency_id, 'currency_id' => (string) $entry->transaction_currency_id,
'currency_code' => $entry->transaction_currency_code, 'currency_code' => $entry->transaction_currency_code,
'currency_symbol' => $entry->transaction_currency_symbol, 'currency_symbol' => $entry->transaction_currency_symbol,
'currency_decimal_places' => $entry->transaction_currency_decimal_places, 'currency_decimal_places' => $entry->transaction_currency_decimal_places,
'primary_currency_id' => (string) $this->primaryCurrency->id, 'primary_currency_id' => (string) $this->primaryCurrency->id,
'primary_currency_code' => $this->primaryCurrency->code, 'primary_currency_code' => $this->primaryCurrency->code,
'primary_currency_symbol' => $this->primaryCurrency->symbol, 'primary_currency_symbol' => $this->primaryCurrency->symbol,
'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places, 'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places,
'amount' => Steam::bcround($entry->amount, $entry->transaction_currency_decimal_places), 'amount' => Steam::bcround($entry->amount, $entry->transaction_currency_decimal_places),
'pc_amount' => null, 'pc_amount' => null,
'foreign_amount' => null, 'foreign_amount' => null,
'pc_foreign_amount' => null, 'pc_foreign_amount' => null,
]; ];
if (null !== $entry->foreign_amount && null !== $entry->foreign_currency_code) { if (null !== $entry->foreign_amount && null !== $entry->foreign_currency_code) {
@@ -292,7 +295,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
// convert to primary, but foreign is NOT already primary. // convert to primary, but foreign is NOT already primary.
if ($this->convertToPrimary && null !== $entry->foreign_currency_id && (int) $entry->foreign_currency_id !== $this->primaryCurrency->id) { if ($this->convertToPrimary && null !== $entry->foreign_currency_id && (int) $entry->foreign_currency_id !== $this->primaryCurrency->id) {
// TODO this is very database intensive. // TODO this is very database intensive.
$foreignCurrency = TransactionCurrency::find($entry->foreign_currency_id); $foreignCurrency = TransactionCurrency::find($entry->foreign_currency_id);
$array['pc_foreign_amount'] = $converter->convert($foreignCurrency, $this->primaryCurrency, $entry->date, $entry->amount); $array['pc_foreign_amount'] = $converter->convert($foreignCurrency, $this->primaryCurrency, $entry->date, $entry->amount);
} }
$result[] = $array; $result[] = $array;
@@ -325,7 +328,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
return $default; return $default;
} }
$latest = $filtered->first()->date; $latest = $filtered->first()->date;
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($filtered as $journal) { foreach ($filtered as $journal) {
@@ -371,12 +374,12 @@ class SubscriptionEnrichment implements EnrichmentInterface
/** @var Bill $subscription */ /** @var Bill $subscription */
foreach ($this->collection as $subscription) { foreach ($this->collection as $subscription) {
$id = (int) $subscription->id; $id = (int) $subscription->id;
$lastPaidDate = $this->getLastPaidDate($paidDates[$id] ?? []); $lastPaidDate = $this->getLastPaidDate($paidDates[$id] ?? []);
$payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate); $payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate);
$payDatesFormatted = []; $payDatesFormatted = [];
foreach ($payDates as $string) { foreach ($payDates as $string) {
$date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone')); $date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone'));
if (!$date instanceof Carbon) { if (!$date instanceof Carbon) {
$date = today(config('app.timezone')); $date = today(config('app.timezone'));
} }
@@ -406,7 +409,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
if (!$nemDate instanceof Carbon) { if (!$nemDate instanceof Carbon) {
$nemDate = today(config('app.timezone')); $nemDate = today(config('app.timezone'));
} }
$nem = $nemDate; $nem = $nemDate;
// nullify again when it's outside the current view range. // nullify again when it's outside the current view range.
if ( if (
@@ -435,7 +438,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
$current = $payDates[0] ?? null; $current = $payDates[0] ?? null;
if (null !== $current && !$nem->isToday()) { if (null !== $current && !$nem->isToday()) {
$temp2 = Carbon::parse($current, config('app.timezone')); $temp2 = Carbon::parse($current, config('app.timezone'));
if (!$temp2 instanceof Carbon) { if (!$temp2 instanceof Carbon) {
$temp2 = today(config('app.timezone')); $temp2 = today(config('app.timezone'));
} }

View File

@@ -66,24 +66,24 @@ class AccountTransformer extends AbstractTransformer
} }
// get account type: // get account type:
$accountType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $account->full_account_type)); $accountType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $account->full_account_type));
$liabilityType = (string) config(sprintf('firefly.shortLiabilityNameByFullName.%s', $account->full_account_type)); $liabilityType = (string) config(sprintf('firefly.shortLiabilityNameByFullName.%s', $account->full_account_type));
$liabilityType = '' === $liabilityType ? null : strtolower($liabilityType); $liabilityType = '' === $liabilityType ? null : strtolower($liabilityType);
$liabilityDirection = $account->meta['liability_direction'] ?? null; $liabilityDirection = $account->meta['liability_direction'] ?? null;
$accountRole = $this->getAccountRole($account, $accountType); $accountRole = $this->getAccountRole($account, $accountType);
$hasCurrencySettings = null !== $account->meta['currency']; $hasCurrencySettings = null !== $account->meta['currency'];
$includeNetWorth = 1 === (int) ($account->meta['include_net_worth'] ?? 0); $includeNetWorth = 1 === (int) ($account->meta['include_net_worth'] ?? 0);
$longitude = $account->meta['location']['longitude'] ?? null; $longitude = $account->meta['location']['longitude'] ?? null;
$latitude = $account->meta['location']['latitude'] ?? null; $latitude = $account->meta['location']['latitude'] ?? null;
$zoomLevel = $account->meta['location']['zoom_level'] ?? null; $zoomLevel = $account->meta['location']['zoom_level'] ?? null;
$order = $account->order; $order = $account->order;
// date (for balance etc.) // date (for balance etc.)
$date = $this->getDate(); $date = $this->getDate();
$date->endOfDay(); $date->endOfDay();
// get primary currency as fallback: // get primary currency as fallback:
$currency = $this->primary; // assume primary currency $currency = $this->primary; // assume primary currency
if ($hasCurrencySettings) { if ($hasCurrencySettings) {
$currency = $account->meta['currency']; $currency = $account->meta['currency'];
} }
@@ -95,8 +95,8 @@ class AccountTransformer extends AbstractTransformer
// get some listed information from the account meta-data: // get some listed information from the account meta-data:
[$creditCardType, $monthlyPaymentDate] = $this->getCCInfo($account, $accountRole, $accountType); [$creditCardType, $monthlyPaymentDate] = $this->getCCInfo($account, $accountRole, $accountType);
$openingBalanceDate = $this->getOpeningBalance($account, $accountType); $openingBalanceDate = $this->getOpeningBalance($account, $accountType);
[$interest, $interestPeriod] = $this->getInterest($account, $accountType); [$interest, $interestPeriod] = $this->getInterest($account, $accountType);
return [ return [
'id' => (string) $account->id, 'id' => (string) $account->id,
@@ -125,33 +125,33 @@ class AccountTransformer extends AbstractTransformer
'current_balance' => $account->meta['balances']['current_balance'], 'current_balance' => $account->meta['balances']['current_balance'],
'pc_current_balance' => $account->meta['balances']['pc_current_balance'], 'pc_current_balance' => $account->meta['balances']['pc_current_balance'],
'opening_balance' => $account->meta['balances']['opening_balance'], 'opening_balance' => $account->meta['balances']['opening_balance'],
'pc_opening_balance' => $account->meta['balances']['pc_opening_balance'], 'pc_opening_balance' => $account->meta['balances']['pc_opening_balance'],
'virtual_balance' => $account->meta['balances']['virtual_balance'], 'virtual_balance' => $account->meta['balances']['virtual_balance'],
'pc_virtual_balance' => $account->meta['balances']['pc_virtual_balance'], 'pc_virtual_balance' => $account->meta['balances']['pc_virtual_balance'],
'debt_amount' => $account->meta['balances']['debt_amount'], 'debt_amount' => $account->meta['balances']['debt_amount'],
'pc_debt_amount' => $account->meta['balances']['pc_debt_amount'], 'pc_debt_amount' => $account->meta['balances']['pc_debt_amount'],
'current_balance_date' => $date->toAtomString(), 'current_balance_date' => $date->toAtomString(),
'notes' => $account->meta['notes'] ?? null, 'notes' => $account->meta['notes'] ?? null,
'monthly_payment_date' => $monthlyPaymentDate, 'monthly_payment_date' => $monthlyPaymentDate,
'credit_card_type' => $creditCardType, 'credit_card_type' => $creditCardType,
'account_number' => $account->meta['account_number'] ?? null, 'account_number' => $account->meta['account_number'] ?? null,
'iban' => '' === $account->iban ? null : $account->iban, 'iban' => '' === $account->iban ? null : $account->iban,
'bic' => $account->meta['BIC'] ?? null, 'bic' => $account->meta['BIC'] ?? null,
'opening_balance_date' => $openingBalanceDate, 'opening_balance_date' => $openingBalanceDate,
'liability_type' => $liabilityType, 'liability_type' => $liabilityType,
'liability_direction' => $liabilityDirection, 'liability_direction' => $liabilityDirection,
'interest' => $interest, 'interest' => $interest,
'interest_period' => $interestPeriod, 'interest_period' => $interestPeriod,
'include_net_worth' => $includeNetWorth, 'include_net_worth' => $includeNetWorth,
'longitude' => $longitude, 'longitude' => $longitude,
'latitude' => $latitude, 'latitude' => $latitude,
'zoom_level' => $zoomLevel, 'zoom_level' => $zoomLevel,
'last_activity' => array_key_exists('last_activity', $account->meta) ? $account->meta['last_activity']->toAtomString() : null, 'last_activity' => array_key_exists('last_activity', $account->meta) ? $account->meta['last_activity']->toAtomString() : null,
'links' => [ 'links' => [
[ [
'rel' => 'self', 'rel' => 'self',
'uri' => sprintf('/accounts/%d', $account->id), 'uri' => sprintf('/accounts/%d', $account->id),
@@ -193,7 +193,7 @@ class AccountTransformer extends AbstractTransformer
if (null !== $monthlyPaymentDate) { if (null !== $monthlyPaymentDate) {
// try classic date: // try classic date:
if (10 === strlen($monthlyPaymentDate)) { if (10 === strlen($monthlyPaymentDate)) {
$object = Carbon::createFromFormat('!Y-m-d', $monthlyPaymentDate, config('app.timezone')); $object = Carbon::createFromFormat('!Y-m-d', $monthlyPaymentDate, config('app.timezone'));
if (!$object instanceof Carbon) { if (!$object instanceof Carbon) {
$object = today(config('app.timezone')); $object = today(config('app.timezone'));
} }
@@ -214,7 +214,7 @@ class AccountTransformer extends AbstractTransformer
$openingBalanceDate = $account->meta['opening_balance_date'] ?? null; $openingBalanceDate = $account->meta['opening_balance_date'] ?? null;
} }
if (null !== $openingBalanceDate) { if (null !== $openingBalanceDate) {
$object = Carbon::createFromFormat('Y-m-d H:i:s', $openingBalanceDate, config('app.timezone')); $object = Carbon::createFromFormat('Y-m-d H:i:s', $openingBalanceDate, config('app.timezone'));
if (!$object instanceof Carbon) { if (!$object instanceof Carbon) {
$object = today(config('app.timezone')); $object = today(config('app.timezone'));
} }

View File

@@ -58,17 +58,17 @@ class AvailableBudgetTransformer extends AbstractTransformer
$pcAmount = Steam::bcround($availableBudget->native_amount, $this->primary->decimal_places); $pcAmount = Steam::bcround($availableBudget->native_amount, $this->primary->decimal_places);
} }
$data = [ return [
'id' => (string) $availableBudget->id, 'id' => (string) $availableBudget->id,
'created_at' => $availableBudget->created_at->toAtomString(), 'created_at' => $availableBudget->created_at->toAtomString(),
'updated_at' => $availableBudget->updated_at->toAtomString(), 'updated_at' => $availableBudget->updated_at->toAtomString(),
// currencies according to 6.3.0 // currencies according to 6.3.0
'object_has_currency_setting' => true, 'object_has_currency_setting' => true,
'currency_id' => (string) $currency->id, 'currency_id' => (string) $currency->id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $currency->decimal_places,
'primary_currency_id' => (string) $this->primary->id, 'primary_currency_id' => (string) $this->primary->id,
'primary_currency_code' => $this->primary->code, 'primary_currency_code' => $this->primary->code,
@@ -76,23 +76,20 @@ class AvailableBudgetTransformer extends AbstractTransformer
'primary_currency_decimal_places' => $this->primary->decimal_places, 'primary_currency_decimal_places' => $this->primary->decimal_places,
'amount' => $amount, 'amount' => $amount,
'pc_amount' => $pcAmount, 'pc_amount' => $pcAmount,
'start' => $availableBudget->start_date->toAtomString(), 'start' => $availableBudget->start_date->toAtomString(),
'end' => $availableBudget->end_date->endOfDay()->toAtomString(), 'end' => $availableBudget->end_date->endOfDay()->toAtomString(),
'spent_in_budgets' => $availableBudget->meta['spent_in_budgets'], 'spent_in_budgets' => $availableBudget->meta['spent_in_budgets'],
'pc_spent_in_budgets' => $availableBudget->meta['pc_spent_in_budgets'], 'pc_spent_in_budgets' => $availableBudget->meta['pc_spent_in_budgets'],
'spent_outside_budgets' => $availableBudget->meta['spent_outside_budgets'], 'spent_outside_budgets' => $availableBudget->meta['spent_outside_budgets'],
'pc_spent_outside_budgets' => $availableBudget->meta['pc_spent_outside_budgets'], 'pc_spent_outside_budgets' => $availableBudget->meta['pc_spent_outside_budgets'],
'links' => [ 'links' => [
[ [
'rel' => 'self', 'rel' => 'self',
'uri' => '/available_budgets/' . $availableBudget->id, 'uri' => '/available_budgets/'.$availableBudget->id,
], ],
], ],
]; ];
return $data;
} }
} }

View File

@@ -52,17 +52,17 @@ class BillTransformer extends AbstractTransformer
return [ return [
'id' => $bill->id, 'id' => $bill->id,
'created_at' => $bill->created_at->toAtomString(), 'created_at' => $bill->created_at->toAtomString(),
'updated_at' => $bill->updated_at->toAtomString(), 'updated_at' => $bill->updated_at->toAtomString(),
'name' => $bill->name, 'name' => $bill->name,
// currencies according to 6.3.0 // currencies according to 6.3.0
'object_has_currency_setting' => true, 'object_has_currency_setting' => true,
'currency_id' => (string) $bill->transaction_currency_id, 'currency_id' => (string) $bill->transaction_currency_id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $currency->decimal_places,
'primary_currency_id' => (string) $this->primary->id, 'primary_currency_id' => (string) $this->primary->id,
'primary_currency_code' => $this->primary->code, 'primary_currency_code' => $this->primary->code,
@@ -73,34 +73,34 @@ class BillTransformer extends AbstractTransformer
'amount_min' => $bill->amounts['amount_min'], 'amount_min' => $bill->amounts['amount_min'],
'pc_amount_min' => $bill->amounts['pc_amount_min'], 'pc_amount_min' => $bill->amounts['pc_amount_min'],
'amount_max' => $bill->amounts['amount_max'], 'amount_max' => $bill->amounts['amount_max'],
'pc_amount_max' => $bill->amounts['pc_amount_max'], 'pc_amount_max' => $bill->amounts['pc_amount_max'],
'amount_avg' => $bill->amounts['average'], 'amount_avg' => $bill->amounts['average'],
'pc_amount_avg' => $bill->amounts['pc_average'], 'pc_amount_avg' => $bill->amounts['pc_average'],
'date' => $bill->date->toAtomString(), 'date' => $bill->date->toAtomString(),
'end_date' => $bill->end_date?->toAtomString(), 'end_date' => $bill->end_date?->toAtomString(),
'extension_date' => $bill->extension_date?->toAtomString(), 'extension_date' => $bill->extension_date?->toAtomString(),
'repeat_freq' => $bill->repeat_freq, 'repeat_freq' => $bill->repeat_freq,
'skip' => $bill->skip, 'skip' => $bill->skip,
'active' => $bill->active, 'active' => $bill->active,
'order' => $bill->order, 'order' => $bill->order,
'notes' => $bill->meta['notes'], 'notes' => $bill->meta['notes'],
'object_group_id' => $bill->meta['object_group_id'], 'object_group_id' => $bill->meta['object_group_id'],
'object_group_order' => $bill->meta['object_group_order'], 'object_group_order' => $bill->meta['object_group_order'],
'object_group_title' => $bill->meta['object_group_title'], 'object_group_title' => $bill->meta['object_group_title'],
'paid_dates' => $bill->meta['paid_dates'], 'paid_dates' => $bill->meta['paid_dates'],
'pay_dates' => $bill->meta['pay_dates'], 'pay_dates' => $bill->meta['pay_dates'],
'next_expected_match' => $bill->meta['nem']?->toAtomString(), 'next_expected_match' => $bill->meta['nem']?->toAtomString(),
'next_expected_match_diff' => $bill->meta['nem_diff'], 'next_expected_match_diff' => $bill->meta['nem_diff'],
'links' => [ 'links' => [
[ [
'rel' => 'self', 'rel' => 'self',
'uri' => '/bills/' . $bill->id, 'uri' => '/bills/'.$bill->id,
], ],
], ],
]; ];

View File

@@ -94,7 +94,7 @@ class TransactionGroupTransformer extends AbstractTransformer
'links' => [ 'links' => [
[ [
'rel' => 'self', 'rel' => 'self',
'uri' => '/transactions/' . $first['transaction_group_id'], 'uri' => '/transactions/'.$first['transaction_group_id'],
], ],
], ],
]; ];
@@ -117,8 +117,8 @@ class TransactionGroupTransformer extends AbstractTransformer
private function transformTransaction(array $transaction): array private function transformTransaction(array $transaction): array
{ {
// amount: // amount:
$amount = Steam::positive((string) ($transaction['amount'] ?? '0')); $amount = Steam::positive((string) ($transaction['amount'] ?? '0'));
$foreignAmount = null; $foreignAmount = null;
if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', (string) $transaction['foreign_amount'])) { if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', (string) $transaction['foreign_amount'])) {
$foreignAmount = Steam::positive($transaction['foreign_amount']); $foreignAmount = Steam::positive($transaction['foreign_amount']);
} }
@@ -131,7 +131,7 @@ class TransactionGroupTransformer extends AbstractTransformer
if (array_key_exists('pc_amount', $transaction) && null !== $transaction['pc_amount']) { if (array_key_exists('pc_amount', $transaction) && null !== $transaction['pc_amount']) {
$transaction['pc_amount'] = Steam::positive($transaction['pc_amount']); $transaction['pc_amount'] = Steam::positive($transaction['pc_amount']);
} }
$type = $this->stringFromArray($transaction, 'transaction_type_type', TransactionTypeEnum::WITHDRAWAL->value); $type = $this->stringFromArray($transaction, 'transaction_type_type', TransactionTypeEnum::WITHDRAWAL->value);
// must be 0 (int) or NULL // must be 0 (int) or NULL
$recurrenceTotal = $transaction['meta']['recurrence_total'] ?? null; $recurrenceTotal = $transaction['meta']['recurrence_total'] ?? null;
@@ -140,20 +140,20 @@ class TransactionGroupTransformer extends AbstractTransformer
$recurrenceCount = null !== $recurrenceCount ? (int) $recurrenceCount : null; $recurrenceCount = null !== $recurrenceCount ? (int) $recurrenceCount : null;
return [ return [
'user' => (string) $transaction['user_id'], 'user' => (string) $transaction['user_id'],
'transaction_journal_id' => (string) $transaction['transaction_journal_id'], 'transaction_journal_id' => (string) $transaction['transaction_journal_id'],
'type' => strtolower((string) $type), 'type' => strtolower((string) $type),
'date' => $transaction['date']->toAtomString(), 'date' => $transaction['date']->toAtomString(),
'order' => $transaction['order'], 'order' => $transaction['order'],
// currency information, structured for 6.3.0. // currency information, structured for 6.3.0.
'object_has_currency_setting' => true, 'object_has_currency_setting' => true,
'currency_id' => (string) $transaction['currency_id'], 'currency_id' => (string) $transaction['currency_id'],
'currency_code' => $transaction['currency_code'], 'currency_code' => $transaction['currency_code'],
'currency_name' => $transaction['currency_name'], 'currency_name' => $transaction['currency_name'],
'currency_symbol' => $transaction['currency_symbol'], 'currency_symbol' => $transaction['currency_symbol'],
'currency_decimal_places' => (int) $transaction['currency_decimal_places'], 'currency_decimal_places' => (int) $transaction['currency_decimal_places'],
'foreign_currency_id' => $this->stringFromArray($transaction, 'foreign_currency_id', null), 'foreign_currency_id' => $this->stringFromArray($transaction, 'foreign_currency_id', null),
'foreign_currency_code' => $transaction['foreign_currency_code'], 'foreign_currency_code' => $transaction['foreign_currency_code'],
@@ -171,76 +171,76 @@ class TransactionGroupTransformer extends AbstractTransformer
'amount' => $amount, 'amount' => $amount,
'pc_amount' => $transaction['pc_amount'] ?? null, 'pc_amount' => $transaction['pc_amount'] ?? null,
'foreign_amount' => $foreignAmount, 'foreign_amount' => $foreignAmount,
'pc_foreign_amount' => null, 'pc_foreign_amount' => null,
'source_balance_after' => $transaction['source_balance_after'] ?? null, 'source_balance_after' => $transaction['source_balance_after'] ?? null,
'pc_source_balance_after' => null, 'pc_source_balance_after' => null,
// destination balance after // destination balance after
'destination_balance_after' => $transaction['destination_balance_after'] ?? null, 'destination_balance_after' => $transaction['destination_balance_after'] ?? null,
'pc_destination_balance_after' => null, 'pc_destination_balance_after' => null,
'source_balance_dirty' => $transaction['source_balance_dirty'], 'source_balance_dirty' => $transaction['source_balance_dirty'],
'destination_balance_dirty' => $transaction['destination_balance_dirty'], 'destination_balance_dirty' => $transaction['destination_balance_dirty'],
'description' => $transaction['description'], 'description' => $transaction['description'],
'source_id' => (string) $transaction['source_account_id'], 'source_id' => (string) $transaction['source_account_id'],
'source_name' => $transaction['source_account_name'], 'source_name' => $transaction['source_account_name'],
'source_iban' => $transaction['source_account_iban'], 'source_iban' => $transaction['source_account_iban'],
'source_type' => $transaction['source_account_type'], 'source_type' => $transaction['source_account_type'],
'destination_id' => (string) $transaction['destination_account_id'], 'destination_id' => (string) $transaction['destination_account_id'],
'destination_name' => $transaction['destination_account_name'], 'destination_name' => $transaction['destination_account_name'],
'destination_iban' => $transaction['destination_account_iban'], 'destination_iban' => $transaction['destination_account_iban'],
'destination_type' => $transaction['destination_account_type'], 'destination_type' => $transaction['destination_account_type'],
'budget_id' => $this->stringFromArray($transaction, 'budget_id', null), 'budget_id' => $this->stringFromArray($transaction, 'budget_id', null),
'budget_name' => $transaction['budget_name'], 'budget_name' => $transaction['budget_name'],
'category_id' => $this->stringFromArray($transaction, 'category_id', null), 'category_id' => $this->stringFromArray($transaction, 'category_id', null),
'category_name' => $transaction['category_name'], 'category_name' => $transaction['category_name'],
'bill_id' => $this->stringFromArray($transaction, 'bill_id', null), 'bill_id' => $this->stringFromArray($transaction, 'bill_id', null),
'bill_name' => $transaction['bill_name'], 'bill_name' => $transaction['bill_name'],
'subscription_id' => $this->stringFromArray($transaction, 'bill_id', null), 'subscription_id' => $this->stringFromArray($transaction, 'bill_id', null),
'subscription_name' => $transaction['bill_name'], 'subscription_name' => $transaction['bill_name'],
'reconciled' => $transaction['reconciled'], 'reconciled' => $transaction['reconciled'],
'notes' => $transaction['notes'], 'notes' => $transaction['notes'],
'tags' => $transaction['tags'], 'tags' => $transaction['tags'],
'internal_reference' => $transaction['meta']['internal_reference'] ?? null, 'internal_reference' => $transaction['meta']['internal_reference'] ?? null,
'external_id' => $transaction['meta']['external_id'] ?? null, 'external_id' => $transaction['meta']['external_id'] ?? null,
'original_source' => $transaction['meta']['original_source'] ?? null, 'original_source' => $transaction['meta']['original_source'] ?? null,
'recurrence_id' => $transaction['meta']['recurrence_id'] ?? null, 'recurrence_id' => $transaction['meta']['recurrence_id'] ?? null,
'recurrence_total' => $recurrenceTotal, 'recurrence_total' => $recurrenceTotal,
'recurrence_count' => $recurrenceCount, 'recurrence_count' => $recurrenceCount,
'external_url' => $transaction['meta']['external_url'] ?? null, 'external_url' => $transaction['meta']['external_url'] ?? null,
'import_hash_v2' => $transaction['meta']['import_hash_v2'] ?? null, 'import_hash_v2' => $transaction['meta']['import_hash_v2'] ?? null,
'sepa_cc' => $transaction['meta']['sepa_cc'] ?? null, 'sepa_cc' => $transaction['meta']['sepa_cc'] ?? null,
'sepa_ct_op' => $transaction['meta']['sepa_ct_op'] ?? null, 'sepa_ct_op' => $transaction['meta']['sepa_ct_op'] ?? null,
'sepa_ct_id' => $transaction['meta']['sepa_ct_id'] ?? null, 'sepa_ct_id' => $transaction['meta']['sepa_ct_id'] ?? null,
'sepa_db' => $transaction['meta']['sepa_db'] ?? null, 'sepa_db' => $transaction['meta']['sepa_db'] ?? null,
'sepa_country' => $transaction['meta']['sepa_country'] ?? null, 'sepa_country' => $transaction['meta']['sepa_country'] ?? null,
'sepa_ep' => $transaction['meta']['sepa_ep'] ?? null, 'sepa_ep' => $transaction['meta']['sepa_ep'] ?? null,
'sepa_ci' => $transaction['meta']['sepa_ci'] ?? null, 'sepa_ci' => $transaction['meta']['sepa_ci'] ?? null,
'sepa_batch_id' => $transaction['meta']['sepa_batch_id'] ?? null, 'sepa_batch_id' => $transaction['meta']['sepa_batch_id'] ?? null,
'interest_date' => array_key_exists('interest_date', $transaction['meta_date']) ? $transaction['meta_date']['interest_date']->toW3CString() : null, 'interest_date' => array_key_exists('interest_date', $transaction['meta_date']) ? $transaction['meta_date']['interest_date']->toW3CString() : null,
'book_date' => array_key_exists('book_date', $transaction['meta_date']) ? $transaction['meta_date']['book_date']->toW3CString() : null, 'book_date' => array_key_exists('book_date', $transaction['meta_date']) ? $transaction['meta_date']['book_date']->toW3CString() : null,
'process_date' => array_key_exists('process_date', $transaction['meta_date']) ? $transaction['meta_date']['process_date']->toW3CString() : null, 'process_date' => array_key_exists('process_date', $transaction['meta_date']) ? $transaction['meta_date']['process_date']->toW3CString() : null,
'due_date' => array_key_exists('due_date', $transaction['meta_date']) ? $transaction['meta_date']['due_date']->toW3CString() : null, 'due_date' => array_key_exists('due_date', $transaction['meta_date']) ? $transaction['meta_date']['due_date']->toW3CString() : null,
'payment_date' => array_key_exists('payment_date', $transaction['meta_date']) ? $transaction['meta_date']['payment_date']->toW3CString() : null, 'payment_date' => array_key_exists('payment_date', $transaction['meta_date']) ? $transaction['meta_date']['payment_date']->toW3CString() : null,
'invoice_date' => array_key_exists('invoice_date', $transaction['meta_date']) ? $transaction['meta_date']['invoice_date']->toW3CString() : null, 'invoice_date' => array_key_exists('invoice_date', $transaction['meta_date']) ? $transaction['meta_date']['invoice_date']->toW3CString() : null,
// location data // location data
'longitude' => $transaction['location']['longitude'], 'longitude' => $transaction['location']['longitude'],
'latitude' => $transaction['location']['latitude'], 'latitude' => $transaction['location']['latitude'],
'zoom_level' => $transaction['location']['zoom_level'], 'zoom_level' => $transaction['location']['zoom_level'],
'has_attachments' => $transaction['attachment_count'] > 0, 'has_attachments' => $transaction['attachment_count'] > 0,
]; ];
} }
@@ -283,7 +283,7 @@ class TransactionGroupTransformer extends AbstractTransformer
'links' => [ 'links' => [
[ [
'rel' => 'self', 'rel' => 'self',
'uri' => '/transactions/' . $group->id, 'uri' => '/transactions/'.$group->id,
], ],
], ],
]; ];
@@ -338,10 +338,10 @@ class TransactionGroupTransformer extends AbstractTransformer
$foreignAmount = Steam::bcround($foreignAmount, $foreignCurrency['decimal_places'] ?? 0); $foreignAmount = Steam::bcround($foreignAmount, $foreignCurrency['decimal_places'] ?? 0);
} }
$longitude = null; $longitude = null;
$latitude = null; $latitude = null;
$zoomLevel = null; $zoomLevel = null;
$location = $this->getLocation($journal); $location = $this->getLocation($journal);
if ($location instanceof Location) { if ($location instanceof Location) {
$longitude = $location->longitude; $longitude = $location->longitude;
$latitude = $location->latitude; $latitude = $location->latitude;
@@ -349,77 +349,77 @@ class TransactionGroupTransformer extends AbstractTransformer
} }
return [ return [
'user' => $journal->user_id, 'user' => $journal->user_id,
'transaction_journal_id' => (string) $journal->id, 'transaction_journal_id' => (string) $journal->id,
'type' => strtolower((string) $type), 'type' => strtolower((string) $type),
'date' => $journal->date->toAtomString(), 'date' => $journal->date->toAtomString(),
'order' => $journal->order, 'order' => $journal->order,
'currency_id' => (string) $currency->id, 'currency_id' => (string) $currency->id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $currency->decimal_places,
'foreign_currency_id' => (string) $foreignCurrency['id'], 'foreign_currency_id' => (string) $foreignCurrency['id'],
'foreign_currency_code' => $foreignCurrency['code'], 'foreign_currency_code' => $foreignCurrency['code'],
'foreign_currency_symbol' => $foreignCurrency['symbol'], 'foreign_currency_symbol' => $foreignCurrency['symbol'],
'foreign_currency_decimal_places' => $foreignCurrency['decimal_places'], 'foreign_currency_decimal_places' => $foreignCurrency['decimal_places'],
'amount' => Steam::bcround($amount, $currency->decimal_places), 'amount' => Steam::bcround($amount, $currency->decimal_places),
'foreign_amount' => $foreignAmount, 'foreign_amount' => $foreignAmount,
'description' => $journal->description, 'description' => $journal->description,
'source_id' => (string) $source->account_id, 'source_id' => (string) $source->account_id,
'source_name' => $source->account->name, 'source_name' => $source->account->name,
'source_iban' => $source->account->iban, 'source_iban' => $source->account->iban,
'source_type' => $source->account->accountType->type, 'source_type' => $source->account->accountType->type,
'destination_id' => (string) $destination->account_id, 'destination_id' => (string) $destination->account_id,
'destination_name' => $destination->account->name, 'destination_name' => $destination->account->name,
'destination_iban' => $destination->account->iban, 'destination_iban' => $destination->account->iban,
'destination_type' => $destination->account->accountType->type, 'destination_type' => $destination->account->accountType->type,
'budget_id' => (string) $budget['id'], 'budget_id' => (string) $budget['id'],
'budget_name' => $budget['name'], 'budget_name' => $budget['name'],
'category_id' => (string) $category['id'], 'category_id' => (string) $category['id'],
'category_name' => $category['name'], 'category_name' => $category['name'],
'bill_id' => (string) $bill['id'], 'bill_id' => (string) $bill['id'],
'bill_name' => $bill['name'], 'bill_name' => $bill['name'],
'reconciled' => $source->reconciled, 'reconciled' => $source->reconciled,
'notes' => $this->groupRepos->getNoteText($journal->id), 'notes' => $this->groupRepos->getNoteText($journal->id),
'tags' => $this->groupRepos->getTags($journal->id), 'tags' => $this->groupRepos->getTags($journal->id),
'internal_reference' => $metaFieldData['internal_reference'], 'internal_reference' => $metaFieldData['internal_reference'],
'external_id' => $metaFieldData['external_id'], 'external_id' => $metaFieldData['external_id'],
'original_source' => $metaFieldData['original_source'], 'original_source' => $metaFieldData['original_source'],
'recurrence_id' => $metaFieldData['recurrence_id'], 'recurrence_id' => $metaFieldData['recurrence_id'],
'bunq_payment_id' => $metaFieldData['bunq_payment_id'], 'bunq_payment_id' => $metaFieldData['bunq_payment_id'],
'import_hash_v2' => $metaFieldData['import_hash_v2'], 'import_hash_v2' => $metaFieldData['import_hash_v2'],
'sepa_cc' => $metaFieldData['sepa_cc'], 'sepa_cc' => $metaFieldData['sepa_cc'],
'sepa_ct_op' => $metaFieldData['sepa_ct_op'], 'sepa_ct_op' => $metaFieldData['sepa_ct_op'],
'sepa_ct_id' => $metaFieldData['sepa_ct_id'], 'sepa_ct_id' => $metaFieldData['sepa_ct_id'],
'sepa_db' => $metaFieldData['sepa_db'], 'sepa_db' => $metaFieldData['sepa_db'],
'sepa_country' => $metaFieldData['sepa_country'], 'sepa_country' => $metaFieldData['sepa_country'],
'sepa_ep' => $metaFieldData['sepa_ep'], 'sepa_ep' => $metaFieldData['sepa_ep'],
'sepa_ci' => $metaFieldData['sepa_ci'], 'sepa_ci' => $metaFieldData['sepa_ci'],
'sepa_batch_id' => $metaFieldData['sepa_batch_id'], 'sepa_batch_id' => $metaFieldData['sepa_batch_id'],
'interest_date' => $metaDates['interest_date'], 'interest_date' => $metaDates['interest_date'],
'book_date' => $metaDates['book_date'], 'book_date' => $metaDates['book_date'],
'process_date' => $metaDates['process_date'], 'process_date' => $metaDates['process_date'],
'due_date' => $metaDates['due_date'], 'due_date' => $metaDates['due_date'],
'payment_date' => $metaDates['payment_date'], 'payment_date' => $metaDates['payment_date'],
'invoice_date' => $metaDates['invoice_date'], 'invoice_date' => $metaDates['invoice_date'],
// location data // location data
'longitude' => $longitude, 'longitude' => $longitude,
'latitude' => $latitude, 'latitude' => $latitude,
'zoom_level' => $zoomLevel, 'zoom_level' => $zoomLevel,
]; ];
} }
@@ -494,7 +494,7 @@ class TransactionGroupTransformer extends AbstractTransformer
private function getForeignCurrency(?TransactionCurrency $currency): array private function getForeignCurrency(?TransactionCurrency $currency): array
{ {
$array = [ $array = [
'id' => null, 'id' => null,
'code' => null, 'code' => null,
'symbol' => null, 'symbol' => null,
@@ -513,7 +513,7 @@ class TransactionGroupTransformer extends AbstractTransformer
private function getBudget(?Budget $budget): array private function getBudget(?Budget $budget): array
{ {
$array = [ $array = [
'id' => null, 'id' => null,
'name' => null, 'name' => null,
]; ];
@@ -528,7 +528,7 @@ class TransactionGroupTransformer extends AbstractTransformer
private function getCategory(?Category $category): array private function getCategory(?Category $category): array
{ {
$array = [ $array = [
'id' => null, 'id' => null,
'name' => null, 'name' => null,
]; ];
@@ -543,7 +543,7 @@ class TransactionGroupTransformer extends AbstractTransformer
private function getBill(?Bill $bill): array private function getBill(?Bill $bill): array
{ {
$array = [ $array = [
'id' => null, 'id' => null,
'name' => null, 'name' => null,
]; ];

10
composer.lock generated
View File

@@ -3711,16 +3711,16 @@
}, },
{ {
"name": "nesbot/carbon", "name": "nesbot/carbon",
"version": "3.10.1", "version": "3.10.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/CarbonPHP/carbon.git", "url": "https://github.com/CarbonPHP/carbon.git",
"reference": "1fd1935b2d90aef2f093c5e35f7ae1257c448d00" "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/1fd1935b2d90aef2f093c5e35f7ae1257c448d00", "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24",
"reference": "1fd1935b2d90aef2f093c5e35f7ae1257c448d00", "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3812,7 +3812,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-06-21T15:19:35+00:00" "time": "2025-08-02T09:36:06+00:00"
}, },
{ {
"name": "nette/schema", "name": "nette/schema",

View File

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

0
public/v1/js/.gitkeep Executable file → Normal file
View File

View File

@@ -110,7 +110,6 @@
"/public/v1/js/lib/jquery.autocomplete.min.js": "/public/v1/js/lib/jquery.autocomplete.min.js", "/public/v1/js/lib/jquery.autocomplete.min.js": "/public/v1/js/lib/jquery.autocomplete.min.js",
"/public/v1/js/lib/jquery.color-2.1.2.min.js": "/public/v1/js/lib/jquery.color-2.1.2.min.js", "/public/v1/js/lib/jquery.color-2.1.2.min.js": "/public/v1/js/lib/jquery.color-2.1.2.min.js",
"/public/v1/js/lib/modernizr-custom.js": "/public/v1/js/lib/modernizr-custom.js", "/public/v1/js/lib/modernizr-custom.js": "/public/v1/js/lib/modernizr-custom.js",
"/public/v1/js/lib/moment/af_ZA.js": "/public/v1/js/lib/moment/af_ZA.js",
"/public/v1/js/lib/moment/bg_BG.js": "/public/v1/js/lib/moment/bg_BG.js", "/public/v1/js/lib/moment/bg_BG.js": "/public/v1/js/lib/moment/bg_BG.js",
"/public/v1/js/lib/moment/ca_ES.js": "/public/v1/js/lib/moment/ca_ES.js", "/public/v1/js/lib/moment/ca_ES.js": "/public/v1/js/lib/moment/ca_ES.js",
"/public/v1/js/lib/moment/cs_CZ.js": "/public/v1/js/lib/moment/cs_CZ.js", "/public/v1/js/lib/moment/cs_CZ.js": "/public/v1/js/lib/moment/cs_CZ.js",

View File

@@ -154,7 +154,7 @@
"url": "URL", "url": "URL",
"active": "Aktywny", "active": "Aktywny",
"interest_date": "Data odsetek", "interest_date": "Data odsetek",
"administration_currency": "Primary currency", "administration_currency": "Waluta podstawowa",
"title": "Tytu\u0142", "title": "Tytu\u0142",
"date": "Data", "date": "Data",
"book_date": "Data ksi\u0119gowania", "book_date": "Data ksi\u0119gowania",
@@ -174,7 +174,7 @@
"list": { "list": {
"title": "Tytu\u0142", "title": "Tytu\u0142",
"active": "Jest aktywny?", "active": "Jest aktywny?",
"primary_currency": "Primary currency", "primary_currency": "Waluta podstawowa",
"trigger": "Wyzwalacz", "trigger": "Wyzwalacz",
"response": "Odpowied\u017a", "response": "Odpowied\u017a",
"delivery": "Dor\u0119czenie", "delivery": "Dor\u0119czenie",