Merge pull request #10786 from firefly-iii/develop

🤖 Automatically merge the PR into the main branch.
This commit is contained in:
github-actions[bot]
2025-08-18 20:08:34 +02:00
committed by GitHub
76 changed files with 3981 additions and 3540 deletions

View File

@@ -49,8 +49,6 @@ class TransactionTypeController extends Controller
function ($request, $next) {
$this->validateUserGroup($request);
$this->repository = app(TransactionTypeRepositoryInterface::class);
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}

View File

@@ -130,6 +130,7 @@ class AccountController extends Controller
'yAxisID' => 0,
'period' => '1D',
'entries' => [],
'pc_entries' => [],
];
if ($this->convertToPrimary) {
$currentSet['pc_entries'] = [];

View File

@@ -96,7 +96,6 @@ class ShowController extends Controller
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.budgets.limits.index', [$budget->id]).$this->buildParams());
// enrich
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);

View File

@@ -24,15 +24,18 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\System;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\System\UpdateRequest;
use FireflyIII\Enums\WebhookDelivery;
use FireflyIII\Enums\WebhookResponse;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Binder\EitherConfigKey;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
/**
@@ -107,8 +110,8 @@ class ConfigurationController extends Controller
return [
'is_demo_site' => $isDemoSite?->data,
'permission_update_check' => null === $updateCheck ? null : (int) $updateCheck->data,
'last_update_check' => null === $lastCheck ? null : (int) $lastCheck->data,
'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data,
'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data,
'single_user_mode' => $singleUser?->data,
];
}
@@ -139,7 +142,20 @@ class ConfigurationController extends Controller
'value' => $dynamic[$shortKey],
'editable' => true,
];
return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE);
}
if (str_starts_with($configKey, 'webhook.')) {
$data = [
'title' => $configKey,
'value' => $this->getWebhookConfiguration($configKey),
'editable' => false,
];
return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE);
}
// fallback
if (!str_starts_with($configKey, 'configuration.')) {
$data = [
'title' => $configKey,
@@ -182,4 +198,39 @@ class ConfigurationController extends Controller
return response()->api(['data' => $data])->header('Content-Type', self::CONTENT_TYPE);
}
private function getWebhookConfiguration(string $configKey): array
{
switch ($configKey) {
case 'webhook.triggers':
$cases = WebhookTrigger::cases();
$data = [];
foreach ($cases as $c) {
$data[$c->name] = $c->value;
}
return $data;
case 'webhook.responses':
$cases = WebhookResponse::cases();
$data = [];
foreach ($cases as $c) {
$data[$c->name] = $c->value;
}
return $data;
case 'webhook.deliveries':
$cases = WebhookDelivery::cases();
$data = [];
foreach ($cases as $c) {
$data[$c->name] = $c->value;
}
return $data;
default:
throw new FireflyException(sprintf('Unknown webhook configuration key "%s".', $configKey));
}
}
}

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Webhook;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Events\RequestedSendWebhookMessages;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
@@ -146,7 +147,7 @@ class ShowController extends Controller
$engine->setUser(auth()->user());
// tell the generator which trigger it should look for
$engine->setTrigger($webhook->trigger);
$engine->setTrigger(WebhookTrigger::tryFrom($webhook->trigger));
// tell the generator which objects to process
$engine->setObjects(new Collection([$group]));
// set the webhook to trigger

View File

@@ -24,11 +24,15 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Webhook;
use FireflyIII\Enums\WebhookResponse;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Models\Webhook;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
/**
* Class CreateRequest
@@ -55,9 +59,9 @@ class CreateRequest extends FormRequest
// this is the way.
$return = $this->getAllData($fields);
$return['trigger'] = $triggers[$return['trigger']] ?? (int) $return['trigger'];
$return['response'] = $responses[$return['response']] ?? (int) $return['response'];
$return['delivery'] = $deliveries[$return['delivery']] ?? (int) $return['delivery'];
$return['trigger'] = $triggers[$return['trigger']] ?? (int)$return['trigger'];
$return['response'] = $responses[$return['response']] ?? (int)$return['response'];
$return['delivery'] = $deliveries[$return['delivery']] ?? (int)$return['delivery'];
return $return;
}
@@ -81,4 +85,45 @@ class CreateRequest extends FormRequest
'url' => ['required', sprintf('url:%s', $validProtocols), 'uniqueWebhook'],
];
}
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator): void {
Log::debug('Validating webhook');
$data = $validator->getData();
$trigger = $data['trigger'] ?? null;
$response = $data['response'] ?? null;
if (null === $trigger || null === $response) {
Log::debug('No trigger or response, return.');
return;
}
$triggers = array_keys(Webhook::getTriggersForValidation());
$responses = array_keys(Webhook::getResponsesForValidation());
if (!in_array($trigger, $triggers, true) || !in_array($response, $responses, true)) {
return;
}
// cannot deliver budget info.
if (is_int($trigger)) {
Log::debug(sprintf('Trigger was integer (%d).', $trigger));
$trigger = WebhookTrigger::from($trigger)->name;
}
if (is_int($response)) {
Log::debug(sprintf('Response was integer (%d).', $response));
$response = WebhookResponse::from($response)->name;
}
Log::debug(sprintf('Trigger is %s, response is %s', $trigger, $response));
if (str_contains($trigger, 'TRANSACTION') && str_contains($response, 'BUDGET')) {
$validator->errors()->add('response', trans('validation.webhook_budget_info'));
}
if (str_contains($trigger, 'BUDGET') && str_contains($response, 'ACCOUNT')) {
$validator->errors()->add('response', trans('validation.webhook_account_info'));
}
if (str_contains($trigger, 'BUDGET') && str_contains($response, 'TRANSACTION')) {
$validator->errors()->add('response', trans('validation.webhook_transaction_info'));
}
}
);
}
}

View File

@@ -24,11 +24,15 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Webhook;
use FireflyIII\Enums\WebhookResponse;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Models\Webhook;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
/**
* Class UpdateRequest
@@ -94,4 +98,45 @@ class UpdateRequest extends FormRequest
'url' => [sprintf('url:%s', $validProtocols), sprintf('uniqueExistingWebhook:%d', $webhook->id)],
];
}
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator): void {
Log::debug('Validating webhook');
$data = $validator->getData();
$trigger = $data['trigger'] ?? null;
$response = $data['response'] ?? null;
if (null === $trigger || null === $response) {
Log::debug('No trigger or response, return.');
return;
}
$triggers = array_keys(Webhook::getTriggersForValidation());
$responses = array_keys(Webhook::getResponsesForValidation());
if (!in_array($trigger, $triggers, true) || !in_array($response, $responses, true)) {
return;
}
// cannot deliver budget info.
if (is_int($trigger)) {
Log::debug(sprintf('Trigger was integer (%d).', $trigger));
$trigger = WebhookTrigger::from($trigger)->name;
}
if (is_int($response)) {
Log::debug(sprintf('Response was integer (%d).', $response));
$response = WebhookResponse::from($response)->name;
}
Log::debug(sprintf('Trigger is %s, response is %s', $trigger, $response));
if (str_contains($trigger, 'TRANSACTION') && str_contains($response, 'BUDGET')) {
$validator->errors()->add('response', trans('validation.webhook_budget_info'));
}
if (str_contains($trigger, 'BUDGET') && str_contains($response, 'ACCOUNT')) {
$validator->errors()->add('response', trans('validation.webhook_account_info'));
}
if (str_contains($trigger, 'BUDGET') && str_contains($response, 'TRANSACTION')) {
$validator->errors()->add('response', trans('validation.webhook_transaction_info'));
}
}
);
}
}

View File

@@ -31,5 +31,6 @@ enum WebhookResponse: int
{
case TRANSACTIONS = 200;
case ACCOUNTS = 210;
case BUDGET = 230;
case NONE = 220;
}

View File

@@ -29,10 +29,11 @@ namespace FireflyIII\Enums;
*/
enum WebhookTrigger: int
{
case STORE_TRANSACTION = 100;
// case BEFORE_STORE_TRANSACTION = 101;
case UPDATE_TRANSACTION = 110;
// case BEFORE_UPDATE_TRANSACTION = 111;
case DESTROY_TRANSACTION = 120;
// case BEFORE_DESTROY_TRANSACTION = 121;
case STORE_TRANSACTION = 100;
case UPDATE_TRANSACTION = 110;
case DESTROY_TRANSACTION = 120;
case STORE_BUDGET = 200;
case UPDATE_BUDGET = 210;
case DESTROY_BUDGET = 220;
case STORE_UPDATE_BUDGET_LIMIT = 230;
}

View File

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

View File

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

View File

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

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Generator\Webhook;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\User;
use Illuminate\Support\Collection;
@@ -38,7 +39,7 @@ interface MessageGeneratorInterface
public function setObjects(Collection $objects): void;
public function setTrigger(int $trigger): void;
public function setTrigger(WebhookTrigger $trigger): void;
public function setUser(User $user): void;

View File

@@ -27,13 +27,19 @@ namespace FireflyIII\Generator\Webhook;
use FireflyIII\Enums\WebhookResponse;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Webhook;
use FireflyIII\Models\WebhookMessage;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment;
use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\BudgetLimitTransformer;
use FireflyIII\Transformers\BudgetTransformer;
use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Model;
@@ -47,11 +53,11 @@ use Symfony\Component\HttpFoundation\ParameterBag;
*/
class StandardMessageGenerator implements MessageGeneratorInterface
{
private Collection $objects;
private int $trigger;
private User $user;
private int $version = 0;
private Collection $webhooks;
private Collection $objects;
private WebhookTrigger $trigger;
private User $user;
private int $version = 0;
private Collection $webhooks;
public function __construct()
{
@@ -68,9 +74,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
}
// do some debugging
Log::debug(
sprintf('StandardMessageGenerator will generate messages for %d object(s) and %d webhook(s).', $this->objects->count(), $this->webhooks->count())
);
Log::debug(sprintf('StandardMessageGenerator will generate messages for %d object(s) and %d webhook(s).', $this->objects->count(), $this->webhooks->count()));
$this->run();
}
@@ -79,6 +83,9 @@ class StandardMessageGenerator implements MessageGeneratorInterface
return $this->user->webhooks()->where('active', true)->where('trigger', $this->trigger)->get(['webhooks.*']);
}
/**
* @throws FireflyException
*/
private function run(): void
{
Log::debug('Now in StandardMessageGenerator::run');
@@ -111,31 +118,43 @@ class StandardMessageGenerator implements MessageGeneratorInterface
$class = $model::class;
// Line is ignored because all of Firefly III's Models have an id property.
Log::debug(sprintf('Now in generateMessage(#%d, %s#%d)', $webhook->id, $class, $model->id));
$uuid = Uuid::uuid4();
$basicMessage = [
'uuid' => $uuid->toString(),
'user_id' => 0,
'trigger' => WebhookTrigger::from($webhook->trigger)->name,
'response' => WebhookResponse::from($webhook->response)->name,
'url' => $webhook->url,
'version' => sprintf('v%d', $this->getVersion()),
'content' => [],
'uuid' => $uuid->toString(),
'user_id' => 0,
'user_group_id' => 0,
'trigger' => WebhookTrigger::from((int)$webhook->trigger)->name,
'response' => WebhookResponse::from((int)$webhook->response)->name,
'url' => $webhook->url,
'version' => sprintf('v%d', $this->getVersion()),
'content' => [],
];
// depends on the model how user_id is set:
switch ($class) {
default:
// Line is ignored because all of Firefly III's Models have an id property.
Log::error(
sprintf('Webhook #%d was given %s#%d to deal with but can\'t extract user ID from it.', $webhook->id, $class, $model->id)
);
Log::error(sprintf('Webhook #%d was given %s#%d to deal with but can\'t extract user ID from it.', $webhook->id, $class, $model->id));
return;
case Budget::class:
/** @var Budget $model */
$basicMessage['user_id'] = $model->user_id;
$basicMessage['user_group_id'] = $model->user_group_id;
break;
case BudgetLimit::class:
$basicMessage['user_id'] = $model->budget->user_id;
$basicMessage['user_group_id'] = $model->budget->user_group_id;
break;
case TransactionGroup::class:
/** @var TransactionGroup $model */
$basicMessage['user_id'] = $model->user->id;
$basicMessage['user_id'] = $model->user_id;
$basicMessage['user_group_id'] = $model->user_group_id;
break;
}
@@ -143,12 +162,42 @@ class StandardMessageGenerator implements MessageGeneratorInterface
// then depends on the response what to put in the message:
switch ($webhook->response) {
default:
Log::error(
sprintf('The response code for webhook #%d is "%d" and the message generator cant handle it. Soft fail.', $webhook->id, $webhook->response)
);
Log::error(sprintf('The response code for webhook #%d is "%d" and the message generator cant handle it. Soft fail.', $webhook->id, $webhook->response));
return;
case WebhookResponse::BUDGET->value:
$basicMessage['content'] = [];
if ($model instanceof Budget) {
$enrichment = new BudgetEnrichment();
$enrichment->setUser($model->user);
$model = $enrichment->enrichSingle($model);
$transformer = new BudgetTransformer();
$basicMessage['content'] = $transformer->transform($model);
}
if ($model instanceof BudgetLimit) {
$user = $model->budget->user;
$enrichment = new BudgetEnrichment();
$enrichment->setUser($user);
$enrichment->setStart($model->start_date);
$enrichment->setEnd($model->end_date);
$budget = $enrichment->enrichSingle($model->budget);
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($user);
$parameters = new ParameterBag();
$parameters->set('start', $model->start_date);
$parameters->set('end', $model->end_date);
$model = $enrichment->enrichSingle($model);
$transformer = new BudgetLimitTransformer();
$transformer->setParameters($parameters);
$basicMessage['content'] = $transformer->transform($model);
}
break;
case WebhookResponse::NONE->value:
$basicMessage['content'] = [];
@@ -224,7 +273,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
$this->objects = $objects;
}
public function setTrigger(int $trigger): void
public function setTrigger(WebhookTrigger $trigger): void
{
$this->trigger = $trigger;
}

View File

@@ -53,7 +53,7 @@ class DestroyedGroupEventHandler
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection([$group]));
$engine->setTrigger(WebhookTrigger::DESTROY_TRANSACTION->value);
$engine->setTrigger(WebhookTrigger::DESTROY_TRANSACTION);
$engine->generateMessages();
event(new RequestedSendWebhookMessages());

View File

@@ -27,17 +27,7 @@ namespace FireflyIII\Handlers\Events\Model;
use FireflyIII\Events\Model\BudgetLimit\Created;
use FireflyIII\Events\Model\BudgetLimit\Deleted;
use FireflyIII\Events\Model\BudgetLimit\Updated;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\User;
use Illuminate\Support\Facades\Log;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Spatie\Period\Boundaries;
use Spatie\Period\Period;
use Spatie\Period\Precision;
/**
* Class BudgetLimitHandler
@@ -47,212 +37,9 @@ class BudgetLimitHandler
public function created(Created $event): void
{
Log::debug(sprintf('BudgetLimitHandler::created(#%s)', $event->budgetLimit->id));
$this->updateAvailableBudget($event->budgetLimit);
}
private function updateAvailableBudget(BudgetLimit $budgetLimit): void
{
Log::debug(sprintf('Now in updateAvailableBudget(limit #%d)', $budgetLimit->id));
public function deleted(Deleted $event): void {}
/** @var null|Budget $budget */
$budget = Budget::find($budgetLimit->budget_id);
if (null === $budget) {
Log::warning('Budget is null, probably deleted, find deleted version.');
/** @var null|Budget $budget */
$budget = Budget::withTrashed()->find($budgetLimit->budget_id);
}
if (null === $budget) {
Log::warning('Budget is still null, cannot continue, will delete budget limit.');
$budgetLimit->forceDelete();
return;
}
/** @var null|User $user */
$user = $budget->user;
// sanity check. It happens when the budget has been deleted so the original user is unknown.
if (null === $user) {
Log::warning('User is null, cannot continue.');
$budgetLimit->forceDelete();
return;
}
// based on the view range of the user (month week quarter etc) the budget limit could
// either overlap multiple available budget periods or be contained in a single one.
// all have to be created or updated.
try {
$viewRange = app('preferences')->getForUser($user, 'viewRange', '1M')->data;
} catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) {
Log::error($e->getMessage());
$viewRange = '1M';
}
// safety catch
if (null === $viewRange || is_array($viewRange)) {
$viewRange = '1M';
}
$viewRange = (string) $viewRange;
$start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange);
$end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange);
$end = app('navigation')->endOfPeriod($end, $viewRange);
// limit period in total is:
$limitPeriod = Period::make($start, $end, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE());
Log::debug(sprintf('Limit period is from %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
// from the start until the end of the budget limit, need to loop!
$current = clone $start;
while ($current <= $end) {
$currentEnd = app('navigation')->endOfPeriod($current, $viewRange);
// create or find AB for this particular period, and set the amount accordingly.
/** @var null|AvailableBudget $availableBudget */
$availableBudget = $user->availableBudgets()->where('start_date', $current->format('Y-m-d'))->where('end_date', $currentEnd->format('Y-m-d'))->where('transaction_currency_id', $budgetLimit->transaction_currency_id)->first();
if (null !== $availableBudget) {
Log::debug('Found 1 AB, will update.');
$this->calculateAmount($availableBudget);
}
if (null === $availableBudget) {
Log::debug('No AB found, will create.');
// if not exists:
$currentPeriod = Period::make($current, $currentEnd, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE());
$daily = $this->getDailyAmount($budgetLimit);
$amount = bcmul($daily, (string) $currentPeriod->length(), 12);
// no need to calculate if period is equal.
if ($currentPeriod->equals($limitPeriod)) {
$amount = 0 === $budgetLimit->id ? '0' : $budgetLimit->amount;
}
if (0 === bccomp($amount, '0')) {
Log::debug('Amount is zero, will not create AB.');
}
if (0 !== bccomp($amount, '0')) {
Log::debug(sprintf('Will create AB for period %s to %s', $current->format('Y-m-d'), $currentEnd->format('Y-m-d')));
$availableBudget = new AvailableBudget(
[
'user_id' => $user->id,
'user_group_id' => $user->user_group_id,
'transaction_currency_id' => $budgetLimit->transaction_currency_id,
'start_date' => $current,
'start_date_tz' => $current->format('e'),
'end_date' => $currentEnd,
'end_date_tz' => $currentEnd->format('e'),
'amount' => $amount,
]
);
$availableBudget->save();
Log::debug(sprintf('ID of new AB is #%d', $availableBudget->id));
$this->calculateAmount($availableBudget);
}
}
// prep for next loop
$current = app('navigation')->addPeriod($current, $viewRange, 0);
}
}
private function calculateAmount(AvailableBudget $availableBudget): void
{
$repository = app(BudgetLimitRepositoryInterface::class);
$repository->setUser($availableBudget->user);
$newAmount = '0';
$abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY());
Log::debug(
sprintf(
'Now at AB #%d, ("%s" to "%s")',
$availableBudget->id,
$availableBudget->start_date->format('Y-m-d'),
$availableBudget->end_date->format('Y-m-d')
)
);
// have to recalculate everything just in case.
$set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date);
Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count()));
/** @var BudgetLimit $budgetLimit */
foreach ($set as $budgetLimit) {
Log::debug(
sprintf(
'Found interesting budget limit #%d ("%s" to "%s")',
$budgetLimit->id,
$budgetLimit->start_date->format('Y-m-d'),
$budgetLimit->end_date->format('Y-m-d')
)
);
// overlap in days:
$limitPeriod = Period::make(
$budgetLimit->start_date,
$budgetLimit->end_date,
precision : Precision::DAY(),
boundaries: Boundaries::EXCLUDE_NONE()
);
// if both equal each other, amount from this BL must be added to the AB
if ($limitPeriod->equals($abPeriod)) {
Log::debug('This budget limit is equal to the available budget period.');
$newAmount = bcadd($newAmount, (string) $budgetLimit->amount);
}
// if budget limit period is inside AB period, it can be added in full.
if (!$limitPeriod->equals($abPeriod) && $abPeriod->contains($limitPeriod)) {
Log::debug('This budget limit is smaller than the available budget period.');
$newAmount = bcadd($newAmount, (string) $budgetLimit->amount);
}
if (!$limitPeriod->equals($abPeriod) && !$abPeriod->contains($limitPeriod) && $abPeriod->overlapsWith($limitPeriod)) {
Log::debug('This budget limit is something else entirely!');
$overlap = $abPeriod->overlap($limitPeriod);
if ($overlap instanceof Period) {
$length = $overlap->length();
$daily = bcmul($this->getDailyAmount($budgetLimit), (string) $length);
$newAmount = bcadd($newAmount, $daily);
}
}
}
if (0 === bccomp('0', $newAmount)) {
Log::debug('New amount is zero, deleting AB.');
$availableBudget->delete();
return;
}
Log::debug(sprintf('Concluded new amount for this AB must be %s', $newAmount));
$availableBudget->amount = app('steam')->bcround($newAmount, $availableBudget->transactionCurrency->decimal_places);
$availableBudget->save();
}
private function getDailyAmount(BudgetLimit $budgetLimit): string
{
if (0 === $budgetLimit->id) {
return '0';
}
$limitPeriod = Period::make(
$budgetLimit->start_date,
$budgetLimit->end_date,
precision : Precision::DAY(),
boundaries: Boundaries::EXCLUDE_NONE()
);
$days = $limitPeriod->length();
$amount = bcdiv($budgetLimit->amount, (string) $days, 12);
Log::debug(
sprintf('Total amount for budget limit #%d is %s. Nr. of days is %d. Amount per day is %s', $budgetLimit->id, $budgetLimit->amount, $days, $amount)
);
return $amount;
}
public function deleted(Deleted $event): void
{
Log::debug(sprintf('BudgetLimitHandler::deleted(#%s)', $event->budgetLimit->id));
$budgetLimit = $event->budgetLimit;
$budgetLimit->id = 0;
$this->updateAvailableBudget($event->budgetLimit);
}
public function updated(Updated $event): void
{
Log::debug(sprintf('BudgetLimitHandler::updated(#%s)', $event->budgetLimit->id));
$this->updateAvailableBudget($event->budgetLimit);
}
public function updated(Updated $event): void {}
}

View File

@@ -32,6 +32,7 @@ use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class StoredGroupEventHandler
@@ -51,11 +52,11 @@ class StoredGroupEventHandler
private function processRules(StoredTransactionGroup $storedGroupEvent): void
{
if (false === $storedGroupEvent->applyRules) {
app('log')->info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id));
Log::info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id));
return;
}
app('log')->debug('Now in StoredGroupEventHandler::processRules()');
Log::debug('Now in StoredGroupEventHandler::processRules()');
$journals = $storedGroupEvent->transactionGroup->transactionJournals;
$array = [];
@@ -65,7 +66,7 @@ class StoredGroupEventHandler
$array[] = $journal->id;
}
$journalIds = implode(',', $array);
app('log')->debug(sprintf('Add local operator for journal(s): %s', $journalIds));
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
// collect rules:
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
@@ -98,10 +99,10 @@ class StoredGroupEventHandler
*/
private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void
{
app('log')->debug(__METHOD__);
Log::debug(__METHOD__);
$group = $storedGroupEvent->transactionGroup;
if (false === $storedGroupEvent->fireWebhooks) {
app('log')->info(sprintf('Will not fire webhooks for transaction group #%d', $group->id));
Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id));
return;
}
@@ -113,7 +114,7 @@ class StoredGroupEventHandler
$engine->setUser($user);
// tell the generator which trigger it should look for
$engine->setTrigger(WebhookTrigger::STORE_TRANSACTION->value);
$engine->setTrigger(WebhookTrigger::STORE_TRANSACTION);
// tell the generator which objects to process
$engine->setObjects(new Collection([$group]));
// tell the generator to generate the messages

View File

@@ -164,7 +164,7 @@ class UpdatedGroupEventHandler
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection([$group]));
$engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION->value);
$engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION);
$engine->generateMessages();
event(new RequestedSendWebhookMessages());

View File

@@ -24,17 +24,36 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Events\RequestedSendWebhookMessages;
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class BudgetLimitObserver
{
use RecalculatesAvailableBudgetsTrait;
public function created(BudgetLimit $budgetLimit): void
{
Log::debug('Observe "created" of a budget limit.');
$this->updatePrimaryCurrencyAmount($budgetLimit);
$this->updateAvailableBudget($budgetLimit);
$user = $budgetLimit->budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budgetLimit));
$engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
$engine->generateMessages();
event(new RequestedSendWebhookMessages());
}
private function updatePrimaryCurrencyAmount(BudgetLimit $budgetLimit): void
@@ -60,5 +79,17 @@ class BudgetLimitObserver
{
Log::debug('Observe "updated" of a budget limit.');
$this->updatePrimaryCurrencyAmount($budgetLimit);
$this->updateAvailableBudget($budgetLimit);
$user = $budgetLimit->budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budgetLimit));
$engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
$engine->generateMessages();
event(new RequestedSendWebhookMessages());
}
}

View File

@@ -23,19 +23,70 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Events\RequestedSendWebhookMessages;
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class BudgetObserver
*/
class BudgetObserver
{
use RecalculatesAvailableBudgetsTrait;
public function created(Budget $budget): void
{
Log::debug(sprintf('Observe "created" of budget #%d ("%s").', $budget->id, $budget->name));
// fire event.
$user = $budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budget));
$engine->setTrigger(WebhookTrigger::STORE_BUDGET);
$engine->generateMessages();
event(new RequestedSendWebhookMessages());
}
public function updated(Budget $budget): void
{
Log::debug(sprintf('Observe "updated" of budget #%d ("%s").', $budget->id, $budget->name));
$user = $budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budget));
$engine->setTrigger(WebhookTrigger::UPDATE_BUDGET);
$engine->generateMessages();
event(new RequestedSendWebhookMessages());
}
public function deleting(Budget $budget): void
{
app('log')->debug('Observe "deleting" of a budget.');
Log::debug('Observe "deleting" of a budget.');
$user = $budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budget));
$engine->setTrigger(WebhookTrigger::DESTROY_BUDGET);
$engine->generateMessages();
event(new RequestedSendWebhookMessages());
$repository = app(AttachmentRepositoryInterface::class);
$repository->setUser($budget->user);
@@ -49,7 +100,10 @@ class BudgetObserver
/** @var BudgetLimit $budgetLimit */
foreach ($budgetLimits as $budgetLimit) {
// this loop exists so several events are fired.
$budgetLimit->delete();
$copy = clone $budgetLimit;
$copy->id = 0;
$this->updateAvailableBudget($copy);
$budgetLimit->deleteQuietly(); // delete is quietly when in a loop.
}
$budget->notes()->delete();

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\TransactionGroup;
use Illuminate\Support\Facades\Log;
/**
* Class TransactionGroup
@@ -32,7 +33,7 @@ class TransactionGroupObserver
{
public function deleting(TransactionGroup $transactionGroup): void
{
app('log')->debug('Observe "deleting" of a transaction group.');
Log::debug('Observe "deleting" of a transaction group.');
foreach ($transactionGroup->transactionJournals()->get() as $journal) {
$journal->delete();
}

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Illuminate\Support\Facades\Log;
/**
* Class TransactionJournalObserver
@@ -34,7 +35,7 @@ class TransactionJournalObserver
{
public function deleting(TransactionJournal $transactionJournal): void
{
app('log')->debug('Observe "deleting" of a transaction journal.');
Log::debug('Observe "deleting" of a transaction journal.');
$repository = app(AttachmentRepositoryInterface::class);
$repository->setUser($transactionJournal->user);

View File

@@ -29,9 +29,6 @@ use FireflyIII\Events\DestroyedTransactionGroup;
use FireflyIII\Events\DetectedNewIPAddress;
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
use FireflyIII\Events\Model\BudgetLimit\Created;
use FireflyIII\Events\Model\BudgetLimit\Deleted;
use FireflyIII\Events\Model\BudgetLimit\Updated;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\Events\Model\PiggyBank\ChangedName;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
@@ -219,17 +216,6 @@ class EventServiceProvider extends ServiceProvider
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName',
],
// budget related events: CRUD budget limit
Created::class => [
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@created',
],
Updated::class => [
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@updated',
],
Deleted::class => [
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted',
],
// rule actions
RuleActionFailedOnArray::class => [
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',

View File

@@ -57,6 +57,11 @@ class EitherConfigKey
'firefly.rule-actions',
'firefly.context-rule-actions',
'search.operators',
// webhooks
'webhook.triggers',
'webhook.responses',
'webhook.deliveries',
];
/**

View File

@@ -73,10 +73,12 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
public function enrich(Collection $collection): Collection
{
$this->collection = $collection;
$this->collectIds();
$this->collectCurrencies();
$this->collectSpentInfo();
$this->appendCollectedData();
if ($this->collection->count() > 0) {
$this->collectIds();
$this->collectCurrencies();
$this->collectSpentInfo();
$this->appendCollectedData();
}
return $this->collection;
}
@@ -85,7 +87,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
public function enrichSingle(array|Model $model): array|Model
{
Log::debug(__METHOD__);
$collection = new Collection([$model]);
$collection = new Collection()->push($model);
$collection = $this->enrich($collection);
return $collection->first();
@@ -119,8 +121,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
private function collectSpentInfo(): void
{
$start = $this->collection->min('start_date');
$end = $this->collection->max('end_date');
$start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth();
$end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth();
$allActive = $this->repository->getActiveBudgets();
$spentInBudgets = $this->opsRepository->collectExpenses($start, $end, null, $allActive, null);
$spentOutsideBudgets = $this->noBudgetRepository->collectExpenses($start, $end, null, null, null);
@@ -139,14 +141,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
$this->pcSpentInBudgets[$id] = array_values($pcFilteredSpentInBudgets);
$this->pcSpentOutsideBudgets[$id] = array_values($pcFilteredSpentOutsideBudgets);
}
// filter arrays on date.
// send them to sumCollection thing.
// save.
}
// first collect, then filter and append.
}
private function appendCollectedData(): void

View File

@@ -47,6 +47,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
$this->collectCurrencies();
$this->collectNotes();
$this->collectBudgets();
$this->stringifyIds();
$this->appendCollectedData();
return $this->collection;
@@ -155,4 +156,23 @@ class BudgetLimitEnrichment implements EnrichmentInterface
$this->currencies[(int)$currency->id] = $currency;
}
}
private function stringifyIds(): void
{
$this->expenses = array_map(function ($first) {
return array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
return $second;
}, $first);
}, $this->expenses);
$this->pcExpenses = array_map(function ($first) {
return array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
return $second;
}, $first);
}, $this->expenses);
}
}

View File

@@ -170,7 +170,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
// add object group if available
if (array_key_exists($id, $this->mappedObjects)) {
$key = $this->mappedObjects[$id];
$meta['object_group_id'] = $this->objectGroups[$key]['id'];
$meta['object_group_id'] = (string) $this->objectGroups[$key]['id'];
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
}

View File

@@ -0,0 +1,212 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Support\Observers;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\User;
use Illuminate\Support\Facades\Log;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Spatie\Period\Boundaries;
use Spatie\Period\Period;
use Spatie\Period\Precision;
trait RecalculatesAvailableBudgetsTrait
{
private function updateAvailableBudget(BudgetLimit $budgetLimit): void
{
Log::debug(sprintf('Now in updateAvailableBudget(limit #%d)', $budgetLimit->id));
/** @var null|Budget $budget */
$budget = Budget::find($budgetLimit->budget_id);
if (null === $budget) {
Log::warning('Budget is null, probably deleted, find deleted version.');
/** @var null|Budget $budget */
$budget = Budget::withTrashed()->find($budgetLimit->budget_id);
}
if (null === $budget) {
Log::warning('Budget is still null, cannot continue, will delete budget limit.');
$budgetLimit->forceDelete();
return;
}
/** @var null|User $user */
$user = $budget->user;
// sanity check. It happens when the budget has been deleted so the original user is unknown.
if (null === $user) {
Log::warning('User is null, cannot continue.');
$budgetLimit->forceDelete();
return;
}
// based on the view range of the user (month week quarter etc) the budget limit could
// either overlap multiple available budget periods or be contained in a single one.
// all have to be created or updated.
try {
$viewRange = app('preferences')->getForUser($user, 'viewRange', '1M')->data;
} catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) {
Log::error($e->getMessage());
$viewRange = '1M';
}
// safety catch
if (null === $viewRange || is_array($viewRange)) {
$viewRange = '1M';
}
$viewRange = (string) $viewRange;
$start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange);
$end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange);
$end = app('navigation')->endOfPeriod($end, $viewRange);
// limit period in total is:
$limitPeriod = Period::make($start, $end, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE());
Log::debug(sprintf('Limit period is from %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
// from the start until the end of the budget limit, need to loop!
$current = clone $start;
while ($current <= $end) {
$currentEnd = app('navigation')->endOfPeriod($current, $viewRange);
// create or find AB for this particular period, and set the amount accordingly.
/** @var null|AvailableBudget $availableBudget */
$availableBudget = $user->availableBudgets()->where('start_date', $current->format('Y-m-d'))->where('end_date', $currentEnd->format('Y-m-d'))->where('transaction_currency_id', $budgetLimit->transaction_currency_id)->first();
if (null !== $availableBudget) {
Log::debug('Found 1 AB, will update.');
$this->calculateAmount($availableBudget);
}
if (null === $availableBudget) {
Log::debug('No AB found, will create.');
// if not exists:
$currentPeriod = Period::make($current, $currentEnd, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE());
$daily = $this->getDailyAmount($budgetLimit);
$amount = bcmul($daily, (string) $currentPeriod->length(), 12);
// no need to calculate if period is equal.
if ($currentPeriod->equals($limitPeriod)) {
$amount = 0 === $budgetLimit->id ? '0' : $budgetLimit->amount;
}
if (0 === bccomp($amount, '0')) {
Log::debug('Amount is zero, will not create AB.');
}
if (0 !== bccomp($amount, '0')) {
Log::debug(sprintf('Will create AB for period %s to %s', $current->format('Y-m-d'), $currentEnd->format('Y-m-d')));
$availableBudget = new AvailableBudget(
[
'user_id' => $user->id,
'user_group_id' => $user->user_group_id,
'transaction_currency_id' => $budgetLimit->transaction_currency_id,
'start_date' => $current,
'start_date_tz' => $current->format('e'),
'end_date' => $currentEnd,
'end_date_tz' => $currentEnd->format('e'),
'amount' => $amount,
]
);
$availableBudget->save();
Log::debug(sprintf('ID of new AB is #%d', $availableBudget->id));
$this->calculateAmount($availableBudget);
}
}
// prep for next loop
$current = app('navigation')->addPeriod($current, $viewRange, 0);
}
}
private function calculateAmount(AvailableBudget $availableBudget): void
{
$repository = app(BudgetLimitRepositoryInterface::class);
$repository->setUser($availableBudget->user);
$newAmount = '0';
$abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY());
Log::debug(
sprintf(
'Now at AB #%d, ("%s" to "%s")',
$availableBudget->id,
$availableBudget->start_date->format('Y-m-d'),
$availableBudget->end_date->format('Y-m-d')
)
);
// have to recalculate everything just in case.
$set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date);
Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count()));
/** @var BudgetLimit $budgetLimit */
foreach ($set as $budgetLimit) {
Log::debug(
sprintf(
'Found interesting budget limit #%d ("%s" to "%s")',
$budgetLimit->id,
$budgetLimit->start_date->format('Y-m-d'),
$budgetLimit->end_date->format('Y-m-d')
)
);
// overlap in days:
$limitPeriod = Period::make(
$budgetLimit->start_date,
$budgetLimit->end_date,
precision : Precision::DAY(),
boundaries: Boundaries::EXCLUDE_NONE()
);
// if both equal each other, amount from this BL must be added to the AB
if ($limitPeriod->equals($abPeriod)) {
Log::debug('This budget limit is equal to the available budget period.');
$newAmount = bcadd($newAmount, (string) $budgetLimit->amount);
}
// if budget limit period is inside AB period, it can be added in full.
if (!$limitPeriod->equals($abPeriod) && $abPeriod->contains($limitPeriod)) {
Log::debug('This budget limit is smaller than the available budget period.');
$newAmount = bcadd($newAmount, (string) $budgetLimit->amount);
}
if (!$limitPeriod->equals($abPeriod) && !$abPeriod->contains($limitPeriod) && $abPeriod->overlapsWith($limitPeriod)) {
Log::debug('This budget limit is something else entirely!');
$overlap = $abPeriod->overlap($limitPeriod);
if ($overlap instanceof Period) {
$length = $overlap->length();
$daily = bcmul($this->getDailyAmount($budgetLimit), (string) $length);
$newAmount = bcadd($newAmount, $daily);
}
}
}
if (0 === bccomp('0', $newAmount)) {
Log::debug('New amount is zero, deleting AB.');
$availableBudget->delete();
return;
}
Log::debug(sprintf('Concluded new amount for this AB must be %s', $newAmount));
$availableBudget->amount = app('steam')->bcround($newAmount, $availableBudget->transactionCurrency->decimal_places);
$availableBudget->save();
}
private function getDailyAmount(BudgetLimit $budgetLimit): string
{
if (0 === $budgetLimit->id) {
return '0';
}
$limitPeriod = Period::make(
$budgetLimit->start_date,
$budgetLimit->end_date,
precision : Precision::DAY(),
boundaries: Boundaries::EXCLUDE_NONE()
);
$days = $limitPeriod->length();
$amount = bcdiv($budgetLimit->amount, (string) $days, 12);
Log::debug(
sprintf('Total amount for budget limit #%d is %s. Nr. of days is %d. Amount per day is %s', $budgetLimit->id, $budgetLimit->amount, $days, $amount)
);
return $amount;
}
}

View File

@@ -356,8 +356,10 @@ class Steam
continue;
}
$accountSum = array_values($accountSum)[0];
$sumOfAmount = (string)$accountSum['sum_of_amount'];
$sumOfAmount = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount);
$sumsByCode = [
$accountSum['code'] => $accountSum['sum_of_amount'],
$accountSum['code'] => $sumOfAmount,
];
// Log::debug('All balances are (joined)', $others);

View File

@@ -28,6 +28,7 @@ use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
use League\Fractal\Resource\Item;
/**
@@ -55,7 +56,15 @@ class BudgetLimitTransformer extends AbstractTransformer
*/
public function includeBudget(BudgetLimit $limit)
{
return $this->item($limit->budget, new BudgetTransformer(), 'budgets');
// enrich budget
$budget = $limit->budget;
$enrichment = new BudgetEnrichment();
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$enrichment->setUser($budget->user);
$budget = $enrichment->enrichSingle($budget);
return $this->item($budget, new BudgetTransformer(), 'budgets');
}
/**
@@ -91,7 +100,7 @@ class BudgetLimitTransformer extends AbstractTransformer
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'primary_currency_id' => (int)$this->primaryCurrency->id,
'primary_currency_id' => (string) $this->primaryCurrency->id,
'primary_currency_name' => $this->primaryCurrency->name,
'primary_currency_code' => $this->primaryCurrency->code,
'primary_currency_symbol' => $this->primaryCurrency->symbol,

View File

@@ -3,7 +3,17 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 6.3.0 - 2025-08-xx
## 6.3.1 - 2025-08-19
### Fixed
- [Discussion 10768](https://github.com/orgs/firefly-iii/discussions/10768) (Argument #1 ($start) must be of type Carbon\Carbon, null given) started by @tangodance
- [Issue 10771](https://github.com/firefly-iii/firefly-iii/issues/10771) (/v1/budgets/{id}/limits seems broken) reported by @Sceptorrh
- [Issue 10773](https://github.com/firefly-iii/firefly-iii/issues/10773) (API: Wrong Return types) reported by @dreautall
- [Issue 10775](https://github.com/firefly-iii/firefly-iii/issues/10775) (API: /v1/chart/account/overview broken) reported by @dreautall
- [Issue 10782](https://github.com/firefly-iii/firefly-iii/issues/10782) ([error'] /accounts/[asset,expense,revenue]) reported by @vkanev
## 6.3.0 - 2025-08-17
> [!WARNING]
> Firefly III v6.3.0 introduces a lot of API changes that deal with multi-currency support. Make sure your beloved apps are updated to support this.

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag.
],
'version' => '6.3.0',
'build_time' => 1755366750,
'version' => '6.3.1',
'build_time' => 1755540382,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 26,

View File

@@ -57,6 +57,7 @@ return [
'liability_direction_credit_short',
'liability_direction_null_short',
'interest_calc_yearly',
'loading',
'interest_calc_',
'interest_calc_null',
'interest_calc_daily',
@@ -246,12 +247,19 @@ return [
'multi_account_warning_withdrawal',
'multi_account_warning_deposit',
'multi_account_warning_transfer',
'webhook_trigger_STORE_TRANSACTION',
'webhook_trigger_UPDATE_TRANSACTION',
'webhook_trigger_DESTROY_TRANSACTION',
'webhook_trigger_STORE_BUDGET',
'webhook_trigger_UPDATE_BUDGET',
'webhook_trigger_DESTROY_BUDGET',
'webhook_trigger_STORE_UPDATE_BUDGET_LIMIT',
'webhook_response_TRANSACTIONS',
'webhook_response_ACCOUNTS',
'webhook_response_none_NONE',
'webhook_response_NONE',
'webhook_delivery_JSON',
'actions',
'meta_data',

178
package-lock.json generated
View File

@@ -2592,9 +2592,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
"integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.3.tgz",
"integrity": "sha512-UmTdvXnLlqQNOCJnyksjPs1G4GqXNGW1LrzCe8+8QoaLhhDeTXYBgJ3k6x61WIhlHX2U+VzEJ55TtIjR/HTySA==",
"cpu": [
"arm"
],
@@ -2606,9 +2606,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
"integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.3.tgz",
"integrity": "sha512-8NoxqLpXm7VyeI0ocidh335D6OKT0UJ6fHdnIxf3+6oOerZZc+O7r+UhvROji6OspyPm+rrIdb1gTXtVIqn+Sg==",
"cpu": [
"arm64"
],
@@ -2620,9 +2620,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
"integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.3.tgz",
"integrity": "sha512-csnNavqZVs1+7/hUKtgjMECsNG2cdB8F7XBHP6FfQjqhjF8rzMzb3SLyy/1BG7YSfQ+bG75Ph7DyedbUqwq1rA==",
"cpu": [
"arm64"
],
@@ -2634,9 +2634,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
"integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.3.tgz",
"integrity": "sha512-r2MXNjbuYabSIX5yQqnT8SGSQ26XQc8fmp6UhlYJd95PZJkQD1u82fWP7HqvGUf33IsOC6qsiV+vcuD4SDP6iw==",
"cpu": [
"x64"
],
@@ -2648,9 +2648,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
"integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.3.tgz",
"integrity": "sha512-uluObTmgPJDuJh9xqxyr7MV61Imq+0IvVsAlWyvxAaBSNzCcmZlhfYcRhCdMaCsy46ccZa7vtDDripgs9Jkqsw==",
"cpu": [
"arm64"
],
@@ -2662,9 +2662,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
"integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.3.tgz",
"integrity": "sha512-AVJXEq9RVHQnejdbFvh1eWEoobohUYN3nqJIPI4mNTMpsyYN01VvcAClxflyk2HIxvLpRcRggpX1m9hkXkpC/A==",
"cpu": [
"x64"
],
@@ -2676,9 +2676,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
"integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.3.tgz",
"integrity": "sha512-byyflM+huiwHlKi7VHLAYTKr67X199+V+mt1iRgJenAI594vcmGGddWlu6eHujmcdl6TqSNnvqaXJqZdnEWRGA==",
"cpu": [
"arm"
],
@@ -2690,9 +2690,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
"integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.3.tgz",
"integrity": "sha512-aLm3NMIjr4Y9LklrH5cu7yybBqoVCdr4Nvnm8WB7PKCn34fMCGypVNpGK0JQWdPAzR/FnoEoFtlRqZbBBLhVoQ==",
"cpu": [
"arm"
],
@@ -2704,9 +2704,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
"integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.3.tgz",
"integrity": "sha512-VtilE6eznJRDIoFOzaagQodUksTEfLIsvXymS+UdJiSXrPW7Ai+WG4uapAc3F7Hgs791TwdGh4xyOzbuzIZrnw==",
"cpu": [
"arm64"
],
@@ -2718,9 +2718,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
"integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.3.tgz",
"integrity": "sha512-dG3JuS6+cRAL0GQ925Vppafi0qwZnkHdPeuZIxIPXqkCLP02l7ka+OCyBoDEv8S+nKHxfjvjW4OZ7hTdHkx8/w==",
"cpu": [
"arm64"
],
@@ -2732,9 +2732,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
"integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.3.tgz",
"integrity": "sha512-iU8DxnxEKJptf8Vcx4XvAUdpkZfaz0KWfRrnIRrOndL0SvzEte+MTM7nDH4A2Now4FvTZ01yFAgj6TX/mZl8hQ==",
"cpu": [
"loong64"
],
@@ -2746,9 +2746,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
"integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.3.tgz",
"integrity": "sha512-VrQZp9tkk0yozJoQvQcqlWiqaPnLM6uY1qPYXvukKePb0fqaiQtOdMJSxNFUZFsGw5oA5vvVokjHrx8a9Qsz2A==",
"cpu": [
"ppc64"
],
@@ -2760,9 +2760,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
"integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.3.tgz",
"integrity": "sha512-uf2eucWSUb+M7b0poZ/08LsbcRgaDYL8NCGjUeFMwCWFwOuFcZ8D9ayPl25P3pl+D2FH45EbHdfyUesQ2Lt9wA==",
"cpu": [
"riscv64"
],
@@ -2774,9 +2774,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
"integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.3.tgz",
"integrity": "sha512-7tnUcDvN8DHm/9ra+/nF7lLzYHDeODKKKrh6JmZejbh1FnCNZS8zMkZY5J4sEipy2OW1d1Ncc4gNHUd0DLqkSg==",
"cpu": [
"riscv64"
],
@@ -2788,9 +2788,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
"integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.3.tgz",
"integrity": "sha512-MUpAOallJim8CsJK+4Lc9tQzlfPbHxWDrGXZm2z6biaadNpvh3a5ewcdat478W+tXDoUiHwErX/dOql7ETcLqg==",
"cpu": [
"s390x"
],
@@ -2802,9 +2802,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
"integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.3.tgz",
"integrity": "sha512-F42IgZI4JicE2vM2PWCe0N5mR5vR0gIdORPqhGQ32/u1S1v3kLtbZ0C/mi9FFk7C5T0PgdeyWEPajPjaUpyoKg==",
"cpu": [
"x64"
],
@@ -2816,9 +2816,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
"integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.3.tgz",
"integrity": "sha512-oLc+JrwwvbimJUInzx56Q3ujL3Kkhxehg7O1gWAYzm8hImCd5ld1F2Gry5YDjR21MNb5WCKhC9hXgU7rRlyegQ==",
"cpu": [
"x64"
],
@@ -2830,9 +2830,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
"integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.3.tgz",
"integrity": "sha512-lOrQ+BVRstruD1fkWg9yjmumhowR0oLAAzavB7yFSaGltY8klttmZtCLvOXCmGE9mLIn8IBV/IFrQOWz5xbFPg==",
"cpu": [
"arm64"
],
@@ -2844,9 +2844,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
"integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.3.tgz",
"integrity": "sha512-vvrVKPRS4GduGR7VMH8EylCBqsDcw6U+/0nPDuIjXQRbHJc6xOBj+frx8ksfZAh6+Fptw5wHrN7etlMmQnPQVg==",
"cpu": [
"ia32"
],
@@ -2858,9 +2858,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
"integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.3.tgz",
"integrity": "sha512-fi3cPxCnu3ZeM3EwKZPgXbWoGzm2XHgB/WShKI81uj8wG0+laobmqy5wbgEwzstlbLu4MyO8C19FyhhWseYKNQ==",
"cpu": [
"x64"
],
@@ -5700,9 +5700,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.5.203",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.203.tgz",
"integrity": "sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==",
"version": "1.5.204",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.204.tgz",
"integrity": "sha512-s9VbBXWxfDrl67PlO4avwh0/GU2vcwx8Fph3wlR8LJl7ySGYId59EFE17VWVcuC3sLWNPENm6Z/uGqKbkPCcXA==",
"dev": true,
"license": "ISC"
},
@@ -10123,9 +10123,9 @@
}
},
"node_modules/rollup": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
"version": "4.46.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.3.tgz",
"integrity": "sha512-RZn2XTjXb8t5g13f5YclGoilU/kwT696DIkY3sywjdZidNSi3+vseaQov7D7BZXVJCPv3pDWUN69C78GGbXsKw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10139,26 +10139,26 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.46.2",
"@rollup/rollup-android-arm64": "4.46.2",
"@rollup/rollup-darwin-arm64": "4.46.2",
"@rollup/rollup-darwin-x64": "4.46.2",
"@rollup/rollup-freebsd-arm64": "4.46.2",
"@rollup/rollup-freebsd-x64": "4.46.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
"@rollup/rollup-linux-arm-musleabihf": "4.46.2",
"@rollup/rollup-linux-arm64-gnu": "4.46.2",
"@rollup/rollup-linux-arm64-musl": "4.46.2",
"@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
"@rollup/rollup-linux-ppc64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-musl": "4.46.2",
"@rollup/rollup-linux-s390x-gnu": "4.46.2",
"@rollup/rollup-linux-x64-gnu": "4.46.2",
"@rollup/rollup-linux-x64-musl": "4.46.2",
"@rollup/rollup-win32-arm64-msvc": "4.46.2",
"@rollup/rollup-win32-ia32-msvc": "4.46.2",
"@rollup/rollup-win32-x64-msvc": "4.46.2",
"@rollup/rollup-android-arm-eabi": "4.46.3",
"@rollup/rollup-android-arm64": "4.46.3",
"@rollup/rollup-darwin-arm64": "4.46.3",
"@rollup/rollup-darwin-x64": "4.46.3",
"@rollup/rollup-freebsd-arm64": "4.46.3",
"@rollup/rollup-freebsd-x64": "4.46.3",
"@rollup/rollup-linux-arm-gnueabihf": "4.46.3",
"@rollup/rollup-linux-arm-musleabihf": "4.46.3",
"@rollup/rollup-linux-arm64-gnu": "4.46.3",
"@rollup/rollup-linux-arm64-musl": "4.46.3",
"@rollup/rollup-linux-loongarch64-gnu": "4.46.3",
"@rollup/rollup-linux-ppc64-gnu": "4.46.3",
"@rollup/rollup-linux-riscv64-gnu": "4.46.3",
"@rollup/rollup-linux-riscv64-musl": "4.46.3",
"@rollup/rollup-linux-s390x-gnu": "4.46.3",
"@rollup/rollup-linux-x64-gnu": "4.46.3",
"@rollup/rollup-linux-x64-musl": "4.46.3",
"@rollup/rollup-win32-arm64-msvc": "4.46.3",
"@rollup/rollup-win32-ia32-msvc": "4.46.3",
"@rollup/rollup-win32-x64-msvc": "4.46.3",
"fsevents": "~2.3.2"
}
},
@@ -11850,9 +11850,9 @@
"license": "BSD-2-Clause"
},
"node_modules/webpack": {
"version": "5.101.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.2.tgz",
"integrity": "sha512-4JLXU0tD6OZNVqlwzm3HGEhAHufSiyv+skb7q0d2367VDMzrU1Q/ZeepvkcHH0rZie6uqEtTQQe0OEOOluH3Mg==",
"version": "5.101.3",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz",
"integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -24,7 +24,10 @@
{{ $t('form.webhook_delivery') }}
</label>
<div class="col-sm-8">
<select
<div v-if="loading" class="form-control-static">
<em class="fa fa-spinner fa-spin"></em> {{ $t('firefly.loading') }}
</div>
<select v-if="!loading"
ref="bill"
v-model="delivery"
:title="$t('form.webhook_delivery')"
@@ -49,6 +52,7 @@ export default {
name: "WebhookDelivery",
data() {
return {
loading: true,
delivery : 0,
deliveries: [
@@ -71,8 +75,24 @@ export default {
mounted() {
this.delivery = this.value;
this.deliveries = [
{id: 300, name: this.$t('firefly.webhook_delivery_JSON')},
//{id: 300, name: this.$t('firefly.webhook_delivery_JSON')},
];
axios.get('./api/v1/configuration/webhook.deliveries').then((response) => {
for (let key in response.data.data.value) {
if (!response.data.data.value.hasOwnProperty(key)) {
continue;
}
this.deliveries.push(
{
id: response.data.data.value[key],
name: this.$t('firefly.webhook_delivery_' + key),
}
);
}
this.loading = false;
}).catch((error) => {
this.loading = false;
});
},
watch: {
value() {

View File

@@ -19,73 +19,89 @@
-->
<template>
<div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<label class="col-sm-4 control-label">
{{ $t('form.webhook_response') }}
</label>
<div class="col-sm-8">
<select
ref="bill"
v-model="response"
:title="$t('form.webhook_response')"
class="form-control"
name="webhook_response"
>
<option v-for="response in this.responses"
:label="response.name"
:value="response.id">{{ response.name }}
</option>
</select>
<p class="help-block" v-text="$t('firefly.webhook_response_form_help')"></p>
<ul v-for="error in this.error" class="list-unstyled">
<li class="text-danger">{{ error }}</li>
</ul>
<div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<label class="col-sm-4 control-label">
{{ $t('form.webhook_response') }}
</label>
<div class="col-sm-8">
<div v-if="loading" class="form-control-static">
<em class="fa fa-spinner fa-spin"></em> {{ $t('firefly.loading') }}
</div>
<select v-if="!loading"
ref="response"
v-model="response"
:title="$t('form.webhook_response')"
class="form-control"
name="webhook_response"
>
<option v-for="response in this.responses"
:label="response.name"
:value="response.id">{{ response.name }}
</option>
</select>
<p class="help-block" v-text="$t('firefly.webhook_response_form_help')"></p>
<ul v-for="error in this.error" class="list-unstyled">
<li class="text-danger">{{ error }}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
name: "WebhookResponse",
data() {
return {
response: 0,
responses: [],
};
},
props: {
error: {
type: Array,
required: true,
default() {
return []
}
name: "WebhookResponse",
data() {
return {
loading: true,
response: 0,
responses: [],
};
},
value: {
type: Number,
required: true,
}
},
watch: {
value() {
this.response = this.value;
props: {
error: {
type: Array,
required: true,
default() {
return []
}
},
value: {
type: Number,
required: true,
}
},
watch: {
value() {
this.response = this.value;
},
response(newValue) {
this.$emit('input', newValue);
}
},
mounted() {
this.response = this.value;
this.responses = [];
axios.get('./api/v1/configuration/webhook.responses').then((response) => {
for (let key in response.data.data.value) {
if (!response.data.data.value.hasOwnProperty(key)) {
continue;
}
this.responses.push(
{
id: response.data.data.value[key],
name: this.$t('firefly.webhook_response_' + key),
}
);
}
this.loading = false;
}).catch((error) => {
this.loading = false;
});
},
methods: {
hasError() {
return this.error?.length > 0;
}
},
response(newValue) {
this.$emit('input', newValue);
}
},
mounted() {
this.response = this.value;
this.responses = [
{id: 200, name: this.$t('firefly.webhook_response_TRANSACTIONS')},
{id: 210, name: this.$t('firefly.webhook_response_ACCOUNTS')},
{id: 220, name: this.$t('firefly.webhook_response_none_NONE')},
];
},
methods: {
hasError() {
return this.error?.length > 0;
}
},
}
</script>

View File

@@ -19,73 +19,91 @@
-->
<template>
<div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<label class="col-sm-4 control-label">
{{ $t('form.webhook_trigger') }}
</label>
<div class="col-sm-8">
<select
ref="bill"
v-model="trigger"
:title="$t('form.webhook_trigger')"
class="form-control"
name="webhook_trigger"
>
<option v-for="trigger in this.triggers"
:label="trigger.name"
:value="trigger.id">{{ trigger.name }}
</option>
</select>
<p class="help-block" v-text="$t('firefly.webhook_trigger_form_help')"></p>
<ul v-for="error in this.error" class="list-unstyled">
<li class="text-danger">{{ error }}</li>
</ul>
<div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<label class="col-sm-4 control-label">
{{ $t('form.webhook_trigger') }}
</label>
<div class="col-sm-8">
<div v-if="loading" class="form-control-static">
<em class="fa fa-spinner fa-spin"></em> {{ $t('firefly.loading') }}
</div>
<select v-if="!loading"
ref="trigger"
v-model="trigger"
:title="$t('form.webhook_trigger')"
class="form-control"
name="webhook_trigger"
>
<option v-for="trigger in this.triggers"
:label="trigger.name"
:value="trigger.id">{{ trigger.name }}
</option>
</select>
<p class="help-block" v-text="$t('firefly.webhook_trigger_form_help')"></p>
<ul v-for="error in this.error" class="list-unstyled">
<li class="text-danger">{{ error }}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
name: "WebhookTrigger",
data() {
return {
trigger: 0,
triggers: [],
};
},
props: {
error: {
type: Array,
required: true,
default() {
return []
}
name: "WebhookTrigger",
data() {
return {
trigger: 0,
loading: true,
triggers: [],
};
},
value: {
type: Number,
required: true,
}
},
mounted() {
this.trigger = this.value;
this.triggers = [
{id: 100, name: this.$t('firefly.webhook_trigger_STORE_TRANSACTION')},
{id: 110, name: this.$t('firefly.webhook_trigger_UPDATE_TRANSACTION')},
{id: 120, name: this.$t('firefly.webhook_trigger_DESTROY_TRANSACTION')},
];
},
watch: {
value() {
this.trigger = this.value;
props: {
error: {
type: Array,
required: true,
default() {
return []
}
},
value: {
type: Number,
required: true,
}
},
mounted() {
this.trigger = this.value;
this.triggers = [];
axios.get('./api/v1/configuration/webhook.triggers').then((response) => {
for (let key in response.data.data.value) {
if (!response.data.data.value.hasOwnProperty(key)) {
continue;
}
this.triggers.push(
{
id: response.data.data.value[key],
name: this.$t('firefly.webhook_trigger_' + key),
}
);
console.log('webhook trigger: id=' + response.data.data.value[key] + ', name=' + key);
}
this.loading = false;
}).catch((error) => {
this.loading = false;
});
},
watch: {
value() {
console.log('Value changed to ' + this.value);
this.trigger = this.value;
},
trigger(newValue) {
this.$emit('input', newValue);
}
},
methods: {
hasError() {
return this.error?.length > 0;
}
},
trigger(newValue) {
this.$emit('input', newValue);
}
},
methods: {
hasError() {
return this.error?.length > 0;
}
},
}
</script>

View File

@@ -129,6 +129,8 @@ export default {
this.id = parseInt(response.data.data.id);
// trigger value on content
console.log('Trigger is ' + response.data.data.attributes.trigger);
// TODO does not need to be hard coded.
if ('STORE_TRANSACTION' === response.data.data.attributes.trigger) {
this.trigger = 100;
}
@@ -138,6 +140,18 @@ export default {
if ('DESTROY_TRANSACTION' === response.data.data.attributes.trigger) {
this.trigger = 120;
}
if ('STORE_BUDGET' === response.data.data.attributes.trigger) {
this.trigger = 200;
}
if ('UPDATE_BUDGET' === response.data.data.attributes.trigger) {
this.trigger = 210;
}
if ('DESTROY_BUDGET' === response.data.data.attributes.trigger) {
this.trigger = 220;
}
if ('STORE_UPDATE_BUDGET_LIMIT' === response.data.data.attributes.trigger) {
this.trigger = 230;
}
// response value
if ('TRANSACTIONS' === response.data.data.attributes.response) {
@@ -146,6 +160,9 @@ export default {
if ('ACCOUNTS' === response.data.data.attributes.response) {
this.response = 210;
}
if ('BUDGET' === response.data.data.attributes.response) {
this.response = 230;
}
if ('NONE' === response.data.data.attributes.response) {
this.response = 220;
}

View File

@@ -19,138 +19,172 @@
-->
<template>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">
{{ $t('firefly.webhooks') }}
</h3>
</div>
<div class="box-body no-padding">
<div style="padding:8px;">
<a href="webhooks/create" class="btn btn-success"><span class="fa fa-plus fa-fw"></span>{{ $t('firefly.create_new_webhook') }}</a>
</div>
<table class="table table-responsive table-hover" v-if="webhooks.length > 0" aria-label="A table.">
<thead>
<tr>
<th>Title</th>
<th>Responds when</th>
<th>Responds with (delivery)</th>
<th style="width:20%;">Secret (show / hide)</th>
<th>URL</th>
<th class="hidden-sm hidden-xs">&nbsp;</th>
</tr>
</thead>
<tbody>
<tr v-for="webhook in webhooks" :key="webhook.id">
<td>
<a :href="'webhooks/show/' + webhook.id">{{ webhook.title }}</a>
</td>
<td>
<span v-if="webhook.active">{{ triggers[webhook.trigger] }}</span>
<span v-if="!webhook.active" class="text-muted"><s>{{ triggers[webhook.trigger] }}</s> ({{ $t('firefly.inactive') }})</span>
</td>
<td>{{ responses[webhook.response] }} ({{ deliveries[webhook.delivery] }})</td>
<td>
<em style="cursor:pointer"
v-if="webhook.show_secret" class="fa fa-eye" @click="toggleSecret(webhook)"></em>
<em style="cursor:pointer"
v-if="!webhook.show_secret" class="fa fa-eye-slash" @click="toggleSecret(webhook)"></em>
<code v-if="webhook.show_secret">{{ webhook.secret }}</code>
<code v-if="!webhook.show_secret">********</code>
</td>
<td>
<code :title="webhook.full_url">{{ webhook.url }}</code>
</td>
<td class="hidden-sm hidden-xs">
<div class="btn-group btn-group-xs pull-right">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ $t('firefly.actions') }} <span class="caret"></span></button>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li><a :href="'webhooks/show/' + webhook.id"><span class="fa fa-fw fa-search"></span> {{ $t('firefly.inspect') }}</a></li>
<li><a :href="'webhooks/edit/' + webhook.id"><span class="fa fa-fw fa-pencil"></span> {{$t( 'firefly.edit') }}</a></li>
<li><a :href="'webhooks/delete/' + webhook.id"><span class="fa fa-fw fa-trash"></span> {{ $t('firefly.delete') }}</a></li>
</ul>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">
{{ $t('firefly.webhooks') }}
</h3>
</div>
</td>
</tr>
</tbody>
</table>
<div class="box-body no-padding">
<div style="padding:8px;">
<a href="webhooks/create" class="btn btn-success"><span
class="fa fa-plus fa-fw"></span>{{ $t('firefly.create_new_webhook') }}</a>
</div>
<div v-if="webhooks.length > 0" style="padding:8px;">
<a href="webhooks/create" class="btn btn-success"><span class="fa fa-plus fa-fw"></span>{{ $t('firefly.create_new_webhook') }}</a>
</div>
<table class="table table-responsive table-hover" v-if="webhooks.length > 0" aria-label="A table.">
<thead>
<tr>
<th>Title</th>
<th>Responds when</th>
<th>Responds with (delivery)</th>
<th style="width:20%;">Secret (show / hide)</th>
<th>URL</th>
<th class="hidden-sm hidden-xs">&nbsp;</th>
</tr>
</thead>
<tbody>
<tr v-for="webhook in webhooks" :key="webhook.id">
<td>
<a :href="'webhooks/show/' + webhook.id">{{ webhook.title }}</a>
</td>
<td>
<span v-if="webhook.active">{{ triggers[webhook.trigger] }}</span>
<span v-if="!webhook.active" class="text-muted"><s>{{ triggers[webhook.trigger] }}</s> ({{
$t('firefly.inactive')
}})</span>
</td>
<td>{{ responses[webhook.response] }} ({{ deliveries[webhook.delivery] }})</td>
<td>
<em style="cursor:pointer"
v-if="webhook.show_secret" class="fa fa-eye" @click="toggleSecret(webhook)"></em>
<em style="cursor:pointer"
v-if="!webhook.show_secret" class="fa fa-eye-slash"
@click="toggleSecret(webhook)"></em>
<code v-if="webhook.show_secret">{{ webhook.secret }}</code>
<code v-if="!webhook.show_secret">********</code>
</td>
<td>
<code :title="webhook.full_url">{{ webhook.url }}</code>
</td>
<td class="hidden-sm hidden-xs">
<div class="btn-group btn-group-xs pull-right">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
{{ $t('firefly.actions') }} <span class="caret"></span></button>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li><a :href="'webhooks/show/' + webhook.id"><span
class="fa fa-fw fa-search"></span> {{ $t('firefly.inspect') }}</a></li>
<li><a :href="'webhooks/edit/' + webhook.id"><span
class="fa fa-fw fa-pencil"></span> {{ $t('firefly.edit') }}</a></li>
<li><a :href="'webhooks/delete/' + webhook.id"><span
class="fa fa-fw fa-trash"></span> {{ $t('firefly.delete') }}</a></li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
<div v-if="webhooks.length > 0" style="padding:8px;">
<a href="webhooks/create" class="btn btn-success"><span
class="fa fa-plus fa-fw"></span>{{ $t('firefly.create_new_webhook') }}</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Index",
data() {
return {
webhooks: [],
triggers: {
STORE_TRANSACTION: this.$t('firefly.webhook_trigger_STORE_TRANSACTION'),
UPDATE_TRANSACTION: this.$t('firefly.webhook_trigger_UPDATE_TRANSACTION'),
DESTROY_TRANSACTION: this.$t('firefly.webhook_trigger_DESTROY_TRANSACTION'),
},
responses: {
TRANSACTIONS: this.$t('firefly.webhook_response_TRANSACTIONS'),
ACCOUNTS: this.$t('firefly.webhook_response_ACCOUNTS'),
NONE: this.$t('firefly.webhook_response_none_NONE'),
},
deliveries: {
JSON: this.$t('firefly.webhook_delivery_JSON'),
},
};
},
mounted() {
this.getWebhooks();
},
methods: {
getWebhooks: function () {
this.webhooks = [];
this.downloadWebhooks(1);
name: "Index",
data() {
return {
webhooks: [],
triggers: {
},
responses: {
},
deliveries: {
},
};
},
toggleSecret: function (webhook) {
webhook.show_secret = !webhook.show_secret;
mounted() {
this.getOptions();
},
downloadWebhooks: function (page) {
axios.get("./api/v1/webhooks?page=" + page).then((response) => {
for (let i in response.data.data) {
if (response.data.data.hasOwnProperty(i)) {
let current = response.data.data[i];
let webhook = {
id: current.id,
title: current.attributes.title,
url: current.attributes.url,
active: current.attributes.active,
full_url: current.attributes.url,
secret: current.attributes.secret,
trigger: current.attributes.trigger,
response: current.attributes.response,
delivery: current.attributes.delivery,
show_secret: false,
};
if(current.attributes.url.length > 20) {
webhook.url = current.attributes.url.slice(0, 20) + '...';
}
this.webhooks.push(webhook);
}
}
methods: {
getOptions: function () {
// get triggers
axios.get('./api/v1/configuration/webhook.triggers').then((response) => {
for (let key in response.data.data.value) {
if (!response.data.data.value.hasOwnProperty(key)) {
continue;
}
this.triggers[key] = this.$t('firefly.webhook_trigger_' + key);
}
if (response.data.meta.pagination.current_page < response.data.meta.pagination.total_pages) {
this.downloadWebhooks(response.data.meta.pagination.current_page + 1);
}
});
},
}
// get responses
axios.get('./api/v1/configuration/webhook.responses').then((response) => {
for (let key in response.data.data.value) {
if (!response.data.data.value.hasOwnProperty(key)) {
continue;
}
this.responses[key] = this.$t('firefly.webhook_response_' + key);
}
// get deliveries
axios.get('./api/v1/configuration/webhook.deliveries').then((response) => {
for (let key in response.data.data.value) {
if (!response.data.data.value.hasOwnProperty(key)) {
continue;
}
this.deliveries[key] = this.$t('firefly.webhook_delivery_' + key);
}
// get webhooks
this.getWebhooks();
})
})
});
},
getWebhooks: function () {
this.webhooks = [];
this.downloadWebhooks(1);
},
toggleSecret: function (webhook) {
webhook.show_secret = !webhook.show_secret;
},
downloadWebhooks: function (page) {
axios.get("./api/v1/webhooks?page=" + page).then((response) => {
for (let i in response.data.data) {
if (response.data.data.hasOwnProperty(i)) {
let current = response.data.data[i];
let webhook = {
id: current.id,
title: current.attributes.title,
url: current.attributes.url,
active: current.attributes.active,
full_url: current.attributes.url,
secret: current.attributes.secret,
trigger: current.attributes.trigger,
response: current.attributes.response,
delivery: current.attributes.delivery,
show_secret: false,
};
if (current.attributes.url.length > 20) {
webhook.url = current.attributes.url.slice(0, 20) + '...';
}
this.webhooks.push(webhook);
}
}
if (response.data.meta.pagination.current_page < response.data.meta.pagination.total_pages) {
this.downloadWebhooks(response.data.meta.pagination.current_page + 1);
}
});
},
}
}
</script>

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "After transaction creation",
"webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaction details",
"webhook_response_ACCOUNTS": "Account details",
"webhook_response_none_NONE": "No details",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Actions",
"meta_data": "Meta data",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "\u0628\u0639\u062f \u0625\u0646\u0634\u0627\u0621 \u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629",
"webhook_trigger_UPDATE_TRANSACTION": "\u0628\u0639\u062f \u062a\u062d\u062f\u064a\u062b \u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629",
"webhook_trigger_DESTROY_TRANSACTION": "\u0628\u0639\u062f \u062d\u0630\u0641 \u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u062a\u0641\u0627\u0635\u064a\u0644 \u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629",
"webhook_response_ACCOUNTS": "\u062a\u0641\u0627\u0635\u064a\u0644 \u0627\u0644\u062d\u0633\u0627\u0628",
"webhook_response_none_NONE": "\u0644\u0627 \u062a\u0648\u062c\u062f \u062a\u0641\u0627\u0635\u064a\u0644",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\u0627\u0644\u0625\u062c\u0631\u0627\u0621\u0627\u062a",
"meta_data": "\u0628\u064a\u0627\u0646\u0627\u062a \u0648\u0635\u0641\u064a\u0629",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "After transaction creation",
"webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaction details",
"webhook_response_ACCOUNTS": "Account details",
"webhook_response_none_NONE": "No details",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f",
"meta_data": "\u041c\u0435\u0442\u0430 \u0434\u0430\u043d\u043d\u0438",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Despr\u00e9s de crear la transacci\u00f3",
"webhook_trigger_UPDATE_TRANSACTION": "Despr\u00e9s d'actualitzar la transacci\u00f3",
"webhook_trigger_DESTROY_TRANSACTION": "Despr\u00e9s d'eliminar la transacci\u00f3",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Detalls de la transacci\u00f3",
"webhook_response_ACCOUNTS": "Detalls del compte",
"webhook_response_none_NONE": "Sense detalls",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Accions",
"meta_data": "Meta dades",

View File

@@ -10,7 +10,7 @@
"welcome_back": "Jak to jde?",
"flash_error": "Chyba!",
"flash_warning": "Varov\u00e1n\u00ed!",
"flash_success": "\u00dasp\u011b\u0161n\u011b dokon\u010deno!",
"flash_success": "V\u00fdborn\u011b!",
"close": "Zav\u0159\u00edt",
"select_dest_account": "Please select or type a valid destination account name",
"select_source_account": "Please select or type a valid source account name",
@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Po vytvo\u0159en\u00ed transakce",
"webhook_trigger_UPDATE_TRANSACTION": "Po aktualizaci transakce",
"webhook_trigger_DESTROY_TRANSACTION": "Po odstran\u011bn\u00ed transakce",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Podrobnosti transakce",
"webhook_response_ACCOUNTS": "Podrobnosti \u00fa\u010dtu",
"webhook_response_none_NONE": "\u017d\u00e1dn\u00e9 detaily",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Akce",
"meta_data": "Metadata",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Efter oprettelse af transaktion",
"webhook_trigger_UPDATE_TRANSACTION": "Efter opdatering af transaktion",
"webhook_trigger_DESTROY_TRANSACTION": "Efter sletning af transaktion",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaktionsdetaljer",
"webhook_response_ACCOUNTS": "Kontodetaljer",
"webhook_response_none_NONE": "Ingen detaljer",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Handlinger",
"meta_data": "Meta data",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Nach Erstellen einer Buchung",
"webhook_trigger_UPDATE_TRANSACTION": "Nach Aktualisierung einer Buchung",
"webhook_trigger_DESTROY_TRANSACTION": "Nach dem L\u00f6schen einer Buchung",
"webhook_trigger_STORE_BUDGET": "Nach der Erstellung des Budgets",
"webhook_trigger_UPDATE_BUDGET": "Nach der Aktualisierung des Budgets",
"webhook_trigger_DESTROY_BUDGET": "Nach dem L\u00f6schen des Budgets",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Nach dem \u00c4ndern des budgetierten Betrags",
"webhook_response_TRANSACTIONS": "Buchungsdetails",
"webhook_response_ACCOUNTS": "Kontodetails",
"webhook_response_none_NONE": "Keine Daten",
"webhook_response_NONE": "Keine Details",
"webhook_delivery_JSON": "JSON",
"actions": "Aktionen",
"meta_data": "Metadaten",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "\u039c\u03b5\u03c4\u03ac \u03c4\u03b7 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2",
"webhook_trigger_UPDATE_TRANSACTION": "\u039c\u03b5\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2",
"webhook_trigger_DESTROY_TRANSACTION": "\u039c\u03b5\u03c4\u03ac \u03c4\u03b7 \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u039b\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2",
"webhook_response_ACCOUNTS": "\u03a0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd",
"webhook_response_none_NONE": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03bd \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\u0395\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b5\u03c2",
"meta_data": "\u039c\u03b5\u03c4\u03b1-\u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "After transaction creation",
"webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaction details",
"webhook_response_ACCOUNTS": "Account details",
"webhook_response_none_NONE": "No details",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Actions",
"meta_data": "Meta data",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "After transaction creation",
"webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaction details",
"webhook_response_ACCOUNTS": "Account details",
"webhook_response_none_NONE": "No details",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Actions",
"meta_data": "Meta data",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Despu\u00e9s de crear la transacci\u00f3n",
"webhook_trigger_UPDATE_TRANSACTION": "Despu\u00e9s de actualizar la transacci\u00f3n",
"webhook_trigger_DESTROY_TRANSACTION": "Despu\u00e9s de eliminar la transacci\u00f3n",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Detalles de la transacci\u00f3n",
"webhook_response_ACCOUNTS": "Detalles de la cuenta",
"webhook_response_none_NONE": "Sin detalles",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Acciones",
"meta_data": "Meta Datos",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "\u067e\u0633 \u0627\u0632 \u0627\u06cc\u062c\u0627\u062f \u062a\u0631\u0627\u06a9\u0646\u0634\n",
"webhook_trigger_UPDATE_TRANSACTION": "\u067e\u0633 \u0627\u0632 \u0628\u0647 \u0631\u0648\u0632 \u0631\u0633\u0627\u0646\u06cc \u062a\u0631\u0627\u06a9\u0646\u0634\n",
"webhook_trigger_DESTROY_TRANSACTION": "\u067e\u0633 \u0627\u0632 \u062a\u0631\u0627\u06a9\u0646\u0634 \u062d\u0630\u0641 \u0634\u0648\u062f\n",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u062c\u0632\u0626\u06cc\u0627\u062a \u062a\u0631\u0627\u06a9\u0646\u0634",
"webhook_response_ACCOUNTS": "\u062c\u0632\u0626\u06cc\u0627\u062a \u062d\u0633\u0627\u0628",
"webhook_response_none_NONE": "\u0628\u062f\u0648\u0646 \u062c\u0632\u0626\u06cc\u0627\u062a",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\u0639\u0645\u0644\u06cc\u0627\u062a \u0647\u0627",
"meta_data": "\u062f\u0627\u062f\u0647 \u0647\u0627\u06cc \u0645\u062a\u0627\n",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "After transaction creation",
"webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaction details",
"webhook_response_ACCOUNTS": "Tilin tiedot",
"webhook_response_none_NONE": "No details",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Toiminnot",
"meta_data": "Metatieto",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Apr\u00e8s la cr\u00e9ation de l'op\u00e9ration",
"webhook_trigger_UPDATE_TRANSACTION": "Apr\u00e8s la mise \u00e0 jour de l'op\u00e9ration",
"webhook_trigger_DESTROY_TRANSACTION": "Apr\u00e8s la suppression de l'op\u00e9ration",
"webhook_trigger_STORE_BUDGET": "Apr\u00e8s la cr\u00e9ation du budget",
"webhook_trigger_UPDATE_BUDGET": "Apr\u00e8s la mise \u00e0 jour du budget",
"webhook_trigger_DESTROY_BUDGET": "Apr\u00e8s la suppression du budget",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Apr\u00e8s le changement du montant budg\u00e9tis\u00e9",
"webhook_response_TRANSACTIONS": "D\u00e9tails de l'op\u00e9ration",
"webhook_response_ACCOUNTS": "D\u00e9tails du compte",
"webhook_response_none_NONE": "Aucun d\u00e9tail",
"webhook_response_NONE": "Aucun d\u00e9tail",
"webhook_delivery_JSON": "JSON",
"actions": "Actions",
"meta_data": "M\u00e9tadonn\u00e9es",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Tranzakci\u00f3 l\u00e9trehoz\u00e1sa ut\u00e1n",
"webhook_trigger_UPDATE_TRANSACTION": "Tranzakci\u00f3 friss\u00edt\u00e9se ut\u00e1n",
"webhook_trigger_DESTROY_TRANSACTION": "Tranzakci\u00f3 t\u00f6rl\u00e9se ut\u00e1n",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Tranzakci\u00f3 r\u00e9szletei",
"webhook_response_ACCOUNTS": "Sz\u00e1mlaadatok",
"webhook_response_none_NONE": "Nincsenek r\u00e9szletek",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "M\u0171veletek",
"meta_data": "Metaadat",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "After transaction creation",
"webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaction details",
"webhook_response_ACCOUNTS": "Account details",
"webhook_response_none_NONE": "Tidak ada detil",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Tindakan",
"meta_data": "Data meta",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Dopo aver creato la transazione",
"webhook_trigger_UPDATE_TRANSACTION": "Dopo aver aggiornato la transazione",
"webhook_trigger_DESTROY_TRANSACTION": "Dopo aver eliminato la transazione",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Dettagli transazione",
"webhook_response_ACCOUNTS": "Dettagli conto",
"webhook_response_none_NONE": "Nessun dettaglio",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Azioni",
"meta_data": "Meta dati",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "\u53d6\u5f15\u4f5c\u6210\u5f8c",
"webhook_trigger_UPDATE_TRANSACTION": "\u53d6\u5f15\u66f4\u65b0\u5f8c",
"webhook_trigger_DESTROY_TRANSACTION": "\u53d6\u5f15\u524a\u9664\u5f8c",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u53d6\u5f15\u8a73\u7d30",
"webhook_response_ACCOUNTS": "\u53e3\u5ea7\u8a73\u7d30",
"webhook_response_none_NONE": "\u8a73\u7d30\u306a\u3057",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\u64cd\u4f5c",
"meta_data": "\u30e1\u30bf\u30c7\u30fc\u30bf",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "\uac70\ub798 \uc0dd\uc131 \uc774\ud6c4",
"webhook_trigger_UPDATE_TRANSACTION": "\uac70\ub798 \uc5c5\ub370\uc774\ud2b8 \uc774\ud6c4",
"webhook_trigger_DESTROY_TRANSACTION": "\uac70\ub798 \uc0ad\uc81c \uc774\ud6c4",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\uac70\ub798 \uc138\ubd80 \uc815\ubcf4",
"webhook_response_ACCOUNTS": "\uacc4\uc815 \uc815\ubcf4",
"webhook_response_none_NONE": "\uc0c1\uc138\uc815\ubcf4 \uc5c6\uc74c",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\uc561\uc158",
"meta_data": "\uba54\ud0c0\ub370\uc774\ud130",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Etter transaksjons opprettelse",
"webhook_trigger_UPDATE_TRANSACTION": "Etter transaksjons oppdatering",
"webhook_trigger_DESTROY_TRANSACTION": "Etter transaksjons sletting",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaksjonsdetaljer",
"webhook_response_ACCOUNTS": "Kontodetaljer",
"webhook_response_none_NONE": "Ingen detaljer",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Handlinger",
"meta_data": "Metadata",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Na het maken van een transactie",
"webhook_trigger_UPDATE_TRANSACTION": "Na het updaten van een transactie",
"webhook_trigger_DESTROY_TRANSACTION": "Na het verwijderen van een transactie",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transactiedetails",
"webhook_response_ACCOUNTS": "Rekeningdetails",
"webhook_response_none_NONE": "Geen details",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Acties",
"meta_data": "Metagegevens",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Etter transaksjons opprettelse",
"webhook_trigger_UPDATE_TRANSACTION": "Etter transaksjons oppdatering",
"webhook_trigger_DESTROY_TRANSACTION": "Etter transaksjons sletting",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaksjonsdetaljer",
"webhook_response_ACCOUNTS": "Kontodetaljer",
"webhook_response_none_NONE": "Ingen detaljer",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Handlinger",
"meta_data": "Metadata",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Po utworzeniu transakcji",
"webhook_trigger_UPDATE_TRANSACTION": "Po zmodyfikowaniu transakcji",
"webhook_trigger_DESTROY_TRANSACTION": "Po usuni\u0119ciu transakcji",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Szczeg\u00f3\u0142y transakcji",
"webhook_response_ACCOUNTS": "Szczeg\u00f3\u0142y konta",
"webhook_response_none_NONE": "Brak szczeg\u00f3\u0142\u00f3w",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Akcje",
"meta_data": "Metadane",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Ap\u00f3s cria\u00e7\u00e3o da transa\u00e7\u00e3o",
"webhook_trigger_UPDATE_TRANSACTION": "Ap\u00f3s atualiza\u00e7\u00e3o da transa\u00e7\u00e3o",
"webhook_trigger_DESTROY_TRANSACTION": "Ap\u00f3s exclus\u00e3o da transa\u00e7\u00e3o",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Detalhes da transa\u00e7\u00e3o",
"webhook_response_ACCOUNTS": "Detalhes da conta",
"webhook_response_none_NONE": "Sem detalhes",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "A\u00e7\u00f5es",
"meta_data": "Meta dados",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Ap\u00f3s criar transa\u00e7\u00e3o",
"webhook_trigger_UPDATE_TRANSACTION": "Ap\u00f3s atualizar transa\u00e7\u00e3o",
"webhook_trigger_DESTROY_TRANSACTION": "Ap\u00f3s eliminar transa\u00e7\u00e3o",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Detalhes da transa\u00e7\u00e3o",
"webhook_response_ACCOUNTS": "Detalhes da conta",
"webhook_response_none_NONE": "Sem dados",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "A\u00e7\u00f5es",
"meta_data": "Meta dados",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Dup\u0103 crearea tranzac\u021biei",
"webhook_trigger_UPDATE_TRANSACTION": "Dup\u0103 actualizarea tranzac\u021biei",
"webhook_trigger_DESTROY_TRANSACTION": "Dup\u0103 \u0219tergerea tranzac\u021biei",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Detaliile tranzac\u021biei",
"webhook_response_ACCOUNTS": "Detalii cont",
"webhook_response_none_NONE": "Fara detalii",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Ac\u021biuni",
"meta_data": "Date meta",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438",
"webhook_trigger_UPDATE_TRANSACTION": "\u041f\u043e\u0441\u043b\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438",
"webhook_trigger_DESTROY_TRANSACTION": "\u041f\u043e\u0441\u043b\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u0414\u0435\u0442\u0430\u043b\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438",
"webhook_response_ACCOUNTS": "\u0421\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u043e\u0431 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438",
"webhook_response_none_NONE": "\u041d\u0435\u0442 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0445 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u0439",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f",
"meta_data": "\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "After transaction creation",
"webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaction details",
"webhook_response_ACCOUNTS": "Account details",
"webhook_response_none_NONE": "No details",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Akcie",
"meta_data": "Metadata",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Po ustvarjanju transakcije",
"webhook_trigger_UPDATE_TRANSACTION": "Po posodabljanju transakcije",
"webhook_trigger_DESTROY_TRANSACTION": "Po brisanju transakcije",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Podrobnosti transakcije",
"webhook_response_ACCOUNTS": "Podrobnosti ra\u010duna",
"webhook_response_none_NONE": "Ni podrobnosti",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Dejanja",
"meta_data": "Meta podatki",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Efter skapande av transaktion",
"webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Transaktionsdetaljer",
"webhook_response_ACCOUNTS": "Kontodetaljer",
"webhook_response_none_NONE": "Inga detaljer",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\u00c5tg\u00e4rder",
"meta_data": "Metadata",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "\u0130\u015flem olu\u015fturma sonras\u0131",
"webhook_trigger_UPDATE_TRANSACTION": "\u0130\u015flem g\u00fcncelleme sonras\u0131",
"webhook_trigger_DESTROY_TRANSACTION": "\u0130\u015flem silme sonras\u0131",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u0130\u015flem detaylar\u0131",
"webhook_response_ACCOUNTS": "Hesap detaylar\u0131",
"webhook_response_none_NONE": "Detay yok",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Eylemler",
"meta_data": "Meta veri",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "\u041f\u0456\u0441\u043b\u044f \u0441\u0442\u0432\u043e\u0440\u0435\u043d\u043d\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0456\u0457",
"webhook_trigger_UPDATE_TRANSACTION": "\u041f\u0456\u0441\u043b\u044f \u043e\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0456\u0457",
"webhook_trigger_DESTROY_TRANSACTION": "\u041f\u0456\u0441\u043b\u044f \u0432\u0438\u0434\u0430\u043b\u0435\u043d\u043d\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0456\u0457",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u0414\u0435\u0442\u0430\u043b\u0456 \u043e\u043f\u0435\u0440\u0430\u0446\u0456\u0457",
"webhook_response_ACCOUNTS": "\u0414\u0430\u043d\u0456 \u0440\u0430\u0445\u0443\u043d\u043a\u0443",
"webhook_response_none_NONE": "\u041d\u0435\u043c\u0430\u0454 \u0434\u0430\u043d\u0438\u0445",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\u0414\u0456\u0457",
"meta_data": "\u041c\u0435\u0442\u0430-\u0434\u0430\u043d\u0456",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "Sau khi t\u1ea1o giao d\u1ecbch",
"webhook_trigger_UPDATE_TRANSACTION": "Sau khi c\u1eadp nh\u1eadt giao d\u1ecbch",
"webhook_trigger_DESTROY_TRANSACTION": "Sau khi x\u00f3a giao d\u1ecbch",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Chi ti\u1ebft giao d\u1ecbch",
"webhook_response_ACCOUNTS": "Chi ti\u1ebft t\u00e0i kho\u1ea3n",
"webhook_response_none_NONE": "Kh\u00f4ng c\u00f3 chi ti\u1ebft",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "H\u00e0nh \u0111\u1ed9ng",
"meta_data": "Meta data",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "\u4ea4\u6613\u521b\u5efa\u540e",
"webhook_trigger_UPDATE_TRANSACTION": "\u4ea4\u6613\u66f4\u65b0\u540e",
"webhook_trigger_DESTROY_TRANSACTION": "\u4ea4\u6613\u5220\u9664\u540e",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u4ea4\u6613\u8be6\u60c5",
"webhook_response_ACCOUNTS": "\u8d26\u6237\u8be6\u60c5",
"webhook_response_none_NONE": "\u65e0\u8be6\u7ec6\u4fe1\u606f",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\u64cd\u4f5c",
"meta_data": "\u540e\u8bbe\u8d44\u6599",

View File

@@ -110,9 +110,13 @@
"webhook_trigger_STORE_TRANSACTION": "\u5728\u4ea4\u6613\u5efa\u7acb\u5f8c",
"webhook_trigger_UPDATE_TRANSACTION": "\u5728\u4ea4\u6613\u66f4\u65b0\u5f8c",
"webhook_trigger_DESTROY_TRANSACTION": "\u5728\u4ea4\u6613\u522a\u9664\u5f8c",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u4ea4\u6613\u8a73\u60c5",
"webhook_response_ACCOUNTS": "\u5e33\u865f\u8a73\u60c5",
"webhook_response_none_NONE": "\u7121\u4efb\u4f55\u8a73\u60c5",
"webhook_response_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "\u64cd\u4f5c",
"meta_data": "\u4e2d\u7e7c\u8cc7\u6599",

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,9 @@
declare(strict_types=1);
return [
'webhook_budget_info' => 'Cannot deliver budget information for transaction related webhooks.',
'webhook_account_info' => 'Cannot deliver account information for budget related webhooks.',
'webhook_transaction_info' => 'Cannot deliver transaction information for budget related webhooks.',
'invalid_account_type' => 'A piggy bank can only be linked to asset accounts and liabilities',
'invalid_account_currency' => 'This account does not use the currency you have selected',
'current_amount_too_much' => 'The combined amount in "current_amount" cannot exceed the "target_amount".',