Merge branch 'release/5.5.8' into main

This commit is contained in:
James Cole 2021-04-18 07:28:12 +02:00
commit adc52f7b63
151 changed files with 1699 additions and 851 deletions

View File

@ -162,9 +162,8 @@ class ExportData extends Command
*/
private function parseOptions(): array
{
$start = $this->getDateParameter('start');
$end = $this->getDateParameter('end');
exit;
$start = $this->getDateParameter('start');
$end = $this->getDateParameter('end');
$accounts = $this->getAccountsParameter();
$export = $this->getExportDirectory();

View File

@ -135,16 +135,21 @@ class BudgetLimitController extends Controller
if (null === $currency || null === $budget) {
throw new FireflyException('No valid currency or budget.');
}
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$amount = (string)$request->get('amount');
$start->startOfDay();
$end->startOfDay();
if ('' === $amount) {
return response()->json([]);
}
Log::debug(sprintf('Start: %s, end: %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
$limit = $this->blRepository->find($budget, $currency, $start, $end);
if (null !== $limit) {
$limit->amount = $request->get('amount');
$limit->amount = $amount;
$limit->save();
}
if (null === $limit) {
@ -154,7 +159,7 @@ class BudgetLimitController extends Controller
'currency_id' => (int)$request->get('transaction_currency_id'),
'start_date' => $start,
'end_date' => $end,
'amount' => $request->get('amount'),
'amount' => $amount,
]
);
}
@ -176,7 +181,7 @@ class BudgetLimitController extends Controller
return response()->json($array);
}
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
return response()->json([]);
}
/**
@ -187,7 +192,10 @@ class BudgetLimitController extends Controller
*/
public function update(Request $request, BudgetLimit $budgetLimit): JsonResponse
{
$amount = $request->get('amount');
$amount = (string)$request->get('amount');
if ('' === $amount) {
$amount = '0';
}
$limit = $this->blRepository->update($budgetLimit, ['amount' => $amount]);
$array = $limit->toArray();

View File

@ -73,12 +73,10 @@ class IndexController extends Controller
*/
public function index()
{
/** @var User $user */
$user = auth()->user();
$this->createDefaultRuleGroup();
$this->createDefaultRule();
$this->ruleGroupRepos->resetOrder();
$ruleGroups = $this->ruleGroupRepos->getRuleGroupsWithRules(null);
$ruleGroups = $this->ruleGroupRepos->getAllRuleGroupsWithRules(null);
return prefixView('rules.index', compact('ruleGroups'));
}

View File

@ -100,7 +100,7 @@ class CreateController extends Controller
$repository = app(AccountRepositoryInterface::class);
$cash = $repository->getCashAccount();
$preFilled = session()->has('preFilled') ? session('preFilled') : [];
$subTitle = (string)trans('breadcrumbs.create_new_transaction');
$subTitle = (string)trans(sprintf('breadcrumbs.create_%s', strtolower((string)$objectType)));
$subTitleIcon = 'fa-plus';
$optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
$allowedOpposingTypes = config('firefly.allowed_opposing_types');

View File

@ -65,7 +65,7 @@ class BudgetFormStoreRequest extends FormRequest
'active' => 'numeric|between:0,1',
'auto_budget_type' => 'numeric|between:0,2',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_amount' => 'min:0|max:1000000000',
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2',
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
];
}

View File

@ -75,7 +75,7 @@ class BudgetFormUpdateRequest extends FormRequest
'active' => 'numeric|between:0,1',
'auto_budget_type' => 'numeric|between:0,2',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_amount' => 'min:0|max:1000000000',
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2',
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
];
}

View File

@ -22,7 +22,6 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use Eloquent;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder;
@ -33,13 +32,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* FireflyIII\Models\Preference
*
* @property int $id
* @property int $id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property int $user_id
* @property string $name
* @property int|string|array|null $data
* @property-read User $user
* @property int $user_id
* @property string $name
* @property int|string|array|null $data
* @property-read User $user
* @method static Builder|Preference newModelQuery()
* @method static Builder|Preference newQuery()
* @method static Builder|Preference query()
@ -73,19 +72,29 @@ class Preference extends Model
*
* @param string $value
*
* @throws NotFoundHttpException
* @return Preference
* @throws NotFoundHttpException
*/
public static function routeBinder(string $value): Preference
{
if (auth()->check()) {
/** @var User $user */
$user = auth()->user();
/** @var Preference $preference */
/** @var Preference|null $preference */
$preference = $user->preferences()->where('name', $value)->first();
if (null !== $preference) {
return $preference;
}
$default = config('firefly.default_preferences');
if (array_key_exists($value, $default)) {
$preference = new Preference;
$preference->name = $value;
$preference->data = $default[$value];
$preference->user_id = $user->id;
$preference->save();
return $preference;
}
}
throw new NotFoundHttpException;
}

View File

@ -258,6 +258,58 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
);
}
/**
* @param string|null $filter
*
* @return Collection
*/
public function getAllRuleGroupsWithRules(?string $filter): Collection
{
$groups = $this->user->ruleGroups()
->orderBy('order', 'ASC')
->with(
[
'rules' => static function (HasMany $query) {
$query->orderBy('order', 'ASC');
},
'rules.ruleTriggers' => static function (HasMany $query) {
$query->orderBy('order', 'ASC');
},
'rules.ruleActions' => static function (HasMany $query) {
$query->orderBy('order', 'ASC');
},
]
)->get();
if (null === $filter) {
return $groups;
}
Log::debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter));
return $groups->map(
function (RuleGroup $group) use ($filter) {
Log::debug(sprintf('Now filtering group #%d', $group->id));
// filter the rules in the rule group:
$group->rules = $group->rules->filter(
function (Rule $rule) use ($filter) {
Log::debug(sprintf('Now filtering rule #%d', $rule->id));
foreach ($rule->ruleTriggers as $trigger) {
if ('user_action' === $trigger->trigger_type && $filter === $trigger->trigger_value) {
Log::debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter));
return true;
}
}
Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter));
return false;
}
);
return $group;
}
);
}
/**
* @param RuleGroup $group
*

View File

@ -114,6 +114,15 @@ interface RuleGroupRepositoryInterface
*/
public function getRuleGroupsWithRules(?string $filter): Collection;
/**
* Also inactive groups.
*
* @param string|null $filter
*
* @return Collection
*/
public function getAllRuleGroupsWithRules(?string $filter): Collection;
/**
* @param RuleGroup $group
*

View File

@ -292,7 +292,7 @@ class JournalUpdateService
$validator->setTransactionType($expectedType);
$validator->setUser($this->transactionJournal->user);
$validator->source = $this->getValidSourceAccount();
$result = $validator->validateDestination($destId, $destName, null);
$result = $validator->validateDestination($destId, $destName, null);
Log::debug(sprintf('hasValidDestinationAccount(%d, "%s") will return %s', $destId, $destName, var_export($result, true)));
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
@ -527,6 +527,10 @@ class JournalUpdateService
Log::debug('Will update budget.');
$this->storeBudget($this->transactionJournal, new NullArrayObject($this->data));
}
// is transfer? remove budget
if (TransactionType::TRANSFER === $this->transactionJournal->transactionType->type) {
$this->transactionJournal->budgets()->sync([]);
}
}
/**

View File

@ -174,6 +174,11 @@ class SearchRuleEngine implements RuleEngineInterface
private function fireRule(Rule $rule): bool
{
Log::debug(sprintf('Now going to fire rule #%d', $rule->id));
if (false === $rule->active) {
Log::debug(sprintf('Rule #%d is not active!', $rule->id));
return false;
}
if (true === $rule->strict) {
Log::debug(sprintf('Rule #%d is a strict rule.', $rule->id));
@ -223,7 +228,7 @@ class SearchRuleEngine implements RuleEngineInterface
Log::debug(sprintf('Now in findStrictRule(#%d)', $rule->id ?? 0));
$searchArray = [];
/** @var RuleTrigger $ruleTrigger */
foreach ($rule->ruleTriggers()->where('active',1)->get() as $ruleTrigger) {
foreach ($rule->ruleTriggers()->where('active', 1)->get() as $ruleTrigger) {
// if needs no context, value is different:
$needsContext = config(sprintf('firefly.search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true;
if (false === $needsContext) {
@ -368,7 +373,7 @@ class SearchRuleEngine implements RuleEngineInterface
{
Log::debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction journal #%d', $transaction['transaction_journal_id']));
/** @var RuleAction $ruleAction */
foreach ($rule->ruleActions()->where('active',1)->get() as $ruleAction) {
foreach ($rule->ruleActions()->where('active', 1)->get() as $ruleAction) {
$break = $this->processRuleAction($ruleAction, $transaction);
if (true === $break) {
break;
@ -444,7 +449,7 @@ class SearchRuleEngine implements RuleEngineInterface
$total = new Collection;
$count = 0;
/** @var RuleTrigger $ruleTrigger */
foreach ($rule->ruleTriggers()->where('active',1)->get() as $ruleTrigger) {
foreach ($rule->ruleTriggers()->where('active', 1)->get() as $ruleTrigger) {
if ('user_action' === $ruleTrigger->trigger_type) {
Log::debug('Skip trigger type.');
continue;

View File

@ -2,6 +2,16 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 5.5.8 (API 1.5.2) 2021-04-17
This update fixes some of the more annoying issues in the new experimental v2 layout (see also [GitHub](https://github.com/firefly-iii/firefly-iii/issues/4618)), but some minor other issues as well.
### Fixed
- [Issue 4656](https://github.com/firefly-iii/firefly-iii/issues/4656) [issue 4660](https://github.com/firefly-iii/firefly-iii/issues/4660) Various fixes in the v2 layout.
- [Issue 4663](https://github.com/firefly-iii/firefly-iii/issues/4663) It was possible to assign a budget to a transfer.
- [Issue 4664](https://github.com/firefly-iii/firefly-iii/issues/4664) Null pointer in bulk editor
- [Issue 4668](https://github.com/firefly-iii/firefly-iii/issues/4668) Inactive rule groups would not be listed.
## 5.5.7 (API 1.5.2) 2021-04-11
### Added

71
composer.lock generated
View File

@ -303,16 +303,16 @@
},
{
"name": "diglactic/laravel-breadcrumbs",
"version": "v6.1.0",
"version": "v6.1.1",
"source": {
"type": "git",
"url": "https://github.com/diglactic/laravel-breadcrumbs.git",
"reference": "027178500b57295290d9930f113f81df1a7188cb"
"reference": "8f73674f9c5403625154768f8e123a620fc2d7a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/diglactic/laravel-breadcrumbs/zipball/027178500b57295290d9930f113f81df1a7188cb",
"reference": "027178500b57295290d9930f113f81df1a7188cb",
"url": "https://api.github.com/repos/diglactic/laravel-breadcrumbs/zipball/8f73674f9c5403625154768f8e123a620fc2d7a5",
"reference": "8f73674f9c5403625154768f8e123a620fc2d7a5",
"shasum": ""
},
"require": {
@ -320,6 +320,9 @@
"laravel/framework": "^6.0 || ^7.0 || ^8.0",
"php": "^7.2 || ^8.0"
},
"conflict": {
"davejamesmiller/laravel-breadcrumbs": "*"
},
"require-dev": {
"orchestra/testbench": "^4.10 || ^5.9 || ^6.4",
"php-coveralls/php-coveralls": "^2.4",
@ -368,9 +371,9 @@
],
"support": {
"issues": "https://github.com/diglactic/laravel-breadcrumbs/issues",
"source": "https://github.com/diglactic/laravel-breadcrumbs/tree/v6.1.0"
"source": "https://github.com/diglactic/laravel-breadcrumbs/tree/v6.1.1"
},
"time": "2021-01-25T18:38:42+00:00"
"time": "2021-04-12T18:06:07+00:00"
},
{
"name": "doctrine/cache",
@ -1641,16 +1644,16 @@
},
{
"name": "laravel/framework",
"version": "v8.36.2",
"version": "v8.37.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "0debd8ad6b5aa1f61ccc73910adf049af4ca0444"
"reference": "cf4082973abc796ec285190f0603380021f6d26f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/0debd8ad6b5aa1f61ccc73910adf049af4ca0444",
"reference": "0debd8ad6b5aa1f61ccc73910adf049af4ca0444",
"url": "https://api.github.com/repos/laravel/framework/zipball/cf4082973abc796ec285190f0603380021f6d26f",
"reference": "cf4082973abc796ec285190f0603380021f6d26f",
"shasum": ""
},
"require": {
@ -1805,7 +1808,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2021-04-07T12:37:22+00:00"
"time": "2021-04-13T13:49:49+00:00"
},
{
"name": "laravel/passport",
@ -2252,16 +2255,16 @@
},
{
"name": "league/csv",
"version": "9.7.0",
"version": "9.7.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/csv.git",
"reference": "4cacd9c72c4aa8bdbef43315b2ca25c46a0f833f"
"reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/4cacd9c72c4aa8bdbef43315b2ca25c46a0f833f",
"reference": "4cacd9c72c4aa8bdbef43315b2ca25c46a0f833f",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1",
"reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1",
"shasum": ""
},
"require": {
@ -2332,7 +2335,7 @@
"type": "github"
}
],
"time": "2021-03-26T22:08:10+00:00"
"time": "2021-04-17T16:32:08+00:00"
},
{
"name": "league/event",
@ -8643,16 +8646,16 @@
},
{
"name": "nunomaduro/larastan",
"version": "v0.7.2",
"version": "v0.7.4",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/larastan.git",
"reference": "cb7fa0b5af3738772e3568c0a0c7a080851e281d"
"reference": "0ceef2a39b45be9d7f7dd96192a1721ba5112278"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nunomaduro/larastan/zipball/cb7fa0b5af3738772e3568c0a0c7a080851e281d",
"reference": "cb7fa0b5af3738772e3568c0a0c7a080851e281d",
"url": "https://api.github.com/repos/nunomaduro/larastan/zipball/0ceef2a39b45be9d7f7dd96192a1721ba5112278",
"reference": "0ceef2a39b45be9d7f7dd96192a1721ba5112278",
"shasum": ""
},
"require": {
@ -8716,7 +8719,7 @@
],
"support": {
"issues": "https://github.com/nunomaduro/larastan/issues",
"source": "https://github.com/nunomaduro/larastan/tree/v0.7.2"
"source": "https://github.com/nunomaduro/larastan/tree/v0.7.4"
},
"funding": [
{
@ -8736,7 +8739,7 @@
"type": "patreon"
}
],
"time": "2021-04-08T10:51:16+00:00"
"time": "2021-04-16T08:25:31+00:00"
},
{
"name": "openlss/lib-array2xml",
@ -9715,12 +9718,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "aa48fe959b0236eede9c51a38f47df2bb81ef137"
"reference": "593c4de369ca852cf3b86037f19435d47c136448"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/aa48fe959b0236eede9c51a38f47df2bb81ef137",
"reference": "aa48fe959b0236eede9c51a38f47df2bb81ef137",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/593c4de369ca852cf3b86037f19435d47c136448",
"reference": "593c4de369ca852cf3b86037f19435d47c136448",
"shasum": ""
},
"conflict": {
@ -9799,7 +9802,7 @@
"friendsofsymfony/user-bundle": ">=1.2,<1.3.5",
"friendsoftypo3/mediace": ">=7.6.2,<7.6.5",
"fuel/core": "<1.8.1",
"getgrav/grav": "<1.7-beta.8",
"getgrav/grav": "<1.7.11",
"getkirby/cms": ">=3,<3.4.5",
"getkirby/panel": "<2.5.14",
"gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3",
@ -9837,6 +9840,9 @@
"monolog/monolog": ">=1.8,<1.12",
"moodle/moodle": "<3.5.17|>=3.7,<3.7.9|>=3.8,<3.8.8|>=3.9,<3.9.5|>=3.10,<3.10.2",
"namshi/jose": "<2.2",
"neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6",
"neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.9.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3",
"neos/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5",
"nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6",
"nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13",
"nystudio107/craft-seomatic": "<3.3",
@ -9883,6 +9889,7 @@
"propel/propel1": ">=1,<=1.7.1",
"pterodactyl/panel": "<0.7.19|>=1-rc.0,<=1-rc.6",
"pusher/pusher-php-server": "<2.2.1",
"pwweb/laravel-core": "<=0.3.6-beta",
"rainlab/debugbar-plugin": "<3.1",
"robrichards/xmlseclibs": "<3.0.4",
"sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1",
@ -9890,8 +9897,9 @@
"scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11",
"sensiolabs/connect": "<4.2.3",
"serluck/phpwhois": "<=4.2.6",
"shopware/core": "<=6.3.4",
"shopware/platform": "<=6.3.5.1",
"shopware/core": "<=6.3.5.2",
"shopware/platform": "<=6.3.5.2",
"shopware/production": "<=6.3.5.2",
"shopware/shopware": "<5.6.9",
"silverstripe/admin": ">=1.0.3,<1.0.4|>=1.1,<1.1.1",
"silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2",
@ -9968,9 +9976,10 @@
"typo3/cms-backend": ">=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1",
"typo3/cms-core": ">=6.2,<=6.2.56|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.25|>=10,<10.4.14|>=11,<11.1.1",
"typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1",
"typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5",
"typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4",
"typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6",
"typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3",
"typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1",
"typo3/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5",
"typo3fluid/fluid": ">=2,<2.0.8|>=2.1,<2.1.7|>=2.2,<2.2.4|>=2.3,<2.3.7|>=2.4,<2.4.4|>=2.5,<2.5.11|>=2.6,<2.6.10",
"ua-parser/uap-php": "<3.8",
"usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2",
@ -10047,7 +10056,7 @@
"type": "tidelift"
}
],
"time": "2021-04-09T08:01:23+00:00"
"time": "2021-04-16T20:01:44+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@ -100,7 +100,7 @@ return [
'handle_debts' => true,
],
'version' => '5.5.7',
'version' => '5.5.8',
'api_version' => '1.5.2',
'db_version' => 16,
'maxUploadSize' => 1073741824, // 1 GB
@ -850,4 +850,11 @@ return [
'valid_asset_fields' => ['account_role', 'account_number', 'currency_id', 'BIC', 'include_net_worth'],
'valid_cc_fields' => ['account_role', 'cc_monthly_payment_date', 'cc_type', 'account_number', 'currency_id', 'BIC', 'include_net_worth'],
'valid_account_fields' => ['account_number', 'currency_id', 'BIC', 'interest', 'interest_period', 'include_net_worth', 'liability_direction'],
'default_preferences' => [
'frontPageAccounts' => [],
'listPageSize' => 50,
'currencyPreference' => 'EUR',
'language' => 'en_US',
'locale' => 'equal',
],
];

View File

@ -47,7 +47,7 @@ yarn prod
# mv public/css ../public/v2
# also copy fonts
cp -r fonts ../public
#cp -r fonts ../public/v2/css
# remove built stuff
rm -rf public
rm -rf public/

View File

@ -11,6 +11,7 @@
},
"devDependencies": {
"axios": "^0.21",
"date-fns": "^2.21.1",
"laravel-mix": "^6.0.6",
"lodash": "^4.17.19",
"lodash.clonedeep": "^4.5.0",

View File

@ -46,7 +46,8 @@
class="btn btn-secondary"
@click="resetDate"
><i class="fas fa-history"></i></button>
<button id="dropdownMenuButton" :title="$t('firefly.select_period')" aria-expanded="false" aria-haspopup="true" class="btn btn-secondary dropdown-toggle"
<button id="dropdownMenuButton" :title="$t('firefly.select_period')" aria-expanded="false" aria-haspopup="true"
class="btn btn-secondary dropdown-toggle"
data-toggle="dropdown"
type="button">
<i class="fas fa-list"></i>
@ -78,8 +79,19 @@
import {createNamespacedHelpers} from "vuex";
import Vue from "vue";
import DatePicker from "v-calendar/lib/components/date-picker.umd";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
import subDays from 'date-fns/subDays'
import addDays from 'date-fns/addDays'
import startOfDay from 'date-fns/startOfDay'
import endOfDay from 'date-fns/endOfDay'
import startOfWeek from 'date-fns/startOfWeek'
import endOfWeek from 'date-fns/endOfWeek'
import format from 'date-fns/format'
import startOfQuarter from 'date-fns/startOfQuarter';
import endOfQuarter from 'date-fns/endOfQuarter';
import subQuarters from 'date-fns/subQuarters';
import addQuarters from 'date-fns/addQuarters';
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
Vue.component('date-picker', DatePicker)
@ -130,16 +142,89 @@ export default {
this.generatePeriods()
return false;
},
generatePeriods: function () {
this.periods = [];
// create periods.
let today;
let end;
generateDaily: function () {
let today = new Date(this.range.start);
// yesterday
this.periods.push(
{
start: startOfDay(subDays(today, 1)).toDateString(),
end: endOfDay(subDays(today, 1)).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(subDays(today, 1))
}
);
today = new Date(this.range.start);
// today
this.periods.push(
{
start: startOfDay(today).toDateString(),
end: endOfDay(today).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(today)
}
);
// tomorrow:
this.periods.push(
{
start: startOfDay(addDays(today, 1)).toDateString(),
end: endOfDay(addDays(today, 1)).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(addDays(today, 1))
}
);
// The Day After Tomorrow dun-dun-dun!
this.periods.push(
{
start: startOfDay(addDays(today, 2)).toDateString(),
end: endOfDay(addDays(today, 2)).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(addDays(today, 2))
}
);
},
generateWeekly: function () {
let today = new Date(this.range.start);
let start = startOfDay(startOfWeek(subDays(today, 7), {weekStartsOn: 1}));
let end = endOfDay(endOfWeek(subDays(today, 7), {weekStartsOn: 1}));
let dateFormat = this.$t('config.week_in_year_fns');
let title = format(start, dateFormat);
// last week
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this week
start = startOfDay(startOfWeek(today, {weekStartsOn: 1}));
end = endOfDay(endOfWeek(today, {weekStartsOn: 1}));
title = format(start, dateFormat);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// next week
start = startOfDay(startOfWeek(addDays(today, 7), {weekStartsOn: 1}));
end = endOfDay(endOfWeek(addDays(today, 7), {weekStartsOn: 1}));
title = format(start, dateFormat);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
},
generateMonthly: function () {
let today = new Date(this.range.start);
// previous month
firstDayOfMonth = new Date(today.getFullYear(), today.getMonth()-1, 1);
firstDayOfMonth = new Date(today.getFullYear(), today.getMonth() - 1, 1);
lastDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 0);
this.periods.push(
{
@ -151,7 +236,7 @@ export default {
// this month
firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
lastDayOfMonth = new Date(today.getFullYear(), today.getMonth()+1, 0);
lastDayOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0);
this.periods.push(
{
start: firstDayOfMonth.toDateString(),
@ -161,8 +246,8 @@ export default {
);
// next month
let firstDayOfMonth = new Date(today.getFullYear(), today.getMonth()+1, 1);
let lastDayOfMonth = new Date(today.getFullYear(), today.getMonth()+2, 0);
let firstDayOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
let lastDayOfMonth = new Date(today.getFullYear(), today.getMonth() + 2, 0);
this.periods.push(
{
start: firstDayOfMonth.toDateString(),
@ -171,9 +256,271 @@ export default {
}
);
},
generateQuarterly: function () {
let today = new Date(this.range.start);
// last quarter
let start = startOfDay(startOfQuarter(subQuarters(today, 1)));
let end = endOfDay(endOfQuarter(subQuarters(today, 1)));
let dateFormat = this.$t('config.quarter_fns');
let title = format(start, dateFormat);
// last week
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this quarter
start = startOfDay(startOfQuarter(today));
end = endOfDay(endOfQuarter(today));
title = format(start, dateFormat);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// next quarter
start = startOfDay(startOfQuarter(addQuarters(today, 1)));
end = endOfDay(endOfQuarter(addQuarters(today, 1)));
title = format(start, dateFormat);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
},
generateHalfYearly: function () {
let today = new Date(this.range.start);
let start;
let end;
let title = 'todo';
let half = 1;
// its currently first half of year:
if (today.getMonth() <= 5) {
// previous year, last half:
start = today;
start.setFullYear(start.getFullYear() - 1);
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
half = 2;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this year, first half:
start = today;
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = today;
end.setMonth(5);
end.setDate(30);
end = endOfDay(start);
half = 1;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this year, second half:
start = today;
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
half = 2;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
return;
}
// this year, first half:
start = today;
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(5);
end.setDate(30);
end = endOfDay(end);
half = 1;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this year, second half:
start = today;
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = today;
end.setMonth(11);
end.setDate(31);
end = endOfDay(start);
half = 2;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// next year, first half:
start = today;
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(5);
end.setDate(30);
end = endOfDay(end);
half = 1;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
},
generateYearly: function () {
let today = new Date(this.range.start);
let start;
let end;
let title;
// last year
start = new Date(today);
start.setFullYear(start.getFullYear() - 1);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setFullYear(end.getFullYear() - 1);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: start.getFullYear()
}
);
// this year
start = new Date(today);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: start.getFullYear()
}
);
// next year
start = new Date(today);
start.setFullYear(start.getFullYear() + 1);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setFullYear(end.getFullYear() + 1);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: start.getFullYear()
}
);
},
generatePeriods: function () {
this.periods = [];
//console.log('The view range is "' + this.viewRange + '".');
switch (this.viewRange) {
case '1D':
this.generateDaily();
break;
case '1W':
this.generateWeekly();
break;
case '1M':
this.generateMonthly();
break;
case '3M':
this.generateQuarterly();
break;
case '6M':
this.generateHalfYearly();
break;
case '1Y':
this.generateYearly();
break;
}
// last 7 days
today = new Date;
end = new Date;
let today = new Date;
let end = new Date;
end.setDate(end.getDate() - 7);
this.periods.push(
{

View File

@ -49,12 +49,10 @@ import DataConverter from "../charts/DataConverter";
import DefaultLineOptions from "../charts/DefaultLineOptions";
import {mapGetters} from "vuex";
import * as ChartJs from 'chart.js'
ChartJs.Chart.register.apply(null, Object.values(ChartJs).filter((chartClass) => (chartClass.id)));
export default {
name: "MainAccount",
components: {}, // MainAccountChart
@ -63,6 +61,7 @@ export default {
loading: true,
error: false,
ready: false,
initialised: false,
dataCollection: {},
chartOptions: {},
_chart: null,
@ -71,18 +70,18 @@ export default {
}
},
created() {
this.ready = true;
this.chartOptions = DefaultLineOptions.methods.getDefaultOptions();
this.localTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
this.systemTimeZone = this.timezone;
this.ready = true;
},
computed: {
...mapGetters('dashboard/index',['start', 'end']),
...mapGetters('root',['timezone']),
...mapGetters('dashboard/index', ['start', 'end']),
...mapGetters('root', ['timezone']),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
},
timezoneDifference: function() {
timezoneDifference: function () {
return this.localTimeZone !== this.systemTimeZone;
}
},
@ -93,10 +92,10 @@ export default {
}
},
start: function () {
//this.initialiseChart();
this.updateChart();
},
end: function () {
//this.initialiseChart();
this.updateChart();
},
},
methods: {
@ -116,18 +115,38 @@ export default {
this.drawChart();
})
.catch(error => {
// console.log('Has error!');
// console.log(error);
console.log('Has error!');
console.log(error);
this.error = true;
});
},
drawChart: function () {
this._chart = new ChartJs.Chart(this.$refs.canvas.getContext('2d'), {
type: 'line',
data: this.dataCollection,
options: this.chartOptions
}
);
//console.log('drawChart');
if ('undefined' !== typeof this._chart) {
//console.log('destroy or update!');
this._chart.data = this.dataCollection;
this._chart.update();
}
if ('undefined' === typeof this._chart) {
//console.log('new!');
this._chart = new ChartJs.Chart(this.$refs.canvas.getContext('2d'), {
type: 'line',
data: this.dataCollection,
options: this.chartOptions
}
);
this.initialised = true;
}
},
updateChart: function () {
//console.log('updateChart');
if (this.initialised) {
//console.log('MUST Update chart!');
// reset some vars so it wont trigger again:
this.initialised = false;
this.initialiseChart();
}
}
},
}

View File

@ -123,7 +123,7 @@ export default {
initialiseList: function () {
this.loading = true;
this.accounts = [];
axios.get('./api/v1/preferences/frontpageAccounts')
axios.get('./api/v1/preferences/frontPageAccounts')
.then(response => {
this.loadAccounts(response);
}

View File

@ -24,7 +24,6 @@ const state = () => (
viewRange: 'default',
start: null,
end: null,
// default range:
defaultStart: null,
defaultEnd: null,
}
@ -53,24 +52,32 @@ const getters = {
// actions
const actions = {
initialiseStore(context) {
if ('default' === context.state.viewRange) {
axios.get('./api/v1/preferences/viewRange')
.then(response => {
let viewRange = response.data.data.attributes.data;
context.commit('setViewRange', viewRange);
// call another action:
// console.log('initialiseStore');
// restore from local storage:
context.dispatch('restoreViewRange');
axios.get('./api/v1/preferences/viewRange')
.then(response => {
let viewRange = response.data.data.attributes.data;
let oldViewRange = context.getters.viewRange;
context.commit('setViewRange', viewRange);
if (viewRange !== oldViewRange) {
// console.log('View range changed from "' + oldViewRange + '" to "' + viewRange + '"');
context.dispatch('setDatesFromViewRange');
}
).catch(error => {
// console.log(error);
context.commit('setViewRange', '1M');
// call another action:
context.dispatch('setDatesFromViewRange');
});
}
if(viewRange === oldViewRange) {
// console.log('Restore view range dates');
context.dispatch('restoreViewRangeDates');
}
}
).catch(() => {
context.commit('setViewRange', '1M');
context.dispatch('setDatesFromViewRange');
});
},
setDatesFromViewRange(context) {
// console.log('Must set dates from viewRange "' + context.state.viewRange + '"');
restoreViewRangeDates: function(context) {
// check local storage first?
if (localStorage.viewRangeStart) {
// console.log('view range start set from local storage.');
@ -91,10 +98,16 @@ const actions = {
// console.log(localStorage.viewRangeDefaultEnd);
context.commit('setDefaultEnd', new Date(localStorage.viewRangeDefaultEnd));
}
if (null !== context.getters.end && null !== context.getters.start) {
return;
},
restoreViewRange: function (context) {
// console.log('restoreViewRange');
let viewRange = localStorage.getItem('viewRange');
if (null !== viewRange) {
// console.log('restored restoreViewRange ' + viewRange );
context.commit('setViewRange', viewRange);
}
},
setDatesFromViewRange(context) {
let start;
let end;
let viewRange = context.getters.viewRange;
@ -206,6 +219,7 @@ const mutations = {
},
setViewRange(state, range) {
state.viewRange = range;
window.localStorage.setItem('viewRange', range);
}
}

View File

@ -46,6 +46,9 @@ const getters = {
transactions: state => {
return state.transactions;
},
defaultErrors: state => {
return state.defaultErrors;
},
groupTitle: state => {
return state.groupTitle;
},

View File

@ -40,6 +40,7 @@
:transaction="transaction"
:transaction-type="transactionType"
v-on:uploaded-attachments="uploadedAttachment($event)"
v-on:selected-attachments="selectedAttachment($event)"
v-on:set-marker-location="storeLocation($event)"
v-on:set-account="storeAccountValue($event)"
v-on:set-date="storeDate($event)"
@ -118,6 +119,7 @@ import SplitPills from "./SplitPills";
import TransactionGroupTitle from "./TransactionGroupTitle";
import SplitForm from "./SplitForm";
import {mapGetters, mapMutations} from "vuex";
import {getDefaultErrors} from "../../shared/transactions";
export default {
@ -138,8 +140,9 @@ export default {
let type = parts[parts.length - 1];
// set a basic date-time string:
let date = new Date;
this.date = [date.getFullYear(), ('0' + (date.getMonth() + 1)).slice(-2), ('0' + date.getDate()).slice(-2)].join('-') + 'T00:00';
this.date = (new Date).toISOString().split('T')[0] + 'T00:00';
//console.log('Date is set to "' + this.date + '"');
this.setTransactionType(type[0].toUpperCase() + type.substring(1));
@ -165,7 +168,7 @@ export default {
// things the process is done working on (3 phases):
submittedTransaction: false,
submittedLinks: false,
submittedAttachments: false,
submittedAttachments: -1, // -1 (no attachments), 0 = uploading, 1 = uploaded
// transaction was actually submitted?
inError: false,
@ -196,18 +199,12 @@ export default {
/**
* Grabbed from the store.
*/
...mapGetters('transactions/create', ['transactionType', 'transactions', 'groupTitle']),
...mapGetters('transactions/create', ['transactionType', 'transactions', 'groupTitle','defaultErrors']),
...mapGetters('root', ['listPageSize'])
},
watch: {
submittedTransaction: function () {
this.finalizeSubmit();
},
submittedLinks: function () {
this.finalizeSubmit();
},
submittedAttachments: function () {
this.finalizeSubmit();
this.finaliseSubmission();
}
},
methods: {
@ -237,58 +234,152 @@ export default {
// console.log('Triggered to remove transaction ' + payload.index);
this.$store.commit('transactions/create/deleteTransaction', payload);
},
/**
* Submitting a transaction consists of 3 steps: submitting the transaction, uploading attachments
* and creating links. Only once all three steps are executed may the message be shown or the user be
* forwarded.
*/
finalizeSubmit() {
// console.log('finalizeSubmit (' + this.submittedTransaction + ', ' + this.submittedAttachments + ', ' + this.submittedLinks + ')');
if (this.submittedTransaction && this.submittedAttachments && this.submittedLinks) {
// console.log('all true');
// console.log('createAnother = ' + this.createAnother);
// console.log('inError = ' + this.inError);
if (false === this.createAnother && false === this.inError) {
// console.log('redirect');
window.location.href = (window.previousURL ?? '/') + '?transaction_group_id=' + this.returnedGroupId + '&message=created';
return;
submitData: function (url, data) {
return axios.post(url, data);
},
handleSubmissionResponse: function (response) {
//console.log('In handleSubmissionResponse()');
// save some meta data:
this.returnedGroupId = parseInt(response.data.data.id);
this.returnedGroupTitle = null === response.data.data.attributes.group_title ? response.data.data.attributes.transactions[0].description : response.data.data.attributes.group_title;
let journals = [];
// save separate journal ID's (useful ahead in the process):
let result = response.data.data.attributes.transactions
for (let i in result) {
if (result.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
journals.push(parseInt(result[i].transaction_journal_id));
}
}
if (false === this.inError) {
// show message:
this.errorMessage = '';
this.successMessage = this.$t('firefly.transaction_stored_link', {ID: this.returnedGroupId, title: this.returnedGroupTitle});
}
// enable flags:
this.enableSubmit = true;
this.submittedTransaction = false;
this.submittedLinks = false;
this.submittedAttachments = false;
this.inError = false;
// reset attachments (always do this)
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
if (this.transactions.hasOwnProperty(i)) {
// console.log('Reset attachment #' + i);
this.updateField({index: i, field: 'transaction_journal_id', value: 0});
return new Promise((resolve) => {
resolve(
{
journals: journals,
}
);
});
},
submitLinks: function (response, submission) {
let promises = [];
// for
for (let i in response.journals) {
if (response.journals.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let journalId = response.journals[i];
let links = submission.transactions[i].links;
for (let ii in links) {
if (links.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
let currentLink = links[ii];
if (0 === currentLink.outward_id) {
currentLink.outward_id = journalId;
}
if (0 === currentLink.inward_id) {
currentLink.inward_id = journalId;
}
promises.push(axios.post('./api/v1/transaction_links', currentLink));
}
}
}
this.submittedAttCount = [];
// reset the form:
if (this.resetFormAfter) {
this.resetTransactions();
// do a short time out?
setTimeout(() => this.addTransaction(), 50);
}
// console.log('Done with finalizeSubmit!');
// return;
}
// console.log('Did nothing in finalizeSubmit');
if (0 === promises.length) {
return new Promise((resolve) => {
resolve(
{
response: 'from submitLinks'
}
);
});
}
return Promise.all(promises);
},
submitAttachments: function (response, submission) {
let anyAttachments = false;
for (let i in response.journals) {
if (response.journals.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let journalId = response.journals[i];
let hasAttachments = submission.transactions[i].attachments;
// console.log('Decided that ' + journalId);
// console.log(hasAttachments);
if (hasAttachments) {
// console.log('upload!');
this.updateField({index: i, field: 'transaction_journal_id', value: journalId});
anyAttachments = true;
}
}
}
if (true === anyAttachments) {
this.submittedAttachments = 0;
}
return new Promise((resolve) => {
resolve(
{
response: 'from submitAttachments'
}
);
});
},
selectedAttachment: function (payload) {
this.updateField({index: payload.index, field: 'attachments', value: true});
},
finaliseSubmission: function () {
if (0 === this.submittedAttachments) {
// console.log('submittedAttachments = ' + this.submittedAttachments);
return;
}
//console.log('In finaliseSubmission');
if (false === this.createAnother) {
window.location.href = (window.previousURL ?? '/') + '?transaction_group_id=' + this.returnedGroupId + '&message=created';
return;
}
//console.log('Is in error?');
//console.log(this.inError);
if (false === this.inError) {
// show message:
this.errorMessage = '';
this.successMessage = this.$t('firefly.transaction_stored_link', {ID: this.returnedGroupId, title: this.returnedGroupTitle});
}
// enable flags:
this.enableSubmit = true;
this.submittedTransaction = false;
this.submittedAttachments = -1;
// reset attachments + errors
if (!this.resetFormAfter) {
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
if (this.transactions.hasOwnProperty(i)) {
//this.
// console.log('Reset attachment #' + i);
this.updateField({index: i, field: 'transaction_journal_id', value: 0});
this.updateField({index: i, field: 'errors', value: this.defaultErrors})
}
}
}
}
// reset the form:
if (this.resetFormAfter) {
this.resetTransactions();
this.addTransaction();
}
return new Promise((resolve) => {
resolve(
{
response: 'from finaliseSubmission'
}
);
});
},
handleSubmissionError: function (error) {
//console.log('in handleSubmissionError');
// oh noes Firefly III has something to bitch about.
this.enableSubmit = true;
// but report an error because error:
this.inError = true;
this.parseErrors(error.response.data);
},
/**
* Actually submit the transaction to Firefly III. This is a fairly complex beast of a thing because multiple things
@ -300,64 +391,26 @@ export default {
// disable the submit button:
this.enableSubmit = false;
// assume nothing breaks
this.inError = false;
// remove old warnings etc.
this.successMessage = '';
this.errorMessage = '';
// convert the data so its ready to be submitted:
const url = './api/v1/transactions';
const data = this.convertData();
// console.log('Will submit:');
// console.log(data);
// POST the transaction.
axios.post(url, data)
this.submitData(url, data)
.then(this.handleSubmissionResponse)
.then(response => {
// console.log('Response is OK!');
// report the transaction is submitted.
this.submittedTransaction = true;
return Promise.all([this.submitLinks(response, data), this.submitAttachments(response, data)]);
}
)
.then(this.finaliseSubmission)
.catch(this.handleSubmissionError);
// submit links and attachments (can only be done when the transaction is created)
this.submitTransactionLinks(data, response);
this.submitAttachments(data, response);
// meanwhile, store the ID and the title in some easy to access variables.
this.returnedGroupId = parseInt(response.data.data.id);
this.returnedGroupTitle = null === response.data.data.attributes.group_title ? response.data.data.attributes.transactions[0].description : response.data.data.attributes.group_title;
// console.log('Group title is now "' + this.groupTitle + '"');
})
.catch(error => {
// oh noes Firefly III has something to bitch about.
this.enableSubmit = true;
// console.log('enable submit = true');
// report the transaction is submitted.
this.submittedTransaction = true;
// also report attachments and links are submitted:
this.submittedAttachments = true;
this.submittedLinks = true;
// but report an error because error:
this.inError = true;
this.parseErrors(error.response.data);
});
},
/**
* Submitting transactions means we will give each TransactionAttachment component
* the ID of the transaction journal (so it works for multiple splits). Each component
* will then start uploading their transactions (so its a separated concern) and report
* back to the "uploadedAttachment" function below via an event emitter.
*
* The ID is set via the store.
*/
submitAttachments: function (data, response) {
// console.log('submitAttachments()');
let result = response.data.data.attributes.transactions
for (let i in data.transactions) {
if (data.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
if (result.hasOwnProperty(i)) {
// console.log('updateField(' + i + ', transaction_journal_id, ' + result[i].transaction_journal_id + ')');
this.updateField({index: i, field: 'transaction_journal_id', value: result[i].transaction_journal_id});
}
}
}
},
/**
* When a attachment component is done uploading it ends up here. We create an object where we count how many
@ -367,6 +420,7 @@ export default {
* Once the number of components matches the number of splits we know all attachments have been uploaded.
*/
uploadedAttachment: function (journalId) {
this.submittedAttachments = 0;
// console.log('Triggered uploadedAttachment(' + journalId + ')');
let key = 'str' + journalId;
this.submittedAttCount[key] = 1;
@ -374,9 +428,9 @@ export default {
// console.log('Count is now ' + count);
// console.log('Length is ' + this.transactions.length);
if (count === this.transactions.length) {
// mark the attachments as stored:
this.submittedAttachments = true;
// console.log('Got them all!');
// mark the attachments as stored:
this.submittedAttachments = 1;
}
},
/**
@ -457,11 +511,13 @@ export default {
this.submittedLinks = true;
});
},
parseErrors: function (errors) {
for (let i in this.transactions) {
this.resetErrors({index: i});
if (this.transactions.hasOwnProperty(i)) {
this.resetErrors({index: i});
}
}
this.successMessage = '';
this.errorMessage = this.$t('firefly.errors_submission');
if (typeof errors.errors === 'undefined') {
@ -470,12 +526,6 @@ export default {
}
let payload;
//payload = {index: 0, field: 'description', errors: ['Test error index 0']};
//this.setTransactionError(payload);
//payload = {index: 1, field: 'description', errors: ['Test error index 1']};
//this.setTransactionError(payload);
let transactionIndex;
let fieldName;
@ -558,6 +608,7 @@ export default {
//console.log('Group title is: "' + this.groupTitle + '"');
if (this.groupTitle.length > 0) {
data.group_title = this.groupTitle;
//console.log('1) data.group_title is now "'+data.group_title+'"');
}
for (let i in this.transactions) {
@ -565,8 +616,9 @@ export default {
data.transactions.push(this.convertSplit(i, this.transactions[i]));
}
}
if (data.transactions.length > 1 && '' !== data.transactions[0].description) {
if (data.transactions.length > 1 && '' !== data.transactions[0].description && (null === data.group_title || '' === data.group_title)) {
data.group_title = data.transactions[0].description;
//console.log('2) data.group_title is now "'+data.group_title+'"');
}
// depending on the transaction type for this thing, we need to
@ -616,27 +668,6 @@ export default {
},
// switchAccounts: function (index) {
// // console.log('user wants to switch Accounts');
// let origSourceId = this.transactions[index].source_account_id;
// let origSourceName = this.transactions[index].source_account_name;
// let origSourceType = this.transactions[index].source_account_type;
//
// let origDestId = this.transactions[index].destination_account_id;
// let origDestName = this.transactions[index].destination_account_name;
// let origDestType = this.transactions[index].destination_account_type;
//
// this.updateField({index: 0, field: 'source_account_id', value: origDestId});
// this.updateField({index: 0, field: 'source_account_name', value: origDestName});
// this.updateField({index: 0, field: 'source_account_type', value: origDestType});
//
// this.updateField({index: 0, field: 'destination_account_id', value: origSourceId});
// this.updateField({index: 0, field: 'destination_account_name', value: origSourceName});
// this.updateField({index: 0, field: 'destination_account_type', value: origSourceType});
// this.calculateTransactionType(0);
// },
/**
*
* @param key
@ -700,6 +731,7 @@ export default {
// from thing:
order: 0,
reconciled: false,
attachments: array.attachments,
};
if (0 !== array.tags.length) {

View File

@ -23,6 +23,7 @@
<Alert :message="errorMessage" type="danger"/>
<Alert :message="successMessage" type="success"/>
<Alert :message="warningMessage" type="warning"/>
<form @submit="submitTransaction" autocomplete="off">
<SplitPills :transactions="transactions"/>
@ -40,7 +41,6 @@
:destination-allowed-types="destinationAllowedTypes"
:source-allowed-types="sourceAllowedTypes"
:allow-switch="false"
:submitted-transaction="submittedTransaction"
v-on:uploaded-attachments="uploadedAttachment($event)"
v-on:set-marker-location="storeLocation($event)"
v-on:set-account="storeAccountValue($event)"
@ -74,7 +74,8 @@
<div class="text-xs d-none d-lg-block d-xl-block">
&nbsp;
</div>
<button type="button" class="btn btn-outline-primary btn-block" @click="addTransaction"><i class="far fa-clone"></i> {{ $t('firefly.add_another_split') }}
<button type="button" class="btn btn-outline-primary btn-block" @click="addTransaction"><i class="far fa-clone"></i>
{{ $t('firefly.add_another_split') }}
</button>
</div>
<div class="col">
@ -109,12 +110,14 @@
</template>
<script>
const lodashClonedeep = require('lodash.clonedeep');
import {mapMutations} from "vuex";
import Alert from '../partials/Alert';
import SplitPills from "./SplitPills";
import SplitForm from "./SplitForm";
import TransactionGroupTitle from "./TransactionGroupTitle";
import {getDefaultErrors, getDefaultTransaction, toW3CString} from '../../shared/transactions';
import {getDefaultErrors, getDefaultTransaction} from '../../shared/transactions';
const lodashClonedeep = require('lodash.clonedeep');
export default {
name: "Edit",
@ -156,10 +159,15 @@ export default {
// things the process is done working on (3 phases):
submittedTransaction: false,
submittedLinks: false,
submittedAttachments: false,
// submittedLinks: false,
submittedAttachments: -1, // -1 = no attachments, 0 = uploading, 1 = uploaded
inError: false,
// number of uploaded attachments
// its an object because we count per transaction journal (which can have multiple attachments)
// and array doesn't work right.
submittedAttCount: {},
// meta data for accounts
allowedOpposingTypes: {},
destinationAllowedTypes: [],
@ -179,21 +187,13 @@ export default {
},
watch: {
submittedTransaction: function () {
// see finalizeSubmit()
this.finalizeSubmit();
},
submittedLinks: function () {
// see finalizeSubmit()
this.finalizeSubmit();
},
submittedAttachments: function () {
// see finalizeSubmit()
this.finalizeSubmit();
this.finaliseSubmission();
}
},
methods: {
...mapMutations('transactions/create', ['updateField',]),
/**
* Grap transaction group from URL and submit GET.
*/
@ -203,8 +203,8 @@ export default {
this.parseTransactionGroup(response.data);
}
).catch(error => {
// console.log('I failed :(');
// console.log(error);
console.log('I failed :(');
console.log(error);
});
},
/**
@ -219,6 +219,10 @@ export default {
this.groupTitle = attributes.group_title;
this.originalGroupTitle = attributes.group_title;
//this.returnedGroupId = parseInt(response.data.id);
this.returnedGroupTitle = null === this.originalGroupTitle ? response.data.attributes.transactions[0].description : this.originalGroupTitle;
for (let i in transactions) {
if (transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let result = this.parseTransaction(parseInt(i), transactions[i]);
@ -239,6 +243,8 @@ export default {
//console.log('index: ' + index);
if (0 === index) {
this.transactionType = array.type.charAt(0).toUpperCase() + array.type.slice(1);
// TODO here you may need to catch stuff like loan/debt/mortgage
this.sourceAllowedTypes = [array.source_type];
this.destinationAllowedTypes = [array.destination_type];
this.date = array.date.substring(0, 16);
@ -292,7 +298,6 @@ export default {
result.zoom_level = array.zoom_level;
result.longitude = array.longitude;
result.latitude = array.latitude;
// error handling
result.errors = getDefaultErrors();
return result;
@ -370,14 +375,12 @@ export default {
});
},
/**
* TODO same method as Create
* Get API value.
*/
getAllowedOpposingTypes: function () {
axios.get('./api/v1/configuration/firefly.allowed_opposing_types')
.then(response => {
this.allowedOpposingTypes = response.data.data.value;
// console.log('Set allowedOpposingTypes');
});
},
/**
@ -389,8 +392,20 @@ export default {
});
},
uploadedAttachment: function (payload) {
// console.log('event: uploadedAttachment');
// console.log(payload);
//console.log('event: uploadedAttachment');
//console.log(payload);
this.submittedAttachments = 0;
// console.log('Triggered uploadedAttachment(' + journalId + ')');
let key = 'str' + payload;
this.submittedAttCount[key] = 1;
let count = Object.keys(this.submittedAttCount).length;
//console.log('Count is now ' + count);
//console.log('Length is ' + this.transactions.length);
if (count === this.transactions.length) {
//console.log('Got them all!');
// mark the attachments as stored:
this.submittedAttachments = 1;
}
},
storeLocation: function (payload) {
this.transactions[payload.index].zoom_level = payload.zoomLevel;
@ -405,26 +420,25 @@ export default {
this.transactions[index][direction + '_account_name'] = payload.name;
},
storeDate: function (payload) {
// console.log('event: storeDate');
// console.log(payload);
this.date = payload.date;
},
storeTime: function (payload) {
this.time = payload.time;
// console.log('event: storeTime');
// console.log(payload);
},
storeField: function (payload) {
let field = payload.field;
if ('category' === field) {
field = 'category_name';
}
// console.log('event: storeField(' + field + ')');
this.transactions[payload.index][field] = payload.value;
},
removeTransaction: function (payload) {
//console.log('removeTransaction()');
//console.log(payload);
//console.log('length: ' + this.transactions.length);
this.transactions.splice(payload.index, 1);
//console.log('length: ' + this.transactions.length);
//this.originalTransactions.splice(payload.index, 1);
// this kills the original transactions.
this.originalTransactions = [];
},
@ -432,9 +446,12 @@ export default {
this.groupTitle = payload;
},
selectedAttachments: function (payload) {
//console.log('Now in selectedAttachments()');
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
if (parseInt(this.transactions[i].transaction_journal_id) === parseInt(payload)) {
// console.log('Payload is');
// console.log(payload);
if (parseInt(this.transactions[i].transaction_journal_id) === parseInt(payload.id)) {
// console.log('selectedAttachments ' + payload);
this.transactions[i].selectedAttachments = true;
}
@ -450,56 +467,55 @@ export default {
submitTransaction: function (event) {
event.preventDefault();
let submission = {transactions: []};
// parse data to see if we should submit anything at all:
let shouldSubmit = false;
let shouldLinks = false;
let shouldUpload = false;
// if the group title has changed, should submit:
if (this.groupTitle !== this.originalGroupTitle) {
submission.group_title = this.groupTitle;
shouldSubmit = true;
}
let transactionCount = this.originalTransactions.length;
let newTransactionCount = this.transactions.length;
// console.log('Found ' + this.transactions.length + ' split(s).');
// if something with the group title:
let newTransactionCount = this.transactions.length;
if (newTransactionCount > 1 && typeof submission.group_title === 'undefined' && (null === this.originalGroupTitle || '' === this.originalGroupTitle)) {
submission.group_title = this.transactions[0].description;
shouldSubmit = true;
}
// loop each transaction (edited by the user):
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
// original transaction present?
// original transaction present:
let currentTransaction = this.transactions[i];
let originalTransaction = this.originalTransactions.hasOwnProperty(i) ? this.originalTransactions[i] : {};
// the difference:
let diff = {};
// compare basic fields:
let basicFields = [
'description',
'source_account_id', 'source_account_name',
'destination_account_id', 'destination_account_name',
'amount', 'foreign_amount', 'foreign_currency_id',
'category_name', 'budget_id', 'bill_id',
'interest_date', 'book_date', 'due_date', 'payment_date', 'invoice_date',
'external_url', 'internal_reference', 'external_id', 'notes',
'zoom_level', 'longitude', 'latitude'
];
let basicFields = ['description', 'source_account_id', 'source_account_name', 'destination_account_id', 'destination_account_name', 'amount', 'foreign_amount', 'foreign_currency_id', 'category_name', 'budget_id', 'bill_id', 'interest_date', 'book_date', 'due_date', 'payment_date', 'invoice_date', 'external_url', 'internal_reference', 'external_id', 'notes', 'zoom_level', 'longitude', 'latitude'];
// source and destination may be overruled:
// source and destination are overruled in some cases:
if (i > 0) {
diff.type = this.transactionType.toLowerCase();
if ('Deposit' === this.transactionType || 'Transfer' === this.transactionType) {
if ('deposit' === this.transactionType.toLowerCase() || 'transfer' === this.transactionType.toLowerCase()) {
// set destination to be whatever is in transaction zero:
currentTransaction.destination_account_name = this.originalTransactions[0].destination_account_name;
currentTransaction.destination_account_id = this.originalTransactions[0].destination_account_id;
}
if ('Withdrawal' === this.transactionType || 'Transfer' === this.transactionType) {
if ('withdrawal' === this.transactionType.toLowerCase() || 'transfer' === this.transactionType.toLowerCase()) {
// set source to be whatever is in transaction zero:
currentTransaction.source_account_name = this.originalTransactions[0].source_account_name;
currentTransaction.source_account_id = this.originalTransactions[0].source_account_id;
}
// console.log('Will overrule accounts for split ' + i);
}
// loop the basic fields and verify
for (let ii in basicFields) {
if (basicFields.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
let fieldName = basicFields[ii];
@ -519,13 +535,7 @@ export default {
continue;
}
// console.log('Index ' + i + ': Field ' + fieldName + ' updated ("' + originalTransaction[fieldName] + '" > "' + currentTransaction[fieldName] + '")');
// console.log(originalTransaction[fieldName]);
// console.log(currentTransaction[fieldName]);
// some field names may need to be different. little basic but it works:
// console.log('pre: ' + submissionFieldName);
if ('source_account_id' === submissionFieldName) {
submissionFieldName = 'source_id';
}
@ -539,26 +549,19 @@ export default {
submissionFieldName = 'destination_name';
}
// otherwise save them and remember them for submission:
diff[submissionFieldName] = currentTransaction[fieldName];
shouldSubmit = true;
}
}
}
if (0 !== currentTransaction.piggy_bank_id) {
diff.piggy_bank_id = currentTransaction.piggy_bank_id;
shouldSubmit = true;
}
if (JSON.stringify(currentTransaction.tags) !== JSON.stringify(originalTransaction.tags)) {
// console.log('tags are different');
// console.log(currentTransaction.tags);
// console.log(originalTransaction.tags);
diff.tags = [];//currentTransaction.tags;
// tags different?
if (JSON.stringify(currentTransaction.tags) !== JSON.stringify(originalTransaction.tags)) {
diff.tags = [];
if (0 !== currentTransaction.tags.length) {
for (let ii in currentTransaction.tags) {
if (currentTransaction.tags.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
// array.tags
let currentTag = currentTransaction.tags[ii];
if (typeof currentTag === 'object' && null !== currentTag) {
diff.tags.push(currentTag.text);
@ -569,79 +572,258 @@ export default {
}
}
}
shouldSubmit = true;
}
// compare links:
let newLinks = this.compareLinks(currentTransaction.links);
let originalLinks = this.compareLinks(originalTransaction.links);
// console.log('links are?');
// console.log(newLinks);
// console.log(originalLinks);
if (newLinks !== originalLinks) {
// console.log('links are different!');
// console.log(newLinks);
// console.log(originalLinks);
shouldLinks = true;
}
// this.transactions[i].selectedAttachments
// console.log(typeof currentTransaction.selectedAttachments);
// console.log(currentTransaction.selectedAttachments);
if (typeof currentTransaction.selectedAttachments !== 'undefined' && true === currentTransaction.selectedAttachments) {
// must upload!
shouldUpload = true;
}
if (
this.date !== this.originalDate
) {
// console.log('Date and/or time is changed');
// set date and time!
if (this.date !== this.originalDate) {
shouldSubmit = true;
diff.date = this.date;
}
// console.log('Now at index ' + i);
// console.log(Object.keys(diff).length);
if (Object.keys(diff).length === 0 && newTransactionCount > 1) {
// console.log('Will submit just the ID!');
// Will submit just the ID!
diff.transaction_journal_id = originalTransaction.transaction_journal_id;
submission.transactions.push(lodashClonedeep(diff));
shouldSubmit = true;
} else if (Object.keys(diff).length !== 0) {
// will submit all:
diff.transaction_journal_id = originalTransaction.transaction_journal_id ?? 0;
submission.transactions.push(lodashClonedeep(diff));
shouldSubmit = true;
}
}
}
this.submitUpdate(submission, shouldSubmit, shouldLinks, shouldUpload);
},
// console.log('submitTransaction');
// console.log('shouldUpload : ' + shouldUpload);
// console.log('shouldLinks : ' + shouldLinks);
// console.log('shouldSubmit : ' + shouldSubmit);
if (shouldSubmit) {
this.submitUpdate(submission, shouldLinks, shouldUpload);
}
submitData: function (shouldSubmit, submission) {
//console.log('submitData');
if (!shouldSubmit) {
this.submittedTransaction = true;
//console.log('No need!');
return new Promise((resolve) => {
resolve({});
});
}
if (!shouldLinks) {
this.submittedLinks = true;
const url = './api/v1/transactions/' + this.groupId;
return axios.put(url, submission);
},
handleSubmissionResponse: function (response) {
//console.log('handleSubmissionResponse()');
// report the transaction is submitted.
this.submittedTransaction = true;
let journals = [];
// meanwhile, store the ID and the title in some easy to access variables.
if (typeof response.data !== 'undefined') {
this.returnedGroupId = parseInt(response.data.data.id) ?? null;
this.returnedGroupTitle = null === response.data.data.attributes.group_title ? response.data.data.attributes.transactions[0].description : response.data.data.attributes.group_title;
let result = response.data.data.attributes.transactions
for (let i in result) {
if (result.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
journals.push(parseInt(result[i].transaction_journal_id));
}
}
} else {
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
journals.push(this.transactions[i].transaction_journal_id);
}
}
}
if (!shouldUpload) {
this.submittedAttachments = true;
journals = journals.reverse();
return new Promise((resolve) => {
resolve(
{
journals: journals,
}
);
});
},
submitLinks: function (shouldSubmit) {
//console.log('submitLinks()');
if (!shouldSubmit) {
//console.log('no need!');
return new Promise((resolve) => {
resolve({});
});
}
if (!shouldSubmit && shouldLinks) {
this.submitTransactionLinks();
return this.deleteAllOriginalLinks().then(() => this.submitNewLinks());
},
submitAttachments: function (shouldSubmit, response) {
//console.log('submitAttachments');
if (!shouldSubmit) {
//console.log('no need!');
return new Promise((resolve) => {
resolve({});
});
}
//console.log('Do upload thing!');
//console.log(response);
let anyAttachments = false;
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let transaction = this.transactions[i];
let journalId = transaction.transaction_journal_id;
//console.log(journalId);
if (typeof response !== 'undefined') {
journalId = response.journals[i]
}
let hasAttachments = transaction.selectedAttachments;
this.transactions[i].transaction_journal_id = journalId;
this.transactions[i].uploadTrigger = true;
//console.log('Decided that ' + journalId);
//console.log('upload index ' + i);
//console.log(hasAttachments);
if (hasAttachments) {
anyAttachments = true;
}
}
}
if (true === anyAttachments) {
this.submittedAttachments = 0;
}
},
finaliseSubmission: function () {
//console.log('finaliseSubmission');
if (0 === this.submittedAttachments) {
return;
}
//console.log('continue (' + this.submittedAttachments + ')');
if (true === this.stayHere && false === this.inError) {
//console.log('no error + no changes + no redirect');
// show message:
this.errorMessage = '';
this.warningMessage = '';
this.successMessage = this.$t('firefly.transaction_updated_link', {ID: this.groupId, title: this.returnedGroupTitle});
}
// no error + changes + redirect
if (false === this.stayHere && false === this.inError) {
//console.log('no error + changes + redirect');
window.location.href = (window.previousURL ?? '/') + '?transaction_group_id=' + this.groupId + '&message=updated';
}
if (!shouldSubmit && shouldLinks) {
// TODO
//this.submittedAttachments();
this.enableSubmit = true;
this.submittedAttachments = -1;
this.inError = false;
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
if (this.transactions.hasOwnProperty(i)) {
this.transactions[i].clearTrigger = true;
}
}
}
// console.log('Done with submit methd.');
//console.log(submission);
},
submitUpdate: function (submission, shouldSubmit, shouldLinks, shouldUpload) {
//console.log('submitUpdate()');
this.inError = false;
this.submitData(shouldSubmit, submission)
.then(this.handleSubmissionResponse) // error or OK
.then(response => {
return Promise.all([
this.submitLinks(shouldLinks, response),
this.submitAttachments(shouldUpload, response)]);
}
)
.then(this.finaliseSubmission)
.catch(this.handleSubmissionError);
// if (shouldLinks) {
// promises.push(this.submitTransactionLinks())
// }
// if (!shouldLinks) {
// promises.push(new Promise((resolve) => {
// resolve({});
// }));
// }
// if (shouldUpload) {
// console.log('Attachments = Respond to promise from shouldSubmit/!shouldSubmit');
// promises.push(submissionPromise.then(result => this.uploadAttachments(result)));
// }
// if (!shouldUpload) {
// promises.push(new Promise((resolve) => {
// resolve({});
// }));
// }
// all promises fulfilled:
// console.log('All promises done, process results?');
// Promise.all(promises).then(function (responses) {
// console.log('I believe all ' + promises.length + ' promises fulfilled!');
// }).catch(function (errors) {
// console.log('Somebody errored?');
// });
//
//
//
// if (shouldSubmit) {
// console.log('shouldSubmit');
// // do submission:
// // console.log(JSON.stringify(submission));
// // console.log(submission);
// axios.put(url, submission)
// .then(response => {
// // console.log('Response is OK!');
// // report the transaction is submitted.
// this.submittedTransaction = true;
//
// // submit links and attachments (can only be done when the transaction is created)
// if (shouldLinks) {
// console.log('Submit links using return from server:');
// this.submitTransactionLinks();
// }
// if (shouldUpload) {
// // console.log('Need to upload.');
// this.submitAttachments(response.data.data.attributes.transactions);
// }
// // meanwhile, store the ID and the title in some easy to access variables.
// this.returnedGroupId = parseInt(response.data.data.id);
// this.returnedGroupTitle = null === response.data.data.attributes.group_title ? response.data.data.attributes.transactions[0].description : response.data.data.attributes.group_title;
// }
// )
// .catch(error => {
// console.log('error :(');
// console.log(error.response.data);
// // oh noes Firefly III has something to bitch about.
// this.enableSubmit = true;
// // report the transaction is submitted.
// this.submittedTransaction = true;
// // // also report attachments and links are submitted:
// this.submittedAttachments = true;
// this.submittedLinks = true;
// //
// // but report an error because error:
// this.inError = true;
// this.parseErrors(error.response.data);
// }
// );
// }
// if (!shouldSubmit && shouldLinks) {
// // update links
// console.log('Submit links using whatever is here:');
// this.submitTransactionLinks();
// }
// if (!shouldSubmit && shouldUpload) {
// // upload
// // console.log('Need to upload.');
// this.submitAttachments(this.transactions);
// }
},
compareLinks: function (array) {
let compare = [];
@ -659,55 +841,26 @@ export default {
);
}
}
// console.log('compareLinks');
// console.log(compare);
return JSON.stringify(compare);
},
submitUpdate: function (submission, shouldLinks, shouldUpload) {
// console.log('submitUpdate');
this.inError = false;
const url = './api/v1/transactions/' + this.groupId;
// console.log(JSON.stringify(submission));
// console.log(submission);
axios.put(url, submission)
.then(response => {
// console.log('Response is OK!');
// report the transaction is submitted.
this.submittedTransaction = true;
uploadAttachments: function (result) {
//console.log('TODO, upload attachments.');
if (0 === Object.keys(result).length) {
// submit links and attachments (can only be done when the transaction is created)
if (shouldLinks) {
// console.log('Need to update links.');
this.submitTransactionLinks();
}
if (!shouldLinks) {
// console.log('No need to update links.');
}
// TODO attachments:
// this.submitAttachments(data, response);
//
// // meanwhile, store the ID and the title in some easy to access variables.
this.returnedGroupId = parseInt(response.data.data.id);
this.returnedGroupTitle = null === response.data.data.attributes.group_title ? response.data.data.attributes.transactions[0].description : response.data.data.attributes.group_title;
}
)
.catch(error => {
console.log('error :(');
console.log(error.response.data);
// oh noes Firefly III has something to bitch about.
this.enableSubmit = true;
// report the transaction is submitted.
this.submittedTransaction = true;
// // also report attachments and links are submitted:
this.submittedAttachments = true;
this.submittedLinks = true;
//
// but report an error because error:
this.inError = true;
this.parseErrors(error.response.data);
}
);
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
//console.log('updateField(' + i + ', transaction_journal_id, ' + result[i].transaction_journal_id + ')');
this.updateField({index: i, field: 'transaction_journal_id', value: result[i].transaction_journal_id});
}
}
//console.log('Transactions not changed, use original objects.');
} else {
//console.log('Transactions changed!');
}
this.submittedAttachments = true;
},
parseErrors: function (errors) {
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
@ -800,83 +953,78 @@ export default {
},
deleteOriginalLinks: function (transaction) {
// console.log(transaction.links);
let promises = [];
for (let i in transaction.links) {
if (transaction.links.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = transaction.links[i];
let url = '/api/v1/transaction_links/' + current.id;
axios.delete(url).then(response => {
// TODO response
});
promises.push(axios.delete(url));
}
}
return Promise.all(promises);
},
/**
* Submit transaction links.
* TODO same method as CREATE
*/
submitTransactionLinks() {
let total = 0;
deleteAllOriginalLinks: function () {
//console.log('deleteAllOriginalLinks()');
// loop to delete old transaction links.
let promises = [];
// console.log('submitTransactionLinks()');
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
// original transaction present?
let currentTransaction = this.transactions[i];
let originalTransaction = this.originalTransactions.hasOwnProperty(i) ? this.originalTransactions[i] : {};
// compare links:
let newLinks = this.compareLinks(currentTransaction.links);
let originalLinks = this.compareLinks(originalTransaction.links);
if (newLinks !== originalLinks) {
if ('[]' !== originalLinks) {
this.deleteOriginalLinks(originalTransaction);
promises.push(this.deleteOriginalLinks(originalTransaction));
}
// console.log('links are different!');
// console.log(newLinks);
// console.log(originalLinks);
for (let ii in currentTransaction.links) {
if (currentTransaction.links.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
let currentLink = currentTransaction.links[ii];
let linkObject = {
inward_id: currentTransaction.transaction_journal_id,
outward_id: currentTransaction.transaction_journal_id,
link_type_id: 'something'
};
let parts = currentLink.link_type_id.split('-');
linkObject.link_type_id = parts[0];
if ('inward' === parts[1]) {
linkObject.inward_id = currentLink.transaction_journal_id;
}
if ('outward' === parts[1]) {
linkObject.outward_id = currentLink.transaction_journal_id;
}
// console.log(linkObject);
total++;
// submit transaction link:
promises.push(axios.post('./api/v1/transaction_links', linkObject).then(response => {
// TODO error handling.
}));
}
}
// shouldLinks = true;
} else {
promises.push(new Promise((resolve) => {
resolve({});
}));
}
}
}
if (0 === total) {
this.submittedLinks = true;
return;
}
Promise.all(promises).then(function () {
this.submittedLinks = true;
});
return Promise.all(promises);
},
finalizeSubmit: function () {
submitNewLinks: function () {
//console.log('submitNewLinks()');
let promises = [];
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let currentTransaction = this.transactions[i];
for (let ii in currentTransaction.links) {
if (currentTransaction.links.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
let currentLink = currentTransaction.links[ii];
let linkObject = {
inward_id: currentTransaction.transaction_journal_id,
outward_id: currentTransaction.transaction_journal_id,
link_type_id: 'something'
};
let parts = currentLink.link_type_id.split('-');
linkObject.link_type_id = parts[0];
if ('inward' === parts[1]) {
linkObject.inward_id = currentLink.transaction_journal_id;
}
if ('outward' === parts[1]) {
linkObject.outward_id = currentLink.transaction_journal_id;
}
promises.push(axios.post('./api/v1/transaction_links', linkObject));
}
}
}
}
return Promise.all(promises);
},
/**
* Submit transaction links.
*/
submitTransactionLinksX: function () {
//return this.deleteAllOriginalLinks().then(() => this.submitNewLinks());
},
finalizeSubmitX: function () {
// console.log('now in finalizeSubmit()');
// console.log('submittedTransaction : ' + this.submittedTransaction);
// console.log('submittedLinks : ' + this.submittedLinks);

View File

@ -267,8 +267,9 @@
v-on="$listeners"
:custom-fields.sync="customFields"
:index="index"
:submitted_transaction="submittedTransaction"
:transaction_journal_id="transaction.transaction_journal_id"
:upload-trigger="transaction.uploadTrigger"
:clear-trigger="transaction.clearTrigger"
/>
<TransactionLocation
v-model="transaction.location"
@ -347,11 +348,6 @@ export default {
type: String,
required: true
},
submittedTransaction: {
type: Boolean,
required: false,
default: false
}, // need to know if transaction is submitted.
sourceAllowedTypes: {
type: Array,
required: false,
@ -381,21 +377,24 @@ export default {
return this.date;
},
sourceAccount: function () {
// console.log('computed::sourceAccount');
//console.log('computed::sourceAccount(' + this.index + ')');
let value = {
id: this.transaction.source_account_id,
name: this.transaction.source_account_name,
type: this.transaction.source_account_type,
};
// console.log(JSON.stringify(value));
//console.log(JSON.stringify(value));
return value;
},
destinationAccount: function () {
return {
//console.log('computed::destinationAccount(' + this.index + ')');
let value = {
id: this.transaction.destination_account_id,
name: this.transaction.destination_account_name,
type: this.transaction.destination_account_type,
};
//console.log(JSON.stringify(value));
return value;
},
hasMetaFields: function () {
let requiredFields = [

View File

@ -23,7 +23,7 @@
<div v-if="visible" class="text-xs d-none d-lg-block d-xl-block">
<span v-if="0 === this.index">{{ $t('firefly.' + this.direction + '_account') }}</span>
<span v-if="this.index > 0" class="text-warning">{{ $t('firefly.first_split_overrules_' + this.direction) }}</span>
<!-- <br><span>{{ selectedAccount }}</span> -->
<!--<br><span>{{ selectedAccount }}</span>-->
</div>
<div v-if="!visible" class="text-xs d-none d-lg-block d-xl-block">
&nbsp;
@ -127,8 +127,8 @@ export default {
this.selectedAccount = event;
},
systemReturnedAccount: function (event) {
// console.log('systemReturnedAccount!');
// console.log('To prevent invalid propogation, set selectedAccountTrigger = false');
//console.log('systemReturnedAccount!');
//console.log('To prevent invalid propogation, set selectedAccountTrigger = false');
this.selectedAccountTrigger = false;
this.selectedAccount = event;
},
@ -192,7 +192,7 @@ export default {
* @param value
*/
selectedAccount: function (value) {
// console.log('TransactionAccount::watch selectedAccount()');
//console.log('TransactionAccount::watch selectedAccount()');
// console.log(value);
if (true === this.selectedAccountTrigger) {
// console.log('$emit alles!');
@ -208,9 +208,18 @@ export default {
currency_symbol: value.currency_symbol,
}
);
// console.log('watch::selectedAccount() will now set accountName because selectedAccountTrigger = true');
//console.log('watch::selectedAccount() will now set accountName because selectedAccountTrigger = true');
this.accountName = value.name;
}
if (false === this.selectedAccountTrigger) {
//console.log('watch::selectedAccount() will NOT set accountName because selectedAccountTrigger = false');
}
if (false === this.selectedAccountTrigger && this.accountName !== value.name && null !== value.name) {
//console.log('watch::selectedAccount() will set accountName. selectedAccountTrigger = false but name is different ("' + this.accountName + '" > "' + value.name + '")');
this.selectedAccountTrigger = true;
this.accountName = value.name;
}
},
accountName: function (value) {
// console.log('now at watch accountName("' + value + '")');
@ -238,7 +247,7 @@ export default {
this.selectedAccountTrigger = false;
},
value: function (value) {
// console.log('TransactionAccount::watch value(' + JSON.stringify(value) + ')');
//console.log('TransactionAccount(' + this.index + ')::watch value(' + JSON.stringify(value) + ')');
this.systemReturnedAccount(value);
// // console.log('Index ' + this.index + ' nwAct: ', value);

View File

@ -39,27 +39,29 @@
<script>
export default {
name: "TransactionAttachments",
props: ['transaction_journal_id', 'customFields'],
props: ['transaction_journal_id', 'customFields', 'index', 'uploadTrigger', 'clearTrigger'],
data() {
return {
availableFields: this.customFields
availableFields: this.customFields,
uploads: 0,
created: 0,
uploaded: 0,
}
},
watch: {
customFields: function (value) {
this.availableFields = value;
},
uploadTrigger: function () {
//console.log('uploadTrigger(' + this.transaction_journal_id + ',' + this.index + ')');
this.doUpload();
},
clearTrigger: function () {
//console.log('clearTrigger(' + this.transaction_journal_id + ',' + this.index + ')');
this.$refs.att.value = null;
},
transaction_journal_id: function (value) {
if (!this.showField) {
// console.log('Field is hidden. Emit event!');
this.$emit('uploaded-attachments', value);
return;
}
// console.log('transaction_journal_id changed to ' + value);
// do upload!
if (0 !== value) {
this.doUpload();
}
//console.log('watch transaction_journal_id: ' + value + ' (index ' + this.index + ')');
}
},
computed: {
@ -71,47 +73,65 @@ export default {
}
},
methods: {
selectedFile: function() {
this.$emit('selected-attachments', this.transaction_journal_id);
selectedFile: function () {
this.$emit('selected-attachments', {index: this.index, id: this.transaction_journal_id});
},
createAttachment: function (name) {
// console.log('Now in createAttachment()');
const uri = './api/v1/attachments';
const data = {
filename: name,
attachable_type: 'TransactionJournal',
attachable_id: this.transaction_journal_id,
};
// create new attachment:
return axios.post(uri, data);
},
uploadAttachment: function (attachmentId, data) {
this.created++;
// console.log('Now in uploadAttachment()');
const uploadUri = './api/v1/attachments/' + attachmentId + '/upload';
return axios.post(uploadUri, data)
},
countAttachment: function () {
this.uploaded++;
//console.log('Uploaded ' + this.uploaded + ' / ' + this.uploads);
if (this.uploaded >= this.uploads) {
//console.log('All files uploaded. Emit event for ' + this.transaction_journal_id + '(' + this.index + ')');
this.$emit('uploaded-attachments', this.transaction_journal_id);
}
},
doUpload: function () {
// console.log('Now in doUpload() for ' + this.$refs.att.files.length + ' files.');
for (let i in this.$refs.att.files) {
if (this.$refs.att.files.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = this.$refs.att.files[i];
let files = this.$refs.att.files;
this.uploads = files.length;
// loop all files and create attachments.
for (let i in files) {
if (files.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
// console.log('Now at file ' + (parseInt(i) + 1) + ' / ' + files.length);
// read file into file reader:
let current = files[i];
let fileReader = new FileReader();
let theParent = this; // dont ask me why i need to do this.
fileReader.onloadend = function (evt) {
fileReader.onloadend = evt => {
if (evt.target.readyState === FileReader.DONE) {
// do upload here
const uri = './api/v1/attachments';
const data = {
filename: current.name,
attachable_type: 'TransactionJournal',
attachable_id: theParent.transaction_journal_id,
};
// create new attachment:
axios.post(uri, data).then(response => {
// upload actual file:
const uploadUri = './api/v1/attachments/' + response.data.data.id + '/upload';
axios
.post(uploadUri, new Blob([evt.target.result]))
.then(attachmentResponse => {
// TODO feedback etc.
// console.log('Uploaded a file. Emit event!');
// console.log(attachmentResponse);
theParent.$emit('uploaded-attachments', this.transaction_journal_id);
});
});
// console.log('I am done reading file ' + (parseInt(i) + 1));
this.createAttachment(current.name).then(response => {
// console.log('Created attachment. Now upload (1)');
return theParent.uploadAttachment(response.data.data.id, new Blob([evt.target.result]));
}).then(theParent.countAttachment);
}
}
fileReader.readAsArrayBuffer(current);
}
}
if (0 === this.$refs.att.files.length) {
// console.log('No files to upload. Emit event!');
if (0 === files.length) {
//console.log('No files to upload. Emit event!');
this.$emit('uploaded-attachments', this.transaction_journal_id);
}
// Promise.all(promises).then(response => {
// console.log('All files uploaded. Emit event!');
// this.$emit('uploaded-attachments', this.transaction_journal_id);
// });
}
}

View File

@ -61,13 +61,12 @@ export default {
return {
categories: [],
initialSet: [],
category: this.value,
emitEvent: true
category: this.value
}
},
created() {
//console.log('Created category(' + this.index + ') "' + this.value + '"');
// initial list of accounts:
axios.get(this.getACURL(''))
.then(response => {
@ -82,11 +81,13 @@ export default {
},
getACURL: function (query) {
// update autocomplete URL:
// console.log('getACURL("' + query + '")');
return document.getElementsByTagName('base')[0].href + 'api/v1/autocomplete/categories?query=' + query;
},
lookupCategory: debounce(function () {
// update autocomplete URL:
axios.get(this.getACURL(this.value))
//console.log('Do a search for "'+this.category+'"');
axios.get(this.getACURL(this.category))
.then(response => {
this.categories = response.data;
})
@ -94,7 +95,6 @@ export default {
},
watch: {
value: function (value) {
this.emitEvent = false;
this.category = value ?? '';
},
category: function (value) {

View File

@ -19,7 +19,7 @@
-->
<template>
<div class="form-group">
<div class="form-group" v-if="0===index">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.date_and_time') }}
</div>
@ -28,7 +28,6 @@
ref="date"
v-model="dateStr"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:disabled="index > 0"
:placeholder="dateStr"
:title="$t('firefly.date')"
autocomplete="off"
@ -39,7 +38,6 @@
ref="time"
v-model="timeStr"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:disabled="index > 0"
:placeholder="timeStr"
:title="$t('firefly.time')"
autocomplete="off"

View File

@ -141,7 +141,10 @@
"next_expected_match": "\u0421\u043b\u0435\u0434\u0432\u0430\u0449o \u043e\u0447\u0430\u043a\u0432\u0430\u043do \u0441\u044a\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435"
},
"config": {
"html_language": "bg"
"html_language": "bg",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "\u0421\u0443\u043c\u0430 \u0432\u044a\u0432 \u0432\u0430\u043b\u0443\u0442\u0430",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Dal\u0161\u00ed o\u010dek\u00e1van\u00e1 shoda"
},
"config": {
"html_language": "cs"
"html_language": "cs",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "\u010c\u00e1stka v ciz\u00ed m\u011bn\u011b",

View File

@ -141,7 +141,10 @@
"next_expected_match": "N\u00e4chste erwartete \u00dcbereinstimmung"
},
"config": {
"html_language": "de"
"html_language": "de",
"week_in_year_fns": "'Woche' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Ausl\u00e4ndischer Betrag",

View File

@ -141,7 +141,10 @@
"next_expected_match": "\u0395\u03c0\u03cc\u03bc\u03b5\u03bd\u03b7 \u03b1\u03bd\u03b1\u03bc\u03b5\u03bd\u03cc\u03bc\u03b5\u03bd\u03b7 \u03b1\u03bd\u03c4\u03b9\u03c3\u03c4\u03bf\u03af\u03c7\u03b9\u03c3\u03b7"
},
"config": {
"html_language": "el"
"html_language": "el",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "\u03a0\u03bf\u03c3\u03cc \u03c3\u03b5 \u03be\u03ad\u03bd\u03bf \u03bd\u03cc\u03bc\u03b9\u03c3\u03bc\u03b1",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Next expected match"
},
"config": {
"html_language": "en-gb"
"html_language": "en-gb",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Foreign amount",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Next expected match"
},
"config": {
"html_language": "en"
"html_language": "en",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Foreign amount",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Pr\u00f3xima coincidencia esperada"
},
"config": {
"html_language": "es"
"html_language": "es",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Cantidad extranjera",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Seuraava lasku odotettavissa"
},
"config": {
"html_language": "fi"
"html_language": "fi",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Ulkomaan summa",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Prochaine association attendue"
},
"config": {
"html_language": "fr"
"html_language": "fr",
"week_in_year_fns": "'Semaine' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Montant en devise \u00e9trang\u00e8re",

View File

@ -141,7 +141,10 @@
"next_expected_match": "K\u00f6vetkez\u0151 v\u00e1rhat\u00f3 egyez\u00e9s"
},
"config": {
"html_language": "hu"
"html_language": "hu",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "K\u00fclf\u00f6ldi \u00f6sszeg",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Prossimo abbinamento previsto"
},
"config": {
"html_language": "it"
"html_language": "it",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Importo estero",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Neste forventede treff"
},
"config": {
"html_language": "nb"
"html_language": "nb",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Utenlandske bel\u00f8p",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Volgende verwachte match"
},
"config": {
"html_language": "nl"
"html_language": "nl",
"week_in_year_fns": "'week' l, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Bedrag in vreemde valuta",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Nast\u0119pne oczekiwane dopasowanie"
},
"config": {
"html_language": "pl"
"html_language": "pl",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Kwota zagraniczna",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Pr\u00f3ximo correspondente esperado"
},
"config": {
"html_language": "pt-br"
"html_language": "pt-br",
"week_in_year_fns": "'Semana' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Montante em moeda estrangeira",

View File

@ -7,7 +7,7 @@
"no_currency": "(sem moeda)",
"date": "Data",
"time": "Hora",
"no_budget": "(sem orcamento)",
"no_budget": "(sem or\u00e7amento)",
"destination_account": "Conta de destino",
"source_account": "Conta de origem",
"single_split": "Dividir",
@ -16,12 +16,12 @@
"transaction_journal_extra": "Informa\u00e7\u00f5es extra",
"transaction_journal_meta": "Meta informa\u00e7\u00e3o",
"basic_journal_information": "Informa\u00e7\u00f5es b\u00e1sicas de transa\u00e7\u00e3o",
"bills_to_pay": "Contas por pagar",
"bills_to_pay": "Faturas a pagar",
"left_to_spend": "Restante para gastar",
"attachments": "Anexos",
"net_worth": "Patrimonio liquido",
"bill": "Conta",
"no_bill": "(sem contas)",
"bill": "Fatura",
"no_bill": "(sem fatura)",
"tags": "Etiquetas",
"internal_reference": "Refer\u00eancia interna",
"external_url": "URL Externo",
@ -46,8 +46,8 @@
"go_to_categories": "Ir para categorias",
"expense_accounts": "Conta de despesas",
"go_to_expenses": "Ir para despesas",
"go_to_bills": "Ir para contas",
"bills": "Contas",
"go_to_bills": "Ir para as faturas",
"bills": "Faturas",
"last_thirty_days": "\u00daltimos trinta dias",
"last_seven_days": "\u00daltimos sete dias",
"go_to_piggies": "Ir para mealheiros",
@ -141,7 +141,10 @@
"next_expected_match": "Proxima correspondencia esperada"
},
"config": {
"html_language": "pt"
"html_language": "pt",
"week_in_year_fns": "'Semana' I, yyyy",
"quarter_fns": "'Trimestre' Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Montante estrangeiro",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Urm\u0103toarea potrivire a\u0219teptat\u0103"
},
"config": {
"html_language": "ro"
"html_language": "ro",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Sum\u0103 str\u0103in\u0103",

View File

@ -141,7 +141,10 @@
"next_expected_match": "\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442"
},
"config": {
"html_language": "ru"
"html_language": "ru",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "\u0421\u0443\u043c\u043c\u0430 \u0432 \u0438\u043d\u043e\u0441\u0442\u0440\u0430\u043d\u043d\u043e\u0439 \u0432\u0430\u043b\u044e\u0442\u0435",

View File

@ -141,7 +141,10 @@
"next_expected_match": "\u010eal\u0161ia o\u010dak\u00e1van\u00e1 zhoda"
},
"config": {
"html_language": "sk"
"html_language": "sk",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Suma v cudzej mene",

View File

@ -141,7 +141,10 @@
"next_expected_match": "N\u00e4sta f\u00f6rv\u00e4ntade tr\u00e4ff"
},
"config": {
"html_language": "sv"
"html_language": "sv",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Utl\u00e4ndskt belopp",

View File

@ -141,7 +141,10 @@
"next_expected_match": "Tr\u1eadn \u0111\u1ea5u d\u1ef1 ki\u1ebfn ti\u1ebfp theo"
},
"config": {
"html_language": "vi"
"html_language": "vi",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Ngo\u1ea1i t\u1ec7",

View File

@ -120,13 +120,13 @@
"mandatoryFields": "\u5fc5\u586b\u5b57\u6bb5",
"optionalFields": "\u9009\u586b\u5b57\u6bb5",
"reconcile_this_account": "\u5bf9\u8d26\u6b64\u8d26\u6237",
"interest_calc_weekly": "Per week",
"interest_calc_weekly": "\u6bcf\u5468",
"interest_calc_monthly": "\u6bcf\u6708",
"interest_calc_quarterly": "Per quarter",
"interest_calc_half-year": "Per half year",
"interest_calc_quarterly": "\u6bcf\u5b63\u5ea6",
"interest_calc_half-year": "\u6bcf\u534a\u5e74",
"interest_calc_yearly": "\u6bcf\u5e74",
"liability_direction_credit": "I am owed this debt",
"liability_direction_debit": "I owe this debt to somebody else",
"liability_direction_credit": "\u6211\u6b20\u4e86\u8fd9\u7b14\u503a\u52a1",
"liability_direction_debit": "\u6211\u6b20\u522b\u4eba\u8fd9\u7b14\u94b1",
"save_transactions_by_moving_js": "No transactions|Save this transaction by moving it to another account. |Save these transactions by moving them to another account.",
"none_in_select_list": "(\u7a7a)"
},
@ -141,7 +141,10 @@
"next_expected_match": "\u9884\u671f\u4e0b\u6b21\u652f\u4ed8"
},
"config": {
"html_language": "zh-cn"
"html_language": "zh-cn",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "\u5916\u5e01\u91d1\u989d",
@ -163,7 +166,7 @@
"interest": "\u5229\u606f",
"interest_period": "\u5229\u606f\u671f",
"currency_id": "\u8d27\u5e01",
"liability_type": "Liability type",
"liability_type": "\u503a\u52a1\u7c7b\u578b",
"account_role": "\u8d26\u6237\u89d2\u8272",
"liability_direction": "Liability in\/out",
"book_date": "\u767b\u8bb0\u65e5\u671f",

View File

@ -141,7 +141,10 @@
"next_expected_match": "\u4e0b\u4e00\u500b\u9810\u671f\u7684\u914d\u5c0d"
},
"config": {
"html_language": "zh-tw"
"html_language": "zh-tw",
"week_in_year_fns": "'Week' I, yyyy",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "\u5916\u5e63\u91d1\u984d",

View File

@ -62,6 +62,10 @@ export function getDefaultTransaction() {
destination_account_currency_id: null,
destination_account_currency_code: null,
destination_account_currency_symbol: null,
attachments: false,
selectedAttachments: false,
uploadTrigger: false,
clearTrigger: false,
source_account: {
id: 0,
@ -112,7 +116,6 @@ export function getDefaultTransaction() {
// transaction links:
links: [],
attachments: [],
// location:
zoom_level: null,
longitude: null,

View File

@ -31,6 +31,7 @@ const mix = require('laravel-mix');
|
*/
mix.setResourceRoot('./');
// production
// require('laravel-mix-bundle-analyzer');
@ -75,7 +76,7 @@ mix.sass('src/app.scss', 'public/css', {
// move to right dir
mix.copy('public/js', '../public/v2/js')
.copy('fonts', '../public/fonts')
.copy('fonts', '../public/v2/css/fonts')
.copy('images', '../public/images')
.copy('public/css', '../public/v2/css');

View File

@ -965,9 +965,9 @@
"@types/estree" "*"
"@types/eslint@*":
version "7.2.9"
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.9.tgz#5d26eadbb6d04a225967176399a18eff622da982"
integrity sha512-SdAAXZNvWfhtf3X3y1cbbCZhP3xyPh7mfTvzV6CgfWc/ZhiHpyr9bVroe2/RCHIf7gczaNcprhaBLsx0CCJHQA==
version "7.2.10"
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.10.tgz#4b7a9368d46c0f8cd5408c23288a59aa2394d917"
integrity sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ==
dependencies:
"@types/estree" "*"
"@types/json-schema" "*"
@ -1056,9 +1056,9 @@
integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==
"@types/node@*":
version "14.14.37"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e"
integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==
version "14.14.41"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615"
integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==
"@types/parse-glob@*":
version "3.0.29"
@ -1285,9 +1285,9 @@ acorn@^7.0.0:
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.0.4:
version "8.1.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe"
integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==
version "8.1.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.1.tgz#fb0026885b9ac9f48bac1e185e4af472971149ff"
integrity sha512-xYiIVjNuqtKXMxlRMDc6mZUhXehod4a3gbZ1qRlM7icK4EbxUFNLhWoPblCvFtB2Y9CIqHP3CF/rdxLItaQv8g==
adjust-sourcemap-loader@3.0.0:
version "3.0.0"
@ -1902,15 +1902,15 @@ browserify-zlib@^0.2.0:
pako "~1.0.5"
browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.3:
version "4.16.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717"
integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==
version "4.16.4"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.4.tgz#7ebf913487f40caf4637b892b268069951c35d58"
integrity sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ==
dependencies:
caniuse-lite "^1.0.30001181"
colorette "^1.2.1"
electron-to-chromium "^1.3.649"
caniuse-lite "^1.0.30001208"
colorette "^1.2.2"
electron-to-chromium "^1.3.712"
escalade "^3.1.1"
node-releases "^1.1.70"
node-releases "^1.1.71"
bs-custom-file-input@^1.3.4:
version "1.3.4"
@ -2041,10 +2041,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001196:
version "1.0.30001208"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz#a999014a35cebd4f98c405930a057a0d75352eb9"
integrity sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001196, caniuse-lite@^1.0.30001208:
version "1.0.30001209"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001209.tgz#1bb4be0bd118e98e21cfb7ef617b1ef2164622f4"
integrity sha512-2Ktt4OeRM7EM/JaOZjuLzPYAIqmbwQMNnYbgooT+icoRGrKOyAxA1xhlnotBD1KArRSPsuJp3TdYcZYrL7qNxA==
chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
@ -2077,9 +2077,9 @@ chart.js@^2.9.4:
moment "^2.10.2"
chart.js@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.1.0.tgz#b99cfe712fa0059134a4ad3a3515135fbb20bcea"
integrity sha512-bKJi2VbC4fqZXlLbK7LKVvmG9crjoG9anfp96utZLyIGPuCx+YN+5/HDXy98QGt3lf74T8gKUPISUZL222tDJQ==
version "3.1.1"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.1.1.tgz#2cdbda7fccea532313332fe822f0cae268f24cf3"
integrity sha512-ghNJersc9VD9MECwa5bL8gqvCkndW6RSCicdEHL9lIriNtXwKawlSmwo+u6KNXLYT2+f24GdFPBoynKW3ke4MQ==
chartjs-color-string@^0.6.0:
version "0.6.0"
@ -2096,7 +2096,7 @@ chartjs-color@^2.1.0:
chartjs-color-string "^0.6.0"
color-convert "^1.9.3"
"chokidar@>=2.0.0 <4.0.0", chokidar@^3.4.3, chokidar@^3.5.1:
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.3, chokidar@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==
@ -2552,22 +2552,21 @@ css-declaration-sorter@^4.0.1:
timsort "^0.3.0"
css-loader@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.1.tgz#15fbd5b6ac4c1b170a098f804c5abd0722f2aa73"
integrity sha512-YCyRzlt/jgG1xanXZDG/DHqAueOtXFHeusP9TS478oP1J++JSKOyEgGW1GHVoCj/rkS+GWOlBwqQJBr9yajQ9w==
version "5.2.2"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.2.tgz#65f2c1482255f15847ecad6cbc515cae8a5b234e"
integrity sha512-IS722y7Lh2Yq+acMR74tdf3faMOLRP2RfLwS0VzSS7T98IHtacMWJLku3A0OBTFHB07zAa4nWBhA8gfxwQVWGQ==
dependencies:
camelcase "^6.2.0"
cssesc "^3.0.0"
icss-utils "^5.1.0"
loader-utils "^2.0.0"
postcss "^8.2.8"
postcss "^8.2.10"
postcss-modules-extract-imports "^3.0.0"
postcss-modules-local-by-default "^4.0.0"
postcss-modules-scope "^3.0.0"
postcss-modules-values "^4.0.0"
postcss-value-parser "^4.1.0"
schema-utils "^3.0.0"
semver "^7.3.4"
semver "^7.3.5"
css-select-base-adapter@^0.1.1:
version "0.1.1"
@ -2945,14 +2944,14 @@ datatables.net@1.10.24, datatables.net@^1.10.15, datatables.net@^1.10.24:
jquery ">=1.7"
date-fns-tz@^1.0.12:
version "1.1.3"
resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-1.1.3.tgz#8783489c9d90e5785548869af5bb8c4642a03855"
integrity sha512-mD26WkejWz842RggjFrKsY6ehGgyBQSJ209mn83/vsjhgQ5WbdVvBzJ0CuosnGdklDxOvOppQ/wn1UgvTOPKPw==
version "1.1.4"
resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-1.1.4.tgz#38282c2bfab08946a4e9bb89d733451e5525048b"
integrity sha512-lQ+FF7xUxxRuRqIY7H/lagnT3PhhSnnvtGHzjE5WZKwRyLU7glJfLys05SZ7zHlEr6RXWiqkmgWq4nCkcElR+g==
date-fns@^2.8.1:
version "2.20.1"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.20.1.tgz#7e60b7035284a5f83e37500376e738d9f49ecfd3"
integrity sha512-8P5M8Kxbnovd0zfvOs7ipkiVJ3/zZQ0F/nrBW4x5E+I0uAZVZ80h6CKd24fSXQ5TLK5hXMtI4yb2O5rEZdUt2A==
date-fns@^2.21.1, date-fns@^2.8.1:
version "2.21.1"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.21.1.tgz#679a4ccaa584c0706ea70b3fa92262ac3009d2b0"
integrity sha512-m1WR0xGiC6j6jNFAyW4Nvh4WxAi4JF4w9jRJwSI8nBmNcyZXPcP9VUQG+6gHQXAmqaGEKDKhOqAtENDC941UkA==
daterangepicker@^3.1.0:
version "3.1.0"
@ -3163,10 +3162,10 @@ domhandler@^3.0.0:
dependencies:
domelementtype "^2.0.1"
domhandler@^4.0.0, domhandler@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.1.0.tgz#c1d8d494d5ec6db22de99e46a149c2a4d23ddd43"
integrity sha512-/6/kmsGlMY4Tup/nGVutdrK9yQi4YjWVcVeoQmixpzjOUK1U7pQkvAPHBJeUxOgxF0J8f8lwCJSlCfD0V4CMGQ==
domhandler@^4.0.0, domhandler@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059"
integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==
dependencies:
domelementtype "^2.2.0"
@ -3179,13 +3178,13 @@ domutils@^1.7.0:
domelementtype "1"
domutils@^2.0.0:
version "2.5.2"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.2.tgz#37ef8ba087dff1a17175e7092e8a042e4b050e6c"
integrity sha512-MHTthCb1zj8f1GVfRpeZUbohQf/HdBos0oX5gZcQFepOZPLLRyj6Wn7XS7EMnY7CVpwv8863u2vyE83Hfu28HQ==
version "2.6.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.6.0.tgz#2e15c04185d43fb16ae7057cb76433c6edb938b7"
integrity sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.2.0"
domhandler "^4.1.0"
domhandler "^4.2.0"
dot-case@^3.0.4:
version "3.0.4"
@ -3234,10 +3233,10 @@ ekko-lightbox@^5.3.0:
resolved "https://registry.yarnpkg.com/ekko-lightbox/-/ekko-lightbox-5.3.0.tgz#fbfcd9df93a8d1cdbf8770adc8c05aaac4d24f56"
integrity sha512-mbacwySuVD3Ad6F2hTkjSTvJt59bcVv2l/TmBerp4xZnLak8tPtA4AScUn4DL42c1ksTiAO6sGhJZ52P/1Qgew==
electron-to-chromium@^1.3.649:
version "1.3.712"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.712.tgz#ae467ffe5f95961c6d41ceefe858fc36eb53b38f"
integrity sha512-3kRVibBeCM4vsgoHHGKHmPocLqtFAGTrebXxxtgKs87hNUzXrX2NuS3jnBys7IozCnw7viQlozxKkmty2KNfrw==
electron-to-chromium@^1.3.712:
version "1.3.717"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.717.tgz#78d4c857070755fb58ab64bcc173db1d51cbc25f"
integrity sha512-OfzVPIqD1MkJ7fX+yTl2nKyOE4FReeVfMCzzxQS+Kp43hZYwHwThlGP+EGIZRXJsxCM7dqo8Y65NOX/HP12iXQ==
elliptic@^6.5.3:
version "6.5.4"
@ -3894,9 +3893,9 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1:
has-symbols "^1.0.1"
get-stream@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718"
integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==
version "6.0.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
@ -4181,9 +4180,9 @@ http-parser-js@>=0.5.1:
integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==
http-proxy-middleware@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.1.1.tgz#48900a68cd9d388c735d1dd97302c919b7e94a13"
integrity sha512-FIDg9zPvOwMhQ3XKB2+vdxK6WWbVAH7s5QpqQCif7a1TNL76GNAATWA1sy6q2gSfss8UJ/Nwza3N6QnFkKclpA==
version "1.1.2"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.1.2.tgz#38d062ce4182b2931442efc2d9a0c429cab634f8"
integrity sha512-YRFUeOG3q85FJjAaYVJUoNRW9a73SDlOtAyQOS5PHLr18QeZ/vEhxywNoOPiEO8BxCegz4RXzTHcvyLEGB78UA==
dependencies:
"@types/http-proxy" "^1.17.5"
http-proxy "^1.18.1"
@ -5222,9 +5221,9 @@ mimic-fn@^3.1.0:
integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==
mini-css-extract-plugin@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.4.1.tgz#975e27c1d0bd8e052972415f47c79cea5ed37548"
integrity sha512-COAGbpAsU0ioFzj+/RRfO5Qv177L1Z/XAx2EmCF33b8GDDqKygMffBTws2lit8iaPdrbKEY5P+zsseBUCREZWQ==
version "1.5.0"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.5.0.tgz#69bee3b273d2d4ee8649a2eb409514b7df744a27"
integrity sha512-SIbuLMv6jsk1FnLIU5OUG/+VMGUprEjM1+o2trOAx8i5KOKMrhyezb1dJ4Ugsykb8Jgq8/w5NEopy6escV9G7g==
dependencies:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
@ -5413,7 +5412,7 @@ node-notifier@^9.0.0:
uuid "^8.3.0"
which "^2.0.2"
node-releases@^1.1.70:
node-releases@^1.1.71:
version "1.1.71"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb"
integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==
@ -5469,9 +5468,9 @@ object-copy@^0.1.0:
kind-of "^3.0.3"
object-inspect@^1.6.0, object-inspect@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a"
integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==
version "1.10.2"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.2.tgz#b6385a3e2b7cae0b5eafcf90cddf85d128767f30"
integrity sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==
object-is@^1.0.1:
version "1.1.5"
@ -6183,7 +6182,7 @@ postcss@7.0.21:
source-map "^0.6.1"
supports-color "^6.1.0"
postcss@^8.1.14, postcss@^8.2.8:
postcss@^8.1.14, postcss@^8.2.10:
version "8.2.10"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.10.tgz#ca7a042aa8aff494b334d0ff3e9e77079f6f702b"
integrity sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw==
@ -6641,11 +6640,11 @@ sass-loader@^11.0.1:
neo-async "^2.6.2"
sass@^1.32.8:
version "1.32.8"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.8.tgz#f16a9abd8dc530add8834e506878a2808c037bdc"
integrity sha512-Sl6mIeGpzjIUZqvKnKETfMf0iDAswD9TNlv13A7aAF3XZlRPMq4VvJWBC2N2DXbp94MQVdNSFG6LfF/iOXrPHQ==
version "1.32.10"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.10.tgz#d40da4e20031b450359ee1c7e69bc8cc89569241"
integrity sha512-Nx0pcWoonAkn7CRp0aE/hket1UP97GiR1IFw3kcjV3pnenhWgZEWUf0ZcfPOV2fK52fnOcK3JdC/YYZ9E47DTQ==
dependencies:
chokidar ">=2.0.0 <4.0.0"
chokidar ">=3.0.0 <4.0.0"
sax@^1.2.1, sax@~1.2.4:
version "1.2.4"
@ -6710,7 +6709,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.2, semver@^7.3.4:
semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
@ -7182,9 +7181,9 @@ svgo@^1.0.0:
util.promisify "~1.0.0"
sweetalert2@^10.15.6:
version "10.16.0"
resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-10.16.0.tgz#33d527af90689baab1078bf447b1986b796d4c75"
integrity sha512-qJp4nIpx0MPDnAdY6MurwH/mAovfCzWo07hFoQ41C46XGLpNkxiqBbJDNdhGfMFwbDY1e9jfttNFxe5IsrkEtA==
version "10.16.3"
resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-10.16.3.tgz#501777087681c25f9894eeba807b1c1ae8b021bf"
integrity sha512-ZSr+U9crlg1+wIf+S7BGBO5LFlNFRh+UM6ZURGPX5o1h14hJs6UdOce059VQz7jzuFB+xsO5TSvShNvp5YTqGQ==
tapable@^2.1.1, tapable@^2.2.0:
version "2.2.0"
@ -7772,9 +7771,9 @@ webpack-sources@^2.1.1:
source-map "^0.6.1"
webpack@^5.25.1, webpack@^5.30.0:
version "5.31.2"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.31.2.tgz#40d9b9d15b7d76af73d3f1cae895b82613a544d6"
integrity sha512-0bCQe4ybo7T5Z0SC5axnIAH+1WuIdV4FwLYkaAlLtvfBhIx8bPS48WHTfiRZS1VM+pSiYt7e/rgLs3gLrH82lQ==
version "5.33.2"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.33.2.tgz#c049717c9b038febf5a72fd2f53319ad59a8c1fc"
integrity sha512-X4b7F1sYBmJx8mlh2B7mV5szEkE0jYNJ2y3akgAP0ERi0vLCG1VvdsIxt8lFd4st6SUy0lf7W0CCQS566MBpJg==
dependencies:
"@types/eslint-scope" "^3.7.0"
"@types/estree" "^0.0.46"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

Before

Width:  |  Height:  |  Size: 730 KiB

After

Width:  |  Height:  |  Size: 730 KiB

View File

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

Before

Width:  |  Height:  |  Size: 898 KiB

After

Width:  |  Height:  |  Size: 898 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -16,7 +16,7 @@
*/
/*!
* Chart.js v3.1.0
* Chart.js v3.1.1
* https://www.chartjs.org
* (c) 2021 Chart.js Contributors
* Released under the MIT License

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,7 @@
"transaction_new_stored_link": "<a href=\"transactions\/show\/{ID}\">Transa\u00e7\u00e3o#{ID}<\/a> foi guardada.",
"transaction_journal_information": "Informa\u00e7\u00e3o da transa\u00e7\u00e3o",
"no_budget_pointer": "Parece que ainda n\u00e3o tem or\u00e7amentos. Pode criar-los na p\u00e1gina de <a href=\"budgets\">or\u00e7amentos<\/a>. Or\u00e7amentos podem ajud\u00e1-lo a controlar as despesas.",
"no_bill_pointer": "Parece que ainda n\u00e3o tem contas. Pode criar-las na p\u00e1gina de <a href=\"bills\">contas<\/a>. Contas podem ajud\u00e1-lo a controlar as despesas.",
"no_bill_pointer": "Parece que ainda n\u00e3o tem faturas. Pode criar-las na p\u00e1gina de <a href=\"bills\">faturas<\/a>. Faturas podem ajud\u00e1-lo a controlar as despesas.",
"source_account": "Conta de origem",
"hidden_fields_preferences": "Pode ativar mais op\u00e7\u00f5es de transa\u00e7\u00f5es nas suas <a href=\"preferences\">prefer\u00eancias<\/a>.",
"destination_account": "Conta de destino",
@ -25,8 +25,8 @@
"amount": "Montante",
"date": "Data",
"tags": "Etiquetas",
"no_budget": "(sem orcamento)",
"no_bill": "(sem contas)",
"no_budget": "(sem or\u00e7amento)",
"no_bill": "(sem fatura)",
"category": "Categoria",
"attachments": "Anexos",
"notes": "Notas",
@ -42,7 +42,7 @@
"destination_account_reconciliation": "N\u00e3o pode editar a conta de destino de uma transac\u00e7\u00e3o de reconcilia\u00e7\u00e3o.",
"source_account_reconciliation": "N\u00e3o pode editar a conta de origem de uma transac\u00e7\u00e3o de reconcilia\u00e7\u00e3o.",
"budget": "Orcamento",
"bill": "Conta",
"bill": "Fatura",
"you_create_withdrawal": "Est\u00e1 a criar um levantamento.",
"you_create_transfer": "Est\u00e1 a criar uma transfer\u00eancia.",
"you_create_deposit": "Est\u00e1 a criar um dep\u00f3sito.",

View File

@ -40,8 +40,11 @@ return [
'date_time_js' => 'Do MMMM, YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'week_in_year_fns' => "'Week' I, yyyy",
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'quarter_fns' => "'Q'Q, yyyy",
'half_year_fns' => "'H{half}', yyyy",
'dow_1' => 'Понеделник',
'dow_2' => 'Вторник',
'dow_3' => 'Сряда',

View File

@ -40,8 +40,11 @@ return [
'date_time_js' => 'D. MMMM YYYY, @ HH:mm:ss',
'specific_day_js' => 'D. MMMM YYYY',
'week_in_year_js' => '[Week] t, RRRR',
'week_in_year_fns' => "'Week' I, yyyy",
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'quarter_fns' => "'Q'Q, yyyy",
'half_year_fns' => "'H{half}', yyyy",
'dow_1' => 'Pondělí',
'dow_2' => 'Úterý',
'dow_3' => 'Středa',

View File

@ -40,8 +40,11 @@ return [
'date_time_js' => 'Do MMMM YYYY um HH:mm:ss',
'specific_day_js' => 'D. MMMM YYYY',
'week_in_year_js' => '[Week]. KW, YYYY',
'week_in_year_fns' => "'Woche' I, yyyy",
'year_js' => 'YYYY',
'half_year_js' => 'Q. Quartal YYYY',
'quarter_fns' => "'Q'Q, yyyy",
'half_year_fns' => "'H{half}', yyyy",
'dow_1' => 'Montag',
'dow_2' => 'Dienstag',
'dow_3' => 'Mittwoch',

View File

@ -40,8 +40,11 @@ return [
'date_time_js' => 'Do MMMM YYYY, HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'week_in_year_fns' => "'Week' I, yyyy",
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'quarter_fns' => "'Q'Q, yyyy",
'half_year_fns' => "'H{half}', yyyy",
'dow_1' => 'Δευτέρα',
'dow_2' => 'Τρίτη',
'dow_3' => 'Τετάρτη',

View File

@ -298,8 +298,8 @@ return [
'search_modifier_has_any_category' => 'Η συναλλαγή πρέπει να έχει μία (οποιαδήποτε) κατηγορία',
'search_modifier_has_no_budget' => 'Η συναλλαγή δεν πρέπει να έχει προϋπολογισμό',
'search_modifier_has_any_budget' => 'Η συναλλαγή πρέπει να έχει έναν (οποιοδήποτε) προϋπολογισμό',
'search_modifier_has_no_bill' => 'The transaction must have no bill',
'search_modifier_has_any_bill' => 'The transaction must have a (any) bill',
'search_modifier_has_no_bill' => 'Η συναλλαγή δεν πρέπει να έχει λογαριασμό',
'search_modifier_has_any_bill' => 'Η συναλλαγή πρέπει να έχει έναν (οποιοδήποτε) λογαριασμό',
'search_modifier_has_no_tag' => 'Η συναλλαγή δεν πρέπει να έχει καμία ετικέτα',
'search_modifier_has_any_tag' => 'Η συναλλαγή πρέπει να έχει μία (οποιαδήποτε) ετικέτα',
'search_modifier_notes_contain' => 'Οι σημειώσεις της συναλλαγής περιέχουν ":value"',
@ -1862,8 +1862,8 @@ return [
'edit_object_group' => 'Επεξεργασία ομάδας ":title"',
'delete_object_group' => 'Διαγραφή ομάδας ":title"',
'update_object_group' => 'Ενημέρωση ομάδας',
'updated_object_group' => 'Successfully updated group ":title"',
'deleted_object_group' => 'Successfully deleted group ":title"',
'updated_object_group' => 'Επιτυχής ενημέρωση της ομάδας ":title"',
'deleted_object_group' => 'Επιτυχής διαγραφή της ομάδας ":title"',
'object_group' => 'Ομάδα',

View File

@ -27,12 +27,12 @@ return [
'zero_or_more' => 'Αυτή η τιμή δεν μπορεί να είναι αρνητική.',
'date_or_time' => 'Αυτή η τιμή πρέπει να είναι έγκυρη ημερομηνία ή τιμή ώρας (ISO 8601).',
'source_equals_destination' => 'Ο λογαριασμός προέλευσης ισούται με το λογαριασμό προορισμού.',
'unique_account_number_for_user' => 'Φαίνεται πως αυτός ο αριθμός λογαριασμού χρησιμοποιήται ήδη.',
'unique_account_number_for_user' => 'Φαίνεται πως αυτός ο αριθμός λογαριασμού χρησιμοποιείται ήδη.',
'unique_iban_for_user' => 'Φαίνεται πως αυτό το IBAN είναι ήδη σε χρήση.',
'deleted_user' => 'Για λόγους ασφαλείας, δεν μπορείτε να εγγραφείτε χρησιμοποιώντας αυτή τη διεύθυνση email.',
'rule_trigger_value' => 'Αυτή η τιμή δεν είναι έγκυρη για το επιλεγμένο κριτήριο ενεργοποίησης.',
'rule_action_value' => 'Αυτή η τιμή δεν είναι έγκυρη για την επιλεγμένη ενέργεια.',
'file_already_attached' => 'Το μεταφορτωμένο αρχείο ":name" είναι ήδη συννημένο σε αυτό το αντικείμενο.',
'file_already_attached' => 'Το μεταφορτωμένο αρχείο ":name" είναι ήδη συνημμένο σε αυτό το αντικείμενο.',
'file_attached' => 'Επιτυχής μεταφόρτωση του αρχείου ":name.',
'must_exist' => 'Το αναγνωριστικό στο πεδίο :attribute δεν υπάρχει στη βάση δεδομένων.',
'all_accounts_equal' => 'Όλοι οι λογαριασμοί σε αυτό το πεδίο πρέπει να είναι ίσοι.',
@ -134,8 +134,8 @@ return [
'starts_with' => 'Η τιμή πρέπει να ξεκινά με :values.',
'unique_webhook' => 'Έχετε ήδη ένα webhook με αυτές τις τιμές.',
'unique_existing_webhook' => 'Έχετε ήδη ένα άλλο webhook με αυτές τις τιμές.',
'same_account_type' => 'Both accounts must be of the same account type',
'same_account_currency' => 'Both accounts must have the same currency setting',
'same_account_type' => 'Και οι δύο λογαριασμοί πρέπει να έχουν τον ίδιο τύπο λογαριασμού',
'same_account_currency' => 'Και οι δύο λογαριασμοί πρέπει να έχουν την ίδια ρύθμιση νομίσματος',
'secure_password' => 'Αυτό δεν είναι ασφαλές συνθηματικό. Παρακαλώ δοκιμάστε ξανά. Για περισσότερες πληροφορίες επισκεφτείτε https://bit.ly/FF3-password-security',
'valid_recurrence_rep_type' => 'Μη έγκυρος τύπος επανάληψης για επαναλαμβανόμενες συναλλαγές.',

View File

@ -40,8 +40,11 @@ return [
'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'week_in_year_fns' => "'Week' I, yyyy",
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'quarter_fns' => "'Q'Q, yyyy",
'half_year_fns' => "'H{half}', yyyy",
'dow_1' => 'Monday',
'dow_2' => 'Tuesday',
'dow_3' => 'Wednesday',

View File

@ -40,8 +40,11 @@ return [
'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'week_in_year_fns' => "'Week' I, yyyy",
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'quarter_fns' => "'Q'Q, yyyy",
'half_year_fns' => "'H{half}', yyyy",
'dow_1' => 'Monday',
'dow_2' => 'Tuesday',
'dow_3' => 'Wednesday',

View File

@ -40,8 +40,11 @@ return [
'date_time_js' => 'D MMMM YYYY, HH:mm:ss',
'specific_day_js' => 'D MMMM YYYY',
'week_in_year_js' => '[Week] w, YYYY',
'week_in_year_fns' => "'Week' I, yyyy",
'year_js' => 'YYYY',
'half_year_js' => 'Q YYYY',
'quarter_fns' => "'Q'Q, yyyy",
'half_year_fns' => "'H{half}', yyyy",
'dow_1' => 'Lunes',
'dow_2' => 'Martes',
'dow_3' => 'Miércoles',

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