diff --git a/.ci/php-cs-fixer/composer.lock b/.ci/php-cs-fixer/composer.lock index 10dbc4c803..97d0a4a42a 100644 --- a/.ci/php-cs-fixer/composer.lock +++ b/.ci/php-cs-fixer/composer.lock @@ -226,16 +226,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.46.0", + "version": "v3.48.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "be6831c9af1740470d2a773119b9273f8ac1c3d2" + "reference": "a92472c6fb66349de25211f31c77eceae3df024e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/be6831c9af1740470d2a773119b9273f8ac1c3d2", - "reference": "be6831c9af1740470d2a773119b9273f8ac1c3d2", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a92472c6fb66349de25211f31c77eceae3df024e", + "reference": "a92472c6fb66349de25211f31c77eceae3df024e", "shasum": "" }, "require": { @@ -305,7 +305,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.46.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.48.0" }, "funding": [ { @@ -313,7 +313,7 @@ "type": "github" } ], - "time": "2024-01-03T21:38:46+00:00" + "time": "2024-01-19T21:44:39+00:00" }, { "name": "psr/container", diff --git a/.env.example b/.env.example index 37d4ca504d..da12344ec9 100644 --- a/.env.example +++ b/.env.example @@ -122,7 +122,7 @@ SESSION_DRIVER=file # If you use Docker or similar, you can set REDIS_HOST_FILE, REDIS_PASSWORD_FILE or # REDIS_PORT_FILE to set the value from a file instead of from an environment variable -# can be tcp, unix or http +# can be tcp or unix. http is not supported REDIS_SCHEME=tcp # use only when using 'unix' for REDIS_SCHEME. Leave empty otherwise. diff --git a/.github/label-actions.yml b/.github/label-actions.yml index 24e17447f0..aa0909dc36 100644 --- a/.github/label-actions.yml +++ b/.github/label-actions.yml @@ -47,7 +47,7 @@ needs-moar-debug: 1. Firefly III version 2. Docker, self-hosted, or hosted by a third party? - 3. Operating system and browser + 3. Operating system and browser Thank you for your contributions. unlabel: needs-moar-debug @@ -81,5 +81,5 @@ v2-layout-issue: Thank you for your contributions. close: true close-reason: completed - lock: true + lock: false unlabel: v2-layout-issue diff --git a/.github/workflows/debug-info-actions.yml b/.github/workflows/debug-info-actions.yml index 90ff6bffa2..be7b336eaf 100644 --- a/.github/workflows/debug-info-actions.yml +++ b/.github/workflows/debug-info-actions.yml @@ -23,7 +23,6 @@ jobs: if [[ $ISSUE_BODY == *".eOxNZAmyGz6CXMyf"* ]]; then gh issue comment "$NUMBER" --body "$V2_ISSUE_REPLY_BODY" gh issue close "$NUMBER" --reason completed - gh issue lock "$NUMBER" fi env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index f80cc38ef4..d32b27d5ee 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -15,5 +15,5 @@ jobs: - uses: JC5/lock-threads@main with: github-token: ${{ github.token }} - issue-inactive-days: 90 - pr-inactive-days: 90 + issue-inactive-days: 7 + pr-inactive-days: 7 diff --git a/app/Api/V1/Controllers/Data/DestroyController.php b/app/Api/V1/Controllers/Data/DestroyController.php index 61da46dcf1..77ea43e76b 100644 --- a/app/Api/V1/Controllers/Data/DestroyController.php +++ b/app/Api/V1/Controllers/Data/DestroyController.php @@ -183,7 +183,7 @@ class DestroyController extends Controller } if (false === $this->unused) { app('log')->info(sprintf('Deleting account #%d "%s"', $account->id, $account->name)); - Log::channel('audit')->info(sprintf('Deleted account #%d "%s"', $account->id, $account->name)); + Log::channel('audit')->warning(sprintf('Deleted account #%d "%s"', $account->id, $account->name)); $service->destroy($account, null); } } diff --git a/app/Api/V1/Controllers/Models/Attachment/DestroyController.php b/app/Api/V1/Controllers/Models/Attachment/DestroyController.php index b886fb757f..4eb91f9d54 100644 --- a/app/Api/V1/Controllers/Models/Attachment/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Attachment/DestroyController.php @@ -67,7 +67,7 @@ class DestroyController extends Controller public function destroy(Attachment $attachment): JsonResponse { if(true === auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); + Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); throw new NotFoundHttpException(); } diff --git a/app/Api/V1/Controllers/Models/Attachment/ShowController.php b/app/Api/V1/Controllers/Models/Attachment/ShowController.php index ad9b89010b..1b792e8101 100644 --- a/app/Api/V1/Controllers/Models/Attachment/ShowController.php +++ b/app/Api/V1/Controllers/Models/Attachment/ShowController.php @@ -76,7 +76,7 @@ class ShowController extends Controller public function download(Attachment $attachment): LaravelResponse { if(true === auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); + Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); throw new NotFoundHttpException(); } @@ -124,7 +124,7 @@ class ShowController extends Controller public function index(): JsonResponse { if(true === auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); + Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); throw new NotFoundHttpException(); } @@ -162,7 +162,7 @@ class ShowController extends Controller public function show(Attachment $attachment): JsonResponse { if(true === auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); + Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); throw new NotFoundHttpException(); } diff --git a/app/Api/V1/Controllers/Models/Attachment/StoreController.php b/app/Api/V1/Controllers/Models/Attachment/StoreController.php index 16560e5985..aaf8e0a558 100644 --- a/app/Api/V1/Controllers/Models/Attachment/StoreController.php +++ b/app/Api/V1/Controllers/Models/Attachment/StoreController.php @@ -75,7 +75,7 @@ class StoreController extends Controller public function store(StoreRequest $request): JsonResponse { if(true === auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); + Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); throw new NotFoundHttpException(); } @@ -99,7 +99,7 @@ class StoreController extends Controller public function upload(Request $request, Attachment $attachment): JsonResponse { if(true === auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); + Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); throw new NotFoundHttpException(); } diff --git a/app/Api/V1/Controllers/Models/Attachment/UpdateController.php b/app/Api/V1/Controllers/Models/Attachment/UpdateController.php index 356e6d8fc0..0983ab0e81 100644 --- a/app/Api/V1/Controllers/Models/Attachment/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Attachment/UpdateController.php @@ -70,7 +70,7 @@ class UpdateController extends Controller public function update(UpdateRequest $request, Attachment $attachment): JsonResponse { if(true === auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); + Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); throw new NotFoundHttpException(); } diff --git a/app/Api/V1/Controllers/Webhook/AttemptController.php b/app/Api/V1/Controllers/Webhook/AttemptController.php index 59c3f352ec..05e293ba79 100644 --- a/app/Api/V1/Controllers/Webhook/AttemptController.php +++ b/app/Api/V1/Controllers/Webhook/AttemptController.php @@ -71,7 +71,7 @@ class AttemptController extends Controller throw new FireflyException('200040: Webhook and webhook message are no match'); } if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info(sprintf('User lists webhook attempts of webhook #%d and message #%d, but webhooks are DISABLED.', $webhook->id, $message->id)); + Log::channel('audit')->warning(sprintf('User lists webhook attempts of webhook #%d and message #%d, but webhooks are DISABLED.', $webhook->id, $message->id)); throw new NotFoundHttpException('Webhooks are not enabled.'); } @@ -115,7 +115,7 @@ class AttemptController extends Controller } if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info(sprintf('User views single webhook attempt #%d of webhook #%d and message #%d, but webhooks are DISABLED', $attempt->id, $webhook->id, $message->id)); + Log::channel('audit')->warning(sprintf('User views single webhook attempt #%d of webhook #%d and message #%d, but webhooks are DISABLED', $attempt->id, $webhook->id, $message->id)); throw new NotFoundHttpException('Webhooks are not enabled.'); } diff --git a/app/Api/V1/Controllers/Webhook/DestroyController.php b/app/Api/V1/Controllers/Webhook/DestroyController.php index 464272eb69..cb5e0d60ee 100644 --- a/app/Api/V1/Controllers/Webhook/DestroyController.php +++ b/app/Api/V1/Controllers/Webhook/DestroyController.php @@ -63,7 +63,7 @@ class DestroyController extends Controller public function destroy(Webhook $webhook): JsonResponse { if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info(sprintf('User tries to destroy webhook #%d. but webhooks are DISABLED.', $webhook->id)); + Log::channel('audit')->warning(sprintf('User tries to destroy webhook #%d. but webhooks are DISABLED.', $webhook->id)); throw new NotFoundHttpException('Webhooks are not enabled.'); } @@ -93,7 +93,7 @@ class DestroyController extends Controller } if (false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info(sprintf('User tries to destroy webhook #%d, message #%d, attempt #%d, but webhooks are DISABLED.', $webhook->id, $message->id, $attempt->id)); + Log::channel('audit')->warning(sprintf('User tries to destroy webhook #%d, message #%d, attempt #%d, but webhooks are DISABLED.', $webhook->id, $message->id, $attempt->id)); throw new NotFoundHttpException('Webhooks are not enabled.'); } @@ -121,7 +121,7 @@ class DestroyController extends Controller } if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info(sprintf('User tries to destroy webhook #%d, message #%d, but webhooks are DISABLED.', $webhook->id, $message->id)); + Log::channel('audit')->warning(sprintf('User tries to destroy webhook #%d, message #%d, but webhooks are DISABLED.', $webhook->id, $message->id)); throw new NotFoundHttpException('Webhooks are not enabled.'); } diff --git a/app/Api/V1/Controllers/Webhook/MessageController.php b/app/Api/V1/Controllers/Webhook/MessageController.php index 36d390cf26..78e5a765b8 100644 --- a/app/Api/V1/Controllers/Webhook/MessageController.php +++ b/app/Api/V1/Controllers/Webhook/MessageController.php @@ -67,7 +67,7 @@ class MessageController extends Controller public function index(Webhook $webhook): JsonResponse { if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info(sprintf('User tries to view messages of webhook #%d, but webhooks are DISABLED.', $webhook->id)); + Log::channel('audit')->warning(sprintf('User tries to view messages of webhook #%d, but webhooks are DISABLED.', $webhook->id)); throw new NotFoundHttpException('Webhooks are not enabled.'); } @@ -107,7 +107,7 @@ class MessageController extends Controller throw new FireflyException('200040: Webhook and webhook message are no match'); } if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info(sprintf('User tries to view message #%d of webhook #%d, but webhooks are DISABLED.', $message->id, $webhook->id)); + Log::channel('audit')->warning(sprintf('User tries to view message #%d of webhook #%d, but webhooks are DISABLED.', $message->id, $webhook->id)); throw new NotFoundHttpException('Webhooks are not enabled.'); } diff --git a/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php b/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php index a08a42e547..100cd1190e 100644 --- a/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php +++ b/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php @@ -28,6 +28,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -72,6 +73,9 @@ class MoveTransactionsRequest extends FormRequest } } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } private function validateMove(Validator $validator): void diff --git a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php index 0ef3692397..eca11ea6ab 100644 --- a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php +++ b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php @@ -30,6 +30,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\Api\Data\Bulk\ValidatesBulkTransactionQuery; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -72,5 +73,8 @@ class TransactionRequest extends FormRequest $this->validateTransactionQuery($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Api/V1/Requests/Data/Export/ExportRequest.php b/app/Api/V1/Requests/Data/Export/ExportRequest.php index 60b6ecab29..3b858c60ff 100644 --- a/app/Api/V1/Requests/Data/Export/ExportRequest.php +++ b/app/Api/V1/Requests/Data/Export/ExportRequest.php @@ -71,7 +71,7 @@ class ExportRequest extends FormRequest { return [ 'type' => 'in:csv', - 'accounts' => 'min:1|max:65536', + 'accounts' => 'min:1|max:32768', 'start' => 'date|before:end', 'end' => 'date|after:start', ]; diff --git a/app/Api/V1/Requests/Models/Account/StoreRequest.php b/app/Api/V1/Requests/Models/Account/StoreRequest.php index e10670f5a0..c78184381d 100644 --- a/app/Api/V1/Requests/Models/Account/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Account/StoreRequest.php @@ -119,7 +119,7 @@ class StoreRequest extends FormRequest 'liability_direction' => 'nullable|required_if:type,liability|required_if:type,liabilities|in:credit,debit', 'interest' => 'min:0|max:100|numeric', 'interest_period' => sprintf('nullable|in:%s', implode(',', config('firefly.interest_periods'))), - 'notes' => 'min:0|max:65536', + 'notes' => 'min:0|max:32768', ]; return Location::requestRules($rules); diff --git a/app/Api/V1/Requests/Models/Account/UpdateRequest.php b/app/Api/V1/Requests/Models/Account/UpdateRequest.php index 998e57b6d9..5ea693c645 100644 --- a/app/Api/V1/Requests/Models/Account/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Account/UpdateRequest.php @@ -107,7 +107,7 @@ class UpdateRequest extends FormRequest 'liability_direction' => 'required_if:type,liability|in:credit,debit', 'interest' => 'required_if:type,liability|min:0|max:100|numeric', 'interest_period' => 'required_if:type,liability|in:daily,monthly,yearly', - 'notes' => 'min:0|max:65536', + 'notes' => 'min:0|max:32768', ]; return Location::requestRules($rules); diff --git a/app/Api/V1/Requests/Models/AvailableBudget/Request.php b/app/Api/V1/Requests/Models/AvailableBudget/Request.php index 5c653194e4..80b0429b8a 100644 --- a/app/Api/V1/Requests/Models/AvailableBudget/Request.php +++ b/app/Api/V1/Requests/Models/AvailableBudget/Request.php @@ -28,6 +28,7 @@ use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -87,5 +88,8 @@ class Request extends FormRequest } } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Api/V1/Requests/Models/Bill/StoreRequest.php b/app/Api/V1/Requests/Models/Bill/StoreRequest.php index 4c518dde55..a8dd1475ae 100644 --- a/app/Api/V1/Requests/Models/Bill/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Bill/StoreRequest.php @@ -29,6 +29,7 @@ use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -103,5 +104,8 @@ class StoreRequest extends FormRequest } } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Api/V1/Requests/Models/Bill/UpdateRequest.php b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php index 99771a6651..e416fac0fb 100644 --- a/app/Api/V1/Requests/Models/Bill/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php @@ -30,6 +30,7 @@ use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -108,5 +109,8 @@ class UpdateRequest extends FormRequest } } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Api/V1/Requests/Models/Budget/StoreRequest.php b/app/Api/V1/Requests/Models/Budget/StoreRequest.php index aae677e4e2..400465f1ff 100644 --- a/app/Api/V1/Requests/Models/Budget/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Budget/StoreRequest.php @@ -29,6 +29,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -91,5 +92,8 @@ class StoreRequest extends FormRequest $this->validateAutoBudgetAmount($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php index 77ef888622..26ab16edb0 100644 --- a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php @@ -30,6 +30,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -103,5 +104,8 @@ class UpdateRequest extends FormRequest $this->validateAutoBudgetAmount($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php index 2795f6fb65..d68879f9ff 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php @@ -28,6 +28,7 @@ use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -87,5 +88,8 @@ class UpdateRequest extends FormRequest } } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php index ebd113c5e1..595257f8c5 100644 --- a/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php @@ -33,6 +33,7 @@ use FireflyIII\Validation\CurrencyValidation; use FireflyIII\Validation\RecurrenceValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -131,6 +132,9 @@ class StoreRequest extends FormRequest $this->validateAccountInformation($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } /** diff --git a/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php index 42bf020df7..2168331b2b 100644 --- a/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php @@ -34,6 +34,7 @@ use FireflyIII\Validation\CurrencyValidation; use FireflyIII\Validation\RecurrenceValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -141,6 +142,9 @@ class UpdateRequest extends FormRequest $this->valUpdateAccountInfo($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } /** diff --git a/app/Api/V1/Requests/Models/Rule/StoreRequest.php b/app/Api/V1/Requests/Models/Rule/StoreRequest.php index a7166c953a..1eda6f6ff9 100644 --- a/app/Api/V1/Requests/Models/Rule/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Rule/StoreRequest.php @@ -28,6 +28,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -108,6 +109,9 @@ class StoreRequest extends FormRequest $this->atLeastOneActiveAction($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } /** diff --git a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php index 933c573c8c..54b5a9a9f2 100644 --- a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php @@ -29,6 +29,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -118,6 +119,9 @@ class UpdateRequest extends FormRequest $this->atLeastOneValidAction($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } /** diff --git a/app/Api/V1/Requests/Models/Tag/StoreRequest.php b/app/Api/V1/Requests/Models/Tag/StoreRequest.php index 3b8085493f..4a17b58cf1 100644 --- a/app/Api/V1/Requests/Models/Tag/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Tag/StoreRequest.php @@ -60,7 +60,7 @@ class StoreRequest extends FormRequest { $rules = [ 'tag' => 'required|min:1|uniqueObjectForUser:tags,tag|max:1024', - 'description' => 'min:1|nullable|max:65536', + 'description' => 'min:1|nullable|max:32768', 'date' => 'date|nullable', ]; diff --git a/app/Api/V1/Requests/Models/Tag/UpdateRequest.php b/app/Api/V1/Requests/Models/Tag/UpdateRequest.php index b0516fc8d7..be069c289e 100644 --- a/app/Api/V1/Requests/Models/Tag/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Tag/UpdateRequest.php @@ -66,7 +66,7 @@ class UpdateRequest extends FormRequest // TODO check if uniqueObjectForUser is obsolete $rules = [ 'tag' => 'min:1|max:1024|uniqueObjectForUser:tags,tag,'.$tag->id, - 'description' => 'min:1|nullable|max:65536', + 'description' => 'min:1|nullable|max:32768', 'date' => 'date|nullable', ]; diff --git a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php index a345c37b37..999701c76c 100644 --- a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php @@ -37,6 +37,7 @@ use FireflyIII\Validation\CurrencyValidation; use FireflyIII\Validation\GroupValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -191,6 +192,9 @@ class StoreRequest extends FormRequest $this->validateGroupDescription($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } /** diff --git a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php index 79653e751d..ab573ae1bd 100644 --- a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php @@ -36,6 +36,7 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\GroupValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -205,6 +206,9 @@ class UpdateRequest extends FormRequest $this->validateAccountInformationUpdate($validator, $transactionGroup); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } /** diff --git a/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php index cf5f3bdfba..1344f8d546 100644 --- a/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php @@ -42,7 +42,7 @@ class UpdateRequest extends FormRequest */ public function getAll(): array { - // return nothing that isn't explicitely in the array: + // return nothing that isn't explicitly in the array: $fields = [ 'name' => ['name', 'convertString'], 'code' => ['code', 'convertString'], diff --git a/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php index 20fcad293e..bedd2bec85 100644 --- a/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php @@ -29,6 +29,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\User; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -77,6 +78,9 @@ class StoreRequest extends FormRequest $this->validateExistingLink($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } private function validateExistingLink(Validator $validator): void diff --git a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php index 8d69b3944f..1590da0cd9 100644 --- a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php @@ -29,6 +29,7 @@ use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -77,6 +78,9 @@ class UpdateRequest extends FormRequest $this->validateUpdate($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } private function validateUpdate(Validator $validator): void diff --git a/app/Api/V1/Requests/System/UserUpdateRequest.php b/app/Api/V1/Requests/System/UserUpdateRequest.php index 0d00b6f464..b6c4673453 100644 --- a/app/Api/V1/Requests/System/UserUpdateRequest.php +++ b/app/Api/V1/Requests/System/UserUpdateRequest.php @@ -29,6 +29,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\User; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -97,5 +98,8 @@ class UserUpdateRequest extends FormRequest } } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Api/V2/Controllers/Model/Transaction/UpdateController.php b/app/Api/V2/Controllers/Model/Transaction/UpdateController.php new file mode 100644 index 0000000000..61129391cf --- /dev/null +++ b/app/Api/V2/Controllers/Model/Transaction/UpdateController.php @@ -0,0 +1,93 @@ +middleware( + function ($request, $next) { + $this->groupRepository = app(TransactionGroupRepositoryInterface::class); + + return $next($request); + } + ); + } + + /** + * This endpoint is documented at: + * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/updateTransaction + * + * Update a transaction. + * + * @throws FireflyException + */ + public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse + { + app('log')->debug('Now in update routine for transaction group [v2]!'); + $data = $request->getAll(); + $transactionGroup = $this->groupRepository->update($transactionGroup, $data); + $applyRules = $data['apply_rules'] ?? true; + $fireWebhooks = $data['fire_webhooks'] ?? true; + + event(new UpdatedTransactionGroup($transactionGroup, $applyRules, $fireWebhooks)); + app('preferences')->mark(); + + /** @var User $admin */ + $admin = auth()->user(); + + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($admin)->setTransactionGroup($transactionGroup); + + $selectedGroup = $collector->getGroups()->first(); + if (null === $selectedGroup) { + throw new FireflyException('200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.'); + } + + $transformer = new TransactionGroupTransformer(); + $transformer->setParameters($this->parameters); + + return response()->api($this->jsonApiObject('transactions', $selectedGroup, $transformer))->header('Content-Type', self::CONTENT_TYPE); + } +} diff --git a/app/Api/V2/Request/Chart/BalanceChartRequest.php b/app/Api/V2/Request/Chart/BalanceChartRequest.php index 90970c768e..2cdcbdb4f0 100644 --- a/app/Api/V2/Request/Chart/BalanceChartRequest.php +++ b/app/Api/V2/Request/Chart/BalanceChartRequest.php @@ -28,6 +28,7 @@ use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -79,5 +80,8 @@ class BalanceChartRequest extends FormRequest } } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Api/V2/Request/Chart/DashboardChartRequest.php b/app/Api/V2/Request/Chart/DashboardChartRequest.php index 8620a3a458..b058b4fb54 100644 --- a/app/Api/V2/Request/Chart/DashboardChartRequest.php +++ b/app/Api/V2/Request/Chart/DashboardChartRequest.php @@ -27,6 +27,7 @@ use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -77,5 +78,8 @@ class DashboardChartRequest extends FormRequest } } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Api/V2/Request/Model/Transaction/StoreRequest.php b/app/Api/V2/Request/Model/Transaction/StoreRequest.php index d5ad470488..feb65800f2 100644 --- a/app/Api/V2/Request/Model/Transaction/StoreRequest.php +++ b/app/Api/V2/Request/Model/Transaction/StoreRequest.php @@ -38,6 +38,7 @@ use FireflyIII\Validation\CurrencyValidation; use FireflyIII\Validation\GroupValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -211,6 +212,9 @@ class StoreRequest extends FormRequest $this->validateGroupDescription($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } /** diff --git a/app/Api/V2/Request/Model/Transaction/UpdateRequest.php b/app/Api/V2/Request/Model/Transaction/UpdateRequest.php new file mode 100644 index 0000000000..40a0b979cf --- /dev/null +++ b/app/Api/V2/Request/Model/Transaction/UpdateRequest.php @@ -0,0 +1,366 @@ +debug(sprintf('Now in %s', __METHOD__)); + $this->integerFields = ['order', 'currency_id', 'foreign_currency_id', 'transaction_journal_id', 'source_id', 'destination_id', 'budget_id', 'category_id', 'bill_id', 'recurrence_id']; + $this->dateFields = ['date', 'interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date']; + $this->textareaFields = ['notes']; + // not really floats, for validation. + $this->floatFields = ['amount', 'foreign_amount']; + $this->stringFields = ['type', 'currency_code', 'foreign_currency_code', 'description', 'source_name', 'source_iban', 'source_number', 'source_bic', 'destination_name', 'destination_iban', 'destination_number', 'destination_bic', 'budget_name', 'category_name', 'bill_name', 'internal_reference', 'external_id', 'bunq_payment_id', 'sepa_cc', 'sepa_ct_op', 'sepa_ct_id', 'sepa_db', 'sepa_country', 'sepa_ep', 'sepa_ci', 'sepa_batch_id', 'external_url']; + $this->booleanFields = ['reconciled']; + $this->arrayFields = ['tags']; + $data = []; + if ($this->has('transactions')) { + $data['transactions'] = $this->getTransactionData(); + } + if ($this->has('apply_rules')) { + $data['apply_rules'] = $this->boolean('apply_rules', true); + } + if ($this->has('fire_webhooks')) { + $data['fire_webhooks'] = $this->boolean('fire_webhooks', true); + } + if ($this->has('group_title')) { + $data['group_title'] = $this->convertString('group_title'); + } + + return $data; + } + + /** + * The rules that the incoming request must be matched against. + */ + public function rules(): array + { + app('log')->debug(sprintf('Now in %s', __METHOD__)); + $validProtocols = config('firefly.valid_url_protocols'); + + return [ + // basic fields for group: + 'group_title' => 'min:1|max:1000|nullable', + 'apply_rules' => [new IsBoolean()], + + // transaction rules (in array for splits): + 'transactions.*.type' => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation', + 'transactions.*.date' => [new IsDateOrTime()], + 'transactions.*.order' => 'numeric|min:0', + + // group id: + 'transactions.*.transaction_journal_id' => ['nullable', 'numeric', new BelongsUser()], + + // currency info + 'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable', + 'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable', + 'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id', + 'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code', + + // amount + 'transactions.*.amount' => ['nullable', new IsValidPositiveAmount()], + 'transactions.*.foreign_amount' => ['nullable', new IsValidZeroOrMoreAmount()], + + // description + 'transactions.*.description' => 'nullable|min:1|max:1000', + + // source of transaction + 'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()], + 'transactions.*.source_name' => 'min:1|max:255|nullable', + + // destination of transaction + 'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()], + 'transactions.*.destination_name' => 'min:1|max:255|nullable', + + // budget, category, bill and piggy + 'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser(), 'nullable'], + 'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()], + 'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser(), 'nullable'], + 'transactions.*.category_name' => 'min:1|max:255|nullable', + 'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()], + 'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()], + + // other interesting fields + 'transactions.*.reconciled' => [new IsBoolean()], + 'transactions.*.notes' => 'min:1|max:32768|nullable', + 'transactions.*.tags' => 'min:0|max:255|nullable', + 'transactions.*.tags.*' => 'min:0|max:255', + + // meta info fields + 'transactions.*.internal_reference' => 'min:1|max:255|nullable', + 'transactions.*.external_id' => 'min:1|max:255|nullable', + 'transactions.*.recurrence_id' => 'min:1|max:255|nullable', + 'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable', + 'transactions.*.external_url' => sprintf('min:1|max:255|nullable|url:%s', $validProtocols), + + // SEPA fields: + 'transactions.*.sepa_cc' => 'min:1|max:255|nullable', + 'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable', + 'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable', + 'transactions.*.sepa_db' => 'min:1|max:255|nullable', + 'transactions.*.sepa_country' => 'min:1|max:255|nullable', + 'transactions.*.sepa_ep' => 'min:1|max:255|nullable', + 'transactions.*.sepa_ci' => 'min:1|max:255|nullable', + 'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable', + + // dates + 'transactions.*.interest_date' => 'date|nullable', + 'transactions.*.book_date' => 'date|nullable', + 'transactions.*.process_date' => 'date|nullable', + 'transactions.*.due_date' => 'date|nullable', + 'transactions.*.payment_date' => 'date|nullable', + 'transactions.*.invoice_date' => 'date|nullable', + ]; + } + + /** + * Configure the validator instance. + */ + public function withValidator(Validator $validator): void + { + app('log')->debug('Now in withValidator'); + + /** @var TransactionGroup $transactionGroup */ + $transactionGroup = $this->route()->parameter('userGroupTransaction'); + $validator->after( + function (Validator $validator) use ($transactionGroup): void { + // if more than one, verify that there are journal ID's present. + $this->validateJournalIds($validator, $transactionGroup); + + // all transaction types must be equal: + $this->validateTransactionTypesForUpdate($validator); + + // user wants to update a reconciled transaction. + // source, destination, amount + foreign_amount cannot be changed + // and must be omitted from the request. + $this->preventUpdateReconciled($validator, $transactionGroup); + + // validate source/destination is equal, depending on the transaction journal type. + $this->validateEqualAccountsForUpdate($validator, $transactionGroup); + + // see method: + // $this->preventNoAccountInfo($validator, ); + + // validate that the currency fits the source and/or destination account. + // validate all account info + $this->validateAccountInformationUpdate($validator, $transactionGroup); + } + ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } + + /** + * Get transaction data. + * + * @throws FireflyException + */ + private function getTransactionData(): array + { + app('log')->debug(sprintf('Now in %s', __METHOD__)); + $return = []; + + /** @var null|array $transactions */ + $transactions = $this->get('transactions'); + + if (!is_countable($transactions)) { + return $return; + } + + /** @var null|array $transaction */ + foreach ($transactions as $transaction) { + if (!is_array($transaction)) { + throw new FireflyException('Invalid data submitted: transaction is not array.'); + } + // default response is to update nothing in the transaction: + $current = []; + $current = $this->getIntegerData($current, $transaction); + $current = $this->getStringData($current, $transaction); + $current = $this->getNlStringData($current, $transaction); + $current = $this->getDateData($current, $transaction); + $current = $this->getBooleanData($current, $transaction); + $current = $this->getArrayData($current, $transaction); + $current = $this->getFloatData($current, $transaction); + $return[] = $current; + } + + return $return; + } + + /** + * For each field, add it to the array if a reference is present in the request: + * + * @param array $current + * @param array $transaction + */ + private function getIntegerData(array $current, array $transaction): array + { + foreach ($this->integerFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $current[$fieldName] = $this->integerFromValue((string) $transaction[$fieldName]); + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getStringData(array $current, array $transaction): array + { + foreach ($this->stringFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $current[$fieldName] = $this->clearString((string) $transaction[$fieldName]); + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getNlStringData(array $current, array $transaction): array + { + foreach ($this->textareaFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $current[$fieldName] = $this->clearStringKeepNewlines((string) $transaction[$fieldName]); // keep newlines + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getDateData(array $current, array $transaction): array + { + foreach ($this->dateFields as $fieldName) { + app('log')->debug(sprintf('Now at date field %s', $fieldName)); + if (array_key_exists($fieldName, $transaction)) { + app('log')->debug(sprintf('New value: "%s"', (string) $transaction[$fieldName])); + $current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]); + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getBooleanData(array $current, array $transaction): array + { + foreach ($this->booleanFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $current[$fieldName] = $this->convertBoolean((string) $transaction[$fieldName]); + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getArrayData(array $current, array $transaction): array + { + foreach ($this->arrayFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]); + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getFloatData(array $current, array $transaction): array + { + foreach ($this->floatFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $value = $transaction[$fieldName]; + if (is_float($value)) { + $current[$fieldName] = sprintf('%.12f', $value); + } + if (!is_float($value)) { + $current[$fieldName] = (string) $value; + } + } + } + + return $current; + } +} diff --git a/app/Console/Commands/Correction/EnableCurrencies.php b/app/Console/Commands/Correction/EnableCurrencies.php index 3dbea108e6..a87843d0f6 100644 --- a/app/Console/Commands/Correction/EnableCurrencies.php +++ b/app/Console/Commands/Correction/EnableCurrencies.php @@ -71,7 +71,6 @@ class EnableCurrencies extends Command $found = [$defaultCurrency->id]; // get all meta entries - /** @var Collection $meta */ $meta = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id') ->where('accounts.user_group_id', $userGroup->id) ->where('account_meta.name', 'currency_id')->groupBy('data')->get(['data']) @@ -108,6 +107,12 @@ class EnableCurrencies extends Command $found[] = $entry->transaction_currency_id; } + // also get all currencies already enabled. + $alreadyEnabled = $userGroup->currencies()->get(['transaction_currencies.id'])->pluck('id')->toArray(); + foreach ($alreadyEnabled as $currencyId) { + $found[] = $currencyId; + } + $found = array_values(array_unique($found)); $found = array_values( array_filter( diff --git a/app/Helpers/Collector/Extensions/MetaCollection.php b/app/Helpers/Collector/Extensions/MetaCollection.php index 5f79b8d0b5..b86a697153 100644 --- a/app/Helpers/Collector/Extensions/MetaCollection.php +++ b/app/Helpers/Collector/Extensions/MetaCollection.php @@ -182,7 +182,7 @@ trait MetaCollection public function excludeInternalReference(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -203,7 +203,7 @@ trait MetaCollection public function externalIdContains(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -215,7 +215,7 @@ trait MetaCollection public function externalIdDoesNotContain(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -227,7 +227,7 @@ trait MetaCollection public function externalIdDoesNotEnd(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -239,7 +239,7 @@ trait MetaCollection public function externalIdDoesNotStart(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -251,7 +251,7 @@ trait MetaCollection public function externalIdEnds(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -263,7 +263,7 @@ trait MetaCollection public function externalIdStarts(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -276,7 +276,7 @@ trait MetaCollection public function externalUrlContains(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', trim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $url)); @@ -287,7 +287,7 @@ trait MetaCollection public function externalUrlDoesNotContain(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', trim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $url)); @@ -298,7 +298,7 @@ trait MetaCollection public function externalUrlDoesNotEnd(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', ltrim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s', $url)); @@ -309,7 +309,7 @@ trait MetaCollection public function externalUrlDoesNotStart(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', rtrim($url, '"')); // var_dump($url); @@ -322,7 +322,7 @@ trait MetaCollection public function externalUrlEnds(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', ltrim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s', $url)); @@ -333,7 +333,7 @@ trait MetaCollection public function externalUrlStarts(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', rtrim($url, '"')); // var_dump($url); @@ -371,7 +371,7 @@ trait MetaCollection public function internalReferenceContains(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); // var_dump($internalReference); // exit; @@ -385,7 +385,7 @@ trait MetaCollection public function internalReferenceDoesNotContain(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -397,7 +397,7 @@ trait MetaCollection public function internalReferenceDoesNotEnd(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -409,7 +409,7 @@ trait MetaCollection public function internalReferenceDoesNotStart(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -421,7 +421,7 @@ trait MetaCollection public function internalReferenceEnds(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -433,7 +433,7 @@ trait MetaCollection public function internalReferenceStarts(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -629,7 +629,7 @@ trait MetaCollection public function setInternalReference(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -682,6 +682,7 @@ trait MetaCollection $list = $tags->pluck('tag')->toArray(); $list = array_map('strtolower', $list); $filter = static function (array $object) use ($list): bool|array { + $includedJournals = []; $return = $object; unset($return['transactions']); $return['transactions'] = []; @@ -701,7 +702,12 @@ trait MetaCollection if (in_array(strtolower($tag['name']), $list, true)) { app('log')->debug(sprintf('Transaction has tag "%s" so count++.', $tag['name'])); ++$foundTagCount; - $return['transactions'][] = $transaction; + $journalId = $transaction['transaction_journal_id']; + // #8377 prevent adding a transaction twice when multiple tag searches find this transaction + if (!in_array($journalId, $includedJournals, true)) { + $includedJournals[] = $journalId; + $return['transactions'][] = $transaction; + } } } } diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index 4b5201e6bb..ddc5972853 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -286,7 +286,7 @@ class GroupCollector implements GroupCollectorInterface foreach ($params as $param) { $replace = sprintf('"%s"', $param); if (is_int($param)) { - $replace = (string) $param; + $replace = (string)$param; } $pos = strpos($query, '?'); if (false !== $pos) { @@ -692,13 +692,13 @@ class GroupCollector implements GroupCollectorInterface /** @var TransactionJournal $augumentedJournal */ foreach ($collection as $augumentedJournal) { - $groupId = (int) $augumentedJournal->transaction_group_id; + $groupId = (int)$augumentedJournal->transaction_group_id; if (!array_key_exists($groupId, $groups)) { // make new array $parsedGroup = $this->parseAugmentedJournal($augumentedJournal); $groupArray = [ - 'id' => (int) $augumentedJournal->transaction_group_id, + 'id' => (int)$augumentedJournal->transaction_group_id, 'user_id' => $augumentedJournal->user_id, 'user_group_id' => $augumentedJournal->user_group_id, // Field transaction_group_title was added by the query. @@ -711,7 +711,7 @@ class GroupCollector implements GroupCollectorInterface 'transactions' => [], ]; // Field transaction_journal_id was added by the query. - $journalId = (int) $augumentedJournal->transaction_journal_id; // @phpstan-ignore-line + $journalId = (int)$augumentedJournal->transaction_journal_id; // @phpstan-ignore-line $groupArray['transactions'][$journalId] = $parsedGroup; $groups[$groupId] = $groupArray; @@ -719,7 +719,7 @@ class GroupCollector implements GroupCollectorInterface } // or parse the rest. // Field transaction_journal_id was added by the query. - $journalId = (int) $augumentedJournal->transaction_journal_id; // @phpstan-ignore-line + $journalId = (int)$augumentedJournal->transaction_journal_id; // @phpstan-ignore-line if (array_key_exists($journalId, $groups[$groupId]['transactions'])) { // append data to existing group + journal (for multiple tags or multiple attachments) $groups[$groupId]['transactions'][$journalId] = $this->mergeTags($groups[$groupId]['transactions'][$journalId], $augumentedJournal); @@ -772,7 +772,7 @@ class GroupCollector implements GroupCollectorInterface $dates = ['interest_date', 'payment_date', 'invoice_date', 'book_date', 'due_date', 'process_date']; if (array_key_exists('meta_name', $result) && in_array($result['meta_name'], $dates, true)) { $name = $result['meta_name']; - if (array_key_exists('meta_data', $result) && '' !== (string) $result['meta_data']) { + if (array_key_exists('meta_data', $result) && '' !== (string)$result['meta_data']) { $result[$name] = Carbon::createFromFormat('!Y-m-d', substr(json_decode($result['meta_data']), 0, 10)); } } @@ -783,9 +783,9 @@ class GroupCollector implements GroupCollectorInterface // convert back to strings because SQLite is dumb like that. $result = $this->convertToStrings($result); - $result['reconciled'] = 1 === (int) $result['reconciled']; + $result['reconciled'] = 1 === (int)$result['reconciled']; if (array_key_exists('tag_id', $result) && null !== $result['tag_id']) { // assume the other fields are present as well. - $tagId = (int) $augumentedJournal['tag_id']; + $tagId = (int)$augumentedJournal['tag_id']; $tagDate = null; try { @@ -795,7 +795,7 @@ class GroupCollector implements GroupCollectorInterface } $result['tags'][$tagId] = [ - 'id' => (int) $result['tag_id'], + 'id' => (int)$result['tag_id'], 'name' => $result['tag_name'], 'date' => $tagDate, 'description' => $result['tag_description'], @@ -804,8 +804,8 @@ class GroupCollector implements GroupCollectorInterface // also merge attachments: if (array_key_exists('attachment_id', $result)) { - $uploaded = 1 === (int) $result['attachment_uploaded']; - $attachmentId = (int) $augumentedJournal['attachment_id']; + $uploaded = 1 === (int)$result['attachment_uploaded']; + $attachmentId = (int)$augumentedJournal['attachment_id']; if (0 !== $attachmentId && $uploaded) { $result['attachments'][$attachmentId] = [ 'id' => $attachmentId, @@ -831,7 +831,7 @@ class GroupCollector implements GroupCollectorInterface private function convertToInteger(array $array): array { foreach ($this->integerFields as $field) { - $array[$field] = array_key_exists($field, $array) ? (int) $array[$field] : null; + $array[$field] = array_key_exists($field, $array) ? (int)$array[$field] : null; } return $array; @@ -840,7 +840,7 @@ class GroupCollector implements GroupCollectorInterface private function convertToStrings(array $array): array { foreach ($this->stringFields as $field) { - $array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (string) $array[$field] : null; + $array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (string)$array[$field] : null; } return $array; @@ -850,7 +850,7 @@ class GroupCollector implements GroupCollectorInterface { $newArray = $newJournal->toArray(); if (array_key_exists('tag_id', $newArray)) { // assume the other fields are present as well. - $tagId = (int) $newJournal['tag_id']; + $tagId = (int)$newJournal['tag_id']; $tagDate = null; @@ -861,7 +861,7 @@ class GroupCollector implements GroupCollectorInterface } $existingJournal['tags'][$tagId] = [ - 'id' => (int) $newArray['tag_id'], + 'id' => (int)$newArray['tag_id'], 'name' => $newArray['tag_name'], 'date' => $tagDate, 'description' => $newArray['tag_description'], @@ -875,7 +875,7 @@ class GroupCollector implements GroupCollectorInterface { $newArray = $newJournal->toArray(); if (array_key_exists('attachment_id', $newArray)) { - $attachmentId = (int) $newJournal['attachment_id']; + $attachmentId = (int)$newJournal['attachment_id']; $existingJournal['attachments'][$attachmentId] = [ 'id' => $attachmentId, @@ -894,7 +894,7 @@ class GroupCollector implements GroupCollectorInterface foreach ($groups as $groudId => $group) { /** @var array $transaction */ foreach ($group['transactions'] as $transaction) { - $currencyId = (int) $transaction['currency_id']; + $currencyId = (int)$transaction['currency_id']; if (null === $transaction['amount']) { throw new FireflyException(sprintf('Amount is NULL for a transaction in group #%d, please investigate.', $groudId)); } @@ -910,7 +910,7 @@ class GroupCollector implements GroupCollectorInterface $groups[$groudId]['sums'][$currencyId]['amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']); if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) { - $currencyId = (int) $transaction['foreign_currency_id']; + $currencyId = (int)$transaction['foreign_currency_id']; // set default: if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) { @@ -931,7 +931,6 @@ class GroupCollector implements GroupCollectorInterface private function postFilterCollection(Collection $collection): Collection { $currentCollection = $collection; - app('log')->debug(sprintf('GroupCollector: postFilterCollection has %d filter(s) and %d transaction(s).', count($this->postFilters), count($currentCollection))); /** diff --git a/app/Http/Controllers/Account/CreateController.php b/app/Http/Controllers/Account/CreateController.php index 4c724ee3bb..b1ebda2f53 100644 --- a/app/Http/Controllers/Account/CreateController.php +++ b/app/Http/Controllers/Account/CreateController.php @@ -161,7 +161,7 @@ class CreateController extends Controller $this->attachments->saveAttachmentsForModel($account, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string) trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Account/EditController.php b/app/Http/Controllers/Account/EditController.php index 2ee78464a3..b5bec2472a 100644 --- a/app/Http/Controllers/Account/EditController.php +++ b/app/Http/Controllers/Account/EditController.php @@ -187,7 +187,7 @@ class EditController extends Controller $this->attachments->saveAttachmentsForModel($account, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string) trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 5c68275371..ad69c15b56 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -78,7 +78,7 @@ class LoginController extends Controller public function login(Request $request): JsonResponse|RedirectResponse { Log::channel('audit')->info(sprintf('User is trying to login using "%s"', $request->get($this->username()))); - app('log')->info('User is trying to login.'); + app('log')->debug('User is trying to login.'); $this->validateLogin($request); app('log')->debug('Login data is present.'); @@ -88,7 +88,7 @@ class LoginController extends Controller // the login attempts for this application. We'll key this by the username and // the IP address of the client making these requests into this application. if ($this->hasTooManyLoginAttempts($request)) { - Log::channel('audit')->info(sprintf('Login for user "%s" was locked out.', $request->get($this->username()))); + Log::channel('audit')->warning(sprintf('Login for user "%s" was locked out.', $request->get($this->username()))); app('log')->error(sprintf('Login for user "%s" was locked out.', $request->get($this->username()))); $this->fireLockoutEvent($request); @@ -114,7 +114,7 @@ class LoginController extends Controller // to login and redirect the user back to the login form. Of course, when this // user surpasses their maximum number of attempts they will get locked out. $this->incrementLoginAttempts($request); - Log::channel('audit')->info(sprintf('Login failed. Attempt for user "%s" failed.', $request->get($this->username()))); + Log::channel('audit')->warning(sprintf('Login failed. Attempt for user "%s" failed.', $request->get($this->username()))); $this->sendFailedLoginResponse($request); diff --git a/app/Http/Controllers/Bill/CreateController.php b/app/Http/Controllers/Bill/CreateController.php index 443d3eb444..f55075856d 100644 --- a/app/Http/Controllers/Bill/CreateController.php +++ b/app/Http/Controllers/Bill/CreateController.php @@ -116,7 +116,7 @@ class CreateController extends Controller $this->attachments->saveAttachmentsForModel($bill, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Bill/EditController.php b/app/Http/Controllers/Bill/EditController.php index 821d54fde9..abb062e606 100644 --- a/app/Http/Controllers/Bill/EditController.php +++ b/app/Http/Controllers/Bill/EditController.php @@ -128,7 +128,7 @@ class EditController extends Controller $this->attachments->saveAttachmentsForModel($bill, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string) trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Budget/BudgetLimitController.php b/app/Http/Controllers/Budget/BudgetLimitController.php index 7eade3fe56..a293e25fae 100644 --- a/app/Http/Controllers/Budget/BudgetLimitController.php +++ b/app/Http/Controllers/Budget/BudgetLimitController.php @@ -202,7 +202,9 @@ class BudgetLimitController extends Controller if ('' === $amount) { $amount = '0'; } - + if ((int)$amount > 268435456) { // 268 million, intentional integer + $amount = '268435456'; + } // sanity check on amount: if (0 === bccomp($amount, '0')) { $budgetId = $budgetLimit->budget_id; @@ -217,9 +219,7 @@ class BudgetLimitController extends Controller return response()->json($array); } - if ((int)$amount > 268435456) { // 268 million, intentional integer - $amount = '268435456'; - } + if (-1 === bccomp($amount, '0')) { $amount = bcmul($amount, '-1'); } diff --git a/app/Http/Controllers/Budget/CreateController.php b/app/Http/Controllers/Budget/CreateController.php index 68b3847b62..135305d1bf 100644 --- a/app/Http/Controllers/Budget/CreateController.php +++ b/app/Http/Controllers/Budget/CreateController.php @@ -127,7 +127,7 @@ class CreateController extends Controller $this->attachments->saveAttachmentsForModel($budget, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Budget/EditController.php b/app/Http/Controllers/Budget/EditController.php index ec0fe37d26..39d9e18120 100644 --- a/app/Http/Controllers/Budget/EditController.php +++ b/app/Http/Controllers/Budget/EditController.php @@ -137,7 +137,7 @@ class EditController extends Controller $this->attachments->saveAttachmentsForModel($budget, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Category/CreateController.php b/app/Http/Controllers/Category/CreateController.php index e7115c063b..95ef28ab02 100644 --- a/app/Http/Controllers/Category/CreateController.php +++ b/app/Http/Controllers/Category/CreateController.php @@ -100,7 +100,7 @@ class CreateController extends Controller $this->attachments->saveAttachmentsForModel($category, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Category/EditController.php b/app/Http/Controllers/Category/EditController.php index 265ee53095..734d93ddfb 100644 --- a/app/Http/Controllers/Category/EditController.php +++ b/app/Http/Controllers/Category/EditController.php @@ -104,7 +104,7 @@ class EditController extends Controller $this->attachments->saveAttachmentsForModel($category, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 6ab91d266b..8ad7bde9d7 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -108,7 +108,7 @@ class ReportController extends Controller continue; } $currencyId = $netWorthItem['currency_id']; - $label = $current->isoFormat((string) trans('config.month_and_day_js', [], $locale)); + $label = $current->isoFormat((string)trans('config.month_and_day_js', [], $locale)); if (!array_key_exists($currencyId, $chartData)) { $chartData[$currencyId] = [ 'label' => 'Net worth in '.$netWorthItem['currency_name'], @@ -145,6 +145,7 @@ class ReportController extends Controller if ($cache->has()) { return response()->json($cache->get()); } + app('log')->debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray()); $format = app('navigation')->preferredCarbonFormat($start, $end); $titleFormat = app('navigation')->preferredCarbonLocalizedFormat($start, $end); @@ -164,13 +165,13 @@ class ReportController extends Controller /** @var array $journal */ foreach ($journals as $journal) { $period = $journal['date']->format($format); - $currencyId = (int) $journal['currency_id']; + $currencyId = (int)$journal['currency_id']; $data[$currencyId] ??= [ 'currency_id' => $currencyId, 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], 'currency_name' => $journal['currency_name'], - 'currency_decimal_places' => (int) $journal['currency_decimal_places'], + 'currency_decimal_places' => (int)$journal['currency_decimal_places'], ]; $data[$currencyId][$period] ??= [ 'period' => $period, @@ -193,7 +194,7 @@ class ReportController extends Controller /** @var array $currency */ foreach ($data as $currency) { $income = [ - 'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]), + 'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]), 'type' => 'bar', 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green 'currency_id' => $currency['currency_id'], @@ -202,7 +203,7 @@ class ReportController extends Controller 'entries' => [], ]; $expense = [ - 'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), + 'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), 'type' => 'bar', 'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red 'currency_id' => $currency['currency_id'], @@ -212,7 +213,13 @@ class ReportController extends Controller ]; // loop all possible periods between $start and $end $currentStart = clone $start; - while ($currentStart <= $end) { + $currentEnd = clone $end; + + // #8374. Sloppy fix for yearly charts. Not really interested in a better fix with v2 layout and all. + if ('1Y' === $preferredRange) { + $currentEnd = app('navigation')->endOfPeriod($currentEnd, $preferredRange); + } + while ($currentStart <= $currentEnd) { $key = $currentStart->format($format); $title = $currentStart->isoFormat($titleFormat); $income['entries'][$title] = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); diff --git a/app/Http/Controllers/PiggyBank/CreateController.php b/app/Http/Controllers/PiggyBank/CreateController.php index 55d1a8acfe..92aac833d9 100644 --- a/app/Http/Controllers/PiggyBank/CreateController.php +++ b/app/Http/Controllers/PiggyBank/CreateController.php @@ -107,7 +107,7 @@ class CreateController extends Controller $this->attachments->saveAttachmentsForModel($piggyBank, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/PiggyBank/EditController.php b/app/Http/Controllers/PiggyBank/EditController.php index 61e729d64c..c0a2de083e 100644 --- a/app/Http/Controllers/PiggyBank/EditController.php +++ b/app/Http/Controllers/PiggyBank/EditController.php @@ -127,7 +127,7 @@ class EditController extends Controller $this->attachments->saveAttachmentsForModel($piggyBank, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Recurring/CreateController.php b/app/Http/Controllers/Recurring/CreateController.php index dec86505dd..af0aa56cc1 100644 --- a/app/Http/Controllers/Recurring/CreateController.php +++ b/app/Http/Controllers/Recurring/CreateController.php @@ -243,7 +243,7 @@ class CreateController extends Controller $this->attachments->saveAttachmentsForModel($recurrence, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Recurring/EditController.php b/app/Http/Controllers/Recurring/EditController.php index 2b67ed8443..7cf6578904 100644 --- a/app/Http/Controllers/Recurring/EditController.php +++ b/app/Http/Controllers/Recurring/EditController.php @@ -181,7 +181,7 @@ class EditController extends Controller $this->attachments->saveAttachmentsForModel($recurrence, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 0b6a39b884..964282de65 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -305,7 +305,7 @@ class TagController extends Controller $this->attachmentsHelper->saveAttachmentsForModel($result, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } @@ -340,7 +340,7 @@ class TagController extends Controller $this->attachmentsHelper->saveAttachmentsForModel($tag, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); + Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); session()->flash('info', (string)trans('firefly.no_att_demo_user')); } diff --git a/app/Http/Controllers/Transaction/EditController.php b/app/Http/Controllers/Transaction/EditController.php index 1945f8ea7d..91dff1c221 100644 --- a/app/Http/Controllers/Transaction/EditController.php +++ b/app/Http/Controllers/Transaction/EditController.php @@ -106,7 +106,7 @@ class EditController extends Controller $optionalFields['location'] ??= false; $optionalFields['location'] = $optionalFields['location'] && true === config('firefly.enable_external_map'); - // map info: + // map info voor v2: $longitude = config('firefly.default_location.longitude'); $latitude = config('firefly.default_location.latitude'); $zoomLevel = config('firefly.default_location.zoom_level'); diff --git a/app/Http/Controllers/Transaction/IndexController.php b/app/Http/Controllers/Transaction/IndexController.php index f51ef06aa4..8013113c6d 100644 --- a/app/Http/Controllers/Transaction/IndexController.php +++ b/app/Http/Controllers/Transaction/IndexController.php @@ -75,45 +75,52 @@ class IndexController extends Controller $objectType = 'transfer'; } - $subTitleIcon = config('firefly.transactionIconsByType.'.$objectType); - $types = config('firefly.transactionTypesByType.'.$objectType); - $page = (int)$request->get('page'); - $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; - if (null === $start) { - $start = session('start'); - $end = session('end'); + // add a split for the (future) v2 release. + $periods = []; + $groups = []; + $subTitle = 'TODO page subtitle in v2'; + + $subTitleIcon = config('firefly.transactionIconsByType.'.$objectType); + $types = config('firefly.transactionTypesByType.'.$objectType); + $page = (int)$request->get('page'); + $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; + + if ('v2' !== (string)config('firefly.layout')) { + if (null === $start) { + $start = session('start'); + $end = session('end'); + } + if (null === $end) { + // get last transaction ever? + $last = $this->repository->getLast(); + $end = null !== $last ? $last->date : session('end'); + } + + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + $startStr = $start->isoFormat($this->monthAndDayFormat); + $endStr = $end->isoFormat($this->monthAndDayFormat); + $subTitle = (string)trans(sprintf('firefly.title_%s_between', $objectType), ['start' => $startStr, 'end' => $endStr]); + $path = route('transactions.index', [$objectType, $start->format('Y-m-d'), $end->format('Y-m-d')]); + $firstJournal = $this->repository->firstNull(); + $startPeriod = null === $firstJournal ? new Carbon() : $firstJournal->date; + $endPeriod = clone $end; + $periods = $this->getTransactionPeriodOverview($objectType, $startPeriod, $endPeriod); + + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + + $collector->setRange($start, $end) + ->setTypes($types) + ->setLimit($pageSize) + ->setPage($page) + ->withBudgetInformation() + ->withCategoryInformation() + ->withAccountInformation() + ->withAttachmentInformation() + ; + $groups = $collector->getPaginatedGroups(); + $groups->setPath($path); } - if (null === $end) { - // get last transaction ever? - $last = $this->repository->getLast(); - $end = null !== $last ? $last->date : session('end'); - } - - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $path = route('transactions.index', [$objectType, $start->format('Y-m-d'), $end->format('Y-m-d')]); - $startStr = $start->isoFormat($this->monthAndDayFormat); - $endStr = $end->isoFormat($this->monthAndDayFormat); - $subTitle = (string)trans(sprintf('firefly.title_%s_between', $objectType), ['start' => $startStr, 'end' => $endStr]); - - $firstJournal = $this->repository->firstNull(); - $startPeriod = null === $firstJournal ? new Carbon() : $firstJournal->date; - $endPeriod = clone $end; - $periods = $this->getTransactionPeriodOverview($objectType, $startPeriod, $endPeriod); - - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - - $collector->setRange($start, $end) - ->setTypes($types) - ->setLimit($pageSize) - ->setPage($page) - ->withBudgetInformation() - ->withCategoryInformation() - ->withAccountInformation() - ->withAttachmentInformation() - ; - $groups = $collector->getPaginatedGroups(); - $groups->setPath($path); return view('transactions.index', compact('subTitle', 'objectType', 'subTitleIcon', 'groups', 'periods', 'start', 'end')); } diff --git a/app/Http/Controllers/TransactionCurrency/CreateController.php b/app/Http/Controllers/TransactionCurrency/CreateController.php index ca96f20a5d..75bb2f9cf3 100644 --- a/app/Http/Controllers/TransactionCurrency/CreateController.php +++ b/app/Http/Controllers/TransactionCurrency/CreateController.php @@ -105,7 +105,7 @@ class CreateController extends Controller $data = $request->getCurrencyData(); if (!$this->userRepository->hasRole($user, 'owner')) { app('log')->error('User '.auth()->user()->id.' is not admin, but tried to store a currency.'); - Log::channel('audit')->info('Tried to create (POST) currency without admin rights.', $data); + Log::channel('audit')->warning('Tried to create (POST) currency without admin rights.', $data); return redirect($this->getPreviousUrl('currencies.create.url'))->withInput(); } @@ -116,7 +116,7 @@ class CreateController extends Controller $currency = $this->repository->store($data); } catch (FireflyException $e) { app('log')->error($e->getMessage()); - Log::channel('audit')->info('Could not store (POST) currency without admin rights.', $data); + Log::channel('audit')->warning('Could not store (POST) currency without admin rights.', $data); $request->session()->flash('error', (string)trans('firefly.could_not_store_currency')); $currency = null; } diff --git a/app/Http/Controllers/TransactionCurrency/DeleteController.php b/app/Http/Controllers/TransactionCurrency/DeleteController.php index ccad32a82e..5ddecbff90 100644 --- a/app/Http/Controllers/TransactionCurrency/DeleteController.php +++ b/app/Http/Controllers/TransactionCurrency/DeleteController.php @@ -74,7 +74,7 @@ class DeleteController extends Controller $user = auth()->user(); if (!$this->userRepository->hasRole($user, 'owner')) { $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); - Log::channel('audit')->info(sprintf('Tried to visit page to delete currency %s but is not site owner.', $currency->code)); + Log::channel('audit')->warning(sprintf('Tried to visit page to delete currency %s but is not site owner.', $currency->code)); return redirect(route('currencies.index')); } @@ -83,7 +83,7 @@ class DeleteController extends Controller $location = $this->repository->currencyInUseAt($currency); $message = (string)trans(sprintf('firefly.cannot_disable_currency_%s', $location), ['name' => e($currency->name)]); $request->session()->flash('error', $message); - Log::channel('audit')->info(sprintf('Tried to visit page to delete currency %s but currency is in use.', $currency->code)); + Log::channel('audit')->warning(sprintf('Tried to visit page to delete currency %s but currency is in use.', $currency->code)); return redirect(route('currencies.index')); } @@ -107,7 +107,7 @@ class DeleteController extends Controller $user = auth()->user(); if (!$this->userRepository->hasRole($user, 'owner')) { $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); - Log::channel('audit')->info(sprintf('Tried to delete currency %s but is not site owner.', $currency->code)); + Log::channel('audit')->warning(sprintf('Tried to delete currency %s but is not site owner.', $currency->code)); return redirect(route('currencies.index')); } diff --git a/app/Http/Controllers/TransactionCurrency/EditController.php b/app/Http/Controllers/TransactionCurrency/EditController.php index 45e3bde766..75ceb9f539 100644 --- a/app/Http/Controllers/TransactionCurrency/EditController.php +++ b/app/Http/Controllers/TransactionCurrency/EditController.php @@ -72,7 +72,7 @@ class EditController extends Controller $user = auth()->user(); if (!$this->userRepository->hasRole($user, 'owner')) { $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); - Log::channel('audit')->info(sprintf('Tried to edit currency %s but is not owner.', $currency->code)); + Log::channel('audit')->warning(sprintf('Tried to edit currency %s but is not owner.', $currency->code)); return redirect(route('currencies.index')); } @@ -120,7 +120,7 @@ class EditController extends Controller if (!$this->userRepository->hasRole($user, 'owner')) { $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); - Log::channel('audit')->info('Tried to update (POST) currency without admin rights.', $data); + Log::channel('audit')->warning('Tried to update (POST) currency without admin rights.', $data); return redirect(route('currencies.index')); } diff --git a/app/Http/Controllers/Webhooks/CreateController.php b/app/Http/Controllers/Webhooks/CreateController.php index f1922eb220..f1ae0ffe45 100644 --- a/app/Http/Controllers/Webhooks/CreateController.php +++ b/app/Http/Controllers/Webhooks/CreateController.php @@ -60,7 +60,7 @@ class CreateController extends Controller public function index() { if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info('User visits webhook create page, but webhooks are DISABLED.'); + Log::channel('audit')->warning('User visits webhook create page, but webhooks are DISABLED.'); throw new NotFoundHttpException('Webhooks are not enabled.'); } diff --git a/app/Http/Controllers/Webhooks/DeleteController.php b/app/Http/Controllers/Webhooks/DeleteController.php index 9de635f6f5..d701a353e4 100644 --- a/app/Http/Controllers/Webhooks/DeleteController.php +++ b/app/Http/Controllers/Webhooks/DeleteController.php @@ -64,7 +64,7 @@ class DeleteController extends Controller public function index(Webhook $webhook) { if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info('User visits webhook delete page, but webhooks are DISABLED.'); + Log::channel('audit')->warning('User visits webhook delete page, but webhooks are DISABLED.'); throw new NotFoundHttpException('Webhooks are not enabled.'); } diff --git a/app/Http/Controllers/Webhooks/EditController.php b/app/Http/Controllers/Webhooks/EditController.php index 3173c44327..667738ab64 100644 --- a/app/Http/Controllers/Webhooks/EditController.php +++ b/app/Http/Controllers/Webhooks/EditController.php @@ -63,7 +63,7 @@ class EditController extends Controller public function index(Webhook $webhook) { if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info('User visits webhook edit page, but webhooks are DISABLED.'); + Log::channel('audit')->warning('User visits webhook edit page, but webhooks are DISABLED.'); throw new NotFoundHttpException('Webhooks are not enabled.'); } diff --git a/app/Http/Controllers/Webhooks/IndexController.php b/app/Http/Controllers/Webhooks/IndexController.php index 3ef74dff77..077cd10925 100644 --- a/app/Http/Controllers/Webhooks/IndexController.php +++ b/app/Http/Controllers/Webhooks/IndexController.php @@ -56,7 +56,7 @@ class IndexController extends Controller public function index() { if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info('User visits webhook index page, but webhooks are DISABLED.'); + Log::channel('audit')->warning('User visits webhook index page, but webhooks are DISABLED.'); throw new NotFoundHttpException('Webhooks are not enabled.'); } diff --git a/app/Http/Controllers/Webhooks/ShowController.php b/app/Http/Controllers/Webhooks/ShowController.php index db7f6d25fa..2cc4de962f 100644 --- a/app/Http/Controllers/Webhooks/ShowController.php +++ b/app/Http/Controllers/Webhooks/ShowController.php @@ -63,7 +63,7 @@ class ShowController extends Controller public function index(Webhook $webhook) { if(false === config('firefly.allow_webhooks')) { - Log::channel('audit')->info(sprintf('User visits webhook #%d page, but webhooks are DISABLED.', $webhook->id)); + Log::channel('audit')->warning(sprintf('User visits webhook #%d page, but webhooks are DISABLED.', $webhook->id)); throw new NotFoundHttpException('Webhooks are not enabled.'); } diff --git a/app/Http/Requests/AccountFormRequest.php b/app/Http/Requests/AccountFormRequest.php index 9369d9890e..c2937e84f5 100644 --- a/app/Http/Requests/AccountFormRequest.php +++ b/app/Http/Requests/AccountFormRequest.php @@ -32,6 +32,8 @@ use FireflyIII\Support\Request\AppendsLocationData; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class AccountFormRequest. @@ -131,4 +133,11 @@ class AccountFormRequest extends FormRequest return $rules; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/AttachmentFormRequest.php b/app/Http/Requests/AttachmentFormRequest.php index 737dca20fc..bd8351952a 100644 --- a/app/Http/Requests/AttachmentFormRequest.php +++ b/app/Http/Requests/AttachmentFormRequest.php @@ -26,6 +26,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class AttachmentFormRequest. @@ -57,4 +59,11 @@ class AttachmentFormRequest extends FormRequest 'notes' => 'min:1|max:32768|nullable', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/BillStoreRequest.php b/app/Http/Requests/BillStoreRequest.php index 0de8e046bd..18c5e167c0 100644 --- a/app/Http/Requests/BillStoreRequest.php +++ b/app/Http/Requests/BillStoreRequest.php @@ -27,6 +27,8 @@ use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class BillStoreRequest. @@ -77,4 +79,11 @@ class BillStoreRequest extends FormRequest 'active' => 'boolean', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/BillUpdateRequest.php b/app/Http/Requests/BillUpdateRequest.php index be34f9abc6..112fe5e14d 100644 --- a/app/Http/Requests/BillUpdateRequest.php +++ b/app/Http/Requests/BillUpdateRequest.php @@ -28,6 +28,8 @@ use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class BillUpdateRequest. @@ -81,4 +83,11 @@ class BillUpdateRequest extends FormRequest 'notes' => 'min:1|max:32768|nullable', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/BudgetFormStoreRequest.php b/app/Http/Requests/BudgetFormStoreRequest.php index 580d4d88f6..fecf49dc6c 100644 --- a/app/Http/Requests/BudgetFormStoreRequest.php +++ b/app/Http/Requests/BudgetFormStoreRequest.php @@ -76,15 +76,15 @@ class BudgetFormStoreRequest extends FormRequest */ public function withValidator(Validator $validator): void { - if($validator->fails()) { - Log::channel('audit')->error('Validation errors for budget', $validator->errors()->toArray()); - } - $validator->after( function (Validator $validator): void { // validate all account info $this->validateAutoBudgetAmount($validator); } ); + + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Http/Requests/BudgetFormUpdateRequest.php b/app/Http/Requests/BudgetFormUpdateRequest.php index 36e67db774..11725f3698 100644 --- a/app/Http/Requests/BudgetFormUpdateRequest.php +++ b/app/Http/Requests/BudgetFormUpdateRequest.php @@ -29,6 +29,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -91,5 +92,8 @@ class BudgetFormUpdateRequest extends FormRequest $this->validateAutoBudgetAmount($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } } diff --git a/app/Http/Requests/BudgetIncomeRequest.php b/app/Http/Requests/BudgetIncomeRequest.php index 06575c0e19..5942f05f3d 100644 --- a/app/Http/Requests/BudgetIncomeRequest.php +++ b/app/Http/Requests/BudgetIncomeRequest.php @@ -26,6 +26,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class BudgetIncomeRequest. @@ -46,4 +48,11 @@ class BudgetIncomeRequest extends FormRequest 'end' => 'required|date|after:start', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/BulkEditJournalRequest.php b/app/Http/Requests/BulkEditJournalRequest.php index 22b4ad2fc8..b3256d175c 100644 --- a/app/Http/Requests/BulkEditJournalRequest.php +++ b/app/Http/Requests/BulkEditJournalRequest.php @@ -26,6 +26,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class MassEditBulkJournalRequest. @@ -46,4 +48,11 @@ class BulkEditJournalRequest extends FormRequest 'tags_action' => 'in:no_nothing,do_replace,do_append', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/CategoryFormRequest.php b/app/Http/Requests/CategoryFormRequest.php index f9e50c26ba..c50192ba83 100644 --- a/app/Http/Requests/CategoryFormRequest.php +++ b/app/Http/Requests/CategoryFormRequest.php @@ -27,6 +27,8 @@ use FireflyIII\Models\Category; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class CategoryFormRequest. @@ -67,4 +69,11 @@ class CategoryFormRequest extends FormRequest 'notes' => 'min:1|max:32768|nullable', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/ConfigurationRequest.php b/app/Http/Requests/ConfigurationRequest.php index e72782d588..2d64eae288 100644 --- a/app/Http/Requests/ConfigurationRequest.php +++ b/app/Http/Requests/ConfigurationRequest.php @@ -25,6 +25,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class ConfigurationRequest. @@ -55,4 +57,11 @@ class ConfigurationRequest extends FormRequest 'is_demo_site' => 'min:0|max:1|numeric', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/CurrencyFormRequest.php b/app/Http/Requests/CurrencyFormRequest.php index e070446503..faa383a85d 100644 --- a/app/Http/Requests/CurrencyFormRequest.php +++ b/app/Http/Requests/CurrencyFormRequest.php @@ -27,6 +27,8 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class CurrencyFormRequest. @@ -79,4 +81,11 @@ class CurrencyFormRequest extends FormRequest return $rules; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/DeleteAccountFormRequest.php b/app/Http/Requests/DeleteAccountFormRequest.php index 884983b50a..e4e901ab7a 100644 --- a/app/Http/Requests/DeleteAccountFormRequest.php +++ b/app/Http/Requests/DeleteAccountFormRequest.php @@ -25,6 +25,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class DeleteAccountFormRequest. @@ -43,4 +45,11 @@ class DeleteAccountFormRequest extends FormRequest 'password' => 'required', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/EmailFormRequest.php b/app/Http/Requests/EmailFormRequest.php index 23d5d9d24c..6cc7f7f1ac 100644 --- a/app/Http/Requests/EmailFormRequest.php +++ b/app/Http/Requests/EmailFormRequest.php @@ -26,6 +26,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class EmailFormRequest. @@ -45,4 +47,11 @@ class EmailFormRequest extends FormRequest 'email' => 'required|email', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/InviteUserFormRequest.php b/app/Http/Requests/InviteUserFormRequest.php index 9cdd8267d6..9992d7f8a9 100644 --- a/app/Http/Requests/InviteUserFormRequest.php +++ b/app/Http/Requests/InviteUserFormRequest.php @@ -27,6 +27,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class InviteUserFormRequest @@ -45,4 +47,11 @@ class InviteUserFormRequest extends FormRequest 'invited_user' => 'required|email|unique:invited_users,email', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/JournalLinkRequest.php b/app/Http/Requests/JournalLinkRequest.php index eaea1fab4d..20c17651ef 100644 --- a/app/Http/Requests/JournalLinkRequest.php +++ b/app/Http/Requests/JournalLinkRequest.php @@ -27,6 +27,8 @@ use FireflyIII\Models\LinkType; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class JournalLink. @@ -74,4 +76,11 @@ class JournalLinkRequest extends FormRequest 'opposing' => 'belongsToUser:transaction_journals', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/LinkTypeFormRequest.php b/app/Http/Requests/LinkTypeFormRequest.php index 9bb6e31968..35fa682537 100644 --- a/app/Http/Requests/LinkTypeFormRequest.php +++ b/app/Http/Requests/LinkTypeFormRequest.php @@ -26,6 +26,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class LinkTypeFormRequest. @@ -59,4 +61,11 @@ class LinkTypeFormRequest extends FormRequest 'outward' => 'required|max:255|min:1|different:inward', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/MassDeleteJournalRequest.php b/app/Http/Requests/MassDeleteJournalRequest.php index ba44776059..d539497570 100644 --- a/app/Http/Requests/MassDeleteJournalRequest.php +++ b/app/Http/Requests/MassDeleteJournalRequest.php @@ -25,6 +25,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class MassDeleteJournalRequest. @@ -43,4 +45,11 @@ class MassDeleteJournalRequest extends FormRequest 'confirm_mass_delete.*' => 'required|belongsToUser:transaction_journals,id', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/MassEditJournalRequest.php b/app/Http/Requests/MassEditJournalRequest.php index 0f2700d89c..664433efe2 100644 --- a/app/Http/Requests/MassEditJournalRequest.php +++ b/app/Http/Requests/MassEditJournalRequest.php @@ -25,6 +25,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class MassEditJournalRequest. @@ -49,4 +51,11 @@ class MassEditJournalRequest extends FormRequest 'expense_account' => 'max:255', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/NewUserFormRequest.php b/app/Http/Requests/NewUserFormRequest.php index 4a41a5d333..defffc4c71 100644 --- a/app/Http/Requests/NewUserFormRequest.php +++ b/app/Http/Requests/NewUserFormRequest.php @@ -27,6 +27,8 @@ use FireflyIII\Rules\IsValidAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class NewUserFormRequest. @@ -52,4 +54,11 @@ class NewUserFormRequest extends FormRequest 'amount_currency_id_credit_card_limit' => 'exists:transaction_currencies,id', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/ObjectGroupFormRequest.php b/app/Http/Requests/ObjectGroupFormRequest.php index b6ed300ea9..5741172852 100644 --- a/app/Http/Requests/ObjectGroupFormRequest.php +++ b/app/Http/Requests/ObjectGroupFormRequest.php @@ -27,6 +27,8 @@ use FireflyIII\Models\ObjectGroup; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class ObjectGroupFormRequest. @@ -63,4 +65,11 @@ class ObjectGroupFormRequest extends FormRequest 'title' => $titleRule, ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/PiggyBankStoreRequest.php b/app/Http/Requests/PiggyBankStoreRequest.php index 0893d607ce..cc703a1f6b 100644 --- a/app/Http/Requests/PiggyBankStoreRequest.php +++ b/app/Http/Requests/PiggyBankStoreRequest.php @@ -27,6 +27,8 @@ use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class PiggyBankStoreRequest. @@ -68,4 +70,11 @@ class PiggyBankStoreRequest extends FormRequest 'notes' => 'min:1|max:32768|nullable', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/PiggyBankUpdateRequest.php b/app/Http/Requests/PiggyBankUpdateRequest.php index bf0afb361e..873878e4ba 100644 --- a/app/Http/Requests/PiggyBankUpdateRequest.php +++ b/app/Http/Requests/PiggyBankUpdateRequest.php @@ -28,6 +28,8 @@ use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class PiggyBankFormRequest. @@ -67,9 +69,16 @@ class PiggyBankUpdateRequest extends FormRequest 'targetamount' => ['nullable', new IsValidPositiveAmount()], 'startdate' => 'date', 'targetdate' => 'date|nullable', - 'order' => 'integer|max:65536|min:1', + 'order' => 'integer|max:32768|min:1', 'object_group' => 'min:0|max:255', 'notes' => 'min:1|max:32768|nullable', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/ProfileFormRequest.php b/app/Http/Requests/ProfileFormRequest.php index 89749b119e..550dba0889 100644 --- a/app/Http/Requests/ProfileFormRequest.php +++ b/app/Http/Requests/ProfileFormRequest.php @@ -25,6 +25,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class ProfileFormRequest. @@ -45,4 +47,11 @@ class ProfileFormRequest extends FormRequest 'new_password_confirmation' => 'required', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/ReconciliationStoreRequest.php b/app/Http/Requests/ReconciliationStoreRequest.php index 4a73ca8ee8..9a80d77ba5 100644 --- a/app/Http/Requests/ReconciliationStoreRequest.php +++ b/app/Http/Requests/ReconciliationStoreRequest.php @@ -28,6 +28,8 @@ use FireflyIII\Rules\ValidJournals; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class ReconciliationStoreRequest @@ -75,4 +77,11 @@ class ReconciliationStoreRequest extends FormRequest 'reconcile' => 'required|in:create,nothing', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index 0075c3babf..81840c686a 100644 --- a/app/Http/Requests/RecurrenceFormRequest.php +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -34,6 +34,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\AccountValidator; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -246,6 +247,9 @@ class RecurrenceFormRequest extends FormRequest $this->validateAccountInformation($validator); } ); + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } } /** diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index 0dd6049add..dc68c4b659 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -33,6 +33,8 @@ use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class CategoryFormRequest. @@ -215,7 +217,7 @@ class ReportFormRequest extends FormRequest app('log')->debug('Set is:', $set); } if (!is_array($set)) { - app('log')->error(sprintf('Set is not an array! "%s"', $set)); + app('log')->debug(sprintf('Set is not an array! "%s"', $set)); return $collection; } @@ -245,4 +247,11 @@ class ReportFormRequest extends FormRequest 'report_type' => 'in:audit,default,category,budget,tag,double', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index 02a7c1c819..55dca94e67 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -28,6 +28,8 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class RuleFormRequest. @@ -119,6 +121,13 @@ class RuleFormRequest extends FormRequest return $rules; } + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } + private function getRuleTriggerData(): array { $return = []; diff --git a/app/Http/Requests/RuleGroupFormRequest.php b/app/Http/Requests/RuleGroupFormRequest.php index c2d96a6994..4072f58989 100644 --- a/app/Http/Requests/RuleGroupFormRequest.php +++ b/app/Http/Requests/RuleGroupFormRequest.php @@ -28,6 +28,8 @@ use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class RuleGroupFormRequest. @@ -74,4 +76,11 @@ class RuleGroupFormRequest extends FormRequest 'active' => [new IsBoolean()], ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/SelectTransactionsRequest.php b/app/Http/Requests/SelectTransactionsRequest.php index c6641acc98..087b2e8e6c 100644 --- a/app/Http/Requests/SelectTransactionsRequest.php +++ b/app/Http/Requests/SelectTransactionsRequest.php @@ -26,6 +26,8 @@ namespace FireflyIII\Http\Requests; use Carbon\Carbon; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class SelectTransactionsRequest. @@ -52,4 +54,11 @@ class SelectTransactionsRequest extends FormRequest 'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/TagFormRequest.php b/app/Http/Requests/TagFormRequest.php index ea1b5a267f..f8f7d529d7 100644 --- a/app/Http/Requests/TagFormRequest.php +++ b/app/Http/Requests/TagFormRequest.php @@ -29,6 +29,8 @@ use FireflyIII\Support\Request\AppendsLocationData; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class TagFormRequest. @@ -71,10 +73,17 @@ class TagFormRequest extends FormRequest $rules = [ 'tag' => $tagRule, 'id' => $idRule, - 'description' => 'max:65536|min:1|nullable', + 'description' => 'max:32768|min:1|nullable', 'date' => 'date|nullable', ]; return Location::requestRules($rules); } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/TestRuleFormRequest.php b/app/Http/Requests/TestRuleFormRequest.php index 7d215391fb..f95dbfbb11 100644 --- a/app/Http/Requests/TestRuleFormRequest.php +++ b/app/Http/Requests/TestRuleFormRequest.php @@ -26,6 +26,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class TestRuleFormRequest. @@ -49,4 +51,11 @@ class TestRuleFormRequest extends FormRequest 'rule-trigger-value.*' => 'required|max:1024|min:1|ruleTriggerValue', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/TokenFormRequest.php b/app/Http/Requests/TokenFormRequest.php index 10df54a479..4d4966d1bc 100644 --- a/app/Http/Requests/TokenFormRequest.php +++ b/app/Http/Requests/TokenFormRequest.php @@ -25,6 +25,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class TokenFormRequest. @@ -43,4 +45,11 @@ class TokenFormRequest extends FormRequest 'code' => 'required|2faCode', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/TriggerRecurrenceRequest.php b/app/Http/Requests/TriggerRecurrenceRequest.php index c09c4ad5a0..c090e10ae6 100644 --- a/app/Http/Requests/TriggerRecurrenceRequest.php +++ b/app/Http/Requests/TriggerRecurrenceRequest.php @@ -27,6 +27,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class TriggerRecurrenceRequest @@ -55,4 +57,11 @@ class TriggerRecurrenceRequest extends FormRequest 'date' => 'required|date', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/UserFormRequest.php b/app/Http/Requests/UserFormRequest.php index e0dc080ef9..7071633404 100644 --- a/app/Http/Requests/UserFormRequest.php +++ b/app/Http/Requests/UserFormRequest.php @@ -26,6 +26,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UserFormRequest. @@ -63,4 +65,11 @@ class UserFormRequest extends FormRequest 'is_owner' => 'min:0|max:1|numeric', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Http/Requests/UserRegistrationRequest.php b/app/Http/Requests/UserRegistrationRequest.php index 1428ec096d..50d444f8d1 100644 --- a/app/Http/Requests/UserRegistrationRequest.php +++ b/app/Http/Requests/UserRegistrationRequest.php @@ -25,6 +25,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UserRegistrationRequest. @@ -53,4 +55,11 @@ class UserRegistrationRequest extends FormRequest 'password' => 'confirmed|secure_password', ]; } + + public function withValidator(Validator $validator): void + { + if($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); + } + } } diff --git a/app/Jobs/CreateRecurringTransactions.php b/app/Jobs/CreateRecurringTransactions.php index 3b0a6371a9..025116a855 100644 --- a/app/Jobs/CreateRecurringTransactions.php +++ b/app/Jobs/CreateRecurringTransactions.php @@ -325,7 +325,7 @@ class CreateRecurringTransactions implements ShouldQueue } /** - * Check if the occurences should be executed. + * Check if the occurrences should be executed. * * @throws DuplicateTransactionException * @throws FireflyException diff --git a/app/Models/Location.php b/app/Models/Location.php index 0eeca7e125..5ab9e58d06 100644 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -63,6 +63,9 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; * @method static Builder|Location whereUpdatedAt($value) * @method static Builder|Location whereZoomLevel($value) * + * @property Collection $transactionJournals + * @property null|int $transaction_journals_count + * * @mixin Eloquent */ class Location extends Model diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index e8e520e9b0..974cc3d7a7 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -106,6 +106,7 @@ class Transaction extends Model 'encrypted' => 'boolean', // model does not have these fields though 'bill_name_encrypted' => 'boolean', 'reconciled' => 'boolean', + 'date' => 'datetime', ]; protected $fillable diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 49c6271e8d..b5a1183717 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Providers; +use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Schema; use Illuminate\Support\ServiceProvider; @@ -54,6 +55,24 @@ class AppServiceProvider extends ServiceProvider ->withHeaders($headers) ; }); + + // blade extension + Blade::directive('activeXRoutePartial', function (string $route) { + $name = \Route::getCurrentRoute()->getName() ?? ''; + if (str_contains($name, $route)) { + return 'menu-open'; + } + + return ''; + }); + Blade::if('partialroute', function (string $route) { + $name = \Route::getCurrentRoute()->getName() ?? ''; + if (str_contains($name, $route)) { + return true; + } + + return false; + }); } /** diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index ef7e1b821a..d2cd12d759 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -454,6 +454,35 @@ class RuleRepository implements RuleRepositoryInterface $type = sprintf('-%s', $type); } + // empty the value in case the rule needs no context + // TODO create a helper to automatically return these. + $needTrue = [ + 'reconciled', + 'has_attachments', + 'has_any_category', + 'has_any_budget', + 'has_any_bill', + 'has_any_tag', + 'any_notes', + 'any_external_url', + 'has_no_attachments', + 'has_no_category', + 'has_no_budget', + 'has_no_bill', + 'has_no_tag', + 'no_notes', + 'no_external_url', + 'source_is_cash', + 'destination_is_cash', + 'account_is_cash', + 'exists', + 'no_external_id', + 'any_external_id', + ]; + if(in_array($type, $needTrue, true)) { + $value = ''; + } + $triggerValues = [ 'action' => $type, 'value' => $value, diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index 486fbf67b1..3f056789aa 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -123,7 +123,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface public function setUser(null|Authenticatable|User $user): void { - if ($user instanceof user) { + if ($user instanceof User) { $this->user = $user; } } diff --git a/app/Services/Internal/Support/CreditRecalculateService.php b/app/Services/Internal/Support/CreditRecalculateService.php index aef6a058d1..37e51e2c60 100644 --- a/app/Services/Internal/Support/CreditRecalculateService.php +++ b/app/Services/Internal/Support/CreditRecalculateService.php @@ -33,6 +33,7 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use Illuminate\Support\Facades\Log; /** * Class CreditRecalculateService @@ -267,6 +268,7 @@ class CreditRecalculateService return $leftOfDebt; } + Log::debug(sprintf('Liability direction is "%s"', $direction)); // amount to use depends on the currency: $usedAmount = $this->getAmountToUse($transaction, $accountCurrency, $foreignCurrency); @@ -334,6 +336,21 @@ class CreditRecalculateService return $result; } + if ($isSameAccount && $isDebit && $this->isTransferIn($usedAmount, $type)) { // case 9 + $usedAmount = app('steam')->positive($usedAmount); + $result = bcadd($leftOfDebt, $usedAmount); + app('log')->debug(sprintf('Case 9 (transfer into debit liability, means you owe more): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + + return $result; + } + if ($isSameAccount && $isDebit && $this->isTransferOut($usedAmount, $type)) { // case 10 + $usedAmount = app('steam')->positive($usedAmount); + $result = bcsub($leftOfDebt, $usedAmount); + app('log')->debug(sprintf('Case 5 (transfer out of debit liability, means you owe less): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + + return $result; + } + // in any other case, remove amount from left of debt. if (in_array($type, [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER], true)) { $usedAmount = app('steam')->negative($usedAmount); @@ -400,7 +417,7 @@ class CreditRecalculateService * because the person is paying us back. * * case 7 - * if it's a credit ("I am owed") this increases the amount due. + * if it's a debit ("I owe") this increases the amount due. * because we are borrowing more money. */ private function isDepositOut(string $amount, string $transactionType): bool @@ -423,9 +440,25 @@ class CreditRecalculateService * case 5: transfer into loan (from other loan). * if it's a credit ("I am owed") this increases the amount due, * because the person has to pay more back. + * + * case 8: transfer into loan (from other loan). + * if it's a debit ("I owe") this decreases the amount due. + * because the person has to pay more back. */ private function isTransferIn(string $amount, string $transactionType): bool { return TransactionType::TRANSFER === $transactionType && 1 === bccomp($amount, '0'); } + + /** + * it's a transfer out of loan (from other loan) + * + * case 9 + * if it's a debit ("I owe") this decreases the amount due. + * because we remove money from the amount left to owe + */ + private function isTransferOut(string $amount, string $transactionType): bool + { + return TransactionType::DEPOSIT === $transactionType && -1 === bccomp($amount, '0'); + } } diff --git a/app/Services/Internal/Update/JournalUpdateService.php b/app/Services/Internal/Update/JournalUpdateService.php index ca34d50187..b6ecdc31a6 100644 --- a/app/Services/Internal/Update/JournalUpdateService.php +++ b/app/Services/Internal/Update/JournalUpdateService.php @@ -131,7 +131,7 @@ class JournalUpdateService app('log')->debug(sprintf('Now in %s', __METHOD__)); app('log')->debug(sprintf('Now in JournalUpdateService for journal #%d.', $this->transactionJournal->id)); - $this->data['reconciled'] = array_key_exists('reconciled', $this->data) ? $this->data['reconciled'] : false; + $this->data['reconciled'] = array_key_exists('reconciled', $this->data) ? $this->data['reconciled'] : null; // can we update account data using the new type? if ($this->hasValidAccounts()) { diff --git a/app/Support/Http/Controllers/ModelInformation.php b/app/Support/Http/Controllers/ModelInformation.php index accf008e3b..5e9217bad3 100644 --- a/app/Support/Http/Controllers/ModelInformation.php +++ b/app/Support/Http/Controllers/ModelInformation.php @@ -41,7 +41,7 @@ trait ModelInformation * * @throws FireflyException */ - protected function getActionsForBill(Bill $bill): array // get info and augument + protected function getActionsForBill(Bill $bill): array // get info and argument { try { $result = view( @@ -108,7 +108,7 @@ trait ModelInformation * * @throws FireflyException */ - protected function getTriggersForBill(Bill $bill): array // get info and augument + protected function getTriggersForBill(Bill $bill): array // get info and argument { // TODO duplicate code $operators = config('search.operators'); diff --git a/app/Support/Models/BillDateCalculator.php b/app/Support/Models/BillDateCalculator.php index 5a525b355f..e5c2ef8f80 100644 --- a/app/Support/Models/BillDateCalculator.php +++ b/app/Support/Models/BillDateCalculator.php @@ -29,6 +29,10 @@ use Illuminate\Support\Facades\Log; class BillDateCalculator { + // #8401 we start keeping track of the diff in periods, because if it can't jump over a period (happens often in February) + // we can force the process along. + private int $diffInMonths = 0; + /** * Returns the dates a bill needs to be paid. * @@ -43,12 +47,16 @@ class BillDateCalculator Log::debug(sprintf('Dates must be between %s and %s.', $earliest->format('Y-m-d'), $latest->format('Y-m-d'))); Log::debug(sprintf('Bill started on %s, period is "%s", skip is %d, last paid = "%s".', $billStart->format('Y-m-d'), $period, $skip, $lastPaid?->format('Y-m-d'))); + $daysUntilEOM = app('navigation')->daysUntilEndOfMonth($billStart); + Log::debug(sprintf('For bill start, days until end of month is %d', $daysUntilEOM)); + $set = new Collection(); $currentStart = clone $earliest; // 2023-06-23 subDay to fix 7655 $currentStart->subDay(); $loop = 0; + Log::debug('Start of loop'); while ($currentStart <= $latest) { Log::debug(sprintf('Current start is %s', $currentStart->format('Y-m-d'))); @@ -79,8 +87,23 @@ class BillDateCalculator $set->push(clone $nextExpectedMatch); } + // #8401 + // a little check for when the day of the bill (ie 30th of the month) is not possible in + // the next expected month because that month has only 28 days (i.e. february). + // this applies to leap years as well. + if ($daysUntilEOM < 4) { + $nextUntilEOM = app('navigation')->daysUntilEndOfMonth($nextExpectedMatch); + $diffEOM = $daysUntilEOM - $nextUntilEOM; + if ($diffEOM > 0) { + Log::debug(sprintf('Bill start is %d days from the end of the month. nextExceptedMatch is %d days from the end of the month.', $daysUntilEOM, $nextUntilEOM)); + $nextExpectedMatch->subDays(1); + Log::debug(sprintf('Subtract %d days from next expected match, which is now %s', $diffEOM, $nextExpectedMatch->format('Y-m-d'))); + } + } + // 2023-10 // for the next loop, go to end of period, THEN add day. + Log::debug('Add one day to nextExpectedMatch/currentStart.'); $nextExpectedMatch->addDay(); $currentStart = clone $nextExpectedMatch; @@ -117,8 +140,13 @@ class BillDateCalculator return $billStartDate; } - $steps = app('navigation')->diffInPeriods($period, $skip, $earliest, $billStartDate); - $result = clone $billStartDate; + $steps = app('navigation')->diffInPeriods($period, $skip, $earliest, $billStartDate); + if ($steps === $this->diffInMonths) { + Log::debug(sprintf('Steps is %d, which is the same as diffInMonths (%d), so we add another 1.', $steps, $this->diffInMonths)); + ++$steps; + } + $this->diffInMonths = $steps; + $result = clone $billStartDate; if ($steps > 0) { --$steps; Log::debug(sprintf('Steps is %d, because addPeriod already adds 1.', $steps)); diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index ae9c6e3c83..4d92ba510d 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -302,6 +302,13 @@ class Navigation return $currentEnd; } + public function daysUntilEndOfMonth(Carbon $date): int + { + $endOfMonth = $date->copy()->endOfMonth(); + + return $date->diffInDays($endOfMonth); + } + public function diffInPeriods(string $period, int $skip, Carbon $beginning, Carbon $end): int { Log::debug(sprintf( diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index aef75bb722..8d78fdd9c7 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -232,7 +232,7 @@ class OperatorQuerySearch implements SearchInterface $this->collector->setUser($user); $this->collector->withAccountInformation()->withCategoryInformation()->withBudgetInformation(); - $this->setLimit((int) app('preferences')->getForUser($user, 'listPageSize', 50)->data); + $this->setLimit((int)app('preferences')->getForUser($user, 'listPageSize', 50)->data); } public function setLimit(int $limit): void @@ -274,7 +274,7 @@ class OperatorQuerySearch implements SearchInterface case Emoticon::class: case Emoji::class: case Mention::class: - $allWords = (string) $searchNode->getValue(); + $allWords = (string)$searchNode->getValue(); app('log')->debug(sprintf('Add words "%s" to search string, because Node class is "%s"', $allWords, $class)); $this->words[] = $allWords; @@ -304,11 +304,11 @@ class OperatorQuerySearch implements SearchInterface // must be valid operator: if ( in_array($operator, $this->validOperators, true) - && $this->updateCollector($operator, (string) $value, $prohibited)) { + && $this->updateCollector($operator, (string)$value, $prohibited)) { $this->operators->push( [ 'type' => self::getRootOperator($operator), - 'value' => (string) $value, + 'value' => (string)$value, 'prohibited' => $prohibited, ] ); @@ -318,7 +318,7 @@ class OperatorQuerySearch implements SearchInterface app('log')->debug(sprintf('Added INVALID operator type "%s"', $operator)); $this->invalidOperators[] = [ 'type' => $operator, - 'value' => (string) $value, + 'value' => (string)$value, ]; } } @@ -518,7 +518,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'source_account_id': - $account = $this->accountRepository->find((int) $value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->setSourceAccounts(new Collection([$account])); } @@ -530,7 +530,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-source_account_id': - $account = $this->accountRepository->find((int) $value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->excludeSourceAccounts(new Collection([$account])); } @@ -646,7 +646,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'destination_account_id': - $account = $this->accountRepository->find((int) $value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->setDestinationAccounts(new Collection([$account])); } @@ -657,7 +657,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-destination_account_id': - $account = $this->accountRepository->find((int) $value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->excludeDestinationAccounts(new Collection([$account])); } @@ -671,7 +671,7 @@ class OperatorQuerySearch implements SearchInterface $parts = explode(',', $value); $collection = new Collection(); foreach ($parts as $accountId) { - $account = $this->accountRepository->find((int) $accountId); + $account = $this->accountRepository->find((int)$accountId); if (null !== $account) { $collection->push($account); } @@ -689,7 +689,7 @@ class OperatorQuerySearch implements SearchInterface $parts = explode(',', $value); $collection = new Collection(); foreach ($parts as $accountId) { - $account = $this->accountRepository->find((int) $accountId); + $account = $this->accountRepository->find((int)$accountId); if (null !== $account) { $collection->push($account); } @@ -1904,6 +1904,7 @@ class OperatorQuerySearch implements SearchInterface * stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) + * @SuppressWarnings(PHPMD.NPathComplexity) */ private function searchAccount(string $value, SearchDirection $searchDirection, StringPosition $stringPosition, bool $prohibited = false): void { @@ -1949,12 +1950,17 @@ class OperatorQuerySearch implements SearchInterface // get accounts: $accounts = $this->accountRepository->searchAccount($value, $searchTypes, 1337); - if (0 === $accounts->count()) { + if (0 === $accounts->count() && false === $prohibited) { app('log')->debug('Found zero accounts, search for non existing account, NO results will be returned.'); $this->collector->findNothing(); return; } + if (0 === $accounts->count() && true === $prohibited) { + app('log')->debug('Found zero accounts, but the search is negated, so effectively we ignore the search parameter.'); + + return; + } app('log')->debug(sprintf('Found %d accounts, will filter.', $accounts->count())); $filtered = $accounts->filter( static function (Account $account) use ($value, $stringMethod) { @@ -2038,7 +2044,7 @@ class OperatorQuerySearch implements SearchInterface $filtered = $accounts->filter( static function (Account $account) use ($value, $stringMethod) { // either IBAN or account number - $ibanMatch = $stringMethod(strtolower((string) $account->iban), strtolower($value)); + $ibanMatch = $stringMethod(strtolower((string)$account->iban), strtolower($value)); $accountNrMatch = false; /** @var AccountMeta $meta */ diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 492656baef..0e08bedaea 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Support; use Carbon\Carbon; +use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\Transaction; @@ -44,7 +45,7 @@ class Steam $repository = app(AccountRepositoryInterface::class); $repository->setUser($account->user); - $currencyId = (int) $repository->getMetaValue($account, 'currency_id'); + $currencyId = (int)$repository->getMetaValue($account, 'currency_id'); $transactions = $account->transactions() ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) @@ -73,7 +74,7 @@ class Steam /** @var array $transaction */ foreach ($transactions as $transaction) { - $value = (string) ($transaction[$key] ?? '0'); + $value = (string)($transaction[$key] ?? '0'); $value = '' === $value ? '0' : $value; $sum = bcadd($sum, $value); } @@ -142,14 +143,14 @@ class Steam /** @var Transaction $entry */ foreach ($set as $entry) { // normal amount and foreign amount - $modified = (string) (null === $entry->modified ? '0' : $entry->modified); - $foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign); + $modified = (string)(null === $entry->modified ? '0' : $entry->modified); + $foreignModified = (string)(null === $entry->modified_foreign ? '0' : $entry->modified_foreign); $amount = '0'; - if ($currencyId === (int) $entry->transaction_currency_id || 0 === $currencyId) { + if ($currencyId === (int)$entry->transaction_currency_id || 0 === $currencyId) { // use normal amount: $amount = $modified; } - if ($currencyId === (int) $entry->foreign_currency_id) { + if ($currencyId === (int)$entry->foreign_currency_id) { // use foreign amount: $amount = $foreignModified; } @@ -268,13 +269,19 @@ class Steam /** @var Transaction $transaction */ foreach ($set as $transaction) { - $day = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date'], config('app.timezone')); + $day = false; + + try { + $day = Carbon::parse($transaction['date'], config('app.timezone')); + } catch (InvalidFormatException $e) { + Log::error(sprintf('Could not parse date "%s" in %s: %s', $transaction['date'], __METHOD__, $e->getMessage())); + } if (false === $day) { $day = today(config('app.timezone')); } $format = $day->format('Y-m-d'); // if the transaction is in the expected currency, change nothing. - if ((int) $transaction['transaction_currency_id'] === $native->id) { + if ((int)$transaction['transaction_currency_id'] === $native->id) { // change the current balance, set it to today, continue the loop. $currentBalance = bcadd($currentBalance, $transaction['amount']); $balances[$format] = $currentBalance; @@ -283,7 +290,7 @@ class Steam continue; } // if foreign currency is in the expected currency, do nothing: - if ((int) $transaction['foreign_currency_id'] === $native->id) { + if ((int)$transaction['foreign_currency_id'] === $native->id) { $currentBalance = bcadd($currentBalance, $transaction['foreign_amount']); $balances[$format] = $currentBalance; Log::debug(sprintf('%s: transaction in %s (foreign), new balance is %s.', $format, $native->code, $currentBalance)); @@ -291,7 +298,7 @@ class Steam continue; } // otherwise, convert 'amount' to the necessary currency: - $currencyId = (int) $transaction['transaction_currency_id']; + $currencyId = (int)$transaction['transaction_currency_id']; $currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId); $currencies[$currencyId] = $currency; @@ -429,7 +436,13 @@ class Steam $converter = new ExchangeRateConverter(); foreach ($new as $set) { foreach ($set as $transaction) { - $currentDate = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date']); + $currentDate = false; + + try { + $currentDate = Carbon::parse($transaction['date'], config('app.timezone')); + } catch (InvalidFormatException $e) { + Log::error(sprintf('Could not parse date "%s" in %s', $transaction['date'], __METHOD__)); + } if (false === $currentDate) { $currentDate = today(config('app.timezone')); } @@ -443,7 +456,13 @@ class Steam foreach ($new as $set) { foreach ($set as $transaction) { - $currentDate = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date']); + $currentDate = false; + + try { + $currentDate = Carbon::parse($transaction['date'], config('app.timezone')); + } catch (InvalidFormatException $e) { + Log::error(sprintf('Could not parse date "%s" in %s', $transaction['date'], __METHOD__)); + } if (false === $currentDate) { $currentDate = today(config('app.timezone')); } @@ -578,7 +597,7 @@ class Steam /** @var \stdClass $entry */ foreach ($balances as $entry) { - $return[(int) $entry->transaction_currency_id] = (string) $entry->sum_for_currency; + $return[(int)$entry->transaction_currency_id] = (string)$entry->sum_for_currency; } $cache->store($return); @@ -681,7 +700,7 @@ class Steam throw new FireflyException($e->getMessage(), 0, $e); } - return (string) $hostName; + return (string)$hostName; } public function getLastActivities(array $accounts): array @@ -716,7 +735,7 @@ class Steam if ('equal' === $locale) { $locale = $this->getLanguage(); } - $locale = (string) $locale; + $locale = (string)$locale; // Check for Windows to replace the locale correctly. if ('WIN' === strtoupper(substr(PHP_OS, 0, 3))) { @@ -815,20 +834,20 @@ class Steam return $value; } - $number = substr($value, 0, (int) strpos($value, 'E')); + $number = substr($value, 0, (int)strpos($value, 'E')); if (str_contains($number, '.')) { - $post = strlen(substr($number, (int) strpos($number, '.') + 1)); - $mantis = substr($value, (int) strpos($value, 'E') + 1); + $post = strlen(substr($number, (int)strpos($number, '.') + 1)); + $mantis = substr($value, (int)strpos($value, 'E') + 1); if ($mantis < 0) { - $post += abs((int) $mantis); + $post += abs((int)$mantis); } // TODO careless float could break financial math. - return number_format((float) $value, $post, '.', ''); + return number_format((float)$value, $post, '.', ''); } // TODO careless float could break financial math. - return number_format((float) $value, 0, '.', ''); + return number_format((float)$value, 0, '.', ''); } public function opposite(string $amount = null): ?string @@ -848,24 +867,24 @@ class Steam // has a K in it, remove the K and multiply by 1024. $bytes = bcmul(rtrim($string, 'k'), '1024'); - return (int) $bytes; + return (int)$bytes; } if (false !== stripos($string, 'm')) { // has a M in it, remove the M and multiply by 1048576. $bytes = bcmul(rtrim($string, 'm'), '1048576'); - return (int) $bytes; + return (int)$bytes; } if (false !== stripos($string, 'g')) { // has a G in it, remove the G and multiply by (1024)^3. $bytes = bcmul(rtrim($string, 'g'), '1073741824'); - return (int) $bytes; + return (int)$bytes; } - return (int) $string; + return (int)$string; } public function positive(string $amount): string diff --git a/app/TransactionRules/Actions/SetNotes.php b/app/TransactionRules/Actions/SetNotes.php index c1ea311cb3..260ccfe4d4 100644 --- a/app/TransactionRules/Actions/SetNotes.php +++ b/app/TransactionRules/Actions/SetNotes.php @@ -33,7 +33,7 @@ use FireflyIII\Models\TransactionJournal; */ class SetNotes implements ActionInterface { - private RuleACtion $action; + private RuleAction $action; /** * TriggerInterface constructor. diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index d0697f1826..d4828acdbf 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -279,24 +279,31 @@ class SearchRuleEngine implements RuleEngineInterface private function findNonStrictRule(Rule $rule): Collection { + app('log')->debug(sprintf('findNonStrictRule(#%d)', $rule->id)); // start a search query for individual each trigger: $total = new Collection(); $count = 0; $triggers = []; if ($this->refreshTriggers) { + app('log')->debug('Will refresh triggers.'); $triggers = $rule->ruleTriggers()->orderBy('order', 'ASC')->get(); } if (!$this->refreshTriggers) { + app('log')->debug('Will not refresh triggers.'); $triggers = $rule->ruleTriggers; } + app('log')->debug(sprintf('Will run %d trigger(s).', count($triggers))); /** @var RuleTrigger $ruleTrigger */ foreach ($triggers as $ruleTrigger) { + app('log')->debug(sprintf('Now at rule trigger #%d: %s:"%s" (%s).', $ruleTrigger->id, $ruleTrigger->trigger_type, $ruleTrigger->trigger_value, var_export($ruleTrigger->stop_processing, true))); if (false === $ruleTrigger->active) { + app('log')->debug('Trigger is not active, continue.'); + continue; } if ('user_action' === $ruleTrigger->trigger_type) { - app('log')->debug('Skip trigger type.'); + app('log')->debug('Skip trigger type. continue.'); continue; } @@ -334,7 +341,7 @@ class SearchRuleEngine implements RuleEngineInterface app('log')->debug(sprintf('Total collection is now %d transactions', $total->count())); ++$count; // if trigger says stop processing, do so. - if ($ruleTrigger->stop_processing && $collection->count() > 0) { + if ($ruleTrigger->stop_processing && $result->count() > 0) { app('log')->debug('The trigger says to stop processing, so stop processing other triggers.'); break; diff --git a/app/Transformers/RuleTransformer.php b/app/Transformers/RuleTransformer.php index 2292999259..bb4431d536 100644 --- a/app/Transformers/RuleTransformer.php +++ b/app/Transformers/RuleTransformer.php @@ -109,12 +109,18 @@ class RuleTransformer extends AbstractTransformer if ('user_action' === $ruleTrigger->trigger_type) { continue; } - $result[] = [ + $triggerValue = (string)$ruleTrigger->trigger_value; + $needsContext = config(sprintf('search.operators.%s.needs_context', $ruleTrigger->trigger_type), true); + if (false === $needsContext) { + $triggerValue = 'true'; + } + + $result[] = [ 'id' => (string)$ruleTrigger->id, 'created_at' => $ruleTrigger->created_at->toAtomString(), 'updated_at' => $ruleTrigger->updated_at->toAtomString(), 'type' => $ruleTrigger->trigger_type, - 'value' => $ruleTrigger->trigger_value, + 'value' => $triggerValue, 'order' => $ruleTrigger->order, 'active' => $ruleTrigger->active, 'stop_processing' => $ruleTrigger->stop_processing, diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index da10d6798e..66e89d4f7b 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -74,7 +74,7 @@ class FireflyValidator extends Validator $secret = ''; } - return (bool) \Google2FA::verifyKey((string) $secret, $value); + return (bool)\Google2FA::verifyKey((string)$secret, $value); } /** @@ -88,7 +88,7 @@ class FireflyValidator extends Validator { $field = $parameters[1] ?? 'id'; - if (0 === (int) $value) { + if (0 === (int)$value) { return true; } $count = \DB::table($parameters[0])->where('user_id', auth()->user()->id)->where($field, $value)->count(); @@ -178,7 +178,7 @@ class FireflyValidator extends Validator $value = strtoupper($value); // replace characters outside of ASCI range. - $value = (string) iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value); + $value = (string)iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value); $search = [' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; $replace = ['', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35']; @@ -201,7 +201,7 @@ class FireflyValidator extends Validator return false; } - return 1 === (int) $checksum; + return 1 === (int)$checksum; } /** @@ -216,7 +216,7 @@ class FireflyValidator extends Validator /** @var mixed $compare */ $compare = $parameters[0] ?? '0'; - return bccomp((string) $value, (string) $compare) < 0; + return bccomp((string)$value, (string)$compare) < 0; } /** @@ -231,7 +231,7 @@ class FireflyValidator extends Validator /** @var mixed $compare */ $compare = $parameters[0] ?? '0'; - return bccomp((string) $value, (string) $compare) > 0; + return bccomp((string)$value, (string)$compare) > 0; } /** @@ -245,7 +245,7 @@ class FireflyValidator extends Validator { $field = $parameters[1] ?? 'id'; - if (0 === (int) $value) { + if (0 === (int)$value) { return true; } $count = \DB::table($parameters[0])->where($field, $value)->count(); @@ -258,7 +258,7 @@ class FireflyValidator extends Validator // first, get the index from this string: $value ??= ''; $parts = explode('.', $attribute); - $index = (int) ($parts[1] ?? '0'); + $index = (int)($parts[1] ?? '0'); // get the name of the trigger from the data array: $actionType = $this->data['actions'][$index]['type'] ?? 'invalid'; @@ -323,7 +323,7 @@ class FireflyValidator extends Validator { // first, get the index from this string: $parts = explode('.', $attribute); - $index = (int) ($parts[1] ?? '0'); + $index = (int)($parts[1] ?? '0'); // get the name of the trigger from the data array: $triggerType = $this->data['triggers'][$index]['type'] ?? 'invalid'; @@ -339,7 +339,23 @@ class FireflyValidator extends Validator return is_numeric($value); } + // these triggers need just the word "true": + // TODO create a helper to automatically return these. + $needTrue = [ + 'reconciled', 'has_attachments', 'has_any_category', 'has_any_budget', 'has_any_bill', 'has_any_tag', 'any_notes', 'any_external_url', 'has_no_attachments', 'has_no_category', 'has_no_budget', 'has_no_bill', 'has_no_tag', 'no_notes', 'no_external_url', + 'source_is_cash', + 'destination_is_cash', + 'account_is_cash', + 'exists', + 'no_external_id', + 'any_external_id', + ]; + if (in_array($triggerType, $needTrue, true)) { + return 'true' === $value; + } + // these trigger types need a simple strlen check: + // TODO create a helper to automatically return these. $length = [ 'source_account_starts', 'source_account_ends', @@ -367,18 +383,21 @@ class FireflyValidator extends Validator } // check if it's an existing account. + // TODO create a helper to automatically return these. if (in_array($triggerType, ['destination_account_id', 'source_account_id'], true)) { - return is_numeric($value) && (int) $value > 0; + return is_numeric($value) && (int)$value > 0; } // check transaction type. + // TODO create a helper to automatically return these. if ('transaction_type' === $triggerType) { $count = TransactionType::where('type', ucfirst($value))->count(); return 1 === $count; } - // if the type is date, the simply try to parse it and throw error when it's bad. + // if the type is date, then simply try to parse it and throw error when it's bad. + // TODO create a helper to automatically return these. if (in_array($triggerType, ['date_is', 'created_on', 'updated_on', 'date_before', 'date_after'], true)) { /** @var ParseDateString $parser */ $parser = app(ParseDateString::class); @@ -405,7 +424,7 @@ class FireflyValidator extends Validator { $verify = false; if (array_key_exists('verify_password', $this->data)) { - $verify = 1 === (int) $this->data['verify_password']; + $verify = 1 === (int)$this->data['verify_password']; } if ($verify) { /** @var Verifier $service */ @@ -440,7 +459,7 @@ class FireflyValidator extends Validator if (array_key_exists('type', $this->data)) { app('log')->debug('validateUniqueAccountForUser::typeString'); - return $this->validateByAccountTypeString($value, $parameters, (string) $this->data['type']); + return $this->validateByAccountTypeString($value, $parameters, (string)$this->data['type']); } if (array_key_exists('account_type_id', $this->data)) { app('log')->debug('validateUniqueAccountForUser::typeId'); @@ -451,7 +470,7 @@ class FireflyValidator extends Validator if (null !== $parameterId) { app('log')->debug('validateUniqueAccountForUser::paramId'); - return $this->validateByParameterId((int) $parameterId, $value); + return $this->validateByParameterId((int)$parameterId, $value); } if (array_key_exists('id', $this->data)) { app('log')->debug('validateUniqueAccountForUser::accountId'); @@ -474,9 +493,9 @@ class FireflyValidator extends Validator */ public function validateUniqueAccountNumberForUser($attribute, $value, $parameters): bool { - $accountId = (int) ($this->data['id'] ?? 0.0); + $accountId = (int)($this->data['id'] ?? 0.0); if (0 === $accountId) { - $accountId = (int) ($parameters[0] ?? 0.0); + $accountId = (int)($parameters[0] ?? 0.0); } $query = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id') @@ -511,7 +530,7 @@ class FireflyValidator extends Validator /** @var AccountMeta $entry */ foreach ($set as $entry) { $otherAccount = $entry->account; - $otherType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $otherAccount->accountType->type)); + $otherType = (string)config(sprintf('firefly.shortNamesByFullName.%s', $otherAccount->accountType->type)); if (('expense' === $otherType || 'revenue' === $otherType) && $otherType !== $type) { app('log')->debug(sprintf('The other account with this account number is a "%s" so return true.', $otherType)); @@ -528,7 +547,7 @@ class FireflyValidator extends Validator */ public function validateUniqueCurrencyCode(null|string $attribute, null|string $value): bool { - return $this->validateUniqueCurrency('code', (string) $attribute, (string) $value); + return $this->validateUniqueCurrency('code', (string)$attribute, (string)$value); } /** @@ -541,12 +560,12 @@ class FireflyValidator extends Validator public function validateUniqueCurrencyName(null|string $attribute, null|string $value): bool { - return $this->validateUniqueCurrency('name', (string) $attribute, (string) $value); + return $this->validateUniqueCurrency('name', (string)$attribute, (string)$value); } public function validateUniqueCurrencySymbol(null|string $attribute, null|string $value): bool { - return $this->validateUniqueCurrency('symbol', (string) $attribute, (string) $value); + return $this->validateUniqueCurrency('symbol', (string)$attribute, (string)$value); } /** @@ -558,7 +577,7 @@ class FireflyValidator extends Validator */ public function validateUniqueExistingWebhook($value, $parameters, $something): bool { - $existingId = (int) ($something[0] ?? 0); + $existingId = (int)($something[0] ?? 0); $trigger = 0; $response = 0; $delivery = 0; @@ -614,15 +633,15 @@ class FireflyValidator extends Validator public function validateUniqueObjectForUser($attribute, $value, $parameters): bool { [$table, $field] = $parameters; - $exclude = (int) ($parameters[2] ?? 0.0); + $exclude = (int)($parameters[2] ?? 0.0); /* * If other data (in $this->getData()) contains * ID field, set that field to be the $exclude. */ $data = $this->getData(); - if (!array_key_exists(2, $parameters) && array_key_exists('id', $data) && (int) $data['id'] > 0) { - $exclude = (int) $data['id']; + if (!array_key_exists(2, $parameters) && array_key_exists('id', $data) && (int)$data['id'] > 0) { + $exclude = (int)$data['id']; } // get entries from table $result = \DB::table($table)->where('user_id', auth()->user()->id)->whereNull('deleted_at') @@ -654,7 +673,7 @@ class FireflyValidator extends Validator ->where('object_groups.title', $value) ; if (null !== $exclude) { - $query->where('object_groups.id', '!=', (int) $exclude); + $query->where('object_groups.id', '!=', (int)$exclude); } return 0 === $query->count(); @@ -674,7 +693,7 @@ class FireflyValidator extends Validator ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('accounts.user_id', auth()->user()->id) ; if (null !== $exclude) { - $query->where('piggy_banks.id', '!=', (int) $exclude); + $query->where('piggy_banks.id', '!=', (int)$exclude); } $query->where('piggy_banks.name', $value); @@ -739,7 +758,7 @@ class FireflyValidator extends Validator } $accountTypes = AccountType::whereIn('type', $search)->get(); - $ignore = (int) ($parameters[0] ?? 0.0); + $ignore = (int)($parameters[0] ?? 0.0); $accountTypeIds = $accountTypes->pluck('id')->toArray(); /** @var null|Account $result */ @@ -758,7 +777,7 @@ class FireflyValidator extends Validator private function validateByAccountTypeId($value, $parameters): bool { $type = AccountType::find($this->data['account_type_id'])->first(); - $ignore = (int) ($parameters[0] ?? 0.0); + $ignore = (int)($parameters[0] ?? 0.0); /** @var null|Account $result */ $result = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore) diff --git a/changelog.md b/changelog.md index 0aa2b0f324..c6cd36d857 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## 6.1.7 - 2024-01-21 + +### Added + +- Layout `v2` has some new features +- [Issue 8369](https://github.com/firefly-iii/firefly-iii/issues/8369) (Additional reconcile link) reported by @chevdor + +### Fixed + +- [Issue 8352](https://github.com/firefly-iii/firefly-iii/issues/8352) (Modifying the direction of a transfer between liabilities yields no effect) reported by @Ezwen +- [Issue 8370](https://github.com/firefly-iii/firefly-iii/pull/8370) (Fix various typos) reported by @luzpaz +- [Issue 8377](https://github.com/firefly-iii/firefly-iii/issues/8377) (Query on multiple tags returns duplicates) reported by @chevdor +- [Issue 8374](https://github.com/firefly-iii/firefly-iii/issues/8374) (Error Graph Income vs. expenses Reports page) reported by @nicolopozzato +- [Issue 8390](https://github.com/firefly-iii/firefly-iii/issues/8390) (Rule with destination_account_is 'not' is never returning a result.) reported by @EricVanCaenenberghe +- [Issue 8349](https://github.com/firefly-iii/firefly-iii/issues/8349) (Currencies not saving correctly) reported by @r1bas4 +- [Issue 8418](https://github.com/firefly-iii/firefly-iii/issues/8418) (Unable to create rule with trigger having type has_no_budget via the API ) reported by @tailg8nj +- [Issue 8425](https://github.com/firefly-iii/firefly-iii/issues/8425) (Error from the net-worth endpoint with `Trailing data`.) reported by @chevdor +- [Issue 8427](https://github.com/firefly-iii/firefly-iii/issues/8427) (Broken batch application of non-strict rules with triggers with stop processing) reported by @alexschlueter +- Various Carbon `createFromFormat` issues fixed. + ## 6.1.6 - 2024-01-07 ### Fixed @@ -54,7 +74,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [Issue 8320](https://github.com/firefly-iii/firefly-iii/issues/8320) nullpointer in new layout - [Issue 8321](https://github.com/firefly-iii/firefly-iii/issues/8321) Networth checkbox for expense and revenue accounts removed - Long date ranges will throw an error -- +- - Max sizes and reasonable limits for most numbers and strings - Links in readme to documentation. @@ -145,7 +165,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - [Issue 8076](https://github.com/firefly-iii/firefly-iii/issues/8076) Added a "Clone and edit"-button -- [Issue 7204](https://github.com/firefly-iii/firefly-iii/issues/7204) Added the ability to customize the URL protocol types Firefly III accepts +- [Issue 7204](https://github.com/firefly-iii/firefly-iii/issues/7204) Added the ability to customize the URL protocol types Firefly III accepts - [Issue 8098](https://github.com/firefly-iii/firefly-iii/issues/8098) More tests in the navigation class, thanks @tonicospinelli! ### Changed diff --git a/composer.json b/composer.json index cb56304420..0fce808942 100644 --- a/composer.json +++ b/composer.json @@ -110,12 +110,13 @@ "therobfonz/laravel-mandrill-driver": "^5.0" }, "require-dev": { + "barryvdh/laravel-debugbar": "^3.9", "barryvdh/laravel-ide-helper": "2.*", "ergebnis/phpstan-rules": "^2.1", "fakerphp/faker": "1.*", "filp/whoops": "2.*", - "mockery/mockery": "1.*", "larastan/larastan": "^2", + "mockery/mockery": "1.*", "phpstan/extension-installer": "^1.3", "phpstan/phpstan": "^1.10", "phpstan/phpstan-deprecation-rules": "^1.1", diff --git a/composer.lock b/composer.lock index c8f5f75f3d..365f2f0182 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "84061ba3494e12bf5871a59c5a4744d4", + "content-hash": "a290b73e0cc243be7afa30409d4069a4", "packages": [ { "name": "bacon/bacon-qr-code", @@ -793,16 +793,16 @@ }, { "name": "doctrine/inflector", - "version": "2.0.8", + "version": "2.0.9", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff" + "reference": "2930cd5ef353871c821d5c43ed030d39ac8cfe65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/f9301a5b2fb1216b2b08f02ba04dc45423db6bff", - "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/2930cd5ef353871c821d5c43ed030d39ac8cfe65", + "reference": "2930cd5ef353871c821d5c43ed030d39ac8cfe65", "shasum": "" }, "require": { @@ -864,7 +864,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.8" + "source": "https://github.com/doctrine/inflector/tree/2.0.9" }, "funding": [ { @@ -880,7 +880,7 @@ "type": "tidelift" } ], - "time": "2023-06-16T13:40:37+00:00" + "time": "2024-01-15T18:05:13+00:00" }, { "name": "doctrine/lexer", @@ -2014,16 +2014,16 @@ }, { "name": "laravel/framework", - "version": "v10.39.0", + "version": "v10.41.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "114926b07bfb5fbf2545c03aa2ce5c8c37be650c" + "reference": "da31969bd35e6ee0bbcd9e876f88952dc754b012" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/114926b07bfb5fbf2545c03aa2ce5c8c37be650c", - "reference": "114926b07bfb5fbf2545c03aa2ce5c8c37be650c", + "url": "https://api.github.com/repos/laravel/framework/zipball/da31969bd35e6ee0bbcd9e876f88952dc754b012", + "reference": "da31969bd35e6ee0bbcd9e876f88952dc754b012", "shasum": "" }, "require": { @@ -2215,20 +2215,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-12-27T14:26:28+00:00" + "time": "2024-01-16T15:23:58+00:00" }, { "name": "laravel/passport", - "version": "v11.10.0", + "version": "v11.10.1", "source": { "type": "git", "url": "https://github.com/laravel/passport.git", - "reference": "966bc8e477d08c86a11dc4c5a86f85fa0abdb89b" + "reference": "e1a651481cabff0ba174aaefbdc04a59e6a096ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/passport/zipball/966bc8e477d08c86a11dc4c5a86f85fa0abdb89b", - "reference": "966bc8e477d08c86a11dc4c5a86f85fa0abdb89b", + "url": "https://api.github.com/repos/laravel/passport/zipball/e1a651481cabff0ba174aaefbdc04a59e6a096ec", + "reference": "e1a651481cabff0ba174aaefbdc04a59e6a096ec", "shasum": "" }, "require": { @@ -2293,20 +2293,20 @@ "issues": "https://github.com/laravel/passport/issues", "source": "https://github.com/laravel/passport" }, - "time": "2023-11-02T17:16:12+00:00" + "time": "2024-01-10T14:44:24+00:00" }, { "name": "laravel/prompts", - "version": "v0.1.14", + "version": "v0.1.15", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "2219fa9c4b944add1e825c3bdb8ecae8bc503bc6" + "reference": "d814a27514d99b03c85aa42b22cfd946568636c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/2219fa9c4b944add1e825c3bdb8ecae8bc503bc6", - "reference": "2219fa9c4b944add1e825c3bdb8ecae8bc503bc6", + "url": "https://api.github.com/repos/laravel/prompts/zipball/d814a27514d99b03c85aa42b22cfd946568636c1", + "reference": "d814a27514d99b03c85aa42b22cfd946568636c1", "shasum": "" }, "require": { @@ -2348,9 +2348,9 @@ ], "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.1.14" + "source": "https://github.com/laravel/prompts/tree/v0.1.15" }, - "time": "2023-12-27T04:18:09+00:00" + "time": "2023-12-29T22:37:42+00:00" }, { "name": "laravel/sanctum", @@ -2480,30 +2480,30 @@ }, { "name": "laravel/slack-notification-channel", - "version": "v3.1.0", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/laravel/slack-notification-channel.git", - "reference": "c74265319b1d0ca710771d6c708558553972e1d6" + "reference": "fc8d1873e3db63a480bc57aebb4bf5ec05332d91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/c74265319b1d0ca710771d6c708558553972e1d6", - "reference": "c74265319b1d0ca710771d6c708558553972e1d6", + "url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/fc8d1873e3db63a480bc57aebb4bf5ec05332d91", + "reference": "fc8d1873e3db63a480bc57aebb4bf5ec05332d91", "shasum": "" }, "require": { "guzzlehttp/guzzle": "^7.0", - "illuminate/http": "^9.0|^10.0", - "illuminate/notifications": "^9.0|^10.0", - "illuminate/support": "^9.0|^10.0", + "illuminate/http": "^9.0|^10.0|^11.0", + "illuminate/notifications": "^9.0|^10.0|^11.0", + "illuminate/support": "^9.0|^10.0|^11.0", "php": "^8.0" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^7.0|^8.0", + "orchestra/testbench": "^7.0|^8.0|^9.0", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^9.0|^10.4" }, "type": "library", "extra": { @@ -2539,34 +2539,34 @@ ], "support": { "issues": "https://github.com/laravel/slack-notification-channel/issues", - "source": "https://github.com/laravel/slack-notification-channel/tree/v3.1.0" + "source": "https://github.com/laravel/slack-notification-channel/tree/v3.2.0" }, - "time": "2023-10-30T17:52:13+00:00" + "time": "2024-01-15T20:07:45+00:00" }, { "name": "laravel/ui", - "version": "v4.3.0", + "version": "v4.4.0", "source": { "type": "git", "url": "https://github.com/laravel/ui.git", - "reference": "d166e09cdcb2e23836f694774cba77a32edb6007" + "reference": "7335d7049b2cde345c029e9d2de839b80af62bc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/ui/zipball/d166e09cdcb2e23836f694774cba77a32edb6007", - "reference": "d166e09cdcb2e23836f694774cba77a32edb6007", + "url": "https://api.github.com/repos/laravel/ui/zipball/7335d7049b2cde345c029e9d2de839b80af62bc0", + "reference": "7335d7049b2cde345c029e9d2de839b80af62bc0", "shasum": "" }, "require": { - "illuminate/console": "^9.21|^10.0", - "illuminate/filesystem": "^9.21|^10.0", - "illuminate/support": "^9.21|^10.0", - "illuminate/validation": "^9.21|^10.0", + "illuminate/console": "^9.21|^10.0|^11.0", + "illuminate/filesystem": "^9.21|^10.0|^11.0", + "illuminate/support": "^9.21|^10.0|^11.0", + "illuminate/validation": "^9.21|^10.0|^11.0", "php": "^8.0" }, "require-dev": { - "orchestra/testbench": "^7.0|^8.0", - "phpunit/phpunit": "^9.3" + "orchestra/testbench": "^7.0|^8.0|^9.0", + "phpunit/phpunit": "^9.3|^10.4" }, "type": "library", "extra": { @@ -2601,9 +2601,9 @@ "ui" ], "support": { - "source": "https://github.com/laravel/ui/tree/v4.3.0" + "source": "https://github.com/laravel/ui/tree/v4.4.0" }, - "time": "2023-12-19T14:46:09+00:00" + "time": "2024-01-12T15:56:45+00:00" }, { "name": "lcobucci/clock", @@ -3937,16 +3937,16 @@ }, { "name": "nette/utils", - "version": "v4.0.3", + "version": "v4.0.4", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015" + "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015", - "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015", + "url": "https://api.github.com/repos/nette/utils/zipball/d3ad0aa3b9f934602cb3e3902ebccf10be34d218", + "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218", "shasum": "" }, "require": { @@ -4017,9 +4017,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.3" + "source": "https://github.com/nette/utils/tree/v4.0.4" }, - "time": "2023-10-29T21:02:13+00:00" + "time": "2024-01-17T16:50:36+00:00" }, { "name": "nunomaduro/collision", @@ -5882,16 +5882,16 @@ }, { "name": "spatie/laravel-ignition", - "version": "2.4.0", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "b9395ba48d3f30d42092cf6ceff75ed7256cd604" + "reference": "005e1e7b1232f3b22d7e7be3f602693efc7dba67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/b9395ba48d3f30d42092cf6ceff75ed7256cd604", - "reference": "b9395ba48d3f30d42092cf6ceff75ed7256cd604", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/005e1e7b1232f3b22d7e7be3f602693efc7dba67", + "reference": "005e1e7b1232f3b22d7e7be3f602693efc7dba67", "shasum": "" }, "require": { @@ -5970,7 +5970,7 @@ "type": "github" } ], - "time": "2024-01-04T14:51:24+00:00" + "time": "2024-01-12T13:14:58+00:00" }, { "name": "spatie/period", @@ -9016,6 +9016,90 @@ } ], "packages-dev": [ + { + "name": "barryvdh/laravel-debugbar", + "version": "v3.9.2", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-debugbar.git", + "reference": "bfd0131c146973cab164e50f5cdd8a67cc60cab1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/bfd0131c146973cab164e50f5cdd8a67cc60cab1", + "reference": "bfd0131c146973cab164e50f5cdd8a67cc60cab1", + "shasum": "" + }, + "require": { + "illuminate/routing": "^9|^10", + "illuminate/session": "^9|^10", + "illuminate/support": "^9|^10", + "maximebf/debugbar": "^1.18.2", + "php": "^8.0", + "symfony/finder": "^6" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "orchestra/testbench-dusk": "^5|^6|^7|^8", + "phpunit/phpunit": "^8.5.30|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.8-dev" + }, + "laravel": { + "providers": [ + "Barryvdh\\Debugbar\\ServiceProvider" + ], + "aliases": { + "Debugbar": "Barryvdh\\Debugbar\\Facades\\Debugbar" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Barryvdh\\Debugbar\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "PHP Debugbar integration for Laravel", + "keywords": [ + "debug", + "debugbar", + "laravel", + "profiler", + "webprofiler" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-debugbar/issues", + "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.9.2" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-08-25T18:43:57+00:00" + }, { "name": "barryvdh/laravel-ide-helper", "version": "v2.13.0", @@ -9494,16 +9578,16 @@ }, { "name": "larastan/larastan", - "version": "v2.8.0", + "version": "v2.8.1", "source": { "type": "git", "url": "https://github.com/larastan/larastan.git", - "reference": "d60c1a6d49fcbb54b78922a955a55820abdbe3c7" + "reference": "b7cc6a29c457a7d4f3de90466392ae9ad3e17022" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/larastan/larastan/zipball/d60c1a6d49fcbb54b78922a955a55820abdbe3c7", - "reference": "d60c1a6d49fcbb54b78922a955a55820abdbe3c7", + "url": "https://api.github.com/repos/larastan/larastan/zipball/b7cc6a29c457a7d4f3de90466392ae9ad3e17022", + "reference": "b7cc6a29c457a7d4f3de90466392ae9ad3e17022", "shasum": "" }, "require": { @@ -9571,7 +9655,7 @@ ], "support": { "issues": "https://github.com/larastan/larastan/issues", - "source": "https://github.com/larastan/larastan/tree/v2.8.0" + "source": "https://github.com/larastan/larastan/tree/v2.8.1" }, "funding": [ { @@ -9591,7 +9675,73 @@ "type": "patreon" } ], - "time": "2024-01-02T22:09:07+00:00" + "time": "2024-01-08T09:11:17+00:00" + }, + { + "name": "maximebf/debugbar", + "version": "v1.19.1", + "source": { + "type": "git", + "url": "https://github.com/maximebf/php-debugbar.git", + "reference": "03dd40a1826f4d585ef93ef83afa2a9874a00523" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/03dd40a1826f4d585ef93ef83afa2a9874a00523", + "reference": "03dd40a1826f4d585ef93ef83afa2a9874a00523", + "shasum": "" + }, + "require": { + "php": "^7.1|^8", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^4|^5|^6" + }, + "require-dev": { + "phpunit/phpunit": ">=7.5.20 <10.0", + "twig/twig": "^1.38|^2.7|^3.0" + }, + "suggest": { + "kriswallsmith/assetic": "The best way to manage assets", + "monolog/monolog": "Log using Monolog", + "predis/predis": "Redis storage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.18-dev" + } + }, + "autoload": { + "psr-4": { + "DebugBar\\": "src/DebugBar/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maxime Bouroumeau-Fuseau", + "email": "maxime.bouroumeau@gmail.com", + "homepage": "http://maximebf.com" + }, + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Debug bar in the browser for php application", + "homepage": "https://github.com/maximebf/php-debugbar", + "keywords": [ + "debug", + "debugbar" + ], + "support": { + "issues": "https://github.com/maximebf/php-debugbar/issues", + "source": "https://github.com/maximebf/php-debugbar/tree/v1.19.1" + }, + "time": "2023-10-12T08:10:52+00:00" }, { "name": "mockery/mockery", @@ -9957,16 +10107,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.7.3", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" + "reference": "fad452781b3d774e3337b0c0b245dd8e5a4455fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/fad452781b3d774e3337b0c0b245dd8e5a4455fc", + "reference": "fad452781b3d774e3337b0c0b245dd8e5a4455fc", "shasum": "" }, "require": { @@ -10009,9 +10159,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.0" }, - "time": "2023-08-12T11:01:26+00:00" + "time": "2024-01-11T11:49:22+00:00" }, { "name": "phpmyadmin/sql-parser", @@ -10193,16 +10343,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.54", + "version": "1.10.56", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "3e25f279dada0adc14ffd7bad09af2e2fc3523bb" + "reference": "27816a01aea996191ee14d010f325434c0ee76fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3e25f279dada0adc14ffd7bad09af2e2fc3523bb", - "reference": "3e25f279dada0adc14ffd7bad09af2e2fc3523bb", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/27816a01aea996191ee14d010f325434c0ee76fa", + "reference": "27816a01aea996191ee14d010f325434c0ee76fa", "shasum": "" }, "require": { @@ -10251,7 +10401,7 @@ "type": "tidelift" } ], - "time": "2024-01-05T15:50:47+00:00" + "time": "2024-01-15T10:43:00+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -10673,16 +10823,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.5", + "version": "10.5.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "ed21115d505b4b4f7dc7b5651464e19a2c7f7856" + "reference": "08f4fa74d5fbfff1ef22abffee47aaedcaea227e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ed21115d505b4b4f7dc7b5651464e19a2c7f7856", - "reference": "ed21115d505b4b4f7dc7b5651464e19a2c7f7856", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/08f4fa74d5fbfff1ef22abffee47aaedcaea227e", + "reference": "08f4fa74d5fbfff1ef22abffee47aaedcaea227e", "shasum": "" }, "require": { @@ -10754,7 +10904,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.5" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.8" }, "funding": [ { @@ -10770,7 +10920,7 @@ "type": "tidelift" } ], - "time": "2023-12-27T15:13:52+00:00" + "time": "2024-01-19T07:07:27+00:00" }, { "name": "sebastian/cli-parser", diff --git a/config/app.php b/config/app.php index 8f179f6221..0b14d37b1b 100644 --- a/config/app.php +++ b/config/app.php @@ -21,6 +21,25 @@ declare(strict_types=1); +use FireflyIII\Providers\AccountServiceProvider; +use FireflyIII\Providers\AdminServiceProvider; +use FireflyIII\Providers\AppServiceProvider; +use FireflyIII\Providers\AttachmentServiceProvider; +use FireflyIII\Providers\BillServiceProvider; +use FireflyIII\Providers\BudgetServiceProvider; +use FireflyIII\Providers\CategoryServiceProvider; +use FireflyIII\Providers\CurrencyServiceProvider; +use FireflyIII\Providers\EventServiceProvider; +use FireflyIII\Providers\FireflyServiceProvider; +use FireflyIII\Providers\JournalServiceProvider; +use FireflyIII\Providers\PiggyBankServiceProvider; +use FireflyIII\Providers\RecurringServiceProvider; +use FireflyIII\Providers\RouteServiceProvider; +use FireflyIII\Providers\RuleGroupServiceProvider; +use FireflyIII\Providers\RuleServiceProvider; +use FireflyIII\Providers\SearchServiceProvider; +use FireflyIII\Providers\SessionServiceProvider; +use FireflyIII\Providers\TagServiceProvider; use FireflyIII\Support\Facades\AccountForm; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\CurrencyForm; @@ -31,6 +50,67 @@ use FireflyIII\Support\Facades\PiggyBankForm; use FireflyIII\Support\Facades\Preferences; use FireflyIII\Support\Facades\RuleForm; use FireflyIII\Support\Facades\Steam; +use Illuminate\Auth\AuthServiceProvider; +use Illuminate\Auth\Passwords\PasswordResetServiceProvider; +use Illuminate\Broadcasting\BroadcastServiceProvider; +use Illuminate\Bus\BusServiceProvider; +use Illuminate\Cache\CacheServiceProvider; +use Illuminate\Cookie\CookieServiceProvider; +use Illuminate\Database\DatabaseServiceProvider; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Encryption\EncryptionServiceProvider; +use Illuminate\Filesystem\FilesystemServiceProvider; +use Illuminate\Foundation\Providers\ConsoleSupportServiceProvider; +use Illuminate\Foundation\Providers\FoundationServiceProvider; +use Illuminate\Hashing\HashServiceProvider; +use Illuminate\Mail\MailServiceProvider; +use Illuminate\Notifications\NotificationServiceProvider; +use Illuminate\Pagination\PaginationServiceProvider; +use Illuminate\Pipeline\PipelineServiceProvider; +use Illuminate\Queue\QueueServiceProvider; +use Illuminate\Redis\RedisServiceProvider; +use Illuminate\Support\Arr; +use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Blade; +use Illuminate\Support\Facades\Broadcast; +use Illuminate\Support\Facades\Bus; +use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Config; +use Illuminate\Support\Facades\Cookie; +use Illuminate\Support\Facades\Crypt; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Event; +use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\Gate; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Lang; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\Notification; +use Illuminate\Support\Facades\Password; +use Illuminate\Support\Facades\Queue; +use Illuminate\Support\Facades\Redirect; +use Illuminate\Support\Facades\Redis; +use Illuminate\Support\Facades\Request; +use Illuminate\Support\Facades\Response; +use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\Schema; +use Illuminate\Support\Facades\Session; +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\URL; +use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Facades\View; +use Illuminate\Support\Str; +use Illuminate\Translation\TranslationServiceProvider; +use Illuminate\Validation\ValidationServiceProvider; +use Illuminate\View\ViewServiceProvider; +use PragmaRX\Google2FALaravel\Facade; +use Spatie\Html\Facades\Html; +use TwigBridge\Facade\Twig; +use TwigBridge\ServiceProvider; return [ 'name' => envNonEmpty('APP_NAME', 'Firefly III'), @@ -44,94 +124,94 @@ return [ 'cipher' => 'AES-256-CBC', 'providers' => [ // Laravel Framework Service Providers... - Illuminate\Auth\AuthServiceProvider::class, - Illuminate\Broadcasting\BroadcastServiceProvider::class, - Illuminate\Bus\BusServiceProvider::class, - Illuminate\Cache\CacheServiceProvider::class, - Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, - Illuminate\Cookie\CookieServiceProvider::class, - Illuminate\Database\DatabaseServiceProvider::class, - Illuminate\Encryption\EncryptionServiceProvider::class, - Illuminate\Filesystem\FilesystemServiceProvider::class, - Illuminate\Foundation\Providers\FoundationServiceProvider::class, - Illuminate\Hashing\HashServiceProvider::class, - Illuminate\Mail\MailServiceProvider::class, - Illuminate\Notifications\NotificationServiceProvider::class, - Illuminate\Pagination\PaginationServiceProvider::class, - Illuminate\Pipeline\PipelineServiceProvider::class, - Illuminate\Queue\QueueServiceProvider::class, - Illuminate\Redis\RedisServiceProvider::class, - Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, - FireflyIII\Providers\SessionServiceProvider::class, - Illuminate\Translation\TranslationServiceProvider::class, - Illuminate\Validation\ValidationServiceProvider::class, - Illuminate\View\ViewServiceProvider::class, + AuthServiceProvider::class, + BroadcastServiceProvider::class, + BusServiceProvider::class, + CacheServiceProvider::class, + ConsoleSupportServiceProvider::class, + CookieServiceProvider::class, + DatabaseServiceProvider::class, + EncryptionServiceProvider::class, + FilesystemServiceProvider::class, + FoundationServiceProvider::class, + HashServiceProvider::class, + MailServiceProvider::class, + NotificationServiceProvider::class, + PaginationServiceProvider::class, + PipelineServiceProvider::class, + QueueServiceProvider::class, + RedisServiceProvider::class, + PasswordResetServiceProvider::class, + SessionServiceProvider::class, + TranslationServiceProvider::class, + ValidationServiceProvider::class, + ViewServiceProvider::class, // Package Service Providers... // Application Service Providers... - FireflyIII\Providers\AppServiceProvider::class, + AppServiceProvider::class, FireflyIII\Providers\AuthServiceProvider::class, // FireflyIII\Providers\BroadcastServiceProvider::class, - FireflyIII\Providers\EventServiceProvider::class, - FireflyIII\Providers\RouteServiceProvider::class, + EventServiceProvider::class, + RouteServiceProvider::class, // own stuff: PragmaRX\Google2FALaravel\ServiceProvider::class, - TwigBridge\ServiceProvider::class, + ServiceProvider::class, // More service providers. - FireflyIII\Providers\AccountServiceProvider::class, - FireflyIII\Providers\AttachmentServiceProvider::class, - FireflyIII\Providers\BillServiceProvider::class, - FireflyIII\Providers\BudgetServiceProvider::class, - FireflyIII\Providers\CategoryServiceProvider::class, - FireflyIII\Providers\CurrencyServiceProvider::class, - FireflyIII\Providers\FireflyServiceProvider::class, - FireflyIII\Providers\JournalServiceProvider::class, - FireflyIII\Providers\PiggyBankServiceProvider::class, - FireflyIII\Providers\RuleServiceProvider::class, - FireflyIII\Providers\RuleGroupServiceProvider::class, - FireflyIII\Providers\SearchServiceProvider::class, - FireflyIII\Providers\TagServiceProvider::class, - FireflyIII\Providers\AdminServiceProvider::class, - FireflyIII\Providers\RecurringServiceProvider::class, + AccountServiceProvider::class, + AttachmentServiceProvider::class, + BillServiceProvider::class, + BudgetServiceProvider::class, + CategoryServiceProvider::class, + CurrencyServiceProvider::class, + FireflyServiceProvider::class, + JournalServiceProvider::class, + PiggyBankServiceProvider::class, + RuleServiceProvider::class, + RuleGroupServiceProvider::class, + SearchServiceProvider::class, + TagServiceProvider::class, + AdminServiceProvider::class, + RecurringServiceProvider::class, ], 'aliases' => [ - 'App' => Illuminate\Support\Facades\App::class, - 'Artisan' => Illuminate\Support\Facades\Artisan::class, - 'Auth' => Illuminate\Support\Facades\Auth::class, - 'Blade' => Illuminate\Support\Facades\Blade::class, - 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, - 'Bus' => Illuminate\Support\Facades\Bus::class, - 'Cache' => Illuminate\Support\Facades\Cache::class, - 'Config' => Illuminate\Support\Facades\Config::class, - 'Cookie' => Illuminate\Support\Facades\Cookie::class, - 'Crypt' => Illuminate\Support\Facades\Crypt::class, - 'DB' => Illuminate\Support\Facades\DB::class, - 'Eloquent' => Illuminate\Database\Eloquent\Model::class, - 'Event' => Illuminate\Support\Facades\Event::class, - 'File' => Illuminate\Support\Facades\File::class, - 'Gate' => Illuminate\Support\Facades\Gate::class, - 'Hash' => Illuminate\Support\Facades\Hash::class, - 'Lang' => Illuminate\Support\Facades\Lang::class, - 'Log' => Illuminate\Support\Facades\Log::class, - 'Mail' => Illuminate\Support\Facades\Mail::class, - 'Notification' => Illuminate\Support\Facades\Notification::class, - 'Password' => Illuminate\Support\Facades\Password::class, - 'Queue' => Illuminate\Support\Facades\Queue::class, - 'Redirect' => Illuminate\Support\Facades\Redirect::class, - 'Redis' => Illuminate\Support\Facades\Redis::class, - 'Request' => Illuminate\Support\Facades\Request::class, - 'Response' => Illuminate\Support\Facades\Response::class, - 'Route' => Illuminate\Support\Facades\Route::class, - 'Schema' => Illuminate\Support\Facades\Schema::class, - 'Session' => Illuminate\Support\Facades\Session::class, - 'Storage' => Illuminate\Support\Facades\Storage::class, - 'URL' => Illuminate\Support\Facades\URL::class, - 'Validator' => Illuminate\Support\Facades\Validator::class, - 'View' => Illuminate\Support\Facades\View::class, - 'Html' => Spatie\Html\Facades\Html::class, + 'App' => App::class, + 'Artisan' => Artisan::class, + 'Auth' => Auth::class, + 'Blade' => Blade::class, + 'Broadcast' => Broadcast::class, + 'Bus' => Bus::class, + 'Cache' => Cache::class, + 'Config' => Config::class, + 'Cookie' => Cookie::class, + 'Crypt' => Crypt::class, + 'DB' => DB::class, + 'Eloquent' => Model::class, + 'Event' => Event::class, + 'File' => File::class, + 'Gate' => Gate::class, + 'Hash' => Hash::class, + 'Lang' => Lang::class, + 'Log' => Log::class, + 'Mail' => Mail::class, + 'Notification' => Notification::class, + 'Password' => Password::class, + 'Queue' => Queue::class, + 'Redirect' => Redirect::class, + 'Redis' => Redis::class, + 'Request' => Request::class, + 'Response' => Response::class, + 'Route' => Route::class, + 'Schema' => Schema::class, + 'Session' => Session::class, + 'Storage' => Storage::class, + 'URL' => URL::class, + 'Validator' => Validator::class, + 'View' => View::class, + 'Html' => Html::class, 'Preferences' => Preferences::class, 'FireflyConfig' => FireflyConfig::class, 'Navigation' => Navigation::class, @@ -142,12 +222,12 @@ return [ 'AccountForm' => AccountForm::class, 'PiggyBankForm' => PiggyBankForm::class, 'RuleForm' => RuleForm::class, - 'Google2FA' => PragmaRX\Google2FALaravel\Facade::class, - 'Twig' => TwigBridge\Facade\Twig::class, + 'Google2FA' => Facade::class, + 'Twig' => Twig::class, - 'Arr' => Illuminate\Support\Arr::class, - 'Http' => Illuminate\Support\Facades\Http::class, - 'Str' => Illuminate\Support\Str::class, + 'Arr' => Arr::class, + 'Http' => Http::class, + 'Str' => Str::class, ], 'asset_url' => env('ASSET_URL', null), diff --git a/config/auth.php b/config/auth.php index 535b97ab5e..523a326e2f 100644 --- a/config/auth.php +++ b/config/auth.php @@ -20,6 +20,7 @@ */ declare(strict_types=1); +use FireflyIII\User; if ('ldap' === strtolower((string)env('AUTHENTICATION_GUARD'))) { exit('LDAP is no longer supported by Firefly III v5.7+. Sorry about that. You will have to switch to "remote_user_guard", and use tools like Authelia or Keycloak to use LDAP together with Firefly III.'); @@ -96,11 +97,11 @@ return [ 'providers' => [ 'users' => [ 'driver' => 'eloquent', - 'model' => FireflyIII\User::class, + 'model' => User::class, ], 'remote_user_provider' => [ 'driver' => 'remote_user_provider', - 'model' => FireflyIII\User::class, + 'model' => User::class, ], ], diff --git a/config/breadcrumbs.php b/config/breadcrumbs.php index 097cdd92e8..d725919eb6 100644 --- a/config/breadcrumbs.php +++ b/config/breadcrumbs.php @@ -21,6 +21,8 @@ */ declare(strict_types=1); +use Diglactic\Breadcrumbs\Generator; +use Diglactic\Breadcrumbs\Manager; return [ /* @@ -90,8 +92,8 @@ return [ */ // Manager - 'manager-class' => Diglactic\Breadcrumbs\Manager::class, + 'manager-class' => Manager::class, // Generator - 'generator-class' => Diglactic\Breadcrumbs\Generator::class, + 'generator-class' => Generator::class, ]; diff --git a/config/debugbar.php b/config/debugbar.php deleted file mode 100644 index 8260a9d88b..0000000000 --- a/config/debugbar.php +++ /dev/null @@ -1,223 +0,0 @@ -. - */ - -declare(strict_types=1); - -return [ - /* - |-------------------------------------------------------------------------- - | Debugbar Settings - |-------------------------------------------------------------------------- - | - | Debugbar is enabled by default, when debug is set to true in app.php. - | You can override the value by setting enable to true or false instead of null. - | - | You can provide an array of URI's that must be ignored (eg. 'api/*') - | - */ - - 'enabled' => env('DEBUGBAR_ENABLED', null), - 'except' => [ - 'telescope*', - ], - - /* - |-------------------------------------------------------------------------- - | Storage settings - |-------------------------------------------------------------------------- - | - | DebugBar stores data for session/ajax requests. - | You can disable this, so the debugbar stores data in headers/session, - | but this can cause problems with large data collectors. - | By default, file storage (in the storage folder) is used. Redis and PDO - | can also be used. For PDO, run the package migrations first. - | - */ - 'storage' => [ - 'enabled' => true, - 'driver' => 'file', // redis, file, pdo, custom - 'path' => storage_path('debugbar'), // For file driver - 'connection' => null, // Leave null for default connection (Redis/PDO) - 'provider' => '', // Instance of StorageInterface for custom driver - ], - - /* - |-------------------------------------------------------------------------- - | Vendors - |-------------------------------------------------------------------------- - | - | Vendor files are included by default, but can be set to false. - | This can also be set to 'js' or 'css', to only include javascript or css vendor files. - | Vendor files are for css: font-awesome (including fonts) and highlight.js (css files) - | and for js: jquery and and highlight.js - | So if you want syntax highlighting, set it to true. - | jQuery is set to not conflict with existing jQuery scripts. - | - */ - - 'include_vendors' => true, - - /* - |-------------------------------------------------------------------------- - | Capture Ajax Requests - |-------------------------------------------------------------------------- - | - | The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors), - | you can use this option to disable sending the data through the headers. - | - | Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools. - */ - - 'capture_ajax' => true, - 'add_ajax_timing' => false, - - /* - |-------------------------------------------------------------------------- - | Custom Error Handler for Deprecated warnings - |-------------------------------------------------------------------------- - | - | When enabled, the Debugbar shows deprecated warnings for Symfony components - | in the Messages tab. - | - */ - 'error_handler' => true, - - /* - |-------------------------------------------------------------------------- - | Clockwork integration - |-------------------------------------------------------------------------- - | - | The Debugbar can emulate the Clockwork headers, so you can use the Chrome - | Extension, without the server-side code. It uses Debugbar collectors instead. - | - */ - 'clockwork' => false, - - /* - |-------------------------------------------------------------------------- - | DataCollectors - |-------------------------------------------------------------------------- - | - | Enable/disable DataCollectors - | - */ - - 'collectors' => [ - 'phpinfo' => true, // Php version - 'messages' => true, // Messages - 'time' => true, // Time Datalogger - 'memory' => true, // Memory usage - 'exceptions' => true, // Exception displayer - 'log' => true, // Logs from Monolog (merged in messages if enabled) - 'db' => true, // Show database (PDO) queries and bindings - 'views' => true, // Views with their data - 'route' => true, // Current route information - 'auth' => true, // Display Laravel authentication status - 'gate' => true, // Display Laravel Gate checks - 'session' => true, // Display session data - 'symfony_request' => true, // Only one can be enabled.. - 'mail' => true, // Catch mail messages - 'laravel' => false, // Laravel version and environment - 'events' => true, // All events fired - 'default_request' => false, // Regular or special Symfony request logger - 'logs' => false, // Add the latest log messages - 'files' => false, // Show the included files - 'config' => false, // Display config settings - 'cache' => true, // Display cache events - 'models' => false, // Display models - ], - - /* - |-------------------------------------------------------------------------- - | Extra options - |-------------------------------------------------------------------------- - | - | Configure some DataCollectors - | - */ - - 'options' => [ - 'auth' => [ - 'show_name' => true, // Also show the users name/email in the debugbar - ], - 'db' => [ - 'with_params' => true, // Render SQL with the parameters substituted - 'backtrace' => true, // Use a backtrace to find the origin of the query in your files. - 'timeline' => false, // Add the queries to the timeline - 'explain' => [ // Show EXPLAIN output on queries - 'enabled' => false, - 'types' => ['SELECT'], - // // workaround ['SELECT'] only. https://github.com/barryvdh/laravel-debugbar/issues/888 ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ - ], - 'hints' => true, // Show hints for common mistakes - ], - 'mail' => [ - 'full_log' => false, - ], - 'views' => [ - 'data' => true, // Note: Can slow down the application, because the data can be quite large.. - ], - 'route' => [ - 'label' => true, // show complete route on bar - ], - 'logs' => [ - 'file' => null, - ], - 'cache' => [ - 'values' => true, // collect cache values - ], - ], - - /* - |-------------------------------------------------------------------------- - | Inject Debugbar in Response - |-------------------------------------------------------------------------- - | - | Usually, the debugbar is added just before , by listening to the - | Response after the App is done. If you disable this, you have to add them - | in your template yourself. See http://phpdebugbar.com/docs/rendering.html - | - */ - - 'inject' => true, - - /* - |-------------------------------------------------------------------------- - | DebugBar route prefix - |-------------------------------------------------------------------------- - | - | Sometimes you want to set route prefix to be used by DebugBar to load - | its resources from. Usually the need comes from misconfigured web server or - | from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97 - | - */ - 'route_prefix' => '_debugbar', - - /* - |-------------------------------------------------------------------------- - | DebugBar route domain - |-------------------------------------------------------------------------- - | - | By default DebugBar route served from the same domain that request served. - | To override default domain, specify it as a non-empty value. - */ - 'route_domain' => null, -]; diff --git a/config/firefly.php b/config/firefly.php index bc1bbfb35c..31ddb135bf 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -115,7 +115,7 @@ return [ 'handle_debts' => true, // see cer.php for exchange rates feature flag. ], - 'version' => '6.1.6', + 'version' => '6.1.7', 'api_version' => '2.0.12', 'db_version' => 22, diff --git a/config/search.php b/config/search.php index dbbfce291e..021da929f3 100644 --- a/config/search.php +++ b/config/search.php @@ -132,6 +132,7 @@ return [ 'has_any_tag' => ['alias' => false, 'needs_context' => false], 'any_notes' => ['alias' => false, 'needs_context' => false], 'has_any_notes' => ['alias' => true, 'alias_for' => 'any_notes', 'needs_context' => false], + 'has_notes' => ['alias' => true, 'alias_for' => 'any_notes', 'needs_context' => false], 'any_external_url' => ['alias' => false, 'needs_context' => false], 'has_any_external_url' => ['alias' => true, 'alias_for' => 'any_external_url', 'needs_context' => false], 'has_no_attachments' => ['alias' => false, 'needs_context' => false], diff --git a/config/services.php b/config/services.php index 24825d4f2c..382f69a808 100644 --- a/config/services.php +++ b/config/services.php @@ -20,6 +20,7 @@ */ declare(strict_types=1); +use FireflyIII\User; return [ /* @@ -51,7 +52,7 @@ return [ ], 'stripe' => [ - 'model' => FireflyIII\User::class, + 'model' => User::class, 'key' => env('STRIPE_KEY'), 'secret' => env('STRIPE_SECRET'), ], diff --git a/package-lock.json b/package-lock.json index 67e6d015bf..fefd80621a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,11 @@ "chart.js": "^4.4.0", "chartjs-adapter-date-fns": "^3.0.0", "chartjs-chart-sankey": "^0.12.0", - "date-fns": "^3.0.6", - "i18n-js": "^4.3.2", + "date-fns": "^3.2.0", + "i18next": "^23.7.16", + "i18next-chained-backend": "^4.6.2", + "i18next-http-backend": "^2.4.2", + "i18next-localstorage-backend": "^4.2.0", "leaflet": "^1.9.4", "store": "^2.0.12" }, @@ -27,6 +30,17 @@ "vite-plugin-manifest-sri": "^0.1.0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -453,14 +467,6 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "engines": { - "node": "*" - } - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -577,10 +583,18 @@ "node": ">= 0.8" } }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, "node_modules/date-fns": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.1.0.tgz", - "integrity": "sha512-ZO7yefXV/wCWzd3I9haCHmfzlfA3i1a2HHO7ZXjtJrRjXt8FULKJ2Vl8wji3XYF4dQ0ZJ/tokXDZeYlFvgms9Q==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.0.tgz", + "integrity": "sha512-xuouT0GuI2W8yXhCMn/AXbSl1Av3wu2hJXxMnnILTY3bYY0UgNK0qOwVXqdFBrobW5qbX1TuOTgMw7c2H2OuhA==", "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" @@ -645,9 +659,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "dev": true, "funding": [ { @@ -704,14 +718,50 @@ "node": ">= 6" } }, - "node_modules/i18n-js": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-4.3.2.tgz", - "integrity": "sha512-n8gbEbQEueym2/q2yrZk5/xKWjFcKtg3/Escw4JHSVWa8qtKqP8j7se3UjkRbHlO/REqFA0V/MG1q8tEfyHeOA==", + "node_modules/i18next": { + "version": "23.7.17", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.7.17.tgz", + "integrity": "sha512-Wh3mNWeVlwx5HrDSPeJNUYZUrEwFbqykjy80ktIbgRKz41P51wRNyMfXSf5fICyHNJX4O/4XWbecFkprfS8G6w==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], "dependencies": { - "bignumber.js": "*", - "lodash": "*", - "make-plural": "*" + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-chained-backend": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/i18next-chained-backend/-/i18next-chained-backend-4.6.2.tgz", + "integrity": "sha512-2P092fR+nAPQlGzPUoIIxbwo7PTBqQYgLxwv1XhSTQUAUoelLo5LkX+FqRxxSDg9WEAsrc8+2WL6mJtMGIa6WQ==", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-http-backend": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.4.2.tgz", + "integrity": "sha512-wKrgGcaFQ4EPjfzBTjzMU0rbFTYpa0S5gv9N/d8WBmWS64+IgJb7cHddMvV+tUkse7vUfco3eVs2lB+nJhPo3w==", + "dependencies": { + "cross-fetch": "4.0.0" + } + }, + "node_modules/i18next-localstorage-backend": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/i18next-localstorage-backend/-/i18next-localstorage-backend-4.2.0.tgz", + "integrity": "sha512-vglEQF0AnLriX7dLA2drHnqAYzHxnLwWQzBDw8YxcIDjOvYZz5rvpal59Dq4In+IHNmGNM32YgF0TDjBT0fHmA==", + "dependencies": { + "@babel/runtime": "^7.22.15" } }, "node_modules/immutable": { @@ -783,16 +833,6 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/make-plural": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.3.0.tgz", - "integrity": "sha512-/K3BC0KIsO+WK2i94LkMPv3wslMrazrQhfi5We9fMbLlLjzoOSJWr7TAdupLlDWaJcWxwoNosBkhFDejiu5VDw==" - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -832,6 +872,25 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -905,6 +964,11 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/rollup": { "version": "3.29.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", @@ -922,9 +986,9 @@ } }, "node_modules/sass": { - "version": "1.69.7", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.7.tgz", - "integrity": "sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", + "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -967,6 +1031,11 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/vite": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", @@ -1037,6 +1106,20 @@ "resolved": "https://registry.npmjs.org/vite-plugin-manifest-sri/-/vite-plugin-manifest-sri-0.1.0.tgz", "integrity": "sha512-m4gcEXwcA1MfCVYTLVHYsB03Xsc6L4VYfhxXmcYcS+rN3kTjuWkXMaA8OuOV1gFdi1bMJFkLTJCPciYApvCm/g==", "dev": true + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } } } } diff --git a/package.json b/package.json index 2a4821f1db..f2b66c5674 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,11 @@ "chart.js": "^4.4.0", "chartjs-adapter-date-fns": "^3.0.0", "chartjs-chart-sankey": "^0.12.0", - "date-fns": "^3.0.6", - "i18n-js": "^4.3.2", + "date-fns": "^3.2.0", + "i18next": "^23.7.16", + "i18next-chained-backend": "^4.6.2", + "i18next-http-backend": "^2.4.2", + "i18next-localstorage-backend": "^4.2.0", "leaflet": "^1.9.4", "store": "^2.0.12" } diff --git a/public/build/assets/autocomplete-functions-31caaca5.js b/public/build/assets/autocomplete-functions-31caaca5.js deleted file mode 100644 index 8d7a04bb34..0000000000 --- a/public/build/assets/autocomplete-functions-31caaca5.js +++ /dev/null @@ -1 +0,0 @@ -import{f as m,m as p}from"./vendor-4332182f.js";import{a as _,b as f,G as g}from"./get-c53daca3.js";function l(){return{id:"",name:"",alpine_name:""}}function y(){return{description:[],amount:[],currency_code:[],foreign_amount:[],foreign_currency_code:[],source_account:[],destination_account:[],budget_id:[],category_name:[],piggy_bank_id:[],bill_id:[],tags:[],notes:[],internal_reference:[],external_url:[],latitude:[],longitude:[],zoom_level:[],date:[],interest_date:[],book_date:[],process_date:[],due_date:[],payment_date:[],invoice_date:[]}}function I(){let t=m(new Date,"yyyy-MM-dd HH:mm");return{description:"",amount:"",currency_code:"EUR",foreign_amount:"",foreign_currency_code:"",source_account:l(),destination_account:l(),budget_id:null,category_name:"",piggy_bank_id:null,bill_id:null,tags:[],notes:"",internal_reference:"",external_url:"",hasLocation:!1,latitude:null,longitude:null,zoomLevel:null,date:t,interest_date:"",book_date:"",process_date:"",due_date:"",payment_date:"",invoice_date:"",errors:y()}}let b=class{list(t){return _.get("/api/v2/currencies",{params:t})}};function x(){let e={page:1,limit:1337};return new b().list(e).then(a=>{let n={defaultCurrency:{},nativeCurrencies:[],foreignCurrencies:[],enabledCurrencies:[]};n.foreignCurrencies.push({id:0,name:"(no foreign currency)",code:"",default:!1,symbol:"",decimal_places:2});for(let i in a.data.data)if(a.data.data.hasOwnProperty(i)){let r=a.data.data[i];if(r.attributes.enabled){let u={id:r.id,name:r.attributes.name,code:r.attributes.code,default:r.attributes.default,symbol:r.attributes.symbol,decimal_places:r.attributes.decimal_places};u.default&&(n.defaultCurrency=u),n.enabledCurrencies.push(u),n.nativeCurrencies.push(u),n.foreignCurrencies.push(u)}}return n})}class h{list(t){return _.get("/api/v2/budgets",{params:t})}}function q(){let e={page:1,limit:1337};return new h().list(e).then(a=>{let n=[{id:0,name:"(no budget)"}];for(let i in a.data.data)if(a.data.data.hasOwnProperty(i)){let r=a.data.data[i],u={id:r.id,name:r.attributes.name};n.push(u)}return n})}function C(){let e={page:1,limit:1337};return new f().list(e).then(a=>{let n={0:{id:0,name:"(no group)",order:0,piggyBanks:[{id:0,name:"(no piggy bank)",order:0}]}};for(let i in a.data.data)if(a.data.data.hasOwnProperty(i)){let r=a.data.data[i],u=r.attributes.object_group_id??"0",c=r.attributes.object_group_title??"(no group)",o={id:r.id,name:r.attributes.name,order:r.attributes.order};n.hasOwnProperty(u)||(n[u]={id:u,name:c,order:r.attributes.object_group_order??0,piggyBanks:[]}),n[u].piggyBanks.push(o),n[u].piggyBanks.sort((d,s)=>d.order-s.order)}return Object.keys(n).sort().reduce((i,r)=>(i[r]=n[r],i),{})})}function w(){let e={page:1,limit:1337};return new g().list(e).then(a=>{let n={0:{id:0,name:"(no group)",order:0,subscriptions:[{id:0,name:"(no subscription)",order:0}]}};for(let i in a.data.data)if(a.data.data.hasOwnProperty(i)){let r=a.data.data[i],u=r.attributes.object_group_id??"0",c=r.attributes.object_group_title??"(no group)",o={id:r.id,name:r.attributes.name,order:r.attributes.order};n.hasOwnProperty(u)||(n[u]={id:u,name:c,order:r.attributes.object_group_order??0,subscriptions:[]}),n[u].subscriptions.push(o),n[u].subscriptions.sort((d,s)=>d.order-s.order)}return Object.keys(n).sort().reduce((i,r)=>(i[r]=n[r],i),{})})}function j(){return{description:"/api/v2/autocomplete/transaction-descriptions",account:"/api/v2/autocomplete/accounts",category:"/api/v2/autocomplete/categories",tag:"/api/v2/autocomplete/tags"}}function $(e){const t={server:e.serverUrl,serverParams:{},fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},hiddenInput:!0,highlightTyped:!0,liveServer:!0};typeof e.filters<"u"&&e.filters.length>0&&(t.serverParams.types=e.filters),typeof e.onRenderItem<"u"&&e.onRenderItem!==null&&(t.onRenderItem=e.onRenderItem),e.valueField&&(t.valueField=e.valueField),e.labelField&&(t.labelField=e.labelField),e.onSelectItem&&(t.onSelectItem=e.onSelectItem),e.onChange&&(t.onChange=e.onChange),e.hiddenValue&&(t.hiddenValue=e.hiddenValue),p.init(e.selector,t)}function A(e,t){const a=parseInt(t._searchInput.attributes["data-index"].value);if(typeof e<"u"&&e.name){document.querySelector("#form")._x_dataStack[0].$data.entries[a].category_name=e.name;return}document.querySelector("#form")._x_dataStack[0].$data.entries[a].category_name=t._searchInput.value}function G(e,t){const a=parseInt(t._searchInput.attributes["data-index"].value);if(typeof e<"u"&&e.description){document.querySelector("#form")._x_dataStack[0].$data.entries[a].description=e.description;return}document.querySelector("#form")._x_dataStack[0].$data.entries[a].description=t._searchInput.value}function O(e,t){if(typeof e>"u"){const a=parseInt(t._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[a].destination_account.name===t._searchInput.value){console.warn('Ignore hallucinated destination account name change to "'+t._searchInput.value+'"');return}document.querySelector("#form")._x_dataStack[0].$data.entries[a].destination_account={name:t._searchInput.value,alpine_name:t._searchInput.value},document.querySelector("#form")._x_dataStack[0].changedDestinationAccount()}}function B(e,t){const a=parseInt(t._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[a].destination_account={id:e.id,name:e.name,alpine_name:e.name,type:e.type,currency_code:e.currency_code},document.querySelector("#form")._x_dataStack[0].changedDestinationAccount()}function P(e,t){if(typeof e>"u"){const a=parseInt(t._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[a].source_account.name===t._searchInput.value)return;document.querySelector("#form")._x_dataStack[0].$data.entries[a].source_account={name:t._searchInput.value,alpine_name:t._searchInput.value},document.querySelector("#form")._x_dataStack[0].changedSourceAccount()}}function D(e,t){const a=parseInt(t._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[a].source_account={id:e.id,name:e.name,alpine_name:e.name,type:e.type,currency_code:e.currency_code},document.querySelector("#form")._x_dataStack[0].changedSourceAccount()}export{q as a,C as b,w as c,y as d,I as e,$ as f,P as g,O as h,B as i,j,A as k,x as l,G as m,D as s}; diff --git a/public/build/assets/create-2569100d.js b/public/build/assets/create-2569100d.js new file mode 100644 index 0000000000..86b90ab1f5 --- /dev/null +++ b/public/build/assets/create-2569100d.js @@ -0,0 +1 @@ +import{a as m,d as y,f as d}from"./format-money-bcfc2969.js";import{d as w,c as b}from"./create-empty-split-c3730b85.js";import{l as v,a as C,b as T,c as E,p as P,d as _,s as L,e as c,f as S,g as D,h as A,i as x,j as k,k as h,m as p}from"./splice-errors-into-transactions-66181f21.js";import{l,i as r,m as B}from"./vendor-5cffaf70.js";import"./get-fa539542.js";class M{post(t){let s="/api/v2/transactions";return m.post(s,t)}}let n=[],u=[];document.addEventListener("location-remove",e=>{u[e.detail.index].remove()});function O(e){let t=0;if(document.querySelector("#form")._x_dataStack[0].$data.entries[t].hasLocation===!1){u[t]=new l.marker(e.latlng,{draggable:!0}),u[t].on("dragend",U),u[t].addTo(n[t]);const i=new CustomEvent("location-set",{detail:{latitude:e.latlng.lat,longitude:e.latlng.lng,index:t,zoomLevel:n[t].getZoom()}});document.dispatchEvent(i)}}function F(e){let t=0;const s=new CustomEvent("location-zoom",{detail:{index:t,zoomLevel:n[t].getZoom()}});document.dispatchEvent(s)}function U(e){let t=e.target,s=t.getLatLng();t.setLatLng(new l.LatLng(s.lat,s.lng),{draggable:"true"});const i=new CustomEvent("location-move",{detail:{latitude:s.lat,longitude:s.lng,index:0}});document.dispatchEvent(i)}function z(e){if(e>0){console.warn("Corwardly refuse to add a map on split #"+(e+1));return}if(typeof n[e]>"u"){let t=document.getElementById("location_map");t&&(n[e]=l.map(t).setView([t.dataset.latitude,t.dataset.longitude],t.dataset.zoomLevel),l.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png",{maxZoom:19,attribution:'© OpenStreetMap'}).addTo(n[e]),n[e].on("click",O),n[e].on("zoomend",F))}}const a=k();let I=function(){return{entries:[],formStates:{loadingCurrencies:!0,loadingBudgets:!0,loadingPiggyBanks:!0,loadingSubscriptions:!0,isSubmitting:!1,returnHereButton:!1,saveAsNewButton:!1,resetButton:!0,rulesButton:!0,webhooksButton:!0},formBehaviour:{formType:"create",foreignCurrencyEnabled:!0},formData:{defaultCurrency:null,enabledCurrencies:[],nativeCurrencies:[],foreignCurrencies:[],budgets:[],piggyBanks:[],subscriptions:[]},groupProperties:{transactionType:"unknown",title:null,id:null,totalAmount:0},notifications:{error:{show:!1,text:"",url:""},success:{show:!1,text:"",url:""},wait:{show:!1,text:""}},filters:{source:[],destination:[]},changedDateTime(e){console.warn("changedDateTime, event is not used")},changedDescription(e){console.warn("changedDescription, event is not used")},changedDestinationAccount(e){this.detectTransactionType()},changedSourceAccount(e){this.detectTransactionType()},detectTransactionType(){const e=this.entries[0].source_account.type??"unknown",t=this.entries[0].destination_account.type??"unknown";if(e==="unknown"&&t==="unknown"){this.groupProperties.transactionType="unknown",console.warn("Cannot infer transaction type from two unknown accounts.");return}if(e===t&&["Asset account","Loan","Debt","Mortgage"].includes(e)){this.groupProperties.transactionType="transfer",console.log('Transaction type is detected to be "'+this.groupProperties.transactionType+'".'),console.log("filter down currencies for transfer."),this.filterNativeCurrencies(this.entries[0].source_account.currency_code),this.filterForeignCurrencies(this.entries[0].destination_account.currency_code);return}if(e==="Asset account"&&["Expense account","Debt","Loan","Mortgage"].includes(t)){this.groupProperties.transactionType="withdrawal",console.log('[a] Transaction type is detected to be "'+this.groupProperties.transactionType+'".'),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(e==="Asset account"&&t==="unknown"){this.groupProperties.transactionType="withdrawal",console.log('[b] Transaction type is detected to be "'+this.groupProperties.transactionType+'".'),console.log(this.entries[0].source_account),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(["Debt","Loan","Mortgage"].includes(e)&&t==="Expense account"){this.groupProperties.transactionType="withdrawal",console.log('[c] Transaction type is detected to be "'+this.groupProperties.transactionType+'".'),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(e==="Revenue account"&&["Asset account","Debt","Loan","Mortgage"].includes(t)){this.groupProperties.transactionType="deposit",console.log('Transaction type is detected to be "'+this.groupProperties.transactionType+'".');return}if(["Debt","Loan","Mortgage"].includes(e)&&t==="Asset account"){this.groupProperties.transactionType="deposit",console.log('Transaction type is detected to be "'+this.groupProperties.transactionType+'".');return}console.warn('Unknown account combination between "'+e+'" and "'+t+'".')},formattedTotalAmount(){return this.entries.length===0?d(this.groupProperties.totalAmount,"EUR"):d(this.groupProperties.totalAmount,this.entries[0].currency_code??"EUR")},filterForeignCurrencies(e){let t=[],s;for(let i in this.formData.enabledCurrencies)if(this.formData.enabledCurrencies.hasOwnProperty(i)){let o=this.formData.enabledCurrencies[i];o.code===e&&(s=o)}t.push(s),this.formData.foreignCurrencies=t,t.length===1&&t[0].code===this.entries[0].source_account.currency_code&&(console.log("Foreign currency is same as source currency. Disable foreign amount."),this.formBehaviour.foreignCurrencyEnabled=!1),t.length===1&&t[0].code!==this.entries[0].source_account.currency_code&&(console.log("Foreign currency is NOT same as source currency. Enable foreign amount."),this.formBehaviour.foreignCurrencyEnabled=!0);for(let i in this.entries)this.entries.hasOwnProperty(i)&&(this.entries[i].foreign_currency_code=e)},filterNativeCurrencies(e){let t=[],s;for(let i in this.formData.enabledCurrencies)if(this.formData.enabledCurrencies.hasOwnProperty(i)){let o=this.formData.enabledCurrencies[i];o.code===e&&(s=o)}t.push(s),this.formData.nativeCurrencies=t;for(let i in this.entries)this.entries.hasOwnProperty(i)&&(this.entries[i].currency_code=e)},changedAmount(e){const t=parseInt(e.target.dataset.index);this.entries[t].amount=parseFloat(e.target.value),this.groupProperties.totalAmount=0;for(let s in this.entries)this.entries.hasOwnProperty(s)&&(this.groupProperties.totalAmount=this.groupProperties.totalAmount+parseFloat(this.entries[s].amount))},addedSplit(){},processUpload(e){this.showMessageOrRedirectUser()},processUploadError(e){this.notifications.success.show=!1,this.notifications.wait.show=!1,this.notifications.error.show=!0,this.formStates.isSubmitting=!1,this.notifications.error.text=r.t("firefly.errors_upload"),console.error(e)},init(){this.addSplit(),v().then(e=>{this.formStates.loadingCurrencies=!1,this.formData.defaultCurrency=e.defaultCurrency,this.formData.enabledCurrencies=e.enabledCurrencies,this.formData.nativeCurrencies=e.nativeCurrencies,this.formData.foreignCurrencies=e.foreignCurrencies}),C().then(e=>{this.formData.budgets=e,this.formStates.loadingBudgets=!1}),T().then(e=>{this.formData.piggyBanks=e,this.formStates.loadingPiggyBanks=!1}),E().then(e=>{this.formData.subscriptions=e,this.formStates.loadingSubscriptions=!1}),document.addEventListener("upload-success",e=>{this.processUpload(e),document.querySelectorAll("input[type=file]").value=""}),document.addEventListener("upload-error",e=>{this.processUploadError(e)}),document.addEventListener("location-move",e=>{this.entries[e.detail.index].latitude=e.detail.latitude,this.entries[e.detail.index].longitude=e.detail.longitude}),document.addEventListener("location-set",e=>{this.entries[e.detail.index].hasLocation=!0,this.entries[e.detail.index].latitude=e.detail.latitude,this.entries[e.detail.index].longitude=e.detail.longitude,this.entries[e.detail.index].zoomLevel=e.detail.zoomLevel}),document.addEventListener("location-zoom",e=>{this.entries[e.detail.index].hasLocation=!0,this.entries[e.detail.index].zoomLevel=e.detail.zoomLevel}),this.filters.source=["Asset account","Loan","Debt","Mortgage","Revenue account"],this.filters.destination=["Expense account","Loan","Debt","Mortgage","Asset account"]},submitTransaction(){this.notifications.error.show=!1,this.notifications.success.show=!1,this.notifications.wait.show=!1;for(let i in this.entries)this.entries.hasOwnProperty(i)&&(this.entries[i].errors=w());this.formStates.isSubmitting=!0,this.detectTransactionType();let e=P(this.entries,null,this.groupProperties.transactionType),t={group_title:this.groupProperties.title,fire_webhooks:this.formStates.webhooksButton,apply_rules:this.formStates.rulesButton,transactions:e};this.groupProperties.title===null&&e.length>1&&(t.group_title=e[0].description);let s=new M;console.log(t),s.post(t).then(i=>{const o=i.data.data;if(this.groupProperties.id=parseInt(o.id),this.groupProperties.title=o.attributes.group_title??o.attributes.transactions[0].description,_(this.groupProperties.id,o.attributes.transactions)>0){this.notifications.wait.show=!0,this.notifications.wait.text=r.t("firefly.wait_attachments");return}this.showMessageOrRedirectUser()}).catch(i=>{this.submitting=!1,console.log(i),typeof i.response<"u"&&this.parseErrors(i.response.data)})},showMessageOrRedirectUser(){if(this.notifications.error.show=!1,this.notifications.success.show=!1,this.notifications.wait.show=!1,this.formStates.returnHereButton){this.notifications.success.show=!0,this.notifications.success.url="transactions/show/"+this.groupProperties.id,this.notifications.success.text=r.t("firefly.stored_journal_js",{description:this.groupProperties.title}),this.formStates.resetButton&&(this.entries=[],this.addSplit(),this.groupProperties.totalAmount=0);return}window.location="transactions/show/"+this.groupProperties.id+"?transaction_group_id="+this.groupProperties.id+"&message=created"},parseErrors(e){this.notifications.error.show=!0,this.notifications.success.show=!1,this.notifications.wait.show=!1,this.formStates.isSubmitting=!1,this.notifications.error.text=r.t("firefly.errors_submission",{errorMessage:e.message}),e.hasOwnProperty("errors")&&(this.entries=L(e.errors,this.entries))},addSplit(){this.entries.push(b()),setTimeout(()=>{B.init("select.ac-tags",{allowClear:!0,server:a.tag,liveServer:!0,clearEnd:!0,allowNew:!0,notFoundMessage:r.t("firefly.nothing_found"),noCache:!0,fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}}});const e=this.entries.length-1;z(e);const t=function(s,i,o){return s.name_with_balance+'
'+r.t("firefly.account_type_"+s.type)+""};c({selector:"input.ac-source",serverUrl:a.account,onChange:S,onSelectItem:D,hiddenValue:this.entries[e].source_account.alpine_name}),c({selector:"input.ac-dest",serverUrl:a.account,filters:this.filters.destination,onRenderItem:t,onChange:A,onSelectItem:x}),c({selector:"input.ac-category",serverUrl:a.category,valueField:"id",labelField:"name",onChange:h,onSelectItem:h}),c({selector:"input.ac-description",serverUrl:a.description,valueField:"id",labelField:"description",onChange:p,onSelectItem:p})},150)},removeSplit(e){this.entries.splice(e,1),document.querySelector("#split-0-tab").click()},clearLocation(e){e.preventDefault();const t=e.currentTarget,s=parseInt(t.attributes["data-index"].value);this.entries[s].hasLocation=!1,this.entries[s].latitude=null,this.entries[s].longitude=null,this.entries[s].zoomLevel=null;const i=new CustomEvent("location-remove",{detail:{index:s}});return document.dispatchEvent(i),!1}}},f={transactions:I,dates:y};function g(){Object.keys(f).forEach(e=>{console.log(`Loading page component "${e}"`);let t=f[e]();Alpine.data(e,()=>t)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),g()});window.bootstrapped&&(console.log("Loaded through window variable."),g()); diff --git a/public/build/assets/create-d5d549ca.js b/public/build/assets/create-d5d549ca.js deleted file mode 100644 index dbeac69f59..0000000000 --- a/public/build/assets/create-d5d549ca.js +++ /dev/null @@ -1 +0,0 @@ -import{a as v,d as P,f as _,g as E,l as S}from"./get-c53daca3.js";import{l as T,a as L,b as k,c as A,d as x,e as D,f as h,g as O,s as B,h as F,i as I,j as U,k as y,m as w}from"./autocomplete-functions-31caaca5.js";import{k as g,I as z,l as M}from"./vendor-4332182f.js";function R(e,t){let o=[];for(let r in e)if(e.hasOwnProperty(r)){const n=e[r];let i={};i.description=n.description,i.source_name=n.source_account.name,i.destination_name=n.destination_account.name,i.amount=n.amount,i.currency_code=n.currency_code,i.date=n.date,i.interest_date=n.interest_date,i.book_date=n.book_date,i.process_date=n.process_date,i.due_date=n.due_date,i.payment_date=n.payment_date,i.invoice_date=n.invoice_date,i.budget_id=n.budget_id,i.category_name=n.category_name,i.piggy_bank_id=n.piggy_bank_id,i.bill_id=n.bill_id,i.tags=n.tags,i.notes=n.notes,i.internal_reference=n.internal_reference,i.external_url=n.external_url,i.store_location=!1,n.hasLocation&&(i.store_location=!0,i.longitude=n.longitude.toString(),i.latitude=n.latitude.toString(),i.zoom_level=n.zoomLevel),typeof n.foreign_currency_code<"u"&&n.foreign_currency_code.toString()!==""&&(i.foreign_currency_code=n.foreign_currency_code,typeof n.foreign_amount<"u"&&n.foreign_amount.toString()!==""&&(i.foreign_amount=n.foreign_amount),(typeof n.foreign_amount>"u"||n.foreign_amount.toString()==="")&&(delete i.foreign_amount,delete i.foreign_currency_code)),typeof n.source_account.id<"u"&&n.source_account.id.toString()!==""&&(i.source_id=n.source_account.id),typeof n.destination_account.id<"u"&&n.destination_account.id.toString()!==""&&(i.destination_id=n.destination_account.id),i.type=t,o.push(i)}return o}let N=class{post(t){let o="/api/v2/transactions";return v.post(o,t)}};class ${post(t,o,r){let n="/api/v1/attachments";return v.post(n,{filename:t,attachable_type:o,attachable_id:r})}upload(t,o){let r="./api/v1/attachments/"+t+"/upload";return axios.post(r,o)}}let j=function(e){let t=e.length,o=0,r=!1;for(const n in e)if(e.hasOwnProperty(n)&&/^0$|^[1-9]\d*$/.test(n)&&n<=4294967294&&r===!1){let i=new $;i.post(e[n].name,"TransactionJournal",e[n].journal).then(s=>{let a=parseInt(s.data.data.id);i.upload(a,e[n].content).then(l=>{if(o++,o===t){const d=new CustomEvent("upload-success",{some:"details"});document.dispatchEvent(d)}}).catch(l=>{console.error("Could not upload"),console.error(l),o++;const d=new CustomEvent("upload-failed",{error:l});document.dispatchEvent(d),r=!0})}).catch(s=>{console.error("Could not create upload."),console.error(s),o++;const a=new CustomEvent("upload-failed",{error:s});document.dispatchEvent(a),r=!0})}};function q(e,t){t=t.reverse();let o=[],r=0,n=[],i=document.querySelectorAll('input[name="attachments[]"]');for(const s in i)if(i.hasOwnProperty(s)&&/^0$|^[1-9]\d*$/.test(s)&&s<=4294967294)for(const a in i[s].files)i[s].files.hasOwnProperty(a)&&/^0$|^[1-9]\d*$/.test(a)&&a<=4294967294&&(o.push({journal:t[s].transaction_journal_id,file:i[s].files[a]}),r++);for(const s in o)o.hasOwnProperty(s)&&/^0$|^[1-9]\d*$/.test(s)&&s<=4294967294&&function(a,l){let d=new FileReader;d.onloadend=function(m){m.target.readyState===FileReader.DONE&&(n.push({name:o[l].file.name,journal:o[l].journal,content:new Blob([m.target.result])}),n.length===r&&j(n))},d.readAsArrayBuffer(a.file)}(o[s],s);return r}function Z(e,t,o){let r=[];for(let n in o)o.hasOwnProperty(n)&&r.push(o[n].replace(e,t));return r}function V(e,t,o){let r,n,i;for(const s in t)if(t.hasOwnProperty(s)){if(s==="group_title"){console.error("Cannot handle error in group title.");continue}if(r=parseInt(s.split(".")[1]),n=s.split(".")[2],i=Z(s,n,t[s]),!o.hasOwnProperty(r)){console.error("Cannot handle errors in index #"+r);continue}switch(n){case"currency_code":case"foreign_currency_code":case"category_name":case"piggy_bank_id":case"notes":case"internal_reference":case"external_url":case"latitude":case"longitude":case"zoom_level":case"interest_date":case"book_date":case"process_date":case"due_date":case"payment_date":case"invoice_date":case"amount":case"date":case"budget_id":case"bill_id":case"description":case"tags":o[r].errors[n]=i;break;case"source_name":case"source_id":o[r].errors.source_account=o[r].errors.source_account.concat(i);break;case"type":o[r].errors.source_account=o[r].errors.source_account.concat([e.t("validation.bad_type_source")]),o[r].errors.destination_account=o[r].errors.destination_account.concat([e.t("validation.bad_type_destination")]);break;case"destination_name":case"destination_id":o[r].errors.destination_account=o[r].errors.destination_account.concat(i);break;case"foreign_amount":case"foreign_currency_id":o[r].errors.foreign_amount=o[r].errors.foreign_amount.concat(i);break}typeof o[r]<"u"&&(o[r].errors.source_account=Array.from(new Set(o[r].errors.source_account)),o[r].errors.destination_account=Array.from(new Set(o[r].errors.destination_account)))}return console.log(o[0].errors),o}let u=[],f=[];document.addEventListener("location-remove",e=>{f[e.detail.index].remove()});function H(e){let t=0;if(document.querySelector("#form")._x_dataStack[0].$data.entries[t].hasLocation===!1){f[t]=new g.marker(e.latlng,{draggable:!0}),f[t].on("dragend",J),f[t].addTo(u[t]);const r=new CustomEvent("location-set",{detail:{latitude:e.latlng.lat,longitude:e.latlng.lng,index:t,zoomLevel:u[t].getZoom()}});document.dispatchEvent(r)}}function K(e){let t=0;const o=new CustomEvent("location-zoom",{detail:{index:t,zoomLevel:u[t].getZoom()}});document.dispatchEvent(o)}function J(e){let t=e.target,o=t.getLatLng();t.setLatLng(new g.LatLng(o.lat,o.lng),{draggable:"true"});const r=new CustomEvent("location-move",{detail:{latitude:o.lat,longitude:o.lng,index:0}});document.dispatchEvent(r)}function X(e){if(e>0){console.warn("Corwardly refuse to add a map on split #"+(e+1));return}if(typeof u[e]>"u"){let t=document.getElementById("location_map");u[e]=g.map(t).setView([t.dataset.latitude,t.dataset.longitude],t.dataset.zoomLevel),g.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png",{maxZoom:19,attribution:'© OpenStreetMap'}).addTo(u[e]),u[e].on("click",H),u[e].on("zoomend",K)}}let c;const p=U();let G=function(){return{entries:[],formStates:{loadingCurrencies:!0,loadingBudgets:!0,loadingPiggyBanks:!0,loadingSubscriptions:!0,isSubmitting:!1,returnHereButton:!1,saveAsNewButton:!1,resetButton:!0,rulesButton:!0,webhooksButton:!0},formBehaviour:{formType:"create",foreignCurrencyEnabled:!0},formData:{defaultCurrency:null,enabledCurrencies:[],nativeCurrencies:[],foreignCurrencies:[],budgets:[],piggyBanks:[],subscriptions:[]},groupProperties:{transactionType:"unknown",title:null,id:null,totalAmount:0},notifications:{error:{show:!1,text:"",url:""},success:{show:!1,text:"",url:""},wait:{show:!1,text:""}},filters:{source:[],destination:[]},changedDateTime(e){console.warn("changedDateTime, event is not used")},changedDescription(e){console.warn("changedDescription, event is not used")},changedDestinationAccount(e){this.detectTransactionType()},changedSourceAccount(e){this.detectTransactionType()},detectTransactionType(){const e=this.entries[0].source_account.type??"unknown",t=this.entries[0].destination_account.type??"unknown";if(e==="unknown"&&t==="unknown"){this.groupProperties.transactionType="unknown",console.warn("Cannot infer transaction type from two unknown accounts.");return}if(e===t&&["Asset account","Loan","Debt","Mortgage"].includes(e)){this.groupProperties.transactionType="transfer",console.log('Transaction type is detected to be "'+this.groupProperties.transactionType+'".'),console.log("filter down currencies for transfer."),this.filterNativeCurrencies(this.entries[0].source_account.currency_code),this.filterForeignCurrencies(this.entries[0].destination_account.currency_code);return}if(e==="Asset account"&&["Expense account","Debt","Loan","Mortgage"].includes(t)){this.groupProperties.transactionType="withdrawal",console.log('[a] Transaction type is detected to be "'+this.groupProperties.transactionType+'".'),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(e==="Asset account"&&t==="unknown"){this.groupProperties.transactionType="withdrawal",console.log('[b] Transaction type is detected to be "'+this.groupProperties.transactionType+'".'),console.log(this.entries[0].source_account),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(["Debt","Loan","Mortgage"].includes(e)&&t==="Expense account"){this.groupProperties.transactionType="withdrawal",console.log('[c] Transaction type is detected to be "'+this.groupProperties.transactionType+'".'),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(e==="Revenue account"&&["Asset account","Debt","Loan","Mortgage"].includes(t)){this.groupProperties.transactionType="deposit",console.log('Transaction type is detected to be "'+this.groupProperties.transactionType+'".');return}if(["Debt","Loan","Mortgage"].includes(e)&&t==="Asset account"){this.groupProperties.transactionType="deposit",console.log('Transaction type is detected to be "'+this.groupProperties.transactionType+'".');return}console.warn('Unknown account combination between "'+e+'" and "'+t+'".')},formattedTotalAmount(){return this.entries.length===0?_(this.groupProperties.totalAmount,"EUR"):_(this.groupProperties.totalAmount,this.entries[0].currency_code??"EUR")},filterForeignCurrencies(e){let t=[],o;for(let r in this.formData.enabledCurrencies)if(this.formData.enabledCurrencies.hasOwnProperty(r)){let n=this.formData.enabledCurrencies[r];n.code===e&&(o=n)}t.push(o),this.formData.foreignCurrencies=t,t.length===1&&t[0].code===this.entries[0].source_account.currency_code&&(console.log("Foreign currency is same as source currency. Disable foreign amount."),this.formBehaviour.foreignCurrencyEnabled=!1),t.length===1&&t[0].code!==this.entries[0].source_account.currency_code&&(console.log("Foreign currency is NOT same as source currency. Enable foreign amount."),this.formBehaviour.foreignCurrencyEnabled=!0);for(let r in this.entries)this.entries.hasOwnProperty(r)&&(this.entries[r].foreign_currency_code=e)},filterNativeCurrencies(e){let t=[],o;for(let r in this.formData.enabledCurrencies)if(this.formData.enabledCurrencies.hasOwnProperty(r)){let n=this.formData.enabledCurrencies[r];n.code===e&&(o=n)}t.push(o),this.formData.nativeCurrencies=t;for(let r in this.entries)this.entries.hasOwnProperty(r)&&(this.entries[r].currency_code=e)},changedAmount(e){const t=parseInt(e.target.dataset.index);this.entries[t].amount=parseFloat(e.target.value),this.groupProperties.totalAmount=0;for(let o in this.entries)this.entries.hasOwnProperty(o)&&(this.groupProperties.totalAmount=this.groupProperties.totalAmount+parseFloat(this.entries[o].amount))},addedSplit(){},processUpload(e){this.showMessageOrRedirectUser()},processUploadError(e){this.notifications.success.show=!1,this.notifications.wait.show=!1,this.notifications.error.show=!0,this.formStates.isSubmitting=!1,this.notifications.error.text=c.t("firefly.errors_upload"),console.error(e)},init(){Promise.all([E("language","en_US")]).then(e=>{c=new z;const t=e[0].replace("-","_");c.locale=t,S(c,t).then(()=>{this.addSplit()})}),T().then(e=>{this.formStates.loadingCurrencies=!1,this.formData.defaultCurrency=e.defaultCurrency,this.formData.enabledCurrencies=e.enabledCurrencies,this.formData.nativeCurrencies=e.nativeCurrencies,this.formData.foreignCurrencies=e.foreignCurrencies}),L().then(e=>{this.formData.budgets=e,this.formStates.loadingBudgets=!1}),k().then(e=>{this.formData.piggyBanks=e,this.formStates.loadingPiggyBanks=!1}),A().then(e=>{this.formData.subscriptions=e,this.formStates.loadingSubscriptions=!1}),document.addEventListener("upload-success",e=>{this.processUpload(e),document.querySelectorAll("input[type=file]").value=""}),document.addEventListener("upload-error",e=>{this.processUploadError(e)}),document.addEventListener("location-move",e=>{this.entries[e.detail.index].latitude=e.detail.latitude,this.entries[e.detail.index].longitude=e.detail.longitude}),document.addEventListener("location-set",e=>{this.entries[e.detail.index].hasLocation=!0,this.entries[e.detail.index].latitude=e.detail.latitude,this.entries[e.detail.index].longitude=e.detail.longitude,this.entries[e.detail.index].zoomLevel=e.detail.zoomLevel}),document.addEventListener("location-zoom",e=>{this.entries[e.detail.index].hasLocation=!0,this.entries[e.detail.index].zoomLevel=e.detail.zoomLevel}),this.filters.source=["Asset account","Loan","Debt","Mortgage","Revenue account"],this.filters.destination=["Expense account","Loan","Debt","Mortgage","Asset account"]},submitTransaction(){this.notifications.error.show=!1,this.notifications.success.show=!1,this.notifications.wait.show=!1;for(let r in this.entries)this.entries.hasOwnProperty(r)&&(this.entries[r].errors=x());this.formStates.isSubmitting=!0,this.detectTransactionType();let e=R(this.entries,this.groupProperties.transactionType),t={group_title:this.groupProperties.title,fire_webhooks:this.formStates.webhooksButton,apply_rules:this.formStates.rulesButton,transactions:e};this.groupProperties.title===null&&e.length>1&&(t.group_title=e[0].description);let o=new N;console.log(t),o.post(t).then(r=>{const n=r.data.data;if(this.groupProperties.id=parseInt(n.id),this.groupProperties.title=n.attributes.group_title??n.attributes.transactions[0].description,q(this.groupProperties.id,n.attributes.transactions)>0){this.notifications.wait.show=!0,this.notifications.wait.text=c.t("firefly.wait_attachments");return}this.showMessageOrRedirectUser()}).catch(r=>{this.submitting=!1,console.log(r),typeof r.response<"u"&&this.parseErrors(r.response.data)})},showMessageOrRedirectUser(){if(this.notifications.error.show=!1,this.notifications.success.show=!1,this.notifications.wait.show=!1,this.formStates.returnHereButton){this.notifications.success.show=!0,this.notifications.success.url="transactions/show/"+this.groupProperties.id,this.notifications.success.text=c.t("firefly.stored_journal_js",{description:this.groupProperties.title}),this.formStates.resetButton&&(this.entries=[],this.addSplit(),this.groupProperties.totalAmount=0);return}window.location="transactions/show/"+this.groupProperties.id+"?transaction_group_id="+this.groupProperties.id+"&message=created"},parseErrors(e){this.notifications.error.show=!0,this.notifications.success.show=!1,this.notifications.wait.show=!1,this.formStates.isSubmitting=!1,this.notifications.error.text=c.t("firefly.errors_submission",{errorMessage:e.message}),e.hasOwnProperty("errors")&&(this.entries=V(c,e.errors,this.entries))},addSplit(){this.entries.push(D()),setTimeout(()=>{M.init("select.ac-tags",{allowClear:!0,server:p.tag,liveServer:!0,clearEnd:!0,allowNew:!0,notFoundMessage:c.t("firefly.nothing_found"),noCache:!0,fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}}});const e=this.entries.length-1;X(e);const t=function(o,r,n){return o.name_with_balance+'
'+c.t("firefly.account_type_"+o.type)+""};h({selector:"input.ac-source",serverUrl:p.account,onChange:O,onSelectItem:B,hiddenValue:this.items[e].source_account.alpine_name}),h({selector:"input.ac-dest",serverUrl:p.account,filters:this.filters.destination,onRenderItem:t,onChange:F,onSelectItem:I}),h({selector:"input.ac-category",serverUrl:p.category,valueField:"id",labelField:"name",onChange:y,onSelectItem:y}),h({selector:"input.ac-description",serverUrl:p.description,valueField:"id",labelField:"description",onChange:w,onSelectItem:w})},150)},removeSplit(e){this.entries.splice(e,1),document.querySelector("#split-0-tab").click()},clearLocation(e){e.preventDefault();const t=e.currentTarget,o=parseInt(t.attributes["data-index"].value);this.entries[o].hasLocation=!1,this.entries[o].latitude=null,this.entries[o].longitude=null,this.entries[o].zoomLevel=null;const r=new CustomEvent("location-remove",{detail:{index:o}});return document.dispatchEvent(r),!1}}},b={transactions:G,dates:P};function C(){Object.keys(b).forEach(e=>{console.log(`Loading page component "${e}"`);let t=b[e]();Alpine.data(e,()=>t)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),C()});window.bootstrapped&&(console.log("Loaded through window variable."),C()); diff --git a/public/build/assets/create-empty-split-c3730b85.js b/public/build/assets/create-empty-split-c3730b85.js new file mode 100644 index 0000000000..881e9b9d8b --- /dev/null +++ b/public/build/assets/create-empty-split-c3730b85.js @@ -0,0 +1 @@ +import{f as n}from"./vendor-5cffaf70.js";function e(){return{id:"",name:"",alpine_name:""}}function o(){return{description:[],amount:[],currency_code:[],foreign_amount:[],foreign_currency_code:[],source_account:[],destination_account:[],budget_id:[],category_name:[],piggy_bank_id:[],bill_id:[],tags:[],notes:[],internal_reference:[],external_url:[],latitude:[],longitude:[],zoom_level:[],date:[],interest_date:[],book_date:[],process_date:[],due_date:[],payment_date:[],invoice_date:[]}}function d(){let t=n(new Date,"yyyy-MM-dd HH:mm");return{description:"",amount:"",currency_code:"EUR",foreign_amount:"",foreign_currency_code:"",source_account:e(),destination_account:e(),budget_id:null,category_name:"",piggy_bank_id:null,bill_id:null,tags:[],notes:"",internal_reference:"",external_url:"",hasLocation:!1,latitude:null,longitude:null,zoomLevel:null,date:t,interest_date:"",book_date:"",process_date:"",due_date:"",payment_date:"",invoice_date:"",errors:o()}}export{d as c,o as d}; diff --git a/public/build/assets/dashboard-82f52d2a.js b/public/build/assets/dashboard-82f52d2a.js new file mode 100644 index 0000000000..7042e15088 --- /dev/null +++ b/public/build/assets/dashboard-82f52d2a.js @@ -0,0 +1 @@ +import{a as O,f,g as v,P as bt,d as wt}from"./format-money-bcfc2969.js";import{f as g,C as m,a as S,i as p,S as vt,F as mt,L as Ct,b as kt,A as Pt,B as Dt,T as xt,P as Ot,c as Mt,d as Ft,p as St,e as At,g as Bt,h as jt,j as Wt,k as It}from"./vendor-5cffaf70.js";import{G as $t}from"./get-32a7112a.js";import{G as Vt,a as Kt}from"./get-fa539542.js";class Lt{get(e,n,a){return O.get("/api/v2/summary/basic",{params:{start:e,end:n,code:a}})}}function P(t,e,n){const a=g(e,"y-MM-dd")+"_"+g(n,"y-MM-dd")+"_"+t;return console.log("getCacheKey: "+a),String(a)}let U=!1;const Et=()=>({balanceBox:{amounts:[],subtitles:[]},billBox:{paid:[],unpaid:[]},leftBox:{left:[],perDay:[]},netBox:{net:[]},autoConversion:!1,loading:!1,boxData:null,boxOptions:null,getFreshData(){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),n=P("dashboard-boxes-data",t,e),a=window.store.get("cacheValid");let r=window.store.get(n);if(a&&typeof r<"u"){this.boxData=r,this.generateOptions(this.boxData);return}new Lt().get(g(t,"yyyy-MM-dd"),g(e,"yyyy-MM-dd"),null).then(i=>{this.boxData=i.data,window.store.set(n,i.data),this.generateOptions(this.boxData)})},generateOptions(t){this.balanceBox={amounts:[],subtitles:[]},this.billBox={paid:[],unpaid:[]},this.leftBox={left:[],perDay:[]},this.netBox={net:[]};let e={};for(const n in t)if(t.hasOwnProperty(n)){const a=t[n];if(!a.hasOwnProperty("key"))continue;let r=a.key;if(this.autoConversion){if(r.startsWith("balance-in-native")){this.balanceBox.amounts.push(f(a.value,a.currency_code)),e.hasOwnProperty(a.currency_code)||(e[a.currency_code]="");continue}if(r.startsWith("spent-in-native")){e.hasOwnProperty(a.currency_code)||(e[a.currency_code]=""),e[a.currency_code]=e[a.currency_code]+f(a.value,a.currency_code);continue}if(r.startsWith("earned-in-native")){e.hasOwnProperty(a.currency_code)||(e[a.currency_code]=""),e[a.currency_code]=f(a.value,a.currency_code)+" + "+e[a.currency_code];continue}if(r.startsWith("bills-unpaid-in-native")){this.billBox.unpaid.push(f(a.value,a.currency_code));continue}if(r.startsWith("bills-paid-in-native")){this.billBox.paid.push(f(a.value,a.currency_code));continue}if(r.startsWith("left-to-spend-in-native")){this.leftBox.left.push(f(a.value,a.currency_code));continue}if(r.startsWith("left-per-day-to-spend-in-native")){this.leftBox.perDay.push(f(a.value,a.currency_code));continue}if(r.startsWith("net-worth-in-native")){this.netBox.net.push(f(a.value,a.currency_code));continue}}if(!this.autoConversion&&!r.endsWith("native")){if(r.startsWith("balance-in-")){this.balanceBox.amounts.push(f(a.value,a.currency_code));continue}if(r.startsWith("spent-in-")){e.hasOwnProperty(a.currency_code)||(e[a.currency_code]=""),e[a.currency_code]=e[a.currency_code]+f(a.value,a.currency_code);continue}if(r.startsWith("earned-in-")){e.hasOwnProperty(a.currency_code)||(e[a.currency_code]=""),e[a.currency_code]=f(a.value,a.currency_code)+" + "+e[a.currency_code];continue}if(r.startsWith("bills-unpaid-in-")){this.billBox.unpaid.push(f(a.value,a.currency_code));continue}if(r.startsWith("bills-paid-in-")){this.billBox.paid.push(f(a.value,a.currency_code));continue}if(r.startsWith("left-to-spend-in-")){this.leftBox.left.push(f(a.value,a.currency_code));continue}if(r.startsWith("left-per-day-to-spend-in-")){this.leftBox.perDay.push(f(a.value,a.currency_code));continue}r.startsWith("net-worth-in-")&&this.netBox.net.push(f(a.value,a.currency_code))}}for(let n in e)e.hasOwnProperty(n)&&this.balanceBox.subtitles.push(e[n]);this.loading=!1},loadBoxes(){if(this.loading!==!0){if(this.loading=!0,this.boxData===null){this.getFreshData();return}this.generateOptions(this.boxData),this.loading=!1}},init(){Promise.all([v("viewRange"),v("autoConversion",!1)]).then(t=>{U=!0,this.autoConversion=t[1],this.loadBoxes()}),window.store.observe("end",()=>{U&&(this.boxData=null,this.loadBoxes())}),window.store.observe("autoConversion",t=>{U&&(this.autoConversion=t,this.loadBoxes())})}});class Gt{put(e,n){let a="/api/v1/preferences/"+e;return O.put(a,{data:n})}}function Tt(t,e=null){window.store.set(t,e),new Gt().put(t,e).then(a=>{}).catch(()=>{new bt().post(t,e).then(r=>{})})}let Rt=class{dashboard(e,n){let a=g(e,"y-MM-dd"),r=g(n,"y-MM-dd");return O.get("/api/v2/chart/account/dashboard",{params:{start:a,end:r}})}expense(e,n){let a=g(e,"y-MM-dd"),r=g(n,"y-MM-dd");return O.get("/api/v2/chart/account/expense-dashboard",{params:{start:a,end:r}})}};class st{get(e,n){let a={date:g(n,"y-MM-dd").slice(0,10)};return n?O.get("/api/v2/accounts/"+e,{params:a}):O.get("/api/v2/accounts/"+e)}transactions(e,n){const a={page:n.page??1};return n.hasOwnProperty("start")&&(a.start=g(n.start,"y-MM-dd")),n.hasOwnProperty("end")&&(a.end=g(n.end,"y-MM-dd")),O.get("/api/v2/accounts/"+e+"/transactions",{params:a})}}function N(t){return t==="sankey"?{type:"sankey",data:{datasets:[]},options:{animations:!1}}:t==="pie"?{type:"pie",data:{datasets:[]}}:t==="column"?{type:"bar",data:{labels:[],datasets:[]},options:{plugins:{tooltip:{callbacks:{label:function(e){let n=e.dataset.currency_code;return f(e.raw,n)}}}},maintainAspectRatio:!1,scales:{}}}:t==="line"?{options:{plugins:{tooltip:{callbacks:{label:function(e){let n=e.dataset.currency_code;return f(e.raw,n)}}}},maintainAspectRatio:!1,scales:{x:{type:"time",time:{tooltipFormat:"PP"}}}},type:"line",data:{labels:[],datasets:[]}}:{}}let q=new m("#36a2eb"),I=new m("#ff6384"),G=new m("#4bc0c0"),ft=new m("#ff9f40"),qt=new m("#9966ff"),Nt=new m("#ffcd56"),Yt=new m("#c9cbcf"),lt=0;window.theme==="dark"&&(I.darken(.3).desaturate(.3),G.darken(.3).desaturate(.3),q.darken(.3).desaturate(.3),ft.darken(.3).desaturate(.3));let z=[I,ft,q,G,qt,Nt,Yt,G];function j(t,e){let n={borderColor:I.rgbString(),backgroundColor:I.rgbString()},a;switch(t){default:let o=Math.floor(lt/2)%z.length;a=new m(z[o].rgbString()),a.lighten(.38),n={borderColor:z[o].hexString(),backgroundColor:a.hexString()};break;case"spent":a=new m(q.rgbString()),a.lighten(.38),n={borderColor:q.rgbString(),backgroundColor:a.rgbString()};break;case"left":a=new m(G.rgbString()),a.lighten(.38),n={borderColor:G.rgbString(),backgroundColor:a.rgbString()};break;case"overspent":a=new m(I.rgbString()),a.lighten(.22),n={borderColor:I.rgbString(),backgroundColor:a.rgbString()};break}return lt++,e==="border"?n.borderColor:e==="background"?n.backgroundColor:"#FF0000"}let A=[],$=null,H=null,J=!1;const Ut=()=>({loading:!1,loadingAccounts:!1,accountList:[],autoConversion:!1,chartOptions:null,switchAutoConversion(){this.autoConversion=!this.autoConversion,Tt("autoConversion",this.autoConversion)},getFreshData(){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),n=P("dashboard-accounts-chart",t,e),a=window.store.get("cacheValid");let r=window.store.get(n);if(a&&typeof r<"u"){console.log(r),this.drawChart(this.generateOptions(r)),this.loading=!1;return}new Rt().dashboard(t,e,null).then(i=>{this.chartData=i.data,window.store.set(n,i.data),console.log(i.data),this.drawChart(this.generateOptions(this.chartData)),this.loading=!1})},generateOptions(t){A=[];let e=N("line");for(let n=0;n0){this.loadingAccounts=!1;return}const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),n=P("dashboard-accounts-data",t,e),a=window.store.get("cacheValid");let r=window.store.get(n);if(a&&typeof r<"u"){this.accountList=r,this.loadingAccounts=!1;return}const o=10;let i=0,l=0,u=[];Promise.all([v("frontpageAccounts")]).then(d=>{i=d[0].length;for(let h in d[0]){let c=d[0];if(c.hasOwnProperty(h)){let _=c[h];new st().get(_,new Date(window.store.get("end"))).then(w=>{let C=w.data.data;const yt={page:1,start:new Date(window.store.get("start")),end:new Date(window.store.get("end"))};new st().transactions(C.id,yt).then(at=>{let nt=[];for(let B=0;B=o);B++){let M=at.data.data[B],rt={title:M.attributes.group_title===null?"":M.attributes.group_title,id:M.id,transactions:[]};for(let Y=0;YB.order-M.order),this.accountList=u,this.loadingAccounts=!1,window.store.set(n,u))})})}}})},init(){Promise.all([v("viewRange","1M"),v("autoConversion",!1),v("language","en_US")]).then(t=>{this.autoConversion=t[1],J=!0,this.loadChart(),this.loadAccounts()}),window.store.observe("end",()=>{J&&(H=null,this.accountList=[],this.loadChart(),this.loadAccounts())}),window.store.observe("autoConversion",()=>{J&&(this.loadChart(),this.loadAccounts())})}});let zt=class{dashboard(e,n){let a=g(e,"y-MM-dd"),r=g(n,"y-MM-dd");return O.get("/api/v2/chart/budget/dashboard",{params:{start:a,end:r}})}},V=[],T=null,F=null,Z=!1;const Ht=()=>({loading:!1,autoConversion:!1,loadChart(){if(this.loading!==!0){if(this.loading=!0,F!==null){this.drawChart(this.generateOptions(F)),this.loading=!1;return}this.getFreshData()}},drawChart(t){if(T!==null){T.data.datasets=t.data.datasets,T.update();return}T=new S(document.querySelector("#budget-chart"),t)},getFreshData(){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),n=P("dashboard-budgets-chart",t,e),a=window.store.get("cacheValid");let r=window.store.get(n);if(a&&typeof r<"u"){F=r,this.drawChart(this.generateOptions(F)),this.loading=!1;return}new zt().dashboard(t,e,null).then(i=>{F=i.data,this.drawChart(this.generateOptions(F)),window.store.set(n,F),this.loading=!1})},generateOptions(t){V=[];let e=N("column");e.options.locale=window.store.get("locale").replace("_","-"),e.options.plugins={tooltip:{callbacks:{title:function(n){return n.label},label:function(n){let a=n.dataset.label||"";return a&&(a+=": "),a+" "+f(n.parsed.y,V[n.parsed.x]??"EUR")}}}},e.data={labels:[],datasets:[{label:p.t("firefly.spent"),data:[],borderWidth:1,stack:1,backgroundColor:j("spent","background"),borderColor:j("spent","border")},{label:p.t("firefly.left"),data:[],borderWidth:1,stack:1,backgroundColor:j("left","background"),borderColor:j("left","border")},{label:p.t("firefly.overspent"),data:[],borderWidth:1,stack:1,backgroundColor:j("overspent","background"),borderColor:j("overspent","border")}]};for(const n in t)if(t.hasOwnProperty(n)){let a=t[n],r=a.label+" ("+a.currency_code+")";e.data.labels.push(r),this.autoConversion&&(V.push(a.native_currency_code),e.data.datasets[0].data.push(parseFloat(a.native_entries.spent)*-1),e.data.datasets[1].data.push(parseFloat(a.native_entries.left)),e.data.datasets[2].data.push(parseFloat(a.native_entries.overspent))),this.autoConversion||(V.push(a.currency_code),e.data.datasets[0].data.push(parseFloat(a.entries.spent)*-1),e.data.datasets[1].data.push(parseFloat(a.entries.left)),e.data.datasets[2].data.push(parseFloat(a.entries.overspent)))}return e.options.scales={y:{ticks:{callback:function(n){return f(n,V[0]??"EUR")}}}},e},init(){Promise.all([v("autoConversion",!1)]).then(t=>{this.autoConversion=t[0],Z=!0,this.loading===!1&&this.loadChart()}),window.store.observe("end",()=>{Z&&this.loading===!1&&(F=null,this.loadChart())}),window.store.observe("autoConversion",t=>{Z&&(this.autoConversion=t,this.loading===!1&&this.loadChart())})}});class Jt{dashboard(e,n){let a=g(e,"y-MM-dd"),r=g(n,"y-MM-dd");return O.get("/api/v2/chart/category/dashboard",{params:{start:a,end:r}})}}let ut=[],K=null,W=null,Q=!1;const Zt=()=>({loading:!1,autoConversion:!1,generateOptions(t){ut=[];let e=N("column"),n={};for(const r in t)if(t.hasOwnProperty(r)){let o=t[r],i=o.currency_code;this.autoConversion&&(i=o.native_currency_code),n.hasOwnProperty(i)||(n[i]={name:i,yAxisID:"",data:{}},ut.push(i))}for(const r in t)if(t.hasOwnProperty(r)){let o=t[r],i=o.currency_code;this.autoConversion&&(i=o.native_currency_code);for(const l in n)if(n.hasOwnProperty(l)){let u=0;i===l&&(u=parseFloat(o.amount),""+o.currency_code,this.autoConversion&&(u=parseFloat(o.native_amount),""+o.native_currency_code)),n[l].data.hasOwnProperty(o.label)&&(n[l].data[o.label]=n[l].data[o.label]+u),n[l].data.hasOwnProperty(o.label)||(n[l].data[o.label]=u)}e.data.labels.includes(o.label)||e.data.labels.push(o.label)}let a=0;for(const r in n){let o="y"+r,i={label:r,currency_code:r,yAxisID:o,data:[]};for(const l in n[r].data)i.data.push(n[r].data[l]);e.data.datasets.push(i),e.options.scales.hasOwnProperty(o)||(e.options.scales[o]={beginAtZero:!0,type:"linear",position:a===1?"right":"left",ticks:{callback:function(l,u,d){return f(l,r)}}},a++)}return e},drawChart(t){if(K!==null){K.options=t.options,K.data=t.data,K.update();return}K=new S(document.querySelector("#category-chart"),t)},getFreshData(){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),n=P("dashboard-categories-chart",t,e),a=window.store.get("cacheValid");let r=window.store.get(n);if(a&&typeof r<"u"){W=r,this.drawChart(this.generateOptions(W)),this.loading=!1;return}new Jt().dashboard(t,e,null).then(i=>{W=i.data,this.drawChart(this.generateOptions(i.data)),window.store.set(n,W),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,W!==null){this.drawChart(this.generateOptions(W)),this.loading=!1;return}this.getFreshData()}},init(){Promise.all([v("autoConversion",!1)]).then(t=>{this.autoConversion=t[0],Q=!0,this.loadChart()}),window.store.observe("end",()=>{Q&&(this.chartData=null,this.loadChart())}),window.store.observe("autoConversion",t=>{Q&&(this.autoConversion=t,this.loadChart())})}});S.register({SankeyController:vt,Flow:mt});const ct="dashboard-sankey-data";let X=!1,R=null,D=[],y=!1,s={category:null,unknown_category:null,in:null,out:null,unknown_source:null,unknown_dest:null,unknown_account:null,expense_account:null,revenue_account:null,budget:null,unknown_budget:null,all_money:null};const dt=function(t){return t.includes(s.revenue_account)?"forestgreen":t.includes("("+s.in+",")?"green":t.includes(s.budget)||t.includes(s.unknown_budget)?"Orchid":t.includes("("+s.out+",")?"MediumOrchid":t.includes(s.all_money)?"blue":"red"};function L(t,e,n,a){if(t==="category"&&e!==null&&n==="in")return s.category+' "'+e+'" ('+s.in+(y?", "+a+")":")");if(t==="category"&&e===null&&n==="in")return s.unknown_category+" ("+s.in+(y?", "+a+")":")");if(t==="category"&&e!==null&&n==="out")return s.category+' "'+e+'" ('+s.out+(y?", "+a+")":")");if(t==="category"&&e===null&&n==="out")return s.unknown_category+" ("+s.out+(y?", "+a+")":")");if(t==="account"&&e===null&&n==="in")return s.unknown_source+(y?" ("+a+")":"");if(t==="account"&&e!==null&&n==="in")return s.revenue_account+'"'+e+'"'+(y?" ("+a+")":"");if(t==="account"&&e===null&&n==="out")return s.unknown_dest+(y?" ("+a+")":"");if(t==="account"&&e!==null&&n==="out")return s.expense_account+' "'+e+'"'+(y?" ("+a+")":"");if(t==="budget"&&e!==null)return s.budget+' "'+e+'"'+(y?" ("+a+")":"");if(t==="budget"&&e===null)return s.unknown_budget+(y?" ("+a+")":"");console.error('Cannot handle: type:"'+t+'", dir: "'+n+'"')}function E(t,e,n){if(t==="category"&&e!==null)return s.category+' "'+e+'"'+(y?" ("+n+")":"");if(t==="category"&&e===null)return s.unknown_category+(y?" ("+n+")":"");if(t==="account"&&e===null)return s.unknown_account+(y?" ("+n+")":"");if(t==="account"&&e!==null)return e+(y?" ("+n+")":"");if(t==="budget"&&e!==null)return s.budget+' "'+e+'"'+(y?" ("+n+")":"");if(t==="budget"&&e===null)return s.unknown_budget+(y?" ("+n+")":"");console.error('Cannot handle: type:"'+t+'"')}const Qt=()=>({loading:!1,autoConversion:!1,generateOptions(){let t=N("sankey"),e={},n={};for(let r in D)if(D.hasOwnProperty(r)){let o=D[r];for(let i in o.attributes.transactions)if(o.attributes.transactions.hasOwnProperty(i)){let l=o.attributes.transactions[i],u=this.autoConversion?l.native_currency_code:l.currency_code,d=this.autoConversion?parseFloat(l.native_amount):parseFloat(l.amount),h;if(l.type==="deposit"){let c=L("category",l.category_name,"in",u),_=L("account",l.source_name,"in",u);n[c]=E("category",l.category_name,u),n[_]=E("account",l.source_name,u),h=_+"-"+c+"-"+u,e.hasOwnProperty(h)||(e[h]={from:_,to:c,amount:0}),e[h].amount+=d,h=c+"-"+s.all_money+"-"+u,e.hasOwnProperty(h)||(e[h]={from:c,to:s.all_money+(this.autoConversion?" ("+u+")":""),amount:0}),e[h].amount+=d}if(l.type==="withdrawal"){let c=L("budget",l.budget_name,"out",u);n[c]=E("budget",l.budget_name,u),h=s.all_money+"-"+c+"-"+u,e.hasOwnProperty(h)||(e[h]={from:s.all_money+(this.autoConversion?" ("+u+")":""),to:c,amount:0}),e[h].amount+=d;let _=L("category",l.category_name,"out",u);n[_]=E("category",l.category_name,u),h=c+"-"+_+"-"+u,e.hasOwnProperty(h)||(e[h]={from:c,to:_,amount:0}),e[h].amount+=d;let w=L("account",l.destination_name,"out",u);n[w]=E("account",l.destination_name,u),h=_+"-"+w+"-"+u,e.hasOwnProperty(h)||(e[h]={from:_,to:w,amount:0}),e[h].amount+=d}}}let a={label:"Firefly III dashboard sankey chart",data:[],colorFrom:r=>dt(r.dataset.data[r.dataIndex]?r.dataset.data[r.dataIndex].from:""),colorTo:r=>dt(r.dataset.data[r.dataIndex]?r.dataset.data[r.dataIndex].to:""),colorMode:"gradient",labels:n,size:"min"};for(let r in e)if(e.hasOwnProperty(r)){let o=e[r];a.data.push({from:o.from,to:o.to,flow:o.amount})}return t.data.datasets.push(a),t},drawChart(t){if(R!==null){R.data.datasets=t.data.datasets,R.update();return}R=new S(document.querySelector("#sankey-chart"),t)},getFreshData(){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),n=P(ct,t,e),a=window.store.get("cacheValid");let r=window.store.get(n);if(a&&typeof r<"u"){D=r,this.drawChart(this.generateOptions()),this.loading=!1;return}let o={start:g(t,"y-MM-dd"),end:g(e,"y-MM-dd"),type:"withdrawal,deposit",page:1};this.downloadTransactions(o)},downloadTransactions(t){const e=new Date(window.store.get("start")),n=new Date(window.store.get("end")),a=P(ct,e,n);new $t().list(t).then(o=>{if(D=[...D,...o.data.data],parseInt(o.data.meta.pagination.total_pages)>t.page){t.page++,this.downloadTransactions(t);return}window.store.set(a,D),this.drawChart(this.generateOptions()),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,D.length!==0){this.drawChart(this.generateOptions()),this.loading=!1;return}this.getFreshData()}},init(){D=[],Promise.all([v("autoConversion",!1)]).then(t=>{this.autoConversion=t[0],y=t[0],s.all_money=p.t("firefly.all_money"),s.category=p.t("firefly.category"),s.in=p.t("firefly.money_flowing_in"),s.out=p.t("firefly.money_flowing_out"),s.unknown_category=p.t("firefly.unknown_category_plain"),s.unknown_source=p.t("firefly.unknown_source_plain"),s.unknown_dest=p.t("firefly.unknown_dest_plain"),s.unknown_account=p.t("firefly.unknown_any_plain"),s.unknown_budget=p.t("firefly.unknown_budget_plain"),s.expense_account=p.t("firefly.expense_account"),s.revenue_account=p.t("firefly.revenue_account"),s.budget=p.t("firefly.budget"),X=!0,this.loadChart()}),window.store.observe("end",()=>{X&&(this.transactions=[],this.loadChart())}),window.store.observe("autoConversion",t=>{X&&(this.autoConversion=t,this.loadChart())})}});let tt=!1,b={};function gt(t){return new Vt().list(t).then(n=>{let a=n.data.data;for(let r in a)if(a.hasOwnProperty(r)){let o=a[r];if(o.attributes.active&&o.attributes.pay_dates.length>0){let i=o.attributes.object_group_id===null?0:o.attributes.object_group_id,l=o.attributes.object_group_title===null?p.t("firefly.default_group_title_name_plain"):o.attributes.object_group_title,u=o.attributes.object_group_order===null?0:o.attributes.object_group_order;b.hasOwnProperty(i)||(b[i]={id:i,title:l,order:u,payment_info:{},bills:[]});let d={id:o.id,name:o.attributes.name,amount_min:o.attributes.amount_min,amount_max:o.attributes.amount_max,amount:(parseFloat(o.attributes.amount_max)+parseFloat(o.attributes.amount_min))/2,currency_code:o.attributes.currency_code,native_amount_min:o.attributes.native_amount_min,native_amount_max:o.attributes.native_amount_max,native_amount:(parseFloat(o.attributes.native_amount_max)+parseFloat(o.attributes.native_amount_min))/2,native_currency_code:o.attributes.native_currency_code,transactions:[],pay_dates:o.attributes.pay_dates,paid:o.attributes.paid_dates.length>0};d.expected_amount=t.autoConversion?f(d.native_amount,d.native_currency_code):f(d.amount,d.currency_code),d.expected_times=p.t("firefly.subscr_expected_x_times",{times:o.attributes.pay_dates.length,amount:d.expected_amount});for(let h in o.attributes.paid_dates)if(o.attributes.paid_dates.hasOwnProperty(h)){const c=o.attributes.paid_dates[h];let _=100;t.autoConversion&&(_=Math.round(-100+parseFloat(c.native_amount)*-1/parseFloat(d.native_amount)*100)),t.autoConversion||(_=Math.round(-100+parseFloat(c.amount)*-1/parseFloat(d.amount)*100));let w={amount:t.autoConversion?f(c.native_amount,c.native_currency_code):f(c.amount,c.currency_code),percentage:_,date:g(new Date(c.date),"PP"),foreign_amount:null};c.foreign_currency_code!==null&&(w.foreign_amount=t.autoConversion?c.foreign_native_amount:c.foreign_amount,w.foreign_currency_code=t.autoConversion?c.native_currency_code:c.foreign_currency_code),d.transactions.push(w)}if(b[i].bills.push(d),o.attributes.paid_dates.length===0){const h=o.attributes.pay_dates.length*d.amount,c=o.attributes.pay_dates.length*d.native_amount;b[i].payment_info.hasOwnProperty(d.currency_code)||(b[i].payment_info[d.currency_code]={currency_code:d.currency_code,paid:0,unpaid:0,native_currency_code:d.native_currency_code,native_paid:0,native_unpaid:0}),b[i].payment_info[d.currency_code].unpaid+=h,b[i].payment_info[d.currency_code].native_unpaid+=c}if(o.attributes.paid_dates.length>0){for(let h in o.attributes.paid_dates)if(o.attributes.paid_dates.hasOwnProperty(h)){let c=o.attributes.paid_dates[h];b[i].payment_info.hasOwnProperty(c.currency_code)||(b[i].payment_info[c.currency_code]={currency_code:d.currency_code,paid:0,unpaid:0,native_currency_code:d.native_currency_code,native_paid:0,native_unpaid:0});const _=parseFloat(c.amount)*-1,w=parseFloat(c.native_amount)*-1;b[i].payment_info[c.currency_code].paid+=_,b[i].payment_info[c.currency_code].native_paid+=w}}}}return parseInt(n.data.meta.pagination.total_pages)>t.page?(t.page++,gt(t)):Promise.resolve()})}const Xt=()=>({loading:!1,autoConversion:!1,subscriptions:[],startSubscriptions(){this.loading=!0;let t=new Date(window.store.get("start")),e=new Date(window.store.get("end"));const n=window.store.get("cacheValid");let a=window.store.get(P("subscriptions-data-dashboard",t,e));n&&typeof a<"u",b={},this.subscriptions=[];let r={start:g(t,"y-MM-dd"),end:g(e,"y-MM-dd"),autoConversion:this.autoConversion,page:1};gt(r).then(()=>{let o=Object.values(b);for(let i in o)if(o.hasOwnProperty(i)){let l=o[i];const u=Object.keys(l.payment_info);l.col_size=u.length===1?"col-6 offset-3":"col-6",l.chart_width=u.length===1?"50%":"100%",l.payment_length=u.length,this.subscriptions.push(l)}this.loading=!1})},drawPieChart(t,e,n){let a="#pie_"+t+"_"+n.currency_code;const r=this.autoConversion?n.native_unpaid:n.unpaid,o=this.autoConversion?n.native_paid:n.paid,i=this.autoConversion?n.native_currency_code:n.currency_code,u={type:"doughnut",data:{labels:[p.t("firefly.paid"),p.t("firefly.unpaid")],datasets:[{label:p.t("firefly.subscriptions_in_group",{title:e}),data:[o,r],backgroundColor:["rgb(54, 162, 235)","rgb(255, 99, 132)"],hoverOffset:4}]},options:{plugins:{tooltip:{callbacks:{label:function(h){return h.dataset.label+": "+f(h.raw,i)}}}}}};var d=S.getChart(document.querySelector(a));typeof d<"u"&&d.destroy(),new S(document.querySelector(a),u)},init(){Promise.all([v("autoConversion",!1)]).then(t=>{this.autoConversion=t[0],tt=!0,this.loading===!1&&this.startSubscriptions()}),window.store.observe("end",()=>{tt&&this.loading===!1&&this.startSubscriptions()}),window.store.observe("autoConversion",t=>{tt&&(this.autoConversion=t,this.loading===!1&&this.startSubscriptions())})}});let x={},et=!1;const ht="dashboard-piggies-data",te=()=>({loading:!1,autoConversion:!1,sankeyGrouping:"account",piggies:[],getFreshData(){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),n=P(ht,t,e),a=window.store.get("cacheValid");let r=window.store.get(n);if(a&&typeof r<"u"){x=r,this.parsePiggies(),this.loading=!1;return}let o={start:g(t,"y-MM-dd"),end:g(e,"y-MM-dd"),page:1};this.downloadPiggyBanks(o)},downloadPiggyBanks(t){const e=new Date(window.store.get("start")),n=new Date(window.store.get("end")),a=P(ht,e,n);new Kt().list(t).then(o=>{if(x=[...x,...o.data.data],parseInt(o.data.meta.pagination.total_pages)>t.page){t.page++,this.downloadPiggyBanks(t);return}window.store.set(a,x),this.parsePiggies(),this.loading=!1})},parsePiggies(){let t=[];for(let e in x)if(x.hasOwnProperty(e)){let n=x[e];if(n.attributes.percentage>=100||n.attributes.percentage===0)continue;let a=n.object_group_title??p.t("firefly.default_group_title_name_plain");t.hasOwnProperty(a)||(t[a]={id:n.object_group_id??0,title:a,order:n.object_group_order??0,piggies:[]});let r={id:n.id,name:n.attributes.name,percentage:parseInt(n.attributes.percentage),amount:this.autoConversion?n.attributes.native_current_amount:n.attributes.current_amount,left_to_save:this.autoConversion?n.attributes.native_left_to_save:n.attributes.left_to_save,target_amount:this.autoConversion?n.attributes.native_target_amount:n.attributes.target_amount,save_per_month:this.autoConversion?n.attributes.native_save_per_month:n.attributes.save_per_month,currency_code:this.autoConversion?n.attributes.native_currency_code:n.attributes.currency_code};t[a].piggies.push(r)}this.piggies=Object.values(t)},loadPiggyBanks(){if(this.loading!==!0){if(this.loading=!0,this.piggies.length!==0){this.parsePiggies(),this.loading=!1;return}this.getFreshData()}},init(){x=[],Promise.all([v("autoConversion",!1)]).then(t=>{et=!0,this.autoConversion=t[0],this.loadPiggyBanks()}),window.store.observe("end",()=>{et&&(x=[],this.loadPiggyBanks())}),window.store.observe("autoConversion",t=>{et&&(this.autoConversion=t,this.loadPiggyBanks())})}});S.register({LineController:Ct,LineElement:kt,ArcElement:Pt,BarController:Dt,TimeScale:xt,PieController:Ot,BarElement:Mt,Filler:Ft,Colors:St,LinearScale:At,CategoryScale:Bt,PointElement:jt,Tooltip:Wt,Legend:It});const pt={dates:wt,boxes:Et,accounts:Ut,budgets:Ht,categories:Zt,sankey:Qt,subscriptions:Xt,piggies:te};function _t(t){Object.keys(t).forEach(e=>{let n=t[e]();Alpine.data(e,()=>n)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),_t(pt)});window.bootstrapped&&(console.log("Loaded through window variable."),_t(pt)); diff --git a/public/build/assets/dashboard-e693d6f4.js b/public/build/assets/dashboard-e693d6f4.js deleted file mode 100644 index 28a741bebc..0000000000 --- a/public/build/assets/dashboard-e693d6f4.js +++ /dev/null @@ -1 +0,0 @@ -import{a as O,f as g,g as y,P as Pt,l as z,G as kt,b as Dt,d as xt}from"./get-c53daca3.js";import{f,C as m,a as A,I as H,S as Ot,F as Mt,L as St,b as Ft,A as At,B as Bt,T as $t,P as jt,c as It,i as Wt,p as Vt,d as Kt,e as Et,g as Lt,h as Gt,j as Tt}from"./vendor-4332182f.js";import{G as Rt}from"./get-c2292133.js";class Ut{get(t,n,a){return O.get("/api/v2/summary/basic",{params:{start:t,end:n,code:a}})}}function k(e,t,n){const a=f(t,"y-MM-dd")+"_"+f(n,"y-MM-dd")+"_"+e;return console.log("getCacheKey: "+a),String(a)}let Q=!1;const qt=()=>({balanceBox:{amounts:[],subtitles:[]},billBox:{paid:[],unpaid:[]},leftBox:{left:[],perDay:[]},netBox:{net:[]},autoConversion:!1,loading:!1,boxData:null,boxOptions:null,getFreshData(){const e=new Date(window.store.get("start")),t=new Date(window.store.get("end")),n=k("dashboard-boxes-data",e,t),a=window.store.get("cacheValid");let o=window.store.get(n);if(a&&typeof o<"u"){this.boxData=o,this.generateOptions(this.boxData);return}new Ut().get(f(e,"yyyy-MM-dd"),f(t,"yyyy-MM-dd"),null).then(i=>{this.boxData=i.data,window.store.set(n,i.data),this.generateOptions(this.boxData)})},generateOptions(e){this.balanceBox={amounts:[],subtitles:[]},this.billBox={paid:[],unpaid:[]},this.leftBox={left:[],perDay:[]},this.netBox={net:[]};let t={};for(const n in e)if(e.hasOwnProperty(n)){const a=e[n];if(!a.hasOwnProperty("key"))continue;let o=a.key;if(this.autoConversion){if(o.startsWith("balance-in-native")){this.balanceBox.amounts.push(g(a.value,a.currency_code)),t.hasOwnProperty(a.currency_code)||(t[a.currency_code]="");continue}if(o.startsWith("spent-in-native")){t.hasOwnProperty(a.currency_code)||(t[a.currency_code]=""),t[a.currency_code]=t[a.currency_code]+g(a.value,a.currency_code);continue}if(o.startsWith("earned-in-native")){t.hasOwnProperty(a.currency_code)||(t[a.currency_code]=""),t[a.currency_code]=g(a.value,a.currency_code)+" + "+t[a.currency_code];continue}if(o.startsWith("bills-unpaid-in-native")){this.billBox.unpaid.push(g(a.value,a.currency_code));continue}if(o.startsWith("bills-paid-in-native")){this.billBox.paid.push(g(a.value,a.currency_code));continue}if(o.startsWith("left-to-spend-in-native")){this.leftBox.left.push(g(a.value,a.currency_code));continue}if(o.startsWith("left-per-day-to-spend-in-native")){this.leftBox.perDay.push(g(a.value,a.currency_code));continue}if(o.startsWith("net-worth-in-native")){this.netBox.net.push(g(a.value,a.currency_code));continue}}if(!this.autoConversion&&!o.endsWith("native")){if(o.startsWith("balance-in-")){this.balanceBox.amounts.push(g(a.value,a.currency_code));continue}if(o.startsWith("spent-in-")){t.hasOwnProperty(a.currency_code)||(t[a.currency_code]=""),t[a.currency_code]=t[a.currency_code]+g(a.value,a.currency_code);continue}if(o.startsWith("earned-in-")){t.hasOwnProperty(a.currency_code)||(t[a.currency_code]=""),t[a.currency_code]=g(a.value,a.currency_code)+" + "+t[a.currency_code];continue}if(o.startsWith("bills-unpaid-in-")){this.billBox.unpaid.push(g(a.value,a.currency_code));continue}if(o.startsWith("bills-paid-in-")){this.billBox.paid.push(g(a.value,a.currency_code));continue}if(o.startsWith("left-to-spend-in-")){this.leftBox.left.push(g(a.value,a.currency_code));continue}if(o.startsWith("left-per-day-to-spend-in-")){this.leftBox.perDay.push(g(a.value,a.currency_code));continue}o.startsWith("net-worth-in-")&&this.netBox.net.push(g(a.value,a.currency_code))}}for(let n in t)t.hasOwnProperty(n)&&this.balanceBox.subtitles.push(t[n]);this.loading=!1},loadBoxes(){if(this.loading!==!0){if(this.loading=!0,this.boxData===null){this.getFreshData();return}this.generateOptions(this.boxData),this.loading=!1}},init(){Promise.all([y("viewRange"),y("autoConversion",!1)]).then(e=>{Q=!0,this.autoConversion=e[1],this.loadBoxes()}),window.store.observe("end",()=>{Q&&(this.boxData=null,this.loadBoxes())}),window.store.observe("autoConversion",e=>{Q&&(this.autoConversion=e,this.loadBoxes())})}});class Nt{put(t,n){let a="/api/v1/preferences/"+t;return O.put(a,{data:n})}}function Yt(e,t=null){window.store.set(e,t),new Nt().put(e,t).then(a=>{}).catch(()=>{new Pt().post(e,t).then(o=>{})})}let zt=class{dashboard(t,n){let a=f(t,"y-MM-dd"),o=f(n,"y-MM-dd");return O.get("/api/v2/chart/account/dashboard",{params:{start:a,end:o}})}expense(t,n){let a=f(t,"y-MM-dd"),o=f(n,"y-MM-dd");return O.get("/api/v2/chart/account/expense-dashboard",{params:{start:a,end:o}})}};class ht{get(t,n){let a={date:f(n,"y-MM-dd").slice(0,10)};return n?O.get("/api/v2/accounts/"+t,{params:a}):O.get("/api/v2/accounts/"+t)}transactions(t,n){const a={page:n.page??1};return n.hasOwnProperty("start")&&(a.start=f(n.start,"y-MM-dd")),n.hasOwnProperty("end")&&(a.end=f(n.end,"y-MM-dd")),O.get("/api/v2/accounts/"+t+"/transactions",{params:a})}}function J(e){return e==="sankey"?{type:"sankey",data:{datasets:[]},options:{animations:!1}}:e==="pie"?{type:"pie",data:{datasets:[]}}:e==="column"?{type:"bar",data:{labels:[],datasets:[]},options:{plugins:{tooltip:{callbacks:{label:function(t){let n=t.dataset.currency_code;return g(t.raw,n)}}}},maintainAspectRatio:!1,scales:{}}}:e==="line"?{options:{plugins:{tooltip:{callbacks:{label:function(t){let n=t.dataset.currency_code;return g(t.raw,n)}}}},maintainAspectRatio:!1,scales:{x:{type:"time",time:{tooltipFormat:"PP"}}}},type:"line",data:{labels:[],datasets:[]}}:{}}let Y=new m("#36a2eb"),V=new m("#ff6384"),R=new m("#4bc0c0"),yt=new m("#ff9f40"),Ht=new m("#9966ff"),Jt=new m("#ffcd56"),Zt=new m("#c9cbcf"),gt=0;window.theme==="dark"&&(V.darken(.3).desaturate(.3),R.darken(.3).desaturate(.3),Y.darken(.3).desaturate(.3),yt.darken(.3).desaturate(.3));let X=[V,yt,Y,R,Ht,Jt,Zt,R];function j(e,t){let n={borderColor:V.rgbString(),backgroundColor:V.rgbString()},a;switch(e){default:let r=Math.floor(gt/2)%X.length;a=new m(X[r].rgbString()),a.lighten(.38),n={borderColor:X[r].hexString(),backgroundColor:a.hexString()};break;case"spent":a=new m(Y.rgbString()),a.lighten(.38),n={borderColor:Y.rgbString(),backgroundColor:a.rgbString()};break;case"left":a=new m(R.rgbString()),a.lighten(.38),n={borderColor:R.rgbString(),backgroundColor:a.rgbString()};break;case"overspent":a=new m(V.rgbString()),a.lighten(.22),n={borderColor:V.rgbString(),backgroundColor:a.rgbString()};break}return gt++,t==="border"?n.borderColor:t==="background"?n.backgroundColor:"#FF0000"}let B=[],K=null,tt=null,et=!1;const Qt=()=>({loading:!1,loadingAccounts:!1,accountList:[],autoConversion:!1,chartOptions:null,switchAutoConversion(){this.autoConversion=!this.autoConversion,Yt("autoConversion",this.autoConversion)},getFreshData(){const e=new Date(window.store.get("start")),t=new Date(window.store.get("end")),n=k("dashboard-accounts-chart",e,t),a=window.store.get("cacheValid");let o=window.store.get(n);if(a&&typeof o<"u"){console.log(o),this.drawChart(this.generateOptions(o)),this.loading=!1;return}new zt().dashboard(e,t,null).then(i=>{this.chartData=i.data,window.store.set(n,i.data),console.log(i.data),this.drawChart(this.generateOptions(this.chartData)),this.loading=!1})},generateOptions(e){B=[];let t=J("line");for(let n=0;n0){this.loadingAccounts=!1;return}const e=new Date(window.store.get("start")),t=new Date(window.store.get("end")),n=k("dashboard-accounts-data",e,t),a=window.store.get("cacheValid");let o=window.store.get(n);if(a&&typeof o<"u"){this.accountList=o,this.loadingAccounts=!1;return}const r=10;let i=0,l=0,u=[];Promise.all([y("frontpageAccounts")]).then(d=>{i=d[0].length;for(let h in d[0]){let c=d[0];if(c.hasOwnProperty(h)){let p=c[h];new ht().get(p,new Date(window.store.get("end"))).then(v=>{let C=v.data.data;const Ct={page:1,start:new Date(window.store.get("start")),end:new Date(window.store.get("end"))};new ht().transactions(C.id,Ct).then(st=>{let lt=[];for(let $=0;$=r);$++){let M=st.data.data[$],ut={title:M.attributes.group_title===null?"":M.attributes.group_title,id:M.id,transactions:[]};for(let Z=0;Z$.order-M.order),this.accountList=u,this.loadingAccounts=!1,window.store.set(n,u))})})}}})},init(){Promise.all([y("viewRange","1M"),y("autoConversion",!1),y("language","en_US")]).then(e=>{this.autoConversion=e[1],et=!0,this.loadChart(),this.loadAccounts()}),window.store.observe("end",()=>{et&&(tt=null,this.accountList=[],this.loadChart(),this.loadAccounts())}),window.store.observe("autoConversion",()=>{et&&(this.loadChart(),this.loadAccounts())})}});let Xt=class{dashboard(t,n){let a=f(t,"y-MM-dd"),o=f(n,"y-MM-dd");return O.get("/api/v2/chart/budget/dashboard",{params:{start:a,end:o}})}},E=[],U=null,S=null,at=!1,I;const te=()=>({loading:!1,autoConversion:!1,loadChart(){if(this.loading!==!0){if(this.loading=!0,S!==null){this.drawChart(this.generateOptions(S)),this.loading=!1;return}this.getFreshData()}},drawChart(e){if(U!==null){U.data.datasets=e.data.datasets,U.update();return}U=new A(document.querySelector("#budget-chart"),e)},getFreshData(){const e=new Date(window.store.get("start")),t=new Date(window.store.get("end")),n=k("dashboard-budgets-chart",e,t),a=window.store.get("cacheValid");let o=window.store.get(n);if(a&&typeof o<"u"){S=o,this.drawChart(this.generateOptions(S)),this.loading=!1;return}new Xt().dashboard(e,t,null).then(i=>{S=i.data,this.drawChart(this.generateOptions(S)),window.store.set(n,S),this.loading=!1})},generateOptions(e){E=[];let t=J("column");t.options.locale=window.store.get("locale").replace("_","-"),t.options.plugins={tooltip:{callbacks:{title:function(n){return n.label},label:function(n){let a=n.dataset.label||"";return a&&(a+=": "),a+" "+g(n.parsed.y,E[n.parsed.x]??"EUR")}}}},t.data={labels:[],datasets:[{label:I.t("firefly.spent"),data:[],borderWidth:1,stack:1,backgroundColor:j("spent","background"),borderColor:j("spent","border")},{label:I.t("firefly.left"),data:[],borderWidth:1,stack:1,backgroundColor:j("left","background"),borderColor:j("left","border")},{label:I.t("firefly.overspent"),data:[],borderWidth:1,stack:1,backgroundColor:j("overspent","background"),borderColor:j("overspent","border")}]};for(const n in e)if(e.hasOwnProperty(n)){let a=e[n],o=a.label+" ("+a.currency_code+")";t.data.labels.push(o),this.autoConversion&&(E.push(a.native_currency_code),t.data.datasets[0].data.push(parseFloat(a.native_entries.spent)*-1),t.data.datasets[1].data.push(parseFloat(a.native_entries.left)),t.data.datasets[2].data.push(parseFloat(a.native_entries.overspent))),this.autoConversion||(E.push(a.currency_code),t.data.datasets[0].data.push(parseFloat(a.entries.spent)*-1),t.data.datasets[1].data.push(parseFloat(a.entries.left)),t.data.datasets[2].data.push(parseFloat(a.entries.overspent)))}return t.options.scales={y:{ticks:{callback:function(n){return g(n,E[0]??"EUR")}}}},t},init(){Promise.all([y("autoConversion",!1),y("language","en_US")]).then(e=>{I=new H;const t=e[1].replace("-","_");I.locale=t,z(I,t).then(()=>{this.autoConversion=e[0],at=!0,this.loading===!1&&this.loadChart()})}),window.store.observe("end",()=>{at&&this.loading===!1&&(S=null,this.loadChart())}),window.store.observe("autoConversion",e=>{at&&(this.autoConversion=e,this.loading===!1&&this.loadChart())})}});class ee{dashboard(t,n){let a=f(t,"y-MM-dd"),o=f(n,"y-MM-dd");return O.get("/api/v2/chart/category/dashboard",{params:{start:a,end:o}})}}let ft=[],L=null,W=null,nt=!1;const ae=()=>({loading:!1,autoConversion:!1,generateOptions(e){ft=[];let t=J("column"),n={};for(const o in e)if(e.hasOwnProperty(o)){let r=e[o],i=r.currency_code;this.autoConversion&&(i=r.native_currency_code),n.hasOwnProperty(i)||(n[i]={name:i,yAxisID:"",data:{}},ft.push(i))}for(const o in e)if(e.hasOwnProperty(o)){let r=e[o],i=r.currency_code;this.autoConversion&&(i=r.native_currency_code);for(const l in n)if(n.hasOwnProperty(l)){let u=0;i===l&&(u=parseFloat(r.amount),""+r.currency_code,this.autoConversion&&(u=parseFloat(r.native_amount),""+r.native_currency_code)),n[l].data.hasOwnProperty(r.label)&&(n[l].data[r.label]=n[l].data[r.label]+u),n[l].data.hasOwnProperty(r.label)||(n[l].data[r.label]=u)}t.data.labels.includes(r.label)||t.data.labels.push(r.label)}let a=0;for(const o in n){let r="y"+o,i={label:o,currency_code:o,yAxisID:r,data:[]};for(const l in n[o].data)i.data.push(n[o].data[l]);t.data.datasets.push(i),t.options.scales.hasOwnProperty(r)||(t.options.scales[r]={beginAtZero:!0,type:"linear",position:a===1?"right":"left",ticks:{callback:function(l,u,d){return g(l,o)}}},a++)}return t},drawChart(e){if(L!==null){L.options=e.options,L.data=e.data,L.update();return}L=new A(document.querySelector("#category-chart"),e)},getFreshData(){const e=new Date(window.store.get("start")),t=new Date(window.store.get("end")),n=k("dashboard-categories-chart",e,t),a=window.store.get("cacheValid");let o=window.store.get(n);if(a&&typeof o<"u"){W=o,this.drawChart(this.generateOptions(W)),this.loading=!1;return}new ee().dashboard(e,t,null).then(i=>{W=i.data,this.drawChart(this.generateOptions(i.data)),window.store.set(n,W),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,W!==null){this.drawChart(this.generateOptions(W)),this.loading=!1;return}this.getFreshData()}},init(){Promise.all([y("autoConversion",!1)]).then(e=>{this.autoConversion=e[0],nt=!0,this.loadChart()}),window.store.observe("end",()=>{nt&&(this.chartData=null,this.loadChart())}),window.store.observe("autoConversion",e=>{nt&&(this.autoConversion=e,this.loadChart())})}});A.register({SankeyController:Ot,Flow:Mt});const pt="dashboard-sankey-data";let b,ot=!1,q=null,D=[],_=!1,s={category:null,unknown_category:null,in:null,out:null,unknown_source:null,unknown_dest:null,unknown_account:null,expense_account:null,revenue_account:null,budget:null,unknown_budget:null,all_money:null};const _t=function(e){return e.includes(s.revenue_account)?"forestgreen":e.includes("("+s.in+",")?"green":e.includes(s.budget)||e.includes(s.unknown_budget)?"Orchid":e.includes("("+s.out+",")?"MediumOrchid":e.includes(s.all_money)?"blue":"red"};function G(e,t,n,a){if(e==="category"&&t!==null&&n==="in")return s.category+' "'+t+'" ('+s.in+(_?", "+a+")":")");if(e==="category"&&t===null&&n==="in")return s.unknown_category+" ("+s.in+(_?", "+a+")":")");if(e==="category"&&t!==null&&n==="out")return s.category+' "'+t+'" ('+s.out+(_?", "+a+")":")");if(e==="category"&&t===null&&n==="out")return s.unknown_category+" ("+s.out+(_?", "+a+")":")");if(e==="account"&&t===null&&n==="in")return s.unknown_source+(_?" ("+a+")":"");if(e==="account"&&t!==null&&n==="in")return s.revenue_account+'"'+t+'"'+(_?" ("+a+")":"");if(e==="account"&&t===null&&n==="out")return s.unknown_dest+(_?" ("+a+")":"");if(e==="account"&&t!==null&&n==="out")return s.expense_account+' "'+t+'"'+(_?" ("+a+")":"");if(e==="budget"&&t!==null)return s.budget+' "'+t+'"'+(_?" ("+a+")":"");if(e==="budget"&&t===null)return s.unknown_budget+(_?" ("+a+")":"");console.error('Cannot handle: type:"'+e+'", dir: "'+n+'"')}function T(e,t,n){if(e==="category"&&t!==null)return s.category+' "'+t+'"'+(_?" ("+n+")":"");if(e==="category"&&t===null)return s.unknown_category+(_?" ("+n+")":"");if(e==="account"&&t===null)return s.unknown_account+(_?" ("+n+")":"");if(e==="account"&&t!==null)return t+(_?" ("+n+")":"");if(e==="budget"&&t!==null)return s.budget+' "'+t+'"'+(_?" ("+n+")":"");if(e==="budget"&&t===null)return s.unknown_budget+(_?" ("+n+")":"");console.error('Cannot handle: type:"'+e+'"')}const ne=()=>({loading:!1,autoConversion:!1,generateOptions(){let e=J("sankey"),t={},n={};for(let o in D)if(D.hasOwnProperty(o)){let r=D[o];for(let i in r.attributes.transactions)if(r.attributes.transactions.hasOwnProperty(i)){let l=r.attributes.transactions[i],u=this.autoConversion?l.native_currency_code:l.currency_code,d=this.autoConversion?parseFloat(l.native_amount):parseFloat(l.amount),h;if(l.type==="deposit"){let c=G("category",l.category_name,"in",u),p=G("account",l.source_name,"in",u);n[c]=T("category",l.category_name,u),n[p]=T("account",l.source_name,u),h=p+"-"+c+"-"+u,t.hasOwnProperty(h)||(t[h]={from:p,to:c,amount:0}),t[h].amount+=d,h=c+"-"+s.all_money+"-"+u,t.hasOwnProperty(h)||(t[h]={from:c,to:s.all_money+(this.autoConversion?" ("+u+")":""),amount:0}),t[h].amount+=d}if(l.type==="withdrawal"){let c=G("budget",l.budget_name,"out",u);n[c]=T("budget",l.budget_name,u),h=s.all_money+"-"+c+"-"+u,t.hasOwnProperty(h)||(t[h]={from:s.all_money+(this.autoConversion?" ("+u+")":""),to:c,amount:0}),t[h].amount+=d;let p=G("category",l.category_name,"out",u);n[p]=T("category",l.category_name,u),h=c+"-"+p+"-"+u,t.hasOwnProperty(h)||(t[h]={from:c,to:p,amount:0}),t[h].amount+=d;let v=G("account",l.destination_name,"out",u);n[v]=T("account",l.destination_name,u),h=p+"-"+v+"-"+u,t.hasOwnProperty(h)||(t[h]={from:p,to:v,amount:0}),t[h].amount+=d}}}let a={label:"Firefly III dashboard sankey chart",data:[],colorFrom:o=>_t(o.dataset.data[o.dataIndex]?o.dataset.data[o.dataIndex].from:""),colorTo:o=>_t(o.dataset.data[o.dataIndex]?o.dataset.data[o.dataIndex].to:""),colorMode:"gradient",labels:n,size:"min"};for(let o in t)if(t.hasOwnProperty(o)){let r=t[o];a.data.push({from:r.from,to:r.to,flow:r.amount})}return e.data.datasets.push(a),e},drawChart(e){if(q!==null){q.data.datasets=e.data.datasets,q.update();return}q=new A(document.querySelector("#sankey-chart"),e)},getFreshData(){const e=new Date(window.store.get("start")),t=new Date(window.store.get("end")),n=k(pt,e,t),a=window.store.get("cacheValid");let o=window.store.get(n);if(a&&typeof o<"u"){D=o,this.drawChart(this.generateOptions()),this.loading=!1;return}let r={start:f(e,"y-MM-dd"),end:f(t,"y-MM-dd"),type:"withdrawal,deposit",page:1};this.downloadTransactions(r)},downloadTransactions(e){const t=new Date(window.store.get("start")),n=new Date(window.store.get("end")),a=k(pt,t,n);new Rt().list(e).then(r=>{if(D=[...D,...r.data.data],parseInt(r.data.meta.pagination.total_pages)>e.page){e.page++,this.downloadTransactions(e);return}window.store.set(a,D),this.drawChart(this.generateOptions()),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,D.length!==0){this.drawChart(this.generateOptions()),this.loading=!1;return}this.getFreshData()}},init(){D=[],Promise.all([y("autoConversion",!1),y("language","en_US")]).then(e=>{this.autoConversion=e[0],_=e[0],b=new H;const t=e[1].replace("-","_");b.locale=t,z(b,t).then(()=>{s.all_money=b.t("firefly.all_money"),s.category=b.t("firefly.category"),s.in=b.t("firefly.money_flowing_in"),s.out=b.t("firefly.money_flowing_out"),s.unknown_category=b.t("firefly.unknown_category_plain"),s.unknown_source=b.t("firefly.unknown_source_plain"),s.unknown_dest=b.t("firefly.unknown_dest_plain"),s.unknown_account=b.t("firefly.unknown_any_plain"),s.unknown_budget=b.t("firefly.unknown_budget_plain"),s.expense_account=b.t("firefly.expense_account"),s.revenue_account=b.t("firefly.revenue_account"),s.budget=b.t("firefly.budget"),ot=!0,this.loadChart()})}),window.store.observe("end",()=>{ot&&(this.transactions=[],this.loadChart())}),window.store.observe("autoConversion",e=>{ot&&(this.autoConversion=e,this.loadChart())})}});let rt=!1,F,w={};function wt(e){return new kt().list(e).then(n=>{let a=n.data.data;for(let o in a)if(a.hasOwnProperty(o)){let r=a[o];if(r.attributes.active&&r.attributes.pay_dates.length>0){let i=r.attributes.object_group_id===null?0:r.attributes.object_group_id,l=r.attributes.object_group_title===null?F.t("firefly.default_group_title_name_plain"):r.attributes.object_group_title,u=r.attributes.object_group_order===null?0:r.attributes.object_group_order;w.hasOwnProperty(i)||(w[i]={id:i,title:l,order:u,payment_info:{},bills:[]});let d={id:r.id,name:r.attributes.name,amount_min:r.attributes.amount_min,amount_max:r.attributes.amount_max,amount:(parseFloat(r.attributes.amount_max)+parseFloat(r.attributes.amount_min))/2,currency_code:r.attributes.currency_code,native_amount_min:r.attributes.native_amount_min,native_amount_max:r.attributes.native_amount_max,native_amount:(parseFloat(r.attributes.native_amount_max)+parseFloat(r.attributes.native_amount_min))/2,native_currency_code:r.attributes.native_currency_code,transactions:[],pay_dates:r.attributes.pay_dates,paid:r.attributes.paid_dates.length>0};d.expected_amount=e.autoConversion?g(d.native_amount,d.native_currency_code):g(d.amount,d.currency_code),d.expected_times=F.t("firefly.subscr_expected_x_times",{times:r.attributes.pay_dates.length,amount:d.expected_amount});for(let h in r.attributes.paid_dates)if(r.attributes.paid_dates.hasOwnProperty(h)){const c=r.attributes.paid_dates[h];let p=100;e.autoConversion&&(p=Math.round(-100+parseFloat(c.native_amount)*-1/parseFloat(d.native_amount)*100)),e.autoConversion||(p=Math.round(-100+parseFloat(c.amount)*-1/parseFloat(d.amount)*100));let v={amount:e.autoConversion?g(c.native_amount,c.native_currency_code):g(c.amount,c.currency_code),percentage:p,date:f(new Date(c.date),"PP"),foreign_amount:null};c.foreign_currency_code!==null&&(v.foreign_amount=e.autoConversion?c.foreign_native_amount:c.foreign_amount,v.foreign_currency_code=e.autoConversion?c.native_currency_code:c.foreign_currency_code),d.transactions.push(v)}if(w[i].bills.push(d),r.attributes.paid_dates.length===0){const h=r.attributes.pay_dates.length*d.amount,c=r.attributes.pay_dates.length*d.native_amount;w[i].payment_info.hasOwnProperty(d.currency_code)||(w[i].payment_info[d.currency_code]={currency_code:d.currency_code,paid:0,unpaid:0,native_currency_code:d.native_currency_code,native_paid:0,native_unpaid:0}),w[i].payment_info[d.currency_code].unpaid+=h,w[i].payment_info[d.currency_code].native_unpaid+=c}if(r.attributes.paid_dates.length>0){for(let h in r.attributes.paid_dates)if(r.attributes.paid_dates.hasOwnProperty(h)){let c=r.attributes.paid_dates[h];w[i].payment_info.hasOwnProperty(c.currency_code)||(w[i].payment_info[c.currency_code]={currency_code:d.currency_code,paid:0,unpaid:0,native_currency_code:d.native_currency_code,native_paid:0,native_unpaid:0});const p=parseFloat(c.amount)*-1,v=parseFloat(c.native_amount)*-1;w[i].payment_info[c.currency_code].paid+=p,w[i].payment_info[c.currency_code].native_paid+=v}}}}return parseInt(n.data.meta.pagination.total_pages)>e.page?(e.page++,wt(e)):Promise.resolve()})}const oe=()=>({loading:!1,autoConversion:!1,subscriptions:[],startSubscriptions(){this.loading=!0;let e=new Date(window.store.get("start")),t=new Date(window.store.get("end"));const n=window.store.get("cacheValid");let a=window.store.get(k("subscriptions-data-dashboard",e,t));n&&typeof a<"u",w={},this.subscriptions=[],console.log("cache is invalid, must download");let o={start:f(e,"y-MM-dd"),end:f(t,"y-MM-dd"),autoConversion:this.autoConversion,page:1};wt(o).then(()=>{console.log("Done with download!");let r=Object.values(w);for(let i in r)if(r.hasOwnProperty(i)){let l=r[i];const u=Object.keys(l.payment_info);l.col_size=u.length===1?"col-6 offset-3":"col-6",l.chart_width=u.length===1?"50%":"100%",l.payment_length=u.length,this.subscriptions.push(l)}this.loading=!1})},drawPieChart(e,t,n){let a="#pie_"+e+"_"+n.currency_code;const o=this.autoConversion?n.native_unpaid:n.unpaid,r=this.autoConversion?n.native_paid:n.paid,i=this.autoConversion?n.native_currency_code:n.currency_code,u={type:"doughnut",data:{labels:[F.t("firefly.paid"),F.t("firefly.unpaid")],datasets:[{label:F.t("firefly.subscriptions_in_group",{title:t}),data:[r,o],backgroundColor:["rgb(54, 162, 235)","rgb(255, 99, 132)"],hoverOffset:4}]},options:{plugins:{tooltip:{callbacks:{label:function(h){return h.dataset.label+": "+g(h.raw,i)}}}}}};var d=A.getChart(document.querySelector(a));typeof d<"u"&&d.destroy(),new A(document.querySelector(a),u)},init(){console.log("subscriptions init"),Promise.all([y("autoConversion",!1),y("language","en_US")]).then(e=>{console.log("subscriptions after promises"),this.autoConversion=e[0],rt=!0,F=new H;const t=e[1].replace("-","_");F.locale=t,z(F,t).then(()=>{this.loading===!1&&this.startSubscriptions()})}),window.store.observe("end",()=>{rt&&(console.log("subscriptions observe end"),this.loading===!1&&this.startSubscriptions())}),window.store.observe("autoConversion",e=>{rt&&(console.log("subscriptions observe autoConversion"),this.autoConversion=e,this.loading===!1&&this.startSubscriptions())})}});let x={},it=!1,N;const bt="dashboard-piggies-data",re=()=>({loading:!1,autoConversion:!1,sankeyGrouping:"account",piggies:[],getFreshData(){const e=new Date(window.store.get("start")),t=new Date(window.store.get("end")),n=k(bt,e,t),a=window.store.get("cacheValid");let o=window.store.get(n);if(a&&typeof o<"u"){x=o,this.parsePiggies(),this.loading=!1;return}let r={start:f(e,"y-MM-dd"),end:f(t,"y-MM-dd"),page:1};this.downloadPiggyBanks(r)},downloadPiggyBanks(e){const t=new Date(window.store.get("start")),n=new Date(window.store.get("end")),a=k(bt,t,n);new Dt().list(e).then(r=>{if(x=[...x,...r.data.data],parseInt(r.data.meta.pagination.total_pages)>e.page){e.page++,this.downloadPiggyBanks(e);return}window.store.set(a,x),this.parsePiggies(),this.loading=!1})},parsePiggies(){let e=[];for(let t in x)if(x.hasOwnProperty(t)){let n=x[t];if(n.attributes.percentage>=100||n.attributes.percentage===0)continue;let a=n.object_group_title??N.t("firefly.default_group_title_name_plain");e.hasOwnProperty(a)||(e[a]={id:n.object_group_id??0,title:a,order:n.object_group_order??0,piggies:[]});let o={id:n.id,name:n.attributes.name,percentage:parseInt(n.attributes.percentage),amount:this.autoConversion?n.attributes.native_current_amount:n.attributes.current_amount,left_to_save:this.autoConversion?n.attributes.native_left_to_save:n.attributes.left_to_save,target_amount:this.autoConversion?n.attributes.native_target_amount:n.attributes.target_amount,save_per_month:this.autoConversion?n.attributes.native_save_per_month:n.attributes.save_per_month,currency_code:this.autoConversion?n.attributes.native_currency_code:n.attributes.currency_code};e[a].piggies.push(o)}this.piggies=Object.values(e)},loadPiggyBanks(){if(this.loading!==!0){if(this.loading=!0,this.piggies.length!==0){this.parsePiggies(),this.loading=!1;return}this.getFreshData()}},init(){x=[],Promise.all([y("autoConversion",!1),y("language","en_US")]).then(e=>{N=new H;const t=e[1].replace("-","_");N.locale=t,z(N,t).then(()=>{it=!0,this.autoConversion=e[0],this.loadPiggyBanks()})}),window.store.observe("end",()=>{it&&(x=[],this.loadPiggyBanks())}),window.store.observe("autoConversion",e=>{it&&(this.autoConversion=e,this.loadPiggyBanks())})}});A.register({LineController:St,LineElement:Ft,ArcElement:At,BarController:Bt,TimeScale:$t,PieController:jt,BarElement:It,Filler:Wt,Colors:Vt,LinearScale:Kt,CategoryScale:Et,PointElement:Lt,Tooltip:Gt,Legend:Tt});const vt={dates:xt,boxes:qt,accounts:Qt,budgets:te,categories:ae,sankey:ne,subscriptions:oe,piggies:re};function mt(e){Object.keys(e).forEach(t=>{console.log(`Loading page component "${t}"`);let n=e[t]();Alpine.data(t,()=>n)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{mt(vt)});window.bootstrapped&&mt(vt); diff --git a/public/build/assets/edit-070e8524.js b/public/build/assets/edit-070e8524.js new file mode 100644 index 0000000000..25819db416 --- /dev/null +++ b/public/build/assets/edit-070e8524.js @@ -0,0 +1 @@ +import{a as p,d as f,f as l}from"./format-money-bcfc2969.js";import{G as g}from"./get-32a7112a.js";import{p as m}from"./parse-downloaded-splits-d8bf7632.js";import{p as w,d as y,e as a,f as b,g as S,h as P,i as v,l as C,a as A,b as _,c as E,s as D,j as T,k as u,m as c}from"./splice-errors-into-transactions-66181f21.js";import{i as r,m as x}from"./vendor-5cffaf70.js";import{d as B}from"./create-empty-split-c3730b85.js";import"./get-fa539542.js";class L{put(s,o){let e="/api/v2/transactions/"+parseInt(o.id);return p.put(e,s)}}const n=T();let U=function(){return{entries:[],originals:[],formStates:{loadingCurrencies:!0,loadingBudgets:!0,loadingPiggyBanks:!0,loadingSubscriptions:!0,isSubmitting:!1,returnHereButton:!1,saveAsNewButton:!1,resetButton:!0,rulesButton:!0,webhooksButton:!0},formBehaviour:{formType:"edit",foreignCurrencyEnabled:!0},formData:{defaultCurrency:null,enabledCurrencies:[],nativeCurrencies:[],foreignCurrencies:[],budgets:[],piggyBanks:[],subscriptions:[]},groupProperties:{transactionType:"unknown",title:null,editTitle:null,id:null,totalAmount:0},notifications:{error:{show:!1,text:"",url:""},success:{show:!1,text:"",url:""},wait:{show:!1,text:""}},submitTransaction(){this.notifications.error.show=!1,this.notifications.success.show=!1,this.notifications.wait.show=!1;for(let e in this.entries)this.entries.hasOwnProperty(e)&&(this.entries[e].errors=B());this.formStates.isSubmitting=!0;let t=w(this.entries,this.originals,this.groupProperties.transactionType),s={group_title:this.groupProperties.editTitle,fire_webhooks:this.formStates.webhooksButton,apply_rules:this.formStates.rulesButton,transactions:t};this.groupProperties.title===null&&t.length>1&&(s.group_title=t[0].description);let o=new L;console.log(s),o.put(s,{id:this.groupProperties.id}).then(e=>{const i=e.data.data;if(this.groupProperties.id=parseInt(i.id),this.groupProperties.title=i.attributes.group_title??i.attributes.transactions[0].description,y(this.groupProperties.id,i.attributes.transactions)>0){this.notifications.wait.show=!0,this.notifications.wait.text=r.t("firefly.wait_attachments");return}this.showMessageOrRedirectUser()}).catch(e=>{this.submitting=!1,console.log(e),typeof e.response<"u"&&this.parseErrors(e.response.data)})},filters:{source:[],destination:[]},addedSplit(){setTimeout(()=>{const t=function(s,o,e){return s.name_with_balance+'
'+r.t("firefly.account_type_"+s.type)+""};a({selector:"input.ac-source",serverUrl:n.account,filters:this.filters.source,onRenderItem:t,onChange:b,onSelectItem:S}),a({selector:"input.ac-dest",serverUrl:n.account,filters:this.filters.destination,onRenderItem:t,onChange:P,onSelectItem:v}),a({selector:"input.ac-category",serverUrl:n.category,valueField:"id",labelField:"name",onChange:u,onSelectItem:u}),a({selector:"input.ac-description",serverUrl:n.description,valueField:"id",labelField:"description",onChange:c,onSelectItem:c})},250)},changedDateTime(t){console.warn("changedDateTime, event is not used")},changedDescription(t){console.warn("changedDescription, event is not used")},changedDestinationAccount(t){console.warn("changedDestinationAccount, event is not used")},changedSourceAccount(t){console.warn("changedSourceAccount, event is not used")},formattedTotalAmount(){return this.entries.length===0?l(this.groupProperties.totalAmount,"EUR"):l(this.groupProperties.totalAmount,this.entries[0].currency_code??"EUR")},getTags(t){return console.log("at get tags "+t),console.log(this.entries[t].tags),this.entries[t].tags??[]},getTransactionGroup(){this.entries=[];const t=window.location.href.split("/"),s=parseInt(t[t.length-1]);new g().show(s,{}).then(e=>{const i=e.data.data;this.groupProperties.id=parseInt(i.id),this.groupProperties.transactionType=i.attributes.transactions[0].type.toLowerCase(),this.groupProperties.title=i.attributes.title??i.attributes.transactions[0].description,this.entries=m(i.attributes.transactions),this.notifications.wait.show=!1}).then(()=>{this.groupProperties.totalAmount=0;for(let e in this.entries)this.entries.hasOwnProperty(e)&&(this.groupProperties.totalAmount=this.groupProperties.totalAmount+parseFloat(this.entries[e].amount),this.filters.source.push(this.entries[e].source_account.type),this.filters.destination.push(this.entries[e].destination_account.type));console.log(this.filters),setTimeout(()=>{x.init("select.ac-tags",{allowClear:!0,server:n.tag,liveServer:!0,clearEnd:!0,allowNew:!0,notFoundMessage:r.t("firefly.nothing_found"),noCache:!0,fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}}})},150)})},init(){this.notifications.wait.show=!0,this.notifications.wait.text=r.t("firefly.wait_loading_transaction"),this.getTransactionGroup(),C().then(t=>{this.formStates.loadingCurrencies=!1,this.formData.defaultCurrency=t.defaultCurrency,this.formData.enabledCurrencies=t.enabledCurrencies,this.formData.nativeCurrencies=t.nativeCurrencies,this.formData.foreignCurrencies=t.foreignCurrencies}),A().then(t=>{this.formData.budgets=t,this.formStates.loadingBudgets=!1}),_().then(t=>{this.formData.piggyBanks=t,this.formStates.loadingPiggyBanks=!1}),E().then(t=>{this.formData.subscriptions=t,this.formStates.loadingSubscriptions=!1}),document.addEventListener("upload-success",t=>{this.processUpload(t),document.querySelectorAll("input[type=file]").value=""}),document.addEventListener("upload-error",t=>{this.processUploadError(t)}),document.addEventListener("location-move",t=>{this.entries[t.detail.index].latitude=t.detail.latitude,this.entries[t.detail.index].longitude=t.detail.longitude}),document.addEventListener("location-set",t=>{this.entries[t.detail.index].hasLocation=!0,this.entries[t.detail.index].latitude=t.detail.latitude,this.entries[t.detail.index].longitude=t.detail.longitude,this.entries[t.detail.index].zoomLevel=t.detail.zoomLevel}),document.addEventListener("location-zoom",t=>{this.entries[t.detail.index].hasLocation=!0,this.entries[t.detail.index].zoomLevel=t.detail.zoomLevel})},changedAmount(t){const s=parseInt(t.target.dataset.index);this.entries[s].amount=parseFloat(t.target.value),this.groupProperties.totalAmount=0;for(let o in this.entries)this.entries.hasOwnProperty(o)&&(this.groupProperties.totalAmount=this.groupProperties.totalAmount+parseFloat(this.entries[o].amount))},showMessageOrRedirectUser(){if(this.notifications.error.show=!1,this.notifications.success.show=!1,this.notifications.wait.show=!1,this.formStates.returnHereButton){this.notifications.success.show=!0,this.notifications.success.url="transactions/show/"+this.groupProperties.id,this.notifications.success.text=r.t("firefly.updated_journal_js",{description:this.groupProperties.title});return}window.location="transactions/show/"+this.groupProperties.id+"?transaction_group_id="+this.groupProperties.id+"&message=updated"},parseErrors(t){this.notifications.error.show=!0,this.notifications.success.show=!1,this.notifications.wait.show=!1,this.formStates.isSubmitting=!1,this.notifications.error.text=r.t("firefly.errors_submission",{errorMessage:t.message}),t.hasOwnProperty("errors")&&(this.entries=D(t.errors,this.entries))},processUpload(t){this.showMessageOrRedirectUser()},processUploadError(t){this.notifications.success.show=!1,this.notifications.wait.show=!1,this.notifications.error.show=!0,this.formStates.isSubmitting=!1,this.notifications.error.text=r.t("firefly.errors_upload"),console.error(t)}}},h={transactions:U,dates:f};function d(){Object.keys(h).forEach(t=>{console.log(`Loading page component "${t}"`);let s=h[t]();Alpine.data(t,()=>s)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),d()});window.bootstrapped&&(console.log("Loaded through window variable."),d()); diff --git a/public/build/assets/edit-6247ff45.js b/public/build/assets/edit-6247ff45.js deleted file mode 100644 index 444f0366c1..0000000000 --- a/public/build/assets/edit-6247ff45.js +++ /dev/null @@ -1 +0,0 @@ -import{d as m,f as l,g as p,l as h}from"./get-c53daca3.js";import{f,l as _,I as y}from"./vendor-4332182f.js";import{G as b}from"./get-c2292133.js";import{e as w,f as r,g as v,s as C,h as S,i as A,l as D,a as x,b as L,c as B,j as k,k as c,m as d}from"./autocomplete-functions-31caaca5.js";function E(e){let o=[];for(let a in e)if(e.hasOwnProperty(a)){let t=e[a],n=w();n.bill_id=t.bill_id,n.budget_id=t.budget_id,n.category_name=t.category_name,n.piggy_bank_id=t.piggy_bank_id,n.book_date=t.book_date,n.due_date=t.due_date,n.interest_date=t.interest_date,n.invoice_date=t.invoice_date,n.payment_date=t.payment_date,n.process_date=t.process_date,n.external_url=t.external_url,n.internal_reference=t.internal_reference,n.notes=t.notes,n.tags=t.tags,n.amount=parseFloat(t.amount).toFixed(t.currency_decimal_places),n.currency_code=t.currency_code,t.foreign_amount!==null&&(n.forein_currency_code=t.foreign_currency_code,n.foreign_amount=parseFloat(t.foreign_amount).toFixed(t.foreign_currency_decimal_places)),n.date=f(new Date(t.date),"yyyy-MM-dd HH:mm"),n.description=t.description,n.destination_account={id:t.destination_id,name:t.destination_name,alpine_name:t.destination_name},n.source_account={id:t.source_id,name:t.source_name,alpine_name:t.source_name},t.latitude!==null&&(n.hasLocation=!0,n.latitude=t.latitude,n.longitude=t.longitude,n.zoomLevel=t.zoom_level),console.log("download:"),console.log(t),console.log("current:"),console.log(n),o.push(n)}return o}let i;const s=k();let P=function(){return{entries:[],formStates:{loadingCurrencies:!0,loadingBudgets:!0,loadingPiggyBanks:!0,loadingSubscriptions:!0,isSubmitting:!1,returnHereButton:!1,saveAsNewButton:!1,resetButton:!0,rulesButton:!0,webhooksButton:!0},formBehaviour:{formType:"create",foreignCurrencyEnabled:!0},formData:{defaultCurrency:null,enabledCurrencies:[],nativeCurrencies:[],foreignCurrencies:[],budgets:[],piggyBanks:[],subscriptions:[]},groupProperties:{transactionType:"unknown",title:null,id:null,totalAmount:0},notifications:{error:{show:!1,text:"",url:""},success:{show:!1,text:"",url:""},wait:{show:!1,text:""}},filters:{source:[],destination:[]},addedSplit(){setTimeout(()=>{const e=function(o,a,t){return o.name_with_balance+'
'+i.t("firefly.account_type_"+o.type)+""};r({selector:"input.ac-source",serverUrl:s.account,filters:this.filters.source,onRenderItem:e,onChange:v,onSelectItem:C}),console.log("ok"),console.log(this.entries[0].source_account.alpine_name),r({selector:"input.ac-dest",serverUrl:s.account,filters:this.filters.destination,onRenderItem:e,onChange:S,onSelectItem:A}),r({selector:"input.ac-category",serverUrl:s.category,valueField:"id",labelField:"name",onChange:c,onSelectItem:c}),r({selector:"input.ac-description",serverUrl:s.description,valueField:"id",labelField:"description",onChange:d,onSelectItem:d})},250)},changedDateTime(e){console.warn("changedDateTime, event is not used")},changedDescription(e){console.warn("changedDescription, event is not used")},changedDestinationAccount(e){console.warn("changedDestinationAccount, event is not used")},changedSourceAccount(e){console.warn("changedSourceAccount, event is not used")},formattedTotalAmount(){return this.entries.length===0?l(this.groupProperties.totalAmount,"EUR"):l(this.groupProperties.totalAmount,this.entries[0].currency_code??"EUR")},getTransactionGroup(){const e=window.location.href.split("/"),o=parseInt(e[e.length-1]);new b().show(o,{}).then(t=>{const n=t.data.data;this.groupProperties.id=parseInt(n.id),this.groupProperties.transactionType=n.attributes.transactions[0].type,this.groupProperties.title=n.attributes.title??n.attributes.transactions[0].description,this.entries=E(n.attributes.transactions),this.notifications.wait.show=!1}).then(()=>{setTimeout(()=>{_.init("select.ac-tags",{allowClear:!0,selected:[{label:"Bla bla",value:1,selected:!0}]})},250)})},init(){Promise.all([p("language","en_US")]).then(e=>{i=new y;const o=e[0].replace("-","_");i.locale=o,h(i,o).then(()=>{this.notifications.wait.show=!0,this.notifications.wait.text=i.t("firefly.wait_loading_transaction"),this.getTransactionGroup()})}),D().then(e=>{this.formStates.loadingCurrencies=!1,this.formData.defaultCurrency=e.defaultCurrency,this.formData.enabledCurrencies=e.enabledCurrencies,this.formData.nativeCurrencies=e.nativeCurrencies,this.formData.foreignCurrencies=e.foreignCurrencies}),x().then(e=>{this.formData.budgets=e,this.formStates.loadingBudgets=!1}),L().then(e=>{this.formData.piggyBanks=e,this.formStates.loadingPiggyBanks=!1}),B().then(e=>{this.formData.subscriptions=e,this.formStates.loadingSubscriptions=!1}),document.addEventListener("upload-success",e=>{this.processUpload(e),document.querySelectorAll("input[type=file]").value=""}),document.addEventListener("upload-error",e=>{this.processUploadError(e)}),document.addEventListener("location-move",e=>{this.entries[e.detail.index].latitude=e.detail.latitude,this.entries[e.detail.index].longitude=e.detail.longitude}),document.addEventListener("location-set",e=>{this.entries[e.detail.index].hasLocation=!0,this.entries[e.detail.index].latitude=e.detail.latitude,this.entries[e.detail.index].longitude=e.detail.longitude,this.entries[e.detail.index].zoomLevel=e.detail.zoomLevel}),document.addEventListener("location-zoom",e=>{this.entries[e.detail.index].hasLocation=!0,this.entries[e.detail.index].zoomLevel=e.detail.zoomLevel})}}},u={transactions:P,dates:m};function g(){Object.keys(u).forEach(e=>{console.log(`Loading page component "${e}"`);let o=u[e]();Alpine.data(e,()=>o)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),g()});window.bootstrapped&&(console.log("Loaded through window variable."),g()); diff --git a/public/build/assets/format-money-bcfc2969.js b/public/build/assets/format-money-bcfc2969.js new file mode 100644 index 0000000000..a2f4192fee --- /dev/null +++ b/public/build/assets/format-money-bcfc2969.js @@ -0,0 +1 @@ +import{o as y,s as d,q as l,r as p,t as _,u as c,v as b,w as N,x as P,y as v,z as g,i as C,D as B,E as x,G as O,H as o,I as E,J as T,f as L,K as S,M as W,N as I,O as U,Q as q,R as Q,U as V,V as Y,W as $,X as j,Y as z,Z as X,_ as F,$ as G,a0 as H,a1 as J,a2 as K,a3 as Z,a4 as ee,a5 as te,a6 as ae,a7 as se,a8 as ne,a9 as re,aa as le,ab as de,ac as oe,ad as ie,ae as ue,af as ge,ag as ce,ah as we,ai as he,aj as ye}from"./vendor-5cffaf70.js";const k="/",h=y.create({baseURL:k,withCredentials:!0});y.defaults.withCredentials=!0;y.defaults.baseURL=k;class R{getByName(e){return h.get("/api/v1/preferences/"+e)}getByNameNow(e){return h.get("/api/v1/preferences/"+e)}postByName(e,a){return h.post("/api/v1/preferences",{name:e,data:a})}}class A{post(e,a){let s="/api/v1/preferences";return h.post(s,{name:e,data:a})}}function fe(t,e=null){return new R().getByName(t).then(s=>Promise.resolve(M(t,s))).catch(()=>{new A().post(t,e).then(i=>Promise.resolve(M(t,i)))})}function M(t,e){return e.data.data.attributes.data}function w(t,e=null){const a=window.store.get("cacheValid");if(a&&window.hasOwnProperty(t))return Promise.resolve(window[t]);const s=window.store.get(t);return a&&typeof s<"u"?Promise.resolve(s):new R().getByName(t).then(u=>Promise.resolve(m(t,u))).catch(()=>{new A().post(t,e).then(n=>Promise.resolve(m(t,n)))})}function m(t,e){let a=e.data.data.attributes.data;return window.store.set(t,a),a}function be(t,e){let a,s;switch(t){case"last365":a=d(g(e,365)),s=l(e);break;case"last90":a=d(g(e,90)),s=l(e);break;case"last30":a=d(g(e,30)),s=l(e);break;case"last7":a=d(g(e,7)),s=l(e);break;case"YTD":a=v(e),s=l(e);break;case"QTD":a=p(e),s=l(e);break;case"MTD":a=c(e),s=l(e);break;case"1D":a=d(e),s=l(e);break;case"1W":a=d(N(e,{weekStartsOn:1})),s=l(P(e,{weekStartsOn:1}));break;case"1M":a=d(c(e)),s=l(b(e));break;case"3M":a=d(p(e)),s=l(_(e));break;case"6M":e.getMonth()<=5&&(a=new Date(e),a.setMonth(0),a.setDate(1),a=d(a),s=new Date(e),s.setMonth(5),s.setDate(30),s=l(a)),e.getMonth()>5&&(a=new Date(e),a.setMonth(6),a.setDate(1),a=d(a),s=new Date(e),s.setMonth(11),s.setDate(31),s=l(a));break;case"1Y":a=new Date(e),a.setMonth(0),a.setDate(1),a=d(a),s=new Date(e),s.setMonth(11),s.setDate(31),s=l(s);break}return{start:a,end:s}}let D=!1;function pe(t){if(D===!1){const e=t.replace("-","_");D=!0;const a=7*24*60*60*1e3;return console.log('Will load language "'+e+'"'),C.use(B).init({load:"languageOnly",fallbackLng:"en",lng:e,debug:!1,backend:{backends:[x,O],backendOptions:[{load:"languageOnly",expirationTime:a},{loadPath:"./v2/i18n/{{lng}}.json"}]}})}return console.warn("Loading translations skipped."),Promise.resolve()}o.addPlugin(E);window.bootstrapped=!1;window.store=o;fe("lastActivity").then(t=>{const e=o.get("lastActivity");o.set("cacheValid",e===t),o.set("lastActivity",t),console.log("Server value: "+t),console.log("Local value: "+e),console.log("Cache valid: "+(e===t))}).then(()=>{Promise.all([w("viewRange"),w("darkMode"),w("locale"),w("language")]).then(t=>{if(!o.get("start")||!o.get("end")){const e=be(t[0],new Date);o.set("start",e.start),o.set("end",e.end)}window.__localeId__=t[2],o.set("language",t[3]),o.set("locale",t[3]),pe(t[3]).then(()=>{const e=new Event("firefly-iii-bootstrapped");document.dispatchEvent(e),window.bootstrapped=!0})})});window.axios=y;window.axios.defaults.headers.common["X-Requested-With"]="XMLHttpRequest";window.Alpine=T;const f={bg:S,cs:W,da:I,de:U,el:q,enGB:Q,enUS:V,es:Y,ca:$,fi:j,fr:z,hu:X,id:F,it:G,ja:H,ko:J,nb:K,nn:Z,nl:ee,pl:te,ptBR:ae,pt:se,ro:ne,ru:re,sk:le,sl:de,sv:oe,tr:ie,uk:ue,vi:ge,zhTW:ce,zhCN:we};function r(t,e="PP"){let a=window.__localeId__.replace("_","");return L(t,e,{locale:f[a]??f[a.slice(0,2)]??f.enUS})}const me=()=>({range:{start:null,end:null},defaultRange:{start:null,end:null},language:"en_US",init(){this.range={start:new Date(window.store.get("start")),end:new Date(window.store.get("end"))},this.defaultRange={start:new Date(window.store.get("start")),end:new Date(window.store.get("end"))},this.language=window.store.get("language"),this.locale=window.store.get("locale"),this.locale=this.locale==="equal"?this.language:this.locale,window.__localeId__=this.language,this.buildDateRange(),window.store.observe("start",t=>{this.range.start=new Date(t)}),window.store.observe("end",t=>{this.range.end=new Date(t),this.buildDateRange()})},buildDateRange(){let t=this.getNextRange(),e=this.getPrevRange(),a=this.lastDays(7),s=this.lastDays(30),i=this.mtd(),u=this.ytd(),n=document.getElementsByClassName("daterange-holder")[0];n.textContent=r(this.range.start)+" - "+r(this.range.end),n.setAttribute("data-start",r(this.range.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(this.range.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-current")[0],n.textContent=r(this.defaultRange.start)+" - "+r(this.defaultRange.end),n.setAttribute("data-start",r(this.defaultRange.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(this.defaultRange.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-next")[0],n.textContent=r(t.start)+" - "+r(t.end),n.setAttribute("data-start",r(t.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(t.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-prev")[0],n.textContent=r(e.start)+" - "+r(e.end),n.setAttribute("data-start",r(e.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(e.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-7d")[0],n.setAttribute("data-start",r(a.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(a.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-90d")[0],n.setAttribute("data-start",r(s.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(s.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-mtd")[0],n.setAttribute("data-start",r(i.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(i.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-ytd")[0],n.setAttribute("data-start",r(u.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(u.end,"yyyy-MM-dd"))},getNextRange(){let t=c(this.range.start),e=he(t,1),a=b(e);return{start:e,end:a}},getPrevRange(){let t=c(this.range.start),e=ye(t,1),a=b(e);return{start:e,end:a}},ytd(){let t=new Date;return{start:v(this.range.start),end:t}},mtd(){let t=new Date;return{start:c(this.range.start),end:t}},lastDays(t){let e=new Date;return{start:g(e,t),end:e}},changeDateRange(t){t.preventDefault();let e=t.currentTarget,a=new Date(e.getAttribute("data-start")),s=new Date(e.getAttribute("data-end"));return window.store.set("start",a),window.store.set("end",s),!1}});function De(t,e){let a=window.__localeId__.replace("_","-");return Intl.NumberFormat(a,{style:"currency",currency:e}).format(t)}export{A as P,h as a,me as d,De as f,w as g}; diff --git a/public/build/assets/get-32a7112a.js b/public/build/assets/get-32a7112a.js new file mode 100644 index 0000000000..d9694d9c0d --- /dev/null +++ b/public/build/assets/get-32a7112a.js @@ -0,0 +1 @@ +import{a as s}from"./format-money-bcfc2969.js";class p{list(a){return s.get("/api/v2/transactions",{params:a})}show(a,t){return s.get("/api/v2/transactions/"+a,{params:t})}}export{p as G}; diff --git a/public/build/assets/get-c2292133.js b/public/build/assets/get-c2292133.js deleted file mode 100644 index ae3eb6dd81..0000000000 --- a/public/build/assets/get-c2292133.js +++ /dev/null @@ -1 +0,0 @@ -import{a as s}from"./get-c53daca3.js";class p{list(a){return s.get("/api/v2/transactions",{params:a})}show(a,t){return s.get("/api/v2/transactions/"+a,{params:t})}}export{p as G}; diff --git a/public/build/assets/get-c53daca3.js b/public/build/assets/get-c53daca3.js deleted file mode 100644 index df70a4c8e3..0000000000 --- a/public/build/assets/get-c53daca3.js +++ /dev/null @@ -1 +0,0 @@ -import{n as y,s as l,o as d,q as f,r as A,t as w,u as b,v as _,w as N,x as D,y as c,z as i,D as C,E as P,f as B,G as x,H as O,J as E,K as S,M as G,N as T,O as U,Q as W,R as I,U as $,V as q,W as L,X as Q,Y as V,Z as Y,_ as j,$ as z,a0 as X,a1 as F,a2 as H,a3 as J,a4 as K,a5 as Z,a6 as tt,a7 as et,a8 as at,a9 as st,aa as nt,ab as rt,ac as dt,ad as lt,ae as it,af as ot,ag as ut}from"./vendor-4332182f.js";const v="/",o=y.create({baseURL:v,withCredentials:!0});y.defaults.withCredentials=!0;y.defaults.baseURL=v;class R{getByName(t){return o.get("/api/v1/preferences/"+t)}getByNameNow(t){return o.get("/api/v1/preferences/"+t)}postByName(t,a){return o.post("/api/v1/preferences",{name:t,data:a})}}class k{post(t,a){let s="/api/v1/preferences";return o.post(s,{name:t,data:a})}}function gt(e,t=null){return new R().getByName(e).then(s=>Promise.resolve(m(e,s))).catch(()=>{new k().post(e,t).then(u=>Promise.resolve(m(e,u)))})}function m(e,t){return t.data.data.attributes.data}function h(e,t=null){const a=window.store.get("cacheValid");if(a&&window.hasOwnProperty(e))return Promise.resolve(window[e]);const s=window.store.get(e);return a&&typeof s<"u"?Promise.resolve(s):new R().getByName(e).then(g=>Promise.resolve(M(e,g))).catch(()=>{new k().post(e,t).then(n=>Promise.resolve(M(e,n)))})}function M(e,t){let a=t.data.data.attributes.data;return window.store.set(e,a),a}function ct(e,t){let a,s;switch(e){case"last365":a=l(c(t,365)),s=d(t);break;case"last90":a=l(c(t,90)),s=d(t);break;case"last30":a=l(c(t,30)),s=d(t);break;case"last7":a=l(c(t,7)),s=d(t);break;case"YTD":a=D(t),s=d(t);break;case"QTD":a=f(t),s=d(t);break;case"MTD":a=w(t),s=d(t);break;case"1D":a=l(t),s=d(t);break;case"1W":a=l(_(t,{weekStartsOn:1})),s=d(N(t,{weekStartsOn:1}));break;case"1M":a=l(w(t)),s=d(b(t));break;case"3M":a=l(f(t)),s=d(A(t));break;case"6M":t.getMonth()<=5&&(a=new Date(t),a.setMonth(0),a.setDate(1),a=l(a),s=new Date(t),s.setMonth(5),s.setDate(30),s=d(a)),t.getMonth()>5&&(a=new Date(t),a.setMonth(6),a.setDate(1),a=l(a),s=new Date(t),s.setMonth(11),s.setDate(31),s=d(a));break;case"1Y":a=new Date(t),a.setMonth(0),a.setDate(1),a=l(a),s=new Date(t),s.setMonth(11),s.setDate(31),s=d(s);break}return{start:a,end:s}}i.addPlugin(C);window.bootstrapped=!1;window.store=i;gt("lastActivity").then(e=>{const t=i.get("lastActivity");i.set("cacheValid",t===e),i.set("lastActivity",e),console.log("Server value: "+e),console.log("Local value: "+t),console.log("Cache valid: "+(t===e))}).then(()=>{Promise.all([h("viewRange"),h("darkMode"),h("locale"),h("language")]).then(e=>{if(!i.get("start")||!i.get("end")){const a=ct(e[0],new Date);i.set("start",a.start),i.set("end",a.end)}window.__localeId__=e[2],i.set("language",e[3]),i.set("locale",e[3]);const t=new Event("firefly-iii-bootstrapped");document.dispatchEvent(t),window.bootstrapped=!0})});window.axios=y;window.axios.defaults.headers.common["X-Requested-With"]="XMLHttpRequest";window.Alpine=P;const p={bg:x,cs:O,da:E,de:S,el:G,enGB:T,enUS:U,es:W,ca:I,fi:$,fr:q,hu:L,id:Q,it:V,ja:Y,ko:j,nb:z,nn:X,nl:F,pl:H,ptBR:J,pt:K,ro:Z,ru:tt,sk:et,sl:at,sv:st,tr:nt,uk:rt,vi:dt,zhTW:lt,zhCN:it};function r(e,t="PP"){let a=window.__localeId__.replace("_","");return B(e,t,{locale:p[a]??p[a.slice(0,2)]??p.enUS})}const ht=()=>({range:{start:null,end:null},defaultRange:{start:null,end:null},language:"en_US",init(){this.range={start:new Date(window.store.get("start")),end:new Date(window.store.get("end"))},this.defaultRange={start:new Date(window.store.get("start")),end:new Date(window.store.get("end"))},this.language=window.store.get("language"),this.locale=window.store.get("locale"),this.locale=this.locale==="equal"?this.language:this.locale,window.__localeId__=this.language,this.buildDateRange(),window.store.observe("start",e=>{this.range.start=new Date(e)}),window.store.observe("end",e=>{this.range.end=new Date(e),this.buildDateRange()})},buildDateRange(){let e=this.getNextRange(),t=this.getPrevRange(),a=this.lastDays(7),s=this.lastDays(30),u=this.mtd(),g=this.ytd(),n=document.getElementsByClassName("daterange-holder")[0];n.textContent=r(this.range.start)+" - "+r(this.range.end),n.setAttribute("data-start",r(this.range.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(this.range.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-current")[0],n.textContent=r(this.defaultRange.start)+" - "+r(this.defaultRange.end),n.setAttribute("data-start",r(this.defaultRange.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(this.defaultRange.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-next")[0],n.textContent=r(e.start)+" - "+r(e.end),n.setAttribute("data-start",r(e.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(e.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-prev")[0],n.textContent=r(t.start)+" - "+r(t.end),n.setAttribute("data-start",r(t.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(t.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-7d")[0],n.setAttribute("data-start",r(a.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(a.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-90d")[0],n.setAttribute("data-start",r(s.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(s.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-mtd")[0],n.setAttribute("data-start",r(u.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(u.end,"yyyy-MM-dd")),n=document.getElementsByClassName("daterange-ytd")[0],n.setAttribute("data-start",r(g.start,"yyyy-MM-dd")),n.setAttribute("data-end",r(g.end,"yyyy-MM-dd"))},getNextRange(){let e=w(this.range.start),t=ot(e,1),a=b(t);return{start:t,end:a}},getPrevRange(){let e=w(this.range.start),t=ut(e,1),a=b(t);return{start:t,end:a}},ytd(){let e=new Date;return{start:D(this.range.start),end:e}},mtd(){let e=new Date;return{start:w(this.range.start),end:e}},lastDays(e){let t=new Date;return{start:c(t,e),end:t}},changeDateRange(e){e.preventDefault();let t=e.currentTarget,a=new Date(t.getAttribute("data-start")),s=new Date(t.getAttribute("data-end"));return window.store.set("start",a),window.store.set("end",s),!1}});function yt(e,t){let a=window.__localeId__.replace("_","-");return Intl.NumberFormat(a,{style:"currency",currency:t}).format(e)}async function pt(e,t){{t=t.replace("-","_");const s=await(await fetch(`./v2/i18n/${t}.json`)).json();e.store(s)}}let bt=class{list(t){return o.get("/api/v2/subscriptions",{params:t})}paid(t){return o.get("/api/v2/subscriptions/sum/paid",{params:t})}unpaid(t){return o.get("/api/v2/subscriptions/sum/unpaid",{params:t})}};class mt{list(t){return o.get("/api/v2/piggy-banks",{params:t})}}export{bt as G,k as P,o as a,mt as b,ht as d,yt as f,h as g,pt as l}; diff --git a/public/build/assets/get-fa539542.js b/public/build/assets/get-fa539542.js new file mode 100644 index 0000000000..86851f0a3a --- /dev/null +++ b/public/build/assets/get-fa539542.js @@ -0,0 +1 @@ +import{a as s}from"./format-money-bcfc2969.js";let t=class{list(a){return s.get("/api/v2/subscriptions",{params:a})}paid(a){return s.get("/api/v2/subscriptions/sum/paid",{params:a})}unpaid(a){return s.get("/api/v2/subscriptions/sum/unpaid",{params:a})}};class e{list(a){return s.get("/api/v2/piggy-banks",{params:a})}}export{t as G,e as a}; diff --git a/public/build/assets/index-e87148c5.js b/public/build/assets/index-e87148c5.js new file mode 100644 index 0000000000..44adf0e0c6 --- /dev/null +++ b/public/build/assets/index-e87148c5.js @@ -0,0 +1 @@ +import{d as c,f as d}from"./format-money-bcfc2969.js";import{f as p,i as n}from"./vendor-5cffaf70.js";import{G as f}from"./get-32a7112a.js";let g=function(){return{notifications:{error:{show:!1,text:"",url:""},success:{show:!1,text:"",url:""},wait:{show:!1,text:""}},transactions:[],totalPages:1,perPage:50,page:1,tableColumns:{description:{enabled:!0},source:{enabled:!0},destination:{enabled:!0},amount:{enabled:!0}},formatMoney(t,i){return d(t,i)},format(t){return p(t,n.t("config.date_time_fns"))},init(){this.notifications.wait.show=!0,this.notifications.wait.text=n.t("firefly.wait_loading_data"),this.getTransactions(this.page)},getTransactions(t){const i=window.location.href.split("/"),a=i[i.length-1];new f().list({page:t,type:a}).then(s=>{this.parseTransactions(s.data.data),this.totalPages=s.data.meta.pagination.total_pages,this.perPage=s.data.meta.pagination.per_page,this.page=s.data.meta.pagination.current_page}).catch(s=>{this.notifications.wait.show=!1,this.notifications.error.show=!0,this.notifications.error.text=s.response.data.message})},parseTransactions(t){for(let i in t)if(t.hasOwnProperty(i)){let a=t[i],o=a.attributes.transactions.length>1,s=!0;for(let r in a.attributes.transactions)if(a.attributes.transactions.hasOwnProperty(r)){let e=a.attributes.transactions[r];e.split=o,e.firstSplit=s,e.group_title=a.attributes.group_title,e.id=a.id,e.created_at=a.attributes.created_at,e.updated_at=a.attributes.updated_at,e.user=a.attributes.user,e.user_group=a.attributes.user_group,s=!1,console.log(e),this.transactions.push(e)}}this.notifications.wait.show=!1}}},l={index:g,dates:c};function u(){Object.keys(l).forEach(t=>{console.log(`Loading page component "${t}"`);let i=l[t]();Alpine.data(t,()=>i)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),u()});window.bootstrapped&&(console.log("Loaded through window variable."),u()); diff --git a/public/build/assets/parse-downloaded-splits-d8bf7632.js b/public/build/assets/parse-downloaded-splits-d8bf7632.js new file mode 100644 index 0000000000..60e8873c34 --- /dev/null +++ b/public/build/assets/parse-downloaded-splits-d8bf7632.js @@ -0,0 +1 @@ +import{c as r}from"./create-empty-split-c3730b85.js";import{f as o}from"./vendor-5cffaf70.js";function c(a){let n=[];for(let i in a)if(a.hasOwnProperty(i)){let e=a[i],t=r();t.bill_id=e.bill_id,t.bill_name=e.bill_name,t.budget_id=e.budget_id,t.budget_name=e.budget_name,t.category_name=e.category_name,t.category_id=e.category_id,t.piggy_bank_id=e.piggy_bank_id,t.piggy_bank_name=e.piggy_bank_name,t.book_date=e.book_date,t.due_date=e.due_date,t.interest_date=e.interest_date,t.invoice_date=e.invoice_date,t.payment_date=e.payment_date,t.process_date=e.process_date,t.external_url=e.external_url,t.internal_reference=e.internal_reference,t.notes=e.notes,t.tags=e.tags,t.amount=parseFloat(e.amount).toFixed(e.currency_decimal_places),t.currency_code=e.currency_code,e.foreign_amount!==null&&(t.forein_currency_code=e.foreign_currency_code,t.foreign_amount=parseFloat(e.foreign_amount).toFixed(e.foreign_currency_decimal_places)),t.date=o(new Date(e.date),"yyyy-MM-dd HH:mm"),t.description=e.description,t.destination_account={id:e.destination_id,name:e.destination_name,type:e.destination_type,alpine_name:e.destination_name},t.source_account={id:e.source_id,name:e.source_name,type:e.source_type,alpine_name:e.source_name},e.latitude!==null&&(t.hasLocation=!0,t.latitude=e.latitude,t.longitude=e.longitude,t.zoomLevel=e.zoom_level),n.push(t)}return n}export{c as p}; diff --git a/public/build/assets/show-8b1429e5.css b/public/build/assets/show-8b1429e5.css new file mode 100644 index 0000000000..b42910d276 --- /dev/null +++ b/public/build/assets/show-8b1429e5.css @@ -0,0 +1 @@ +.dark-editable-element{border-bottom:dashed 1px #0088cc;text-decoration:none;cursor:pointer}.dark-editable-element-disabled{border-bottom:none;cursor:default}.dark-editable-element-empty{font-style:italic;color:#d14}.dark-editable{max-width:none}.dark-editable-loader{font-size:5px;left:50%;top:50%;width:1em;height:1em;border-radius:50%;position:relative;text-indent:-9999em;-webkit-animation:load5 1.1s infinite ease;animation:load5 1.1s infinite ease;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}@-webkit-keyframes load5{0%,to{box-shadow:0 -2.6em #000,1.8em -1.8em #0003,2.5em 0 #0003,1.75em 1.75em #0003,0 2.5em #0003,-1.8em 1.8em #0003,-2.6em 0 #00000080,-1.8em -1.8em #000000b3}12.5%{box-shadow:0 -2.6em #000000b3,1.8em -1.8em #000,2.5em 0 #0003,1.75em 1.75em #0003,0 2.5em #0003,-1.8em 1.8em #0003,-2.6em 0 #0003,-1.8em -1.8em #00000080}25%{box-shadow:0 -2.6em #00000080,1.8em -1.8em #000000b3,2.5em 0 #000,1.75em 1.75em #0003,0 2.5em #0003,-1.8em 1.8em #0003,-2.6em 0 #0003,-1.8em -1.8em #0003}37.5%{box-shadow:0 -2.6em #0003,1.8em -1.8em #00000080,2.5em 0 #000000b3,1.75em 1.75em #000,0 2.5em #0003,-1.8em 1.8em #0003,-2.6em 0 #0003,-1.8em -1.8em #0003}50%{box-shadow:0 -2.6em #0003,1.8em -1.8em #0003,2.5em 0 #00000080,1.75em 1.75em #000000b3,0 2.5em #000,-1.8em 1.8em #0003,-2.6em 0 #0003,-1.8em -1.8em #0003}62.5%{box-shadow:0 -2.6em #0003,1.8em -1.8em #0003,2.5em 0 #0003,1.75em 1.75em #00000080,0 2.5em #000000b3,-1.8em 1.8em #000,-2.6em 0 #0003,-1.8em -1.8em #0003}75%{box-shadow:0 -2.6em #0003,1.8em -1.8em #0003,2.5em 0 #0003,1.75em 1.75em #0003,0 2.5em #00000080,-1.8em 1.8em #000000b3,-2.6em 0 #000,-1.8em -1.8em #0003}87.5%{box-shadow:0 -2.6em #0003,1.8em -1.8em #0003,2.5em 0 #0003,1.75em 1.75em #0003,0 2.5em #0003,-1.8em 1.8em #00000080,-2.6em 0 #000000b3,-1.8em -1.8em #000}}@keyframes load5{0%,to{box-shadow:0 -2.6em #000,1.8em -1.8em #0003,2.5em 0 #0003,1.75em 1.75em #0003,0 2.5em #0003,-1.8em 1.8em #0003,-2.6em 0 #00000080,-1.8em -1.8em #000000b3}12.5%{box-shadow:0 -2.6em #000000b3,1.8em -1.8em #000,2.5em 0 #0003,1.75em 1.75em #0003,0 2.5em #0003,-1.8em 1.8em #0003,-2.6em 0 #0003,-1.8em -1.8em #00000080}25%{box-shadow:0 -2.6em #00000080,1.8em -1.8em #000000b3,2.5em 0 #000,1.75em 1.75em #0003,0 2.5em #0003,-1.8em 1.8em #0003,-2.6em 0 #0003,-1.8em -1.8em #0003}37.5%{box-shadow:0 -2.6em #0003,1.8em -1.8em #00000080,2.5em 0 #000000b3,1.75em 1.75em #000,0 2.5em #0003,-1.8em 1.8em #0003,-2.6em 0 #0003,-1.8em -1.8em #0003}50%{box-shadow:0 -2.6em #0003,1.8em -1.8em #0003,2.5em 0 #00000080,1.75em 1.75em #000000b3,0 2.5em #000,-1.8em 1.8em #0003,-2.6em 0 #0003,-1.8em -1.8em #0003}62.5%{box-shadow:0 -2.6em #0003,1.8em -1.8em #0003,2.5em 0 #0003,1.75em 1.75em #00000080,0 2.5em #000000b3,-1.8em 1.8em #000,-2.6em 0 #0003,-1.8em -1.8em #0003}75%{box-shadow:0 -2.6em #0003,1.8em -1.8em #0003,2.5em 0 #0003,1.75em 1.75em #0003,0 2.5em #00000080,-1.8em 1.8em #000000b3,-2.6em 0 #000,-1.8em -1.8em #0003}87.5%{box-shadow:0 -2.6em #0003,1.8em -1.8em #0003,2.5em 0 #0003,1.75em 1.75em #0003,0 2.5em #0003,-1.8em 1.8em #00000080,-2.6em 0 #000000b3,-1.8em -1.8em #000}} diff --git a/public/build/assets/show-ddb72bf9.js b/public/build/assets/show-ddb72bf9.js new file mode 100644 index 0000000000..7d7ffd3298 --- /dev/null +++ b/public/build/assets/show-ddb72bf9.js @@ -0,0 +1 @@ +var m=Object.defineProperty;var f=(i,t,e)=>t in i?m(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var r=(i,t,e)=>(f(i,typeof t!="symbol"?t+"":t,e),e);import{d as x,f as w}from"./format-money-bcfc2969.js";import{f as v,i as h}from"./vendor-5cffaf70.js";import{G as y}from"./get-32a7112a.js";import{p as b}from"./parse-downloaded-splits-d8bf7632.js";import"./create-empty-split-c3730b85.js";class l{constructor(t){if(this.constructor===l)throw new Error("It's abstract class");this.context=t}event_show(){this.context.typeElement.hideError(),this.context.typeElement.element.value=this.context.value,this.context.element.dispatchEvent(new CustomEvent("show"))}event_shown(){this.context.element.dispatchEvent(new CustomEvent("shown"))}event_hide(){this.context.element.dispatchEvent(new CustomEvent("hide"))}event_hidden(){this.context.element.dispatchEvent(new CustomEvent("hidden"))}init(){throw new Error("Method `init` not define!")}enable(){throw new Error("Method `enable` not define!")}disable(){throw new Error("Method `disable` not define!")}hide(){throw new Error("Method `hide` not define!")}}class E extends l{init(){this.popover=new bootstrap.Popover(this.context.element,{container:"body",content:this.context.typeElement.create(),html:!0,customClass:"dark-editable",title:this.context.title}),this.context.element.addEventListener("show.bs.popover",()=>{this.event_show()}),this.context.element.addEventListener("shown.bs.popover",()=>{this.event_shown()}),this.context.element.addEventListener("hide.bs.popover",()=>{this.event_hide()}),this.context.element.addEventListener("hidden.bs.popover",()=>{this.event_hidden()}),document.addEventListener("click",t=>{const e=t.target;if(e===this.popover.tip||e===this.context.element)return;let n=e;for(;n=n.parentNode;)if(n===this.popover.tip)return;this.hide()})}enable(){this.popover.enable()}disable(){this.popover.disable()}hide(){this.popover.hide()}}class _ extends l{init(){const t=()=>{if(!this.context.disabled){const e=this.context.typeElement.create();this.event_show(),this.context.element.removeEventListener("click",t),this.context.element.innerHTML="",this.context.element.append(e),this.event_shown()}};this.context.element.addEventListener("click",t)}enable(){}disable(){}hide(){this.event_hide(),this.context.element.innerHTML=this.context.value,setTimeout(()=>{this.init(),this.event_hidden()},100)}}class a{constructor(t){r(this,"context",null);r(this,"element",null);r(this,"error",null);r(this,"form",null);r(this,"load",null);r(this,"buttonGroup",null);r(this,"buttons",{success:null,cancel:null});if(this.constructor===a)throw new Error("It's abstract class");this.context=t}create(){throw new Error("Method `create` not define!")}createContainer(t){const e=document.createElement("div");this.element=t,this.error=this.createContainerError(),this.form=this.createContainerForm(),this.load=this.createContainerLoad(),this.buttons.success=this.createButtonSuccess(),this.buttons.cancel=this.createButtonCancel();const n=document.createElement("div");n.classList.add("col-12");const s=document.createElement("label");s.classList.add("visually-hidden"),s.for=t.id,n.append(s,t);const o=document.createElement("div");return o.classList.add("col-12"),this.buttonGroup=this.createButtonGroup(),this.buttonGroup.append(this.buttons.success,this.buttons.cancel),o.append(this.buttonGroup),this.form.append(n,o),e.append(this.error,this.form),e}createButtonGroup(){const t=document.createElement("div");return t.classList.add("btn-group","btn-group-sm"),t}createContainerError(){const t=document.createElement("div");return t.classList.add("text-danger","fst-italic","mb-2","fw-bold"),t.style.display="none",t}createContainerForm(){const t=document.createElement("form");return t.classList.add("row","row-cols-lg-auto","g-3","align-items-center"),t.addEventListener("submit",async e=>{e.preventDefault();const n=this.getValue();if(this.context.send&&this.context.pk&&this.context.url&&this.context.value!==n){this.showLoad();let s;try{const o=await this.ajax(n);o.ok?s=await this.context.success(o,n):s=await this.context.error(o,n)||`${o.status} ${o.statusText}`}catch(o){console.error(o),s=o}s?(this.setError(s),this.showError()):(this.setError(null),this.hideError(),this.context.value=this.getValue(),this.context.modeElement.hide(),this.initText()),this.hideLoad()}else this.context.value=this.getValue(),this.context.modeElement.hide(),this.initText();this.context.element.dispatchEvent(new CustomEvent("save"))}),t}createContainerLoad(){const t=document.createElement("div");t.style.display="none",t.style.position="absolute",t.style.background="white",t.style.width="100%",t.style.height="100%",t.style.top=0,t.style.left=0;const e=document.createElement("div");return e.classList.add("dark-editable-loader"),t.append(e),t}createButton(){const t=document.createElement("button");return t.type="button",t.classList.add("btn","btn-sm"),t.style.color="transparent",t.style.textShadow="0 0 0 white",t}createButtonSuccess(){const t=this.createButton();return t.type="submit",t.classList.add("btn-success"),t.innerHTML="✔",t}createButtonCancel(){const t=this.createButton();t.classList.add("btn-danger");const e=document.createElement("div");return e.innerHTML="✖",t.append(e),t.addEventListener("click",()=>{this.context.modeElement.hide()}),t}hideLoad(){this.load.style.display="none"}showLoad(){this.load.style.display="block"}ajax(t){let e=this.context.url;const n=new FormData;n.append("pk",this.context.pk),n.append("name",this.context.name),n.append("value",t);const s={};return s.method=this.context.ajaxOptions.method,s.method==="POST"?s.body=n:e+="?"+new URLSearchParams(n).toString(),fetch(e,s)}async successResponse(t,e){}async errorResponse(t,e){}setError(t){this.error.innerHTML=t}showError(){this.error.style.display="block"}hideError(){this.error&&(this.error.style.display="none")}createElement(t){const e=document.createElement(t);return console.log(e),e.classList.add("form-control"),this.context.required&&(e.required=this.context.required),this.add_focus(e),e}add_focus(t){this.context.element.addEventListener("shown",function(){t.focus()})}initText(){return this.context.value===""?(this.context.element.innerHTML=this.context.emptytext,!0):(this.context.element.innerHTML=this.context.value,!1)}initOptions(){}getValue(){return this.element.value}}class g extends a{create(){const t=this.createElement("input"),e=this.context.element.id+"_input";return t.type=this.context.type,t.id=e,t.autocomplete="off",t.placeholder=this.context.element.innerText,t.classList.add("form-control","form-control-md"),this.createContainer(t)}}class L extends a{create(){const t=this.createElement("textarea");return this.createContainer(t)}}class T extends a{create(){const t=this.createElement("select");return this.context.source.forEach(e=>{const n=document.createElement("option");n.value=e.value,n.innerHTML=e.text,t.append(n)}),this.createContainer(t)}initText(){if(this.context.element.innerHTML=this.context.emptytext,this.context.value!==""&&this.context.source.length>0)for(const t in this.context.source){const e=this.context.source[t];if(e.value==this.context.value)return this.context.element.innerHTML=e.text,!1}return!0}initOptions(){this.context.get_opt("source",[]),typeof this.context.source=="string"&&this.context.source!==""&&(this.context.source=JSON.parse(this.context.source))}}class u extends a{create(){const t=this.createElement("input");return t.type="date",this.createContainer(t)}initText(){return this.value===""?(this.context.element.innerHTML=this.context.emptytext,!0):(this.context.element.innerHTML=moment(this.context.value).format(this.context.viewformat),!1)}initOptions(){this.context.get_opt("format","YYYY-MM-DD"),this.context.get_opt("viewformat","YYYY-MM-DD")}}class M extends u{create(){const t=this.createElement("input");return t.type="datetime-local",this.createContainer(t)}initOptions(){this.context.get_opt("format","YYYY-MM-DD HH:mm"),this.context.get_opt("viewformat","YYYY-MM-DD HH:mm"),this.context.value=moment(this.context.value).format("YYYY-MM-DDTHH:mm")}}class k{constructor(t,e={}){r(this,"modeElement",null);r(this,"typeElement",null);r(this,"mode",null);r(this,"type",null);r(this,"emptytext",null);r(this,"viewformat",null);r(this,"pk",null);r(this,"name",null);this.element=t,this.options=e,this.init_options(),this.typeElement=this.route_type(),this.typeElement.initOptions(),this.modeElement=this.route_mode(),this.modeElement.init(),this.init_text(),this.init_style(),this.disabled&&this.disable(),this.element.dispatchEvent(new CustomEvent("init"))}get_opt(t,e){var n,s;return this[t]=((n=this.element.dataset)==null?void 0:n[t])??((s=this.options)==null?void 0:s[t])??e}get_opt_bool(t,e){return this.get_opt(t,e),typeof this[t]!="boolean"&&(this[t]==="true"?this[t]=!0:this[t]==="false"?this[t]=!1:this[t]=e),this[t]}init_options(){var t,e,n,s;this.get_opt("value",this.element.innerHTML),this.get_opt("name",this.element.id),this.get_opt("pk",null),this.get_opt("title",""),this.get_opt("type","text"),this.get_opt("emptytext","Empty"),this.get_opt("mode","popup"),this.get_opt("url",null),this.get_opt("ajaxOptions",{}),this.ajaxOptions=Object.assign({method:"POST",dataType:"text"},this.ajaxOptions),this.get_opt_bool("send",!0),this.get_opt_bool("disabled",!1),this.get_opt_bool("required",!1),(t=this.options)!=null&&t.success&&typeof((e=this.options)==null?void 0:e.success)=="function"&&(this.success=this.options.success),(n=this.options)!=null&&n.error&&typeof((s=this.options)==null?void 0:s.error)=="function"&&(this.error=this.options.error)}init_text(){const t="dark-editable-element-empty";this.element.classList.remove(t),this.typeElement.initText()&&this.element.classList.add(t)}init_style(){this.element.classList.add("dark-editable-element")}route_mode(){switch(this.mode){default:throw new Error(`Mode ${this.mode} not found!`);case"popup":return new E(this);case"inline":return new _(this)}}route_type(){if(this.type.prototype instanceof a)return new this.type(this);if(typeof this.type=="string")switch(this.type){case"text":case"password":case"email":case"url":case"tel":case"number":case"range":case"time":return new g(this);case"textarea":return new L(this);case"select":return new T(this);case"date":return new u(this);case"datetime":return new M(this)}throw new Error("Undefined type")}async success(t,e){return await this.typeElement.successResponse(t,e)}async error(t,e){return await this.typeElement.errorResponse(t,e)}enable(){this.disabled=!1,this.element.classList.remove("dark-editable-element-disabled"),this.modeElement.enable()}disable(){this.disabled=!0,this.element.classList.add("dark-editable-element-disabled"),this.modeElement.enable()}setValue(t){this.value=t,this.init_text()}getValue(){return this.value}}let C=function(){return{notifications:{error:{show:!1,text:"",url:""},success:{show:!1,text:"",url:""},wait:{show:!1,text:""}},groupProperties:{id:0,transactionType:"",transactionTypeTranslated:"",title:"",date:new Date},dateFields:["book_date","due_date","interest_date","invoice_date","payment_date","process_date"],metaFields:["external_id","internal_reference","sepa_batch_id","sepa_ct_id","sepa_ct_op","sepa_db","sepa_country","sepa_cc","sepa_ep","sepa_ci","external_url"],amounts:{},entries:[],pageProperties:{},formatMoney(i,t){return console.log("formatting",i,t),t===""&&(t="EUR"),w(i,t)},format(i){return v(i,h.t("config.date_time_fns"))},init(){this.notifications.wait.show=!0,this.notifications.wait.text=h.t("firefly.wait_loading_data");const i=window.location.href.split("/"),t=parseInt(i[i.length-1]);new y().show(t,{}).then(n=>{const s=n.data.data;this.groupProperties.id=parseInt(s.id),this.groupProperties.transactionType=s.attributes.transactions[0].type,this.groupProperties.transactionTypeTranslated=h.t("firefly."+s.attributes.transactions[0].type),this.groupProperties.title=s.attributes.title??s.attributes.transactions[0].description,this.entries=b(s.attributes.transactions),this.notifications.wait.show=!1}).then(()=>{for(let s in this.entries)if(this.entries.hasOwnProperty(s)){const o=this.entries[s].currency_code,c=this.entries[s].foreign_currency_code;this.amounts[o]===void 0&&(this.amounts[o]=0,this.amounts[o]+=parseFloat(this.entries[s].amount)),c!==null&&c!==""&&this.amounts[c]===void 0&&(this.amounts[c]=0,this.amounts[c]+=parseFloat(this.entries[s].foreign_amount)),parseInt(s)===0&&(this.groupProperties.date=this.entries[s].date)}const n=document.getElementById("journal_description");new k(n,{mode:"inline",url:"/something-else"})}).catch(n=>{this.notifications.error.show=!0,this.notifications.error.text=n.message})}}},d={show:C,dates:x};function p(){Object.keys(d).forEach(i=>{console.log(`Loading page component "${i}"`);let t=d[i]();Alpine.data(i,()=>t)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),p()});window.bootstrapped&&(console.log("Loaded through window variable."),p()); diff --git a/public/build/assets/splice-errors-into-transactions-66181f21.js b/public/build/assets/splice-errors-into-transactions-66181f21.js new file mode 100644 index 0000000000..aaeda07fb9 --- /dev/null +++ b/public/build/assets/splice-errors-into-transactions-66181f21.js @@ -0,0 +1 @@ +import{a as l}from"./format-money-bcfc2969.js";import{a as p,G as f}from"./get-fa539542.js";import{n as m,i as _}from"./vendor-5cffaf70.js";function x(r,t,a){let n=[];for(let o in r)if(r.hasOwnProperty(o)){const e=r[o];let i=!1,u={};t!==null&&t.hasOwnProperty(o)&&(i=!0,t[o]);let c={};(i&&u.description!==e.description||!i)&&(c.description=e.description),c.source_name=e.source_account.name,c.destination_name=e.destination_account.name,c.amount=e.amount,c.currency_code=e.currency_code,c.date=e.date,c.interest_date=e.interest_date,c.book_date=e.book_date,c.process_date=e.process_date,c.due_date=e.due_date,c.payment_date=e.payment_date,c.invoice_date=e.invoice_date,c.budget_id=e.budget_id,c.category_name=e.category_name,c.piggy_bank_id=e.piggy_bank_id,c.bill_id=e.bill_id,c.tags=e.tags,c.notes=e.notes,c.internal_reference=e.internal_reference,c.external_url=e.external_url,c.store_location=!1,e.hasLocation&&(c.store_location=!0,c.longitude=e.longitude.toString(),c.latitude=e.latitude.toString(),c.zoom_level=e.zoomLevel),typeof e.foreign_currency_code<"u"&&e.foreign_currency_code.toString()!==""&&(c.foreign_currency_code=e.foreign_currency_code,typeof e.foreign_amount<"u"&&e.foreign_amount.toString()!==""&&(c.foreign_amount=e.foreign_amount),(typeof e.foreign_amount>"u"||e.foreign_amount.toString()==="")&&(delete c.foreign_amount,delete c.foreign_currency_code)),typeof e.source_account.id<"u"&&e.source_account.id.toString()!==""&&(c.source_id=e.source_account.id),typeof e.destination_account.id<"u"&&e.destination_account.id.toString()!==""&&(c.destination_id=e.destination_account.id),c.type=a,n.push(c)}return n}let g=class{list(t){return l.get("/api/v2/currencies",{params:t})}};function C(){let r={page:1,limit:1337};return new g().list(r).then(a=>{let n={defaultCurrency:{},nativeCurrencies:[],foreignCurrencies:[],enabledCurrencies:[]};n.foreignCurrencies.push({id:0,name:"(no foreign currency)",code:"",default:!1,symbol:"",decimal_places:2});for(let o in a.data.data)if(a.data.data.hasOwnProperty(o)){let e=a.data.data[o];if(e.attributes.enabled){let i={id:e.id,name:e.attributes.name,code:e.attributes.code,default:e.attributes.default,symbol:e.attributes.symbol,decimal_places:e.attributes.decimal_places};i.default&&(n.defaultCurrency=i),n.enabledCurrencies.push(i),n.nativeCurrencies.push(i),n.foreignCurrencies.push(i)}}return n})}class y{list(t){return l.get("/api/v2/budgets",{params:t})}}function $(){let r={page:1,limit:1337};return new y().list(r).then(a=>{let n=[{id:0,name:"(no budget)"}];for(let o in a.data.data)if(a.data.data.hasOwnProperty(o)){let e=a.data.data[o],i={id:e.id,name:e.attributes.name};n.push(i)}return n})}function O(){let r={page:1,limit:1337};return new p().list(r).then(a=>{let n={0:{id:0,name:"(no group)",order:0,piggyBanks:[{id:0,name:"(no piggy bank)",order:0}]}};for(let o in a.data.data)if(a.data.data.hasOwnProperty(o)){let e=a.data.data[o],i=e.attributes.object_group_id??"0",u=e.attributes.object_group_title??"(no group)",c={id:e.id,name:e.attributes.name,order:e.attributes.order};n.hasOwnProperty(i)||(n[i]={id:i,name:u,order:e.attributes.object_group_order??0,piggyBanks:[]}),n[i].piggyBanks.push(c),n[i].piggyBanks.sort((d,s)=>d.order-s.order)}return Object.keys(n).sort().reduce((o,e)=>(o[e]=n[e],o),{})})}function P(){let r={page:1,limit:1337};return new f().list(r).then(a=>{let n={0:{id:0,name:"(no group)",order:0,subscriptions:[{id:0,name:"(no subscription)",order:0}]}};for(let o in a.data.data)if(a.data.data.hasOwnProperty(o)){let e=a.data.data[o],i=e.attributes.object_group_id??"0",u=e.attributes.object_group_title??"(no group)",c={id:e.id,name:e.attributes.name,order:e.attributes.order};n.hasOwnProperty(i)||(n[i]={id:i,name:u,order:e.attributes.object_group_order??0,subscriptions:[]}),n[i].subscriptions.push(c),n[i].subscriptions.sort((d,s)=>d.order-s.order)}return Object.keys(n).sort().reduce((o,e)=>(o[e]=n[e],o),{})})}function j(){return{description:"/api/v2/autocomplete/transaction-descriptions",account:"/api/v2/autocomplete/accounts",category:"/api/v2/autocomplete/categories",tag:"/api/v2/autocomplete/tags"}}function A(r){const t={server:r.serverUrl,serverParams:{},fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},hiddenInput:!0,highlightTyped:!0,liveServer:!0};typeof r.filters<"u"&&r.filters.length>0&&(t.serverParams.types=r.filters),typeof r.onRenderItem<"u"&&r.onRenderItem!==null&&(t.onRenderItem=r.onRenderItem),r.valueField&&(t.valueField=r.valueField),r.labelField&&(t.labelField=r.labelField),r.onSelectItem&&(t.onSelectItem=r.onSelectItem),r.onChange&&(t.onChange=r.onChange),r.hiddenValue&&(t.hiddenValue=r.hiddenValue),m.init(r.selector,t)}function q(r,t){const a=parseInt(t._searchInput.attributes["data-index"].value);if(typeof r<"u"&&r.name){document.querySelector("#form")._x_dataStack[0].$data.entries[a].category_name=r.name;return}document.querySelector("#form")._x_dataStack[0].$data.entries[a].category_name=t._searchInput.value}function E(r,t){const a=parseInt(t._searchInput.attributes["data-index"].value);if(typeof r<"u"&&r.description){document.querySelector("#form")._x_dataStack[0].$data.entries[a].description=r.description;return}document.querySelector("#form")._x_dataStack[0].$data.entries[a].description=t._searchInput.value}function B(r,t){if(typeof r>"u"){const a=parseInt(t._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[a].destination_account.name===t._searchInput.value){console.warn('Ignore hallucinated destination account name change to "'+t._searchInput.value+'"');return}document.querySelector("#form")._x_dataStack[0].$data.entries[a].destination_account={name:t._searchInput.value,alpine_name:t._searchInput.value},document.querySelector("#form")._x_dataStack[0].changedDestinationAccount()}}function F(r,t){const a=parseInt(t._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[a].destination_account={id:r.id,name:r.name,alpine_name:r.name,type:r.type,currency_code:r.currency_code},document.querySelector("#form")._x_dataStack[0].changedDestinationAccount()}function G(r,t){if(typeof r>"u"){const a=parseInt(t._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[a].source_account.name===t._searchInput.value)return;document.querySelector("#form")._x_dataStack[0].$data.entries[a].source_account={name:t._searchInput.value,alpine_name:t._searchInput.value},document.querySelector("#form")._x_dataStack[0].changedSourceAccount()}}function R(r,t){const a=parseInt(t._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[a].source_account={id:r.id,name:r.name,alpine_name:r.name,type:r.type,currency_code:r.currency_code},document.querySelector("#form")._x_dataStack[0].changedSourceAccount()}class h{post(t,a,n){let o="/api/v1/attachments";return l.post(o,{filename:t,attachable_type:a,attachable_id:n})}upload(t,a){let n="./api/v1/attachments/"+t+"/upload";return axios.post(n,a)}}let b=function(r){let t=r.length,a=0,n=!1;for(const o in r)if(r.hasOwnProperty(o)&&/^0$|^[1-9]\d*$/.test(o)&&o<=4294967294&&n===!1){let e=new h;e.post(r[o].name,"TransactionJournal",r[o].journal).then(i=>{let u=parseInt(i.data.data.id);e.upload(u,r[o].content).then(c=>{if(a++,a===t){const d=new CustomEvent("upload-success",{some:"details"});document.dispatchEvent(d)}}).catch(c=>{console.error("Could not upload"),console.error(c),a++;const d=new CustomEvent("upload-failed",{error:c});document.dispatchEvent(d),n=!0})}).catch(i=>{console.error("Could not create upload."),console.error(i),a++;const u=new CustomEvent("upload-failed",{error:i});document.dispatchEvent(u),n=!0})}};function T(r,t){t=t.reverse();let a=[],n=0,o=[],e=document.querySelectorAll('input[name="attachments[]"]');for(const i in e)if(e.hasOwnProperty(i)&&/^0$|^[1-9]\d*$/.test(i)&&i<=4294967294)for(const u in e[i].files)e[i].files.hasOwnProperty(u)&&/^0$|^[1-9]\d*$/.test(u)&&u<=4294967294&&(a.push({journal:t[i].transaction_journal_id,file:e[i].files[u]}),n++);for(const i in a)a.hasOwnProperty(i)&&/^0$|^[1-9]\d*$/.test(i)&&i<=4294967294&&function(u,c){let d=new FileReader;d.onloadend=function(s){s.target.readyState===FileReader.DONE&&(o.push({name:a[c].file.name,journal:a[c].journal,content:new Blob([s.target.result])}),o.length===n&&b(o))},d.readAsArrayBuffer(u.file)}(a[i],i);return n}function v(r,t,a){let n=[];for(let o in a)a.hasOwnProperty(o)&&n.push(a[o].replace(r,t));return n}function D(r,t){let a,n,o;for(const e in r)if(r.hasOwnProperty(e)){if(e==="group_title"){console.error("Cannot handle error in group title.");continue}if(a=parseInt(e.split(".")[1]),n=e.split(".")[2],o=v(e,n,r[e]),!t.hasOwnProperty(a)){console.error("Cannot handle errors in index #"+a);continue}switch(n){case"currency_code":case"foreign_currency_code":case"category_name":case"piggy_bank_id":case"notes":case"internal_reference":case"external_url":case"latitude":case"longitude":case"zoom_level":case"interest_date":case"book_date":case"process_date":case"due_date":case"payment_date":case"invoice_date":case"amount":case"date":case"budget_id":case"bill_id":case"description":case"tags":t[a].errors[n]=o;break;case"source_name":case"source_id":t[a].errors.source_account=t[a].errors.source_account.concat(o);break;case"type":t[a].errors.source_account=t[a].errors.source_account.concat([_.t("validation.bad_type_source")]),t[a].errors.destination_account=t[a].errors.destination_account.concat([_.t("validation.bad_type_destination")]);break;case"destination_name":case"destination_id":t[a].errors.destination_account=t[a].errors.destination_account.concat(o);break;case"foreign_amount":case"foreign_currency_id":t[a].errors.foreign_amount=t[a].errors.foreign_amount.concat(o);break}typeof t[a]<"u"&&(t[a].errors.source_account=Array.from(new Set(t[a].errors.source_account)),t[a].errors.destination_account=Array.from(new Set(t[a].errors.destination_account)))}return console.log(t[0].errors),t}export{$ as a,O as b,P as c,T as d,A as e,G as f,R as g,B as h,F as i,j,q as k,C as l,E as m,x as p,D as s}; diff --git a/public/build/assets/vendor-4332182f.js b/public/build/assets/vendor-4332182f.js deleted file mode 100644 index b2d9cb282d..0000000000 --- a/public/build/assets/vendor-4332182f.js +++ /dev/null @@ -1,52 +0,0 @@ -var ra=Object.defineProperty;var aa=(e,t,n)=>t in e?ra(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Y=(e,t,n)=>(aa(e,typeof t!="symbol"?t+"":t,n),n);function bind$4(e,t){return function(){return e.apply(t,arguments)}}const{toString:toString$7}=Object.prototype,{getPrototypeOf}=Object,kindOf=(e=>t=>{const n=toString$7.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),kindOfTest=e=>(e=e.toLowerCase(),t=>kindOf(t)===e),typeOfTest=e=>t=>typeof t===e,{isArray:isArray$d}=Array,isUndefined=typeOfTest("undefined");function isBuffer$3(e){return e!==null&&!isUndefined(e)&&e.constructor!==null&&!isUndefined(e.constructor)&&isFunction$6(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const isArrayBuffer=kindOfTest("ArrayBuffer");function isArrayBufferView(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&isArrayBuffer(e.buffer),t}const isString$1=typeOfTest("string"),isFunction$6=typeOfTest("function"),isNumber$1=typeOfTest("number"),isObject$c=e=>e!==null&&typeof e=="object",isBoolean=e=>e===!0||e===!1,isPlainObject=e=>{if(kindOf(e)!=="object")return!1;const t=getPrototypeOf(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)},isDate$1=kindOfTest("Date"),isFile=kindOfTest("File"),isBlob=kindOfTest("Blob"),isFileList=kindOfTest("FileList"),isStream=e=>isObject$c(e)&&isFunction$6(e.pipe),isFormData=e=>{let t;return e&&(typeof FormData=="function"&&e instanceof FormData||isFunction$6(e.append)&&((t=kindOf(e))==="formdata"||t==="object"&&isFunction$6(e.toString)&&e.toString()==="[object FormData]"))},isURLSearchParams=kindOfTest("URLSearchParams"),trim$2=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function forEach(e,t,{allOwnKeys:n=!1}={}){if(e===null||typeof e>"u")return;let i,r;if(typeof e!="object"&&(e=[e]),isArray$d(e))for(i=0,r=e.length;i0;)if(r=n[i],t===r.toLowerCase())return r;return null}const _global=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),isContextDefined=e=>!isUndefined(e)&&e!==_global;function merge$1(){const{caseless:e}=isContextDefined(this)&&this||{},t={},n=(i,r)=>{const a=e&&findKey$1(t,r)||r;isPlainObject(t[a])&&isPlainObject(i)?t[a]=merge$1(t[a],i):isPlainObject(i)?t[a]=merge$1({},i):isArray$d(i)?t[a]=i.slice():t[a]=i};for(let i=0,r=arguments.length;i(forEach(t,(r,a)=>{n&&isFunction$6(r)?e[a]=bind$4(r,n):e[a]=r},{allOwnKeys:i}),e),stripBOM=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),inherits=(e,t,n,i)=>{e.prototype=Object.create(t.prototype,i),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),n&&Object.assign(e.prototype,n)},toFlatObject=(e,t,n,i)=>{let r,a,o;const l={};if(t=t||{},e==null)return t;do{for(r=Object.getOwnPropertyNames(e),a=r.length;a-- >0;)o=r[a],(!i||i(o,e,t))&&!l[o]&&(t[o]=e[o],l[o]=!0);e=n!==!1&&getPrototypeOf(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},endsWith=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;const i=e.indexOf(t,n);return i!==-1&&i===n},toArray=e=>{if(!e)return null;if(isArray$d(e))return e;let t=e.length;if(!isNumber$1(t))return null;const n=new Array(t);for(;t-- >0;)n[t]=e[t];return n},isTypedArray$3=(e=>t=>e&&t instanceof e)(typeof Uint8Array<"u"&&getPrototypeOf(Uint8Array)),forEachEntry=(e,t)=>{const i=(e&&e[Symbol.iterator]).call(e);let r;for(;(r=i.next())&&!r.done;){const a=r.value;t.call(e,a[0],a[1])}},matchAll=(e,t)=>{let n;const i=[];for(;(n=e.exec(t))!==null;)i.push(n);return i},isHTMLForm=kindOfTest("HTMLFormElement"),toCamelCase=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(n,i,r){return i.toUpperCase()+r}),hasOwnProperty$c=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),isRegExp=kindOfTest("RegExp"),reduceDescriptors=(e,t)=>{const n=Object.getOwnPropertyDescriptors(e),i={};forEach(n,(r,a)=>{let o;(o=t(r,a,e))!==!1&&(i[a]=o||r)}),Object.defineProperties(e,i)},freezeMethods=e=>{reduceDescriptors(e,(t,n)=>{if(isFunction$6(e)&&["arguments","caller","callee"].indexOf(n)!==-1)return!1;const i=e[n];if(isFunction$6(i)){if(t.enumerable=!1,"writable"in t){t.writable=!1;return}t.set||(t.set=()=>{throw Error("Can not rewrite read-only method '"+n+"'")})}})},toObjectSet=(e,t)=>{const n={},i=r=>{r.forEach(a=>{n[a]=!0})};return isArray$d(e)?i(e):i(String(e).split(t)),n},noop$4=()=>{},toFiniteNumber=(e,t)=>(e=+e,Number.isFinite(e)?e:t),ALPHA="abcdefghijklmnopqrstuvwxyz",DIGIT="0123456789",ALPHABET={DIGIT,ALPHA,ALPHA_DIGIT:ALPHA+ALPHA.toUpperCase()+DIGIT},generateString=(e=16,t=ALPHABET.ALPHA_DIGIT)=>{let n="";const{length:i}=t;for(;e--;)n+=t[Math.random()*i|0];return n};function isSpecCompliantForm(e){return!!(e&&isFunction$6(e.append)&&e[Symbol.toStringTag]==="FormData"&&e[Symbol.iterator])}const toJSONObject=e=>{const t=new Array(10),n=(i,r)=>{if(isObject$c(i)){if(t.indexOf(i)>=0)return;if(!("toJSON"in i)){t[r]=i;const a=isArray$d(i)?[]:{};return forEach(i,(o,l)=>{const u=n(o,r+1);!isUndefined(u)&&(a[l]=u)}),t[r]=void 0,a}}return i};return n(e,0)},isAsyncFn=kindOfTest("AsyncFunction"),isThenable=e=>e&&(isObject$c(e)||isFunction$6(e))&&isFunction$6(e.then)&&isFunction$6(e.catch),utils$1={isArray:isArray$d,isArrayBuffer,isBuffer:isBuffer$3,isFormData,isArrayBufferView,isString:isString$1,isNumber:isNumber$1,isBoolean,isObject:isObject$c,isPlainObject,isUndefined,isDate:isDate$1,isFile,isBlob,isRegExp,isFunction:isFunction$6,isStream,isURLSearchParams,isTypedArray:isTypedArray$3,isFileList,forEach,merge:merge$1,extend,trim:trim$2,stripBOM,inherits,toFlatObject,kindOf,kindOfTest,endsWith,toArray,forEachEntry,matchAll,isHTMLForm,hasOwnProperty:hasOwnProperty$c,hasOwnProp:hasOwnProperty$c,reduceDescriptors,freezeMethods,toObjectSet,toCamelCase,noop:noop$4,toFiniteNumber,findKey:findKey$1,global:_global,isContextDefined,ALPHABET,generateString,isSpecCompliantForm,toJSONObject,isAsyncFn,isThenable};function AxiosError(e,t,n,i,r){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack,this.message=e,this.name="AxiosError",t&&(this.code=t),n&&(this.config=n),i&&(this.request=i),r&&(this.response=r)}utils$1.inherits(AxiosError,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:utils$1.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});const prototype$1=AxiosError.prototype,descriptors$1={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach(e=>{descriptors$1[e]={value:e}});Object.defineProperties(AxiosError,descriptors$1);Object.defineProperty(prototype$1,"isAxiosError",{value:!0});AxiosError.from=(e,t,n,i,r,a)=>{const o=Object.create(prototype$1);return utils$1.toFlatObject(e,o,function(u){return u!==Error.prototype},l=>l!=="isAxiosError"),AxiosError.call(o,e.message,t,n,i,r),o.cause=e,o.name=e.name,a&&Object.assign(o,a),o};const httpAdapter=null;function isVisitable(e){return utils$1.isPlainObject(e)||utils$1.isArray(e)}function removeBrackets(e){return utils$1.endsWith(e,"[]")?e.slice(0,-2):e}function renderKey(e,t,n){return e?e.concat(t).map(function(r,a){return r=removeBrackets(r),!n&&a?"["+r+"]":r}).join(n?".":""):t}function isFlatArray(e){return utils$1.isArray(e)&&!e.some(isVisitable)}const predicates=utils$1.toFlatObject(utils$1,{},null,function(t){return/^is[A-Z]/.test(t)});function toFormData(e,t,n){if(!utils$1.isObject(e))throw new TypeError("target must be an object");t=t||new FormData,n=utils$1.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(E,x){return!utils$1.isUndefined(x[E])});const i=n.metaTokens,r=n.visitor||m,a=n.dots,o=n.indexes,u=(n.Blob||typeof Blob<"u"&&Blob)&&utils$1.isSpecCompliantForm(t);if(!utils$1.isFunction(r))throw new TypeError("visitor must be a function");function d(w){if(w===null)return"";if(utils$1.isDate(w))return w.toISOString();if(!u&&utils$1.isBlob(w))throw new AxiosError("Blob is not supported. Use a Buffer instead.");return utils$1.isArrayBuffer(w)||utils$1.isTypedArray(w)?u&&typeof Blob=="function"?new Blob([w]):Buffer.from(w):w}function m(w,E,x){let M=w;if(w&&!x&&typeof w=="object"){if(utils$1.endsWith(E,"{}"))E=i?E:E.slice(0,-2),w=JSON.stringify(w);else if(utils$1.isArray(w)&&isFlatArray(w)||(utils$1.isFileList(w)||utils$1.endsWith(E,"[]"))&&(M=utils$1.toArray(w)))return E=removeBrackets(E),M.forEach(function(R,W){!(utils$1.isUndefined(R)||R===null)&&t.append(o===!0?renderKey([E],W,a):o===null?E:E+"[]",d(R))}),!1}return isVisitable(w)?!0:(t.append(renderKey(x,E,a),d(w)),!1)}const g=[],_=Object.assign(predicates,{defaultVisitor:m,convertValue:d,isVisitable});function v(w,E){if(!utils$1.isUndefined(w)){if(g.indexOf(w)!==-1)throw Error("Circular reference detected in "+E.join("."));g.push(w),utils$1.forEach(w,function(M,S){(!(utils$1.isUndefined(M)||M===null)&&r.call(t,M,utils$1.isString(S)?S.trim():S,E,_))===!0&&v(M,E?E.concat(S):[S])}),g.pop()}}if(!utils$1.isObject(e))throw new TypeError("data must be an object");return v(e),t}function encode$1(e){const t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(i){return t[i]})}function AxiosURLSearchParams(e,t){this._pairs=[],e&&toFormData(e,this,t)}const prototype=AxiosURLSearchParams.prototype;prototype.append=function(t,n){this._pairs.push([t,n])};prototype.toString=function(t){const n=t?function(i){return t.call(this,i,encode$1)}:encode$1;return this._pairs.map(function(r){return n(r[0])+"="+n(r[1])},"").join("&")};function encode(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function buildURL(e,t,n){if(!t)return e;const i=n&&n.encode||encode,r=n&&n.serialize;let a;if(r?a=r(t,n):a=utils$1.isURLSearchParams(t)?t.toString():new AxiosURLSearchParams(t,n).toString(i),a){const o=e.indexOf("#");o!==-1&&(e=e.slice(0,o)),e+=(e.indexOf("?")===-1?"?":"&")+a}return e}class InterceptorManager{constructor(){this.handlers=[]}use(t,n,i){return this.handlers.push({fulfilled:t,rejected:n,synchronous:i?i.synchronous:!1,runWhen:i?i.runWhen:null}),this.handlers.length-1}eject(t){this.handlers[t]&&(this.handlers[t]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(t){utils$1.forEach(this.handlers,function(i){i!==null&&t(i)})}}const InterceptorManager$1=InterceptorManager,transitionalDefaults={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},URLSearchParams$1=typeof URLSearchParams<"u"?URLSearchParams:AxiosURLSearchParams,FormData$1=typeof FormData<"u"?FormData:null,Blob$1=typeof Blob<"u"?Blob:null,platform$1={isBrowser:!0,classes:{URLSearchParams:URLSearchParams$1,FormData:FormData$1,Blob:Blob$1},protocols:["http","https","file","blob","url","data"]},hasBrowserEnv=typeof window<"u"&&typeof document<"u",hasStandardBrowserEnv=(e=>hasBrowserEnv&&["ReactNative","NativeScript","NS"].indexOf(e)<0)(typeof navigator<"u"&&navigator.product),hasStandardBrowserWebWorkerEnv=(()=>typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope&&typeof self.importScripts=="function")(),utils=Object.freeze(Object.defineProperty({__proto__:null,hasBrowserEnv,hasStandardBrowserEnv,hasStandardBrowserWebWorkerEnv},Symbol.toStringTag,{value:"Module"})),platform={...utils,...platform$1};function toURLEncodedForm(e,t){return toFormData(e,new platform.classes.URLSearchParams,Object.assign({visitor:function(n,i,r,a){return platform.isNode&&utils$1.isBuffer(n)?(this.append(i,n.toString("base64")),!1):a.defaultVisitor.apply(this,arguments)}},t))}function parsePropPath(e){return utils$1.matchAll(/\w+|\[(\w*)]/g,e).map(t=>t[0]==="[]"?"":t[1]||t[0])}function arrayToObject(e){const t={},n=Object.keys(e);let i;const r=n.length;let a;for(i=0;i=n.length;return o=!o&&utils$1.isArray(r)?r.length:o,u?(utils$1.hasOwnProp(r,o)?r[o]=[r[o],i]:r[o]=i,!l):((!r[o]||!utils$1.isObject(r[o]))&&(r[o]=[]),t(n,i,r[o],a)&&utils$1.isArray(r[o])&&(r[o]=arrayToObject(r[o])),!l)}if(utils$1.isFormData(e)&&utils$1.isFunction(e.entries)){const n={};return utils$1.forEachEntry(e,(i,r)=>{t(parsePropPath(i),r,n,0)}),n}return null}function stringifySafely(e,t,n){if(utils$1.isString(e))try{return(t||JSON.parse)(e),utils$1.trim(e)}catch(i){if(i.name!=="SyntaxError")throw i}return(n||JSON.stringify)(e)}const defaults$1={transitional:transitionalDefaults,adapter:["xhr","http"],transformRequest:[function(t,n){const i=n.getContentType()||"",r=i.indexOf("application/json")>-1,a=utils$1.isObject(t);if(a&&utils$1.isHTMLForm(t)&&(t=new FormData(t)),utils$1.isFormData(t))return r&&r?JSON.stringify(formDataToJSON(t)):t;if(utils$1.isArrayBuffer(t)||utils$1.isBuffer(t)||utils$1.isStream(t)||utils$1.isFile(t)||utils$1.isBlob(t))return t;if(utils$1.isArrayBufferView(t))return t.buffer;if(utils$1.isURLSearchParams(t))return n.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),t.toString();let l;if(a){if(i.indexOf("application/x-www-form-urlencoded")>-1)return toURLEncodedForm(t,this.formSerializer).toString();if((l=utils$1.isFileList(t))||i.indexOf("multipart/form-data")>-1){const u=this.env&&this.env.FormData;return toFormData(l?{"files[]":t}:t,u&&new u,this.formSerializer)}}return a||r?(n.setContentType("application/json",!1),stringifySafely(t)):t}],transformResponse:[function(t){const n=this.transitional||defaults$1.transitional,i=n&&n.forcedJSONParsing,r=this.responseType==="json";if(t&&utils$1.isString(t)&&(i&&!this.responseType||r)){const o=!(n&&n.silentJSONParsing)&&r;try{return JSON.parse(t)}catch(l){if(o)throw l.name==="SyntaxError"?AxiosError.from(l,AxiosError.ERR_BAD_RESPONSE,this,null,this.response):l}}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:platform.classes.FormData,Blob:platform.classes.Blob},validateStatus:function(t){return t>=200&&t<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};utils$1.forEach(["delete","get","head","post","put","patch"],e=>{defaults$1.headers[e]={}});const defaults$2=defaults$1,ignoreDuplicateOf=utils$1.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),parseHeaders=e=>{const t={};let n,i,r;return e&&e.split(` -`).forEach(function(o){r=o.indexOf(":"),n=o.substring(0,r).trim().toLowerCase(),i=o.substring(r+1).trim(),!(!n||t[n]&&ignoreDuplicateOf[n])&&(n==="set-cookie"?t[n]?t[n].push(i):t[n]=[i]:t[n]=t[n]?t[n]+", "+i:i)}),t},$internals=Symbol("internals");function normalizeHeader(e){return e&&String(e).trim().toLowerCase()}function normalizeValue(e){return e===!1||e==null?e:utils$1.isArray(e)?e.map(normalizeValue):String(e)}function parseTokens(e){const t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let i;for(;i=n.exec(e);)t[i[1]]=i[2];return t}const isValidHeaderName=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function matchHeaderValue(e,t,n,i,r){if(utils$1.isFunction(i))return i.call(this,t,n);if(r&&(t=n),!!utils$1.isString(t)){if(utils$1.isString(i))return t.indexOf(i)!==-1;if(utils$1.isRegExp(i))return i.test(t)}}function formatHeader(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,n,i)=>n.toUpperCase()+i)}function buildAccessors(e,t){const n=utils$1.toCamelCase(" "+t);["get","set","has"].forEach(i=>{Object.defineProperty(e,i+n,{value:function(r,a,o){return this[i].call(this,t,r,a,o)},configurable:!0})})}class AxiosHeaders{constructor(t){t&&this.set(t)}set(t,n,i){const r=this;function a(l,u,d){const m=normalizeHeader(u);if(!m)throw new Error("header name must be a non-empty string");const g=utils$1.findKey(r,m);(!g||r[g]===void 0||d===!0||d===void 0&&r[g]!==!1)&&(r[g||u]=normalizeValue(l))}const o=(l,u)=>utils$1.forEach(l,(d,m)=>a(d,m,u));return utils$1.isPlainObject(t)||t instanceof this.constructor?o(t,n):utils$1.isString(t)&&(t=t.trim())&&!isValidHeaderName(t)?o(parseHeaders(t),n):t!=null&&a(n,t,i),this}get(t,n){if(t=normalizeHeader(t),t){const i=utils$1.findKey(this,t);if(i){const r=this[i];if(!n)return r;if(n===!0)return parseTokens(r);if(utils$1.isFunction(n))return n.call(this,r,i);if(utils$1.isRegExp(n))return n.exec(r);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,n){if(t=normalizeHeader(t),t){const i=utils$1.findKey(this,t);return!!(i&&this[i]!==void 0&&(!n||matchHeaderValue(this,this[i],i,n)))}return!1}delete(t,n){const i=this;let r=!1;function a(o){if(o=normalizeHeader(o),o){const l=utils$1.findKey(i,o);l&&(!n||matchHeaderValue(i,i[l],l,n))&&(delete i[l],r=!0)}}return utils$1.isArray(t)?t.forEach(a):a(t),r}clear(t){const n=Object.keys(this);let i=n.length,r=!1;for(;i--;){const a=n[i];(!t||matchHeaderValue(this,this[a],a,t,!0))&&(delete this[a],r=!0)}return r}normalize(t){const n=this,i={};return utils$1.forEach(this,(r,a)=>{const o=utils$1.findKey(i,a);if(o){n[o]=normalizeValue(r),delete n[a];return}const l=t?formatHeader(a):String(a).trim();l!==a&&delete n[a],n[l]=normalizeValue(r),i[l]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const n=Object.create(null);return utils$1.forEach(this,(i,r)=>{i!=null&&i!==!1&&(n[r]=t&&utils$1.isArray(i)?i.join(", "):i)}),n}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([t,n])=>t+": "+n).join(` -`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...n){const i=new this(t);return n.forEach(r=>i.set(r)),i}static accessor(t){const i=(this[$internals]=this[$internals]={accessors:{}}).accessors,r=this.prototype;function a(o){const l=normalizeHeader(o);i[l]||(buildAccessors(r,o),i[l]=!0)}return utils$1.isArray(t)?t.forEach(a):a(t),this}}AxiosHeaders.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);utils$1.reduceDescriptors(AxiosHeaders.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(i){this[n]=i}}});utils$1.freezeMethods(AxiosHeaders);const AxiosHeaders$1=AxiosHeaders;function transformData(e,t){const n=this||defaults$2,i=t||n,r=AxiosHeaders$1.from(i.headers);let a=i.data;return utils$1.forEach(e,function(l){a=l.call(n,a,r.normalize(),t?t.status:void 0)}),r.normalize(),a}function isCancel(e){return!!(e&&e.__CANCEL__)}function CanceledError(e,t,n){AxiosError.call(this,e??"canceled",AxiosError.ERR_CANCELED,t,n),this.name="CanceledError"}utils$1.inherits(CanceledError,AxiosError,{__CANCEL__:!0});function settle(e,t,n){const i=n.config.validateStatus;!n.status||!i||i(n.status)?e(n):t(new AxiosError("Request failed with status code "+n.status,[AxiosError.ERR_BAD_REQUEST,AxiosError.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}const cookies=platform.hasStandardBrowserEnv?{write(e,t,n,i,r,a){const o=[e+"="+encodeURIComponent(t)];utils$1.isNumber(n)&&o.push("expires="+new Date(n).toGMTString()),utils$1.isString(i)&&o.push("path="+i),utils$1.isString(r)&&o.push("domain="+r),a===!0&&o.push("secure"),document.cookie=o.join("; ")},read(e){const t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove(e){this.write(e,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function isAbsoluteURL(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function combineURLs(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""):e}function buildFullPath(e,t){return e&&!isAbsoluteURL(t)?combineURLs(e,t):t}const isURLSameOrigin=platform.hasStandardBrowserEnv?function(){const t=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement("a");let i;function r(a){let o=a;return t&&(n.setAttribute("href",o),o=n.href),n.setAttribute("href",o),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,""):"",host:n.host,search:n.search?n.search.replace(/^\?/,""):"",hash:n.hash?n.hash.replace(/^#/,""):"",hostname:n.hostname,port:n.port,pathname:n.pathname.charAt(0)==="/"?n.pathname:"/"+n.pathname}}return i=r(window.location.href),function(o){const l=utils$1.isString(o)?r(o):o;return l.protocol===i.protocol&&l.host===i.host}}():function(){return function(){return!0}}();function parseProtocol(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}function speedometer(e,t){e=e||10;const n=new Array(e),i=new Array(e);let r=0,a=0,o;return t=t!==void 0?t:1e3,function(u){const d=Date.now(),m=i[a];o||(o=d),n[r]=u,i[r]=d;let g=a,_=0;for(;g!==r;)_+=n[g++],g=g%e;if(r=(r+1)%e,r===a&&(a=(a+1)%e),d-o{const a=r.loaded,o=r.lengthComputable?r.total:void 0,l=a-n,u=i(l),d=a<=o;n=a;const m={loaded:a,total:o,progress:o?a/o:void 0,bytes:l,rate:u||void 0,estimated:u&&o&&d?(o-a)/u:void 0,event:r};m[t?"download":"upload"]=!0,e(m)}}const isXHRAdapterSupported=typeof XMLHttpRequest<"u",xhrAdapter=isXHRAdapterSupported&&function(e){return new Promise(function(n,i){let r=e.data;const a=AxiosHeaders$1.from(e.headers).normalize();let{responseType:o,withXSRFToken:l}=e,u;function d(){e.cancelToken&&e.cancelToken.unsubscribe(u),e.signal&&e.signal.removeEventListener("abort",u)}let m;if(utils$1.isFormData(r)){if(platform.hasStandardBrowserEnv||platform.hasStandardBrowserWebWorkerEnv)a.setContentType(!1);else if((m=a.getContentType())!==!1){const[E,...x]=m?m.split(";").map(M=>M.trim()).filter(Boolean):[];a.setContentType([E||"multipart/form-data",...x].join("; "))}}let g=new XMLHttpRequest;if(e.auth){const E=e.auth.username||"",x=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";a.set("Authorization","Basic "+btoa(E+":"+x))}const _=buildFullPath(e.baseURL,e.url);g.open(e.method.toUpperCase(),buildURL(_,e.params,e.paramsSerializer),!0),g.timeout=e.timeout;function v(){if(!g)return;const E=AxiosHeaders$1.from("getAllResponseHeaders"in g&&g.getAllResponseHeaders()),M={data:!o||o==="text"||o==="json"?g.responseText:g.response,status:g.status,statusText:g.statusText,headers:E,config:e,request:g};settle(function(R){n(R),d()},function(R){i(R),d()},M),g=null}if("onloadend"in g?g.onloadend=v:g.onreadystatechange=function(){!g||g.readyState!==4||g.status===0&&!(g.responseURL&&g.responseURL.indexOf("file:")===0)||setTimeout(v)},g.onabort=function(){g&&(i(new AxiosError("Request aborted",AxiosError.ECONNABORTED,e,g)),g=null)},g.onerror=function(){i(new AxiosError("Network Error",AxiosError.ERR_NETWORK,e,g)),g=null},g.ontimeout=function(){let x=e.timeout?"timeout of "+e.timeout+"ms exceeded":"timeout exceeded";const M=e.transitional||transitionalDefaults;e.timeoutErrorMessage&&(x=e.timeoutErrorMessage),i(new AxiosError(x,M.clarifyTimeoutError?AxiosError.ETIMEDOUT:AxiosError.ECONNABORTED,e,g)),g=null},platform.hasStandardBrowserEnv&&(l&&utils$1.isFunction(l)&&(l=l(e)),l||l!==!1&&isURLSameOrigin(_))){const E=e.xsrfHeaderName&&e.xsrfCookieName&&cookies.read(e.xsrfCookieName);E&&a.set(e.xsrfHeaderName,E)}r===void 0&&a.setContentType(null),"setRequestHeader"in g&&utils$1.forEach(a.toJSON(),function(x,M){g.setRequestHeader(M,x)}),utils$1.isUndefined(e.withCredentials)||(g.withCredentials=!!e.withCredentials),o&&o!=="json"&&(g.responseType=e.responseType),typeof e.onDownloadProgress=="function"&&g.addEventListener("progress",progressEventReducer(e.onDownloadProgress,!0)),typeof e.onUploadProgress=="function"&&g.upload&&g.upload.addEventListener("progress",progressEventReducer(e.onUploadProgress)),(e.cancelToken||e.signal)&&(u=E=>{g&&(i(!E||E.type?new CanceledError(null,e,g):E),g.abort(),g=null)},e.cancelToken&&e.cancelToken.subscribe(u),e.signal&&(e.signal.aborted?u():e.signal.addEventListener("abort",u)));const w=parseProtocol(_);if(w&&platform.protocols.indexOf(w)===-1){i(new AxiosError("Unsupported protocol "+w+":",AxiosError.ERR_BAD_REQUEST,e));return}g.send(r||null)})},knownAdapters={http:httpAdapter,xhr:xhrAdapter};utils$1.forEach(knownAdapters,(e,t)=>{if(e){try{Object.defineProperty(e,"name",{value:t})}catch{}Object.defineProperty(e,"adapterName",{value:t})}});const renderReason=e=>`- ${e}`,isResolvedHandle=e=>utils$1.isFunction(e)||e===null||e===!1,adapters$1={getAdapter:e=>{e=utils$1.isArray(e)?e:[e];const{length:t}=e;let n,i;const r={};for(let a=0;a`adapter ${l} `+(u===!1?"is not supported by the environment":"is not available in the build"));let o=t?a.length>1?`since : -`+a.map(renderReason).join(` -`):" "+renderReason(a[0]):"as no adapter specified";throw new AxiosError("There is no suitable adapter to dispatch the request "+o,"ERR_NOT_SUPPORT")}return i},adapters:knownAdapters};function throwIfCancellationRequested(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new CanceledError(null,e)}function dispatchRequest(e){return throwIfCancellationRequested(e),e.headers=AxiosHeaders$1.from(e.headers),e.data=transformData.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),adapters$1.getAdapter(e.adapter||defaults$2.adapter)(e).then(function(i){return throwIfCancellationRequested(e),i.data=transformData.call(e,e.transformResponse,i),i.headers=AxiosHeaders$1.from(i.headers),i},function(i){return isCancel(i)||(throwIfCancellationRequested(e),i&&i.response&&(i.response.data=transformData.call(e,e.transformResponse,i.response),i.response.headers=AxiosHeaders$1.from(i.response.headers))),Promise.reject(i)})}const headersToObject=e=>e instanceof AxiosHeaders$1?e.toJSON():e;function mergeConfig(e,t){t=t||{};const n={};function i(d,m,g){return utils$1.isPlainObject(d)&&utils$1.isPlainObject(m)?utils$1.merge.call({caseless:g},d,m):utils$1.isPlainObject(m)?utils$1.merge({},m):utils$1.isArray(m)?m.slice():m}function r(d,m,g){if(utils$1.isUndefined(m)){if(!utils$1.isUndefined(d))return i(void 0,d,g)}else return i(d,m,g)}function a(d,m){if(!utils$1.isUndefined(m))return i(void 0,m)}function o(d,m){if(utils$1.isUndefined(m)){if(!utils$1.isUndefined(d))return i(void 0,d)}else return i(void 0,m)}function l(d,m,g){if(g in t)return i(d,m);if(g in e)return i(void 0,d)}const u={url:a,method:a,data:a,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:l,headers:(d,m)=>r(headersToObject(d),headersToObject(m),!0)};return utils$1.forEach(Object.keys(Object.assign({},e,t)),function(m){const g=u[m]||r,_=g(e[m],t[m],m);utils$1.isUndefined(_)&&g!==l||(n[m]=_)}),n}const VERSION$1="1.6.5",validators$1={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{validators$1[e]=function(i){return typeof i===e||"a"+(t<1?"n ":" ")+e}});const deprecatedWarnings={};validators$1.transitional=function(t,n,i){function r(a,o){return"[Axios v"+VERSION$1+"] Transitional option '"+a+"'"+o+(i?". "+i:"")}return(a,o,l)=>{if(t===!1)throw new AxiosError(r(o," has been removed"+(n?" in "+n:"")),AxiosError.ERR_DEPRECATED);return n&&!deprecatedWarnings[o]&&(deprecatedWarnings[o]=!0,console.warn(r(o," has been deprecated since v"+n+" and will be removed in the near future"))),t?t(a,o,l):!0}};function assertOptions(e,t,n){if(typeof e!="object")throw new AxiosError("options must be an object",AxiosError.ERR_BAD_OPTION_VALUE);const i=Object.keys(e);let r=i.length;for(;r-- >0;){const a=i[r],o=t[a];if(o){const l=e[a],u=l===void 0||o(l,a,e);if(u!==!0)throw new AxiosError("option "+a+" must be "+u,AxiosError.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new AxiosError("Unknown option "+a,AxiosError.ERR_BAD_OPTION)}}const validator={assertOptions,validators:validators$1},validators=validator.validators;class Axios{constructor(t){this.defaults=t,this.interceptors={request:new InterceptorManager$1,response:new InterceptorManager$1}}request(t,n){typeof t=="string"?(n=n||{},n.url=t):n=t||{},n=mergeConfig(this.defaults,n);const{transitional:i,paramsSerializer:r,headers:a}=n;i!==void 0&&validator.assertOptions(i,{silentJSONParsing:validators.transitional(validators.boolean),forcedJSONParsing:validators.transitional(validators.boolean),clarifyTimeoutError:validators.transitional(validators.boolean)},!1),r!=null&&(utils$1.isFunction(r)?n.paramsSerializer={serialize:r}:validator.assertOptions(r,{encode:validators.function,serialize:validators.function},!0)),n.method=(n.method||this.defaults.method||"get").toLowerCase();let o=a&&utils$1.merge(a.common,a[n.method]);a&&utils$1.forEach(["delete","get","head","post","put","patch","common"],w=>{delete a[w]}),n.headers=AxiosHeaders$1.concat(o,a);const l=[];let u=!0;this.interceptors.request.forEach(function(E){typeof E.runWhen=="function"&&E.runWhen(n)===!1||(u=u&&E.synchronous,l.unshift(E.fulfilled,E.rejected))});const d=[];this.interceptors.response.forEach(function(E){d.push(E.fulfilled,E.rejected)});let m,g=0,_;if(!u){const w=[dispatchRequest.bind(this),void 0];for(w.unshift.apply(w,l),w.push.apply(w,d),_=w.length,m=Promise.resolve(n);g<_;)m=m.then(w[g++],w[g++]);return m}_=l.length;let v=n;for(g=0;g<_;){const w=l[g++],E=l[g++];try{v=w(v)}catch(x){E.call(this,x);break}}try{m=dispatchRequest.call(this,v)}catch(w){return Promise.reject(w)}for(g=0,_=d.length;g<_;)m=m.then(d[g++],d[g++]);return m}getUri(t){t=mergeConfig(this.defaults,t);const n=buildFullPath(t.baseURL,t.url);return buildURL(n,t.params,t.paramsSerializer)}}utils$1.forEach(["delete","get","head","options"],function(t){Axios.prototype[t]=function(n,i){return this.request(mergeConfig(i||{},{method:t,url:n,data:(i||{}).data}))}});utils$1.forEach(["post","put","patch"],function(t){function n(i){return function(a,o,l){return this.request(mergeConfig(l||{},{method:t,headers:i?{"Content-Type":"multipart/form-data"}:{},url:a,data:o}))}}Axios.prototype[t]=n(),Axios.prototype[t+"Form"]=n(!0)});const Axios$1=Axios;class CancelToken{constructor(t){if(typeof t!="function")throw new TypeError("executor must be a function.");let n;this.promise=new Promise(function(a){n=a});const i=this;this.promise.then(r=>{if(!i._listeners)return;let a=i._listeners.length;for(;a-- >0;)i._listeners[a](r);i._listeners=null}),this.promise.then=r=>{let a;const o=new Promise(l=>{i.subscribe(l),a=l}).then(r);return o.cancel=function(){i.unsubscribe(a)},o},t(function(a,o,l){i.reason||(i.reason=new CanceledError(a,o,l),n(i.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const n=this._listeners.indexOf(t);n!==-1&&this._listeners.splice(n,1)}static source(){let t;return{token:new CancelToken(function(r){t=r}),cancel:t}}}const CancelToken$1=CancelToken;function spread(e){return function(n){return e.apply(null,n)}}function isAxiosError(e){return utils$1.isObject(e)&&e.isAxiosError===!0}const HttpStatusCode={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(HttpStatusCode).forEach(([e,t])=>{HttpStatusCode[t]=e});const HttpStatusCode$1=HttpStatusCode;function createInstance(e){const t=new Axios$1(e),n=bind$4(Axios$1.prototype.request,t);return utils$1.extend(n,Axios$1.prototype,t,{allOwnKeys:!0}),utils$1.extend(n,t,null,{allOwnKeys:!0}),n.create=function(r){return createInstance(mergeConfig(e,r))},n}const axios=createInstance(defaults$2);axios.Axios=Axios$1;axios.CanceledError=CanceledError;axios.CancelToken=CancelToken$1;axios.isCancel=isCancel;axios.VERSION=VERSION$1;axios.toFormData=toFormData;axios.AxiosError=AxiosError;axios.Cancel=axios.CanceledError;axios.all=function(t){return Promise.all(t)};axios.spread=spread;axios.isAxiosError=isAxiosError;axios.mergeConfig=mergeConfig;axios.AxiosHeaders=AxiosHeaders$1;axios.formToJSON=e=>formDataToJSON(utils$1.isHTMLForm(e)?new FormData(e):e);axios.getAdapter=adapters$1.getAdapter;axios.HttpStatusCode=HttpStatusCode$1;axios.default=axios;const axios$1=axios;var commonjsGlobal=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function getDefaultExportFromCjs(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var assign=make_assign(),create$2=make_create(),trim$1=make_trim(),Global$5=typeof window<"u"?window:commonjsGlobal,util$7={assign,create:create$2,trim:trim$1,bind:bind$3,slice:slice$2,each:each$9,map:map$2,pluck:pluck$1,isList:isList$1,isFunction:isFunction$5,isObject:isObject$b,Global:Global$5};function make_assign(){return Object.assign?Object.assign:function(t,n,i,r){for(var a=1;a"u"?null:console;if(e){var t=e.warn?e.warn:e.log;t.apply(e,arguments)}}function createStore(e,t,n){n||(n=""),e&&!isList(e)&&(e=[e]),t&&!isList(t)&&(t=[t]);var i=n?"__storejs_"+n+"_":"",r=n?new RegExp("^"+i):null,a=/^[a-zA-Z0-9_\-]*$/;if(!a.test(n))throw new Error("store.js namespaces can only have alphanumerics + underscores and dashes");var o={_namespacePrefix:i,_namespaceRegexp:r,_testStorage:function(u){try{var d="__storejs__test__";u.write(d,d);var m=u.read(d)===d;return u.remove(d),m}catch{return!1}},_assignPluginFnProp:function(u,d){var m=this[d];this[d]=function(){var _=slice$1(arguments,0),v=this;function w(){if(m)return each$8(arguments,function(x,M){_[M]=x}),m.apply(v,_)}var E=[w].concat(_);return u.apply(v,E)}},_serialize:function(u){return JSON.stringify(u)},_deserialize:function(u,d){if(!u)return d;var m="";try{m=JSON.parse(u)}catch{m=u}return m!==void 0?m:d},_addStorage:function(u){this.enabled||this._testStorage(u)&&(this.storage=u,this.enabled=!0)},_addPlugin:function(u){var d=this;if(isList(u)){each$8(u,function(_){d._addPlugin(_)});return}var m=pluck(this.plugins,function(_){return u===_});if(!m){if(this.plugins.push(u),!isFunction$4(u))throw new Error("Plugins must be function values that return objects");var g=u.call(this);if(!isObject$a(g))throw new Error("Plugins must return an object of function properties");each$8(g,function(_,v){if(!isFunction$4(_))throw new Error("Bad plugin property: "+v+" from plugin "+u.name+". Plugins should only return functions.");d._assignPluginFnProp(_,v)})}},addStorage:function(u){_warn("store.addStorage(storage) is deprecated. Use createStore([storages])"),this._addStorage(u)}},l=create$1(o,storeAPI,{plugins:[]});return l.raw={},each$8(l,function(u,d){isFunction$4(u)&&(l.raw[d]=bind$2(l,u))}),each$8(e,function(u){l._addStorage(u)}),each$8(t,function(u){l._addPlugin(u)}),l}var util$5=util$7,Global$4=util$5.Global,localStorage_1={name:"localStorage",read:read$6,write:write$6,each:each$7,remove:remove$5,clearAll:clearAll$5};function localStorage(){return Global$4.localStorage}function read$6(e){return localStorage().getItem(e)}function write$6(e,t){return localStorage().setItem(e,t)}function each$7(e){for(var t=localStorage().length-1;t>=0;t--){var n=localStorage().key(t);e(read$6(n),n)}}function remove$5(e){return localStorage().removeItem(e)}function clearAll$5(){return localStorage().clear()}var util$4=util$7,Global$3=util$4.Global,oldFFGlobalStorage={name:"oldFF-globalStorage",read:read$5,write:write$5,each:each$6,remove:remove$4,clearAll:clearAll$4},globalStorage=Global$3.globalStorage;function read$5(e){return globalStorage[e]}function write$5(e,t){globalStorage[e]=t}function each$6(e){for(var t=globalStorage.length-1;t>=0;t--){var n=globalStorage.key(t);e(globalStorage[n],n)}}function remove$4(e){return globalStorage.removeItem(e)}function clearAll$4(){each$6(function(e,t){delete globalStorage[e]})}var util$3=util$7,Global$2=util$3.Global,oldIEUserDataStorage={name:"oldIE-userDataStorage",write:write$4,read:read$4,each:each$5,remove:remove$3,clearAll:clearAll$3},storageName="storejs",doc$1=Global$2.document,_withStorageEl=_makeIEStorageElFunction(),disable=(Global$2.navigator?Global$2.navigator.userAgent:"").match(/ (MSIE 8|MSIE 9|MSIE 10)\./);function write$4(e,t){if(!disable){var n=fixKey(e);_withStorageEl(function(i){i.setAttribute(n,t),i.save(storageName)})}}function read$4(e){if(!disable){var t=fixKey(e),n=null;return _withStorageEl(function(i){n=i.getAttribute(t)}),n}}function each$5(e){_withStorageEl(function(t){for(var n=t.XMLDocument.documentElement.attributes,i=n.length-1;i>=0;i--){var r=n[i];e(t.getAttribute(r.name),r.name)}})}function remove$3(e){var t=fixKey(e);_withStorageEl(function(n){n.removeAttribute(t),n.save(storageName)})}function clearAll$3(){_withStorageEl(function(e){var t=e.XMLDocument.documentElement.attributes;e.load(storageName);for(var n=t.length-1;n>=0;n--)e.removeAttribute(t[n].name);e.save(storageName)})}var forbiddenCharsRegex=new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]","g");function fixKey(e){return e.replace(/^\d/,"___$&").replace(forbiddenCharsRegex,"___")}function _makeIEStorageElFunction(){if(!doc$1||!doc$1.documentElement||!doc$1.documentElement.addBehavior)return null;var e="script",t,n,i;try{n=new ActiveXObject("htmlfile"),n.open(),n.write("<"+e+">document.w=window'),n.close(),t=n.w.frames[0].document,i=t.createElement("div")}catch{i=doc$1.createElement("div"),t=doc$1.body}return function(r){var a=[].slice.call(arguments,0);a.unshift(i),t.appendChild(i),i.addBehavior("#default#userData"),i.load(storageName),r.apply(this,a),t.removeChild(i)}}var util$2=util$7,Global$1=util$2.Global,trim=util$2.trim,cookieStorage={name:"cookieStorage",read:read$3,write:write$3,each:each$4,remove:remove$2,clearAll:clearAll$2},doc=Global$1.document;function read$3(e){if(!e||!_has(e))return null;var t="(?:^|.*;\\s*)"+escape(e).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";return unescape(doc.cookie.replace(new RegExp(t),"$1"))}function each$4(e){for(var t=doc.cookie.split(/; ?/g),n=t.length-1;n>=0;n--)if(trim(t[n])){var i=t[n].split("="),r=unescape(i[0]),a=unescape(i[1]);e(a,r)}}function write$3(e,t){e&&(doc.cookie=escape(e)+"="+escape(t)+"; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/")}function remove$2(e){!e||!_has(e)||(doc.cookie=escape(e)+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/")}function clearAll$2(){each$4(function(e,t){remove$2(t)})}function _has(e){return new RegExp("(?:^|;\\s*)"+escape(e).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=").test(doc.cookie)}var util$1=util$7,Global=util$1.Global,sessionStorage_1={name:"sessionStorage",read:read$2,write:write$2,each:each$3,remove:remove$1,clearAll:clearAll$1};function sessionStorage(){return Global.sessionStorage}function read$2(e){return sessionStorage().getItem(e)}function write$2(e,t){return sessionStorage().setItem(e,t)}function each$3(e){for(var t=sessionStorage().length-1;t>=0;t--){var n=sessionStorage().key(t);e(read$2(n),n)}}function remove$1(e){return sessionStorage().removeItem(e)}function clearAll$1(){return sessionStorage().clear()}var memoryStorage_1={name:"memoryStorage",read:read$1,write:write$1,each:each$2,remove,clearAll},memoryStorage={};function read$1(e){return memoryStorage[e]}function write$1(e,t){memoryStorage[e]=t}function each$2(e){for(var t in memoryStorage)memoryStorage.hasOwnProperty(t)&&e(memoryStorage[t],t)}function remove(e){delete memoryStorage[e]}function clearAll(e){memoryStorage={}}var all=[localStorage_1,oldFFGlobalStorage,oldIEUserDataStorage,cookieStorage,sessionStorage_1,memoryStorage_1],json2$1={},hasRequiredJson2;function requireJson2(){return hasRequiredJson2||(hasRequiredJson2=1,typeof JSON!="object"&&(JSON={}),function(){var rx_one=/^[\],:{}\s]*$/,rx_two=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,rx_three=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,rx_four=/(?:^|:|,)(?:\s*\[)+/g,rx_escapable=/[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,rx_dangerous=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;function f(e){return e<10?"0"+e:e}function this_value(){return this.valueOf()}typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=this_value,Number.prototype.toJSON=this_value,String.prototype.toJSON=this_value);var gap,indent,meta,rep;function quote(e){return rx_escapable.lastIndex=0,rx_escapable.test(e)?'"'+e.replace(rx_escapable,function(t){var n=meta[t];return typeof n=="string"?n:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function str(e,t){var n,i,r,a,o=gap,l,u=t[e];switch(u&&typeof u=="object"&&typeof u.toJSON=="function"&&(u=u.toJSON(e)),typeof rep=="function"&&(u=rep.call(t,e,u)),typeof u){case"string":return quote(u);case"number":return isFinite(u)?String(u):"null";case"boolean":case"null":return String(u);case"object":if(!u)return"null";if(gap+=indent,l=[],Object.prototype.toString.apply(u)==="[object Array]"){for(a=u.length,n=0;nlastFlushedIndex&&queue.splice(t,1)}function queueFlush(){!flushing&&!flushPending&&(flushPending=!0,queueMicrotask(flushJobs))}function flushJobs(){flushPending=!1,flushing=!0;for(let e=0;ee.effect(t,{scheduler:n=>{shouldSchedule?scheduler(n):n()}}),raw=e.raw}function overrideEffect(e){effect$3=e}function elementBoundEffect(e){let t=()=>{};return[i=>{let r=effect$3(i);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(a=>a())}),e._x_effects.add(r),t=()=>{r!==void 0&&(e._x_effects.delete(r),release(r))},r},()=>{t()}]}function dispatch(e,t,n={}){e.dispatchEvent(new CustomEvent(t,{detail:n,bubbles:!0,composed:!0,cancelable:!0}))}function walk(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(r=>walk(r,t));return}let n=!1;if(t(e,()=>n=!0),n)return;let i=e.firstElementChild;for(;i;)walk(i,t),i=i.nextElementSibling}function warn(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var started=!1;function start$1(){started&&warn("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),started=!0,document.body||warn("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's `