Merge branch 'release/4.8.0.3'

This commit is contained in:
James Cole 2019-08-23 14:25:17 +02:00
commit f090a9534f
138 changed files with 14901 additions and 14006 deletions

View File

@ -70,7 +70,7 @@ php artisan firefly-iii:cc-liabilities
php artisan firefly-iii:back-to-journals
php artisan firefly-iii:rename-account-meta
# there are 13 verify commands
# there are 14 verify commands
php artisan firefly-iii:fix-piggies
php artisan firefly-iii:create-link-types
php artisan firefly-iii:create-access-tokens
@ -80,10 +80,11 @@ php artisan firefly-iii:fix-transfer-budgets
php artisan firefly-iii:fix-uneven-amount
php artisan firefly-iii:delete-zero-amount
php artisan firefly-iii:delete-orphaned-transactions
php artisan firefly-iii:delete-empty-journals
php artisan firefly-iii:delete-empty-journals
php artisan firefly-iii:delete-empty-groups
php artisan firefly-iii:fix-account-types
php artisan firefly-iii:rename-meta-fields
php artisan firefly-iii:fix-ob-currencies
# report commands
php artisan firefly-iii:report-empty-objects

View File

@ -1,3 +1,24 @@
# 4.8.0.3 (API 0.10.1)
- #2438 Some balance issues when working with multiple currencies (a known issue)
- #2425 Transaction edit/create form is weird with the enter button
- #2424 auto complete tab doesn't work.
- #2441 Inconsistent character limit for currencies.
- #2443 500 error when submitting budgets
- #2446 Can't update current amount for piggy bank
- #2440 Errors when interacting with recurring transactions
- #2439 SQL error in API post new user
- Transaction report (after import, over email) is mostly empty
- Mass edit checkboxes doesn't work in a tag overview
- #2437 CPU issues when viewing accounts, probably run-away queries.
- #2432 Can't disable all currencies except one / can't disable EUR and switch to something else.
- Option to edit the budget is gone from edit transaction form.
- #2453 Search view things
- #2449 Can't add invoice date.
- #2448 Bad link in transaction overview
- #2447 Bad link in bill overview
- Improvements to various API end-points. Docs are updated.
# 4.8.0.2 (API 0.10.0)
- [Issue 2203](https://github.com/firefly-iii/firefly-iii/issues/2203) Reconciliation inconsistencies.

View File

@ -15,8 +15,8 @@ const pkgdef :Spk.PackageDefinition = (
manifest = (
appTitle = (defaultText = "Firefly III"),
appVersion = 34,
appMarketingVersion = (defaultText = "4.8.0.2"),
appVersion = 35,
appMarketingVersion = (defaultText = "4.8.0.3"),
actions = [
# Define your "new document" handlers here.

View File

@ -1,7 +1,7 @@
sudo: required
language: bash
env:
- VERSION=4.8.0.2
- VERSION=4.8.0.3
dist: xenial

View File

@ -1,28 +1,10 @@
FROM php:7.2-apache
FROM jc5x/firefly-iii-base-image:latest
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.5" maintainer="thegrumpydictator@gmail.com"
# Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Install stuff Firefly III runs with & depends on: php extensions, locales, dev headers and composer
RUN apt-get update && apt-get install -y locales unzip && apt-get clean && rm -rf /var/lib/apt/lists/*
ADD https://raw.githubusercontent.com/mlocati/docker-php-extension-installer/master/install-php-extensions /usr/local/bin/
RUN chmod uga+x /usr/local/bin/install-php-extensions && sync && \
install-php-extensions --cleanup bcmath ldap gd pdo_pgsql pdo_sqlite pdo_mysql intl opcache memcached
RUN a2enmod rewrite && a2enmod ssl
RUN echo "hu_HU.UTF-8 UTF-8\nro_RO.UTF-8 UTF-8\nnb_NO.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\ncs_CZ.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen
RUN locale-gen
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# configure PHP
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini && \
sed -i 's/max_execution_time = 30/max_execution_time = 600/' /usr/local/etc/php/php.ini && \
sed -i 's/memory_limit = 128M/memory_limit = 512M/' /usr/local/etc/php/php.ini
# Copy in Firefly III source
WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH
@ -32,15 +14,6 @@ RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest
# copy ca certs to correct location
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Expose port 80
EXPOSE 80

View File

@ -1,29 +1,10 @@
FROM php:7.2-apache
ARG ARCH
FROM jc5x/firefly-iii-base-image:latest
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.5" maintainer="thegrumpydictator@gmail.com"
# Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Install stuff Firefly III runs with & depends on: php extensions, locales, dev headers and composer
RUN apt-get update && apt-get install -y locales unzip && apt-get clean && rm -rf /var/lib/apt/lists/*
ADD https://raw.githubusercontent.com/mlocati/docker-php-extension-installer/master/install-php-extensions /usr/local/bin/
RUN chmod uga+x /usr/local/bin/install-php-extensions && sync && \
install-php-extensions --cleanup bcmath ldap gd pdo_pgsql pdo_sqlite pdo_mysql intl opcache memcached
RUN a2enmod rewrite && a2enmod ssl
RUN echo "hu_HU.UTF-8 UTF-8\nro_RO.UTF-8 UTF-8\nnb_NO.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\ncs_CZ.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen
RUN locale-gen
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# configure PHP
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini && \
sed -i 's/max_execution_time = 30/max_execution_time = 600/' /usr/local/etc/php/php.ini && \
sed -i 's/memory_limit = 128M/memory_limit = 512M/' /usr/local/etc/php/php.ini
# Copy in Firefly III source
WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH
@ -33,15 +14,6 @@ RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest
# copy ca certs to correct location
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Expose port 80
EXPOSE 80

View File

@ -1,25 +1,10 @@
FROM arm32v7/php:7.2-apache-stretch
ARG ARCH
COPY tmp/qemu-arm-static /usr/bin/qemu-arm-static
FROM jc5x/firefly-iii-base-image:latest
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.5" maintainer="thegrumpydictator@gmail.com"
# Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Install stuff Firefly III runs with & depends on: php extensions, locales, dev headers and composer
RUN apt-get update && apt-get install -y locales unzip && apt-get clean && rm -rf /var/lib/apt/lists/*
ADD https://raw.githubusercontent.com/mlocati/docker-php-extension-installer/master/install-php-extensions /usr/local/bin/
RUN chmod uga+x /usr/local/bin/install-php-extensions && sync && \
install-php-extensions --cleanup bcmath ldap gd pdo_pgsql pdo_sqlite pdo_mysql intl opcache memcached
RUN a2enmod rewrite && a2enmod ssl
RUN echo "hu_HU.UTF-8 UTF-8\nro_RO.UTF-8 UTF-8\nnb_NO.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\ncs_CZ.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen
RUN locale-gen
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Copy in Firefly III source
WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH
@ -29,15 +14,6 @@ RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest
# copy ca certs to correct location
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Expose port 80
EXPOSE 80

View File

@ -226,7 +226,14 @@ class PiggyBankController extends Controller
*/
public function update(PiggyBankRequest $request, PiggyBank $piggyBank): JsonResponse
{
$piggyBank = $this->repository->update($piggyBank, $request->getAll());
$data = $request->getAll();
$piggyBank = $this->repository->update($piggyBank, $data);
if ('' !== $data['current_amount']) {
$this->repository->setCurrentAmount($piggyBank, $data['current_amount']);
}
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));

View File

@ -24,7 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\UserRequest;
use FireflyIII\Api\V1\Requests\UserStoreRequest;
use FireflyIII\Api\V1\Requests\UserUpdateRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Transformers\UserTransformer;
@ -155,11 +156,11 @@ class UserController extends Controller
/**
* Store a new user.
*
* @param UserRequest $request
* @param UserStoreRequest $request
*
* @return JsonResponse
*/
public function store(UserRequest $request): JsonResponse
public function store(UserStoreRequest $request): JsonResponse
{
$data = $request->getAll();
$user = $this->repository->store($data);
@ -183,12 +184,12 @@ class UserController extends Controller
/**
* Update a user.
*
* @param UserRequest $request
* @param UserUpdateRequest $request
* @param User $user
*
* @return JsonResponse
*/
public function update(UserRequest $request, User $user): JsonResponse
public function update(UserUpdateRequest $request, User $user): JsonResponse
{
$data = $request->getAll();
$user = $this->repository->update($user, $data);

View File

@ -80,7 +80,7 @@ class CurrencyRequest extends Request
$rules = [
'name' => 'required|between:1,255|unique:transaction_currencies,name',
'code' => 'required|between:3,3|unique:transaction_currencies,code',
'symbol' => 'required|between:1,5|unique:transaction_currencies,symbol',
'symbol' => 'required|between:1,8|unique:transaction_currencies,symbol',
'decimal_places' => 'between:0,20|numeric|min:0|max:20',
'enabled' => [new IsBoolean()],
'default' => [new IsBoolean()],
@ -94,8 +94,8 @@ class CurrencyRequest extends Request
case 'PATCH':
$currency = $this->route()->parameter('currency_code');
$rules['name'] = 'required|between:1,255|unique:transaction_currencies,name,' . $currency->id;
$rules['code'] = 'required|between:1,255|unique:transaction_currencies,code,' . $currency->id;
$rules['symbol'] = 'required|between:1,255|unique:transaction_currencies,symbol,' . $currency->id;
$rules['code'] = 'required|between:3,3|unique:transaction_currencies,code,' . $currency->id;
$rules['symbol'] = 'required|between:1,8|unique:transaction_currencies,symbol,' . $currency->id;
break;
}

View File

@ -25,6 +25,7 @@ namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Rules\IsAssetAccountId;
use FireflyIII\Rules\LessThanPiggyTarget;
use FireflyIII\Rules\ZeroOrMore;
/**
@ -53,14 +54,11 @@ class PiggyBankRequest extends Request
*/
public function getAll(): array
{
$current = $this->string('current_amount');
$current = '' === $current ? '0' : $current;
return [
'name' => $this->string('name'),
'account_id' => $this->integer('account_id'),
'targetamount' => $this->string('target_amount'),
'current_amount' => $current,
'current_amount' => $this->string('current_amount'),
'startdate' => $this->date('start_date'),
'targetdate' => $this->date('target_date'),
'notes' => $this->string('notes'),
@ -76,8 +74,6 @@ class PiggyBankRequest extends Request
{
$rules = [
'name' => 'required|between:1,255|uniquePiggyBankForUser',
'account_id' => ['required', 'belongsToUser:accounts', new IsAssetAccountId],
'target_amount' => 'required|numeric|more:0',
'current_amount' => ['numeric', new ZeroOrMore, 'lte:target_amount'],
'start_date' => 'date|nullable',
'target_date' => 'date|nullable|after:start_date',
@ -90,8 +86,11 @@ class PiggyBankRequest extends Request
case 'PUT':
case 'PATCH':
/** @var PiggyBank $piggyBank */
$piggyBank = $this->route()->parameter('piggyBank');
$rules['name'] = 'required|between:1,255|uniquePiggyBankForUser:' . $piggyBank->id;
$piggyBank = $this->route()->parameter('piggyBank');
$rules['name'] = 'between:1,255|uniquePiggyBankForUser:' . $piggyBank->id;
$rules['account_id'] = ['belongsToUser:accounts', new IsAssetAccountId];
$rules['target_amount'] = 'numeric|more:0';
$rules['current_amount'] = ['numeric', new ZeroOrMore, new LessThanPiggyTarget];
break;
}

View File

@ -23,6 +23,9 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use Carbon\Carbon;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Validation\RecurrenceValidation;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
@ -52,7 +55,37 @@ class RecurrenceStoreRequest extends Request
*/
public function rules(): array
{
return $this->rulesRecurrence();
$today = Carbon::now()->addDay();
return [
'type' => 'required|in:withdrawal,transfer,deposit',
'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title',
'description' => 'between:1,65000',
'first_date' => sprintf('required|date|after:%s', $today->format('Y-m-d')),
'apply_rules' => [new IsBoolean],
'active' => [new IsBoolean],
'repeat_until' => sprintf('date|after:%s', $today->format('Y-m-d')),
'nr_of_repetitions' => 'numeric|between:1,31',
'tags' => 'between:1,64000',
'piggy_bank_id' => 'numeric',
'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly',
'repetitions.*.moment' => 'between:0,10',
'repetitions.*.skip' => 'required|numeric|between:0,31',
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
'transactions.*.description' => 'required|between:1,255',
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.foreign_amount' => 'numeric|more:0',
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.destination_name' => 'between:1,255|nullable',
];
}
/**

View File

@ -23,6 +23,10 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use Carbon\Carbon;
use FireflyIII\Models\Recurrence;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Validation\RecurrenceValidation;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
@ -52,7 +56,40 @@ class RecurrenceUpdateRequest extends Request
*/
public function rules(): array
{
return $this->rulesRecurrence();
/** @var Recurrence $recurrence */
$recurrence = $this->route()->parameter('recurrence');
$first = clone $recurrence->first_date;
$first->subDay();
return [
'type' => 'required|in:withdrawal,transfer,deposit',
'title' => sprintf('required|between:1,255|uniqueObjectForUser:recurrences,title,%d', $recurrence->id),
'description' => 'between:1,65000',
'first_date' => sprintf('required|date|after:%s', $first->format('Y-m-d')),
'apply_rules' => [new IsBoolean],
'active' => [new IsBoolean],
'repeat_until' => sprintf('date|after:%s', $first->format('Y-m-d')),
'nr_of_repetitions' => 'numeric|between:1,31',
'tags' => 'between:1,64000',
'piggy_bank_id' => 'numeric',
'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly',
'repetitions.*.moment' => 'between:0,10',
'repetitions.*.skip' => 'required|numeric|between:0,31',
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
'transactions.*.description' => 'required|between:1,255',
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.foreign_amount' => 'numeric|more:0',
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.destination_name' => 'between:1,255|nullable',
];
}
/**

View File

@ -121,48 +121,6 @@ class Request extends FireflyIIIRequest
return $return;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
protected function rulesRecurrence(): array
{
$today = Carbon::now()->addDay();
return [
'type' => 'required|in:withdrawal,transfer,deposit',
'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title',
'description' => 'between:1,65000',
'first_date' => sprintf('required|date|after:%s', $today->format('Y-m-d')),
'apply_rules' => [new IsBoolean],
'active' => [new IsBoolean],
'repeat_until' => sprintf('date|after:%s', $today->format('Y-m-d')),
'nr_of_repetitions' => 'numeric|between:1,31',
'tags' => 'between:1,64000',
'piggy_bank_id' => 'numeric',
'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly',
'repetitions.*.moment' => 'between:0,10',
'repetitions.*.skip' => 'required|numeric|between:0,31',
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
'transactions.*.description' => 'required|between:1,255',
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.foreign_amount' => 'numeric|more:0',
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.destination_name' => 'between:1,255|nullable',
];
}
/**
* Returns the repetition data as it is found in the submitted data.
*

View File

@ -1,7 +1,7 @@
<?php
/**
* UserRequest.php
* UserStoreRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
@ -30,11 +30,9 @@ use FireflyIII\User;
/**
* Class UserRequest
* @codeCoverageIgnore
* TODO AFTER 4.8,0: split this into two request classes.
* Class UserStoreRequest
*/
class UserRequest extends Request
class UserStoreRequest extends Request
{
/**
* Authorize logged in users.
@ -68,7 +66,7 @@ class UserRequest extends Request
public function getAll(): array
{
$blocked = false;
if (null === $this->get('blocked')) {
if (null !== $this->get('blocked')) {
$blocked = $this->boolean('blocked');
}
$data = [
@ -88,23 +86,12 @@ class UserRequest extends Request
*/
public function rules(): array
{
$rules = [
return [
'email' => 'required|email|unique:users,email,',
'blocked' => [new IsBoolean],
'blocked_code' => 'in:email_changed',
'role' => 'in:owner,demo',
];
switch ($this->method()) {
default:
break;
case 'PUT':
case 'PATCH':
$user = $this->route()->parameter('user');
$rules['email'] = 'required|email|unique:users,email,' . $user->id;
break;
}
return $rules;
}
}

View File

@ -0,0 +1,100 @@
<?php
/**
* UserUpdateRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\User;
/**
* Class UserUpdateRequest
*/
class UserUpdateRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
$result = false;
// Only allow authenticated users
if (auth()->check()) {
/** @var User $user */
$user = auth()->user();
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
if ($repository->hasRole($user, 'owner')) {
$result = true; // @codeCoverageIgnore
}
}
return $result;
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
$blocked = false;
if (null !== $this->get('blocked')) {
$blocked = $this->boolean('blocked');
}
$data = [
'email' => $this->string('email'),
'blocked' => $blocked,
'blocked_code' => $this->string('blocked_code'),
'role' => $this->string('role'),
];
return $data;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
$user = $this->route()->parameter('user');
$rules = [
'email' => sprintf('email|unique:users,email,%d', $user->id),
'blocked' => [new IsBoolean],
'blocked_code' => 'in:email_changed',
'role' => 'in:owner,demo,',
];
return $rules;
}
}

View File

@ -70,6 +70,7 @@ class CorrectDatabase extends Command
'firefly-iii:delete-empty-groups',
'firefly-iii:fix-account-types',
'firefly-iii:rename-meta-fields',
'firefly-iii:fix-ob-currencies'
];
foreach ($commands as $command) {
$this->line(sprintf('Now executing %s', $command));

View File

@ -0,0 +1,158 @@
<?php
/**
* CorrectOpeningBalanceCurrencies.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Console\Command;
use Log;
/**
* Class CorrectOpeningBalanceCurrencies
*/
class CorrectOpeningBalanceCurrencies extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Will make sure that opening balance transaction currencies match the account they\'re for.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:fix-ob-currencies';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
// get all OB journals:
$set = TransactionJournal
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->whereNull('transaction_journals.deleted_at')
->where('transaction_types.type', TransactionType::OPENING_BALANCE)->get(['transaction_journals.*']);
$this->line(sprintf('Going to verify %d opening balance transactions.', $set->count()));
$count = 0;
/** @var TransactionJournal $journal */
foreach ($set as $journal) {
$count += $this->correctJournal($journal);
}
if ($count > 0) {
$this->line(sprintf('Corrected %d opening balance transactions.', $count));
}
if (0 === $count) {
$this->info('There was nothing to fix in the opening balance transactions.');
}
return 0;
}
/**
* @param TransactionJournal $journal
*
* @return int
*/
private function correctJournal(TransactionJournal $journal): int
{
Log::debug(sprintf('Going to correct journal #%d', $journal->id));
// get the asset account for this opening balance:
$account = $this->getAccount($journal);
if (null === $account) {
$this->warn(sprintf('Transaction journal #%d has no valid account. Cant fix this line.', $journal->id));
return 0;
}
Log::debug(sprintf('Found %s #%d "%s".', $account->accountType->type, $account->id, $account->name));
$currency = $this->getCurrency($account);
Log::debug(sprintf('Found currency #%d (%s)', $currency->id, $currency->code));
// update journal and all transactions:
$this->setCurrency($journal, $currency);
return 1;
}
/**
* @param TransactionJournal $journal
*
* @return Account|null
*/
private function getAccount(TransactionJournal $journal): ?Account
{
$excluded = [];
$transactions = $journal->transactions()->with(['account', 'account.accountType'])->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$account = $transaction->account;
if (null !== $account) {
if (AccountType::INITIAL_BALANCE !== $account->accountType->type) {
return $account;
}
}
}
return null;
}
/**
* @param Account $account
*
* @return TransactionCurrency
*/
private function getCurrency(Account $account): TransactionCurrency
{
/** @var AccountRepositoryInterface $repos */
$repos = app(AccountRepositoryInterface::class);
$repos->setUser($account->user);
return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user);
}
/**
* @param TransactionJournal $journal
* @param TransactionCurrency $currency
*/
private function setCurrency(TransactionJournal $journal, TransactionCurrency $currency): void
{
$journal->transaction_currency_id = $currency->id;
$journal->save();
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
$transaction->transaction_currency_id = $currency->id;
$transaction->save();
}
}
}

View File

@ -53,6 +53,7 @@ class UpgradeDatabase extends Command
*/
public function handle(): int
{
$this->callInitialCommands();
@ -71,7 +72,7 @@ class UpgradeDatabase extends Command
'firefly-iii:back-to-journals',
'firefly-iii:rename-account-meta',
// there are 13 verify commands.
// there are 14 verify commands.
'firefly-iii:fix-piggies',
'firefly-iii:create-link-types',
'firefly-iii:create-access-tokens',
@ -85,6 +86,7 @@ class UpgradeDatabase extends Command
'firefly-iii:delete-empty-groups',
'firefly-iii:fix-account-types',
'firefly-iii:rename-meta-fields',
'firefly-iii:fix-ob-currencies',
// two report commands
'firefly-iii:report-empty-objects',
@ -103,8 +105,8 @@ class UpgradeDatabase extends Command
$result = Artisan::output();
echo $result;
}
// set new DB version.
app('fireflyconfig')->set('db_version', (int)config('firefly.db_version'));
// index will set FF3 version.
app('fireflyconfig')->set('ff3_version', (string)config('firefly.version'));
@ -113,8 +115,21 @@ class UpgradeDatabase extends Command
private function callInitialCommands(): void
{
Artisan::call('migrate', ['--seed' => true]);
$this->line('Now seeding the database...');
Artisan::call('migrate', ['--seed' => true, '--force' => true]);
$result = Artisan::output();
echo $result;
$this->line('Now decrypting the database (if necessary)...');
Artisan::call('firefly-iii:decrypt-all');
$result = Artisan::output();
echo $result;
$this->line('Now installing OAuth2 keys...');
Artisan::call('passport:install');
$result = Artisan::output();
echo $result;
$this->line('Done!');
}
}

View File

@ -60,8 +60,8 @@ class RequestedReportOnJournals
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/** @var Collection The journals to report on. */
public $journals;
/** @var Collection The transaction groups to report on. */
public $groups;
/** @var int The ID of the user. */
public $userId;
@ -69,13 +69,13 @@ class RequestedReportOnJournals
* Create a new event instance.
*
* @param int $userId
* @param Collection $journals
* @param Collection $groups
*/
public function __construct(int $userId, Collection $journals)
public function __construct(int $userId, Collection $groups)
{
Log::debug('In event RequestedReportOnJournals.');
$this->userId = $userId;
$this->journals = $journals;
$this->groups = $groups;
}
/**

View File

@ -297,9 +297,9 @@ class TransactionJournalFactory
$foreignCurrency = $this->getCurrency($foreignCurrency, $destinationAccount);
$sourceCurrency = $currency;
$destCurrency = $foreignCurrency;
$destCurrency = $currency;
$sourceForeignCurrency = $foreignCurrency;
$destForeignCurrency = $currency;
$destForeignCurrency = $foreignCurrency;
}
/** Create a basic journal. */

View File

@ -55,10 +55,10 @@ class AutomationHandler
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
$user = $repository->findNull($event->userId);
if (null !== $user && 0 !== $event->journals->count()) {
if (null !== $user && 0 !== $event->groups->count()) {
try {
Log::debug('Trying to mail...');
Mail::to($user->email)->send(new ReportNewJournalsMail($user->email, '127.0.0.1', $event->journals));
Mail::to($user->email)->send(new ReportNewJournalsMail($user->email, '127.0.0.1', $event->groups));
// @codeCoverageIgnoreStart
} catch (Exception $e) {
Log::debug('Send message failed! :(');

File diff suppressed because it is too large Load Diff

View File

@ -82,12 +82,16 @@ class BudgetReportHelper implements BudgetReportHelperInterface
'rows' => [],
];
// get multi currency expenses first:
$budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end);
$expenses = $this->repository->spentInPeriodMc(new Collection([$budget]), $accounts, $start, $end);
$budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end);
$expenses = $this->repository->spentInPeriodMc(new Collection([$budget]), $accounts, $start, $end);
$defaultCurrency = app('amount')->getDefaultCurrencyByUser($budget->user);
Log::debug(sprintf('Default currency for getBudgetReport is %s', $defaultCurrency->code));
if (0 === count($expenses)) {
// list the budget limits, basic amounts.
/** @var BudgetLimit $limit */
foreach ($budgetLimits as $limit) {
$currency = $limit->transactionCurrency ?? $defaultCurrency;
Log::debug(sprintf('Default currency for limit #%d is %s', $limit->id, $currency->code));
$row = [
'limit_id' => $limit->id,
'start_date' => $limit->start_date,
@ -96,11 +100,11 @@ class BudgetReportHelper implements BudgetReportHelperInterface
'spent' => '0',
'left' => $limit->amount,
'overspent' => null,
'currency_id' => $limit->transactionCurrency->id,
'currency_code' => $limit->transactionCurrency->code,
'currency_name' => $limit->transactionCurrency->name,
'currency_symbol' => $limit->transactionCurrency->symbol,
'currency_decimal_places' => $limit->transactionCurrency->decimal_places,
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
];
$entry['rows'][] = $row;
@ -169,8 +173,8 @@ class BudgetReportHelper implements BudgetReportHelperInterface
foreach ($array['budgets'] as $budget) {
/** @var array $row */
foreach ($budget['rows'] as $row) {
$currencyId = $row['currency_id'];
$array['sums'][$currencyId] = $array['sums'][$currencyId] ?? [
$currencyId = $row['currency_id'];
$array['sums'][$currencyId] = $array['sums'][$currencyId] ?? [
'currency_id' => $row['currency_id'],
'currency_code' => $row['currency_code'],
'currency_name' => $row['currency_name'],
@ -181,12 +185,13 @@ class BudgetReportHelper implements BudgetReportHelperInterface
'left' => '0',
'overspent' => '0',
];
$array['sums'][$currencyId]['budgeted'] = bcadd($array['sums'][$currencyId]['budgeted'], $row['budgeted'] ?? '0');
$array['sums'][$currencyId]['spent'] = bcadd($array['sums'][$currencyId]['spent'], $row['spent'] ?? '0');
$array['sums'][$currencyId]['left'] = bcadd($array['sums'][$currencyId]['left'], $row['left'] ?? '0');
$array['sums'][$currencyId]['budgeted'] = bcadd($array['sums'][$currencyId]['budgeted'], $row['budgeted'] ?? '0');
$array['sums'][$currencyId]['spent'] = bcadd($array['sums'][$currencyId]['spent'], $row['spent'] ?? '0');
$array['sums'][$currencyId]['left'] = bcadd($array['sums'][$currencyId]['left'], $row['left'] ?? '0');
$array['sums'][$currencyId]['overspent'] = bcadd($array['sums'][$currencyId]['overspent'], $row['overspent'] ?? '0');
}
}
return $array;
}

View File

@ -54,6 +54,7 @@ class BillController extends Controller
/**
* BillController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
@ -124,7 +125,7 @@ class BillController extends Controller
* Destroy a bill.
*
* @param Request $request
* @param Bill $bill
* @param Bill $bill
*
* @return RedirectResponse|\Illuminate\Routing\Redirector
*/
@ -143,7 +144,7 @@ class BillController extends Controller
* Edit a bill.
*
* @param Request $request
* @param Bill $bill
* @param Bill $bill
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
@ -191,11 +192,12 @@ class BillController extends Controller
*/
public function index()
{
$start = session('start');
$end = session('end');
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
$paginator = $this->billRepository->getPaginator($pageSize);
$parameters = new ParameterBag();
$start = session('start');
$end = session('end');
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
$paginator = $this->billRepository->getPaginator($pageSize);
$defaultCurrency = app('amount')->getDefaultCurrency();
$parameters = new ParameterBag();
$parameters->set('start', $start);
$parameters->set('end', $end);
@ -205,9 +207,9 @@ class BillController extends Controller
/** @var Collection $bills */
$bills = $paginator->getCollection()->map(
function (Bill $bill) use ($transformer) {
static function (Bill $bill) use ($transformer, $defaultCurrency) {
$return = $transformer->transform($bill);
$return['currency'] = $bill->transactionCurrency;
$return['currency'] = $bill->transactionCurrency ?? $defaultCurrency;
return $return;
}
@ -232,35 +234,45 @@ class BillController extends Controller
* Rescan bills for transactions.
*
* @param Request $request
* @param Bill $bill
* @param Bill $bill
*
* @return RedirectResponse|\Illuminate\Routing\Redirector
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function rescan(Request $request, Bill $bill)
{
$total = 0;
if (false === $bill->active) {
$request->session()->flash('warning', (string)trans('firefly.cannot_scan_inactive_bill'));
return redirect(route('bills.show', [$bill->id]));
}
$set = new Collection;
if (true === $bill->active) {
$set = $this->billRepository->getRulesForBill($bill);
$total = 0;
foreach ($set as $rule) {
// simply fire off all rules?
/** @var TransactionMatcher $matcher */
$matcher = app(TransactionMatcher::class);
$matcher->setSearchLimit(100000); // large upper limit
$matcher->setTriggeredLimit(100000); // large upper limit
$matcher->setRule($rule);
$matchingTransactions = $matcher->findTransactionsByRule();
$total += count($matchingTransactions);
$this->billRepository->linkCollectionToBill($bill, $matchingTransactions);
}
$request->session()->flash('success', (string)trans('firefly.rescanned_bill', ['total' => $total]));
app('preferences')->mark();
}
if (0 === $set->count()) {
$request->session()->flash('error', (string)trans('firefly.no_rules_for_bill'));
return redirect(route('bills.show', [$bill->id]));
}
foreach ($set as $rule) {
// simply fire off all rules?
/** @var TransactionMatcher $matcher */
$matcher = app(TransactionMatcher::class);
$matcher->setSearchLimit(100000); // large upper limit
$matcher->setTriggeredLimit(100000); // large upper limit
$matcher->setRule($rule);
$matchingTransactions = $matcher->findTransactionsByRule();
$total += count($matchingTransactions);
$this->billRepository->linkCollectionToBill($bill, $matchingTransactions);
}
$request->session()->flash('success', (string)trans('firefly.rescanned_bill', ['total' => $total]));
app('preferences')->mark();
return redirect(route('bills.show', [$bill->id]));
}
@ -269,7 +281,7 @@ class BillController extends Controller
* Show a bill.
*
* @param Request $request
* @param Bill $bill
* @param Bill $bill
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
@ -325,6 +337,7 @@ class BillController extends Controller
}
);
}
// @codeCoverageIgnoreEnd
@ -368,7 +381,7 @@ class BillController extends Controller
* Update a bill.
*
* @param BillFormRequest $request
* @param Bill $bill
* @param Bill $bill
*
* @return RedirectResponse
*/

View File

@ -55,7 +55,7 @@ class Controller extends BaseController
public function __construct()
{
// for transaction lists:
app('view')->share('hideBudgets', false);
app('view')->share('hideBudgets', false); // TODO add a comma when PHP7.3 hits so everybody will notice it.
app('view')->share('hideCategories', false);
app('view')->share('hideBills', false);
app('view')->share('hideTags', false);

View File

@ -212,7 +212,11 @@ class CurrencyController extends Controller
}
if ($this->repository->currencyInUse($currency)) {
$request->session()->flash('error', (string)trans('firefly.cannot_disable_currency', ['name' => e($currency->name)]));
$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 disable currency %s but is in use.', $currency->code));
return redirect(route('currencies.index'));
@ -233,6 +237,10 @@ class CurrencyController extends Controller
app('preferences')->mark();
}
if ('EUR' === $currency->code) {
session()->flash('warning', (string)trans('firefly.disable_EUR_side_effects'));
}
session()->flash('success', (string)trans('firefly.currency_is_now_disabled', ['name' => $currency->name]));
return redirect(route('currencies.index'));

View File

@ -105,7 +105,6 @@ class HomeController extends Controller
{
$types = config('firefly.accountTypesByIdentifier.asset');
$count = $repository->count($types);
Log::channel('audit')->info('User visits homepage.');
if (0 === $count) {
@ -127,7 +126,6 @@ class HomeController extends Controller
/** @var BillRepositoryInterface $billRepository */
$billRepository = app(BillRepositoryInterface::class);
$billCount = $billRepository->getBills()->count();
foreach ($accounts as $account) {
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);

View File

@ -176,13 +176,7 @@ class AutoCompleteController extends Controller
$filtered = $result->unique('description');
$limited = $filtered->slice(0, 15);
$array = $limited->toArray();
foreach ($array as $index => $item) {
// give another key for consistency
$array[$index]['name'] = $item['description'];
}
return response()->json($array);
return response()->json(array_values($array));
}
/**

View File

@ -89,7 +89,7 @@ class SearchController extends Controller
$searcher->parseQuery($fullQuery);
$searcher->setLimit((int)config('firefly.search_result_limit'));
$groups = $searcher->searchTransactions();
$searchTime = $searcher->searchTime(); // in seconds
$searchTime = round($searcher->searchTime(), 3); // in seconds
try {
$html = view('search.search', compact('groups','searchTime'))->render();

View File

@ -84,7 +84,7 @@ class InstallController extends Controller
'firefly-iii:back-to-journals' => [],
'firefly-iii:rename-account-meta' => [],
// there are 13 verify commands.
// there are 14 verify commands.
'firefly-iii:fix-piggies' => [],
'firefly-iii:create-link-types' => [],
'firefly-iii:create-access-tokens' => [],
@ -98,6 +98,7 @@ class InstallController extends Controller
'firefly-iii:delete-empty-groups' => [],
'firefly-iii:fix-account-types' => [],
'firefly-iii:rename-meta-fields' => [],
'firefly-iii:fix-ob-currencies' => [],
];
}
@ -111,6 +112,9 @@ class InstallController extends Controller
// index will set FF3 version.
app('fireflyconfig')->set('ff3_version', (string)config('firefly.version'));
// set new DB version.
app('fireflyconfig')->set('db_version', (int)config('firefly.db_version'));
return view('install.index');
}

View File

@ -65,6 +65,8 @@ class CreateController extends Controller
*/
public function create(?string $objectType)
{
app('preferences')->mark();
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$cash = $repository->getCashAccount();

View File

@ -95,6 +95,10 @@ class Range
$dateTimeFormat = (string)trans('config.date_time');
$defaultCurrency = app('amount')->getDefaultCurrency();
// also format for moment JS:
$madMomentJS = (string)trans('config.month_and_day_moment_js');
app('view')->share('madMomentJS', $madMomentJS);
app('view')->share('monthAndDayFormat', $monthAndDayFormat);
app('view')->share('dateTimeFormat', $dateTimeFormat);
app('view')->share('defaultCurrency', $defaultCurrency);

View File

@ -22,6 +22,8 @@ declare(strict_types=1);
namespace FireflyIII\Mail;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Transformers\TransactionGroupTransformer;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
@ -43,21 +45,24 @@ class ReportNewJournalsMail extends Mailable
/** @var string IP address of user (if known) */
public $ipAddress;
/** @var Collection A collection of journals */
public $journals;
/** @var Collection A collection of groups */
public $groups;
/** @var array All groups, transformed to array. */
public $transformed;
/**
* ConfirmEmailChangeMail constructor.
*
* @param string $email
* @param string $ipAddress
* @param Collection $journals
* @param Collection $groups
*/
public function __construct(string $email, string $ipAddress, Collection $journals)
public function __construct(string $email, string $ipAddress, Collection $groups)
{
$this->email = $email;
$this->ipAddress = $ipAddress;
$this->journals = $journals;
$this->groups = $groups;
}
/**
@ -67,13 +72,25 @@ class ReportNewJournalsMail extends Mailable
*/
public function build(): self
{
$subject = 1 === $this->journals->count()
$subject = 1 === $this->groups->count()
? 'Firefly III has created a new transaction'
: sprintf(
'Firefly III has created new %d transactions', $this->journals->count()
'Firefly III has created new %d transactions', $this->groups->count()
);
$this->transform();
return $this->view('emails.report-new-journals-html')->text('emails.report-new-journals-text')
->subject($subject);
}
private function transform(): void
{
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);
/** @var TransactionGroup $group */
foreach ($this->groups as $group) {
$this->transformed[] = $transformer->transformObject($group);
}
}
}

View File

@ -51,8 +51,8 @@ class AccountTasker implements AccountTaskerInterface
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
@ -106,7 +106,7 @@ class AccountTasker implements AccountTaskerInterface
$entry['end_balance'] = $endSet[$account->id] ?? '0';
// first journal exists, and is on start, then this is the actual opening balance:
if (null !== $first && $first->date->isSameDay($start)) {
if (null !== $first && $first->date->isSameDay($start) && TransactionType::OPENING_BALANCE === $first->transactionType->type) {
Log::debug(sprintf('Date of first journal for %s is %s', $account->name, $first->date->format('Y-m-d')));
$entry['start_balance'] = $first->transactions()->where('account_id', $account->id)->first()->amount;
Log::debug(sprintf('Account %s was opened on %s, so opening balance is %f', $account->name, $start->format('Y-m-d'), $entry['start_balance']));
@ -124,8 +124,8 @@ class AccountTasker implements AccountTaskerInterface
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
@ -159,8 +159,8 @@ class AccountTasker implements AccountTaskerInterface
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
@ -216,11 +216,11 @@ class AccountTasker implements AccountTaskerInterface
/** @var array $journal */
foreach ($array as $journal) {
$sourceId = (int)$journal['destination_account_id'];
$currencyId = (int)$journal['currency_id'];
$key = sprintf('%s-%s', $sourceId, $currencyId);
$currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepos->findNull($currencyId);
$report['accounts'][$key] = $report['accounts'][$key] ?? [
$sourceId = (int)$journal['destination_account_id'];
$currencyId = (int)$journal['currency_id'];
$key = sprintf('%s-%s', $sourceId, $currencyId);
$currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepos->findNull($currencyId);
$report['accounts'][$key] = $report['accounts'][$key] ?? [
'id' => $sourceId,
'name' => $journal['destination_account_name'],
'sum' => '0',
@ -255,6 +255,7 @@ class AccountTasker implements AccountTaskerInterface
];
$report['sums'][$currencyId]['sum'] = bcadd($report['sums'][$currencyId]['sum'], $report['accounts'][$key]['sum']);
}
return $report;
}
@ -276,9 +277,9 @@ class AccountTasker implements AccountTaskerInterface
/** @var array $journal */
foreach ($array as $journal) {
$sourceId = (int)$journal['source_account_id'];
$currencyId = (int)$journal['currency_id'];
$key = sprintf('%s-%s', $sourceId, $currencyId);
$sourceId = (int)$journal['source_account_id'];
$currencyId = (int)$journal['currency_id'];
$key = sprintf('%s-%s', $sourceId, $currencyId);
if (!isset($report['accounts'][$key])) {
$currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepos->findNull($currencyId);
$report['accounts'][$key] = [

View File

@ -399,7 +399,9 @@ class BillRepository implements BillRepositoryInterface
*/
public function getPaginator(int $size): LengthAwarePaginator
{
return $this->user->bills()->paginate($size);
return $this->user->bills()
->orderBy('active', 'DESC')
->orderBy('name', 'ASC')->paginate($size);
}
/**
@ -413,13 +415,13 @@ class BillRepository implements BillRepositoryInterface
*/
public function getPaidDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection
{
$dates = $bill->transactionJournals()->before($end)->after($start)->get(
[
'transaction_journals.id', 'transaction_journals.date',
]
)->pluck('date', 'id');
return $dates;
return $bill->transactionJournals()
->before($end)->after($start)->get(
[
'transaction_journals.id', 'transaction_journals.date',
'transaction_journals.transaction_group_id',
]
);
}
/**
@ -544,14 +546,14 @@ class BillRepository implements BillRepositoryInterface
/**
* Link a set of journals to a bill.
*
* @param Bill $bill
* @param Bill $bill
* @param array $transactions
*/
public function linkCollectionToBill(Bill $bill, array $transactions): void
{
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$journal = $transaction->transactionJournal;
$journal = $bill->user->transactionJournals()->find((int)$transaction['transaction_journal_id']);
$journal->bill_id = $bill->id;
$journal->save();
Log::debug(sprintf('Linked journal #%d to bill #%d', $journal->id, $bill->id));

View File

@ -96,12 +96,46 @@ class BudgetRepository implements BudgetRepositoryInterface
return $avg;
}
/**
* @return bool
* // it's 5.
*/
public function cleanupBudgets(): bool
{
// delete limits with amount 0:
try {
BudgetLimit::where('amount', 0)->delete();
} catch (Exception $e) {
Log::debug(sprintf('Could not delete budget limit: %s', $e->getMessage()));
}
Budget::where('order', 0)->update(['order' => 100]);
// do the clean up by hand because Sqlite can be tricky with this.
$budgetLimits = BudgetLimit::orderBy('created_at', 'DESC')->get(['id', 'budget_id', 'start_date', 'end_date']);
$count = [];
/** @var BudgetLimit $budgetLimit */
foreach ($budgetLimits as $budgetLimit) {
$key = $budgetLimit->budget_id . '-' . $budgetLimit->start_date->format('Y-m-d') . $budgetLimit->end_date->format('Y-m-d');
if (isset($count[$key])) {
// delete it!
try {
BudgetLimit::find($budgetLimit->id)->delete();
} catch (Exception $e) {
Log::debug(sprintf('Could not delete budget limit: %s', $e->getMessage()));
}
}
$count[$key] = true;
}
return true;
}
/**
* This method collects various info on budgets, used on the budget page and on the index.
*
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return array
*
@ -143,95 +177,6 @@ class BudgetRepository implements BudgetRepositoryInterface
return $return;
}
/**
* @param Collection $budgets
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user);
$collector->setRange($start, $end)->setBudgets($budgets)->withBudgetInformation();
if ($accounts->count() > 0) {
$collector->setAccounts($accounts);
}
return $collector->getSum();
}
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*
*
*/
public function getBudgetLimits(Budget $budget, Carbon $start = null, Carbon $end = null): Collection
{
if (null === $end && null === $start) {
return $budget->budgetlimits()->with(['transactionCurrency'])->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*']);
}
if (null === $end xor null === $start) {
$query = $budget->budgetlimits()->with(['transactionCurrency'])->orderBy('budget_limits.start_date', 'DESC');
// one of the two is null
if (null !== $end) {
// end date must be before $end.
$query->where('end_date', '<=', $end->format('Y-m-d 00:00:00'));
}
if (null !== $start) {
// start date must be after $start.
$query->where('start_date', '>=', $start->format('Y-m-d 00:00:00'));
}
$set = $query->get(['budget_limits.*']);
return $set;
}
// when both dates are set:
$set = $budget->budgetlimits()
->where(
function (Builder $q5) use ($start, $end) {
$q5->where(
function (Builder $q1) use ($start, $end) {
// budget limit ends within period
$q1->where(
function (Builder $q2) use ($start, $end) {
$q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d 00:00:00'));
$q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d 00:00:00'));
}
)
// budget limit start within period
->orWhere(
function (Builder $q3) use ($start, $end) {
$q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00'));
$q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 00:00:00'));
}
);
}
)
->orWhere(
function (Builder $q4) use ($start, $end) {
// or start is before start AND end is after end.
$q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d 00:00:00'));
$q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d 00:00:00'));
}
);
}
)->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*']);
return $set;
}
/**
* @param Budget $budget
*
@ -273,7 +218,7 @@ class BudgetRepository implements BudgetRepositoryInterface
}
/**
* @param int|null $budgetId
* @param int|null $budgetId
* @param string|null $budgetName
*
* @return Budget|null
@ -295,22 +240,6 @@ class BudgetRepository implements BudgetRepositoryInterface
return $result;
}
/**
* Find a budget or return NULL
*
* @param int $budgetId |null
*
* @return Budget|null
*/
public function findNull(int $budgetId = null): ?Budget
{
if (null === $budgetId) {
return null;
}
return $this->user->budgets()->find($budgetId);
}
/**
* Find budget by name.
*
@ -328,6 +257,22 @@ class BudgetRepository implements BudgetRepositoryInterface
return $this->user->budgets()->where('name', 'LIKE', $query)->first();
}
/**
* Find a budget or return NULL
*
* @param int $budgetId |null
*
* @return Budget|null
*/
public function findNull(int $budgetId = null): ?Budget
{
if (null === $budgetId) {
return null;
}
return $this->user->budgets()->find($budgetId);
}
/**
* This method returns the oldest journal or transaction date known to this budget.
* Will cache result.
@ -448,8 +393,24 @@ class BudgetRepository implements BudgetRepositoryInterface
/**
* @param TransactionCurrency $currency
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getAllBudgetLimitsByCurrency(TransactionCurrency $currency, Carbon $start = null, Carbon $end = null): Collection
{
return $this->getAllBudgetLimits($start, $end)->filter(
function (BudgetLimit $budgetLimit) use ($currency) {
return $budgetLimit->transaction_currency_id === $currency->id;
}
);
}
/**
* @param TransactionCurrency $currency
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
@ -468,7 +429,6 @@ class BudgetRepository implements BudgetRepositoryInterface
}
/**
* @param Carbon $start
* @param Carbon $end
@ -489,14 +449,114 @@ class BudgetRepository implements BudgetRepositoryInterface
return $return;
}
/**
* Returns all available budget objects.
*
* @param TransactionCurrency $currency
*
* @return Collection
*/
public function getAvailableBudgetsByCurrency(TransactionCurrency $currency): Collection
{
return $this->user->availableBudgets()->where('transaction_currency_id', $currency->id)->get();
}
/**
* Returns all available budget objects.
*
* @param Carbon|null $start
* @param Carbon|null $end
*
* @return Collection
*
*/
public function getAvailableBudgetsByDate(?Carbon $start, ?Carbon $end): Collection
{
$query = $this->user->availableBudgets();
if (null !== $start) {
$query->where('start_date', '>=', $start->format('Y-m-d H:i:s'));
}
if (null !== $end) {
$query->where('end_date', '<=', $end->format('Y-m-d H:i:s'));
}
return $query->get();
}
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*
*
*/
public function getBudgetLimits(Budget $budget, Carbon $start = null, Carbon $end = null): Collection
{
if (null === $end && null === $start) {
return $budget->budgetlimits()->with(['transactionCurrency'])->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*']);
}
if (null === $end xor null === $start) {
$query = $budget->budgetlimits()->with(['transactionCurrency'])->orderBy('budget_limits.start_date', 'DESC');
// one of the two is null
if (null !== $end) {
// end date must be before $end.
$query->where('end_date', '<=', $end->format('Y-m-d 00:00:00'));
}
if (null !== $start) {
// start date must be after $start.
$query->where('start_date', '>=', $start->format('Y-m-d 00:00:00'));
}
$set = $query->get(['budget_limits.*']);
return $set;
}
// when both dates are set:
$set = $budget->budgetlimits()
->where(
function (Builder $q5) use ($start, $end) {
$q5->where(
function (Builder $q1) use ($start, $end) {
// budget limit ends within period
$q1->where(
function (Builder $q2) use ($start, $end) {
$q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d 00:00:00'));
$q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d 00:00:00'));
}
)
// budget limit start within period
->orWhere(
function (Builder $q3) use ($start, $end) {
$q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00'));
$q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 00:00:00'));
}
);
}
)
->orWhere(
function (Builder $q4) use ($start, $end) {
// or start is before start AND end is after end.
$q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d 00:00:00'));
$q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d 00:00:00'));
}
);
}
)->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*']);
return $set;
}
/**
* This method is being used to generate the budget overview in the year/multi-year report. Its used
* in both the year/multi-year budget overview AND in the accompanying chart.
*
* @param Collection $budgets
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
@ -504,7 +564,7 @@ class BudgetRepository implements BudgetRepositoryInterface
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
$data = [];
$data = [];
// prep data array:
/** @var Budget $budget */
foreach ($budgets as $budget) {
@ -529,6 +589,7 @@ class BudgetRepository implements BudgetRepositoryInterface
$date = $journal['date']->format($carbonFormat);
$data[$budgetId]['entries'][$date] = bcadd($data[$budgetId]['entries'][$date] ?? '0', $journal['amount']);
}
return $data;
}
@ -544,8 +605,6 @@ class BudgetRepository implements BudgetRepositoryInterface
return $set;
}
/**
* Get all budgets with these ID's.
*
@ -570,12 +629,10 @@ class BudgetRepository implements BudgetRepositoryInterface
return $set;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
@ -627,9 +684,9 @@ class BudgetRepository implements BudgetRepositoryInterface
/**
* @param TransactionCurrency $currency
* @param Carbon $start
* @param Carbon $end
* @param string $amount
* @param Carbon $start
* @param Carbon $end
* @param string $amount
*
* @return AvailableBudget
*/
@ -654,7 +711,7 @@ class BudgetRepository implements BudgetRepositoryInterface
/**
* @param Budget $budget
* @param int $order
* @param int $order
*/
public function setBudgetOrder(Budget $budget, int $order): void
{
@ -673,8 +730,32 @@ class BudgetRepository implements BudgetRepositoryInterface
/**
* @param Collection $budgets
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user);
$collector->setRange($start, $end)->setBudgets($budgets)->withBudgetInformation();
if ($accounts->count() > 0) {
$collector->setAccounts($accounts);
}
return $collector->getSum();
}
/**
* @param Collection $budgets
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* TODO refactor me.
*
* @return array
@ -733,8 +814,8 @@ class BudgetRepository implements BudgetRepositoryInterface
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
@ -755,8 +836,8 @@ class BudgetRepository implements BudgetRepositoryInterface
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
@ -822,8 +903,6 @@ class BudgetRepository implements BudgetRepositoryInterface
return $newBudget;
}
/**
* @param array $data
*
@ -868,43 +947,9 @@ class BudgetRepository implements BudgetRepositoryInterface
return $limit;
}
/**
* @return bool
* // it's 5.
*/
public function cleanupBudgets(): bool
{
// delete limits with amount 0:
try {
BudgetLimit::where('amount', 0)->delete();
} catch (Exception $e) {
Log::debug(sprintf('Could not delete budget limit: %s', $e->getMessage()));
}
Budget::where('order', 0)->update(['order' => 100]);
// do the clean up by hand because Sqlite can be tricky with this.
$budgetLimits = BudgetLimit::orderBy('created_at', 'DESC')->get(['id', 'budget_id', 'start_date', 'end_date']);
$count = [];
/** @var BudgetLimit $budgetLimit */
foreach ($budgetLimits as $budgetLimit) {
$key = $budgetLimit->budget_id . '-' . $budgetLimit->start_date->format('Y-m-d') . $budgetLimit->end_date->format('Y-m-d');
if (isset($count[$key])) {
// delete it!
try {
BudgetLimit::find($budgetLimit->id)->delete();
} catch (Exception $e) {
Log::debug(sprintf('Could not delete budget limit: %s', $e->getMessage()));
}
}
$count[$key] = true;
}
return true;
}
/**
* @param Budget $budget
* @param array $data
* @param array $data
*
* @return Budget
*/
@ -922,8 +967,10 @@ class BudgetRepository implements BudgetRepositoryInterface
}
/**
* TODO only used in the API.
*
* @param AvailableBudget $availableBudget
* @param array $data
* @param array $data
*
* @return AvailableBudget
* @throws FireflyException
@ -931,9 +978,9 @@ class BudgetRepository implements BudgetRepositoryInterface
public function updateAvailableBudget(AvailableBudget $availableBudget, array $data): AvailableBudget
{
$existing = $this->user->availableBudgets()
->where('transaction_currency_id', $data['transaction_currency_id'])
->where('start_date', $data['start_date']->format('Y-m-d 00:00:00'))
->where('end_date', $data['end_date']->format('Y-m-d 00:00:00'))
->where('transaction_currency_id', $data['currency_id'])
->where('start_date', $data['start']->format('Y-m-d 00:00:00'))
->where('end_date', $data['end']->format('Y-m-d 00:00:00'))
->where('id', '!=', $availableBudget->id)
->first();
@ -941,8 +988,8 @@ class BudgetRepository implements BudgetRepositoryInterface
throw new FireflyException(sprintf('An entry already exists for these parameters: available budget object with ID #%d', $existing->id));
}
$availableBudget->transaction_currency_id = $data['currency_id'];
$availableBudget->start_date = $data['start_date'];
$availableBudget->end_date = $data['end_date'];
$availableBudget->start_date = $data['start'];
$availableBudget->end_date = $data['end'];
$availableBudget->amount = $data['amount'];
$availableBudget->save();
@ -952,7 +999,7 @@ class BudgetRepository implements BudgetRepositoryInterface
/**
* @param BudgetLimit $budgetLimit
* @param array $data
* @param array $data
*
* @return BudgetLimit
* @throws Exception
@ -1057,52 +1104,24 @@ class BudgetRepository implements BudgetRepositoryInterface
}
/**
* Returns all available budget objects.
*
* @param Carbon|null $start
* @param Carbon|null $end
* @return Collection
*
* @param string $oldName
* @param string $newName
*/
public function getAvailableBudgetsByDate(?Carbon $start, ?Carbon $end): Collection
private function updateRuleActions(string $oldName, string $newName): void
{
$query = $this->user->availableBudgets();
if (null !== $start) {
$query->where('start_date', '>=', $start->format('Y-m-d H:i:s'));
$types = ['set_budget',];
$actions = RuleAction::leftJoin('rules', 'rules.id', '=', 'rule_actions.rule_id')
->where('rules.user_id', $this->user->id)
->whereIn('rule_actions.action_type', $types)
->where('rule_actions.action_value', $oldName)
->get(['rule_actions.*']);
Log::debug(sprintf('Found %d actions to update.', $actions->count()));
/** @var RuleAction $action */
foreach ($actions as $action) {
$action->action_value = $newName;
$action->save();
Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value));
}
if (null !== $end) {
$query->where('end_date', '<=', $end->format('Y-m-d H:i:s'));
}
return $query->get();
}
/**
* Returns all available budget objects.
*
* @param TransactionCurrency $currency
* @return Collection
*/
public function getAvailableBudgetsByCurrency(TransactionCurrency $currency): Collection
{
return $this->user->availableBudgets()->where('transaction_currency_id', $currency->id)->get();
}
/**
* @param TransactionCurrency $currency
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getAllBudgetLimitsByCurrency(TransactionCurrency $currency, Carbon $start = null, Carbon $end = null): Collection
{
return $this->getAllBudgetLimits($start, $end)->filter(
function (BudgetLimit $budgetLimit) use ($currency) {
return $budgetLimit->transaction_currency_id === $currency->id;
}
);
}
/**
@ -1125,25 +1144,4 @@ class BudgetRepository implements BudgetRepositoryInterface
Log::debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value));
}
}
/**
* @param string $oldName
* @param string $newName
*/
private function updateRuleActions(string $oldName, string $newName): void
{
$types = ['set_budget',];
$actions = RuleAction::leftJoin('rules', 'rules.id', '=', 'rule_actions.rule_id')
->where('rules.user_id', $this->user->id)
->whereIn('rule_actions.action_type', $types)
->where('rule_actions.action_value', $oldName)
->get(['rule_actions.*']);
Log::debug(sprintf('Found %d actions to update.', $actions->count()));
/** @var RuleAction $action */
foreach ($actions as $action) {
$action->action_value = $newName;
$action->save();
Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value));
}
}
}

View File

@ -65,7 +65,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
*/
public function countJournals(TransactionCurrency $currency): int
{
return $currency->transactions()->count() + $currency->transactionJournals()->count();
return $currency->transactions()->whereNull('deleted_at')->count() + $currency->transactionJournals()->whereNull('deleted_at')->count();
}
@ -75,20 +75,32 @@ class CurrencyRepository implements CurrencyRepositoryInterface
* @return bool
*/
public function currencyInUse(TransactionCurrency $currency): bool
{
$result = $this->currencyInUseAt($currency);
return null !== $result;
}
/**
* @param TransactionCurrency $currency
*
* @return string|null
*/
public function currencyInUseAt(TransactionCurrency $currency): ?string
{
Log::debug(sprintf('Now in currencyInUse() for #%d ("%s")', $currency->id, $currency->code));
$countJournals = $this->countJournals($currency);
if ($countJournals > 0) {
Log::info(sprintf('Count journals is %d, return true.', $countJournals));
return true;
return 'journals';
}
// is the only currency left
if (1 === $this->getAll()->count()) {
Log::info('Is the last currency in the system, return true. ');
return true;
return 'last_left';
}
// is being used in accounts:
@ -96,7 +108,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
if ($meta > 0) {
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
return true;
return 'account_meta';
}
// is being used in bills:
@ -104,7 +116,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
if ($bills > 0) {
Log::info(sprintf('Used in %d bills as currency, return true. ', $bills));
return true;
return 'bills';
}
// is being used in recurring transactions
@ -114,15 +126,18 @@ class CurrencyRepository implements CurrencyRepositoryInterface
if ($recurringAmount > 0 || $recurringForeign > 0) {
Log::info(sprintf('Used in %d recurring transactions as (foreign) currency id, return true. ', $recurringAmount + $recurringForeign));
return true;
return 'recurring';
}
// is being used in accounts (as integer)
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((int)$currency->id))->count();
$meta = AccountMeta
::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id')
->whereNull('accounts.deleted_at')
->where('account_meta.name', 'currency_id')->where('account_meta.data', json_encode((int)$currency->id))->count();
if ($meta > 0) {
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
return true;
return 'account_meta';
}
// is being used in available budgets
@ -130,7 +145,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
if ($availableBudgets > 0) {
Log::info(sprintf('Used in %d available budgets as currency, return true. ', $availableBudgets));
return true;
return 'available_budgets';
}
// is being used in budget limits
@ -138,7 +153,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
if ($budgetLimit > 0) {
Log::info(sprintf('Used in %d budget limits as currency, return true. ', $budgetLimit));
return true;
return 'budget_limits';
}
// is the default currency for the user or the system
@ -146,20 +161,20 @@ class CurrencyRepository implements CurrencyRepositoryInterface
if ($currency->code === $defaultCode) {
Log::info('Is the default currency of the user, return true.');
return true;
return 'current_default';
}
// is the default currency for the system
// $defaultSystemCode = config('firefly.default_currency', 'EUR');
// $result = $currency->code === $defaultSystemCode;
// if (true === $result) {
// Log::info('Is the default currency of the SYSTEM, return true.');
//
// return true;
// }
// // is the default currency for the system
// $defaultSystemCode = config('firefly.default_currency', 'EUR');
// $result = $currency->code === $defaultSystemCode;
// if (true === $result) {
// Log::info('Is the default currency of the SYSTEM, return true.');
//
// return 'system_fallback';
// }
Log::debug('Currency is not used, return false.');
return false;
return null;
}
/**

View File

@ -54,6 +54,15 @@ interface CurrencyRepositoryInterface
*/
public function currencyInUse(TransactionCurrency $currency): bool;
/**
* Currency is in use where exactly.
*
* @param TransactionCurrency $currency
*
* @return string|null
*/
public function currencyInUseAt(TransactionCurrency $currency): ?string;
/**
* @param TransactionCurrency $currency
*

View File

@ -534,20 +534,27 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
*/
public function update(PiggyBank $piggyBank, array $data): PiggyBank
{
$piggyBank->name = $data['name'];
$piggyBank->account_id = (int)$data['account_id'];
$piggyBank->targetamount = $data['targetamount'];
$piggyBank->targetdate = $data['targetdate'];
if (isset($data['name']) && '' !== $data['name']) {
$piggyBank->name = $data['name'];
}
if (isset($data['account_id']) && 0 !== $data['account_id']) {
$piggyBank->account_id = (int)$data['account_id'];
}
if (isset($data['targetamount']) && '' !== $data['targetamount']) {
$piggyBank->targetamount = $data['targetamount'];
}
if (isset($data['targetdate']) && '' !== $data['targetdate']) {
$piggyBank->targetdate = $data['targetdate'];
}
$piggyBank->startdate = $data['startdate'] ?? $piggyBank->startdate;
$piggyBank->save();
$this->updateNote($piggyBank, $data['notes']);
$this->updateNote($piggyBank, $data['notes'] ?? '');
// if the piggy bank is now smaller than the current relevant rep,
// remove money from the rep.
$repetition = $this->getRepetition($piggyBank);
if ($repetition->currentamount > $piggyBank->targetamount) {
if (null !== $repetition && $repetition->currentamount > $piggyBank->targetamount) {
$diff = bcsub($piggyBank->targetamount, $repetition->currentamount);
$this->createEvent($piggyBank, $diff);
@ -558,12 +565,36 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return $piggyBank;
}
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return PiggyBank
*/
public function setCurrentAmount(PiggyBank $piggyBank, string $amount): PiggyBank
{
$repetition = $this->getRepetition($piggyBank);
if (null === $repetition) {
return $piggyBank;
}
$max = $piggyBank->targetamount;
if (1 === bccomp($amount, $max)) {
$amount = $max;
}
$repetition->currentamount = $amount;
$repetition->save();
// create event
$this->createEvent($piggyBank, $amount);
return $piggyBank;
}
/**
* @param PiggyBank $piggyBank
* @param string $note
*
* @return bool
* @throws \Exception
*/
private function updateNote(PiggyBank $piggyBank, string $note): bool
{

View File

@ -35,6 +35,14 @@ use Illuminate\Support\Collection;
*/
interface PiggyBankRepositoryInterface
{
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return PiggyBank
*/
public function setCurrentAmount(PiggyBank $piggyBank, string $amount): PiggyBank;
/**
* @param PiggyBank $piggyBank
* @param string $amount

View File

@ -151,7 +151,7 @@ class RecurringRepository implements RecurringRepositoryInterface
/**
* Returns the journals created for this recurrence, possibly limited by time.
*
* @param Recurrence $recurrence
* @param Recurrence $recurrence
* @param Carbon|null $start
* @param Carbon|null $end
*
@ -209,30 +209,12 @@ class RecurringRepository implements RecurringRepositoryInterface
return '';
}
/**
* @param Recurrence $recurrence
* @return PiggyBank|null
*/
public function getPiggyBank(Recurrence $recurrence): ?PiggyBank
{
$meta = $recurrence->recurrenceMeta;
/** @var RecurrenceMeta $metaEntry */
foreach ($meta as $metaEntry) {
if ('piggy_bank_id' === $metaEntry->name) {
$piggyId = (int)$metaEntry->value;
return $this->user->piggyBanks()->where('id', $piggyId)->first(['piggy_banks.*']);
}
}
return null;
}
/**
* Generate events in the date range.
*
* @param RecurrenceRepetition $repetition
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return array
*
@ -269,6 +251,26 @@ class RecurringRepository implements RecurringRepositoryInterface
return $occurrences;
}
/**
* @param Recurrence $recurrence
*
* @return PiggyBank|null
*/
public function getPiggyBank(Recurrence $recurrence): ?PiggyBank
{
$meta = $recurrence->recurrenceMeta;
/** @var RecurrenceMeta $metaEntry */
foreach ($meta as $metaEntry) {
if ('piggy_bank_id' === $metaEntry->name) {
$piggyId = (int)$metaEntry->value;
return $this->user->piggyBanks()->where('piggy_banks.id', $piggyId)->first(['piggy_banks.*']);
}
}
return null;
}
/**
* Get the tags from the recurring transaction.
*
@ -291,8 +293,8 @@ class RecurringRepository implements RecurringRepositoryInterface
/**
* @param Recurrence $recurrence
* @param int $page
* @param int $pageSize
* @param int $page
* @param int $pageSize
*
* @return LengthAwarePaginator
*/
@ -336,14 +338,12 @@ class RecurringRepository implements RecurringRepositoryInterface
->get()->pluck('transaction_journal_id')->toArray();
$search = [];
foreach ($journalMeta as $journalId) {
$search[] = (int)$journalId;
}
if (0 === count($search)) {
return [];
return new Collection;
}
/** @var GroupCollectorInterface $collector */
@ -361,11 +361,11 @@ class RecurringRepository implements RecurringRepositoryInterface
* Calculate the next X iterations starting on the date given in $date.
*
* @param RecurrenceRepetition $repetition
* @param Carbon $date
* @param int $count
* @param Carbon $date
* @param int $count
*
* @return array
*
*
*/
public function getXOccurrences(RecurrenceRepetition $repetition, Carbon $date, int $count): array
{
@ -399,7 +399,7 @@ class RecurringRepository implements RecurringRepositoryInterface
* @param RecurrenceRepetition $repetition
*
* @return string
*
*
*/
public function repetitionDescription(RecurrenceRepetition $repetition): string
{
@ -472,7 +472,7 @@ class RecurringRepository implements RecurringRepositoryInterface
* Update a recurring transaction.
*
* @param Recurrence $recurrence
* @param array $data
* @param array $data
*
* @return Recurrence
* @throws FireflyException

View File

@ -86,10 +86,10 @@ class UserRepository implements UserRepositoryInterface
* @param User $user
* @param string $newEmail
*
* @see updateEmail
*
* @return bool
* @throws \Exception
* @see updateEmail
*
*/
public function changeEmail(User $user, string $newEmail): bool
{
@ -245,14 +245,14 @@ class UserRepository implements UserRepositoryInterface
$return = [];
// two factor:
$return['has_2fa'] = $user->mfa_secret !== null;
$return['is_admin'] = $this->hasRole($user, 'owner');
$return['blocked'] = 1 === (int)$user->blocked;
$return['blocked_code'] = $user->blocked_code;
$return['accounts'] = $user->accounts()->count();
$return['journals'] = $user->transactionJournals()->count();
$return['transactions'] = $user->transactions()->count();
$return['attachments'] = $user->attachments()->count();
$return['has_2fa'] = $user->mfa_secret !== null;
$return['is_admin'] = $this->hasRole($user, 'owner');
$return['blocked'] = 1 === (int)$user->blocked;
$return['blocked_code'] = $user->blocked_code;
$return['accounts'] = $user->accounts()->count();
$return['journals'] = $user->transactionJournals()->count();
$return['transactions'] = $user->transactions()->count();
$return['attachments'] = $user->attachments()->count();
$return['attachments_size'] = $user->attachments()->sum('size');
$return['bills'] = $user->bills()->count();
$return['categories'] = $user->categories()->count();
@ -291,6 +291,28 @@ class UserRepository implements UserRepositoryInterface
return false;
}
/**
* Remove any role the user has.
*
* @param User $user
*/
public function removeRole(User $user): void
{
$user->roles()->sync([]);
}
/**
* Set MFA code.
*
* @param User $user
* @param string|null $code
*/
public function setMFACode(User $user, ?string $code): void
{
$user->mfa_secret = $code;
$user->save();
}
/**
* @param array $data
*
@ -335,9 +357,17 @@ class UserRepository implements UserRepositoryInterface
*/
public function update(User $user, array $data): User
{
$this->updateEmail($user, $data['email']);
$user->blocked = $data['blocked'] ?? false;
$user->blocked_code = $data['blocked_code'] ?? null;
$this->updateEmail($user, $data['email'] ?? '');
if (isset($data['blocked']) && is_bool($data['blocked'])) {
$user->blocked = $data['blocked'];
}
if (isset($data['blocked_code']) && '' !== $data['blocked_code'] && is_string($data['blocked_code'])) {
$user->blocked_code = $data['blocked_code'];
}
if (isset($data['role']) && '' === $data['role']) {
$this->removeRole($user);
}
$user->save();
return $user;
@ -350,12 +380,15 @@ class UserRepository implements UserRepositoryInterface
* @param User $user
* @param string $newEmail
*
* @return bool
* @see changeEmail
*
* @return bool
*/
public function updateEmail(User $user, string $newEmail): bool
{
if ('' === $newEmail) {
return true;
}
$oldEmail = $user->email;
// save old email as pref
@ -367,16 +400,4 @@ class UserRepository implements UserRepositoryInterface
return true;
}
/**
* Set MFA code.
*
* @param User $user
* @param string|null $code
*/
public function setMFACode(User $user, ?string $code): void
{
$user->mfa_secret = $code;
$user->save();
}
}

View File

@ -39,14 +39,6 @@ interface UserRepositoryInterface
*/
public function all(): Collection;
/**
* Set MFA code.
*
* @param User $user
* @param string|null $code
*/
public function setMFACode(User $user, ?string $code): void;
/**
* Gives a user a role.
*
@ -64,9 +56,9 @@ interface UserRepositoryInterface
* @param User $user
* @param string $newEmail
*
* @return bool
* @see updateEmail
*
* @return bool
*/
public function changeEmail(User $user, string $newEmail): bool;
@ -162,6 +154,21 @@ interface UserRepositoryInterface
*/
public function hasRole(User $user, string $role): bool;
/**
* Remove any role the user has.
*
* @param User $user
*/
public function removeRole(User $user): void;
/**
* Set MFA code.
*
* @param User $user
* @param string|null $code
*/
public function setMFACode(User $user, ?string $code): void;
/**
* @param array $data
*
@ -191,9 +198,9 @@ interface UserRepositoryInterface
* @param User $user
* @param string $newEmail
*
* @return bool
* @see changeEmail
*
* @return bool
*/
public function updateEmail(User $user, string $newEmail): bool;
}

View File

@ -0,0 +1,34 @@
<?php
namespace FireflyIII\Rules;
use Illuminate\Contracts\Validation\Rule;
/**
* Class LessThanPiggyTarget
*/
class LessThanPiggyTarget implements Rule
{
/**
* Get the validation error message.
*
* @return string
*/
public function message(): string
{
return (string)trans('validation.current_target_amount');
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
*
* @return bool
*/
public function passes($attribute, $value): bool
{
return true;
}
}

View File

@ -30,6 +30,7 @@ use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Database\Eloquent\Builder;
use Log;
@ -58,6 +59,9 @@ class AccountDestroyService
*/
public function destroy(Account $account, ?Account $moveTo): void
{
// find and delete opening balance journal + opposing account
$this->destroyOpeningBalance($account);
if (null !== $moveTo) {
$this->moveTransactions($account, $moveTo);
$this->updateRecurrences($account, $moveTo);
@ -72,6 +76,10 @@ class AccountDestroyService
// delete piggy banks:
PiggyBank::where('account_id', $account->id)->delete();
// delete account meta:
$account->accountMeta()->delete();
// delete account.
try {
$account->delete();
@ -102,6 +110,43 @@ class AccountDestroyService
}
}
/**
* @param Account $account
*/
private function destroyOpeningBalance(Account $account): void
{
Log::debug(sprintf('Searching for opening balance for account #%d "%s"', $account->id, $account->name));
$set = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transaction_types.type', TransactionType::OPENING_BALANCE)
->get(['transactions.transaction_journal_id']);
if ($set->count() > 0) {
$journalId = (int)$set->first()->transaction_journal_id;
Log::debug(sprintf('Found opening balance journal with ID #%d', $journalId));
// get transactions with this journal (should be just one):
$transactions = Transaction
::where('transaction_journal_id', $journalId)
->where('account_id', '!=', $account->id)
->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
Log::debug(sprintf('Found transaction with ID #%d', $transaction->id));
$ibAccount = $transaction->account;
Log::debug(sprintf('Connected to account #%d "%s"', $ibAccount->id, $ibAccount->name));
$ibAccount->accountMeta()->delete();
$transaction->delete();
$ibAccount->delete();
}
$journal = TransactionJournal::find($journalId);
/** @var JournalDestroyService $service */
$service = app(JournalDestroyService::class);
$service->destroy($journal);
}
}
/**
* @param Account $account
*/

View File

@ -243,6 +243,7 @@ trait AccountServiceTrait
'destination_id' => $destId,
'destination_name' => $destName,
'user' => $account->user_id,
'currency_id' => $data['currency_id'],
'order' => 0,
'amount' => $amount,
'foreign_amount' => null,

View File

@ -25,6 +25,7 @@ namespace FireflyIII\Transformers;
use Carbon\Carbon;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
@ -109,11 +110,11 @@ class BillTransformer extends AbstractTransformer
if (0 === $dates->count()) {
return $default; // @codeCoverageIgnore
}
$latest = $dates->first();
/** @var Carbon $date */
foreach ($dates as $date) {
if ($date->gte($latest)) {
$latest = $date;
$latest = $dates->first()->date;
/** @var TransactionJournal $date */
foreach ($dates as $journal) {
if ($journal->date->gte($latest)) {
$latest = $journal->date;
}
}
@ -160,11 +161,6 @@ class BillTransformer extends AbstractTransformer
$set = $this->repository->getPaidDatesInRange($bill, $this->parameters->get('start'), $this->parameters->get('end'));
Log::debug(sprintf('Count %d entries in getPaidDatesInRange()', $set->count()));
$simple = $set->map(
function (Carbon $date) {
return $date->format('Y-m-d');
}
);
// calculate next expected match:
$lastPaidDate = $this->lastPaidDate($set, $this->parameters->get('start'));
@ -173,13 +169,20 @@ class BillTransformer extends AbstractTransformer
$nextMatch = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip);
}
$end = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip);
$journalCount = $this->repository->getPaidDatesInRange($bill, $nextMatch, $end)->count();
if ($journalCount > 0) {
if ($set->count() > 0) {
$nextMatch = clone $end;
}
$result = [];
foreach ($set as $entry) {
$result[] = [
'transaction_group_id' => (int)$entry->transaction_group_id,
'transaction_journal_id' => (int)$entry->id,
'date' => $entry->date->format('Y-m-d'),
];
}
return [
'paid_dates' => $simple->toArray(),
'paid_dates' => $result,
'next_expected_match' => $nextMatch->format('Y-m-d'),
];
}

View File

@ -82,7 +82,7 @@ trait RecurrenceValidation
* @var array $repetition
*/
foreach ($repetitions as $index => $repetition) {
switch ($repetition['type']) {
switch ($repetition['type'] ?? 'empty') {
default:
$validator->errors()->add(sprintf('repetitions.%d.type', $index), (string)trans('validation.valid_recurrence_rep_type'));

View File

@ -2,6 +2,35 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [4.8.0.3 (API 0.10.1)] - 2019-08-23
Fixes many other issues in the previous release.
### Added
- Autocomplete for transaction description.
### Fixed
- #2438 Some balance issues when working with multiple currencies (a known issue)
- #2425 Transaction edit/create form is weird with the enter button
- #2424 auto complete tab doesn't work.
- #2441 Inconsistent character limit for currencies.
- #2443 500 error when submitting budgets
- #2446 Can't update current amount for piggy bank
- #2440 Errors when interacting with recurring transactions
- #2439 SQL error in API post new user
- Transaction report (after import, over email) is mostly empty
- Mass edit checkboxes doesn't work in a tag overview
- #2437 CPU issues when viewing accounts, probably run-away queries.
- #2432 Can't disable all currencies except one / can't disable EUR and switch to something else.
- Option to edit the budget is gone from edit transaction form.
- #2453 Search view things
- #2449 Can't add invoice date.
- #2448 Bad link in transaction overview
- #2447 Bad link in bill overview
### API
- Improvements to various API end-points. Docs are updated.
## [4.8.0.2 (API 0.10.0)] - 2019-08-17
Fixes many other issues in the previous release.

View File

@ -161,6 +161,7 @@
"@php artisan firefly-iii:delete-empty-groups",
"@php artisan firefly-iii:fix-account-types",
"@php artisan firefly-iii:rename-meta-fields",
"@php artisan firefly-iii:fix-ob-currencies",
"@php artisan firefly-iii:report-empty-objects",
"@php artisan firefly-iii:report-sum",

113
composer.lock generated
View File

@ -210,9 +210,9 @@
"authors": [
{
"name": "bunq",
"role": "Owner",
"email": "sdk@bunq.com",
"homepage": "https://www.bunq.com",
"role": "Owner"
"homepage": "https://www.bunq.com"
}
],
"description": "bunq PHP SDK",
@ -1125,13 +1125,13 @@
"authors": [
{
"name": "Neuman Vong",
"role": "Developer",
"email": "neuman+pear@twilio.com"
"email": "neuman+pear@twilio.com",
"role": "Developer"
},
{
"name": "Anant Narayanan",
"role": "Developer",
"email": "anant@php.net"
"email": "anant@php.net",
"role": "Developer"
}
],
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
@ -1327,16 +1327,16 @@
},
{
"name": "laravel/framework",
"version": "v5.8.32",
"version": "v5.8.33",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "ee16d719516dfd77ed6c9538000bca49ded284e2"
"reference": "58b81842cbdcfbbd8302790ac0f98119ea1c56e5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/ee16d719516dfd77ed6c9538000bca49ded284e2",
"reference": "ee16d719516dfd77ed6c9538000bca49ded284e2",
"url": "https://api.github.com/repos/laravel/framework/zipball/58b81842cbdcfbbd8302790ac0f98119ea1c56e5",
"reference": "58b81842cbdcfbbd8302790ac0f98119ea1c56e5",
"shasum": ""
},
"require": {
@ -1470,20 +1470,20 @@
"framework",
"laravel"
],
"time": "2019-08-13T14:11:52+00:00"
"time": "2019-08-20T15:45:17+00:00"
},
{
"name": "laravel/passport",
"version": "v7.3.5",
"version": "v7.4.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/passport.git",
"reference": "57937b08dc8e444b4756782a5ba172b5ba54d4f5"
"reference": "4460bd1fb5d913d75e547caf02a5a19c6d77794d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/passport/zipball/57937b08dc8e444b4756782a5ba172b5ba54d4f5",
"reference": "57937b08dc8e444b4756782a5ba172b5ba54d4f5",
"url": "https://api.github.com/repos/laravel/passport/zipball/4460bd1fb5d913d75e547caf02a5a19c6d77794d",
"reference": "4460bd1fb5d913d75e547caf02a5a19c6d77794d",
"shasum": ""
},
"require": {
@ -1494,6 +1494,7 @@
"illuminate/console": "~5.6.0|~5.7.0|~5.8.0|^6.0",
"illuminate/container": "~5.6.0|~5.7.0|~5.8.0|^6.0",
"illuminate/contracts": "~5.6.0|~5.7.0|~5.8.0|^6.0",
"illuminate/cookie": "~5.6.0|~5.7.0|~5.8.0|^6.0",
"illuminate/database": "~5.6.0|~5.7.0|~5.8.0|^6.0",
"illuminate/encryption": "~5.6.0|~5.7.0|~5.8.0|^6.0",
"illuminate/http": "~5.6.0|~5.7.0|~5.8.0|^6.0",
@ -1540,7 +1541,7 @@
"oauth",
"passport"
],
"time": "2019-08-06T18:10:19+00:00"
"time": "2019-08-20T18:10:43+00:00"
},
{
"name": "laravelcollective/html",
@ -1654,8 +1655,8 @@
"authors": [
{
"name": "Luís Otávio Cobucci Oblonczyk",
"email": "lcobucci@gmail.com",
"role": "Developer"
"role": "Developer",
"email": "lcobucci@gmail.com"
}
],
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
@ -1721,9 +1722,9 @@
"authors": [
{
"name": "Colin O'Dell",
"role": "Lead Developer",
"email": "colinodell@gmail.com",
"homepage": "https://www.colinodell.com",
"role": "Lead Developer"
"homepage": "https://www.colinodell.com"
}
],
"description": "PHP Markdown parser based on the CommonMark spec",
@ -2075,9 +2076,9 @@
"authors": [
{
"name": "Phil Sturgeon",
"role": "Developer",
"email": "me@philsturgeon.uk",
"homepage": "http://philsturgeon.uk/",
"role": "Developer"
"homepage": "http://philsturgeon.uk/"
}
],
"description": "Handle the output of complex data structures ready for API output.",
@ -2323,16 +2324,16 @@
},
{
"name": "nesbot/carbon",
"version": "2.23.0",
"version": "2.23.1",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "97a08830a22ce0b69549a4966773c0eae900468d"
"reference": "767617a047e5b8b8b3b0b6023a2650847ed7df02"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/97a08830a22ce0b69549a4966773c0eae900468d",
"reference": "97a08830a22ce0b69549a4966773c0eae900468d",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/767617a047e5b8b8b3b0b6023a2650847ed7df02",
"reference": "767617a047e5b8b8b3b0b6023a2650847ed7df02",
"shasum": ""
},
"require": {
@ -2386,7 +2387,7 @@
"datetime",
"time"
],
"time": "2019-08-12T17:19:41+00:00"
"time": "2019-08-17T13:57:34+00:00"
},
{
"name": "opis/closure",
@ -2483,15 +2484,15 @@
"authors": [
{
"name": "Paragon Initiative Enterprises",
"role": "Maintainer",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"role": "Original Developer",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com"
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
@ -2741,8 +2742,8 @@
"authors": [
{
"name": "Antonio Carlos Ribeiro",
"role": "Creator & Designer",
"email": "acr@antoniocarlosribeiro.com"
"email": "acr@antoniocarlosribeiro.com",
"role": "Creator & Designer"
}
],
"description": "A One Time Password Authentication package, compatible with Google Authenticator.",
@ -4997,7 +4998,7 @@
},
{
"name": "tightenco/collect",
"version": "v5.8.32",
"version": "v5.8.33",
"source": {
"type": "git",
"url": "https://github.com/tightenco/collect.git",
@ -6183,18 +6184,18 @@
"authors": [
{
"name": "Arne Blankerts",
"role": "Developer",
"email": "arne@blankerts.de"
"email": "arne@blankerts.de",
"role": "Developer"
},
{
"name": "Sebastian Heuer",
"role": "Developer",
"email": "sebastian@phpeople.de"
"email": "sebastian@phpeople.de",
"role": "Developer"
},
{
"name": "Sebastian Bergmann",
"role": "Developer",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "Developer"
}
],
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
@ -6230,18 +6231,18 @@
"authors": [
{
"name": "Arne Blankerts",
"role": "Developer",
"email": "arne@blankerts.de"
"email": "arne@blankerts.de",
"role": "Developer"
},
{
"name": "Sebastian Heuer",
"role": "Developer",
"email": "sebastian@phpeople.de"
"email": "sebastian@phpeople.de",
"role": "Developer"
},
{
"name": "Sebastian Bergmann",
"role": "Developer",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "Developer"
}
],
"description": "Library for handling version information and constraints",
@ -6563,8 +6564,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
@ -6605,8 +6606,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Simple template engine.",
@ -7005,8 +7006,8 @@
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"role": "maintainer"
"role": "maintainer",
"email": "ocramius@gmail.com"
}
],
"description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it",
@ -7619,8 +7620,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
@ -7803,8 +7804,8 @@
"authors": [
{
"name": "Arne Blankerts",
"email": "arne@blankerts.de",
"role": "Developer"
"role": "Developer",
"email": "arne@blankerts.de"
}
],
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",

View File

@ -125,8 +125,8 @@ return [
'is_demo_site' => false,
],
'encryption' => null === env('USE_ENCRYPTION') || env('USE_ENCRYPTION') === true,
'version' => '4.8.0.2',
'api_version' => '0.10.0',
'version' => '4.8.0.3',
'api_version' => '0.10.1',
'db_version' => 11,
'maxUploadSize' => 15242880,
'send_error_message' => env('SEND_ERROR_MESSAGE', true),

2
public/v1/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,9 @@
var count = 0;
$(document).ready(function () {
updateListButtons();
});
function updateListButtons() {
// top button to select all / deselect all:
$('input[name="select-all"]').change(function () {
if (this.checked) {
@ -46,9 +49,7 @@ $(document).ready(function () {
countChecked();
updateActionButtons();
});
});
}
/**
*

View File

@ -42,17 +42,6 @@ function presentSearchResults(data) {
$('.search_box').find('.overlay').remove();
$('.search_results').html(data.html).show();
$('.select_all_single').unbind('change').change(function () {
countChecked();
});
// make sure select button works:
$('input[name="select_all"]').change(function () {
if (this.checked) {
checkAll();
countChecked();
} else {
uncheckAll();
countChecked();
}
});
updateListButtons();
}

View File

@ -21,7 +21,7 @@
/** global: edit_selected_txt, edit_bulk_selected_txt, delete_selected_txt, token */
/**
*
* @deprecated
*/
$(document).ready(function () {
"use strict";

File diff suppressed because one or more lines are too long

View File

@ -122,7 +122,7 @@ For all other contributions, see below.
## The goal
Firefly III should give you **insight** into and **control** over your finances. Money should be useful, not scary. You should be able to *see* where it is going, to *feel* your expenses and to... wow, I'm going overboard with this aren't I?
But you get the idea: this is your money. These are your expenses. Stop them from controlling you. I built this tool because I started to dislike money. Having it, not having, paying bills with it, etc. But no more. I want to feel "safe", whatever my balance is. And I hoop this tool can help. I know it helps me.
But you get the idea: this is your money. These are your expenses. Stop them from controlling you. I built this tool because I started to dislike money. Having it, not having, paying bills with it, etc. But no more. I want to feel "safe", whatever my balance is. And I hope this tool can help. I know it helps me.
## Contact
You can contact me at [thegrumpydictator@gmail.com](mailto:thegrumpydictator@gmail.com), you may open an issue or contact me through the various social media pages there are: [reddit](https://www.reddit.com/r/FireflyIII/), [Twitter](https://twitter.com/Firefly_III) and [Facebook](https://www.facebook.com/FireflyIII/).

View File

@ -22,11 +22,13 @@
<template>
<div class="form-group"
v-bind:class="{ 'has-error': hasError()}"
v-if="typeof this.transactionType === 'undefined' || this.transactionType === 'Withdrawal' || this.transactionType === '' || null === this.transactionType">
v-if="typeof this.transactionType === 'undefined' || this.transactionType === 'withdrawal' || this.transactionType === 'Withdrawal' || this.transactionType === '' || null === this.transactionType">
<div class="col-sm-12">
<select name="budget[]" ref="budget" @input="handleInput" class="form-control"
<select name="budget[]" ref="budget" v-model="value" @input="handleInput" class="form-control"
v-if="this.budgets.length > 0">
<option v-for="budget in this.budgets" :label="budget.name" :value="budget.id">{{budget.name}}</option>
<option v-for="cBudget in this.budgets" :label="cBudget.name" :value="cBudget.id"
>{{cBudget.name}}</option>
</select>
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
@ -41,6 +43,8 @@
props: ['transactionType', 'value', 'error'],
mounted() {
this.loadBudgets();
console.log('budget value');
console.log(this.value);
},
data() {
return {

View File

@ -19,8 +19,7 @@
-->
<template>
<form method="POST" action="#" accept-charset="UTF-8" class="form-horizontal" id="store"
enctype="multipart/form-data">
<form accept-charset="UTF-8" class="form-horizontal" enctype="multipart/form-data">
<input name="_token" type="hidden" value="xxx">
<div class="row" v-if="error_message !== ''">
<div class="col-lg-12">
@ -164,7 +163,7 @@
</div>
</div>
<div class="box-footer" v-if="transactions.length-1 === index">
<button class="split_add_btn btn btn-primary" @click="addTransactionToArray">Add another split</button>
<button class="split_add_btn btn btn-primary" type="button" @click="addTransactionToArray">Add another split</button>
</div>
</div>
</div>
@ -369,9 +368,6 @@
submit(e) {
const uri = './api/v1/transactions?_token=' + document.head.querySelector('meta[name="csrf-token"]').content;
const data = this.convertData();
if (this.resetFormAfter) {
this.resetTransactions();
}
let button = $(e.currentTarget);
button.prop("disabled", true);

View File

@ -29,7 +29,7 @@
<input type="date" class="form-control" :name="name"
:title="title" autocomplete="off"
ref="date"
:value="value.substr(0,10)" @input="handleInput"
:value="value ? value.substr(0,10): ''" @input="handleInput"
:placeholder="title">
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>

View File

@ -35,7 +35,10 @@
<component
:error="error.payment_date"
v-model="value.payment_date" v-if="this.fields.payment_date" name="payment_date[]" title="Payment date" v-bind:is="dateComponent"></component>
<component v-model="value.invoice_date" v-if="this.fields.invoice_date" name="invoice_date[]" title="Invoice date" v-bind:is="dateComponent"></component>
<component
:error="error.invoice_date"
v-model="value.invoice_date" v-if="this.fields.invoice_date" name="invoice_date[]" title="Invoice date" v-bind:is="dateComponent"></component>
<component
:error="error.internal_reference"

View File

@ -147,13 +147,6 @@
v-model="transaction.category"
:error="transaction.errors.category"
></category>
<!--
<piggy-bank
:transactionType="transactionType"
v-model="transaction.piggy_bank"
:error="transaction.errors.piggy_bank"
></piggy-bank>
-->
<tags
:tags="transaction.tags"
v-model="transaction.tags"
@ -167,7 +160,7 @@
</div>
</div>
<div class="box-footer" v-if="transactions.length-1 === index">
<button class="btn btn-primary" @click="addTransaction">Add another split</button>
<button class="btn btn-primary" type="button" @click="addTransaction">Add another split</button>
</div>
</div>
</div>

View File

@ -19,7 +19,7 @@
-->
<template>
<div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<div class="form-group" v-bind:class="{ 'has-error': hasError()}" v-if="this.enabledCurrencies.length > 0">
<div class="col-sm-4">
<select class="form-control" ref="currency_select" name="foreign_currency[]"
v-if="this.enabledCurrencies.length > 0" @input="handleInput">
@ -136,8 +136,10 @@
];
for (const key in res.data) {
if (res.data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
this.currencies.push(res.data[key]);
this.enabledCurrencies.push(res.data[key]);
if (res.data[key].enabled) {
this.currencies.push(res.data[key]);
this.enabledCurrencies.push(res.data[key]);
}
}
}
});

View File

@ -26,11 +26,22 @@
class="form-control"
name="description[]"
title="Description"
v-on:keypress="handleEnter"
v-on:submit.prevent
ref="descr"
autocomplete="off"
placeholder="Description"
:value="value" @input="handleInput"
>
<typeahead
:open-on-empty=true
:open-on-focus=true
v-on:input="selectedItem"
:async-src="descriptionAutoCompleteURI"
v-model="name"
:target="target"
item-key="description"
></typeahead>
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
@ -42,13 +53,43 @@
export default {
props: ['error', 'value', 'index'],
name: "TransactionDescription",
mounted() {
this.target = this.$refs.descr;
this.descriptionAutoCompleteURI = document.getElementsByTagName('base')[0].href + "json/transaction-journals/all?search=";
},
data() {
return {
descriptionAutoCompleteURI: null,
name: null,
description: null,
target: null,
}
},
methods: {
hasError: function () {
return this.error.length > 0;
},
handleInput(e) {
this.$emit('input', this.$refs.descr.value);
}
},
handleEnter: function (e) {
// todo feels sloppy
if (e.keyCode === 13) {
e.preventDefault();
}
},
selectedItem: function (e) {
console.log('selectedItem for descr()');
// if (typeof this.name === 'undefined') {
// return;
// }
// if(typeof this.name === 'string') {
// console.log('Is a string.');
// }
// // emit the fact that the user selected a type of account
// // (influencing the destination)
// this.$emit('select:account', this.name);
},
}
}
</script>

View File

@ -23,29 +23,30 @@
declare(strict_types=1);
return [
'html_language' => 'cs',
'locale' => 'cs, Čeština, cs_CZ, cs_CZ.utf8, cs_CZ.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
'month_and_date_day' => '%A %B %e, %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%e %B %Y, @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Týden %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'D. MMMM YYYY',
'date_time_js' => 'D. MMMM YYYY, @ HH:mm:ss',
'specific_day_js' => 'D. MMMM YYYY',
'week_in_year_js' => '[Week] t, RRRR',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Pondělí',
'dow_2' => 'Úterý',
'dow_3' => 'Středa',
'dow_4' => 'Čtvrtek',
'dow_5' => 'Pátek',
'dow_6' => 'Sobota',
'dow_7' => 'Neděle',
'html_language' => 'cs',
'locale' => 'cs, Čeština, cs_CZ, cs_CZ.utf8, cs_CZ.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
'month_and_day_moment_js' => 'MMM D, YYYY',
'month_and_date_day' => '%A %B %e, %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%e %B %Y, @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Týden %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'D. MMMM YYYY',
'date_time_js' => 'D. MMMM YYYY, @ HH:mm:ss',
'specific_day_js' => 'D. MMMM YYYY',
'week_in_year_js' => '[Week] t, RRRR',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Pondělí',
'dow_2' => 'Úterý',
'dow_3' => 'Středa',
'dow_4' => 'Čtvrtek',
'dow_5' => 'Pátek',
'dow_6' => 'Sobota',
'dow_7' => 'Neděle',
];

File diff suppressed because it is too large Load Diff

View File

@ -120,18 +120,19 @@ return [
'string' => 'Je třeba, aby :attribute byl řetězec.',
'url' => 'Formát :attribute není platný.',
'timezone' => 'Je třeba, aby :attribute byla platná zóna.',
'2fa_code' => 'Kolonka :attribute není platná.',
'dimensions' => ':attribute nemá platné rozměry obrázku.',
'distinct' => 'Kolonka :attribute má duplicitní hodnotu.',
'file' => 'Je třeba, aby :attribute byl soubor.',
'in_array' => 'The :attribute field does not exist in :other.',
'present' => 'Je třeba, aby kolonka :attribute byla přítomna.',
'amount_zero' => 'Celková částka nemůže být nula.',
'unique_piggy_bank_for_user' => 'Je třeba, aby se názvy pokladniček neopakovaly.',
'secure_password' => 'Toto není bezpečné heslo. Zkuste jiné. Více se dozvíte na http://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Neplatný typ opakování pro opakované transakce.',
'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.',
'invalid_account_info' => 'Neplatná informace o účtu.',
'2fa_code' => 'Kolonka :attribute není platná.',
'dimensions' => ':attribute nemá platné rozměry obrázku.',
'distinct' => 'Kolonka :attribute má duplicitní hodnotu.',
'file' => 'Je třeba, aby :attribute byl soubor.',
'in_array' => 'The :attribute field does not exist in :other.',
'present' => 'Je třeba, aby kolonka :attribute byla přítomna.',
'amount_zero' => 'Celková částka nemůže být nula.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'Je třeba, aby se názvy pokladniček neopakovaly.',
'secure_password' => 'Toto není bezpečné heslo. Zkuste jiné. Více se dozvíte na http://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Neplatný typ opakování pro opakované transakce.',
'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.',
'invalid_account_info' => 'Neplatná informace o účtu.',
'attributes' => [
'email' => 'e-mailová adresa',
'description' => 'popis',

View File

@ -23,29 +23,30 @@
declare(strict_types=1);
return [
'html_language' => 'de',
'locale' => 'de, Deutsch, de_DE, de_DE.utf8, de_DE.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e. %B %Y',
'month_and_date_day' => '%A, %B %e. %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%e %B %Y, @ %T',
'specific_day' => '%e. %B %Y',
'week_in_year' => 'KW %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'Do MMMM YYYY',
'date_time_js' => 'Do MMMM YYYY um HH:mm:ss',
'specific_day_js' => 'D. MMMM YYYY',
'week_in_year_js' => '[Week]. KW, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q. Quartal YYYY',
'dow_1' => 'Montag',
'dow_2' => 'Dienstag',
'dow_3' => 'Mittwoch',
'dow_4' => 'Donnerstag',
'dow_5' => 'Freitag',
'dow_6' => 'Samstag',
'dow_7' => 'Sonntag',
'html_language' => 'de',
'locale' => 'de, Deutsch, de_DE, de_DE.utf8, de_DE.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e. %B %Y',
'month_and_day_moment_js' => 'MMM D, YYYY',
'month_and_date_day' => '%A, %B %e. %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%e %B %Y, @ %T',
'specific_day' => '%e. %B %Y',
'week_in_year' => 'KW %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'Do MMMM YYYY',
'date_time_js' => 'Do MMMM YYYY um HH:mm:ss',
'specific_day_js' => 'D. MMMM YYYY',
'week_in_year_js' => '[Week]. KW, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q. Quartal YYYY',
'dow_1' => 'Montag',
'dow_2' => 'Dienstag',
'dow_3' => 'Mittwoch',
'dow_4' => 'Donnerstag',
'dow_5' => 'Freitag',
'dow_6' => 'Samstag',
'dow_7' => 'Sonntag',
];

File diff suppressed because it is too large Load Diff

View File

@ -255,7 +255,7 @@ return [
'withdrawal_destination_id' => 'Zielkonto',
'deposit_source_id' => 'Quellkonto',
'expected_on' => 'Expected on',
'paid' => 'Paid',
'expected_on' => 'Erwartet am',
'paid' => 'Bezahlt',
];

View File

@ -120,18 +120,19 @@ return [
'string' => ':attribute muss eine Zeichenfolge sein.',
'url' => ':attribute Format ist ungültig.',
'timezone' => ':attribute muss in einem gültigen Bereich liegen.',
'2fa_code' => ':attribute Feld ist ungültig.',
'dimensions' => 'Das :attribute hat eine ungültige Auflösung.',
'distinct' => 'Der Wert von :attribute existiert bereits.',
'file' => 'Das :attribute muss eine Datei sein.',
'in_array' => ':attribute existiert nicht in :other.',
'present' => 'Das :attribute Feld muss vorhanden sein.',
'amount_zero' => 'Der Gesamtbetrag darf nicht Null sein.',
'unique_piggy_bank_for_user' => 'Der Name des Sparschweins muss eindeutig sein.',
'secure_password' => 'Dies ist ein unsicheres Passwort. Bitte versuchen Sie es erneut. Weitere Informationen finden Sie unter https://github.com/firefly-iii/help/wiki/Secure-password',
'valid_recurrence_rep_type' => 'Ungültige Wiederholungsart für Daueraufträge.',
'valid_recurrence_rep_moment' => 'Ungültiges Wiederholungsmoment für diese Art der Wiederholung.',
'invalid_account_info' => 'Ungültige Kontodaten.',
'2fa_code' => ':attribute Feld ist ungültig.',
'dimensions' => 'Das :attribute hat eine ungültige Auflösung.',
'distinct' => 'Der Wert von :attribute existiert bereits.',
'file' => 'Das :attribute muss eine Datei sein.',
'in_array' => ':attribute existiert nicht in :other.',
'present' => 'Das :attribute Feld muss vorhanden sein.',
'amount_zero' => 'Der Gesamtbetrag darf nicht Null sein.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'Der Name des Sparschweins muss eindeutig sein.',
'secure_password' => 'Dies ist ein unsicheres Passwort. Bitte versuchen Sie es erneut. Weitere Informationen finden Sie unter https://github.com/firefly-iii/help/wiki/Secure-password',
'valid_recurrence_rep_type' => 'Ungültige Wiederholungsart für Daueraufträge.',
'valid_recurrence_rep_moment' => 'Ungültiges Wiederholungsmoment für diese Art der Wiederholung.',
'invalid_account_info' => 'Ungültige Kontodaten.',
'attributes' => [
'email' => 'E-Mail Adresse',
'description' => 'Beschreibung',

View File

@ -23,29 +23,30 @@
declare(strict_types=1);
return [
'html_language' => 'en',
'locale' => 'en, English, en_US.utf8, en_US.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%B %e, %Y',
'month_and_date_day' => '%A %B %e, %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%B %e, %Y, @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Week %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'MMMM Do, YYYY',
'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Monday',
'dow_2' => 'Tuesday',
'dow_3' => 'Wednesday',
'dow_4' => 'Thursday',
'dow_5' => 'Friday',
'dow_6' => 'Saturday',
'dow_7' => 'Sunday',
'html_language' => 'en',
'locale' => 'en, English, en_US.utf8, en_US.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%B %e, %Y',
'month_and_day_moment_js' => 'MMM D, YYYY',
'month_and_date_day' => '%A %B %e, %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%B %e, %Y, @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Week %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'MMMM Do, YYYY',
'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Monday',
'dow_2' => 'Tuesday',
'dow_3' => 'Wednesday',
'dow_4' => 'Thursday',
'dow_5' => 'Friday',
'dow_6' => 'Saturday',
'dow_7' => 'Sunday',
];

File diff suppressed because it is too large Load Diff

View File

@ -120,18 +120,19 @@ return [
'string' => 'The :attribute must be a string.',
'url' => 'The :attribute format is invalid.',
'timezone' => 'The :attribute must be a valid zone.',
'2fa_code' => 'The :attribute field is invalid.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'file' => 'The :attribute must be a file.',
'in_array' => 'The :attribute field does not exist in :other.',
'present' => 'The :attribute field must be present.',
'amount_zero' => 'The total amount cannot be zero.',
'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.',
'secure_password' => 'This is not a secure password. Please try again. For more information, visit https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.',
'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.',
'invalid_account_info' => 'Invalid account information.',
'2fa_code' => 'The :attribute field is invalid.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'file' => 'The :attribute must be a file.',
'in_array' => 'The :attribute field does not exist in :other.',
'present' => 'The :attribute field must be present.',
'amount_zero' => 'The total amount cannot be zero.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.',
'secure_password' => 'This is not a secure password. Please try again. For more information, visit https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.',
'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.',
'invalid_account_info' => 'Invalid account information.',
'attributes' => [
'email' => 'email address',
'description' => 'description',

View File

@ -23,29 +23,30 @@
declare(strict_types=1);
return [
'html_language' => 'es',
'locale' => 'es, Spanish, es_ES.utf8, es_ES.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e de %B %Y',
'month_and_date_day' => '%A %e de %B %Y',
'month_and_day_no_year' => '%e %B',
'date_time' => '%e de %B %Y a las %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Semana %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'D MMM YYYY',
'date_time_js' => 'D MMMM YYYY, HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Lunes',
'dow_2' => 'Martes',
'dow_3' => 'Miércoles',
'dow_4' => 'Jueves',
'dow_5' => 'Viernes',
'dow_6' => 'Sábado',
'dow_7' => 'Domingo',
'html_language' => 'es',
'locale' => 'es, Spanish, es_ES.utf8, es_ES.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e de %B %Y',
'month_and_day_moment_js' => 'MMM D, YYYY',
'month_and_date_day' => '%A %e de %B %Y',
'month_and_day_no_year' => '%e %B',
'date_time' => '%e de %B %Y a las %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Semana %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'D MMM YYYY',
'date_time_js' => 'D MMMM YYYY, HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Lunes',
'dow_2' => 'Martes',
'dow_3' => 'Miércoles',
'dow_4' => 'Jueves',
'dow_5' => 'Viernes',
'dow_6' => 'Sábado',
'dow_7' => 'Domingo',
];

File diff suppressed because it is too large Load Diff

View File

@ -120,18 +120,19 @@ return [
'string' => 'El :attribute debería ser una cadena de caracteres.',
'url' => 'El formato del campo :attribute no es válido.',
'timezone' => 'El campo :attribute debe contener una zona válida.',
'2fa_code' => 'El campo :attribute no es válido.',
'dimensions' => 'Las dimensiones de la imagen :attribute son incorrectas.',
'distinct' => 'El campo :attribute tiene un valor duplicado.',
'file' => 'El campo :attribute debe ser un fichero.',
'in_array' => 'El campo :attribute no existe en :other.',
'present' => 'El campo :attribute debe estar presente.',
'amount_zero' => 'La cantidad total no puede ser cero.',
'unique_piggy_bank_for_user' => 'En nombre de la hucha debe ser único.',
'secure_password' => 'Esta contraseña no es segura. Por favor inténtalo de nuevo. Para más información, visita https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Tipo de repetición no válido para transacciones recurrentes.',
'valid_recurrence_rep_moment' => 'Momento de repetición no válido para este tipo de repetición.',
'invalid_account_info' => 'Información de cuenta no válida.',
'2fa_code' => 'El campo :attribute no es válido.',
'dimensions' => 'Las dimensiones de la imagen :attribute son incorrectas.',
'distinct' => 'El campo :attribute tiene un valor duplicado.',
'file' => 'El campo :attribute debe ser un fichero.',
'in_array' => 'El campo :attribute no existe en :other.',
'present' => 'El campo :attribute debe estar presente.',
'amount_zero' => 'La cantidad total no puede ser cero.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'En nombre de la hucha debe ser único.',
'secure_password' => 'Esta contraseña no es segura. Por favor inténtalo de nuevo. Para más información, visita https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Tipo de repetición no válido para transacciones recurrentes.',
'valid_recurrence_rep_moment' => 'Momento de repetición no válido para este tipo de repetición.',
'invalid_account_info' => 'Información de cuenta no válida.',
'attributes' => [
'email' => 'dirección de correo electrónico',
'description' => 'descripcion',

View File

@ -39,7 +39,7 @@ return [
'reports' => 'Rapport',
'search_result' => 'Résultats de recherche pour ":query"',
'withdrawal_list' => 'Dépenses',
'Withdrawal_list' => 'Expenses',
'Withdrawal_list' => 'penses',
'deposit_list' => 'Revenu, salaire et versements',
'transfer_list' => 'Virements',
'transfers_list' => 'Virements',

View File

@ -23,29 +23,30 @@
declare(strict_types=1);
return [
'html_language' => 'fr',
'locale' => 'fr_FR.utf8, fr_FR.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
'month_and_date_day' => '%A %e %B %Y',
'month_and_day_no_year' => '%e %B',
'date_time' => '%B %e %Y @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Semaine %W %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'Do MMMM YYYY',
'date_time_js' => 'Do MMMM YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Lundi',
'dow_2' => 'Mardi',
'dow_3' => 'Mercredi',
'dow_4' => 'Jeudi',
'dow_5' => 'Vendredi',
'dow_6' => 'Samedi',
'dow_7' => 'Dimanche',
'html_language' => 'fr',
'locale' => 'fr_FR.utf8, fr_FR.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
'month_and_day_moment_js' => 'MMM D, YYYY',
'month_and_date_day' => '%A %e %B %Y',
'month_and_day_no_year' => '%e %B',
'date_time' => '%B %e %Y @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Semaine %W %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'Do MMMM YYYY',
'date_time_js' => 'Do MMMM YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Lundi',
'dow_2' => 'Mardi',
'dow_3' => 'Mercredi',
'dow_4' => 'Jeudi',
'dow_5' => 'Vendredi',
'dow_6' => 'Samedi',
'dow_7' => 'Dimanche',
];

File diff suppressed because it is too large Load Diff

View File

@ -253,9 +253,9 @@ return [
'weekend' => 'Week-end',
'client_secret' => 'Clé secrète',
'withdrawal_destination_id' => 'Destination account',
'deposit_source_id' => 'Source account',
'expected_on' => 'Expected on',
'paid' => 'Paid',
'withdrawal_destination_id' => 'Compte de destination',
'deposit_source_id' => 'Compte source',
'expected_on' => 'Prévu le',
'paid' => 'Pa',
];

View File

@ -210,7 +210,7 @@ return [
'specific_belfius_name' => 'Belfius BE',
'specific_belfius_descr' => 'Corrige d\'éventuels problèmes avec les fichiers Belfius',
'specific_ingbelgium_name' => 'ING BE',
'specific_ingbelgium_descr' => 'Fixes potential problems with ING Belgium files',
'specific_ingbelgium_descr' => 'Corrige d\'éventuels problèmes avec les fichiers ING Belgium',
// job configuration for file provider (stage: roles)
'job_config_roles_title' => 'Configuration de l\'importation (3/4) - Définir le rôle de chaque colonne',
'job_config_roles_text' => 'Chaque colonne de votre fichier CSV contient des données différentes. Veuillez indiquer quel type de données limportateur doit attendre. Loption de « mapper » les données signifie que vous allez lier chaque entrée trouvée dans la colonne à une valeur dans votre base de données. Une colonne souvent mappée est celle contenant l\'IBAN du compte opposé. Il est facile de le faire correspondre avec un IBAN déjà présent dans votre base de données.',
@ -311,6 +311,6 @@ return [
'column_internal-reference' => 'Référence interne',
// error message
'duplicate_row' => 'Row #:row (":description") could not be imported. It already exists.',
'duplicate_row' => 'La ligne n°:row (":description") n\'a pas pu être importée. Elle existe déjà.',
];

View File

@ -30,13 +30,13 @@ return [
'index_help' => 'Si vous avez besoin daide avec une page ou un formulaire, appuyez sur ce bouton.',
'index_outro' => 'La plupart des pages de Firefly III vont commencer avec un petit tour comme celui-ci. Merci de me contacter si vous avez des questions ou des commentaires. Profitez-en !',
'index_sidebar-toggle' => 'Pour créer de nouvelles transactions, comptes ou autres choses, utilisez le menu sous cette icône.',
'index_cash_account' => 'These are the accounts created so far. You can use the cash account to track cash expenses but it\'s not mandatory of course.',
'index_cash_account' => 'Voici les comptes créés jusqu\'ici. Vous pouvez utilier le compte de trésorerie pour faire le suivi de vos dépenses en espèces, mais ce n\'est pas obligatoire, bien sûr.',
// transactions (withdrawal)
'transactions_create_withdrawal_source' => 'Select your favorite asset account or liability from this dropdown.',
'transactions_create_withdrawal_destination' => 'Select an expense account here. Leave it empty if you want to make a cash expense.',
'transactions_create_withdrawal_foreign_currency' => 'Use this field to set a foreign currency and amount.',
'transactions_create_withdrawal_more_meta' => 'Plenty of other meta data you set in these fields.',
'transactions_create_withdrawal_source' => 'Choisissez votre compte d\'actif ou passif préféré à partir de ce menu déroulant.',
'transactions_create_withdrawal_destination' => 'Sélectionnez un compte de dépenses ici. Laissez-le vide si vous voulez faire une dépense en espèces.',
'transactions_create_withdrawal_foreign_currency' => 'Utilisez ce champ pour définir une devise étrangère et un montant.',
'transactions_create_withdrawal_more_meta' => 'Beaucoup d\'autres métadonnées que vous avez définies dans ces champs.',
'transactions_create_withdrawal_split_add' => 'If you want to split a transaction, add more splits with this button',
// transactions (deposit)

View File

@ -120,18 +120,19 @@ return [
'string' => 'Le champ :attribute doit être une chaîne de caractères.',
'url' => 'Le format de l\'URL de :attribute n\'est pas valide.',
'timezone' => 'Le champ :attribute doit être un fuseau horaire valide.',
'2fa_code' => 'Le champ :attribute est invalide.',
'dimensions' => 'Le :attribute possède des dimensions dimage non valides.',
'distinct' => ':attribute possède une valeur en double.',
'file' => 'Le :attribute doit être un fichier.',
'in_array' => 'Le champ :attribute n\'existe pas dans :other.',
'present' => 'Le champs :attribute doit être rempli.',
'amount_zero' => 'Le montant total ne peut pas être zéro.',
'unique_piggy_bank_for_user' => 'Le nom de la tirelire doit être unique.',
'secure_password' => 'Ce n\'est pas un mot de passe sécurisé. Veuillez essayez à nouveau. Pour plus d\'informations, visitez https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Type de répétition non valide pour des opérations périodiques.',
'valid_recurrence_rep_moment' => 'Période de répétition non valide pour ce type de répétition.',
'invalid_account_info' => 'Informations de compte non valides.',
'2fa_code' => 'Le champ :attribute est invalide.',
'dimensions' => 'Le :attribute possède des dimensions dimage non valides.',
'distinct' => ':attribute possède une valeur en double.',
'file' => 'Le :attribute doit être un fichier.',
'in_array' => 'Le champ :attribute n\'existe pas dans :other.',
'present' => 'Le champs :attribute doit être rempli.',
'amount_zero' => 'Le montant total ne peut pas être zéro.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'Le nom de la tirelire doit être unique.',
'secure_password' => 'Ce n\'est pas un mot de passe sécurisé. Veuillez essayez à nouveau. Pour plus d\'informations, visitez https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Type de répétition non valide pour des opérations périodiques.',
'valid_recurrence_rep_moment' => 'Période de répétition non valide pour ce type de répétition.',
'invalid_account_info' => 'Informations de compte non valides.',
'attributes' => [
'email' => 'adresse email',
'description' => 'description',
@ -190,6 +191,6 @@ return [
'ob_dest_need_data' => 'Vous devez obtenir un ID de compte de destination valide et/ou un nom de compte de destination valide pour continuer.',
'ob_dest_bad_data' => 'Impossible de trouver un compte de destination valide lors de la recherche de l\'ID ":id" ou du nom ":name".',
'generic_invalid_source' => 'You can\'t use this account as the source account.',
'generic_invalid_destination' => 'You can\'t use this account as the destination account.',
'generic_invalid_source' => 'Vous ne pouvez pas utiliser ce compte comme compte source.',
'generic_invalid_destination' => 'Vous ne pouvez pas utiliser ce compte comme compte destinataire.',
];

View File

@ -23,29 +23,30 @@
declare(strict_types=1);
return [
'html_language' => 'hu',
'locale' => 'hu, Hungarian, hu_HU.utf8, hu_HU.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%B %e, %Y',
'month_and_date_day' => '%Y %e %B, %A',
'month_and_day_no_year' => '%e %B',
'date_time' => '%Y %B %e @ %T',
'specific_day' => '%Y %e %B',
'week_in_year' => 'Hét %W, %Y',
'year' => '%Y',
'half_year' => '%Y %B',
'month_js' => 'YYYY MMMM',
'month_and_day_js' => 'YYYY MMMM DD',
'date_time_js' => 'YYYY MMMM DD @ HH:mm:ss',
'specific_day_js' => 'YYYY MMMM DD',
'week_in_year_js' => 'YYYY, w [Week]',
'year_js' => 'YYYY',
'half_year_js' => 'YYYY Q',
'dow_1' => 'Hétfő',
'dow_2' => 'Kedd',
'dow_3' => 'Szerda',
'dow_4' => 'Csütörtök',
'dow_5' => 'Péntek',
'dow_6' => 'Szombat',
'dow_7' => 'Vasárnap',
'html_language' => 'hu',
'locale' => 'hu, Hungarian, hu_HU.utf8, hu_HU.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%B %e, %Y',
'month_and_day_moment_js' => 'MMM D, YYYY',
'month_and_date_day' => '%Y %e %B, %A',
'month_and_day_no_year' => '%e %B',
'date_time' => '%Y %B %e @ %T',
'specific_day' => '%Y %e %B',
'week_in_year' => 'Hét %W, %Y',
'year' => '%Y',
'half_year' => '%Y %B',
'month_js' => 'YYYY MMMM',
'month_and_day_js' => 'YYYY MMMM DD',
'date_time_js' => 'YYYY MMMM DD @ HH:mm:ss',
'specific_day_js' => 'YYYY MMMM DD',
'week_in_year_js' => 'YYYY, w [Week]',
'year_js' => 'YYYY',
'half_year_js' => 'YYYY Q',
'dow_1' => 'Hétfő',
'dow_2' => 'Kedd',
'dow_3' => 'Szerda',
'dow_4' => 'Csütörtök',
'dow_5' => 'Péntek',
'dow_6' => 'Szombat',
'dow_7' => 'Vasárnap',
];

File diff suppressed because it is too large Load Diff

View File

@ -120,18 +120,19 @@ return [
'string' => ':attribute egy karakterlánc kell legyen.',
'url' => ':attribute attribútum formátuma érvénytelen.',
'timezone' => ':attribute érvényes zóna kell legyen.',
'2fa_code' => ':attribute mező érvénytelen.',
'dimensions' => ':attribute attribútum képfelbontása érvénytelen.',
'distinct' => ':attribute mezőben duplikált érték van.',
'file' => ':attribute egy fájl kell legyen.',
'in_array' => ':attribute nem létezik itt: :other.',
'present' => ':attribute mezőnek jelen kell lennie.',
'amount_zero' => 'A teljes mennyiség nem lehet nulla.',
'unique_piggy_bank_for_user' => 'A malacpersely nevének egyedinek kell lennie.',
'secure_password' => 'Ez nem biztonságos jelszó. Kérlek próbáld meg újra. További információért lásd: https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Érvénytelen ismétléstípus az ismétlődő tranzakciókhoz.',
'valid_recurrence_rep_moment' => 'Érvénytelen ismétlési időpont ehhez az ismétléstípushoz.',
'invalid_account_info' => 'Érvénytelen számlainformáció.',
'2fa_code' => ':attribute mező érvénytelen.',
'dimensions' => ':attribute attribútum képfelbontása érvénytelen.',
'distinct' => ':attribute mezőben duplikált érték van.',
'file' => ':attribute egy fájl kell legyen.',
'in_array' => ':attribute nem létezik itt: :other.',
'present' => ':attribute mezőnek jelen kell lennie.',
'amount_zero' => 'A teljes mennyiség nem lehet nulla.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'A malacpersely nevének egyedinek kell lennie.',
'secure_password' => 'Ez nem biztonságos jelszó. Kérlek próbáld meg újra. További információért lásd: https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Érvénytelen ismétléstípus az ismétlődő tranzakciókhoz.',
'valid_recurrence_rep_moment' => 'Érvénytelen ismétlési időpont ehhez az ismétléstípushoz.',
'invalid_account_info' => 'Érvénytelen számlainformáció.',
'attributes' => [
'email' => 'email cím',
'description' => 'leírás',

View File

@ -23,29 +23,30 @@
declare(strict_types=1);
return [
'html_language' => 'id',
'locale' => 'id, Bahasa Indonesia, id_ID.utf8, id_ID.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
'month_and_date_day' => '%A, %B %e %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%e %B %Y, @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Minggu %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'DD MMMM YYYY',
'date_time_js' => 'DD MMMM YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Senin',
'dow_2' => 'Selasa',
'dow_3' => 'Rabu',
'dow_4' => 'Kamis',
'dow_5' => 'Jumat',
'dow_6' => 'Sabtu',
'dow_7' => 'Minggu',
'html_language' => 'id',
'locale' => 'id, Bahasa Indonesia, id_ID.utf8, id_ID.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
'month_and_day_moment_js' => 'MMM D, YYYY',
'month_and_date_day' => '%A, %B %e %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%e %B %Y, @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Minggu %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'DD MMMM YYYY',
'date_time_js' => 'DD MMMM YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Senin',
'dow_2' => 'Selasa',
'dow_3' => 'Rabu',
'dow_4' => 'Kamis',
'dow_5' => 'Jumat',
'dow_6' => 'Sabtu',
'dow_7' => 'Minggu',
];

File diff suppressed because it is too large Load Diff

View File

@ -120,18 +120,19 @@ return [
'string' => ':attribute harus sebuah string.',
'url' => 'Format atribut tidak valid.',
'timezone' => ':attribute harus zona yang valid.',
'2fa_code' => 'Bidang :attribute tidak valid.',
'dimensions' => ':attribute memiliki dimensi gambar yang tidak valid.',
'distinct' => 'Bidang :attribute memiliki nilai duplikat.',
'file' => ':attribute harus berupa file.',
'in_array' => 'Bidang :attribute tidak ada in :other.',
'present' => 'Bidang :attribute harus ada.',
'amount_zero' => 'Jumlah total tidak boleh nol.',
'unique_piggy_bank_for_user' => 'Nama celengan harus unik.',
'secure_password' => 'This is not a secure password. Please try again. For more information, visit https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Tipe pengulangan yang tidak valid untuk transaksi berkala.',
'valid_recurrence_rep_moment' => 'Waktu pengulangan tidaklah valid untuk tipe pengulangan ini.',
'invalid_account_info' => 'Informasi akun tidak valid.',
'2fa_code' => 'Bidang :attribute tidak valid.',
'dimensions' => ':attribute memiliki dimensi gambar yang tidak valid.',
'distinct' => 'Bidang :attribute memiliki nilai duplikat.',
'file' => ':attribute harus berupa file.',
'in_array' => 'Bidang :attribute tidak ada in :other.',
'present' => 'Bidang :attribute harus ada.',
'amount_zero' => 'Jumlah total tidak boleh nol.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'Nama celengan harus unik.',
'secure_password' => 'This is not a secure password. Please try again. For more information, visit https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Tipe pengulangan yang tidak valid untuk transaksi berkala.',
'valid_recurrence_rep_moment' => 'Waktu pengulangan tidaklah valid untuk tipe pengulangan ini.',
'invalid_account_info' => 'Informasi akun tidak valid.',
'attributes' => [
'email' => 'alamat email',
'description' => 'keterangan',

View File

@ -23,29 +23,30 @@
declare(strict_types=1);
return [
'html_language' => 'it',
'locale' => 'it, Italiano, it_IT.utf8, it_IT.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
'month_and_date_day' => '%A %B %e %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%e %B %Y, @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Settimana %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM AAAA',
'month_and_day_js' => 'Do MMMM YYYY',
'date_time_js' => 'Do MMMM YYYY, @ HH:mm:ss',
'specific_day_js' => 'G MMMM AAAA',
'week_in_year_js' => '[Week] s, AAAA',
'year_js' => 'AAAA',
'half_year_js' => 'T AAAA',
'dow_1' => 'Lunedì',
'dow_2' => 'Martedì',
'dow_3' => 'Mercoledì',
'dow_4' => 'Giovedì',
'dow_5' => 'Venerdì',
'dow_6' => 'Sabato',
'dow_7' => 'Domenica',
'html_language' => 'it',
'locale' => 'it, Italiano, it_IT.utf8, it_IT.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
'month_and_day_moment_js' => 'MMM D, YYYY',
'month_and_date_day' => '%A %B %e %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%e %B %Y, @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'Settimana %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM AAAA',
'month_and_day_js' => 'Do MMMM YYYY',
'date_time_js' => 'Do MMMM YYYY, @ HH:mm:ss',
'specific_day_js' => 'G MMMM AAAA',
'week_in_year_js' => '[Week] s, AAAA',
'year_js' => 'AAAA',
'half_year_js' => 'T AAAA',
'dow_1' => 'Lunedì',
'dow_2' => 'Martedì',
'dow_3' => 'Mercoledì',
'dow_4' => 'Giovedì',
'dow_5' => 'Venerdì',
'dow_6' => 'Sabato',
'dow_7' => 'Domenica',
];

File diff suppressed because it is too large Load Diff

View File

@ -255,7 +255,7 @@ return [
'withdrawal_destination_id' => 'Conto di destinazione',
'deposit_source_id' => 'Conto di origine',
'expected_on' => 'Expected on',
'paid' => 'Paid',
'expected_on' => 'Prevista il',
'paid' => 'Pagata',
];

View File

@ -120,18 +120,19 @@ return [
'string' => ':attribute deve essere una stringa.',
'url' => ':attribute il formato non è valido.',
'timezone' => ':attribute deve essere una zona valida.',
'2fa_code' => 'Il campo :attribute non è valido.',
'dimensions' => ':attribute ha dimensioni di immagine non valide.',
'distinct' => ':attribute il campo ha un valore doppio.',
'file' => ':attribute deve essere un file.',
'in_array' => ':attribute il campo non esiste in :other.',
'present' => ':attribute il campo deve essere presente.',
'amount_zero' => 'L\'importo totale non può essere zero.',
'unique_piggy_bank_for_user' => 'Il nome del salvadanaio deve essere unico.',
'secure_password' => 'Questa non è una password sicura. Riprova. Per maggiori informazioni visita https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Il tipo di ripetizione della transazione ricorrente non è valido.',
'valid_recurrence_rep_moment' => 'Il momento di ripetizione per questo tipo di ripetizione non è valido.',
'invalid_account_info' => 'Informazione sul conto non valida.',
'2fa_code' => 'Il campo :attribute non è valido.',
'dimensions' => ':attribute ha dimensioni di immagine non valide.',
'distinct' => ':attribute il campo ha un valore doppio.',
'file' => ':attribute deve essere un file.',
'in_array' => ':attribute il campo non esiste in :other.',
'present' => ':attribute il campo deve essere presente.',
'amount_zero' => 'L\'importo totale non può essere zero.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'Il nome del salvadanaio deve essere unico.',
'secure_password' => 'Questa non è una password sicura. Riprova. Per maggiori informazioni visita https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Il tipo di ripetizione della transazione ricorrente non è valido.',
'valid_recurrence_rep_moment' => 'Il momento di ripetizione per questo tipo di ripetizione non è valido.',
'invalid_account_info' => 'Informazione sul conto non valida.',
'attributes' => [
'email' => 'indirizzo email',
'description' => 'descrizione',

View File

@ -23,29 +23,30 @@
declare(strict_types=1);
return [
'html_language' => 'no',
'locale' => 'no, nb, Norsk, nb_NO, nb_NO.utf8, nb_NO.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B, %Y',
'month_and_date_day' => '%A %B. %e, %Y',
'month_and_day_no_year' => '%B. %e',
'date_time' => '%e. %B, %Y @ %T',
'specific_day' => '%e. %B %Y',
'week_in_year' => 'Uke %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'MMMM Do, YYYY',
'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Mandag',
'dow_2' => 'Tirsdag',
'dow_3' => 'Onsdag',
'dow_4' => 'Torsdag',
'dow_5' => 'Fredag',
'dow_6' => 'Lørdag',
'dow_7' => 'Søndag',
'html_language' => 'no',
'locale' => 'no, nb, Norsk, nb_NO, nb_NO.utf8, nb_NO.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B, %Y',
'month_and_day_moment_js' => 'MMM D, YYYY',
'month_and_date_day' => '%A %B. %e, %Y',
'month_and_day_no_year' => '%B. %e',
'date_time' => '%e. %B, %Y @ %T',
'specific_day' => '%e. %B %Y',
'week_in_year' => 'Uke %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'MMMM Do, YYYY',
'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Mandag',
'dow_2' => 'Tirsdag',
'dow_3' => 'Onsdag',
'dow_4' => 'Torsdag',
'dow_5' => 'Fredag',
'dow_6' => 'Lørdag',
'dow_7' => 'Søndag',
];

File diff suppressed because it is too large Load Diff

View File

@ -120,18 +120,19 @@ return [
'string' => ':attribute må være en streng.',
'url' => ':attribute formatet er ugyldig.',
'timezone' => ':attribute må være en gyldig tidssone.',
'2fa_code' => ':attribute formatet er ugyldig.',
'dimensions' => ':attribute har ugyldig bilde dimensjoner.',
'distinct' => ':attribute feltet har en duplikatverdi.',
'file' => ':attribute må være en fil.',
'in_array' => 'Feltet :attribute finnes ikke i :other.',
'present' => ':attribute feltet må være definert.',
'amount_zero' => 'Totalbeløpet kan ikke være null.',
'unique_piggy_bank_for_user' => 'Navnet på sparegris må være unik.',
'secure_password' => 'Dette er ikke et sikkert passord. Vennligst prøv igjen. For mer informasjon, se https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Ugyldig repetisjons type for gjentakende transaksjoner.',
'valid_recurrence_rep_moment' => 'Ugyldig repetisjons tid for denne type repetisjon.',
'invalid_account_info' => 'Ugyldig konto informasjon.',
'2fa_code' => ':attribute formatet er ugyldig.',
'dimensions' => ':attribute har ugyldig bilde dimensjoner.',
'distinct' => ':attribute feltet har en duplikatverdi.',
'file' => ':attribute må være en fil.',
'in_array' => 'Feltet :attribute finnes ikke i :other.',
'present' => ':attribute feltet må være definert.',
'amount_zero' => 'Totalbeløpet kan ikke være null.',
'current_target_amount' => 'The current amount must be less than the target amount.',
'unique_piggy_bank_for_user' => 'Navnet på sparegris må være unik.',
'secure_password' => 'Dette er ikke et sikkert passord. Vennligst prøv igjen. For mer informasjon, se https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Ugyldig repetisjons type for gjentakende transaksjoner.',
'valid_recurrence_rep_moment' => 'Ugyldig repetisjons tid for denne type repetisjon.',
'invalid_account_info' => 'Ugyldig konto informasjon.',
'attributes' => [
'email' => 'epostadresse',
'description' => 'beskrivelse',

View File

@ -23,29 +23,30 @@
declare(strict_types=1);
return [
'html_language' => 'nl',
'locale' => 'nl, Dutch, nl_NL.utf8, nl_NL.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
'month_and_date_day' => '%A %e %B %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%e %B %Y, @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'week %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'D MMMM YYYY',
'date_time_js' => 'D MMMM YYYY @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Maandag',
'dow_2' => 'Dinsdag',
'dow_3' => 'Woensdag',
'dow_4' => 'Donderdag',
'dow_5' => 'Vrijdag',
'dow_6' => 'Zaterdag',
'dow_7' => 'Zondag',
'html_language' => 'nl',
'locale' => 'nl, Dutch, nl_NL.utf8, nl_NL.UTF-8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
'month_and_day_moment_js' => 'D MMM YYYY',
'month_and_date_day' => '%A %e %B %Y',
'month_and_day_no_year' => '%B %e',
'date_time' => '%e %B %Y, @ %T',
'specific_day' => '%e %B %Y',
'week_in_year' => 'week %W, %Y',
'year' => '%Y',
'half_year' => '%B %Y',
'month_js' => 'MMMM YYYY',
'month_and_day_js' => 'D MMMM YYYY',
'date_time_js' => 'D MMMM YYYY @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'dow_1' => 'Maandag',
'dow_2' => 'Dinsdag',
'dow_3' => 'Woensdag',
'dow_4' => 'Donderdag',
'dow_5' => 'Vrijdag',
'dow_6' => 'Zaterdag',
'dow_7' => 'Zondag',
];

Some files were not shown because too many files have changed in this diff Show More