mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-03-07 00:11:45 +00:00
Compare commits
15 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f256253f2 | ||
|
|
489b7c12e5 | ||
|
|
1049a8314d | ||
|
|
48301b6b9c | ||
|
|
5a92215921 | ||
|
|
ccfc75852a | ||
|
|
9804cffff3 | ||
|
|
901e113fef | ||
|
|
a4021ff056 | ||
|
|
902d91ad29 | ||
|
|
fa2cf22e73 | ||
|
|
970dad4c49 | ||
|
|
9d01c7bdb8 | ||
|
|
dc7d4fb258 | ||
|
|
a807ca5002 |
@@ -28,7 +28,9 @@ use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\CategoryEnrichment;
|
||||
use FireflyIII\Transformers\CategoryTransformer;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
@@ -78,6 +80,15 @@ class ShowController extends Controller
|
||||
$count = $collection->count();
|
||||
$categories = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// enrich
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$enrichment = new CategoryEnrichment();
|
||||
$enrichment->setUser($admin);
|
||||
$enrichment->setStart($this->parameters->get('start'));
|
||||
$enrichment->setEnd($this->parameters->get('end'));
|
||||
$categories = $enrichment->enrich($categories);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($categories, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.categories.index').$this->buildParams());
|
||||
@@ -105,6 +116,15 @@ class ShowController extends Controller
|
||||
$transformer = app(CategoryTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
// enrich
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$enrichment = new CategoryEnrichment();
|
||||
$enrichment->setUser($admin);
|
||||
$enrichment->setStart($this->parameters->get('start'));
|
||||
$enrichment->setEnd($this->parameters->get('end'));
|
||||
$category = $enrichment->enrichSingle($category);
|
||||
|
||||
$resource = new Item($category, $transformer, 'categories');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
|
||||
@@ -210,7 +210,6 @@ class CategoryReportController extends Controller
|
||||
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$category]));
|
||||
$earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$category]));
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
|
||||
// loop expenses.
|
||||
foreach ($spent as $currency) {
|
||||
// add things to chart Data for each currency:
|
||||
|
||||
@@ -44,7 +44,6 @@ use Illuminate\View\View;
|
||||
|
||||
use function Safe\json_decode;
|
||||
use function Safe\file_get_contents;
|
||||
use function Safe\strtotime;
|
||||
|
||||
/**
|
||||
* Class PreferencesController.
|
||||
@@ -277,10 +276,10 @@ class PreferencesController extends Controller
|
||||
|
||||
// custom fiscal year
|
||||
$customFiscalYear = 1 === (int) $request->get('customFiscalYear');
|
||||
$string = strtotime((string) $request->get('fiscalYearStart'));
|
||||
if (false !== $string) {
|
||||
$fiscalYearStart = Carbon::createFromTimestamp($string)->format('m-d');
|
||||
Preferences::set('customFiscalYear', $customFiscalYear);
|
||||
Preferences::set('customFiscalYear', $customFiscalYear);
|
||||
$fiscalYearString = (string) $request->get('fiscalYearStart');
|
||||
if ('' !== $fiscalYearString) {
|
||||
$fiscalYearStart = Carbon::parse($fiscalYearString, config('app.timezone'))->format('m-d');
|
||||
Preferences::set('fiscalYearStart', $fiscalYearStart);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace FireflyIII\Repositories\Category;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Report\Summarizer\TransactionSummarizer;
|
||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
|
||||
@@ -444,4 +445,74 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array
|
||||
{
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
|
||||
|
||||
if ($accounts instanceof Collection && $accounts->count() > 0) {
|
||||
$collector->setAccounts($accounts);
|
||||
}
|
||||
if (!$categories instanceof Collection || 0 === $categories->count()) {
|
||||
$categories = $this->getCategories();
|
||||
}
|
||||
$collector->setCategories($categories);
|
||||
$collector->withCategoryInformation();
|
||||
|
||||
return $collector->getExtractedJournals();
|
||||
}
|
||||
|
||||
public function collectIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array
|
||||
{
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($this->user)->setRange($start, $end)
|
||||
->setTypes([TransactionTypeEnum::DEPOSIT->value])
|
||||
;
|
||||
|
||||
if ($accounts instanceof Collection && $accounts->count() > 0) {
|
||||
$collector->setAccounts($accounts);
|
||||
}
|
||||
if (!$categories instanceof Collection || 0 === $categories->count()) {
|
||||
$categories = $this->getCategories();
|
||||
}
|
||||
$collector->setCategories($categories);
|
||||
|
||||
return $collector->getExtractedJournals();
|
||||
}
|
||||
|
||||
public function collectTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array
|
||||
{
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($this->user)->setRange($start, $end)
|
||||
->setTypes([TransactionTypeEnum::TRANSFER->value])
|
||||
;
|
||||
|
||||
if ($accounts instanceof Collection && $accounts->count() > 0) {
|
||||
$collector->setAccounts($accounts);
|
||||
}
|
||||
if (!$categories instanceof Collection || 0 === $categories->count()) {
|
||||
$categories = $this->getCategories();
|
||||
}
|
||||
$collector->setCategories($categories);
|
||||
|
||||
return $collector->getExtractedJournals();
|
||||
}
|
||||
|
||||
public function sumCollectedTransactionsByCategory(array $expenses, Category $category, string $method, bool $convertToPrimary = false): array
|
||||
{
|
||||
Log::debug(sprintf('Start of %s.', __METHOD__));
|
||||
$summarizer = new TransactionSummarizer($this->user);
|
||||
$summarizer->setConvertToPrimary($convertToPrimary);
|
||||
|
||||
// filter $journals by range AND currency if it is present.
|
||||
$expenses = array_filter($expenses, static function (array $expense) use ($category): bool {
|
||||
return $expense['category_id'] === $category->id;
|
||||
});
|
||||
|
||||
return $summarizer->groupByCurrencyId($expenses, $method, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace FireflyIII\Repositories\Category;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
@@ -78,6 +79,14 @@ interface OperationsRepositoryInterface
|
||||
*/
|
||||
public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array;
|
||||
|
||||
public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array;
|
||||
|
||||
public function collectIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array;
|
||||
|
||||
public function collectTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array;
|
||||
|
||||
public function sumCollectedTransactionsByCategory(array $expenses, Category $category, string $method, bool $convertToPrimary = false): array;
|
||||
|
||||
/**
|
||||
* Sum of income journals in period for a set of categories, grouped per currency. Amounts are always positive.
|
||||
*/
|
||||
|
||||
@@ -77,6 +77,7 @@ class BudgetEnrichment implements EnrichmentInterface
|
||||
foreach ($this->collection as $budget) {
|
||||
$this->ids[] = (int)$budget->id;
|
||||
}
|
||||
$this->ids = array_unique($this->ids);
|
||||
}
|
||||
|
||||
private function collectNotes(): void
|
||||
|
||||
136
app/Support/JsonApi/Enrichments/CategoryEnrichment.php
Normal file
136
app/Support/JsonApi/Enrichments/CategoryEnrichment.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\JsonApi\Enrichments;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Repositories\Category\OperationsRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CategoryEnrichment implements EnrichmentInterface
|
||||
{
|
||||
private Collection $collection;
|
||||
private User $user;
|
||||
private UserGroup $userGroup;
|
||||
private array $ids = [];
|
||||
private array $notes = [];
|
||||
private ?Carbon $start = null;
|
||||
private ?Carbon $end = null;
|
||||
private array $spent = [];
|
||||
private array $pcSpent = [];
|
||||
private array $earned = [];
|
||||
private array $pcEarned = [];
|
||||
private array $transfers = [];
|
||||
private array $pcTransfers = [];
|
||||
|
||||
public function enrich(Collection $collection): Collection
|
||||
{
|
||||
$this->collection = $collection;
|
||||
$this->collectIds();
|
||||
$this->collectNotes();
|
||||
$this->collectTransactions();
|
||||
$this->appendCollectedData();
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
public function enrichSingle(array|Model $model): array|Model
|
||||
{
|
||||
Log::debug(__METHOD__);
|
||||
$collection = new Collection([$model]);
|
||||
$collection = $this->enrich($collection);
|
||||
|
||||
return $collection->first();
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->setUserGroup($user->userGroup);
|
||||
}
|
||||
|
||||
public function setUserGroup(UserGroup $userGroup): void
|
||||
{
|
||||
$this->userGroup = $userGroup;
|
||||
}
|
||||
|
||||
private function collectIds(): void
|
||||
{
|
||||
/** @var Category $category */
|
||||
foreach ($this->collection as $category) {
|
||||
$this->ids[] = (int)$category->id;
|
||||
}
|
||||
$this->ids = array_unique($this->ids);
|
||||
}
|
||||
|
||||
private function appendCollectedData(): void
|
||||
{
|
||||
$this->collection = $this->collection->map(function (Category $item) {
|
||||
$id = (int)$item->id;
|
||||
$meta = [
|
||||
'notes' => $this->notes[$id] ?? null,
|
||||
'spent' => $this->spent[$id] ?? null,
|
||||
'pc_spent' => $this->pcSpent[$id] ?? null,
|
||||
'earned' => $this->earned[$id] ?? null,
|
||||
'pc_earned' => $this->pcEarned[$id] ?? null,
|
||||
'transfers' => $this->transfers[$id] ?? null,
|
||||
'pc_transfers' => $this->pcTransfers[$id] ?? null,
|
||||
];
|
||||
$item->meta = $meta;
|
||||
|
||||
return $item;
|
||||
});
|
||||
}
|
||||
|
||||
public function setEnd(?Carbon $end): void
|
||||
{
|
||||
$this->end = $end;
|
||||
}
|
||||
|
||||
public function setStart(?Carbon $start): void
|
||||
{
|
||||
$this->start = $start;
|
||||
}
|
||||
|
||||
private function collectNotes(): void
|
||||
{
|
||||
$notes = Note::query()->whereIn('noteable_id', $this->ids)
|
||||
->whereNotNull('notes.text')
|
||||
->where('notes.text', '!=', '')
|
||||
->where('noteable_type', Category::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
|
||||
;
|
||||
foreach ($notes as $note) {
|
||||
$this->notes[(int)$note['noteable_id']] = (string)$note['text'];
|
||||
}
|
||||
Log::debug(sprintf('Enrich with %d note(s)', count($this->notes)));
|
||||
}
|
||||
|
||||
private function collectTransactions(): void
|
||||
{
|
||||
if (null !== $this->start && null !== $this->end) {
|
||||
/** @var OperationsRepositoryInterface $opsRepository */
|
||||
$opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$opsRepository->setUser($this->user);
|
||||
$opsRepository->setUserGroup($this->userGroup);
|
||||
$expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection);
|
||||
$income = $opsRepository->collectIncome($this->start, $this->end, null, $this->collection);
|
||||
$transfers = $opsRepository->collectTransfers($this->start, $this->end, null, $this->collection);
|
||||
foreach ($this->collection as $item) {
|
||||
$id = (int)$item->id;
|
||||
$this->spent[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($expenses, $item, 'negative', false));
|
||||
$this->pcSpent[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($expenses, $item, 'negative', true));
|
||||
$this->earned[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($income, $item, 'positive', false));
|
||||
$this->pcEarned[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($income, $item, 'positive', true));
|
||||
$this->transfers[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($transfers, $item, 'positive', false));
|
||||
$this->pcTransfers[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($transfers, $item, 'positive', true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -565,11 +565,12 @@ class Navigation
|
||||
public function preferredCarbonLocalizedFormat(Carbon $start, Carbon $end): string
|
||||
{
|
||||
$locale = app('steam')->getLocale();
|
||||
if ($start->diffInMonths($end, true) > 1) {
|
||||
$diff = $start->diffInMonths($end, true);
|
||||
if ($diff >= 1.001) {
|
||||
return (string) trans('config.month_js', [], $locale);
|
||||
}
|
||||
|
||||
if ($start->diffInMonths($end, true) > 12) {
|
||||
if ($diff >= 12.001) {
|
||||
return (string) trans('config.year_js', [], $locale);
|
||||
}
|
||||
|
||||
@@ -582,11 +583,12 @@ class Navigation
|
||||
*/
|
||||
public function preferredEndOfPeriod(Carbon $start, Carbon $end): string
|
||||
{
|
||||
if ((int) $start->diffInMonths($end, true) > 1 && (int) $start->diffInMonths($end, true) <= 12) {
|
||||
$diff = $start->diffInMonths($end, true);
|
||||
if ($diff >= 1.001) {
|
||||
return 'endOfMonth';
|
||||
}
|
||||
|
||||
if ((int) $start->diffInMonths($end, true) > 12) {
|
||||
if ($diff >= 12.001) {
|
||||
return 'endOfYear';
|
||||
}
|
||||
|
||||
@@ -599,11 +601,12 @@ class Navigation
|
||||
*/
|
||||
public function preferredRangeFormat(Carbon $start, Carbon $end): string
|
||||
{
|
||||
if ((int) $start->diffInMonths($end, true) > 1 && (int) $start->diffInMonths($end, true) <= 12) {
|
||||
$diff = $start->diffInMonths($end, true);
|
||||
if ($diff >= 1.001) {
|
||||
return '1M';
|
||||
}
|
||||
|
||||
if ((int) $start->diffInMonths($end, true) > 12) {
|
||||
if ($diff >= 12.001) {
|
||||
return '1Y';
|
||||
}
|
||||
|
||||
@@ -616,11 +619,12 @@ class Navigation
|
||||
*/
|
||||
public function preferredSqlFormat(Carbon $start, Carbon $end): string
|
||||
{
|
||||
if ((int) $start->diffInMonths($end, true) > 1 && (int) $start->diffInMonths($end, true) <= 12) {
|
||||
$diff = $start->diffInMonths($end, true);
|
||||
if ($diff >= 1.001) {
|
||||
return '%Y-%m';
|
||||
}
|
||||
|
||||
if ((int) $start->diffInMonths($end, true) > 12) {
|
||||
if ($diff >= 12.001) {
|
||||
return '%Y';
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace FireflyIII\Transformers;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
|
||||
/**
|
||||
* Class AvailableBudgetTransformer
|
||||
|
||||
@@ -103,8 +103,8 @@ class BudgetTransformer extends AbstractTransformer
|
||||
|
||||
'auto_budget_amount' => $abAmount,
|
||||
'pc_auto_budget_amount' => $abPrimary,
|
||||
'spent' => $this->beautify($budget->meta['spent']), // always in primary currency.
|
||||
'pc_spent' => $this->beautify($budget->meta['pc_spent']), // always in primary currency.
|
||||
'spent' => $this->beautify($budget->meta['spent']),
|
||||
'pc_spent' => $this->beautify($budget->meta['pc_spent']),
|
||||
'links' => [
|
||||
[
|
||||
'rel' => 'self',
|
||||
|
||||
@@ -26,30 +26,22 @@ namespace FireflyIII\Transformers;
|
||||
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\OperationsRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use Illuminate\Support\Collection;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
|
||||
/**
|
||||
* Class CategoryTransformer
|
||||
*/
|
||||
class CategoryTransformer extends AbstractTransformer
|
||||
{
|
||||
private readonly bool $convertToNative;
|
||||
private readonly TransactionCurrency $primary;
|
||||
private readonly OperationsRepositoryInterface $opsRepository;
|
||||
private readonly CategoryRepositoryInterface $repository;
|
||||
private readonly TransactionCurrency $primaryCurrency;
|
||||
|
||||
/**
|
||||
* CategoryTransformer constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$this->repository = app(CategoryRepositoryInterface::class);
|
||||
$this->primary = Amount::getPrimaryCurrency();
|
||||
$this->convertToNative = Amount::convertToPrimary();
|
||||
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,35 +49,28 @@ class CategoryTransformer extends AbstractTransformer
|
||||
*/
|
||||
public function transform(Category $category): array
|
||||
{
|
||||
$this->opsRepository->setUser($category->user);
|
||||
$this->repository->setUser($category->user);
|
||||
|
||||
$spent = [];
|
||||
$earned = [];
|
||||
$start = $this->parameters->get('start');
|
||||
$end = $this->parameters->get('end');
|
||||
if (null !== $start && null !== $end) {
|
||||
$earned = $this->beautify($this->opsRepository->sumIncome($start, $end, null, new Collection([$category])));
|
||||
$spent = $this->beautify($this->opsRepository->sumExpenses($start, $end, null, new Collection([$category])));
|
||||
}
|
||||
$primary = $this->primary;
|
||||
if (!$this->convertToNative) {
|
||||
$primary = null;
|
||||
}
|
||||
$notes = $this->repository->getNoteText($category);
|
||||
|
||||
return [
|
||||
'id' => $category->id,
|
||||
'created_at' => $category->created_at->toAtomString(),
|
||||
'updated_at' => $category->updated_at->toAtomString(),
|
||||
'name' => $category->name,
|
||||
'notes' => $notes,
|
||||
'primary_currency_id' => $primary instanceof TransactionCurrency ? (string)$primary->id : null,
|
||||
'primary_currency_code' => $primary?->code,
|
||||
'primary_currency_symbol' => $primary?->symbol,
|
||||
'primary_currency_decimal_places' => $primary?->decimal_places,
|
||||
'spent' => $spent,
|
||||
'earned' => $earned,
|
||||
'notes' => $category->meta['notes'],
|
||||
|
||||
// category never has currency settings.
|
||||
'object_has_currency_setting' => false,
|
||||
|
||||
|
||||
'primary_currency_id' => (string)$this->primaryCurrency->id,
|
||||
'primary_currency_code' => $this->primaryCurrency->code,
|
||||
'primary_currency_symbol' => $this->primaryCurrency->symbol,
|
||||
'primary_currency_decimal_places' => (int)$this->primaryCurrency->decimal_places,
|
||||
'spent' => $this->beautify($category->meta['spent']),
|
||||
'pc_spent' => $this->beautify($category->meta['pc_spent']),
|
||||
'earned' => $this->beautify($category->meta['earned']),
|
||||
'pc_earned' => $this->beautify($category->meta['pc_earned']),
|
||||
'transferred' => $this->beautify($category->meta['transfers']),
|
||||
'pc_transferred' => $this->beautify($category->meta['pc_transfers']),
|
||||
'links' => [
|
||||
[
|
||||
'rel' => 'self',
|
||||
|
||||
@@ -28,6 +28,8 @@ use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
|
||||
/**
|
||||
* Class PiggyBankEventTransformer
|
||||
@@ -61,7 +63,7 @@ class PiggyBankEventTransformer extends AbstractTransformer
|
||||
$this->piggyRepos->setUser($account->user);
|
||||
|
||||
// get associated currency or fall back to the default:
|
||||
$currency = $this->repository->getAccountCurrency($account) ?? app('amount')->getPrimaryCurrencyByUserGroup($account->user->userGroup);
|
||||
$currency = $this->repository->getAccountCurrency($account) ?? Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup);
|
||||
|
||||
// get associated journal and transaction, if any:
|
||||
$journalId = $event->transaction_journal_id;
|
||||
|
||||
@@ -29,6 +29,7 @@ use FireflyIII\Models\ObjectGroup;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
|
||||
/**
|
||||
* Class PiggyBankTransformer
|
||||
|
||||
@@ -36,6 +36,8 @@ use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use function Safe\json_decode;
|
||||
|
||||
@@ -69,17 +71,17 @@ class RecurrenceTransformer extends AbstractTransformer
|
||||
*/
|
||||
public function transform(Recurrence $recurrence): array
|
||||
{
|
||||
app('log')->debug('Now in Recurrence::transform()');
|
||||
Log::debug('Now in Recurrence::transform()');
|
||||
$this->repository->setUser($recurrence->user);
|
||||
$this->piggyRepos->setUser($recurrence->user);
|
||||
$this->factory->setUser($recurrence->user);
|
||||
$this->budgetRepos->setUser($recurrence->user);
|
||||
app('log')->debug('Set user.');
|
||||
Log::debug('Set user.');
|
||||
|
||||
$shortType = (string) config(sprintf('firefly.transactionTypesToShort.%s', $recurrence->transactionType->type));
|
||||
$notes = $this->repository->getNoteText($recurrence);
|
||||
$reps = 0 === (int) $recurrence->repetitions ? null : (int) $recurrence->repetitions;
|
||||
app('log')->debug('Get basic data.');
|
||||
Log::debug('Get basic data.');
|
||||
|
||||
// basic data.
|
||||
return [
|
||||
@@ -112,7 +114,7 @@ class RecurrenceTransformer extends AbstractTransformer
|
||||
*/
|
||||
private function getRepetitions(Recurrence $recurrence): array
|
||||
{
|
||||
app('log')->debug('Now in getRepetitions().');
|
||||
Log::debug('Now in getRepetitions().');
|
||||
$fromDate = $recurrence->latest_date ?? $recurrence->first_date;
|
||||
$return = [];
|
||||
|
||||
@@ -150,7 +152,7 @@ class RecurrenceTransformer extends AbstractTransformer
|
||||
*/
|
||||
private function getTransactions(Recurrence $recurrence): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$return = [];
|
||||
|
||||
// get all transactions:
|
||||
@@ -239,7 +241,7 @@ class RecurrenceTransformer extends AbstractTransformer
|
||||
*/
|
||||
private function getTransactionMeta(RecurrenceTransaction $transaction, array $array): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$array['tags'] = [];
|
||||
$array['category_id'] = null;
|
||||
$array['category_name'] = null;
|
||||
|
||||
@@ -288,8 +288,8 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
],
|
||||
];
|
||||
} catch (FireflyException $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
app('log')->error($e->getTraceAsString());
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
|
||||
throw new FireflyException(sprintf('Transaction group #%d is broken. Please check out your log files.', $group->id), 0, $e);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Transformers;
|
||||
|
||||
use FireflyIII\Models\WebhookMessage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use JsonException;
|
||||
|
||||
use function Safe\json_encode;
|
||||
@@ -44,7 +45,7 @@ class WebhookMessageTransformer extends AbstractTransformer
|
||||
try {
|
||||
$json = json_encode($message->message, JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException $e) {
|
||||
app('log')->error(sprintf('Could not encode webhook message #%d: %s', $message->id, $e->getMessage()));
|
||||
Log::error(sprintf('Could not encode webhook message #%d: %s', $message->id, $e->getMessage()));
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
10
composer.lock
generated
10
composer.lock
generated
@@ -11128,16 +11128,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.21",
|
||||
"version": "2.1.22",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6"
|
||||
"reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ccf445757458c06a04eb3f803603cb118fe5fa6",
|
||||
"reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/41600c8379eb5aee63e9413fe9e97273e25d57e4",
|
||||
"reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11182,7 +11182,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-28T19:35:08+00:00"
|
||||
"time": "2025-08-04T19:17:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-deprecation-rules",
|
||||
|
||||
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2025-08-04',
|
||||
'build_time' => 1754278851,
|
||||
'version' => 'develop/2025-08-05',
|
||||
'build_time' => 1754394714,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 26,
|
||||
|
||||
@@ -121,6 +121,7 @@ return [
|
||||
'languages' => [
|
||||
// currently enabled languages
|
||||
// 'af_ZA' => ['name_locale' => 'Afrikaans', 'name_english' => 'Afrikaans'],
|
||||
'ar_SA' => ['name_locale' => 'العربية', 'name_english' => 'Arabic'],
|
||||
'bg_BG' => ['name_locale' => 'Български', 'name_english' => 'Bulgarian'],
|
||||
'cs_CZ' => ['name_locale' => 'Czech', 'name_english' => 'Czech'],
|
||||
'da_DK' => ['name_locale' => 'Danish', 'name_english' => 'Danish'],
|
||||
|
||||
@@ -333,6 +333,7 @@ return [
|
||||
'languages' => [
|
||||
// currently enabled languages
|
||||
'af_ZA',
|
||||
'ar_SA',
|
||||
'bg_BG',
|
||||
'cs_CZ',
|
||||
'da_DK',
|
||||
|
||||
@@ -67,6 +67,7 @@ class TransactionCurrencySeeder extends Seeder
|
||||
// asian currencies
|
||||
$currencies[] = ['code' => 'JPY', 'name' => 'Japanese yen', 'symbol' => '¥', 'decimal_places' => 0];
|
||||
$currencies[] = ['code' => 'CNY', 'name' => 'Chinese yuan', 'symbol' => '¥', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'KRW', 'name' => 'South Korean won','symbol' => '₩', 'decimal_places' => 2,];
|
||||
// $currencies[] = ['code' => 'RMB', 'name' => 'Chinese yuan', 'symbol' => '¥', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'RUB', 'name' => 'Russian ruble', 'symbol' => '₽', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'INR', 'name' => 'Indian rupee', 'symbol' => '₹', 'decimal_places' => 2];
|
||||
|
||||
26
package-lock.json
generated
26
package-lock.json
generated
@@ -3148,13 +3148,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz",
|
||||
"integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz",
|
||||
"integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.8.0"
|
||||
"undici-types": "~7.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node-forge": {
|
||||
@@ -4930,9 +4930,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/core-js-compat": {
|
||||
"version": "3.44.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.44.0.tgz",
|
||||
"integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==",
|
||||
"version": "3.45.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.0.tgz",
|
||||
"integrity": "sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5700,9 +5700,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.194",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.194.tgz",
|
||||
"integrity": "sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==",
|
||||
"version": "1.5.195",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.195.tgz",
|
||||
"integrity": "sha512-URclP0iIaDUzqcAyV1v2PgduJ9N0IdXmWsnPzPfelvBmjmZzEy6xJcjb1cXj+TbYqXgtLrjHEoaSIdTYhw4ezg==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -11327,9 +11327,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
||||
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
|
||||
"version": "7.10.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
|
||||
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
@@ -364,6 +364,8 @@ function updateTriggerInput(selectList) {
|
||||
createAutoComplete(inputResult, 'api/v1/autocomplete/transactions');
|
||||
break;
|
||||
case 'has_no_category':
|
||||
case 'no_external_id':
|
||||
case 'any_external_id':
|
||||
case 'has_any_category':
|
||||
case 'has_no_budget':
|
||||
case 'has_any_budget':
|
||||
|
||||
71
public/v1/js/lib/moment/af_ZA.js
Normal file
71
public/v1/js/lib/moment/af_ZA.js
Normal file
@@ -0,0 +1,71 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : Afrikaans [af]
|
||||
//! author : Werner Mollentze : https://github.com/wernerm
|
||||
|
||||
import moment from '../moment';
|
||||
|
||||
export default moment.defineLocale('af', {
|
||||
months: 'Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember'.split(
|
||||
'_'
|
||||
),
|
||||
monthsShort: 'Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des'.split('_'),
|
||||
weekdays: 'Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag'.split(
|
||||
'_'
|
||||
),
|
||||
weekdaysShort: 'Son_Maa_Din_Woe_Don_Vry_Sat'.split('_'),
|
||||
weekdaysMin: 'So_Ma_Di_Wo_Do_Vr_Sa'.split('_'),
|
||||
meridiemParse: /vm|nm/i,
|
||||
isPM: function (input) {
|
||||
return /^nm$/i.test(input);
|
||||
},
|
||||
meridiem: function (hours, minutes, isLower) {
|
||||
if (hours < 12) {
|
||||
return isLower ? 'vm' : 'VM';
|
||||
} else {
|
||||
return isLower ? 'nm' : 'NM';
|
||||
}
|
||||
},
|
||||
longDateFormat: {
|
||||
LT: 'HH:mm',
|
||||
LTS: 'HH:mm:ss',
|
||||
L: 'DD/MM/YYYY',
|
||||
LL: 'D MMMM YYYY',
|
||||
LLL: 'D MMMM YYYY HH:mm',
|
||||
LLLL: 'dddd, D MMMM YYYY HH:mm',
|
||||
},
|
||||
calendar: {
|
||||
sameDay: '[Vandag om] LT',
|
||||
nextDay: '[Môre om] LT',
|
||||
nextWeek: 'dddd [om] LT',
|
||||
lastDay: '[Gister om] LT',
|
||||
lastWeek: '[Laas] dddd [om] LT',
|
||||
sameElse: 'L',
|
||||
},
|
||||
relativeTime: {
|
||||
future: 'oor %s',
|
||||
past: '%s gelede',
|
||||
s: "'n paar sekondes",
|
||||
ss: '%d sekondes',
|
||||
m: "'n minuut",
|
||||
mm: '%d minute',
|
||||
h: "'n uur",
|
||||
hh: '%d ure',
|
||||
d: "'n dag",
|
||||
dd: '%d dae',
|
||||
M: "'n maand",
|
||||
MM: '%d maande',
|
||||
y: "'n jaar",
|
||||
yy: '%d jaar',
|
||||
},
|
||||
dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/,
|
||||
ordinal: function (number) {
|
||||
return (
|
||||
number +
|
||||
(number === 1 || number === 8 || number >= 20 ? 'ste' : 'de')
|
||||
); // Thanks to Joris Röling : https://github.com/jjupiter
|
||||
},
|
||||
week: {
|
||||
dow: 1, // Maandag is die eerste dag van die week.
|
||||
doy: 4, // Die week wat die 4de Januarie bevat is die eerste week van die jaar.
|
||||
},
|
||||
});
|
||||
105
public/v1/js/lib/moment/ar_SA.js
Normal file
105
public/v1/js/lib/moment/ar_SA.js
Normal file
@@ -0,0 +1,105 @@
|
||||
//! moment.js locale configuration
|
||||
//! locale : Arabic (Saudi Arabia) [ar-sa]
|
||||
//! author : Suhail Alkowaileet : https://github.com/xsoh
|
||||
|
||||
import moment from '../moment';
|
||||
|
||||
var symbolMap = {
|
||||
1: '١',
|
||||
2: '٢',
|
||||
3: '٣',
|
||||
4: '٤',
|
||||
5: '٥',
|
||||
6: '٦',
|
||||
7: '٧',
|
||||
8: '٨',
|
||||
9: '٩',
|
||||
0: '٠',
|
||||
},
|
||||
numberMap = {
|
||||
'١': '1',
|
||||
'٢': '2',
|
||||
'٣': '3',
|
||||
'٤': '4',
|
||||
'٥': '5',
|
||||
'٦': '6',
|
||||
'٧': '7',
|
||||
'٨': '8',
|
||||
'٩': '9',
|
||||
'٠': '0',
|
||||
};
|
||||
|
||||
export default moment.defineLocale('ar-sa', {
|
||||
months: 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split(
|
||||
'_'
|
||||
),
|
||||
monthsShort:
|
||||
'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split(
|
||||
'_'
|
||||
),
|
||||
weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
|
||||
weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
|
||||
weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'),
|
||||
weekdaysParseExact: true,
|
||||
longDateFormat: {
|
||||
LT: 'HH:mm',
|
||||
LTS: 'HH:mm:ss',
|
||||
L: 'DD/MM/YYYY',
|
||||
LL: 'D MMMM YYYY',
|
||||
LLL: 'D MMMM YYYY HH:mm',
|
||||
LLLL: 'dddd D MMMM YYYY HH:mm',
|
||||
},
|
||||
meridiemParse: /ص|م/,
|
||||
isPM: function (input) {
|
||||
return 'م' === input;
|
||||
},
|
||||
meridiem: function (hour, minute, isLower) {
|
||||
if (hour < 12) {
|
||||
return 'ص';
|
||||
} else {
|
||||
return 'م';
|
||||
}
|
||||
},
|
||||
calendar: {
|
||||
sameDay: '[اليوم على الساعة] LT',
|
||||
nextDay: '[غدا على الساعة] LT',
|
||||
nextWeek: 'dddd [على الساعة] LT',
|
||||
lastDay: '[أمس على الساعة] LT',
|
||||
lastWeek: 'dddd [على الساعة] LT',
|
||||
sameElse: 'L',
|
||||
},
|
||||
relativeTime: {
|
||||
future: 'في %s',
|
||||
past: 'منذ %s',
|
||||
s: 'ثوان',
|
||||
ss: '%d ثانية',
|
||||
m: 'دقيقة',
|
||||
mm: '%d دقائق',
|
||||
h: 'ساعة',
|
||||
hh: '%d ساعات',
|
||||
d: 'يوم',
|
||||
dd: '%d أيام',
|
||||
M: 'شهر',
|
||||
MM: '%d أشهر',
|
||||
y: 'سنة',
|
||||
yy: '%d سنوات',
|
||||
},
|
||||
preparse: function (string) {
|
||||
return string
|
||||
.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) {
|
||||
return numberMap[match];
|
||||
})
|
||||
.replace(/،/g, ',');
|
||||
},
|
||||
postformat: function (string) {
|
||||
return string
|
||||
.replace(/\d/g, function (match) {
|
||||
return symbolMap[match];
|
||||
})
|
||||
.replace(/,/g, '،');
|
||||
},
|
||||
week: {
|
||||
dow: 0, // Sunday is the first day of the week.
|
||||
doy: 6, // The week that contains Jan 6th is the first week of the year.
|
||||
},
|
||||
});
|
||||
@@ -110,6 +110,8 @@
|
||||
"/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/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/ar_SA.js": "/public/v1/js/lib/moment/ar_SA.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/cs_CZ.js": "/public/v1/js/lib/moment/cs_CZ.js",
|
||||
|
||||
@@ -24,6 +24,7 @@ module.exports = new vuei18n({
|
||||
fallbackLocale: 'en',
|
||||
messages: {
|
||||
'af': require('./locales/af.json'),
|
||||
'ar': require('./locales/ar.json'),
|
||||
'bg': require('./locales/bg.json'),
|
||||
'ca-es': require('./locales/ca.json'),
|
||||
'cs': require('./locales/cs.json'),
|
||||
|
||||
188
resources/assets/v1/src/locales/ar.json
Normal file
188
resources/assets/v1/src/locales/ar.json
Normal file
@@ -0,0 +1,188 @@
|
||||
{
|
||||
"firefly": {
|
||||
"administrations_page_title": "Financial administrations",
|
||||
"administrations_index_menu": "Financial administrations",
|
||||
"expires_at": "Expires at",
|
||||
"temp_administrations_introduction": "Firefly III will soon get the ability to manage multiple financial administrations. Right now, you only have the one. You can set the title of this administration and its primary currency. This replaces the previous setting where you would set your \"default currency\". This setting is now tied to the financial administration and can be different per administration.",
|
||||
"administration_currency_form_help": "It may take a long time for the page to load if you change the primary currency because transaction may need to be converted to your (new) primary currency.",
|
||||
"administrations_page_edit_sub_title_js": "Edit financial administration \"{title}\"",
|
||||
"table": "Table",
|
||||
"welcome_back": "What's playing?",
|
||||
"flash_error": "Error!",
|
||||
"flash_warning": "Warning!",
|
||||
"flash_success": "Success!",
|
||||
"close": "Close",
|
||||
"select_dest_account": "Please select or type a valid destination account name",
|
||||
"select_source_account": "Please select or type a valid source account name",
|
||||
"split_transaction_title": "Description of the split transaction",
|
||||
"errors_submission": "There was something wrong with your submission. Please check out the errors below.",
|
||||
"is_reconciled": "Is reconciled",
|
||||
"split": "Split",
|
||||
"single_split": "Split",
|
||||
"not_enough_currencies": "Not enough currencies",
|
||||
"not_enough_currencies_enabled": "If you have just one currency enabled, there is no need to add exchange rates.",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
|
||||
"webhook_stored_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID} (\"{title}\")<\/a> has been stored.",
|
||||
"webhook_updated_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID}<\/a> (\"{title}\") has been updated.",
|
||||
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") has been updated.",
|
||||
"transaction_new_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> has been stored.",
|
||||
"transaction_journal_information": "Transaction information",
|
||||
"submission_options": "Submission options",
|
||||
"apply_rules_checkbox": "Apply rules",
|
||||
"fire_webhooks_checkbox": "Fire webhooks",
|
||||
"no_budget_pointer": "You seem to have no budgets yet. You should create some on the <a href=\"budgets\">budgets<\/a>-page. Budgets can help you keep track of expenses.",
|
||||
"no_bill_pointer": "You seem to have no subscription yet. You should create some on the <a href=\"subscriptions\">subscription<\/a>-page. Subscriptions can help you keep track of expenses.",
|
||||
"source_account": "Source account",
|
||||
"hidden_fields_preferences": "You can enable more transaction options in your <a href=\"preferences\">preferences<\/a>.",
|
||||
"destination_account": "Destination account",
|
||||
"add_another_split": "Add another split",
|
||||
"submission": "Submission",
|
||||
"stored_journal": "Successfully created new transaction \":description\"",
|
||||
"create_another": "After storing, return here to create another one.",
|
||||
"reset_after": "Reset form after submission",
|
||||
"submit": "Submit",
|
||||
"amount": "Amount",
|
||||
"date": "Date",
|
||||
"is_reconciled_fields_dropped": "Because this transaction is reconciled, you will not be able to update the accounts, nor the amount(s) unless you remove the reconciliation flag.",
|
||||
"tags": "Tags",
|
||||
"no_budget": "(no budget)",
|
||||
"no_bill": "(no subscription)",
|
||||
"category": "Category",
|
||||
"attachments": "Attachments",
|
||||
"notes": "Notes",
|
||||
"external_url": "External URL",
|
||||
"update_transaction": "Update transaction",
|
||||
"after_update_create_another": "After updating, return here to continue editing.",
|
||||
"store_as_new": "Store as a new transaction instead of updating.",
|
||||
"split_title_help": "If you create a split transaction, there must be a global description for all splits of the transaction.",
|
||||
"none_in_select_list": "(none)",
|
||||
"no_piggy_bank": "(no piggy bank)",
|
||||
"description": "Description",
|
||||
"split_transaction_title_help": "If you create a split transaction, there must be a global description for all splits of the transaction.",
|
||||
"destination_account_reconciliation": "You can't edit the destination account of a reconciliation transaction.",
|
||||
"source_account_reconciliation": "You can't edit the source account of a reconciliation transaction.",
|
||||
"budget": "Budget",
|
||||
"bill": "Subscription",
|
||||
"you_create_withdrawal": "You're creating a withdrawal.",
|
||||
"you_create_transfer": "You're creating a transfer.",
|
||||
"you_create_deposit": "You're creating a deposit.",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"name": "Name",
|
||||
"profile_whoops": "Whoops!",
|
||||
"profile_something_wrong": "Something went wrong!",
|
||||
"profile_try_again": "Something went wrong. Please try again.",
|
||||
"profile_oauth_clients": "OAuth Clients",
|
||||
"profile_oauth_no_clients": "You have not created any OAuth clients.",
|
||||
"profile_oauth_clients_header": "Clients",
|
||||
"profile_oauth_client_id": "Client ID",
|
||||
"profile_oauth_client_name": "Name",
|
||||
"profile_oauth_client_secret": "Secret",
|
||||
"profile_oauth_create_new_client": "Create New Client",
|
||||
"profile_oauth_create_client": "Create Client",
|
||||
"profile_oauth_edit_client": "Edit Client",
|
||||
"profile_oauth_name_help": "Something your users will recognize and trust.",
|
||||
"profile_oauth_redirect_url": "Redirect URL",
|
||||
"profile_oauth_clients_external_auth": "If you're using an external authentication provider like Authelia, OAuth Clients will not work. You can use Personal Access Tokens only.",
|
||||
"profile_oauth_redirect_url_help": "Your application's authorization callback URL.",
|
||||
"profile_authorized_apps": "Authorized applications",
|
||||
"profile_authorized_clients": "Authorized clients",
|
||||
"profile_scopes": "Scopes",
|
||||
"profile_revoke": "Revoke",
|
||||
"profile_personal_access_tokens": "Personal Access Tokens",
|
||||
"profile_personal_access_token": "Personal Access Token",
|
||||
"profile_personal_access_token_explanation": "Here is your new personal access token. This is the only time it will be shown so don't lose it! You may now use this token to make API requests.",
|
||||
"profile_no_personal_access_token": "You have not created any personal access tokens.",
|
||||
"profile_create_new_token": "Create new token",
|
||||
"profile_create_token": "Create token",
|
||||
"profile_create": "Create",
|
||||
"profile_save_changes": "Save changes",
|
||||
"default_group_title_name": "(ungrouped)",
|
||||
"piggy_bank": "Piggy bank",
|
||||
"profile_oauth_client_secret_title": "Client Secret",
|
||||
"profile_oauth_client_secret_expl": "Here is your new client secret. This is the only time it will be shown so don't lose it! You may now use this secret to make API requests.",
|
||||
"profile_oauth_confidential": "Confidential",
|
||||
"profile_oauth_confidential_help": "Require the client to authenticate with a secret. Confidential clients can hold credentials in a secure way without exposing them to unauthorized parties. Public applications, such as primary desktop or JavaScript SPA applications, are unable to hold secrets securely.",
|
||||
"multi_account_warning_unknown": "Depending on the type of transaction you create, the source and\/or destination account of subsequent splits may be overruled by whatever is defined in the first split of the transaction.",
|
||||
"multi_account_warning_withdrawal": "Keep in mind that the source account of subsequent splits will be overruled by whatever is defined in the first split of the withdrawal.",
|
||||
"multi_account_warning_deposit": "Keep in mind that the destination account of subsequent splits will be overruled by whatever is defined in the first split of the deposit.",
|
||||
"multi_account_warning_transfer": "Keep in mind that the source + destination account of subsequent splits will be overruled by whatever is defined in the first split of the transfer.",
|
||||
"webhook_trigger_STORE_TRANSACTION": "After transaction creation",
|
||||
"webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
|
||||
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
|
||||
"webhook_response_TRANSACTIONS": "Transaction details",
|
||||
"webhook_response_ACCOUNTS": "Account details",
|
||||
"webhook_response_none_NONE": "No details",
|
||||
"webhook_delivery_JSON": "JSON",
|
||||
"actions": "Actions",
|
||||
"meta_data": "Meta data",
|
||||
"webhook_messages": "Webhook message",
|
||||
"inactive": "Inactive",
|
||||
"no_webhook_messages": "There are no webhook messages",
|
||||
"inspect": "Inspect",
|
||||
"create_new_webhook": "Create new webhook",
|
||||
"webhooks": "Webhooks",
|
||||
"webhook_trigger_form_help": "Indicate on what event the webhook will trigger",
|
||||
"webhook_response_form_help": "Indicate what the webhook must submit to the URL.",
|
||||
"webhook_delivery_form_help": "Which format the webhook must deliver data in.",
|
||||
"webhook_active_form_help": "The webhook must be active or it won't be called.",
|
||||
"edit_webhook_js": "Edit webhook \"{title}\"",
|
||||
"webhook_was_triggered": "The webhook was triggered on the indicated transaction. Please wait for results to appear.",
|
||||
"view_message": "View message",
|
||||
"view_attempts": "View failed attempts",
|
||||
"message_content_title": "Webhook message content",
|
||||
"message_content_help": "This is the content of the message that was sent (or tried) using this webhook.",
|
||||
"attempt_content_title": "Webhook attempts",
|
||||
"attempt_content_help": "These are all the unsuccessful attempts of this webhook message to submit to the configured URL. After some time, Firefly III will stop trying.",
|
||||
"no_attempts": "There are no unsuccessful attempts. That's a good thing!",
|
||||
"webhook_attempt_at": "Attempt at {moment}",
|
||||
"logs": "Logs",
|
||||
"response": "Response",
|
||||
"visit_webhook_url": "Visit webhook URL",
|
||||
"reset_webhook_secret": "Reset webhook secret",
|
||||
"header_exchange_rates": "Exchange rates",
|
||||
"exchange_rates_intro": "Firefly III supports downloading and using exchange rates. Read more about this in <a href=\"https:\/\/docs.firefly-iii.org\/explanation\/financial-concepts\/exchange-rates\/\">the documentation<\/a>.",
|
||||
"exchange_rates_from_to": "Between {from} and {to} (and the other way around)",
|
||||
"exchange_rates_intro_rates": "Firefly III uses the following exchange rates. The inverse is automatically calculated when it is not provided. If no exchange rate exists for the date of the transaction, Firefly III will go back in time to find one. If none are present, the rate \"1\" will be used.",
|
||||
"header_exchange_rates_rates": "Exchange rates",
|
||||
"header_exchange_rates_table": "Table with exchange rates",
|
||||
"help_rate_form": "On this day, how many {to} will you get for one {from}?",
|
||||
"add_new_rate": "Add a new exchange rate",
|
||||
"save_new_rate": "Save new rate"
|
||||
},
|
||||
"form": {
|
||||
"url": "URL",
|
||||
"active": "Active",
|
||||
"interest_date": "Interest date",
|
||||
"administration_currency": "Primary currency",
|
||||
"title": "Title",
|
||||
"date": "Date",
|
||||
"book_date": "Book date",
|
||||
"process_date": "Processing date",
|
||||
"due_date": "Due date",
|
||||
"foreign_amount": "Foreign amount",
|
||||
"payment_date": "Payment date",
|
||||
"invoice_date": "Invoice date",
|
||||
"internal_reference": "Internal reference",
|
||||
"webhook_response": "Response",
|
||||
"webhook_trigger": "Trigger",
|
||||
"webhook_delivery": "Delivery",
|
||||
"from_currency_to_currency": "{from} → {to}",
|
||||
"to_currency_from_currency": "{to} → {from}",
|
||||
"rate": "Rate"
|
||||
},
|
||||
"list": {
|
||||
"title": "Title",
|
||||
"active": "Is active?",
|
||||
"primary_currency": "Primary currency",
|
||||
"trigger": "Trigger",
|
||||
"response": "Response",
|
||||
"delivery": "Delivery",
|
||||
"url": "URL",
|
||||
"secret": "Secret"
|
||||
},
|
||||
"config": {
|
||||
"html_language": "ar",
|
||||
"date_time_fns": "MMMM do, yyyy @ HH:mm:ss"
|
||||
}
|
||||
}
|
||||
@@ -952,12 +952,12 @@ return [
|
||||
'rule_trigger_journal_id_choice' => 'Transaction journal ID is..',
|
||||
'rule_trigger_journal_id' => 'Transaction journal ID is ":trigger_value"',
|
||||
'rule_trigger_any_external_url' => 'Transaction has an (any) external URL',
|
||||
'rule_trigger_any_external_url_choice' => 'Transaction has an (any) external URL',
|
||||
'rule_trigger_any_external_url_choice' => 'Has an (any) external URL',
|
||||
'rule_trigger_any_external_id' => 'Transaction has an (any) external ID',
|
||||
'rule_trigger_any_external_id_choice' => 'Transaction has an (any) external ID',
|
||||
'rule_trigger_no_external_url_choice' => 'Transaction has no external URL',
|
||||
'rule_trigger_any_external_id_choice' => 'Has an (any) external ID',
|
||||
'rule_trigger_no_external_url_choice' => 'Has no external URL',
|
||||
'rule_trigger_no_external_url' => 'Transaction has no external URL',
|
||||
'rule_trigger_no_external_id_choice' => 'Transaction has no external ID',
|
||||
'rule_trigger_no_external_id_choice' => 'Has no external ID',
|
||||
'rule_trigger_no_external_id' => 'Transaction has no external ID',
|
||||
'rule_trigger_id_choice' => 'Transaction ID is..',
|
||||
'rule_trigger_id' => 'Transaction ID is ":trigger_value"',
|
||||
|
||||
1
resources/locales/ar_SA/locales.json
Normal file
1
resources/locales/ar_SA/locales.json
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user