mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-03-01 14:51:07 +00:00
Compare commits
16 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd0c342131 | ||
|
|
bb51baaa38 | ||
|
|
6bae8ab70a | ||
|
|
88030784a5 | ||
|
|
53b733fddb | ||
|
|
ca89159ccd | ||
|
|
a5db3dd2e9 | ||
|
|
b299465fb2 | ||
|
|
12a877d489 | ||
|
|
7f64251a55 | ||
|
|
c06d8263d8 | ||
|
|
5451328ea6 | ||
|
|
3e5ef0b431 | ||
|
|
87fb1fcc92 | ||
|
|
85da46243b | ||
|
|
0c0736d336 |
@@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
|
||||
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
|
||||
|
||||
## 2026
|
||||
- Matthew Grove
|
||||
- Cinnamon Pyro
|
||||
- R1DEN
|
||||
- RiDEN
|
||||
|
||||
@@ -128,21 +128,16 @@ class ListController extends Controller
|
||||
*/
|
||||
public function transactions(ListRequest $request, Account $account): JsonResponse
|
||||
{
|
||||
[
|
||||
'limit' => $limit,
|
||||
'page' => $page,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'types' => $types,
|
||||
] = $request->attributes->all();
|
||||
$manager = $this->getManager();
|
||||
['limit' => $limit, 'page' => $page, 'start' => $start, 'end' => $end, 'type' => $type] = $request->attributes->all();
|
||||
$types = $this->mapTransactionTypes($type ?? 'default');
|
||||
$manager = $this->getManager();
|
||||
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$admin = auth()->user();
|
||||
|
||||
// use new group collector:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($admin)->setAccounts(new Collection()->push($account))->withAPIInformation()->setLimit($limit)->setPage($page)->setTypes($types);
|
||||
if (null !== $start) {
|
||||
$collector->setStart($start);
|
||||
@@ -151,18 +146,18 @@ class ListController extends Controller
|
||||
$collector->setEnd($end);
|
||||
}
|
||||
|
||||
$paginator = $collector->getPaginatedGroups();
|
||||
$paginator = $collector->getPaginatedGroups();
|
||||
$paginator->setPath(route('api.v1.accounts.transactions', [$account->id]).$this->buildParams());
|
||||
|
||||
// enrich
|
||||
$enrichment = new TransactionGroupEnrichment();
|
||||
$enrichment = new TransactionGroupEnrichment();
|
||||
$enrichment->setUser($admin);
|
||||
$transactions = $enrichment->enrich($paginator->getCollection());
|
||||
$transactions = $enrichment->enrich($paginator->getCollection());
|
||||
|
||||
/** @var TransactionGroupTransformer $transformer */
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
|
||||
$resource = new FractalCollection($transactions, $transformer, 'transactions');
|
||||
$resource = new FractalCollection($transactions, $transformer, 'transactions');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
|
||||
@@ -74,8 +74,8 @@ class UpdateController extends Controller
|
||||
*/
|
||||
public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse
|
||||
{
|
||||
Log::debug('Now in update routine for transaction group');
|
||||
$data = $request->getAll();
|
||||
Log::debug('Now in update routine for transaction group', $data);
|
||||
$oldHash = $this->groupRepository->getCompareHash($transactionGroup);
|
||||
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($transactionGroup);
|
||||
$transactionGroup = $this->groupRepository->update($transactionGroup, $data);
|
||||
@@ -92,6 +92,7 @@ class UpdateController extends Controller
|
||||
$flags->applyRules = $applyRules;
|
||||
$flags->fireWebhooks = $fireWebhooks;
|
||||
$flags->recalculateCredit = $runRecalculations;
|
||||
$flags->batchSubmission = $data['batch_submission'] ?? false;
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class BatchController extends Controller
|
||||
{
|
||||
@@ -52,19 +53,25 @@ class BatchController extends Controller
|
||||
|
||||
public function finishBatch(Request $request): JsonResponse
|
||||
{
|
||||
Log::debug('Now in finishBatch.');
|
||||
$journals = $this->repository->getUncompletedJournals();
|
||||
if (0 === count($journals)) {
|
||||
Log::debug('Counted zero journals, return.');
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
Log::debug(sprintf('Counted %d journals.', count($journals)));
|
||||
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $journals->first();
|
||||
$group = $first?->transactionGroup;
|
||||
if (null === $group) {
|
||||
Log::debug('First group is NULL.');
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$flags->applyRules = 'true' === $request->get('apply_rules');
|
||||
$flags->applyRules = 'true' === $request->input('apply_rules');
|
||||
event(new UserRequestedBatchProcessing($flags));
|
||||
// event(new CreatedSingleTransactionGroup($group, $flags));
|
||||
|
||||
|
||||
@@ -603,7 +603,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
public function setSearchWords(array $array): GroupCollectorInterface
|
||||
{
|
||||
if (0 === count($array)) {
|
||||
Log::debug('No words in array');
|
||||
// Log::debug('No words in array');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -34,10 +34,10 @@ use FireflyIII\Support\CacheProperties;
|
||||
use FireflyIII\Support\Chart\Category\FrontpageChartGenerator;
|
||||
use FireflyIII\Support\Chart\Category\WholePeriodChartGenerator;
|
||||
use FireflyIII\Support\Facades\Navigation;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||
use FireflyIII\Support\Http\Controllers\ChartGeneration;
|
||||
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
||||
use FireflyIII\Support\Http\Controllers\ResolvesJournalAmountAndCurrency;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
@@ -51,6 +51,7 @@ class CategoryController extends Controller
|
||||
use AugumentData;
|
||||
use ChartGeneration;
|
||||
use DateCalculation;
|
||||
use ResolvesJournalAmountAndCurrency;
|
||||
|
||||
protected GeneratorInterface $generator;
|
||||
|
||||
@@ -267,7 +268,8 @@ class CategoryController extends Controller
|
||||
// loop income and expenses for this category.:
|
||||
$outSet = $expenses[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []];
|
||||
foreach ($outSet['transaction_journals'] as $journal) {
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currencyInfo);
|
||||
$amount = $journalData['amount'];
|
||||
$date = $journal['date']->isoFormat($format);
|
||||
$chartData[$outKey]['entries'][$date] ??= '0';
|
||||
|
||||
@@ -276,7 +278,8 @@ class CategoryController extends Controller
|
||||
|
||||
$inSet = $income[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []];
|
||||
foreach ($inSet['transaction_journals'] as $journal) {
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currencyInfo);
|
||||
$amount = $journalData['amount'];
|
||||
$date = $journal['date']->isoFormat($format);
|
||||
$chartData[$inKey]['entries'][$date] ??= '0';
|
||||
$chartData[$inKey]['entries'][$date] = bcadd($amount, $chartData[$inKey]['entries'][$date]);
|
||||
|
||||
@@ -29,8 +29,8 @@ use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Category\OperationsRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Navigation;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||
use FireflyIII\Support\Http\Controllers\ResolvesJournalAmountAndCurrency;
|
||||
use FireflyIII\Support\Http\Controllers\TransactionCalculation;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -43,6 +43,7 @@ use Illuminate\Support\Collection;
|
||||
class CategoryReportController extends Controller
|
||||
{
|
||||
use AugumentData;
|
||||
use ResolvesJournalAmountAndCurrency;
|
||||
use TransactionCalculation;
|
||||
|
||||
private GeneratorInterface $generator;
|
||||
@@ -73,15 +74,15 @@ class CategoryReportController extends Controller
|
||||
/** @var array $category */
|
||||
foreach ($currency['categories'] as $category) {
|
||||
foreach ($category['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['budget_name'] ?? trans('firefly.no_budget');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,15 +101,15 @@ class CategoryReportController extends Controller
|
||||
foreach ($spent as $currency) {
|
||||
/** @var array $category */
|
||||
foreach ($currency['categories'] as $category) {
|
||||
$title = sprintf('%s (%s)', $category['name'], $currency['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
];
|
||||
foreach ($category['transaction_journals'] as $journal) {
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$title = sprintf('%s (%s)', $category['name'], $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,15 +128,15 @@ class CategoryReportController extends Controller
|
||||
foreach ($earned as $currency) {
|
||||
/** @var array $category */
|
||||
foreach ($currency['categories'] as $category) {
|
||||
$title = sprintf('%s (%s)', $category['name'], $currency['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
];
|
||||
foreach ($category['transaction_journals'] as $journal) {
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$title = sprintf('%s (%s)', $category['name'], $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,15 +156,15 @@ class CategoryReportController extends Controller
|
||||
/** @var array $category */
|
||||
foreach ($currency['categories'] as $category) {
|
||||
foreach ($category['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['destination_account_name'] ?? trans('firefly.empty');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,15 +184,15 @@ class CategoryReportController extends Controller
|
||||
/** @var array $category */
|
||||
foreach ($currency['categories'] as $category) {
|
||||
foreach ($category['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['destination_account_name'] ?? trans('firefly.empty');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,26 +211,25 @@ class CategoryReportController extends Controller
|
||||
// loop expenses.
|
||||
foreach ($spent as $currency) {
|
||||
// add things to chart Data for each currency:
|
||||
$spentKey = sprintf('%d-spent', $currency['currency_id']);
|
||||
$chartData[$spentKey] ??= [
|
||||
'label' => sprintf(
|
||||
'%s (%s)',
|
||||
(string) trans('firefly.spent_in_specific_category', ['category' => $category->name]),
|
||||
$currency['currency_name']
|
||||
),
|
||||
'type' => 'bar',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_id' => $currency['currency_id'],
|
||||
'entries' => $this->makeEntries($start, $end),
|
||||
];
|
||||
|
||||
foreach ($currency['categories'] as $currentCategory) {
|
||||
foreach ($currentCategory['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$spentKey = sprintf('%d-spent', $journalData['currency_id']);
|
||||
$chartData[$spentKey] ??= [
|
||||
'label' => sprintf(
|
||||
'%s (%s)',
|
||||
(string) trans('firefly.spent_in_specific_category', ['category' => $category->name]),
|
||||
$journalData['currency_name']
|
||||
),
|
||||
'type' => 'bar',
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
'currency_id' => $journalData['currency_id'],
|
||||
'entries' => $this->makeEntries($start, $end),
|
||||
];
|
||||
$key = $journal['date']->isoFormat($format);
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$chartData[$spentKey]['entries'][$key] ??= '0';
|
||||
$chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount);
|
||||
$chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,26 +237,25 @@ class CategoryReportController extends Controller
|
||||
// loop income.
|
||||
foreach ($earned as $currency) {
|
||||
// add things to chart Data for each currency:
|
||||
$spentKey = sprintf('%d-earned', $currency['currency_id']);
|
||||
$chartData[$spentKey] ??= [
|
||||
'label' => sprintf(
|
||||
'%s (%s)',
|
||||
(string) trans('firefly.earned_in_specific_category', ['category' => $category->name]),
|
||||
$currency['currency_name']
|
||||
),
|
||||
'type' => 'bar',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_id' => $currency['currency_id'],
|
||||
'entries' => $this->makeEntries($start, $end),
|
||||
];
|
||||
|
||||
foreach ($currency['categories'] as $currentCategory) {
|
||||
foreach ($currentCategory['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$spentKey = sprintf('%d-earned', $journalData['currency_id']);
|
||||
$chartData[$spentKey] ??= [
|
||||
'label' => sprintf(
|
||||
'%s (%s)',
|
||||
(string) trans('firefly.earned_in_specific_category', ['category' => $category->name]),
|
||||
$journalData['currency_name']
|
||||
),
|
||||
'type' => 'bar',
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
'currency_id' => $journalData['currency_id'],
|
||||
'entries' => $this->makeEntries($start, $end),
|
||||
];
|
||||
$key = $journal['date']->isoFormat($format);
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$chartData[$spentKey]['entries'][$key] ??= '0';
|
||||
$chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount);
|
||||
$chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,15 +275,15 @@ class CategoryReportController extends Controller
|
||||
/** @var array $category */
|
||||
foreach ($currency['categories'] as $category) {
|
||||
foreach ($category['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['source_account_name'] ?? trans('firefly.empty');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,15 +303,15 @@ class CategoryReportController extends Controller
|
||||
/** @var array $category */
|
||||
foreach ($currency['categories'] as $category) {
|
||||
foreach ($category['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['source_account_name'] ?? trans('firefly.empty');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ use FireflyIII\Support\Facades\Navigation;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Controllers\BasicDataSupport;
|
||||
use FireflyIII\Support\Http\Controllers\ChartGeneration;
|
||||
use FireflyIII\Support\Http\Controllers\ResolvesJournalAmountAndCurrency;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -47,6 +48,7 @@ class ReportController extends Controller
|
||||
{
|
||||
use BasicDataSupport;
|
||||
use ChartGeneration;
|
||||
use ResolvesJournalAmountAndCurrency;
|
||||
|
||||
protected GeneratorInterface $generator;
|
||||
|
||||
@@ -149,7 +151,7 @@ class ReportController extends Controller
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($this->convertToPrimary);
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get());
|
||||
// return response()->json($cache->get());
|
||||
}
|
||||
|
||||
Log::debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray());
|
||||
@@ -177,21 +179,13 @@ class ReportController extends Controller
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$period = $journal['date']->format($format);
|
||||
$currencyId = (int) $journal['currency_id'];
|
||||
$currencySymbol = (string) $journal['currency_symbol'];
|
||||
$currencyCode = (string) $journal['currency_code'];
|
||||
$currencyName = (string) $journal['currency_name'];
|
||||
$currencyDecimalPlaces = (int) $journal['currency_decimal_places'];
|
||||
$amount = (string) $journal['amount'];
|
||||
|
||||
if ($this->convertToPrimary && null !== $this->primaryCurrency && $journal['currency_id'] !== $this->primaryCurrency->id) {
|
||||
$currencyId = $this->primaryCurrency->id;
|
||||
$currencySymbol = $this->primaryCurrency->symbol;
|
||||
$currencyCode = $this->primaryCurrency->code;
|
||||
$currencyName = $this->primaryCurrency->name;
|
||||
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
|
||||
$amount = $journal['foreign_currency_id'] === $this->primaryCurrency->id ? $journal['foreign_amount'] : $journal['pc_amount'];
|
||||
}
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $journal);
|
||||
$currencyId = $journalData['currency_id'];
|
||||
$currencySymbol = $journalData['currency_symbol'];
|
||||
$currencyCode = $journalData['currency_code'];
|
||||
$currencyName = $journalData['currency_name'];
|
||||
$currencyDecimalPlaces = $journalData['currency_decimal_places'];
|
||||
$amount = $journalData['amount'];
|
||||
|
||||
$data[$currencyId] ??= [
|
||||
'currency_id' => $currencyId,
|
||||
@@ -203,7 +197,6 @@ class ReportController extends Controller
|
||||
$data[$currencyId][$period] ??= ['period' => $period, 'spent' => '0', 'earned' => '0'];
|
||||
// in our outgoing?
|
||||
$key = 'spent';
|
||||
$amount = Steam::positive($amount);
|
||||
|
||||
// deposit = incoming
|
||||
// transfer or reconcile or opening balance, and these accounts are the destination.
|
||||
@@ -226,7 +219,7 @@ class ReportController extends Controller
|
||||
|
||||
/** @var array $currency */
|
||||
foreach ($data as $currency) {
|
||||
Log::debug(sprintf('Now processing currency "%s"', $currency['currency_name']));
|
||||
Log::debug(sprintf('Now processing currency %s', $currency['currency_code']));
|
||||
$income = [
|
||||
'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
|
||||
'type' => 'bar',
|
||||
@@ -254,7 +247,11 @@ class ReportController extends Controller
|
||||
if ('1Y' === $preferredRange) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentEnd, $preferredRange);
|
||||
}
|
||||
Log::debug('Start of sub-loop');
|
||||
// 2026-03-01 similar fix for monthly ranges.
|
||||
if ('1M' === $preferredRange) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentEnd, $preferredRange);
|
||||
}
|
||||
Log::debug(sprintf('Start of sub-loop, current end is %s', $currentEnd->toW3cString()));
|
||||
while ($currentStart <= $currentEnd) {
|
||||
Log::debug(sprintf('Current start: %s', $currentStart->toW3cString()));
|
||||
$key = $currentStart->format($format);
|
||||
|
||||
@@ -29,9 +29,8 @@ use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Repositories\Tag\OperationsRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Navigation;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||
use FireflyIII\Support\Http\Controllers\TransactionCalculation;
|
||||
use FireflyIII\Support\Http\Controllers\ResolvesJournalAmountAndCurrency;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@@ -41,13 +40,11 @@ use Illuminate\Support\Collection;
|
||||
class TagReportController extends Controller
|
||||
{
|
||||
use AugumentData;
|
||||
use TransactionCalculation;
|
||||
use ResolvesJournalAmountAndCurrency;
|
||||
|
||||
/** @var GeneratorInterface Chart generation methods. */
|
||||
protected $generator;
|
||||
private GeneratorInterface $generator;
|
||||
|
||||
/** @var OperationsRepositoryInterface */
|
||||
private $opsRepository;
|
||||
private OperationsRepositoryInterface $opsRepository;
|
||||
|
||||
/**
|
||||
* TagReportController constructor.
|
||||
@@ -55,10 +52,8 @@ class TagReportController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
|
||||
$this->middleware(function ($request, $next) {
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
@@ -75,15 +70,15 @@ class TagReportController extends Controller
|
||||
/** @var array $tag */
|
||||
foreach ($currency['tags'] as $tag) {
|
||||
foreach ($tag['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['budget_name'] ?? trans('firefly.no_budget');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,15 +98,15 @@ class TagReportController extends Controller
|
||||
/** @var array $tag */
|
||||
foreach ($currency['tags'] as $tag) {
|
||||
foreach ($tag['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['category_name'] ?? trans('firefly.no_category');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,15 +126,15 @@ class TagReportController extends Controller
|
||||
/** @var array $tag */
|
||||
foreach ($currency['tags'] as $tag) {
|
||||
foreach ($tag['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['category_name'] ?? trans('firefly.no_category');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,15 +154,15 @@ class TagReportController extends Controller
|
||||
/** @var array $tag */
|
||||
foreach ($currency['tags'] as $tag) {
|
||||
foreach ($tag['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['destination_account_name'] ?? trans('firefly.empty');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,15 +182,15 @@ class TagReportController extends Controller
|
||||
/** @var array $tag */
|
||||
foreach ($currency['tags'] as $tag) {
|
||||
foreach ($tag['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['destination_account_name'] ?? trans('firefly.empty');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,22 +213,25 @@ class TagReportController extends Controller
|
||||
// loop expenses.
|
||||
foreach ($spent as $currency) {
|
||||
// add things to chart Data for each currency:
|
||||
$spentKey = sprintf('%d-spent', $currency['currency_id']);
|
||||
$chartData[$spentKey] ??= [
|
||||
'label' => sprintf('%s (%s)', (string) trans('firefly.spent_in_specific_tag', ['tag' => $tag->tag]), $currency['currency_name']),
|
||||
'type' => 'bar',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_id' => $currency['currency_id'],
|
||||
'entries' => $this->makeEntries($start, $end),
|
||||
];
|
||||
|
||||
foreach ($currency['tags'] as $currentTag) {
|
||||
foreach ($currentTag['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$spentKey = sprintf('%d-spent', $journalData['currency_id']);
|
||||
$chartData[$spentKey] ??= [
|
||||
'label' => sprintf(
|
||||
'%s (%s)',
|
||||
(string) trans('firefly.spent_in_specific_tag', ['tag' => $tag->tag]),
|
||||
$journalData['currency_name']
|
||||
),
|
||||
'type' => 'bar',
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
'currency_id' => $journalData['currency_id'],
|
||||
'entries' => $this->makeEntries($start, $end),
|
||||
];
|
||||
$key = $journal['date']->isoFormat($format);
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$chartData[$spentKey]['entries'][$key] ??= '0';
|
||||
$chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount);
|
||||
$chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,22 +239,25 @@ class TagReportController extends Controller
|
||||
// loop income.
|
||||
foreach ($earned as $currency) {
|
||||
// add things to chart Data for each currency:
|
||||
$spentKey = sprintf('%d-earned', $currency['currency_id']);
|
||||
$chartData[$spentKey] ??= [
|
||||
'label' => sprintf('%s (%s)', (string) trans('firefly.earned_in_specific_tag', ['tag' => $tag->tag]), $currency['currency_name']),
|
||||
'type' => 'bar',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_id' => $currency['currency_id'],
|
||||
'entries' => $this->makeEntries($start, $end),
|
||||
];
|
||||
|
||||
foreach ($currency['tags'] as $currentTag) {
|
||||
foreach ($currentTag['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$spentKey = sprintf('%d-earned', $journalData['currency_id']);
|
||||
$chartData[$spentKey] ??= [
|
||||
'label' => sprintf(
|
||||
'%s (%s)',
|
||||
(string) trans('firefly.earned_in_specific_tag', ['tag' => $tag->tag]),
|
||||
$journalData['currency_name']
|
||||
),
|
||||
'type' => 'bar',
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
'currency_id' => $journalData['currency_id'],
|
||||
'entries' => $this->makeEntries($start, $end),
|
||||
];
|
||||
$key = $journal['date']->isoFormat($format);
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$chartData[$spentKey]['entries'][$key] ??= '0';
|
||||
$chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount);
|
||||
$chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,15 +277,15 @@ class TagReportController extends Controller
|
||||
/** @var array $tag */
|
||||
foreach ($currency['tags'] as $tag) {
|
||||
foreach ($tag['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['source_account_name'] ?? trans('firefly.empty');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,15 +305,15 @@ class TagReportController extends Controller
|
||||
/** @var array $tag */
|
||||
foreach ($currency['tags'] as $tag) {
|
||||
foreach ($tag['transaction_journals'] as $journal) {
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$objectName = $journal['source_account_name'] ?? trans('firefly.empty');
|
||||
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
|
||||
$title = sprintf('%s (%s)', $objectName, $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -331,15 +332,15 @@ class TagReportController extends Controller
|
||||
foreach ($spent as $currency) {
|
||||
/** @var array $tag */
|
||||
foreach ($currency['tags'] as $tag) {
|
||||
$title = sprintf('%s (%s)', $tag['name'], $currency['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
];
|
||||
foreach ($tag['transaction_journals'] as $journal) {
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$title = sprintf('%s (%s)', $tag['name'], $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,15 +358,15 @@ class TagReportController extends Controller
|
||||
foreach ($earned as $currency) {
|
||||
/** @var array $tag */
|
||||
foreach ($currency['tags'] as $tag) {
|
||||
$title = sprintf('%s (%s)', $tag['name'], $currency['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $currency['currency_symbol'],
|
||||
'currency_code' => $currency['currency_code'],
|
||||
];
|
||||
foreach ($tag['transaction_journals'] as $journal) {
|
||||
$amount = Steam::positive($journal['amount']);
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
|
||||
$journalData = $this->resolveJournalAmountAndCurrency($journal, $currency);
|
||||
$title = sprintf('%s (%s)', $tag['name'], $journalData['currency_name']);
|
||||
$result[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $journalData['currency_symbol'],
|
||||
'currency_code' => $journalData['currency_code'],
|
||||
];
|
||||
$result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use FireflyIII\Support\Http\Controllers\ResolvesJournalAmountAndCurrency;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
/**
|
||||
@@ -37,6 +38,8 @@ use Illuminate\Http\JsonResponse;
|
||||
*/
|
||||
class TransactionController extends Controller
|
||||
{
|
||||
use ResolvesJournalAmountAndCurrency;
|
||||
|
||||
/** @var GeneratorInterface Chart generation methods. */
|
||||
protected $generator;
|
||||
|
||||
@@ -57,6 +60,7 @@ class TransactionController extends Controller
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($this->convertToPrimary);
|
||||
$cache->addProperty('chart.transactions.budgets');
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get());
|
||||
@@ -74,14 +78,15 @@ class TransactionController extends Controller
|
||||
// group by category.
|
||||
/** @var array $journal */
|
||||
foreach ($result as $journal) {
|
||||
$resolved = $this->resolveJournalAmountAndCurrency($journal);
|
||||
$budget = $journal['budget_name'] ?? (string) trans('firefly.no_budget');
|
||||
$title = sprintf('%s (%s)', $budget, $journal['currency_symbol']);
|
||||
$title = sprintf('%s (%s)', $budget, $resolved['currency_symbol']);
|
||||
$data[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $journal['currency_symbol'],
|
||||
'currency_code' => $journal['currency_code'],
|
||||
'currency_symbol' => $resolved['currency_symbol'],
|
||||
'currency_code' => $resolved['currency_code'],
|
||||
];
|
||||
$data[$title]['amount'] = bcadd($data[$title]['amount'], (string) $journal['amount']);
|
||||
$data[$title]['amount'] = bcadd($data[$title]['amount'], $resolved['amount']);
|
||||
}
|
||||
$chart = $this->generator->multiCurrencyPieChart($data);
|
||||
$cache->store($chart);
|
||||
@@ -98,6 +103,7 @@ class TransactionController extends Controller
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($objectType);
|
||||
$cache->addProperty($this->convertToPrimary);
|
||||
$cache->addProperty('chart.transactions.categories');
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get());
|
||||
@@ -124,14 +130,15 @@ class TransactionController extends Controller
|
||||
// group by category.
|
||||
/** @var array $journal */
|
||||
foreach ($result as $journal) {
|
||||
$resolved = $this->resolveJournalAmountAndCurrency($journal);
|
||||
$category = $journal['category_name'] ?? (string) trans('firefly.no_category');
|
||||
$title = sprintf('%s (%s)', $category, $journal['currency_symbol']);
|
||||
$title = sprintf('%s (%s)', $category, $resolved['currency_symbol']);
|
||||
$data[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $journal['currency_symbol'],
|
||||
'currency_code' => $journal['currency_code'],
|
||||
'currency_symbol' => $resolved['currency_symbol'],
|
||||
'currency_code' => $resolved['currency_code'],
|
||||
];
|
||||
$data[$title]['amount'] = bcadd($data[$title]['amount'], (string) $journal['amount']);
|
||||
$data[$title]['amount'] = bcadd($data[$title]['amount'], $resolved['amount']);
|
||||
}
|
||||
$chart = $this->generator->multiCurrencyPieChart($data);
|
||||
$cache->store($chart);
|
||||
@@ -148,6 +155,7 @@ class TransactionController extends Controller
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($objectType);
|
||||
$cache->addProperty($this->convertToPrimary);
|
||||
$cache->addProperty('chart.transactions.destinations');
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get());
|
||||
@@ -174,14 +182,15 @@ class TransactionController extends Controller
|
||||
// group by category.
|
||||
/** @var array $journal */
|
||||
foreach ($result as $journal) {
|
||||
$resolved = $this->resolveJournalAmountAndCurrency($journal);
|
||||
$name = $journal['destination_account_name'];
|
||||
$title = sprintf('%s (%s)', $name, $journal['currency_symbol']);
|
||||
$title = sprintf('%s (%s)', $name, $resolved['currency_symbol']);
|
||||
$data[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $journal['currency_symbol'],
|
||||
'currency_code' => $journal['currency_code'],
|
||||
'currency_symbol' => $resolved['currency_symbol'],
|
||||
'currency_code' => $resolved['currency_code'],
|
||||
];
|
||||
$data[$title]['amount'] = bcadd($data[$title]['amount'], (string) $journal['amount']);
|
||||
$data[$title]['amount'] = bcadd($data[$title]['amount'], $resolved['amount']);
|
||||
}
|
||||
$chart = $this->generator->multiCurrencyPieChart($data);
|
||||
$cache->store($chart);
|
||||
@@ -198,6 +207,7 @@ class TransactionController extends Controller
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($objectType);
|
||||
$cache->addProperty($this->convertToPrimary);
|
||||
$cache->addProperty('chart.transactions.sources');
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get());
|
||||
@@ -224,14 +234,15 @@ class TransactionController extends Controller
|
||||
// group by category.
|
||||
/** @var array $journal */
|
||||
foreach ($result as $journal) {
|
||||
$resolved = $this->resolveJournalAmountAndCurrency($journal);
|
||||
$name = $journal['source_account_name'];
|
||||
$title = sprintf('%s (%s)', $name, $journal['currency_symbol']);
|
||||
$title = sprintf('%s (%s)', $name, $resolved['currency_symbol']);
|
||||
$data[$title] ??= [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $journal['currency_symbol'],
|
||||
'currency_code' => $journal['currency_code'],
|
||||
'currency_symbol' => $resolved['currency_symbol'],
|
||||
'currency_code' => $resolved['currency_code'],
|
||||
];
|
||||
$data[$title]['amount'] = bcadd($data[$title]['amount'], (string) $journal['amount']);
|
||||
$data[$title]['amount'] = bcadd($data[$title]['amount'], $resolved['amount']);
|
||||
}
|
||||
$chart = $this->generator->multiCurrencyPieChart($data);
|
||||
$cache->store($chart);
|
||||
|
||||
@@ -54,9 +54,13 @@ trait SupportsGroupProcessingTrait
|
||||
// create and fire rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($user);
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
|
||||
$newRuleEngine->setRuleGroups($groups);
|
||||
$newRuleEngine->fire();
|
||||
foreach ($array as $journalId) {
|
||||
$newRuleEngine->removeOperator('journal_id');
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalId]);
|
||||
$newRuleEngine->fire();
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Done with processRules("%s") for %d journal(s)', $type, $set->count()));
|
||||
}
|
||||
|
||||
|
||||
@@ -78,8 +78,16 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface, UserGroupIn
|
||||
// only a subset of the fields.
|
||||
$journalId = (int) $journal['transaction_journal_id'];
|
||||
$array[$currencyId]['categories'][0]['transaction_journals'][$journalId] = [
|
||||
'amount' => Steam::negative($journal['amount']),
|
||||
'date' => $journal['date'],
|
||||
'amount' => Steam::negative($journal['amount']),
|
||||
'currency_id' => (int) $journal['currency_id'],
|
||||
'currency_name' => (string) $journal['currency_name'],
|
||||
'currency_symbol' => (string) $journal['currency_symbol'],
|
||||
'currency_code' => (string) $journal['currency_code'],
|
||||
'currency_decimal_places' => (int) $journal['currency_decimal_places'],
|
||||
'foreign_currency_id' => (int) ($journal['foreign_currency_id'] ?? 0),
|
||||
'foreign_amount' => isset($journal['foreign_amount']) ? Steam::negative((string) $journal['foreign_amount']) : null,
|
||||
'pc_amount' => isset($journal['pc_amount']) ? Steam::negative((string) $journal['pc_amount']) : null,
|
||||
'date' => $journal['date'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -124,8 +132,16 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface, UserGroupIn
|
||||
// only a subset of the fields.
|
||||
$journalId = (int) $journal['transaction_journal_id'];
|
||||
$array[$currencyId]['categories'][0]['transaction_journals'][$journalId] = [
|
||||
'amount' => Steam::positive($journal['amount']),
|
||||
'date' => $journal['date'],
|
||||
'amount' => Steam::positive($journal['amount']),
|
||||
'currency_id' => (int) $journal['currency_id'],
|
||||
'currency_name' => (string) $journal['currency_name'],
|
||||
'currency_symbol' => (string) $journal['currency_symbol'],
|
||||
'currency_code' => (string) $journal['currency_code'],
|
||||
'currency_decimal_places' => (int) $journal['currency_decimal_places'],
|
||||
'foreign_currency_id' => (int) ($journal['foreign_currency_id'] ?? 0),
|
||||
'foreign_amount' => isset($journal['foreign_amount']) ? Steam::positive((string) $journal['foreign_amount']) : null,
|
||||
'pc_amount' => isset($journal['pc_amount']) ? Steam::positive((string) $journal['pc_amount']) : null,
|
||||
'date' => $journal['date'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -153,6 +153,14 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
|
||||
$journalId = (int) $journal['transaction_journal_id'];
|
||||
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
|
||||
'amount' => Steam::negative($journal['amount']),
|
||||
'currency_id' => (int) $journal['currency_id'],
|
||||
'currency_name' => (string) $journal['currency_name'],
|
||||
'currency_symbol' => (string) $journal['currency_symbol'],
|
||||
'currency_code' => (string) $journal['currency_code'],
|
||||
'currency_decimal_places' => (int) $journal['currency_decimal_places'],
|
||||
'foreign_currency_id' => (int) ($journal['foreign_currency_id'] ?? 0),
|
||||
'foreign_amount' => isset($journal['foreign_amount']) ? Steam::negative((string) $journal['foreign_amount']) : null,
|
||||
'pc_amount' => isset($journal['pc_amount']) ? Steam::negative((string) $journal['pc_amount']) : null,
|
||||
'date' => $journal['date'],
|
||||
'source_account_id' => (string) $journal['source_account_id'],
|
||||
'budget_name' => $journal['budget_name'],
|
||||
@@ -223,6 +231,14 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
|
||||
$journalId = (int) $journal['transaction_journal_id'];
|
||||
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
|
||||
'amount' => Steam::positive($journal['amount']),
|
||||
'currency_id' => (int) $journal['currency_id'],
|
||||
'currency_name' => (string) $journal['currency_name'],
|
||||
'currency_symbol' => (string) $journal['currency_symbol'],
|
||||
'currency_code' => (string) $journal['currency_code'],
|
||||
'currency_decimal_places' => (int) $journal['currency_decimal_places'],
|
||||
'foreign_currency_id' => (int) ($journal['foreign_currency_id'] ?? 0),
|
||||
'foreign_amount' => isset($journal['foreign_amount']) ? Steam::positive((string) $journal['foreign_amount']) : null,
|
||||
'pc_amount' => isset($journal['pc_amount']) ? Steam::positive((string) $journal['pc_amount']) : null,
|
||||
'date' => $journal['date'],
|
||||
'source_account_id' => (string) $journal['source_account_id'],
|
||||
'destination_account_id' => (string) $journal['destination_account_id'],
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ResolvesJournalAmountAndCurrency.php
|
||||
* Copyright (c) 2026 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\Support\Http\Controllers;
|
||||
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
|
||||
trait ResolvesJournalAmountAndCurrency
|
||||
{
|
||||
/**
|
||||
* Normalize journal currency metadata and positive amount, honoring primary currency conversion.
|
||||
*/
|
||||
protected function resolveJournalAmountAndCurrency(array $journal, ?array $currency = null): array
|
||||
{
|
||||
$currency ??= $journal;
|
||||
|
||||
$currencyId = (int) ($journal['currency_id'] ?? $currency['currency_id']);
|
||||
$currencyName = (string) ($journal['currency_name'] ?? $currency['currency_name']);
|
||||
$currencySymbol = (string) ($journal['currency_symbol'] ?? $currency['currency_symbol']);
|
||||
$currencyCode = (string) ($journal['currency_code'] ?? $currency['currency_code']);
|
||||
$currencyDecimalPlaces = (int) ($journal['currency_decimal_places'] ?? $currency['currency_decimal_places'] ?? 2);
|
||||
$amount = (string) $journal['amount'];
|
||||
|
||||
if ($this->convertToPrimary && null !== $this->primaryCurrency && $currencyId !== $this->primaryCurrency->id) {
|
||||
$currencyId = $this->primaryCurrency->id;
|
||||
$currencyName = $this->primaryCurrency->name;
|
||||
$currencySymbol = $this->primaryCurrency->symbol;
|
||||
$currencyCode = $this->primaryCurrency->code;
|
||||
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
|
||||
$amount = (int) ($journal['foreign_currency_id'] ?? 0) === $this->primaryCurrency->id
|
||||
? (string) ($journal['foreign_amount'] ?? '0')
|
||||
: (string) ($journal['pc_amount'] ?? '0');
|
||||
}
|
||||
|
||||
return [
|
||||
'currency_id' => $currencyId,
|
||||
'currency_name' => $currencyName,
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'currency_code' => $currencyCode,
|
||||
'currency_decimal_places' => $currencyDecimalPlaces,
|
||||
'amount' => Steam::positive($amount),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -453,7 +453,7 @@ class Navigation
|
||||
$diff = $start->diffInMonths($end, true);
|
||||
// increment by month (for year)
|
||||
if ($diff >= 1.0001 && $diff < 12.001) {
|
||||
$increment = 'addMonth';
|
||||
$increment = 'addMonthsNoOverflow';
|
||||
$displayFormat = (string) trans('config.month_js');
|
||||
}
|
||||
|
||||
@@ -464,12 +464,15 @@ class Navigation
|
||||
}
|
||||
$begin = clone $start;
|
||||
$entries = [];
|
||||
Log::debug(sprintf('listOfPeriods start of loop (end: %s).', $end->format('Y-m-d H:i:s')));
|
||||
while ($begin < $end) {
|
||||
Log::debug(sprintf('Begin is now %s.', $begin->format('Y-m-d H:i:s')));
|
||||
$formatted = $begin->format($format);
|
||||
$displayed = $begin->isoFormat($displayFormat);
|
||||
$entries[$formatted] = $displayed;
|
||||
$begin->{$increment}(); // @phpstan-ignore-line
|
||||
}
|
||||
Log::debug('listOfPeriods end of loop.');
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
/** @var QueryParserInterface $parser */
|
||||
$parser = app(QueryParserInterface::class);
|
||||
Log::debug(sprintf('Using %s as implementation for QueryParserInterface', $parser::class));
|
||||
// Log::debug(sprintf('Using %s as implementation for QueryParserInterface', $parser::class));
|
||||
|
||||
try {
|
||||
$parsedQuery = $parser->parse($query);
|
||||
@@ -189,7 +189,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
throw new FireflyException(sprintf('Invalid search value "%s". See the logs.', e($query)), 0, $e);
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Found %d node(s) at top-level', count($parsedQuery->getNodes())));
|
||||
// Log::debug(sprintf('Found %d node(s) at top-level', count($parsedQuery->getNodes())));
|
||||
$this->handleSearchNode($parsedQuery, $parsedQuery->isProhibited(false));
|
||||
|
||||
// add missing information
|
||||
@@ -329,7 +329,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
*/
|
||||
private function handleSearchNode(Node $node, bool $flipProhibitedFlag): void
|
||||
{
|
||||
Log::debug(sprintf('Now in handleSearchNode(%s)', $node::class));
|
||||
// Log::debug(sprintf('Now in handleSearchNode(%s)', $node::class));
|
||||
|
||||
switch (true) {
|
||||
case $node instanceof StringNode:
|
||||
|
||||
@@ -145,7 +145,7 @@ class QueryParser implements QueryParserInterface
|
||||
$skipNext = true;
|
||||
}
|
||||
if ('' !== $tokenUnderConstruction && !$skipNext) { // @phpstan-ignore-line
|
||||
Log::debug(sprintf('Turns out that "%s" is a field name. Reset the token.', $tokenUnderConstruction));
|
||||
// Log::debug(sprintf('Turns out that "%s" is a field name. Reset the token.', $tokenUnderConstruction));
|
||||
// If we meet a colon with a left-hand side string, we know we're in a field and are about to set up the value
|
||||
$fieldName = $tokenUnderConstruction;
|
||||
$tokenUnderConstruction = '';
|
||||
|
||||
@@ -52,6 +52,8 @@ interface RuleEngineInterface
|
||||
*/
|
||||
public function getResults(): int;
|
||||
|
||||
public function removeOperator(string $type): void;
|
||||
|
||||
public function setRefreshTriggers(bool $refreshTriggers): void;
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,6 +38,7 @@ use FireflyIII\TransactionRules\Factory\ActionFactory;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Class SearchRuleEngine
|
||||
@@ -45,6 +46,7 @@ use Illuminate\Support\Facades\Log;
|
||||
class SearchRuleEngine implements RuleEngineInterface
|
||||
{
|
||||
private readonly Collection $groups;
|
||||
|
||||
private array $operators = [];
|
||||
// always collect the triggers from the database, unless indicated otherwise.
|
||||
private bool $refreshTriggers = true;
|
||||
@@ -133,6 +135,21 @@ class SearchRuleEngine implements RuleEngineInterface
|
||||
return count($this->resultCount);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function removeOperator(string $type): void
|
||||
{
|
||||
$new = [];
|
||||
foreach ($this->operators as $operator) {
|
||||
if ($type === $operator['type']) {
|
||||
Log::debug(sprintf('Removing operator "%s"', $type));
|
||||
|
||||
continue;
|
||||
}
|
||||
$new[] = $operator;
|
||||
}
|
||||
$this->operators = $new;
|
||||
}
|
||||
|
||||
public function setRefreshTriggers(bool $refreshTriggers): void
|
||||
{
|
||||
$this->refreshTriggers = $refreshTriggers;
|
||||
@@ -331,6 +348,7 @@ class SearchRuleEngine implements RuleEngineInterface
|
||||
$searchEngine->setLimit(31337);
|
||||
$searchEngine->setDate($date);
|
||||
Log::debug('Search array', $searchArray);
|
||||
|
||||
foreach ($searchArray as $type => $searches) {
|
||||
foreach ($searches as $value) {
|
||||
$query = sprintf('%s:%s', $type, $value);
|
||||
@@ -356,15 +374,7 @@ class SearchRuleEngine implements RuleEngineInterface
|
||||
}
|
||||
if (!$group->relationLoaded('rules')) {
|
||||
Log::debug('Group rules have NOT been pre-loaded, load them NOW.');
|
||||
$rules = $group
|
||||
->rules()
|
||||
->orderBy('rules.order', 'ASC')
|
||||
// ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
|
||||
// ->where('rule_triggers.trigger_type', 'user_action')
|
||||
// ->where('rule_triggers.trigger_value', 'store-journal')
|
||||
->where('rules.active', true)
|
||||
->get(['rules.*'])
|
||||
;
|
||||
$rules = $group->rules()->orderBy('rules.order', 'ASC')->where('rules.active', true)->get(['rules.*']);
|
||||
}
|
||||
Log::debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $rules->count()));
|
||||
|
||||
@@ -458,6 +468,10 @@ class SearchRuleEngine implements RuleEngineInterface
|
||||
Log::debug('Found a journal_id trigger with 1 journal, true.');
|
||||
$journalTrigger = true;
|
||||
}
|
||||
if ('journal_id' === $triggerName && is_string($values) && !str_contains($values, ',')) {
|
||||
Log::debug('Found a journal_id trigger with 1 journal, true.');
|
||||
$journalTrigger = true;
|
||||
}
|
||||
if (in_array($triggerName, ['date_is', 'date', 'on', 'date_before', 'before', 'date_after', 'after'], true)) {
|
||||
Log::debug('Found a date related trigger, set to true.');
|
||||
$dateTrigger = true;
|
||||
|
||||
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used.
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2026-02-28',
|
||||
'build_time' => 1772261249,
|
||||
'version' => 'develop/2026-03-01',
|
||||
'build_time' => 1772348761,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
|
||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -3246,9 +3246,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.2.tgz",
|
||||
"integrity": "sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q==",
|
||||
"version": "25.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz",
|
||||
"integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4597,9 +4597,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001774",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz",
|
||||
"integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==",
|
||||
"version": "1.0.30001775",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz",
|
||||
"integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -5859,9 +5859,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.19.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
|
||||
"integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
|
||||
"version": "5.20.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz",
|
||||
"integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
Reference in New Issue
Block a user