mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2024-11-21 16:38:36 -06:00
PHPStorm can order methods by alphabet, who knew.
This commit is contained in:
parent
f9d4a43e05
commit
68c9c4ec3c
@ -77,7 +77,7 @@ class AccountController extends Controller
|
||||
$query = $data['query'];
|
||||
$date = $data['date'] ?? today(config('app.timezone'));
|
||||
$return = [];
|
||||
$result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit'));
|
||||
$result = $this->repository->searchAccount((string)$query, $types, $this->parameters->get('limit'));
|
||||
|
||||
// TODO this code is duplicated in the V2 Autocomplete controller, which means this code is due to be deprecated.
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
@ -97,11 +97,11 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
$return[] = [
|
||||
'id' => (string) $account->id,
|
||||
'id' => (string)$account->id,
|
||||
'name' => $account->name,
|
||||
'name_with_balance' => $nameWithBalance,
|
||||
'type' => $account->accountType->type,
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
@ -114,8 +114,8 @@ class AccountController extends Controller
|
||||
$return,
|
||||
static function (array $left, array $right) {
|
||||
$order = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE];
|
||||
$posA = (int) array_search($left['type'], $order, true);
|
||||
$posB = (int) array_search($right['type'], $order, true);
|
||||
$posA = (int)array_search($left['type'], $order, true);
|
||||
$posB = (int)array_search($right['type'], $order, true);
|
||||
|
||||
return $posA - $posB;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class BillController extends Controller
|
||||
$filtered = $result->map(
|
||||
static function (Bill $item) {
|
||||
return [
|
||||
'id' => (string) $item->id,
|
||||
'id' => (string)$item->id,
|
||||
'name' => $item->name,
|
||||
'active' => $item->active,
|
||||
];
|
||||
|
@ -66,7 +66,7 @@ class BudgetController extends Controller
|
||||
$filtered = $result->map(
|
||||
static function (Budget $item) {
|
||||
return [
|
||||
'id' => (string) $item->id,
|
||||
'id' => (string)$item->id,
|
||||
'name' => $item->name,
|
||||
];
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class CategoryController extends Controller
|
||||
$filtered = $result->map(
|
||||
static function (Category $item) {
|
||||
return [
|
||||
'id' => (string) $item->id,
|
||||
'id' => (string)$item->id,
|
||||
'name' => $item->name,
|
||||
];
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class CurrencyController extends Controller
|
||||
/** @var TransactionCurrency $currency */
|
||||
foreach ($collection as $currency) {
|
||||
$result[] = [
|
||||
'id' => (string) $currency->id,
|
||||
'id' => (string)$currency->id,
|
||||
'name' => $currency->name,
|
||||
'code' => $currency->code,
|
||||
'symbol' => $currency->symbol,
|
||||
@ -94,7 +94,7 @@ class CurrencyController extends Controller
|
||||
/** @var TransactionCurrency $currency */
|
||||
foreach ($collection as $currency) {
|
||||
$result[] = [
|
||||
'id' => (string) $currency->id,
|
||||
'id' => (string)$currency->id,
|
||||
'name' => sprintf('%s (%s)', $currency->name, $currency->code),
|
||||
'code' => $currency->code,
|
||||
'symbol' => $currency->symbol,
|
||||
|
@ -68,7 +68,7 @@ class ObjectGroupController extends Controller
|
||||
/** @var ObjectGroup $objectGroup */
|
||||
foreach ($result as $objectGroup) {
|
||||
$return[] = [
|
||||
'id' => (string) $objectGroup->id,
|
||||
'id' => (string)$objectGroup->id,
|
||||
'name' => $objectGroup->title,
|
||||
'title' => $objectGroup->title,
|
||||
];
|
||||
|
@ -75,14 +75,14 @@ class PiggyBankController extends Controller
|
||||
$currency = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency;
|
||||
$objectGroup = $piggy->objectGroups()->first();
|
||||
$response[] = [
|
||||
'id' => (string) $piggy->id,
|
||||
'id' => (string)$piggy->id,
|
||||
'name' => $piggy->name,
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'object_group_id' => null === $objectGroup ? null : (string) $objectGroup->id,
|
||||
'object_group_id' => null === $objectGroup ? null : (string)$objectGroup->id,
|
||||
'object_group_title' => $objectGroup?->title,
|
||||
];
|
||||
}
|
||||
@ -107,7 +107,7 @@ class PiggyBankController extends Controller
|
||||
$currentAmount = $this->piggyRepository->getRepetition($piggy)->currentamount ?? '0';
|
||||
$objectGroup = $piggy->objectGroups()->first();
|
||||
$response[] = [
|
||||
'id' => (string) $piggy->id,
|
||||
'id' => (string)$piggy->id,
|
||||
'name' => $piggy->name,
|
||||
'name_with_balance' => sprintf(
|
||||
'%s (%s / %s)',
|
||||
@ -115,12 +115,12 @@ class PiggyBankController extends Controller
|
||||
app('amount')->formatAnything($currency, $currentAmount, false),
|
||||
app('amount')->formatAnything($currency, $piggy->targetamount, false),
|
||||
),
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'object_group_id' => null === $objectGroup ? null : (string) $objectGroup->id,
|
||||
'object_group_id' => null === $objectGroup ? null : (string)$objectGroup->id,
|
||||
'object_group_title' => $objectGroup?->title,
|
||||
];
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class RecurrenceController extends Controller
|
||||
/** @var Recurrence $recurrence */
|
||||
foreach ($recurrences as $recurrence) {
|
||||
$response[] = [
|
||||
'id' => (string) $recurrence->id,
|
||||
'id' => (string)$recurrence->id,
|
||||
'name' => $recurrence->title,
|
||||
'description' => $recurrence->description,
|
||||
];
|
||||
|
@ -65,7 +65,7 @@ class RuleController extends Controller
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
$response[] = [
|
||||
'id' => (string) $rule->id,
|
||||
'id' => (string)$rule->id,
|
||||
'name' => $rule->title,
|
||||
'description' => $rule->description,
|
||||
];
|
||||
|
@ -65,7 +65,7 @@ class RuleGroupController extends Controller
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
$response[] = [
|
||||
'id' => (string) $group->id,
|
||||
'id' => (string)$group->id,
|
||||
'name' => $group->title,
|
||||
'description' => $group->description,
|
||||
];
|
||||
|
@ -68,7 +68,7 @@ class TagController extends Controller
|
||||
/** @var Tag $tag */
|
||||
foreach ($result as $tag) {
|
||||
$array[] = [
|
||||
'id' => (string) $tag->id,
|
||||
'id' => (string)$tag->id,
|
||||
'name' => $tag->tag,
|
||||
'tag' => $tag->tag,
|
||||
];
|
||||
|
@ -76,8 +76,8 @@ class TransactionController extends Controller
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($filtered as $journal) {
|
||||
$array[] = [
|
||||
'id' => (string) $journal->id,
|
||||
'transaction_group_id' => (string) $journal->transaction_group_id,
|
||||
'id' => (string)$journal->id,
|
||||
'transaction_group_id' => (string)$journal->transaction_group_id,
|
||||
'name' => $journal->description,
|
||||
'description' => $journal->description,
|
||||
];
|
||||
@ -96,7 +96,7 @@ class TransactionController extends Controller
|
||||
$result = new Collection();
|
||||
if (is_numeric($data['query'])) {
|
||||
// search for group, not journal.
|
||||
$firstResult = $this->groupRepository->find((int) $data['query']);
|
||||
$firstResult = $this->groupRepository->find((int)$data['query']);
|
||||
if (null !== $firstResult) {
|
||||
// group may contain multiple journals, each a result:
|
||||
foreach ($firstResult->transactionJournals as $journal) {
|
||||
@ -114,8 +114,8 @@ class TransactionController extends Controller
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($result as $journal) {
|
||||
$array[] = [
|
||||
'id' => (string) $journal->id,
|
||||
'transaction_group_id' => (string) $journal->transaction_group_id,
|
||||
'id' => (string)$journal->id,
|
||||
'transaction_group_id' => (string)$journal->transaction_group_id,
|
||||
'name' => sprintf('#%d: %s', $journal->transaction_group_id, $journal->description),
|
||||
'description' => sprintf('#%d: %s', $journal->transaction_group_id, $journal->description),
|
||||
];
|
||||
|
@ -65,7 +65,7 @@ class TransactionTypeController extends Controller
|
||||
foreach ($types as $type) {
|
||||
// different key for consistency.
|
||||
$array[] = [
|
||||
'id' => (string) $type->id,
|
||||
'id' => (string)$type->id,
|
||||
'name' => $type->type,
|
||||
'type' => $type->type,
|
||||
];
|
||||
|
@ -104,7 +104,7 @@ class AccountController extends Controller
|
||||
}
|
||||
$currentSet = [
|
||||
'label' => $account->name,
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
|
@ -76,38 +76,6 @@ abstract class Controller extends BaseController
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to help build URL's.
|
||||
*/
|
||||
final protected function buildParams(): string
|
||||
{
|
||||
$return = '?';
|
||||
$params = [];
|
||||
foreach ($this->parameters as $key => $value) {
|
||||
if ('page' === $key) {
|
||||
continue;
|
||||
}
|
||||
if ($value instanceof Carbon) {
|
||||
$params[$key] = $value->format('Y-m-d');
|
||||
|
||||
continue;
|
||||
}
|
||||
$params[$key] = $value;
|
||||
}
|
||||
|
||||
return $return.http_build_query($params);
|
||||
}
|
||||
|
||||
final protected function getManager(): Manager
|
||||
{
|
||||
// create some objects:
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost().'/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
return $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to grab all parameters from the URL.
|
||||
*/
|
||||
@ -216,4 +184,36 @@ abstract class Controller extends BaseController
|
||||
|
||||
return $bag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to help build URL's.
|
||||
*/
|
||||
final protected function buildParams(): string
|
||||
{
|
||||
$return = '?';
|
||||
$params = [];
|
||||
foreach ($this->parameters as $key => $value) {
|
||||
if ('page' === $key) {
|
||||
continue;
|
||||
}
|
||||
if ($value instanceof Carbon) {
|
||||
$params[$key] = $value->format('Y-m-d');
|
||||
|
||||
continue;
|
||||
}
|
||||
$params[$key] = $value;
|
||||
}
|
||||
|
||||
return $return.http_build_query($params);
|
||||
}
|
||||
|
||||
final protected function getManager(): Manager
|
||||
{
|
||||
// create some objects:
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost().'/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
return $manager;
|
||||
}
|
||||
}
|
||||
|
@ -70,8 +70,8 @@ class TransactionController extends Controller
|
||||
// to respond to what is in the $query.
|
||||
// this is OK because only one thing can be in the query at the moment.
|
||||
if ($this->isUpdateTransactionAccount($params)) {
|
||||
$original = $this->repository->find((int) $params['where']['account_id']);
|
||||
$destination = $this->repository->find((int) $params['update']['account_id']);
|
||||
$original = $this->repository->find((int)$params['where']['account_id']);
|
||||
$destination = $this->repository->find((int)$params['update']['account_id']);
|
||||
|
||||
/** @var AccountDestroyService $service */
|
||||
$service = app(AccountDestroyService::class);
|
||||
|
@ -68,6 +68,32 @@ class ExportController extends Controller
|
||||
return $this->returnExport('accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function returnExport(string $key): LaravelResponse
|
||||
{
|
||||
$date = date('Y-m-d-H-i-s');
|
||||
$fileName = sprintf('%s-export-%s.csv', $date, $key);
|
||||
$data = $this->exporter->export();
|
||||
|
||||
/** @var LaravelResponse $response */
|
||||
$response = response($data[$key]);
|
||||
$response
|
||||
->header('Content-Description', 'File Transfer')
|
||||
->header('Content-Type', 'application/octet-stream')
|
||||
->header('Content-Disposition', 'attachment; filename='.$fileName)
|
||||
->header('Content-Transfer-Encoding', 'binary')
|
||||
->header('Connection', 'Keep-Alive')
|
||||
->header('Expires', '0')
|
||||
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
||||
->header('Pragma', 'public')
|
||||
->header('Content-Length', (string)strlen($data[$key]))
|
||||
;
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBills
|
||||
@ -189,30 +215,4 @@ class ExportController extends Controller
|
||||
|
||||
return $this->returnExport('transactions');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function returnExport(string $key): LaravelResponse
|
||||
{
|
||||
$date = date('Y-m-d-H-i-s');
|
||||
$fileName = sprintf('%s-export-%s.csv', $date, $key);
|
||||
$data = $this->exporter->export();
|
||||
|
||||
/** @var LaravelResponse $response */
|
||||
$response = response($data[$key]);
|
||||
$response
|
||||
->header('Content-Description', 'File Transfer')
|
||||
->header('Content-Type', 'application/octet-stream')
|
||||
->header('Content-Disposition', 'attachment; filename='.$fileName)
|
||||
->header('Content-Transfer-Encoding', 'binary')
|
||||
->header('Connection', 'Keep-Alive')
|
||||
->header('Expires', '0')
|
||||
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
||||
->header('Pragma', 'public')
|
||||
->header('Content-Length', (string) strlen($data[$key]))
|
||||
;
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
@ -75,28 +75,28 @@ class TagController extends Controller
|
||||
$genericSet = $collector->getExtractedJournals();
|
||||
|
||||
foreach ($genericSet as $journal) {
|
||||
$currencyId = (int) $journal['currency_id'];
|
||||
$foreignCurrencyId = (int) $journal['foreign_currency_id'];
|
||||
$currencyId = (int)$journal['currency_id'];
|
||||
$foreignCurrencyId = (int)$journal['foreign_currency_id'];
|
||||
|
||||
if (0 !== $currencyId) {
|
||||
$response[$currencyId] ??= [
|
||||
'difference' => '0',
|
||||
'difference_float' => 0,
|
||||
'currency_id' => (string) $currencyId,
|
||||
'currency_id' => (string)$currencyId,
|
||||
'currency_code' => $journal['currency_code'],
|
||||
];
|
||||
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], $journal['amount']);
|
||||
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // float but on purpose.
|
||||
$response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; // float but on purpose.
|
||||
}
|
||||
if (0 !== $foreignCurrencyId) {
|
||||
$response[$foreignCurrencyId] ??= [
|
||||
'difference' => '0',
|
||||
'difference_float' => 0,
|
||||
'currency_id' => (string) $foreignCurrencyId,
|
||||
'currency_id' => (string)$foreignCurrencyId,
|
||||
'currency_code' => $journal['foreign_currency_code'],
|
||||
];
|
||||
$response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], $journal['foreign_amount']);
|
||||
$response[$foreignCurrencyId]['difference_float'] = (float) $response[$foreignCurrencyId]['difference']; // float but on purpose.
|
||||
$response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; // float but on purpose.
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,8 +130,8 @@ class TagController extends Controller
|
||||
|
||||
/** @var array $journal */
|
||||
foreach ($genericSet as $journal) {
|
||||
$currencyId = (int) $journal['currency_id'];
|
||||
$foreignCurrencyId = (int) $journal['foreign_currency_id'];
|
||||
$currencyId = (int)$journal['currency_id'];
|
||||
$foreignCurrencyId = (int)$journal['foreign_currency_id'];
|
||||
|
||||
/** @var array $tag */
|
||||
foreach ($journal['tags'] as $tag) {
|
||||
@ -142,15 +142,15 @@ class TagController extends Controller
|
||||
// on currency ID
|
||||
if (0 !== $currencyId) {
|
||||
$response[$key] ??= [
|
||||
'id' => (string) $tagId,
|
||||
'id' => (string)$tagId,
|
||||
'name' => $tag['name'],
|
||||
'difference' => '0',
|
||||
'difference_float' => 0,
|
||||
'currency_id' => (string) $currencyId,
|
||||
'currency_id' => (string)$currencyId,
|
||||
'currency_code' => $journal['currency_code'],
|
||||
];
|
||||
$response[$key]['difference'] = bcadd($response[$key]['difference'], $journal['amount']);
|
||||
$response[$key]['difference_float'] = (float) $response[$key]['difference']; // float but on purpose.
|
||||
$response[$key]['difference_float'] = (float)$response[$key]['difference']; // float but on purpose.
|
||||
}
|
||||
|
||||
// on foreign ID
|
||||
@ -158,11 +158,11 @@ class TagController extends Controller
|
||||
$response[$foreignKey] = $journal[$foreignKey] ?? [
|
||||
'difference' => '0',
|
||||
'difference_float' => 0,
|
||||
'currency_id' => (string) $foreignCurrencyId,
|
||||
'currency_id' => (string)$foreignCurrencyId,
|
||||
'currency_code' => $journal['foreign_currency_code'],
|
||||
];
|
||||
$response[$foreignKey]['difference'] = bcadd($response[$foreignKey]['difference'], $journal['foreign_amount']);
|
||||
$response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference']; // float but on purpose.
|
||||
$response[$foreignKey]['difference_float'] = (float)$response[$foreignKey]['difference']; // float but on purpose.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,23 +118,6 @@ class BasicController extends Controller
|
||||
return response()->json($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if date is outside session range.
|
||||
*/
|
||||
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
|
||||
{
|
||||
$result = false;
|
||||
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
// start and end in the past? use $end
|
||||
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getBalanceInformation(Carbon $start, Carbon $end): array
|
||||
{
|
||||
// prep some arrays:
|
||||
@ -152,7 +135,7 @@ class BasicController extends Controller
|
||||
|
||||
/** @var array $transactionJournal */
|
||||
foreach ($set as $transactionJournal) {
|
||||
$currencyId = (int) $transactionJournal['currency_id'];
|
||||
$currencyId = (int)$transactionJournal['currency_id'];
|
||||
$incomes[$currencyId] ??= '0';
|
||||
$incomes[$currencyId] = bcadd(
|
||||
$incomes[$currencyId],
|
||||
@ -170,7 +153,7 @@ class BasicController extends Controller
|
||||
|
||||
/** @var array $transactionJournal */
|
||||
foreach ($set as $transactionJournal) {
|
||||
$currencyId = (int) $transactionJournal['currency_id'];
|
||||
$currencyId = (int)$transactionJournal['currency_id'];
|
||||
$expenses[$currencyId] ??= '0';
|
||||
$expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']);
|
||||
$sums[$currencyId] ??= '0';
|
||||
@ -189,7 +172,7 @@ class BasicController extends Controller
|
||||
'key' => sprintf('balance-in-%s', $currency->code),
|
||||
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
|
||||
'monetary_value' => $sums[$currencyId] ?? '0',
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
@ -202,7 +185,7 @@ class BasicController extends Controller
|
||||
'key' => sprintf('spent-in-%s', $currency->code),
|
||||
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
|
||||
'monetary_value' => $expenses[$currencyId] ?? '0',
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
@ -214,7 +197,7 @@ class BasicController extends Controller
|
||||
'key' => sprintf('earned-in-%s', $currency->code),
|
||||
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
|
||||
'monetary_value' => $incomes[$currencyId] ?? '0',
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
@ -248,7 +231,7 @@ class BasicController extends Controller
|
||||
'key' => sprintf('bills-paid-in-%s', $info['code']),
|
||||
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $info['symbol']]),
|
||||
'monetary_value' => $amount,
|
||||
'currency_id' => (string) $info['id'],
|
||||
'currency_id' => (string)$info['id'],
|
||||
'currency_code' => $info['code'],
|
||||
'currency_symbol' => $info['symbol'],
|
||||
'currency_decimal_places' => $info['decimal_places'],
|
||||
@ -267,7 +250,7 @@ class BasicController extends Controller
|
||||
'key' => sprintf('bills-unpaid-in-%s', $info['code']),
|
||||
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $info['symbol']]),
|
||||
'monetary_value' => $amount,
|
||||
'currency_id' => (string) $info['id'],
|
||||
'currency_id' => (string)$info['id'],
|
||||
'currency_code' => $info['code'],
|
||||
'currency_symbol' => $info['symbol'],
|
||||
'currency_decimal_places' => $info['decimal_places'],
|
||||
@ -294,21 +277,21 @@ class BasicController extends Controller
|
||||
|
||||
foreach ($spent as $row) {
|
||||
// either an amount was budgeted or 0 is available.
|
||||
$amount = (string) ($available[$row['currency_id']] ?? '0');
|
||||
$amount = (string)($available[$row['currency_id']] ?? '0');
|
||||
$spentInCurrency = $row['sum'];
|
||||
$leftToSpend = bcadd($amount, $spentInCurrency);
|
||||
|
||||
$days = $today->diffInDays($end) + 1;
|
||||
$perDay = '0';
|
||||
if (0 !== $days && bccomp($leftToSpend, '0') > -1) {
|
||||
$perDay = bcdiv($leftToSpend, (string) $days);
|
||||
$perDay = bcdiv($leftToSpend, (string)$days);
|
||||
}
|
||||
|
||||
$return[] = [
|
||||
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
|
||||
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]),
|
||||
'monetary_value' => $leftToSpend,
|
||||
'currency_id' => (string) $row['currency_id'],
|
||||
'currency_id' => (string)$row['currency_id'],
|
||||
'currency_code' => $row['currency_code'],
|
||||
'currency_symbol' => $row['currency_symbol'],
|
||||
'currency_decimal_places' => $row['currency_decimal_places'],
|
||||
@ -368,7 +351,7 @@ class BasicController extends Controller
|
||||
'key' => sprintf('net-worth-in-%s', $data['currency_code']),
|
||||
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $data['currency_symbol']]),
|
||||
'monetary_value' => $amount,
|
||||
'currency_id' => (string) $data['currency_id'],
|
||||
'currency_id' => (string)$data['currency_id'],
|
||||
'currency_code' => $data['currency_code'],
|
||||
'currency_symbol' => $data['currency_symbol'],
|
||||
'currency_decimal_places' => $data['currency_decimal_places'],
|
||||
@ -380,4 +363,21 @@ class BasicController extends Controller
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if date is outside session range.
|
||||
*/
|
||||
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
|
||||
{
|
||||
$result = false;
|
||||
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
// start and end in the past? use $end
|
||||
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +88,35 @@ class ConfigurationController extends Controller
|
||||
return response()->json($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all config values.
|
||||
*/
|
||||
private function getDynamicConfiguration(): array
|
||||
{
|
||||
$isDemoSite = app('fireflyconfig')->get('is_demo_site');
|
||||
$updateCheck = app('fireflyconfig')->get('permission_update_check');
|
||||
$lastCheck = app('fireflyconfig')->get('last_update_check');
|
||||
$singleUser = app('fireflyconfig')->get('single_user_mode');
|
||||
|
||||
return [
|
||||
'is_demo_site' => $isDemoSite?->data,
|
||||
'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data,
|
||||
'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data,
|
||||
'single_user_mode' => $singleUser?->data,
|
||||
];
|
||||
}
|
||||
|
||||
private function getStaticConfiguration(): array
|
||||
{
|
||||
$list = EitherConfigKey::$static;
|
||||
$return = [];
|
||||
foreach ($list as $key) {
|
||||
$return[$key] = config($key);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/configuration/getSingleConfiguration
|
||||
@ -145,33 +174,4 @@ class ConfigurationController extends Controller
|
||||
|
||||
return response()->json(['data' => $data])->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all config values.
|
||||
*/
|
||||
private function getDynamicConfiguration(): array
|
||||
{
|
||||
$isDemoSite = app('fireflyconfig')->get('is_demo_site');
|
||||
$updateCheck = app('fireflyconfig')->get('permission_update_check');
|
||||
$lastCheck = app('fireflyconfig')->get('last_update_check');
|
||||
$singleUser = app('fireflyconfig')->get('single_user_mode');
|
||||
|
||||
return [
|
||||
'is_demo_site' => $isDemoSite?->data,
|
||||
'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data,
|
||||
'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data,
|
||||
'single_user_mode' => $singleUser?->data,
|
||||
];
|
||||
}
|
||||
|
||||
private function getStaticConfiguration(): array
|
||||
{
|
||||
$list = EitherConfigKey::$static;
|
||||
$return = [];
|
||||
foreach ($list as $key) {
|
||||
$return[$key] = config($key);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -83,12 +83,12 @@ class MoveTransactionsRequest extends FormRequest
|
||||
$data = $validator->getData();
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$original = $repository->find((int) $data['original_account']);
|
||||
$destination = $repository->find((int) $data['destination_account']);
|
||||
$original = $repository->find((int)$data['original_account']);
|
||||
$destination = $repository->find((int)$data['destination_account']);
|
||||
|
||||
// not the same type:
|
||||
if ($original->accountType->type !== $destination->accountType->type) {
|
||||
$validator->errors()->add('title', (string) trans('validation.same_account_type'));
|
||||
$validator->errors()->add('title', (string)trans('validation.same_account_type'));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -98,7 +98,7 @@ class MoveTransactionsRequest extends FormRequest
|
||||
|
||||
// check different scenario's.
|
||||
if (null === $originalCurrency xor null === $destinationCurrency) {
|
||||
$validator->errors()->add('title', (string) trans('validation.same_account_currency'));
|
||||
$validator->errors()->add('title', (string)trans('validation.same_account_currency'));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -107,7 +107,7 @@ class MoveTransactionsRequest extends FormRequest
|
||||
return;
|
||||
}
|
||||
if ($originalCurrency->code !== $destinationCurrency->code) {
|
||||
$validator->errors()->add('title', (string) trans('validation.same_account_currency'));
|
||||
$validator->errors()->add('title', (string)trans('validation.same_account_currency'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,25 @@ class GenericRequest extends FormRequest
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function parseAccounts(): void
|
||||
{
|
||||
if (0 !== $this->accounts->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('accounts');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $accountId) {
|
||||
$accountId = (int)$accountId;
|
||||
$account = $repository->find($accountId);
|
||||
if (null !== $account) {
|
||||
$this->accounts->push($account);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getBills(): Collection
|
||||
{
|
||||
$this->parseBills();
|
||||
@ -85,6 +104,25 @@ class GenericRequest extends FormRequest
|
||||
return $this->bills;
|
||||
}
|
||||
|
||||
private function parseBills(): void
|
||||
{
|
||||
if (0 !== $this->bills->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(BillRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('bills');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $billId) {
|
||||
$billId = (int)$billId;
|
||||
$bill = $repository->find($billId);
|
||||
if (null !== $bill) {
|
||||
$this->bills->push($bill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getBudgets(): Collection
|
||||
{
|
||||
$this->parseBudgets();
|
||||
@ -92,6 +130,25 @@ class GenericRequest extends FormRequest
|
||||
return $this->budgets;
|
||||
}
|
||||
|
||||
private function parseBudgets(): void
|
||||
{
|
||||
if (0 !== $this->budgets->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('budgets');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $budgetId) {
|
||||
$budgetId = (int)$budgetId;
|
||||
$budget = $repository->find($budgetId);
|
||||
if (null !== $budget) {
|
||||
$this->budgets->push($budget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getCategories(): Collection
|
||||
{
|
||||
$this->parseCategories();
|
||||
@ -99,6 +156,25 @@ class GenericRequest extends FormRequest
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
private function parseCategories(): void
|
||||
{
|
||||
if (0 !== $this->categories->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('categories');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $categoryId) {
|
||||
$categoryId = (int)$categoryId;
|
||||
$category = $repository->find($categoryId);
|
||||
if (null !== $category) {
|
||||
$this->categories->push($category);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getEnd(): Carbon
|
||||
{
|
||||
$date = $this->getCarbonDate('end');
|
||||
@ -154,100 +230,6 @@ class GenericRequest extends FormRequest
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
// this is cheating, but it works to initialize the collections.
|
||||
$this->accounts = new Collection();
|
||||
$this->budgets = new Collection();
|
||||
$this->categories = new Collection();
|
||||
$this->bills = new Collection();
|
||||
$this->tags = new Collection();
|
||||
|
||||
return [
|
||||
'start' => 'required|date',
|
||||
'end' => 'required|date|after_or_equal:start',
|
||||
];
|
||||
}
|
||||
|
||||
private function parseAccounts(): void
|
||||
{
|
||||
if (0 !== $this->accounts->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('accounts');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $accountId) {
|
||||
$accountId = (int)$accountId;
|
||||
$account = $repository->find($accountId);
|
||||
if (null !== $account) {
|
||||
$this->accounts->push($account);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parseBills(): void
|
||||
{
|
||||
if (0 !== $this->bills->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(BillRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('bills');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $billId) {
|
||||
$billId = (int)$billId;
|
||||
$bill = $repository->find($billId);
|
||||
if (null !== $bill) {
|
||||
$this->bills->push($bill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parseBudgets(): void
|
||||
{
|
||||
if (0 !== $this->budgets->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('budgets');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $budgetId) {
|
||||
$budgetId = (int)$budgetId;
|
||||
$budget = $repository->find($budgetId);
|
||||
if (null !== $budget) {
|
||||
$this->budgets->push($budget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parseCategories(): void
|
||||
{
|
||||
if (0 !== $this->categories->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('categories');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $categoryId) {
|
||||
$categoryId = (int)$categoryId;
|
||||
$category = $repository->find($categoryId);
|
||||
if (null !== $category) {
|
||||
$this->categories->push($category);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parseTags(): void
|
||||
{
|
||||
if (0 !== $this->tags->count()) {
|
||||
@ -266,4 +248,22 @@ class GenericRequest extends FormRequest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
// this is cheating, but it works to initialize the collections.
|
||||
$this->accounts = new Collection();
|
||||
$this->budgets = new Collection();
|
||||
$this->categories = new Collection();
|
||||
$this->bills = new Collection();
|
||||
$this->tags = new Collection();
|
||||
|
||||
return [
|
||||
'start' => 'required|date',
|
||||
'end' => 'required|date|after_or_equal:start',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ class UpdateRequest extends FormRequest
|
||||
$start = new Carbon($data['start']);
|
||||
$end = new Carbon($data['end']);
|
||||
if ($end->isBefore($start)) {
|
||||
$validator->errors()->add('end', (string) trans('validation.date_after'));
|
||||
$validator->errors()->add('end', (string)trans('validation.date_after'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,65 @@ class StoreRequest extends FormRequest
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
|
||||
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
|
||||
*/
|
||||
private function getTransactionData(): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
// transaction data:
|
||||
/** @var null|array $transactions */
|
||||
$transactions = $this->get('transactions');
|
||||
if (null === $transactions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = $this->getSingleTransactionData($transaction);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repetition data as it is found in the submitted data.
|
||||
*/
|
||||
private function getRepetitionData(): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
// repetition data:
|
||||
/** @var null|array $repetitions */
|
||||
$repetitions = $this->get('repetitions');
|
||||
if (null === $repetitions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var array $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$current = [];
|
||||
if (array_key_exists('type', $repetition)) {
|
||||
$current['type'] = $repetition['type'];
|
||||
}
|
||||
if (array_key_exists('moment', $repetition)) {
|
||||
$current['moment'] = $repetition['moment'];
|
||||
}
|
||||
if (array_key_exists('skip', $repetition)) {
|
||||
$current['skip'] = (int)$repetition['skip'];
|
||||
}
|
||||
if (array_key_exists('weekend', $repetition)) {
|
||||
$current['weekend'] = (int)$repetition['weekend'];
|
||||
}
|
||||
|
||||
$return[] = $current;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
@ -136,63 +195,4 @@ class StoreRequest extends FormRequest
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
|
||||
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
|
||||
*/
|
||||
private function getTransactionData(): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
// transaction data:
|
||||
/** @var null|array $transactions */
|
||||
$transactions = $this->get('transactions');
|
||||
if (null === $transactions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = $this->getSingleTransactionData($transaction);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repetition data as it is found in the submitted data.
|
||||
*/
|
||||
private function getRepetitionData(): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
// repetition data:
|
||||
/** @var null|array $repetitions */
|
||||
$repetitions = $this->get('repetitions');
|
||||
if (null === $repetitions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var array $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$current = [];
|
||||
if (array_key_exists('type', $repetition)) {
|
||||
$current['type'] = $repetition['type'];
|
||||
}
|
||||
if (array_key_exists('moment', $repetition)) {
|
||||
$current['moment'] = $repetition['moment'];
|
||||
}
|
||||
if (array_key_exists('skip', $repetition)) {
|
||||
$current['skip'] = (int)$repetition['skip'];
|
||||
}
|
||||
if (array_key_exists('weekend', $repetition)) {
|
||||
$current['weekend'] = (int)$repetition['weekend'];
|
||||
}
|
||||
|
||||
$return[] = $current;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,70 @@ class UpdateRequest extends FormRequest
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repetition data as it is found in the submitted data.
|
||||
*/
|
||||
private function getRepetitionData(): ?array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
// repetition data:
|
||||
/** @var null|array $repetitions */
|
||||
$repetitions = $this->get('repetitions');
|
||||
if (null === $repetitions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var array $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$current = [];
|
||||
if (array_key_exists('type', $repetition)) {
|
||||
$current['type'] = $repetition['type'];
|
||||
}
|
||||
|
||||
if (array_key_exists('moment', $repetition)) {
|
||||
$current['moment'] = (string)$repetition['moment'];
|
||||
}
|
||||
|
||||
if (array_key_exists('skip', $repetition)) {
|
||||
$current['skip'] = (int)$repetition['skip'];
|
||||
}
|
||||
|
||||
if (array_key_exists('weekend', $repetition)) {
|
||||
$current['weekend'] = (int)$repetition['weekend'];
|
||||
}
|
||||
$return[] = $current;
|
||||
}
|
||||
if (0 === count($return)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
|
||||
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
|
||||
*/
|
||||
private function getTransactionData(): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
// transaction data:
|
||||
/** @var null|array $transactions */
|
||||
$transactions = $this->get('transactions');
|
||||
if (null === $transactions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = $this->getSingleTransactionData($transaction);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
@ -146,68 +210,4 @@ class UpdateRequest extends FormRequest
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repetition data as it is found in the submitted data.
|
||||
*/
|
||||
private function getRepetitionData(): ?array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
// repetition data:
|
||||
/** @var null|array $repetitions */
|
||||
$repetitions = $this->get('repetitions');
|
||||
if (null === $repetitions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var array $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$current = [];
|
||||
if (array_key_exists('type', $repetition)) {
|
||||
$current['type'] = $repetition['type'];
|
||||
}
|
||||
|
||||
if (array_key_exists('moment', $repetition)) {
|
||||
$current['moment'] = (string) $repetition['moment'];
|
||||
}
|
||||
|
||||
if (array_key_exists('skip', $repetition)) {
|
||||
$current['skip'] = (int) $repetition['skip'];
|
||||
}
|
||||
|
||||
if (array_key_exists('weekend', $repetition)) {
|
||||
$current['weekend'] = (int) $repetition['weekend'];
|
||||
}
|
||||
$return[] = $current;
|
||||
}
|
||||
if (0 === count($return)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
|
||||
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
|
||||
*/
|
||||
private function getTransactionData(): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
// transaction data:
|
||||
/** @var null|array $transactions */
|
||||
$transactions = $this->get('transactions');
|
||||
if (null === $transactions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = $this->getSingleTransactionData($transaction);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,42 @@ class StoreRequest extends FormRequest
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getRuleTriggers(): array
|
||||
{
|
||||
$triggers = $this->get('triggers');
|
||||
$return = [];
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'active' => $this->convertBoolean((string)($trigger['active'] ?? 'true')),
|
||||
'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function getRuleActions(): array
|
||||
{
|
||||
$actions = $this->get('actions');
|
||||
$return = [];
|
||||
if (is_array($actions)) {
|
||||
foreach ($actions as $action) {
|
||||
$return[] = [
|
||||
'type' => $action['type'],
|
||||
'value' => $action['value'],
|
||||
'active' => $this->convertBoolean((string)($action['active'] ?? 'true')),
|
||||
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
@ -197,40 +233,4 @@ class StoreRequest extends FormRequest
|
||||
$validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action'));
|
||||
}
|
||||
}
|
||||
|
||||
private function getRuleTriggers(): array
|
||||
{
|
||||
$triggers = $this->get('triggers');
|
||||
$return = [];
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'active' => $this->convertBoolean((string)($trigger['active'] ?? 'true')),
|
||||
'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function getRuleActions(): array
|
||||
{
|
||||
$actions = $this->get('actions');
|
||||
$return = [];
|
||||
if (is_array($actions)) {
|
||||
foreach ($actions as $action) {
|
||||
$return[] = [
|
||||
'type' => $action['type'],
|
||||
'value' => $action['value'],
|
||||
'active' => $this->convertBoolean((string)($action['active'] ?? 'true')),
|
||||
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -47,16 +47,6 @@ class TestRequest extends FormRequest
|
||||
];
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
|
||||
private function getPage(): int
|
||||
{
|
||||
return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page');
|
||||
@ -81,4 +71,14 @@ class TestRequest extends FormRequest
|
||||
{
|
||||
return $this->get('accounts');
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -46,16 +46,6 @@ class TriggerRequest extends FormRequest
|
||||
];
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
$value = $this->query($field);
|
||||
@ -75,4 +65,14 @@ class TriggerRequest extends FormRequest
|
||||
{
|
||||
return $this->get('accounts') ?? [];
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,50 @@ class UpdateRequest extends FormRequest
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function getRuleTriggers(): ?array
|
||||
{
|
||||
if (!$this->has('triggers')) {
|
||||
return null;
|
||||
}
|
||||
$triggers = $this->get('triggers');
|
||||
$return = [];
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$active = array_key_exists('active', $trigger) ? $trigger['active'] : true;
|
||||
$stopProcessing = array_key_exists('stop_processing', $trigger) ? $trigger['stop_processing'] : false;
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'active' => $active,
|
||||
'stop_processing' => $stopProcessing,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function getRuleActions(): ?array
|
||||
{
|
||||
if (!$this->has('actions')) {
|
||||
return null;
|
||||
}
|
||||
$actions = $this->get('actions');
|
||||
$return = [];
|
||||
if (is_array($actions)) {
|
||||
foreach ($actions as $action) {
|
||||
$return[] = [
|
||||
'type' => $action['type'],
|
||||
'value' => $action['value'],
|
||||
'active' => $this->convertBoolean((string)($action['active'] ?? 'false')),
|
||||
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
@ -204,48 +248,4 @@ class UpdateRequest extends FormRequest
|
||||
$validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action'));
|
||||
}
|
||||
}
|
||||
|
||||
private function getRuleTriggers(): ?array
|
||||
{
|
||||
if (!$this->has('triggers')) {
|
||||
return null;
|
||||
}
|
||||
$triggers = $this->get('triggers');
|
||||
$return = [];
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$active = array_key_exists('active', $trigger) ? $trigger['active'] : true;
|
||||
$stopProcessing = array_key_exists('stop_processing', $trigger) ? $trigger['stop_processing'] : false;
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'active' => $active,
|
||||
'stop_processing' => $stopProcessing,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function getRuleActions(): ?array
|
||||
{
|
||||
if (!$this->has('actions')) {
|
||||
return null;
|
||||
}
|
||||
$actions = $this->get('actions');
|
||||
$return = [];
|
||||
if (is_array($actions)) {
|
||||
foreach ($actions as $action) {
|
||||
$return[] = [
|
||||
'type' => $action['type'],
|
||||
'value' => $action['value'],
|
||||
'active' => $this->convertBoolean((string)($action['active'] ?? 'false')),
|
||||
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -46,16 +46,6 @@ class TestRequest extends FormRequest
|
||||
];
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
$value = $this->query($field);
|
||||
@ -75,4 +65,14 @@ class TestRequest extends FormRequest
|
||||
{
|
||||
return $this->get('accounts');
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -46,14 +46,6 @@ class TriggerRequest extends FormRequest
|
||||
];
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
];
|
||||
}
|
||||
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
$value = $this->query($field);
|
||||
@ -77,4 +69,12 @@ class TriggerRequest extends FormRequest
|
||||
|
||||
return $this->get('accounts');
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -69,134 +69,6 @@ class StoreRequest extends FormRequest
|
||||
// TODO include location and ability to process it.
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
app('log')->debug('Collect rules of TransactionStoreRequest');
|
||||
$validProtocols = config('firefly.valid_url_protocols');
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'error_if_duplicate_hash' => [new IsBoolean()],
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => ['required', new IsDateOrTime()],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => ['required', new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidZeroOrMoreAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|min:1|max:1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.source_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_bic' => 'min:1|max:255|nullable|bic',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.destination_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_bic' => 'min:1|max:255|nullable|bic',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser()],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()],
|
||||
'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUser()],
|
||||
'transactions.*.piggy_bank_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_url' => sprintf('min:1|max:255|nullable|url:%s', $validProtocols),
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
// must be valid array.
|
||||
$this->validateTransactionArray($validator);
|
||||
|
||||
// must submit at least one transaction.
|
||||
app('log')->debug('Now going to validateOneTransaction');
|
||||
$this->validateOneTransaction($validator);
|
||||
app('log')->debug('Now done with validateOneTransaction');
|
||||
|
||||
// all journals must have a description
|
||||
$this->validateDescriptions($validator);
|
||||
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypes($validator);
|
||||
|
||||
// validate foreign currency info
|
||||
$this->validateForeignCurrencyInformation($validator);
|
||||
|
||||
// validate all account info
|
||||
$this->validateAccountInformation($validator);
|
||||
|
||||
// validate source/destination is equal, depending on the transaction journal type.
|
||||
$this->validateEqualAccounts($validator);
|
||||
|
||||
// the group must have a description if > 1 journal.
|
||||
$this->validateGroupDescription($validator);
|
||||
}
|
||||
);
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction data.
|
||||
*/
|
||||
@ -291,4 +163,132 @@ class StoreRequest extends FormRequest
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
app('log')->debug('Collect rules of TransactionStoreRequest');
|
||||
$validProtocols = config('firefly.valid_url_protocols');
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'error_if_duplicate_hash' => [new IsBoolean()],
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => ['required', new IsDateOrTime()],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => ['required', new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidZeroOrMoreAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|min:1|max:1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.source_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_bic' => 'min:1|max:255|nullable|bic',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.destination_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_bic' => 'min:1|max:255|nullable|bic',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser()],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()],
|
||||
'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUser()],
|
||||
'transactions.*.piggy_bank_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_url' => sprintf('min:1|max:255|nullable|url:%s', $validProtocols),
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
// must be valid array.
|
||||
$this->validateTransactionArray($validator);
|
||||
|
||||
// must submit at least one transaction.
|
||||
app('log')->debug('Now going to validateOneTransaction');
|
||||
$this->validateOneTransaction($validator);
|
||||
app('log')->debug('Now done with validateOneTransaction');
|
||||
|
||||
// all journals must have a description
|
||||
$this->validateDescriptions($validator);
|
||||
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypes($validator);
|
||||
|
||||
// validate foreign currency info
|
||||
$this->validateForeignCurrencyInformation($validator);
|
||||
|
||||
// validate all account info
|
||||
$this->validateAccountInformation($validator);
|
||||
|
||||
// validate source/destination is equal, depending on the transaction journal type.
|
||||
$this->validateEqualAccounts($validator);
|
||||
|
||||
// the group must have a description if > 1 journal.
|
||||
$this->validateGroupDescription($validator);
|
||||
}
|
||||
);
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,127 +90,6 @@ class UpdateRequest extends FormRequest
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
$validProtocols = config('firefly.valid_url_protocols');
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => [new IsDateOrTime()],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// group id:
|
||||
'transactions.*.transaction_journal_id' => ['nullable', 'numeric', new BelongsUser()],
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => [new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidZeroOrMoreAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|min:1|max:1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()],
|
||||
'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255|nullable',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_url' => sprintf('min:1|max:255|nullable|url:%s', $validProtocols),
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in withValidator');
|
||||
|
||||
/** @var TransactionGroup $transactionGroup */
|
||||
$transactionGroup = $this->route()->parameter('transactionGroup');
|
||||
$validator->after(
|
||||
function (Validator $validator) use ($transactionGroup): void {
|
||||
// if more than one, verify that there are journal ID's present.
|
||||
$this->validateJournalIds($validator, $transactionGroup);
|
||||
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypesForUpdate($validator);
|
||||
|
||||
// user wants to update a reconciled transaction.
|
||||
// source, destination, amount + foreign_amount cannot be changed
|
||||
// and must be omitted from the request.
|
||||
$this->preventUpdateReconciled($validator, $transactionGroup);
|
||||
|
||||
// validate source/destination is equal, depending on the transaction journal type.
|
||||
$this->validateEqualAccountsForUpdate($validator, $transactionGroup);
|
||||
|
||||
// see method:
|
||||
// $this->preventNoAccountInfo($validator, );
|
||||
|
||||
// validate that the currency fits the source and/or destination account.
|
||||
// validate all account info
|
||||
$this->validateAccountInformationUpdate($validator, $transactionGroup);
|
||||
}
|
||||
);
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction data.
|
||||
*
|
||||
@ -258,7 +137,7 @@ class UpdateRequest extends FormRequest
|
||||
{
|
||||
foreach ($this->integerFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->integerFromValue((string) $transaction[$fieldName]);
|
||||
$current[$fieldName] = $this->integerFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +152,7 @@ class UpdateRequest extends FormRequest
|
||||
{
|
||||
foreach ($this->stringFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->clearString((string) $transaction[$fieldName]);
|
||||
$current[$fieldName] = $this->clearString((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,7 +167,7 @@ class UpdateRequest extends FormRequest
|
||||
{
|
||||
foreach ($this->textareaFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->clearStringKeepNewlines((string) $transaction[$fieldName]); // keep newlines
|
||||
$current[$fieldName] = $this->clearStringKeepNewlines((string)$transaction[$fieldName]); // keep newlines
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,8 +183,8 @@ class UpdateRequest extends FormRequest
|
||||
foreach ($this->dateFields as $fieldName) {
|
||||
app('log')->debug(sprintf('Now at date field %s', $fieldName));
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
app('log')->debug(sprintf('New value: "%s"', (string) $transaction[$fieldName]));
|
||||
$current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]);
|
||||
app('log')->debug(sprintf('New value: "%s"', (string)$transaction[$fieldName]));
|
||||
$current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,7 +199,7 @@ class UpdateRequest extends FormRequest
|
||||
{
|
||||
foreach ($this->booleanFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->convertBoolean((string) $transaction[$fieldName]);
|
||||
$current[$fieldName] = $this->convertBoolean((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,11 +234,132 @@ class UpdateRequest extends FormRequest
|
||||
$current[$fieldName] = sprintf('%.12f', $value);
|
||||
}
|
||||
if (!is_float($value)) {
|
||||
$current[$fieldName] = (string) $value;
|
||||
$current[$fieldName] = (string)$value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
$validProtocols = config('firefly.valid_url_protocols');
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => [new IsDateOrTime()],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// group id:
|
||||
'transactions.*.transaction_journal_id' => ['nullable', 'numeric', new BelongsUser()],
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => [new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidZeroOrMoreAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|min:1|max:1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()],
|
||||
'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255|nullable',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_url' => sprintf('min:1|max:255|nullable|url:%s', $validProtocols),
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in withValidator');
|
||||
|
||||
/** @var TransactionGroup $transactionGroup */
|
||||
$transactionGroup = $this->route()->parameter('transactionGroup');
|
||||
$validator->after(
|
||||
function (Validator $validator) use ($transactionGroup): void {
|
||||
// if more than one, verify that there are journal ID's present.
|
||||
$this->validateJournalIds($validator, $transactionGroup);
|
||||
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypesForUpdate($validator);
|
||||
|
||||
// user wants to update a reconciled transaction.
|
||||
// source, destination, amount + foreign_amount cannot be changed
|
||||
// and must be omitted from the request.
|
||||
$this->preventUpdateReconciled($validator, $transactionGroup);
|
||||
|
||||
// validate source/destination is equal, depending on the transaction journal type.
|
||||
$this->validateEqualAccountsForUpdate($validator, $transactionGroup);
|
||||
|
||||
// see method:
|
||||
// $this->preventNoAccountInfo($validator, );
|
||||
|
||||
// validate that the currency fits the source and/or destination account.
|
||||
// validate all account info
|
||||
$this->validateAccountInformationUpdate($validator, $transactionGroup);
|
||||
}
|
||||
);
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class CategoryController extends Controller
|
||||
$filtered = $result->map(
|
||||
static function (Category $item) {
|
||||
return [
|
||||
'id' => (string) $item->id,
|
||||
'id' => (string)$item->id,
|
||||
'name' => $item->name,
|
||||
];
|
||||
}
|
||||
|
@ -72,10 +72,10 @@ class TagController extends Controller
|
||||
$filtered = $result->map(
|
||||
static function (Tag $item) {
|
||||
return [
|
||||
'id' => (string) $item->id,
|
||||
'name' => $item->tag,
|
||||
'value' => (string) $item->id,
|
||||
'label' => $item->tag,
|
||||
'id' => (string)$item->id,
|
||||
'name' => $item->tag,
|
||||
'value' => (string)$item->id,
|
||||
'label' => $item->tag,
|
||||
];
|
||||
}
|
||||
);
|
||||
|
@ -126,23 +126,23 @@ class AccountController extends Controller
|
||||
$currency = $default;
|
||||
}
|
||||
$currentSet = [
|
||||
'label' => $account->name,
|
||||
'label' => $account->name,
|
||||
// the currency that belongs to the account.
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
|
||||
// the default currency of the user (could be the same!)
|
||||
'native_currency_id' => (string)$default->id,
|
||||
'native_currency_code' => $default->code,
|
||||
'native_currency_symbol' => $default->symbol,
|
||||
'native_currency_decimal_places' => $default->decimal_places,
|
||||
'start' => $start->toAtomString(),
|
||||
'end' => $end->toAtomString(),
|
||||
'period' => '1D',
|
||||
'entries' => [],
|
||||
'native_entries' => [],
|
||||
'native_currency_id' => (string)$default->id,
|
||||
'native_currency_code' => $default->code,
|
||||
'native_currency_symbol' => $default->symbol,
|
||||
'native_currency_decimal_places' => $default->decimal_places,
|
||||
'start' => $start->toAtomString(),
|
||||
'end' => $end->toAtomString(),
|
||||
'period' => '1D',
|
||||
'entries' => [],
|
||||
'native_entries' => [],
|
||||
];
|
||||
$currentStart = clone $start;
|
||||
$range = app('steam')->balanceInRange($account, $start, clone $end, $currency);
|
||||
|
@ -124,11 +124,11 @@ class BudgetController extends Controller
|
||||
foreach ($rows as $row) {
|
||||
$current = [
|
||||
'label' => $budget->name,
|
||||
'currency_id' => (string) $row['currency_id'],
|
||||
'currency_id' => (string)$row['currency_id'],
|
||||
'currency_code' => $row['currency_code'],
|
||||
'currency_name' => $row['currency_name'],
|
||||
'currency_decimal_places' => $row['currency_decimal_places'],
|
||||
'native_currency_id' => (string) $row['native_currency_id'],
|
||||
'native_currency_id' => (string)$row['native_currency_id'],
|
||||
'native_currency_code' => $row['native_currency_code'],
|
||||
'native_currency_name' => $row['native_currency_name'],
|
||||
'native_currency_decimal_places' => $row['native_currency_decimal_places'],
|
||||
@ -189,12 +189,12 @@ class BudgetController extends Controller
|
||||
foreach ($array as $currencyId => $block) {
|
||||
$this->currencies[$currencyId] ??= TransactionCurrency::find($currencyId);
|
||||
$return[$currencyId] ??= [
|
||||
'currency_id' => (string) $currencyId,
|
||||
'currency_id' => (string)$currencyId,
|
||||
'currency_code' => $block['currency_code'],
|
||||
'currency_name' => $block['currency_name'],
|
||||
'currency_symbol' => $block['currency_symbol'],
|
||||
'currency_decimal_places' => (int) $block['currency_decimal_places'],
|
||||
'native_currency_id' => (string) $this->currency->id,
|
||||
'currency_decimal_places' => (int)$block['currency_decimal_places'],
|
||||
'native_currency_id' => (string)$this->currency->id,
|
||||
'native_currency_code' => $this->currency->code,
|
||||
'native_currency_name' => $this->currency->name,
|
||||
'native_currency_symbol' => $this->currency->symbol,
|
||||
|
@ -112,22 +112,22 @@ class CategoryController extends Controller
|
||||
}
|
||||
// create arrays
|
||||
$return[$key] ??= [
|
||||
'label' => $categoryName,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'native_currency_id' => (string)$default->id,
|
||||
'native_currency_code' => $default->code,
|
||||
'native_currency_name' => $default->name,
|
||||
'native_currency_symbol' => $default->symbol,
|
||||
'native_currency_decimal_places' => $default->decimal_places,
|
||||
'period' => null,
|
||||
'start' => $start->toAtomString(),
|
||||
'end' => $end->toAtomString(),
|
||||
'amount' => '0',
|
||||
'native_amount' => '0',
|
||||
'label' => $categoryName,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'native_currency_id' => (string)$default->id,
|
||||
'native_currency_code' => $default->code,
|
||||
'native_currency_name' => $default->name,
|
||||
'native_currency_symbol' => $default->symbol,
|
||||
'native_currency_decimal_places' => $default->decimal_places,
|
||||
'period' => null,
|
||||
'start' => $start->toAtomString(),
|
||||
'end' => $end->toAtomString(),
|
||||
'amount' => '0',
|
||||
'native_amount' => '0',
|
||||
];
|
||||
|
||||
// add monies
|
||||
|
@ -67,43 +67,6 @@ class Controller extends BaseController
|
||||
);
|
||||
}
|
||||
|
||||
final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array
|
||||
{
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$objects = $paginator->getCollection();
|
||||
|
||||
// the transformer, at this point, needs to collect information that ALL items in the collection
|
||||
// require, like meta-data and stuff like that, and save it for later.
|
||||
$transformer->collectMetaData($objects);
|
||||
|
||||
$resource = new FractalCollection($objects, $transformer, $key);
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return $manager->createData($resource)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON API object and returns it.
|
||||
*
|
||||
* @param array<int, mixed>|Model $object
|
||||
*/
|
||||
final protected function jsonApiObject(string $key, array|Model $object, AbstractTransformer $transformer): array
|
||||
{
|
||||
// create some objects:
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$transformer->collectMetaData(new Collection([$object]));
|
||||
|
||||
$resource = new Item($object, $transformer, $key);
|
||||
|
||||
return $manager->createData($resource)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate from V1 controller
|
||||
* Method to grab all parameters from the URL.
|
||||
@ -184,4 +147,41 @@ class Controller extends BaseController
|
||||
|
||||
return $bag;
|
||||
}
|
||||
|
||||
final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array
|
||||
{
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$objects = $paginator->getCollection();
|
||||
|
||||
// the transformer, at this point, needs to collect information that ALL items in the collection
|
||||
// require, like meta-data and stuff like that, and save it for later.
|
||||
$transformer->collectMetaData($objects);
|
||||
|
||||
$resource = new FractalCollection($objects, $transformer, $key);
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return $manager->createData($resource)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON API object and returns it.
|
||||
*
|
||||
* @param array<int, mixed>|Model $object
|
||||
*/
|
||||
final protected function jsonApiObject(string $key, array|Model $object, AbstractTransformer $transformer): array
|
||||
{
|
||||
// create some objects:
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$transformer->collectMetaData(new Collection([$object]));
|
||||
|
||||
$resource = new Item($object, $transformer, $key);
|
||||
|
||||
return $manager->createData($resource)->toArray();
|
||||
}
|
||||
}
|
||||
|
@ -51,20 +51,6 @@ class ShowController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a budget.
|
||||
*/
|
||||
public function show(Budget $budget): JsonResponse
|
||||
{
|
||||
$transformer = new BudgetTransformer();
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
return response()
|
||||
->api($this->jsonApiObject('budgets', $budget, $transformer))
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2023-10-29 removed the cerSum reference, not sure where this is used atm
|
||||
* so removed from api.php. Also applies to "spent" method.
|
||||
@ -80,6 +66,20 @@ class ShowController extends Controller
|
||||
return response()->json($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a budget.
|
||||
*/
|
||||
public function show(Budget $budget): JsonResponse
|
||||
{
|
||||
$transformer = new BudgetTransformer();
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
return response()
|
||||
->api($this->jsonApiObject('budgets', $budget, $transformer))
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* TODO add URL
|
||||
|
@ -114,23 +114,6 @@ class BasicController extends Controller
|
||||
return response()->json($total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if date is outside session range.
|
||||
*/
|
||||
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
|
||||
{
|
||||
$result = false;
|
||||
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
// start and end in the past? use $end
|
||||
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
@ -411,4 +394,21 @@ class BasicController extends Controller
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if date is outside session range.
|
||||
*/
|
||||
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
|
||||
{
|
||||
$result = false;
|
||||
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
// start and end in the past? use $end
|
||||
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -35,43 +35,6 @@ use Illuminate\Http\JsonResponse;
|
||||
*/
|
||||
class TransactionController extends Controller
|
||||
{
|
||||
public function listByCount(ListByCountRequest $request): JsonResponse
|
||||
{
|
||||
// collect transactions:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUserGroup(auth()->user()->userGroup)
|
||||
->withAPIInformation()
|
||||
->setStartRow($request->getStartRow())
|
||||
->setEndRow($request->getEndRow())
|
||||
->setTypes($request->getTransactionTypes())
|
||||
;
|
||||
|
||||
$start = $this->parameters->get('start');
|
||||
$end = $this->parameters->get('end');
|
||||
if (null !== $start) {
|
||||
$collector->setStart($start);
|
||||
}
|
||||
if (null !== $end) {
|
||||
$collector->setEnd($end);
|
||||
}
|
||||
|
||||
$paginator = $collector->getPaginatedGroups();
|
||||
$params = $request->buildParams();
|
||||
$paginator->setPath(
|
||||
sprintf(
|
||||
'%s?%s',
|
||||
route('api.v2.transactions.list'),
|
||||
$params
|
||||
)
|
||||
);
|
||||
|
||||
return response()
|
||||
->json($this->jsonApiList('transactions', $paginator, new TransactionGroupTransformer()))
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
|
||||
public function list(ListRequest $request): JsonResponse
|
||||
{
|
||||
// collect transactions:
|
||||
@ -115,4 +78,40 @@ class TransactionController extends Controller
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
|
||||
public function listByCount(ListByCountRequest $request): JsonResponse
|
||||
{
|
||||
// collect transactions:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUserGroup(auth()->user()->userGroup)
|
||||
->withAPIInformation()
|
||||
->setStartRow($request->getStartRow())
|
||||
->setEndRow($request->getEndRow())
|
||||
->setTypes($request->getTransactionTypes())
|
||||
;
|
||||
|
||||
$start = $this->parameters->get('start');
|
||||
$end = $this->parameters->get('end');
|
||||
if (null !== $start) {
|
||||
$collector->setStart($start);
|
||||
}
|
||||
if (null !== $end) {
|
||||
$collector->setEnd($end);
|
||||
}
|
||||
|
||||
$paginator = $collector->getPaginatedGroups();
|
||||
$params = $request->buildParams();
|
||||
$paginator->setPath(
|
||||
sprintf(
|
||||
'%s?%s',
|
||||
route('api.v2.transactions.list'),
|
||||
$params
|
||||
)
|
||||
);
|
||||
|
||||
return response()
|
||||
->json($this->jsonApiList('transactions', $paginator, new TransactionGroupTransformer()))
|
||||
->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
}
|
||||
|
@ -57,13 +57,6 @@ class ListByCountRequest extends FormRequest
|
||||
return http_build_query($array);
|
||||
}
|
||||
|
||||
public function getPage(): int
|
||||
{
|
||||
$page = $this->convertInteger('page');
|
||||
|
||||
return 0 === $page || $page > 65536 ? 1 : $page;
|
||||
}
|
||||
|
||||
public function getStartRow(): int
|
||||
{
|
||||
$startRow = $this->convertInteger('start_row');
|
||||
@ -88,6 +81,13 @@ class ListByCountRequest extends FormRequest
|
||||
return $this->getCarbonDate('end');
|
||||
}
|
||||
|
||||
public function getPage(): int
|
||||
{
|
||||
$page = $this->convertInteger('page');
|
||||
|
||||
return 0 === $page || $page > 65536 ? 1 : $page;
|
||||
}
|
||||
|
||||
public function getTransactionTypes(): array
|
||||
{
|
||||
$type = (string)$this->get('type', 'default');
|
||||
|
@ -78,145 +78,6 @@ class StoreRequest extends FormRequest
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
app('log')->debug('V2: Collect rules of TransactionStoreRequest');
|
||||
|
||||
// at this point the userGroup can't be NULL because the
|
||||
// authorize() method will complain. Loudly.
|
||||
/** @var UserGroup $userGroup */
|
||||
$userGroup = $this->getUserGroup();
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'error_if_duplicate_hash' => [new IsBoolean()],
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => ['required', new IsDateOrTime()],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => ['required', new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|min:1|max:1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.source_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_bic' => 'min:1|max:255|nullable|bic',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.destination_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_bic' => 'min:1|max:255|nullable|bic',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUserGroup($userGroup), 'nullable'],
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.piggy_bank_name' => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_url' => 'min:1|max:255|nullable|url',
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
|
||||
// TODO include location and ability to process it.
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
/** @var UserGroup $userGroup */
|
||||
$userGroup = $this->getUserGroup();
|
||||
$validator->after(
|
||||
function (Validator $validator) use ($user, $userGroup): void {
|
||||
// must be valid array.
|
||||
$this->validateTransactionArray($validator); // does not need group validation.
|
||||
|
||||
// must submit at least one transaction.
|
||||
app('log')->debug('Now going to validateOneTransaction');
|
||||
$this->validateOneTransaction($validator); // does not need group validation.
|
||||
app('log')->debug('Now done with validateOneTransaction');
|
||||
|
||||
// all journals must have a description
|
||||
$this->validateDescriptions($validator); // does not need group validation.
|
||||
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypes($validator); // does not need group validation.
|
||||
|
||||
// validate foreign currency info
|
||||
$this->validateForeignCurrencyInformation($validator); // does not need group validation.
|
||||
|
||||
// validate all account info
|
||||
$this->validateAccountInformation($validator, $user, $userGroup);
|
||||
|
||||
// validate source/destination is equal, depending on the transaction journal type.
|
||||
$this->validateEqualAccounts($validator);
|
||||
|
||||
// the group must have a description if > 1 journal.
|
||||
$this->validateGroupDescription($validator);
|
||||
}
|
||||
);
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction data.
|
||||
*/
|
||||
@ -313,4 +174,143 @@ class StoreRequest extends FormRequest
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
app('log')->debug('V2: Collect rules of TransactionStoreRequest');
|
||||
|
||||
// at this point the userGroup can't be NULL because the
|
||||
// authorize() method will complain. Loudly.
|
||||
/** @var UserGroup $userGroup */
|
||||
$userGroup = $this->getUserGroup();
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'error_if_duplicate_hash' => [new IsBoolean()],
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => ['required', new IsDateOrTime()],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => ['required', new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|min:1|max:1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.source_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_bic' => 'min:1|max:255|nullable|bic',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.destination_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_bic' => 'min:1|max:255|nullable|bic',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUserGroup($userGroup), 'nullable'],
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.piggy_bank_name' => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_url' => 'min:1|max:255|nullable|url',
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
|
||||
// TODO include location and ability to process it.
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
/** @var UserGroup $userGroup */
|
||||
$userGroup = $this->getUserGroup();
|
||||
$validator->after(
|
||||
function (Validator $validator) use ($user, $userGroup): void {
|
||||
// must be valid array.
|
||||
$this->validateTransactionArray($validator); // does not need group validation.
|
||||
|
||||
// must submit at least one transaction.
|
||||
app('log')->debug('Now going to validateOneTransaction');
|
||||
$this->validateOneTransaction($validator); // does not need group validation.
|
||||
app('log')->debug('Now done with validateOneTransaction');
|
||||
|
||||
// all journals must have a description
|
||||
$this->validateDescriptions($validator); // does not need group validation.
|
||||
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypes($validator); // does not need group validation.
|
||||
|
||||
// validate foreign currency info
|
||||
$this->validateForeignCurrencyInformation($validator); // does not need group validation.
|
||||
|
||||
// validate all account info
|
||||
$this->validateAccountInformation($validator, $user, $userGroup);
|
||||
|
||||
// validate source/destination is equal, depending on the transaction journal type.
|
||||
$this->validateEqualAccounts($validator);
|
||||
|
||||
// the group must have a description if > 1 journal.
|
||||
$this->validateGroupDescription($validator);
|
||||
}
|
||||
);
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,127 +91,6 @@ class UpdateRequest extends Request
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
$validProtocols = config('firefly.valid_url_protocols');
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => [new IsDateOrTime()],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// group id:
|
||||
'transactions.*.transaction_journal_id' => ['nullable', 'numeric', new BelongsUser()],
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidZeroOrMoreAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|min:1|max:1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()],
|
||||
'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255|nullable',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_url' => sprintf('min:1|max:255|nullable|url:%s', $validProtocols),
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in withValidator');
|
||||
|
||||
/** @var TransactionGroup $transactionGroup */
|
||||
$transactionGroup = $this->route()->parameter('userGroupTransaction');
|
||||
$validator->after(
|
||||
function (Validator $validator) use ($transactionGroup): void {
|
||||
// if more than one, verify that there are journal ID's present.
|
||||
$this->validateJournalIds($validator, $transactionGroup);
|
||||
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypesForUpdate($validator);
|
||||
|
||||
// user wants to update a reconciled transaction.
|
||||
// source, destination, amount + foreign_amount cannot be changed
|
||||
// and must be omitted from the request.
|
||||
$this->preventUpdateReconciled($validator, $transactionGroup);
|
||||
|
||||
// validate source/destination is equal, depending on the transaction journal type.
|
||||
$this->validateEqualAccountsForUpdate($validator, $transactionGroup);
|
||||
|
||||
// see method:
|
||||
// $this->preventNoAccountInfo($validator, );
|
||||
|
||||
// validate that the currency fits the source and/or destination account.
|
||||
// validate all account info
|
||||
$this->validateAccountInformationUpdate($validator, $transactionGroup);
|
||||
}
|
||||
);
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction data.
|
||||
*
|
||||
@ -259,7 +138,7 @@ class UpdateRequest extends Request
|
||||
{
|
||||
foreach ($this->integerFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->integerFromValue((string) $transaction[$fieldName]);
|
||||
$current[$fieldName] = $this->integerFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,7 +153,7 @@ class UpdateRequest extends Request
|
||||
{
|
||||
foreach ($this->stringFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->clearString((string) $transaction[$fieldName]);
|
||||
$current[$fieldName] = $this->clearString((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,7 +168,7 @@ class UpdateRequest extends Request
|
||||
{
|
||||
foreach ($this->textareaFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->clearStringKeepNewlines((string) $transaction[$fieldName]); // keep newlines
|
||||
$current[$fieldName] = $this->clearStringKeepNewlines((string)$transaction[$fieldName]); // keep newlines
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,8 +184,8 @@ class UpdateRequest extends Request
|
||||
foreach ($this->dateFields as $fieldName) {
|
||||
app('log')->debug(sprintf('Now at date field %s', $fieldName));
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
app('log')->debug(sprintf('New value: "%s"', (string) $transaction[$fieldName]));
|
||||
$current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]);
|
||||
app('log')->debug(sprintf('New value: "%s"', (string)$transaction[$fieldName]));
|
||||
$current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,7 +200,7 @@ class UpdateRequest extends Request
|
||||
{
|
||||
foreach ($this->booleanFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->convertBoolean((string) $transaction[$fieldName]);
|
||||
$current[$fieldName] = $this->convertBoolean((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,11 +235,132 @@ class UpdateRequest extends Request
|
||||
$current[$fieldName] = sprintf('%.12f', $value);
|
||||
}
|
||||
if (!is_float($value)) {
|
||||
$current[$fieldName] = (string) $value;
|
||||
$current[$fieldName] = (string)$value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
$validProtocols = config('firefly.valid_url_protocols');
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => [new IsDateOrTime()],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// group id:
|
||||
'transactions.*.transaction_journal_id' => ['nullable', 'numeric', new BelongsUser()],
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidZeroOrMoreAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|min:1|max:1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()],
|
||||
'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255|nullable',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_url' => sprintf('min:1|max:255|nullable|url:%s', $validProtocols),
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in withValidator');
|
||||
|
||||
/** @var TransactionGroup $transactionGroup */
|
||||
$transactionGroup = $this->route()->parameter('userGroupTransaction');
|
||||
$validator->after(
|
||||
function (Validator $validator) use ($transactionGroup): void {
|
||||
// if more than one, verify that there are journal ID's present.
|
||||
$this->validateJournalIds($validator, $transactionGroup);
|
||||
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypesForUpdate($validator);
|
||||
|
||||
// user wants to update a reconciled transaction.
|
||||
// source, destination, amount + foreign_amount cannot be changed
|
||||
// and must be omitted from the request.
|
||||
$this->preventUpdateReconciled($validator, $transactionGroup);
|
||||
|
||||
// validate source/destination is equal, depending on the transaction journal type.
|
||||
$this->validateEqualAccountsForUpdate($validator, $transactionGroup);
|
||||
|
||||
// see method:
|
||||
// $this->preventNoAccountInfo($validator, );
|
||||
|
||||
// validate that the currency fits the source and/or destination account.
|
||||
// validate all account info
|
||||
$this->validateAccountInformationUpdate($validator, $transactionGroup);
|
||||
}
|
||||
);
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ class DeleteEmptyGroups extends Command
|
||||
{
|
||||
$groupIds
|
||||
= TransactionGroup::leftJoin('transaction_journals', 'transaction_groups.id', '=', 'transaction_journals.transaction_group_id')
|
||||
->whereNull('transaction_journals.id')->get(['transaction_groups.id'])->pluck('id')->toArray()
|
||||
->whereNull('transaction_journals.id')->get(['transaction_groups.id'])->pluck('id')->toArray()
|
||||
;
|
||||
|
||||
$total = count($groupIds);
|
||||
|
@ -248,29 +248,14 @@ class FixAccountTypes extends Command
|
||||
}
|
||||
}
|
||||
|
||||
private function isLiability(string $destinationType): bool
|
||||
{
|
||||
return AccountType::LOAN === $destinationType || AccountType::DEBT === $destinationType || AccountType::MORTGAGE === $destinationType;
|
||||
}
|
||||
|
||||
private function shouldBeTransfer(string $transactionType, string $sourceType, string $destinationType): bool
|
||||
{
|
||||
return TransactionType::TRANSFER === $transactionType && AccountType::ASSET === $sourceType && $this->isLiability($destinationType);
|
||||
}
|
||||
|
||||
private function shouldBeDeposit(string $transactionType, string $sourceType, string $destinationType): bool
|
||||
private function isLiability(string $destinationType): bool
|
||||
{
|
||||
return TransactionType::TRANSFER === $transactionType && $this->isLiability($sourceType) && AccountType::ASSET === $destinationType;
|
||||
}
|
||||
|
||||
private function shouldGoToExpenseAccount(string $transactionType, string $sourceType, string $destinationType): bool
|
||||
{
|
||||
return TransactionType::WITHDRAWAL === $transactionType && AccountType::ASSET === $sourceType && AccountType::REVENUE === $destinationType;
|
||||
}
|
||||
|
||||
private function shouldComeFromRevenueAccount(string $transactionType, string $sourceType, string $destinationType): bool
|
||||
{
|
||||
return TransactionType::DEPOSIT === $transactionType && AccountType::EXPENSE === $sourceType && AccountType::ASSET === $destinationType;
|
||||
return AccountType::LOAN === $destinationType || AccountType::DEBT === $destinationType || AccountType::MORTGAGE === $destinationType;
|
||||
}
|
||||
|
||||
private function makeTransfer(TransactionJournal $journal): void
|
||||
@ -286,6 +271,11 @@ class FixAccountTypes extends Command
|
||||
$this->inspectJournal($journal);
|
||||
}
|
||||
|
||||
private function shouldBeDeposit(string $transactionType, string $sourceType, string $destinationType): bool
|
||||
{
|
||||
return TransactionType::TRANSFER === $transactionType && $this->isLiability($sourceType) && AccountType::ASSET === $destinationType;
|
||||
}
|
||||
|
||||
private function makeDeposit(TransactionJournal $journal): void
|
||||
{
|
||||
// from a liability to an asset should be a deposit.
|
||||
@ -299,6 +289,11 @@ class FixAccountTypes extends Command
|
||||
$this->inspectJournal($journal);
|
||||
}
|
||||
|
||||
private function shouldGoToExpenseAccount(string $transactionType, string $sourceType, string $destinationType): bool
|
||||
{
|
||||
return TransactionType::WITHDRAWAL === $transactionType && AccountType::ASSET === $sourceType && AccountType::REVENUE === $destinationType;
|
||||
}
|
||||
|
||||
private function makeExpenseDestination(TransactionJournal $journal, Transaction $destination): void
|
||||
{
|
||||
// withdrawals with a revenue account as destination instead of an expense account.
|
||||
@ -320,6 +315,11 @@ class FixAccountTypes extends Command
|
||||
$this->inspectJournal($journal);
|
||||
}
|
||||
|
||||
private function shouldComeFromRevenueAccount(string $transactionType, string $sourceType, string $destinationType): bool
|
||||
{
|
||||
return TransactionType::DEPOSIT === $transactionType && AccountType::EXPENSE === $sourceType && AccountType::ASSET === $destinationType;
|
||||
}
|
||||
|
||||
private function makeRevenueSource(TransactionJournal $journal, Transaction $source): void
|
||||
{
|
||||
// deposits with an expense account as source instead of a revenue account.
|
||||
@ -355,11 +355,6 @@ class FixAccountTypes extends Command
|
||||
return in_array($accountType, $validTypes, true);
|
||||
}
|
||||
|
||||
private function canCreateDestination(array $validDestinations): bool
|
||||
{
|
||||
return in_array(AccountTypeEnum::EXPENSE->value, $validDestinations, true);
|
||||
}
|
||||
|
||||
private function giveNewRevenue(TransactionJournal $journal, Transaction $source): void
|
||||
{
|
||||
app('log')->debug(sprintf('An account of type "%s" could be a valid source.', AccountTypeEnum::REVENUE->value));
|
||||
@ -373,6 +368,11 @@ class FixAccountTypes extends Command
|
||||
$this->inspectJournal($journal);
|
||||
}
|
||||
|
||||
private function canCreateDestination(array $validDestinations): bool
|
||||
{
|
||||
return in_array(AccountTypeEnum::EXPENSE->value, $validDestinations, true);
|
||||
}
|
||||
|
||||
private function giveNewExpense(TransactionJournal $journal, Transaction $destination): void
|
||||
{
|
||||
app('log')->debug(sprintf('An account of type "%s" could be a valid destination.', AccountTypeEnum::EXPENSE->value));
|
||||
|
@ -57,6 +57,19 @@ class CreateGroupMemberships extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function createGroupMemberships(): void
|
||||
{
|
||||
$users = User::get();
|
||||
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
self::createGroupMembership($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO move to helper.
|
||||
*
|
||||
@ -93,17 +106,4 @@ class CreateGroupMemberships extends Command
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function createGroupMemberships(): void
|
||||
{
|
||||
$users = User::get();
|
||||
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
self::createGroupMembership($user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,19 +74,19 @@ class ForceDecimalSize extends Command
|
||||
private string $regularExpression;
|
||||
private array $tables
|
||||
= [
|
||||
'accounts' => ['virtual_balance'],
|
||||
'auto_budgets' => ['amount'],
|
||||
'available_budgets' => ['amount'],
|
||||
'bills' => ['amount_min', 'amount_max'],
|
||||
'budget_limits' => ['amount'],
|
||||
'currency_exchange_rates' => ['rate', 'user_rate'],
|
||||
'limit_repetitions' => ['amount'],
|
||||
'piggy_bank_events' => ['amount'],
|
||||
'piggy_bank_repetitions' => ['currentamount'],
|
||||
'piggy_banks' => ['targetamount'],
|
||||
'recurrences_transactions' => ['amount', 'foreign_amount'],
|
||||
'transactions' => ['amount', 'foreign_amount'],
|
||||
];
|
||||
'accounts' => ['virtual_balance'],
|
||||
'auto_budgets' => ['amount'],
|
||||
'available_budgets' => ['amount'],
|
||||
'bills' => ['amount_min', 'amount_max'],
|
||||
'budget_limits' => ['amount'],
|
||||
'currency_exchange_rates' => ['rate', 'user_rate'],
|
||||
'limit_repetitions' => ['amount'],
|
||||
'piggy_bank_events' => ['amount'],
|
||||
'piggy_bank_repetitions' => ['currentamount'],
|
||||
'piggy_banks' => ['targetamount'],
|
||||
'recurrences_transactions' => ['amount', 'foreign_amount'],
|
||||
'transactions' => ['amount', 'foreign_amount'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
|
@ -71,7 +71,7 @@ class MigrateRecurrenceMeta extends Command
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool) $configVar->data;
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -104,7 +104,7 @@ class MigrateToGroups extends Command
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool) $configVar->data;
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -193,22 +193,6 @@ class MigrateToGroups extends Command
|
||||
);
|
||||
}
|
||||
|
||||
private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction
|
||||
{
|
||||
$set = $journal->transactions->filter(
|
||||
static function (Transaction $subject) use ($transaction) {
|
||||
$amount = (float) $transaction->amount * -1 === (float) $subject->amount; // intentional float
|
||||
$identifier = $transaction->identifier === $subject->identifier;
|
||||
app('log')->debug(sprintf('Amount the same? %s', var_export($amount, true)));
|
||||
app('log')->debug(sprintf('ID the same? %s', var_export($identifier, true)));
|
||||
|
||||
return $amount && $identifier;
|
||||
}
|
||||
);
|
||||
|
||||
return $set->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
*/
|
||||
@ -299,6 +283,22 @@ class MigrateToGroups extends Command
|
||||
];
|
||||
}
|
||||
|
||||
private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction
|
||||
{
|
||||
$set = $journal->transactions->filter(
|
||||
static function (Transaction $subject) use ($transaction) {
|
||||
$amount = (float)$transaction->amount * -1 === (float)$subject->amount; // intentional float
|
||||
$identifier = $transaction->identifier === $subject->identifier;
|
||||
app('log')->debug(sprintf('Amount the same? %s', var_export($amount, true)));
|
||||
app('log')->debug(sprintf('ID the same? %s', var_export($identifier, true)));
|
||||
|
||||
return $amount && $identifier;
|
||||
}
|
||||
);
|
||||
|
||||
return $set->first();
|
||||
}
|
||||
|
||||
private function getTransactionBudget(Transaction $left, Transaction $right): ?int
|
||||
{
|
||||
app('log')->debug('Now in getTransactionBudget()');
|
||||
|
@ -128,7 +128,7 @@ class Handler extends ExceptionHandler
|
||||
$errorCode = 500;
|
||||
$errorCode = $e instanceof MethodNotAllowedHttpException ? 405 : $errorCode;
|
||||
|
||||
$isDebug = (bool) config('app.debug', false);
|
||||
$isDebug = (bool)config('app.debug', false);
|
||||
if ($isDebug) {
|
||||
app('log')->debug(sprintf('Return JSON %s with debug.', get_class($e)));
|
||||
|
||||
@ -185,7 +185,7 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
public function report(\Throwable $e): void
|
||||
{
|
||||
$doMailError = (bool) config('firefly.send_error_message');
|
||||
$doMailError = (bool)config('firefly.send_error_message');
|
||||
if ($this->shouldntReportLocal($e) || !$doMailError) {
|
||||
parent::report($e);
|
||||
|
||||
@ -221,12 +221,22 @@ class Handler extends ExceptionHandler
|
||||
|
||||
// create job that will mail.
|
||||
$ipAddress = request()->ip() ?? '0.0.0.0';
|
||||
$job = new MailError($userData, (string) config('firefly.site_owner'), $ipAddress, $data);
|
||||
$job = new MailError($userData, (string)config('firefly.site_owner'), $ipAddress, $data);
|
||||
dispatch($job);
|
||||
|
||||
parent::report($e);
|
||||
}
|
||||
|
||||
private function shouldntReportLocal(\Throwable $e): bool
|
||||
{
|
||||
return null !== Arr::first(
|
||||
$this->dontReport,
|
||||
static function ($type) use ($e) {
|
||||
return $e instanceof $type;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a validation exception into a response.
|
||||
*
|
||||
@ -244,16 +254,6 @@ class Handler extends ExceptionHandler
|
||||
;
|
||||
}
|
||||
|
||||
private function shouldntReportLocal(\Throwable $e): bool
|
||||
{
|
||||
return null !== Arr::first(
|
||||
$this->dontReport,
|
||||
static function ($type) use ($e) {
|
||||
return $e instanceof $type;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only return the redirectTo property from the exception if it is a valid URL. Return NULL otherwise.
|
||||
*/
|
||||
|
@ -48,7 +48,7 @@ final class IntervalException extends \Exception
|
||||
Periodicity $periodicity,
|
||||
array $intervals,
|
||||
int $code = 0,
|
||||
?\Throwable $previous = null
|
||||
?\Throwable $previous = null
|
||||
): self {
|
||||
$message = sprintf(
|
||||
'The periodicity %s is unknown. Choose one of available periodicity: %s',
|
||||
|
@ -121,21 +121,6 @@ class AccountFactory
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function find(string $accountName, string $accountType): ?Account
|
||||
{
|
||||
app('log')->debug(sprintf('Now in AccountFactory::find("%s", "%s")', $accountName, $accountType));
|
||||
$type = AccountType::whereType($accountType)->first();
|
||||
|
||||
// @var Account|null
|
||||
return $this->user->accounts()->where('account_type_id', $type->id)->where('name', $accountName)->first();
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->accountRepository->setUser($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
@ -169,6 +154,15 @@ class AccountFactory
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function find(string $accountName, string $accountType): ?Account
|
||||
{
|
||||
app('log')->debug(sprintf('Now in AccountFactory::find("%s", "%s")', $accountName, $accountType));
|
||||
$type = AccountType::whereType($accountType)->first();
|
||||
|
||||
// @var Account|null
|
||||
return $this->user->accounts()->where('account_type_id', $type->id)->where('name', $accountName)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
@ -365,4 +359,10 @@ class AccountFactory
|
||||
$updateService->setUser($account->user);
|
||||
$updateService->update($account, ['order' => $order]);
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->accountRepository->setUser($user);
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ class RecurrenceFactory
|
||||
$firstDate = $data['recurrence']['first_date'];
|
||||
}
|
||||
if (array_key_exists('nr_of_repetitions', $data['recurrence'])) {
|
||||
$repetitions = (int) $data['recurrence']['nr_of_repetitions'];
|
||||
$repetitions = (int)$data['recurrence']['nr_of_repetitions'];
|
||||
}
|
||||
if (array_key_exists('repeat_until', $data['recurrence'])) {
|
||||
$repeatUntil = $data['recurrence']['repeat_until'];
|
||||
@ -116,7 +116,7 @@ class RecurrenceFactory
|
||||
$recurrence->save();
|
||||
|
||||
if (array_key_exists('notes', $data['recurrence'])) {
|
||||
$this->updateNote($recurrence, (string) $data['recurrence']['notes']);
|
||||
$this->updateNote($recurrence, (string)$data['recurrence']['notes']);
|
||||
}
|
||||
|
||||
$this->createRepetitions($recurrence, $data['repetitions'] ?? []);
|
||||
|
@ -72,64 +72,6 @@ class TransactionFactory
|
||||
return $this->create(app('steam')->negative($amount), $foreignAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create transaction with positive amount (for destination accounts).
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function createPositive(string $amount, ?string $foreignAmount): Transaction
|
||||
{
|
||||
if ('' === $foreignAmount) {
|
||||
$foreignAmount = null;
|
||||
}
|
||||
if (null !== $foreignAmount) {
|
||||
$foreignAmount = app('steam')->positive($foreignAmount);
|
||||
}
|
||||
|
||||
return $this->create(app('steam')->positive($amount), $foreignAmount);
|
||||
}
|
||||
|
||||
public function setAccount(Account $account): void
|
||||
{
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
public function setAccountInformation(array $accountInformation): void
|
||||
{
|
||||
$this->accountInformation = $accountInformation;
|
||||
}
|
||||
|
||||
public function setCurrency(TransactionCurrency $currency): void
|
||||
{
|
||||
$this->currency = $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|TransactionCurrency $foreignCurrency |null
|
||||
*/
|
||||
public function setForeignCurrency(?TransactionCurrency $foreignCurrency): void
|
||||
{
|
||||
$this->foreignCurrency = $foreignCurrency;
|
||||
}
|
||||
|
||||
public function setJournal(TransactionJournal $journal): void
|
||||
{
|
||||
$this->journal = $journal;
|
||||
}
|
||||
|
||||
public function setReconciled(bool $reconciled): void
|
||||
{
|
||||
$this->reconciled = $reconciled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
// empty function.
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
@ -224,4 +166,62 @@ class TransactionFactory
|
||||
$service = app(AccountUpdateService::class);
|
||||
$service->update($this->account, ['iban' => $this->accountInformation['iban']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create transaction with positive amount (for destination accounts).
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function createPositive(string $amount, ?string $foreignAmount): Transaction
|
||||
{
|
||||
if ('' === $foreignAmount) {
|
||||
$foreignAmount = null;
|
||||
}
|
||||
if (null !== $foreignAmount) {
|
||||
$foreignAmount = app('steam')->positive($foreignAmount);
|
||||
}
|
||||
|
||||
return $this->create(app('steam')->positive($amount), $foreignAmount);
|
||||
}
|
||||
|
||||
public function setAccount(Account $account): void
|
||||
{
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
public function setAccountInformation(array $accountInformation): void
|
||||
{
|
||||
$this->accountInformation = $accountInformation;
|
||||
}
|
||||
|
||||
public function setCurrency(TransactionCurrency $currency): void
|
||||
{
|
||||
$this->currency = $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|TransactionCurrency $foreignCurrency |null
|
||||
*/
|
||||
public function setForeignCurrency(?TransactionCurrency $foreignCurrency): void
|
||||
{
|
||||
$this->foreignCurrency = $foreignCurrency;
|
||||
}
|
||||
|
||||
public function setJournal(TransactionJournal $journal): void
|
||||
{
|
||||
$this->journal = $journal;
|
||||
}
|
||||
|
||||
public function setReconciled(bool $reconciled): void
|
||||
{
|
||||
$this->reconciled = $reconciled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
// empty function.
|
||||
}
|
||||
}
|
||||
|
@ -141,49 +141,6 @@ class TransactionJournalFactory
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user.
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->currencyRepository->setUser($this->user);
|
||||
$this->tagFactory->setUser($user);
|
||||
$this->billRepository->setUser($this->user);
|
||||
$this->budgetRepository->setUser($this->user);
|
||||
$this->categoryRepository->setUser($this->user);
|
||||
$this->piggyRepository->setUser($this->user);
|
||||
$this->accountRepository->setUser($this->user);
|
||||
}
|
||||
|
||||
public function setErrorOnHash(bool $errorOnHash): void
|
||||
{
|
||||
$this->errorOnHash = $errorOnHash;
|
||||
if (true === $errorOnHash) {
|
||||
app('log')->info('Will trigger duplication alert for this journal.');
|
||||
}
|
||||
}
|
||||
|
||||
protected function storeMeta(TransactionJournal $journal, NullArrayObject $data, string $field): void
|
||||
{
|
||||
$set = [
|
||||
'journal' => $journal,
|
||||
'name' => $field,
|
||||
'data' => (string) ($data[$field] ?? ''),
|
||||
];
|
||||
if ($data[$field] instanceof Carbon) {
|
||||
$data[$field]->setTimezone(config('app.timezone'));
|
||||
app('log')->debug(sprintf('%s Date: %s (%s)', $field, $data[$field], $data[$field]->timezone->getName()));
|
||||
$set['data'] = $data[$field]->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
app('log')->debug(sprintf('Going to store meta-field "%s", with value "%s".', $set['name'], $set['data']));
|
||||
|
||||
/** @var TransactionJournalMetaFactory $factory */
|
||||
$factory = app(TransactionJournalMetaFactory::class);
|
||||
$factory->updateOrCreate($set);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO typeOverrule: the account validator may have another opinion on the transaction type. not sure what to do
|
||||
* with this.
|
||||
@ -203,11 +160,11 @@ class TransactionJournalFactory
|
||||
$type = $this->typeRepository->findTransactionType(null, $row['type']);
|
||||
$carbon = $row['date'] ?? today(config('app.timezone'));
|
||||
$order = $row['order'] ?? 0;
|
||||
$currency = $this->currencyRepository->findCurrency((int) $row['currency_id'], $row['currency_code']);
|
||||
$currency = $this->currencyRepository->findCurrency((int)$row['currency_id'], $row['currency_code']);
|
||||
$foreignCurrency = $this->currencyRepository->findCurrencyNull($row['foreign_currency_id'], $row['foreign_currency_code']);
|
||||
$bill = $this->billRepository->findBill((int) $row['bill_id'], $row['bill_name']);
|
||||
$bill = $this->billRepository->findBill((int)$row['bill_id'], $row['bill_name']);
|
||||
$billId = TransactionType::WITHDRAWAL === $type->type && null !== $bill ? $bill->id : null;
|
||||
$description = (string) $row['description'];
|
||||
$description = (string)$row['description'];
|
||||
|
||||
// Manipulate basic fields
|
||||
$carbon->setTimezone(config('app.timezone'));
|
||||
@ -286,7 +243,7 @@ class TransactionJournalFactory
|
||||
$transactionFactory->setReconciled($row['reconciled'] ?? false);
|
||||
|
||||
try {
|
||||
$negative = $transactionFactory->createNegative((string) $row['amount'], (string) $row['foreign_amount']);
|
||||
$negative = $transactionFactory->createNegative((string)$row['amount'], (string)$row['foreign_amount']);
|
||||
} catch (FireflyException $e) {
|
||||
app('log')->error(sprintf('Exception creating negative transaction: %s', $e->getMessage()));
|
||||
$this->forceDeleteOnError(new Collection([$journal]));
|
||||
@ -305,7 +262,7 @@ class TransactionJournalFactory
|
||||
$transactionFactory->setReconciled($row['reconciled'] ?? false);
|
||||
|
||||
try {
|
||||
$transactionFactory->createPositive((string) $row['amount'], (string) $row['foreign_amount']);
|
||||
$transactionFactory->createPositive((string)$row['amount'], (string)$row['foreign_amount']);
|
||||
} catch (FireflyException $e) {
|
||||
app('log')->error(sprintf('Exception creating positive transaction: %s', $e->getMessage()));
|
||||
$this->forceTrDelete($negative);
|
||||
@ -326,18 +283,6 @@ class TransactionJournalFactory
|
||||
return $journal;
|
||||
}
|
||||
|
||||
private function storeLocation(TransactionJournal $journal, NullArrayObject $data): void
|
||||
{
|
||||
if (true === $data['store_location']) {
|
||||
$location = new Location();
|
||||
$location->longitude = $data['longitude'];
|
||||
$location->latitude = $data['latitude'];
|
||||
$location->zoom_level = $data['zoom_level'];
|
||||
$location->locatable()->associate($journal);
|
||||
$location->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function hashArray(NullArrayObject $row): string
|
||||
{
|
||||
$dataRow = $row->getArrayCopy();
|
||||
@ -382,7 +327,7 @@ class TransactionJournalFactory
|
||||
app('log')->warning(sprintf('Found a duplicate in errorIfDuplicate because hash %s is not unique!', $hash));
|
||||
$journal = $result->transactionJournal()->withTrashed()->first();
|
||||
$group = $journal?->transactionGroup()->withTrashed()->first();
|
||||
$groupId = (int) $group?->id;
|
||||
$groupId = (int)$group?->id;
|
||||
|
||||
throw new DuplicateTransactionException(sprintf('Duplicate of transaction #%d.', $groupId));
|
||||
}
|
||||
@ -400,10 +345,10 @@ class TransactionJournalFactory
|
||||
|
||||
// validate source account.
|
||||
$array = [
|
||||
'id' => null !== $data['source_id'] ? (int) $data['source_id'] : null,
|
||||
'name' => null !== $data['source_name'] ? (string) $data['source_name'] : null,
|
||||
'iban' => null !== $data['source_iban'] ? (string) $data['source_iban'] : null,
|
||||
'number' => null !== $data['source_number'] ? (string) $data['source_number'] : null,
|
||||
'id' => null !== $data['source_id'] ? (int)$data['source_id'] : null,
|
||||
'name' => null !== $data['source_name'] ? (string)$data['source_name'] : null,
|
||||
'iban' => null !== $data['source_iban'] ? (string)$data['source_iban'] : null,
|
||||
'number' => null !== $data['source_number'] ? (string)$data['source_number'] : null,
|
||||
];
|
||||
$validSource = $this->accountValidator->validateSource($array);
|
||||
|
||||
@ -415,10 +360,10 @@ class TransactionJournalFactory
|
||||
|
||||
// validate destination account
|
||||
$array = [
|
||||
'id' => null !== $data['destination_id'] ? (int) $data['destination_id'] : null,
|
||||
'name' => null !== $data['destination_name'] ? (string) $data['destination_name'] : null,
|
||||
'iban' => null !== $data['destination_iban'] ? (string) $data['destination_iban'] : null,
|
||||
'number' => null !== $data['destination_number'] ? (string) $data['destination_number'] : null,
|
||||
'id' => null !== $data['destination_id'] ? (int)$data['destination_id'] : null,
|
||||
'name' => null !== $data['destination_name'] ? (string)$data['destination_name'] : null,
|
||||
'iban' => null !== $data['destination_iban'] ? (string)$data['destination_iban'] : null,
|
||||
'number' => null !== $data['destination_number'] ? (string)$data['destination_number'] : null,
|
||||
];
|
||||
|
||||
$validDestination = $this->accountValidator->validateDestination($array);
|
||||
@ -428,6 +373,21 @@ class TransactionJournalFactory
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user.
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->currencyRepository->setUser($this->user);
|
||||
$this->tagFactory->setUser($user);
|
||||
$this->billRepository->setUser($this->user);
|
||||
$this->budgetRepository->setUser($this->user);
|
||||
$this->categoryRepository->setUser($this->user);
|
||||
$this->piggyRepository->setUser($this->user);
|
||||
$this->accountRepository->setUser($this->user);
|
||||
}
|
||||
|
||||
private function reconciliationSanityCheck(?Account $sourceAccount, ?Account $destinationAccount): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
@ -550,7 +510,7 @@ class TransactionJournalFactory
|
||||
{
|
||||
app('log')->debug('Will now store piggy event.');
|
||||
|
||||
$piggyBank = $this->piggyRepository->findPiggyBank((int) $data['piggy_bank_id'], $data['piggy_bank_name']);
|
||||
$piggyBank = $this->piggyRepository->findPiggyBank((int)$data['piggy_bank_id'], $data['piggy_bank_name']);
|
||||
|
||||
if (null !== $piggyBank) {
|
||||
$this->piggyEventFactory->create($journal, $piggyBank);
|
||||
@ -567,4 +527,44 @@ class TransactionJournalFactory
|
||||
$this->storeMeta($journal, $transaction, $field);
|
||||
}
|
||||
}
|
||||
|
||||
protected function storeMeta(TransactionJournal $journal, NullArrayObject $data, string $field): void
|
||||
{
|
||||
$set = [
|
||||
'journal' => $journal,
|
||||
'name' => $field,
|
||||
'data' => (string)($data[$field] ?? ''),
|
||||
];
|
||||
if ($data[$field] instanceof Carbon) {
|
||||
$data[$field]->setTimezone(config('app.timezone'));
|
||||
app('log')->debug(sprintf('%s Date: %s (%s)', $field, $data[$field], $data[$field]->timezone->getName()));
|
||||
$set['data'] = $data[$field]->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
app('log')->debug(sprintf('Going to store meta-field "%s", with value "%s".', $set['name'], $set['data']));
|
||||
|
||||
/** @var TransactionJournalMetaFactory $factory */
|
||||
$factory = app(TransactionJournalMetaFactory::class);
|
||||
$factory->updateOrCreate($set);
|
||||
}
|
||||
|
||||
private function storeLocation(TransactionJournal $journal, NullArrayObject $data): void
|
||||
{
|
||||
if (true === $data['store_location']) {
|
||||
$location = new Location();
|
||||
$location->longitude = $data['longitude'];
|
||||
$location->latitude = $data['latitude'];
|
||||
$location->zoom_level = $data['zoom_level'];
|
||||
$location->locatable()->associate($journal);
|
||||
$location->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function setErrorOnHash(bool $errorOnHash): void
|
||||
{
|
||||
$this->errorOnHash = $errorOnHash;
|
||||
if (true === $errorOnHash) {
|
||||
app('log')->info('Will trigger duplication alert for this journal.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,14 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the preferred period.
|
||||
*/
|
||||
protected function preferredPeriod(): string
|
||||
{
|
||||
return 'day';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set accounts.
|
||||
*/
|
||||
@ -130,12 +138,4 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the preferred period.
|
||||
*/
|
||||
protected function preferredPeriod(): string
|
||||
{
|
||||
return 'day';
|
||||
}
|
||||
}
|
||||
|
@ -125,26 +125,6 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the involved budgets.
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): ReportGeneratorInterface
|
||||
{
|
||||
$this->budgets = $budgets;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the involved accounts.
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the expenses.
|
||||
*/
|
||||
@ -170,4 +150,24 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
|
||||
return $journals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the involved budgets.
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): ReportGeneratorInterface
|
||||
{
|
||||
$this->budgets = $budgets;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the involved accounts.
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -124,26 +124,6 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the categories involved in this report.
|
||||
*/
|
||||
public function setCategories(Collection $categories): ReportGeneratorInterface
|
||||
{
|
||||
$this->categories = $categories;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the involved accounts.
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the expenses for this report.
|
||||
*/
|
||||
@ -168,6 +148,26 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the categories involved in this report.
|
||||
*/
|
||||
public function setCategories(Collection $categories): ReportGeneratorInterface
|
||||
{
|
||||
$this->categories = $categories;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the involved accounts.
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the income for this report.
|
||||
*/
|
||||
|
@ -49,20 +49,6 @@ class BudgetLimitHandler
|
||||
$this->updateAvailableBudget($event->budgetLimit);
|
||||
}
|
||||
|
||||
public function deleted(Deleted $event): void
|
||||
{
|
||||
app('log')->debug(sprintf('BudgetLimitHandler::deleted(#%s)', $event->budgetLimit->id));
|
||||
$budgetLimit = $event->budgetLimit;
|
||||
$budgetLimit->id = 0;
|
||||
$this->updateAvailableBudget($event->budgetLimit);
|
||||
}
|
||||
|
||||
public function updated(Updated $event): void
|
||||
{
|
||||
app('log')->debug(sprintf('BudgetLimitHandler::updated(#%s)', $event->budgetLimit->id));
|
||||
$this->updateAvailableBudget($event->budgetLimit);
|
||||
}
|
||||
|
||||
private function updateAvailableBudget(BudgetLimit $budgetLimit): void
|
||||
{
|
||||
app('log')->debug(sprintf('Now in updateAvailableBudget(#%d)', $budgetLimit->id));
|
||||
@ -248,4 +234,18 @@ class BudgetLimitHandler
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
public function deleted(Deleted $event): void
|
||||
{
|
||||
app('log')->debug(sprintf('BudgetLimitHandler::deleted(#%s)', $event->budgetLimit->id));
|
||||
$budgetLimit = $event->budgetLimit;
|
||||
$budgetLimit->id = 0;
|
||||
$this->updateAvailableBudget($event->budgetLimit);
|
||||
}
|
||||
|
||||
public function updated(Updated $event): void
|
||||
{
|
||||
app('log')->debug(sprintf('BudgetLimitHandler::updated(#%s)', $event->budgetLimit->id));
|
||||
$this->updateAvailableBudget($event->budgetLimit);
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ class UserEventHandler
|
||||
|
||||
public function sendAdminRegistrationNotification(RegisteredUser $event): void
|
||||
{
|
||||
$sendMail = (bool) app('fireflyconfig')->get('notification_admin_new_reg', true)->data;
|
||||
$sendMail = (bool)app('fireflyconfig')->get('notification_admin_new_reg', true)->data;
|
||||
if ($sendMail) {
|
||||
/** @var UserRepositoryInterface $repository */
|
||||
$repository = app(UserRepositoryInterface::class);
|
||||
@ -285,7 +285,7 @@ class UserEventHandler
|
||||
$oldEmail = $event->oldEmail;
|
||||
$user = $event->user;
|
||||
$token = app('preferences')->getForUser($user, 'email_change_undo_token', 'invalid');
|
||||
$hashed = hash('sha256', sprintf('%s%s', (string) config('app.key'), $oldEmail));
|
||||
$hashed = hash('sha256', sprintf('%s%s', (string)config('app.key'), $oldEmail));
|
||||
$url = route('profile.undo-email-change', [$token->data, $hashed]);
|
||||
|
||||
try {
|
||||
@ -347,7 +347,7 @@ class UserEventHandler
|
||||
*/
|
||||
public function sendRegistrationMail(RegisteredUser $event): void
|
||||
{
|
||||
$sendMail = (bool) app('fireflyconfig')->get('notification_user_new_reg', true)->data;
|
||||
$sendMail = (bool)app('fireflyconfig')->get('notification_user_new_reg', true)->data;
|
||||
if ($sendMail) {
|
||||
try {
|
||||
Notification::send($event->user, new UserRegistrationNotification());
|
||||
|
@ -81,6 +81,27 @@ trait AttachmentCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join table to get attachment information.
|
||||
*/
|
||||
private function joinAttachmentTables(): void
|
||||
{
|
||||
if (false === $this->hasJoinedAttTables) {
|
||||
// join some extra tables:
|
||||
$this->hasJoinedAttTables = true;
|
||||
$this->query->leftJoin('attachments', 'attachments.attachable_id', '=', 'transaction_journals.id')
|
||||
->where(
|
||||
static function (EloquentBuilder $q1): void { // @phpstan-ignore-line
|
||||
$q1->where('attachments.attachable_type', TransactionJournal::class);
|
||||
$q1->where('attachments.uploaded', true);
|
||||
$q1->whereNull('attachments.deleted_at');
|
||||
$q1->orWhereNull('attachments.attachable_type');
|
||||
}
|
||||
)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public function withAttachmentInformation(): GroupCollectorInterface
|
||||
{
|
||||
$this->fields[] = 'attachments.id as attachment_id';
|
||||
@ -511,25 +532,4 @@ trait AttachmentCollection
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join table to get attachment information.
|
||||
*/
|
||||
private function joinAttachmentTables(): void
|
||||
{
|
||||
if (false === $this->hasJoinedAttTables) {
|
||||
// join some extra tables:
|
||||
$this->hasJoinedAttTables = true;
|
||||
$this->query->leftJoin('attachments', 'attachments.attachable_id', '=', 'transaction_journals.id')
|
||||
->where(
|
||||
static function (EloquentBuilder $q1): void { // @phpstan-ignore-line
|
||||
$q1->where('attachments.attachable_type', TransactionJournal::class);
|
||||
$q1->where('attachments.uploaded', true);
|
||||
$q1->whereNull('attachments.deleted_at');
|
||||
$q1->orWhereNull('attachments.attachable_type');
|
||||
}
|
||||
)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
trait CollectorProperties
|
||||
{
|
||||
public const string TEST = 'Test';
|
||||
private ?int $endRow;
|
||||
private bool $expandGroupSearch;
|
||||
private array $fields;
|
||||
private bool $hasAccountInfo;
|
||||
@ -49,10 +50,8 @@ trait CollectorProperties
|
||||
private ?int $page;
|
||||
private array $postFilters;
|
||||
private HasMany $query;
|
||||
private array $stringFields;
|
||||
|
||||
private ?int $startRow;
|
||||
private ?int $endRow;
|
||||
private array $stringFields;
|
||||
/*
|
||||
* This array is used to collect ALL tags the user may search for (using 'setTags').
|
||||
* This way the user can call 'setTags' multiple times and get a joined result.
|
||||
|
@ -171,6 +171,19 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join table to get tag information.
|
||||
*/
|
||||
protected function joinMetaDataTables(): void
|
||||
{
|
||||
if (false === $this->hasJoinedMetaTables) {
|
||||
$this->hasJoinedMetaTables = true;
|
||||
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
|
||||
$this->fields[] = 'journal_meta.name as meta_name';
|
||||
$this->fields[] = 'journal_meta.data as meta_data';
|
||||
}
|
||||
}
|
||||
|
||||
public function excludeExternalUrl(string $url): GroupCollectorInterface
|
||||
{
|
||||
$this->joinMetaDataTables();
|
||||
@ -369,6 +382,19 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join table to get tag information.
|
||||
*/
|
||||
protected function joinTagTables(): void
|
||||
{
|
||||
if (false === $this->hasJoinedTagTables) {
|
||||
// join some extra tables:
|
||||
$this->hasJoinedTagTables = true;
|
||||
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
$this->query->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id');
|
||||
}
|
||||
}
|
||||
|
||||
public function internalReferenceContains(string $internalReference): GroupCollectorInterface
|
||||
{
|
||||
$internalReference = (string)json_encode($internalReference);
|
||||
@ -539,6 +565,63 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to a SPECIFIC set of tags.
|
||||
*/
|
||||
public function setAllTags(Collection $tags): GroupCollectorInterface
|
||||
{
|
||||
Log::debug(sprintf('Now in setAllTags(%d tag(s))', $tags->count()));
|
||||
$this->withTagInformation();
|
||||
$this->query->whereNotNull('tag_transaction_journal.tag_id');
|
||||
|
||||
// this method adds a "postFilter" to the collector.
|
||||
$list = $tags->pluck('tag')->toArray();
|
||||
$list = array_map('strtolower', $list);
|
||||
$filter = static function (array $object) use ($list): bool|array {
|
||||
$includedJournals = [];
|
||||
$return = $object;
|
||||
unset($return['transactions']);
|
||||
$return['transactions'] = [];
|
||||
Log::debug(sprintf('Now in setAllTags(%s) filter', implode(', ', $list)));
|
||||
$expectedTagCount = count($list);
|
||||
$foundTagCount = 0;
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
$transactionTagCount = count($transaction['tags']);
|
||||
app('log')->debug(sprintf('Transaction #%d has %d tag(s)', $transaction['transaction_journal_id'], $transactionTagCount));
|
||||
if ($transactionTagCount < $expectedTagCount) {
|
||||
app('log')->debug(sprintf('Transaction has %d tag(s), we expect %d tag(s), return false.', $transactionTagCount, $expectedTagCount));
|
||||
|
||||
return false;
|
||||
}
|
||||
foreach ($transaction['tags'] as $tag) {
|
||||
Log::debug(sprintf('"%s" versus', strtolower($tag['name'])), $list);
|
||||
if (in_array(strtolower($tag['name']), $list, true)) {
|
||||
app('log')->debug(sprintf('Transaction has tag "%s" so count++.', $tag['name']));
|
||||
++$foundTagCount;
|
||||
$journalId = $transaction['transaction_journal_id'];
|
||||
// #8377 prevent adding a transaction twice when multiple tag searches find this transaction
|
||||
if (!in_array($journalId, $includedJournals, true)) {
|
||||
$includedJournals[] = $journalId;
|
||||
$return['transactions'][] = $transaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::debug(sprintf('Found %d tags, need at least %d.', $foundTagCount, $expectedTagCount));
|
||||
|
||||
// found at least the expected tags.
|
||||
$result = $foundTagCount >= $expectedTagCount;
|
||||
if (true === $result) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
$this->postFilters[] = $filter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the search to a specific bill.
|
||||
*/
|
||||
@ -669,63 +752,6 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to a SPECIFIC set of tags.
|
||||
*/
|
||||
public function setAllTags(Collection $tags): GroupCollectorInterface
|
||||
{
|
||||
Log::debug(sprintf('Now in setAllTags(%d tag(s))', $tags->count()));
|
||||
$this->withTagInformation();
|
||||
$this->query->whereNotNull('tag_transaction_journal.tag_id');
|
||||
|
||||
// this method adds a "postFilter" to the collector.
|
||||
$list = $tags->pluck('tag')->toArray();
|
||||
$list = array_map('strtolower', $list);
|
||||
$filter = static function (array $object) use ($list): bool|array {
|
||||
$includedJournals = [];
|
||||
$return = $object;
|
||||
unset($return['transactions']);
|
||||
$return['transactions'] = [];
|
||||
Log::debug(sprintf('Now in setAllTags(%s) filter', implode(', ', $list)));
|
||||
$expectedTagCount = count($list);
|
||||
$foundTagCount = 0;
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
$transactionTagCount = count($transaction['tags']);
|
||||
app('log')->debug(sprintf('Transaction #%d has %d tag(s)', $transaction['transaction_journal_id'], $transactionTagCount));
|
||||
if ($transactionTagCount < $expectedTagCount) {
|
||||
app('log')->debug(sprintf('Transaction has %d tag(s), we expect %d tag(s), return false.', $transactionTagCount, $expectedTagCount));
|
||||
|
||||
return false;
|
||||
}
|
||||
foreach ($transaction['tags'] as $tag) {
|
||||
Log::debug(sprintf('"%s" versus', strtolower($tag['name'])), $list);
|
||||
if (in_array(strtolower($tag['name']), $list, true)) {
|
||||
app('log')->debug(sprintf('Transaction has tag "%s" so count++.', $tag['name']));
|
||||
++$foundTagCount;
|
||||
$journalId = $transaction['transaction_journal_id'];
|
||||
// #8377 prevent adding a transaction twice when multiple tag searches find this transaction
|
||||
if (!in_array($journalId, $includedJournals, true)) {
|
||||
$includedJournals[] = $journalId;
|
||||
$return['transactions'][] = $transaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::debug(sprintf('Found %d tags, need at least %d.', $foundTagCount, $expectedTagCount));
|
||||
|
||||
// found at least the expected tags.
|
||||
$result = $foundTagCount >= $expectedTagCount;
|
||||
if (true === $result) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
$this->postFilters[] = $filter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to any of the tags in the list.
|
||||
*/
|
||||
@ -938,30 +964,4 @@ trait MetaCollection
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join table to get tag information.
|
||||
*/
|
||||
protected function joinMetaDataTables(): void
|
||||
{
|
||||
if (false === $this->hasJoinedMetaTables) {
|
||||
$this->hasJoinedMetaTables = true;
|
||||
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
|
||||
$this->fields[] = 'journal_meta.name as meta_name';
|
||||
$this->fields[] = 'journal_meta.data as meta_data';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join table to get tag information.
|
||||
*/
|
||||
protected function joinTagTables(): void
|
||||
{
|
||||
if (false === $this->hasJoinedTagTables) {
|
||||
// join some extra tables:
|
||||
$this->hasJoinedTagTables = true;
|
||||
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
$this->query->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace FireflyIII\Helpers\Collector;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\Extensions\AccountCollection;
|
||||
use FireflyIII\Helpers\Collector\Extensions\AmountCollection;
|
||||
@ -483,226 +484,6 @@ class GroupCollector implements GroupCollectorInterface
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as getGroups but everything is in a paginator.
|
||||
*/
|
||||
public function getPaginatedGroups(): LengthAwarePaginator
|
||||
{
|
||||
$set = $this->getGroups();
|
||||
if (0 === $this->limit) {
|
||||
$this->setLimit(50);
|
||||
}
|
||||
if (null !== $this->startRow && null !== $this->endRow) {
|
||||
$total = $this->endRow - $this->startRow;
|
||||
|
||||
return new LengthAwarePaginator($set, $this->total, $total, 1);
|
||||
}
|
||||
|
||||
return new LengthAwarePaginator($set, $this->total, $this->limit, $this->page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the number of returned entries.
|
||||
*/
|
||||
public function setLimit(int $limit): GroupCollectorInterface
|
||||
{
|
||||
$this->limit = $limit;
|
||||
// app('log')->debug(sprintf('GroupCollector: The limit is now %d', $limit));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isNotReconciled(): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where('source.reconciled', 0)->where('destination.reconciled', 0);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isReconciled(): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where('source.reconciled', 1)->where('destination.reconciled', 1);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to a specific currency, either foreign or normal one.
|
||||
*/
|
||||
public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($currency): void { // @phpstan-ignore-line
|
||||
$q->where('source.transaction_currency_id', $currency->id);
|
||||
$q->orWhere('source.foreign_currency_id', $currency->id);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setExpandGroupSearch(bool $expandGroupSearch): GroupCollectorInterface
|
||||
{
|
||||
$this->expandGroupSearch = $expandGroupSearch;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where('source.foreign_currency_id', $currency->id);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the result to a set of specific transaction groups.
|
||||
*/
|
||||
public function setIds(array $groupIds): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereIn('transaction_groups.id', $groupIds);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the result to a set of specific journals.
|
||||
*/
|
||||
public function setJournalIds(array $journalIds): GroupCollectorInterface
|
||||
{
|
||||
if (0 !== count($journalIds)) {
|
||||
// make all integers.
|
||||
$integerIDs = array_map('intval', $journalIds);
|
||||
Log::debug(sprintf('GroupCollector: setJournalIds: %s', implode(', ', $integerIDs)));
|
||||
|
||||
$this->query->whereIn('transaction_journals.id', $integerIDs);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the page to get.
|
||||
*/
|
||||
public function setPage(int $page): GroupCollectorInterface
|
||||
{
|
||||
$page = 0 === $page ? 1 : $page;
|
||||
$this->page = $page;
|
||||
// app('log')->debug(sprintf('GroupCollector: page is now %d', $page));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for words in descriptions.
|
||||
*/
|
||||
public function setSearchWords(array $array): GroupCollectorInterface
|
||||
{
|
||||
if (0 === count($array)) {
|
||||
return $this;
|
||||
}
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($array): void { // @phpstan-ignore-line
|
||||
$q->where(
|
||||
static function (EloquentBuilder $q1) use ($array): void {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%%%s%%', $word);
|
||||
$q1->where('transaction_journals.description', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
$q->orWhere(
|
||||
static function (EloquentBuilder $q2) use ($array): void {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%%%s%%', $word);
|
||||
$q2->where('transaction_groups.title', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the search to one specific transaction group.
|
||||
*/
|
||||
public function setTransactionGroup(TransactionGroup $transactionGroup): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where('transaction_groups.id', $transactionGroup->id);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the included transaction types.
|
||||
*/
|
||||
public function setTypes(array $types): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereIn('transaction_types.type', $types);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user object and start the query.
|
||||
*/
|
||||
public function setUser(User $user): GroupCollectorInterface
|
||||
{
|
||||
if (null === $this->user) {
|
||||
$this->user = $user;
|
||||
$this->startQuery();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user object and start the query.
|
||||
*/
|
||||
public function setUserGroup(UserGroup $userGroup): GroupCollectorInterface
|
||||
{
|
||||
if (null === $this->userGroup) {
|
||||
$this->userGroup = $userGroup;
|
||||
$this->startQueryForGroup();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically include all stuff required to make API calls work.
|
||||
*/
|
||||
public function withAPIInformation(): GroupCollectorInterface
|
||||
{
|
||||
// include source + destination account name and type.
|
||||
$this->withAccountInformation()
|
||||
// include category ID + name (if any)
|
||||
->withCategoryInformation()
|
||||
// include budget ID + name (if any)
|
||||
->withBudgetInformation()
|
||||
// include bill ID + name (if any)
|
||||
->withBillInformation()
|
||||
;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEndRow(int $endRow): self
|
||||
{
|
||||
$this->endRow = $endRow;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStartRow(int $startRow): self
|
||||
{
|
||||
$this->startRow = $startRow;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getCollectedGroupIds(): array
|
||||
{
|
||||
return $this->query->get(['transaction_journals.transaction_group_id'])->pluck('transaction_group_id')->toArray();
|
||||
@ -998,6 +779,195 @@ class GroupCollector implements GroupCollectorInterface
|
||||
return $currentCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as getGroups but everything is in a paginator.
|
||||
*/
|
||||
public function getPaginatedGroups(): LengthAwarePaginator
|
||||
{
|
||||
$set = $this->getGroups();
|
||||
if (0 === $this->limit) {
|
||||
$this->setLimit(50);
|
||||
}
|
||||
if (null !== $this->startRow && null !== $this->endRow) {
|
||||
$total = $this->endRow - $this->startRow;
|
||||
|
||||
return new LengthAwarePaginator($set, $this->total, $total, 1);
|
||||
}
|
||||
|
||||
return new LengthAwarePaginator($set, $this->total, $this->limit, $this->page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the number of returned entries.
|
||||
*/
|
||||
public function setLimit(int $limit): GroupCollectorInterface
|
||||
{
|
||||
$this->limit = $limit;
|
||||
// app('log')->debug(sprintf('GroupCollector: The limit is now %d', $limit));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isNotReconciled(): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where('source.reconciled', 0)->where('destination.reconciled', 0);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isReconciled(): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where('source.reconciled', 1)->where('destination.reconciled', 1);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to a specific currency, either foreign or normal one.
|
||||
*/
|
||||
public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($currency): void { // @phpstan-ignore-line
|
||||
$q->where('source.transaction_currency_id', $currency->id);
|
||||
$q->orWhere('source.foreign_currency_id', $currency->id);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEndRow(int $endRow): self
|
||||
{
|
||||
$this->endRow = $endRow;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setExpandGroupSearch(bool $expandGroupSearch): GroupCollectorInterface
|
||||
{
|
||||
$this->expandGroupSearch = $expandGroupSearch;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where('source.foreign_currency_id', $currency->id);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the result to a set of specific transaction groups.
|
||||
*/
|
||||
public function setIds(array $groupIds): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereIn('transaction_groups.id', $groupIds);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the result to a set of specific journals.
|
||||
*/
|
||||
public function setJournalIds(array $journalIds): GroupCollectorInterface
|
||||
{
|
||||
if (0 !== count($journalIds)) {
|
||||
// make all integers.
|
||||
$integerIDs = array_map('intval', $journalIds);
|
||||
Log::debug(sprintf('GroupCollector: setJournalIds: %s', implode(', ', $integerIDs)));
|
||||
|
||||
$this->query->whereIn('transaction_journals.id', $integerIDs);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the page to get.
|
||||
*/
|
||||
public function setPage(int $page): GroupCollectorInterface
|
||||
{
|
||||
$page = 0 === $page ? 1 : $page;
|
||||
$this->page = $page;
|
||||
// app('log')->debug(sprintf('GroupCollector: page is now %d', $page));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for words in descriptions.
|
||||
*/
|
||||
public function setSearchWords(array $array): GroupCollectorInterface
|
||||
{
|
||||
if (0 === count($array)) {
|
||||
return $this;
|
||||
}
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($array): void { // @phpstan-ignore-line
|
||||
$q->where(
|
||||
static function (EloquentBuilder $q1) use ($array): void {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%%%s%%', $word);
|
||||
$q1->where('transaction_journals.description', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
$q->orWhere(
|
||||
static function (EloquentBuilder $q2) use ($array): void {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%%%s%%', $word);
|
||||
$q2->where('transaction_groups.title', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStartRow(int $startRow): self
|
||||
{
|
||||
$this->startRow = $startRow;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the search to one specific transaction group.
|
||||
*/
|
||||
public function setTransactionGroup(TransactionGroup $transactionGroup): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where('transaction_groups.id', $transactionGroup->id);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the included transaction types.
|
||||
*/
|
||||
public function setTypes(array $types): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereIn('transaction_types.type', $types);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user object and start the query.
|
||||
*/
|
||||
public function setUser(User $user): GroupCollectorInterface
|
||||
{
|
||||
if (null === $this->user) {
|
||||
$this->user = $user;
|
||||
$this->startQuery();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the query.
|
||||
*/
|
||||
@ -1044,6 +1014,19 @@ class GroupCollector implements GroupCollectorInterface
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user object and start the query.
|
||||
*/
|
||||
public function setUserGroup(UserGroup $userGroup): GroupCollectorInterface
|
||||
{
|
||||
if (null === $this->userGroup) {
|
||||
$this->userGroup = $userGroup;
|
||||
$this->startQueryForGroup();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the query.
|
||||
*/
|
||||
@ -1087,4 +1070,21 @@ class GroupCollector implements GroupCollectorInterface
|
||||
->orderBy('source.amount', 'DESC')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically include all stuff required to make API calls work.
|
||||
*/
|
||||
public function withAPIInformation(): GroupCollectorInterface
|
||||
{
|
||||
// include source + destination account name and type.
|
||||
$this->withAccountInformation()
|
||||
// include category ID + name (if any)
|
||||
->withCategoryInformation()
|
||||
// include budget ID + name (if any)
|
||||
->withBudgetInformation()
|
||||
// include bill ID + name (if any)
|
||||
->withBillInformation();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -401,6 +401,11 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setAfter(Carbon $date): self;
|
||||
|
||||
/**
|
||||
* Limit results to a SPECIFIC set of tags.
|
||||
*/
|
||||
public function setAllTags(Collection $tags): self;
|
||||
|
||||
/**
|
||||
* Collect transactions before a specific date.
|
||||
*/
|
||||
@ -461,6 +466,11 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setEnd(Carbon $end): self;
|
||||
|
||||
/**
|
||||
* Set the page to get.
|
||||
*/
|
||||
public function setEndRow(int $endRow): self;
|
||||
|
||||
public function setExpandGroupSearch(bool $expandGroupSearch): self;
|
||||
|
||||
/**
|
||||
@ -526,16 +536,6 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setPage(int $page): self;
|
||||
|
||||
/**
|
||||
* Set the page to get.
|
||||
*/
|
||||
public function setStartRow(int $startRow): self;
|
||||
|
||||
/**
|
||||
* Set the page to get.
|
||||
*/
|
||||
public function setEndRow(int $endRow): self;
|
||||
|
||||
/**
|
||||
* Set the start and end time of the results to return.
|
||||
*/
|
||||
@ -563,6 +563,11 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setStart(Carbon $start): self;
|
||||
|
||||
/**
|
||||
* Set the page to get.
|
||||
*/
|
||||
public function setStartRow(int $startRow): self;
|
||||
|
||||
/**
|
||||
* Limit results to a specific tag.
|
||||
*/
|
||||
@ -573,11 +578,6 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setTags(Collection $tags): self;
|
||||
|
||||
/**
|
||||
* Limit results to a SPECIFIC set of tags.
|
||||
*/
|
||||
public function setAllTags(Collection $tags): self;
|
||||
|
||||
/**
|
||||
* Limit the search to one specific transaction group.
|
||||
*/
|
||||
|
@ -121,12 +121,12 @@ class NetWorth implements NetWorthInterface
|
||||
$netWorth[$currencyCode] ??= [
|
||||
'balance' => '0',
|
||||
'native_balance' => '0',
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'native_currency_id' => (string) $default->id,
|
||||
'native_currency_id' => (string)$default->id,
|
||||
'native_currency_code' => $default->code,
|
||||
'native_currency_name' => $default->name,
|
||||
'native_currency_symbol' => $default->symbol,
|
||||
@ -144,6 +144,15 @@ class NetWorth implements NetWorthInterface
|
||||
return $netWorth;
|
||||
}
|
||||
|
||||
private function getRepository(): AccountRepositoryInterface|AdminAccountRepositoryInterface
|
||||
{
|
||||
if (null === $this->userGroup) {
|
||||
return $this->accountRepository;
|
||||
}
|
||||
|
||||
return $this->adminAccountRepository;
|
||||
}
|
||||
|
||||
public function setUser(null|Authenticatable|User $user): void
|
||||
{
|
||||
if (!$user instanceof User) {
|
||||
@ -189,7 +198,7 @@ class NetWorth implements NetWorthInterface
|
||||
}
|
||||
|
||||
$return[$currency->id] ??= [
|
||||
'id' => (string) $currency->id,
|
||||
'id' => (string)$currency->id,
|
||||
'name' => $currency->name,
|
||||
'symbol' => $currency->symbol,
|
||||
'code' => $currency->code,
|
||||
@ -202,15 +211,6 @@ class NetWorth implements NetWorthInterface
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function getRepository(): AccountRepositoryInterface|AdminAccountRepositoryInterface
|
||||
{
|
||||
if (null === $this->userGroup) {
|
||||
return $this->accountRepository;
|
||||
}
|
||||
|
||||
return $this->adminAccountRepository;
|
||||
}
|
||||
|
||||
private function getAccounts(): Collection
|
||||
{
|
||||
$accounts = $this->getRepository()->getAccountsByType(
|
||||
@ -220,7 +220,7 @@ class NetWorth implements NetWorthInterface
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if (1 === (int) $this->getRepository()->getMetaValue($account, 'include_net_worth')) {
|
||||
if (1 === (int)$this->getRepository()->getMetaValue($account, 'include_net_worth')) {
|
||||
$filtered->push($account);
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ class CreateController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('mainTitleIcon', 'fa-credit-card');
|
||||
app('view')->share('title', (string) trans('firefly.accounts'));
|
||||
app('view')->share('title', (string)trans('firefly.accounts'));
|
||||
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->attachments = app(AttachmentHelperInterface::class);
|
||||
@ -77,7 +77,7 @@ class CreateController extends Controller
|
||||
{
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
|
||||
$subTitle = (string) trans(sprintf('firefly.make_new_%s_account', $objectType));
|
||||
$subTitle = (string)trans(sprintf('firefly.make_new_%s_account', $objectType));
|
||||
$roles = $this->getRoles();
|
||||
$liabilityTypes = $this->getLiabilityTypes();
|
||||
$hasOldInput = null !== $request->old('_token');
|
||||
@ -96,9 +96,9 @@ class CreateController extends Controller
|
||||
|
||||
// interest calculation periods:
|
||||
$interestPeriods = [
|
||||
'daily' => (string) trans('firefly.interest_calc_daily'),
|
||||
'monthly' => (string) trans('firefly.interest_calc_monthly'),
|
||||
'yearly' => (string) trans('firefly.interest_calc_yearly'),
|
||||
'daily' => (string)trans('firefly.interest_calc_daily'),
|
||||
'monthly' => (string)trans('firefly.interest_calc_monthly'),
|
||||
'yearly' => (string)trans('firefly.interest_calc_yearly'),
|
||||
];
|
||||
|
||||
// pre fill some data
|
||||
@ -106,7 +106,7 @@ class CreateController extends Controller
|
||||
'preFilled',
|
||||
[
|
||||
'currency_id' => $defaultCurrency->id,
|
||||
'include_net_worth' => $hasOldInput ? (bool) $request->old('include_net_worth') : true,
|
||||
'include_net_worth' => $hasOldInput ? (bool)$request->old('include_net_worth') : true,
|
||||
]
|
||||
);
|
||||
// issue #8321
|
||||
@ -139,7 +139,7 @@ class CreateController extends Controller
|
||||
{
|
||||
$data = $request->getAccountData();
|
||||
$account = $this->repository->store($data);
|
||||
$request->session()->flash('success', (string) trans('firefly.stored_new_account', ['name' => $account->name]));
|
||||
$request->session()->flash('success', (string)trans('firefly.stored_new_account', ['name' => $account->name]));
|
||||
app('preferences')->mark();
|
||||
|
||||
Log::channel('audit')->info('Stored new account.', $data);
|
||||
@ -162,7 +162,7 @@ class CreateController extends Controller
|
||||
}
|
||||
if (null !== $files && auth()->user()->hasRole('demo')) {
|
||||
Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
|
||||
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
|
||||
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
|
||||
}
|
||||
|
||||
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
||||
@ -171,7 +171,7 @@ class CreateController extends Controller
|
||||
|
||||
// redirect to previous URL.
|
||||
$redirect = redirect($this->getPreviousUrl('accounts.create.url'));
|
||||
if (1 === (int) $request->get('create_another')) {
|
||||
if (1 === (int)$request->get('create_another')) {
|
||||
// set value so create routine will not overwrite URL:
|
||||
$request->session()->put('accounts.create.fromStore', true);
|
||||
|
||||
|
@ -57,7 +57,7 @@ class EditController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('mainTitleIcon', 'fa-credit-card');
|
||||
app('view')->share('title', (string) trans('firefly.accounts'));
|
||||
app('view')->share('title', (string)trans('firefly.accounts'));
|
||||
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->attachments = app(AttachmentHelperInterface::class);
|
||||
@ -81,7 +81,7 @@ class EditController extends Controller
|
||||
}
|
||||
|
||||
$objectType = config('firefly.shortNamesByFullName')[$account->accountType->type];
|
||||
$subTitle = (string) trans(sprintf('firefly.edit_%s_account', $objectType), ['name' => $account->name]);
|
||||
$subTitle = (string)trans(sprintf('firefly.edit_%s_account', $objectType), ['name' => $account->name]);
|
||||
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
|
||||
$roles = $this->getRoles();
|
||||
$liabilityTypes = $this->getLiabilityTypes();
|
||||
@ -106,9 +106,9 @@ class EditController extends Controller
|
||||
|
||||
// interest calculation periods:
|
||||
$interestPeriods = [
|
||||
'daily' => (string) trans('firefly.interest_calc_daily'),
|
||||
'monthly' => (string) trans('firefly.interest_calc_monthly'),
|
||||
'yearly' => (string) trans('firefly.interest_calc_yearly'),
|
||||
'daily' => (string)trans('firefly.interest_calc_daily'),
|
||||
'monthly' => (string)trans('firefly.interest_calc_monthly'),
|
||||
'yearly' => (string)trans('firefly.interest_calc_yearly'),
|
||||
];
|
||||
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
@ -117,7 +117,7 @@ class EditController extends Controller
|
||||
}
|
||||
$request->session()->forget('accounts.edit.fromUpdate');
|
||||
|
||||
$openingBalanceAmount = (string) $repository->getOpeningBalanceAmount($account);
|
||||
$openingBalanceAmount = (string)$repository->getOpeningBalanceAmount($account);
|
||||
if ('0' === $openingBalanceAmount) {
|
||||
$openingBalanceAmount = '';
|
||||
}
|
||||
@ -143,17 +143,17 @@ class EditController extends Controller
|
||||
'cc_type' => $repository->getMetaValue($account, 'cc_type'),
|
||||
'cc_monthly_payment_date' => $repository->getMetaValue($account, 'cc_monthly_payment_date'),
|
||||
'BIC' => $repository->getMetaValue($account, 'BIC'),
|
||||
'opening_balance_date' => substr((string) $openingBalanceDate, 0, 10),
|
||||
'opening_balance_date' => substr((string)$openingBalanceDate, 0, 10),
|
||||
'liability_type_id' => $account->account_type_id,
|
||||
'opening_balance' => app('steam')->bcround($openingBalanceAmount, $currency->decimal_places),
|
||||
'liability_direction' => $this->repository->getMetaValue($account, 'liability_direction'),
|
||||
'virtual_balance' => app('steam')->bcround($virtualBalance, $currency->decimal_places),
|
||||
'currency_id' => $currency->id,
|
||||
'include_net_worth' => $hasOldInput ? (bool) $request->old('include_net_worth') : $includeNetWorth,
|
||||
'include_net_worth' => $hasOldInput ? (bool)$request->old('include_net_worth') : $includeNetWorth,
|
||||
'interest' => $repository->getMetaValue($account, 'interest'),
|
||||
'interest_period' => $repository->getMetaValue($account, 'interest_period'),
|
||||
'notes' => $this->repository->getNoteText($account),
|
||||
'active' => $hasOldInput ? (bool) $request->old('active') : $account->active,
|
||||
'active' => $hasOldInput ? (bool)$request->old('active') : $account->active,
|
||||
];
|
||||
if ('' === $openingBalanceAmount) {
|
||||
$preFilled['opening_balance'] = '';
|
||||
@ -178,7 +178,7 @@ class EditController extends Controller
|
||||
$data = $request->getAccountData();
|
||||
$this->repository->update($account, $data);
|
||||
Log::channel('audit')->info(sprintf('Updated account #%d.', $account->id), $data);
|
||||
$request->session()->flash('success', (string) trans('firefly.updated_account', ['name' => $account->name]));
|
||||
$request->session()->flash('success', (string)trans('firefly.updated_account', ['name' => $account->name]));
|
||||
|
||||
// store new attachment(s):
|
||||
/** @var null|array $files */
|
||||
@ -188,7 +188,7 @@ class EditController extends Controller
|
||||
}
|
||||
if (null !== $files && auth()->user()->hasRole('demo')) {
|
||||
Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
|
||||
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
|
||||
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
|
||||
}
|
||||
|
||||
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
||||
@ -197,7 +197,7 @@ class EditController extends Controller
|
||||
|
||||
// redirect
|
||||
$redirect = redirect($this->getPreviousUrl('accounts.edit.url'));
|
||||
if (1 === (int) $request->get('return_to_edit')) {
|
||||
if (1 === (int)$request->get('return_to_edit')) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
$request->session()->put('accounts.edit.fromUpdate', true);
|
||||
|
||||
|
@ -95,6 +95,21 @@ class ForgotPasswordController extends Controller
|
||||
return back()->with('status', trans($response));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function validateHost(): void
|
||||
{
|
||||
$configuredHost = parse_url((string)config('app.url'), PHP_URL_HOST);
|
||||
if (false === $configuredHost || null === $configuredHost) {
|
||||
throw new FireflyException('Please set a valid and correct Firefly III URL in the APP_URL environment variable.');
|
||||
}
|
||||
$host = request()->host();
|
||||
if ($configuredHost !== $host) {
|
||||
throw new FireflyException('The Host-header does not match the host in the APP_URL environment variable. Please make sure these match. See also: https://bit.ly/FF3-host-header');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show form for email recovery.
|
||||
*
|
||||
@ -121,19 +136,4 @@ class ForgotPasswordController extends Controller
|
||||
|
||||
return view('auth.passwords.email')->with(compact('allowRegistration', 'pageTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function validateHost(): void
|
||||
{
|
||||
$configuredHost = parse_url((string)config('app.url'), PHP_URL_HOST);
|
||||
if (false === $configuredHost || null === $configuredHost) {
|
||||
throw new FireflyException('Please set a valid and correct Firefly III URL in the APP_URL environment variable.');
|
||||
}
|
||||
$host = request()->host();
|
||||
if ($configuredHost !== $host) {
|
||||
throw new FireflyException('The Host-header does not match the host in the APP_URL environment variable. Please make sure these match. See also: https://bit.ly/FF3-host-header');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +132,25 @@ class LoginController extends Controller
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the failed login response instance.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function sendFailedLoginResponse(Request $request): void
|
||||
{
|
||||
$exception = ValidationException::withMessages(
|
||||
[
|
||||
$this->username() => [trans('auth.failed')],
|
||||
]
|
||||
);
|
||||
$exception->redirectTo = route('login');
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the user out of the application.
|
||||
*
|
||||
@ -210,23 +229,4 @@ class LoginController extends Controller
|
||||
|
||||
return view('auth.login', compact('allowRegistration', 'email', 'remember', 'allowReset', 'title', 'usernameField'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the failed login response instance.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function sendFailedLoginResponse(Request $request): void
|
||||
{
|
||||
$exception = ValidationException::withMessages(
|
||||
[
|
||||
$this->username() => [trans('auth.failed')],
|
||||
]
|
||||
);
|
||||
$exception->redirectTo = route('login');
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +109,31 @@ class RegisterController extends Controller
|
||||
return redirect($this->redirectPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function allowedToRegister(): bool
|
||||
{
|
||||
// is allowed to register?
|
||||
$allowRegistration = true;
|
||||
|
||||
try {
|
||||
$singleUserMode = app('fireflyconfig')->get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
|
||||
} catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) {
|
||||
$singleUserMode = true;
|
||||
}
|
||||
$userCount = User::count();
|
||||
$guard = config('auth.defaults.guard');
|
||||
if (true === $singleUserMode && $userCount > 0 && 'web' === $guard) {
|
||||
$allowRegistration = false;
|
||||
}
|
||||
if ('web' !== $guard) {
|
||||
$allowRegistration = false;
|
||||
}
|
||||
|
||||
return $allowRegistration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the application registration form if the invitation code is valid.
|
||||
*
|
||||
@ -164,29 +189,4 @@ class RegisterController extends Controller
|
||||
|
||||
return view('auth.register', compact('isDemoSite', 'email', 'pageTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function allowedToRegister(): bool
|
||||
{
|
||||
// is allowed to register?
|
||||
$allowRegistration = true;
|
||||
|
||||
try {
|
||||
$singleUserMode = app('fireflyconfig')->get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
|
||||
} catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) {
|
||||
$singleUserMode = true;
|
||||
}
|
||||
$userCount = User::count();
|
||||
$guard = config('auth.defaults.guard');
|
||||
if (true === $singleUserMode && $userCount > 0 && 'web' === $guard) {
|
||||
$allowRegistration = false;
|
||||
}
|
||||
if ('web' !== $guard) {
|
||||
$allowRegistration = false;
|
||||
}
|
||||
|
||||
return $allowRegistration;
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Redirector;
|
||||
use PragmaRX\Google2FALaravel\Support\Authenticator;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class TwoFactorController.
|
||||
|
@ -52,7 +52,7 @@ class EditController extends Controller
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('title', (string) trans('firefly.bills'));
|
||||
app('view')->share('title', (string)trans('firefly.bills'));
|
||||
app('view')->share('mainTitleIcon', 'fa-calendar-o');
|
||||
$this->attachments = app(AttachmentHelperInterface::class);
|
||||
$this->repository = app(BillRepositoryInterface::class);
|
||||
@ -75,10 +75,10 @@ class EditController extends Controller
|
||||
$billPeriods = config('firefly.bill_periods');
|
||||
|
||||
foreach ($billPeriods as $current) {
|
||||
$periods[$current] = (string) trans('firefly.'.$current);
|
||||
$periods[$current] = (string)trans('firefly.'.$current);
|
||||
}
|
||||
|
||||
$subTitle = (string) trans('firefly.edit_bill', ['name' => $bill->name]);
|
||||
$subTitle = (string)trans('firefly.edit_bill', ['name' => $bill->name]);
|
||||
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
if (true !== session('bills.edit.fromUpdate')) {
|
||||
@ -99,7 +99,7 @@ class EditController extends Controller
|
||||
'extension_date' => $bill->extension_date,
|
||||
'notes' => $this->repository->getNoteText($bill),
|
||||
'transaction_currency_id' => $bill->transaction_currency_id,
|
||||
'active' => $hasOldInput ? (bool) $request->old('active') : $bill->active,
|
||||
'active' => $hasOldInput ? (bool)$request->old('active') : $bill->active,
|
||||
'object_group' => null !== $bill->objectGroups->first() ? $bill->objectGroups->first()->title : '',
|
||||
];
|
||||
|
||||
@ -119,7 +119,7 @@ class EditController extends Controller
|
||||
|
||||
Log::channel('audit')->info(sprintf('Updated bill #%d.', $bill->id), $billData);
|
||||
|
||||
$request->session()->flash('success', (string) trans('firefly.updated_bill', ['name' => $bill->name]));
|
||||
$request->session()->flash('success', (string)trans('firefly.updated_bill', ['name' => $bill->name]));
|
||||
app('preferences')->mark();
|
||||
|
||||
/** @var null|array $files */
|
||||
@ -129,7 +129,7 @@ class EditController extends Controller
|
||||
}
|
||||
if (null !== $files && auth()->user()->hasRole('demo')) {
|
||||
Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
|
||||
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
|
||||
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
|
||||
}
|
||||
|
||||
// flash messages
|
||||
@ -138,7 +138,7 @@ class EditController extends Controller
|
||||
}
|
||||
$redirect = redirect($this->getPreviousUrl('bills.edit.url'));
|
||||
|
||||
if (1 === (int) $request->get('return_to_edit')) {
|
||||
if (1 === (int)$request->get('return_to_edit')) {
|
||||
$request->session()->put('bills.edit.fromUpdate', true);
|
||||
|
||||
$redirect = redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);
|
||||
|
@ -134,24 +134,6 @@ class IndexController extends Controller
|
||||
return view('bills.index', compact('bills', 'sums', 'total', 'totals', 'today'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order of a bill.
|
||||
*/
|
||||
public function setOrder(Request $request, Bill $bill): JsonResponse
|
||||
{
|
||||
$objectGroupTitle = (string)$request->get('objectGroupTitle');
|
||||
$newOrder = (int)$request->get('order');
|
||||
$this->repository->setOrder($bill, $newOrder);
|
||||
if ('' !== $objectGroupTitle) {
|
||||
$this->repository->setObjectGroup($bill, $objectGroupTitle);
|
||||
}
|
||||
if ('' === $objectGroupTitle) {
|
||||
$this->repository->removeObjectGroup($bill);
|
||||
}
|
||||
|
||||
return response()->json(['data' => 'OK']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
@ -268,4 +250,22 @@ class IndexController extends Controller
|
||||
|
||||
return $totals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order of a bill.
|
||||
*/
|
||||
public function setOrder(Request $request, Bill $bill): JsonResponse
|
||||
{
|
||||
$objectGroupTitle = (string)$request->get('objectGroupTitle');
|
||||
$newOrder = (int)$request->get('order');
|
||||
$this->repository->setOrder($bill, $newOrder);
|
||||
if ('' !== $objectGroupTitle) {
|
||||
$this->repository->setObjectGroup($bill, $objectGroupTitle);
|
||||
}
|
||||
if ('' === $objectGroupTitle) {
|
||||
$this->repository->removeObjectGroup($bill);
|
||||
}
|
||||
|
||||
return response()->json(['data' => 'OK']);
|
||||
}
|
||||
}
|
||||
|
@ -159,24 +159,6 @@ class IndexController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
public function reorder(Request $request, BudgetRepositoryInterface $repository): JsonResponse
|
||||
{
|
||||
$this->abRepository->cleanup();
|
||||
$budgetIds = $request->get('budgetIds');
|
||||
|
||||
foreach ($budgetIds as $index => $budgetId) {
|
||||
$budgetId = (int)$budgetId;
|
||||
$budget = $repository->find($budgetId);
|
||||
if (null !== $budget) {
|
||||
app('log')->debug(sprintf('Set budget #%d ("%s") to position %d', $budget->id, $budget->name, $index + 1));
|
||||
$repository->setBudgetOrder($budget, $index + 1);
|
||||
}
|
||||
}
|
||||
app('preferences')->mark();
|
||||
|
||||
return response()->json(['OK']);
|
||||
}
|
||||
|
||||
private function getAllAvailableBudgets(Carbon $start, Carbon $end): array
|
||||
{
|
||||
// get all available budgets.
|
||||
@ -296,11 +278,11 @@ class IndexController extends Controller
|
||||
// also calculate how much left from budgeted:
|
||||
$sums['left'][$currencyId]
|
||||
??= [
|
||||
'amount' => '0',
|
||||
'currency_id' => $budgeted['currency_id'],
|
||||
'currency_symbol' => $budgeted['currency_symbol'],
|
||||
'currency_decimal_places' => $budgeted['currency_decimal_places'],
|
||||
];
|
||||
'amount' => '0',
|
||||
'currency_id' => $budgeted['currency_id'],
|
||||
'currency_symbol' => $budgeted['currency_symbol'],
|
||||
'currency_decimal_places' => $budgeted['currency_decimal_places'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,4 +298,22 @@ class IndexController extends Controller
|
||||
|
||||
return $sums;
|
||||
}
|
||||
|
||||
public function reorder(Request $request, BudgetRepositoryInterface $repository): JsonResponse
|
||||
{
|
||||
$this->abRepository->cleanup();
|
||||
$budgetIds = $request->get('budgetIds');
|
||||
|
||||
foreach ($budgetIds as $index => $budgetId) {
|
||||
$budgetId = (int)$budgetId;
|
||||
$budget = $repository->find($budgetId);
|
||||
if (null !== $budget) {
|
||||
app('log')->debug(sprintf('Set budget #%d ("%s") to position %d', $budget->id, $budget->name, $index + 1));
|
||||
$repository->setBudgetOrder($budget, $index + 1);
|
||||
}
|
||||
}
|
||||
app('preferences')->mark();
|
||||
|
||||
return response()->json(['OK']);
|
||||
}
|
||||
}
|
||||
|
@ -409,6 +409,58 @@ class AccountController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function periodByCurrency(Carbon $start, Carbon $end, Account $account, TransactionCurrency $currency): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in periodByCurrency("%s", "%s", %s, "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'), $account->id, $currency->code));
|
||||
$locale = app('steam')->getLocale();
|
||||
$step = $this->calculateStep($start, $end);
|
||||
$result = [
|
||||
'label' => sprintf('%s (%s)', $account->name, $currency->symbol),
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_code' => $currency->code,
|
||||
];
|
||||
$entries = [];
|
||||
$current = clone $start;
|
||||
app('log')->debug(sprintf('Step is %s', $step));
|
||||
|
||||
// fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
|
||||
// have to make sure this chart is always based on the balance at the END of the period.
|
||||
// This period depends on the size of the chart
|
||||
$current = app('navigation')->endOfX($current, $step, null);
|
||||
app('log')->debug(sprintf('$current date is %s', $current->format('Y-m-d')));
|
||||
if ('1D' === $step) {
|
||||
// per day the entire period, balance for every day.
|
||||
$format = (string)trans('config.month_and_day_js', [], $locale);
|
||||
$range = app('steam')->balanceInRange($account, $start, $end, $currency);
|
||||
$previous = array_values($range)[0];
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = $range[$theDate] ?? $previous;
|
||||
$label = $current->isoFormat($format);
|
||||
$entries[$label] = (float)$balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
}
|
||||
if ('1W' === $step || '1M' === $step || '1Y' === $step) {
|
||||
while ($end >= $current) {
|
||||
app('log')->debug(sprintf('Current is: %s', $current->format('Y-m-d')));
|
||||
$balance = (float)app('steam')->balance($account, $current, $currency);
|
||||
$label = app('navigation')->periodShow($current, $step);
|
||||
$entries[$label] = $balance;
|
||||
$current = app('navigation')->addPeriod($current, $step, 0);
|
||||
// here too, to fix #8041, the data is corrected to the end of the period.
|
||||
$current = app('navigation')->endOfX($current, $step, null);
|
||||
}
|
||||
}
|
||||
$result['entries'] = $entries;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the balances for a given set of dates and accounts.
|
||||
*
|
||||
@ -512,56 +564,4 @@ class AccountController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function periodByCurrency(Carbon $start, Carbon $end, Account $account, TransactionCurrency $currency): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in periodByCurrency("%s", "%s", %s, "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'), $account->id, $currency->code));
|
||||
$locale = app('steam')->getLocale();
|
||||
$step = $this->calculateStep($start, $end);
|
||||
$result = [
|
||||
'label' => sprintf('%s (%s)', $account->name, $currency->symbol),
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_code' => $currency->code,
|
||||
];
|
||||
$entries = [];
|
||||
$current = clone $start;
|
||||
app('log')->debug(sprintf('Step is %s', $step));
|
||||
|
||||
// fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
|
||||
// have to make sure this chart is always based on the balance at the END of the period.
|
||||
// This period depends on the size of the chart
|
||||
$current = app('navigation')->endOfX($current, $step, null);
|
||||
app('log')->debug(sprintf('$current date is %s', $current->format('Y-m-d')));
|
||||
if ('1D' === $step) {
|
||||
// per day the entire period, balance for every day.
|
||||
$format = (string)trans('config.month_and_day_js', [], $locale);
|
||||
$range = app('steam')->balanceInRange($account, $start, $end, $currency);
|
||||
$previous = array_values($range)[0];
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = $range[$theDate] ?? $previous;
|
||||
$label = $current->isoFormat($format);
|
||||
$entries[$label] = (float)$balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
}
|
||||
if ('1W' === $step || '1M' === $step || '1Y' === $step) {
|
||||
while ($end >= $current) {
|
||||
app('log')->debug(sprintf('Current is: %s', $current->format('Y-m-d')));
|
||||
$balance = (float)app('steam')->balance($account, $current, $currency);
|
||||
$label = app('navigation')->periodShow($current, $step);
|
||||
$entries[$label] = $balance;
|
||||
$current = app('navigation')->addPeriod($current, $step, 0);
|
||||
// here too, to fix #8041, the data is corrected to the end of the period.
|
||||
$current = app('navigation')->endOfX($current, $step, null);
|
||||
}
|
||||
}
|
||||
$result['entries'] = $entries;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -195,6 +195,23 @@ class BudgetReportController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = app('navigation')->preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart that groups expenses by the account.
|
||||
*/
|
||||
@ -224,21 +241,4 @@ class BudgetReportController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = app('navigation')->preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +94,11 @@ class CategoryController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
private function getDate(): Carbon
|
||||
{
|
||||
return today(config('app.timezone'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the category chart on the front page.
|
||||
* TODO test method for category refactor.
|
||||
@ -141,6 +146,85 @@ class CategoryController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate report chart for either with or without category.
|
||||
*/
|
||||
private function reportPeriodChart(Collection $accounts, Carbon $start, Carbon $end, ?Category $category): array
|
||||
{
|
||||
$income = [];
|
||||
$expenses = [];
|
||||
$categoryId = 0;
|
||||
if (null === $category) {
|
||||
/** @var NoCategoryRepositoryInterface $noCatRepository */
|
||||
$noCatRepository = app(NoCategoryRepositoryInterface::class);
|
||||
|
||||
// this gives us all currencies
|
||||
$expenses = $noCatRepository->listExpenses($start, $end, $accounts);
|
||||
$income = $noCatRepository->listIncome($start, $end, $accounts);
|
||||
}
|
||||
|
||||
if (null !== $category) {
|
||||
/** @var OperationsRepositoryInterface $opsRepository */
|
||||
$opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$categoryId = $category->id;
|
||||
// this gives us all currencies
|
||||
$collection = new Collection([$category]);
|
||||
$expenses = $opsRepository->listExpenses($start, $end, null, $collection);
|
||||
$income = $opsRepository->listIncome($start, $end, null, $collection);
|
||||
}
|
||||
$currencies = array_unique(array_merge(array_keys($income), array_keys($expenses)));
|
||||
$periods = app('navigation')->listOfPeriods($start, $end);
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
$chartData = [];
|
||||
// make empty data array:
|
||||
// double foreach (bad) to make empty array:
|
||||
foreach ($currencies as $currencyId) {
|
||||
$currencyInfo = $expenses[$currencyId] ?? $income[$currencyId];
|
||||
$outKey = sprintf('%d-out', $currencyId);
|
||||
$inKey = sprintf('%d-in', $currencyId);
|
||||
$chartData[$outKey]
|
||||
= [
|
||||
'label' => sprintf('%s (%s)', (string)trans('firefly.spent'), $currencyInfo['currency_name']),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red
|
||||
];
|
||||
|
||||
$chartData[$inKey]
|
||||
= [
|
||||
'label' => sprintf('%s (%s)', (string)trans('firefly.earned'), $currencyInfo['currency_name']),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green
|
||||
];
|
||||
// loop empty periods:
|
||||
foreach (array_keys($periods) as $period) {
|
||||
$label = $periods[$period];
|
||||
$chartData[$outKey]['entries'][$label] = '0';
|
||||
$chartData[$inKey]['entries'][$label] = '0';
|
||||
}
|
||||
// loop income and expenses for this category.:
|
||||
$outSet = $expenses[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []];
|
||||
foreach ($outSet['transaction_journals'] as $journal) {
|
||||
$amount = app('steam')->positive($journal['amount']);
|
||||
$date = $journal['date']->isoFormat($format);
|
||||
$chartData[$outKey]['entries'][$date] ??= '0';
|
||||
|
||||
$chartData[$outKey]['entries'][$date] = bcadd($amount, $chartData[$outKey]['entries'][$date]);
|
||||
}
|
||||
|
||||
$inSet = $income[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []];
|
||||
foreach ($inSet['transaction_journals'] as $journal) {
|
||||
$amount = app('steam')->positive($journal['amount']);
|
||||
$date = $journal['date']->isoFormat($format);
|
||||
$chartData[$inKey]['entries'][$date] ??= '0';
|
||||
$chartData[$inKey]['entries'][$date] = bcadd($amount, $chartData[$inKey]['entries'][$date]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->generator->multiSet($chartData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart for period for transactions without a category.
|
||||
* TODO test me.
|
||||
@ -195,88 +279,4 @@ class CategoryController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
private function getDate(): Carbon
|
||||
{
|
||||
return today(config('app.timezone'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate report chart for either with or without category.
|
||||
*/
|
||||
private function reportPeriodChart(Collection $accounts, Carbon $start, Carbon $end, ?Category $category): array
|
||||
{
|
||||
$income = [];
|
||||
$expenses = [];
|
||||
$categoryId = 0;
|
||||
if (null === $category) {
|
||||
/** @var NoCategoryRepositoryInterface $noCatRepository */
|
||||
$noCatRepository = app(NoCategoryRepositoryInterface::class);
|
||||
|
||||
// this gives us all currencies
|
||||
$expenses = $noCatRepository->listExpenses($start, $end, $accounts);
|
||||
$income = $noCatRepository->listIncome($start, $end, $accounts);
|
||||
}
|
||||
|
||||
if (null !== $category) {
|
||||
/** @var OperationsRepositoryInterface $opsRepository */
|
||||
$opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$categoryId = $category->id;
|
||||
// this gives us all currencies
|
||||
$collection = new Collection([$category]);
|
||||
$expenses = $opsRepository->listExpenses($start, $end, null, $collection);
|
||||
$income = $opsRepository->listIncome($start, $end, null, $collection);
|
||||
}
|
||||
$currencies = array_unique(array_merge(array_keys($income), array_keys($expenses)));
|
||||
$periods = app('navigation')->listOfPeriods($start, $end);
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
$chartData = [];
|
||||
// make empty data array:
|
||||
// double foreach (bad) to make empty array:
|
||||
foreach ($currencies as $currencyId) {
|
||||
$currencyInfo = $expenses[$currencyId] ?? $income[$currencyId];
|
||||
$outKey = sprintf('%d-out', $currencyId);
|
||||
$inKey = sprintf('%d-in', $currencyId);
|
||||
$chartData[$outKey]
|
||||
= [
|
||||
'label' => sprintf('%s (%s)', (string)trans('firefly.spent'), $currencyInfo['currency_name']),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red
|
||||
];
|
||||
|
||||
$chartData[$inKey]
|
||||
= [
|
||||
'label' => sprintf('%s (%s)', (string)trans('firefly.earned'), $currencyInfo['currency_name']),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green
|
||||
];
|
||||
// loop empty periods:
|
||||
foreach (array_keys($periods) as $period) {
|
||||
$label = $periods[$period];
|
||||
$chartData[$outKey]['entries'][$label] = '0';
|
||||
$chartData[$inKey]['entries'][$label] = '0';
|
||||
}
|
||||
// loop income and expenses for this category.:
|
||||
$outSet = $expenses[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []];
|
||||
foreach ($outSet['transaction_journals'] as $journal) {
|
||||
$amount = app('steam')->positive($journal['amount']);
|
||||
$date = $journal['date']->isoFormat($format);
|
||||
$chartData[$outKey]['entries'][$date] ??= '0';
|
||||
|
||||
$chartData[$outKey]['entries'][$date] = bcadd($amount, $chartData[$outKey]['entries'][$date]);
|
||||
}
|
||||
|
||||
$inSet = $income[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []];
|
||||
foreach ($inSet['transaction_journals'] as $journal) {
|
||||
$amount = app('steam')->positive($journal['amount']);
|
||||
$date = $journal['date']->isoFormat($format);
|
||||
$chartData[$inKey]['entries'][$date] ??= '0';
|
||||
$chartData[$inKey]['entries'][$date] = bcadd($amount, $chartData[$inKey]['entries'][$date]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->generator->multiSet($chartData);
|
||||
}
|
||||
}
|
||||
|
@ -269,6 +269,26 @@ class CategoryReportController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = app('navigation')->preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function sourceExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
|
||||
{
|
||||
$result = [];
|
||||
@ -324,24 +344,4 @@ class CategoryReportController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = app('navigation')->preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -212,6 +212,44 @@ class DoubleReportController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->name === $name && $account->id !== $id) {
|
||||
return $account->name;
|
||||
}
|
||||
if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) {
|
||||
return $account->iban;
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = app('navigation')->preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function tagExpense(Collection $accounts, Collection $others, Carbon $start, Carbon $end): JsonResponse
|
||||
{
|
||||
$result = [];
|
||||
@ -317,42 +355,4 @@ class DoubleReportController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->name === $name && $account->id !== $id) {
|
||||
return $account->name;
|
||||
}
|
||||
if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) {
|
||||
return $account->iban;
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = app('navigation')->preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -102,27 +102,27 @@ class ExpenseReportController extends Controller
|
||||
/** @var Account $exp */
|
||||
$exp = $combination->first();
|
||||
$chartData[$exp->id.'-in'] = [
|
||||
'label' => sprintf('%s (%s)', $name, (string) trans('firefly.income')),
|
||||
'label' => sprintf('%s (%s)', $name, (string)trans('firefly.income')),
|
||||
'type' => 'bar',
|
||||
'yAxisID' => 'y-axis-0',
|
||||
'entries' => [],
|
||||
];
|
||||
$chartData[$exp->id.'-out'] = [
|
||||
'label' => sprintf('%s (%s)', $name, (string) trans('firefly.expenses')),
|
||||
'label' => sprintf('%s (%s)', $name, (string)trans('firefly.expenses')),
|
||||
'type' => 'bar',
|
||||
'yAxisID' => 'y-axis-0',
|
||||
'entries' => [],
|
||||
];
|
||||
// total in, total out:
|
||||
$chartData[$exp->id.'-total-in'] = [
|
||||
'label' => sprintf('%s (%s)', $name, (string) trans('firefly.sum_of_income')),
|
||||
'label' => sprintf('%s (%s)', $name, (string)trans('firefly.sum_of_income')),
|
||||
'type' => 'line',
|
||||
'fill' => false,
|
||||
'yAxisID' => 'y-axis-1',
|
||||
'entries' => [],
|
||||
];
|
||||
$chartData[$exp->id.'-total-out'] = [
|
||||
'label' => sprintf('%s (%s)', $name, (string) trans('firefly.sum_of_expenses')),
|
||||
'label' => sprintf('%s (%s)', $name, (string)trans('firefly.sum_of_expenses')),
|
||||
'type' => 'line',
|
||||
'fill' => false,
|
||||
'yAxisID' => 'y-axis-1',
|
||||
|
@ -274,6 +274,26 @@ class TagReportController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = app('navigation')->preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function sourceExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
|
||||
{
|
||||
$result = [];
|
||||
@ -381,24 +401,4 @@ class TagReportController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = app('navigation')->preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Middleware\IsDemoUser;
|
||||
use FireflyIII\Models\AccountType;
|
||||
@ -140,21 +141,6 @@ class DebugController extends Controller
|
||||
return view('debug', compact('table', 'now', 'logContent'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash all types of messages.
|
||||
*
|
||||
* @return Redirector|RedirectResponse
|
||||
*/
|
||||
public function testFlash(Request $request)
|
||||
{
|
||||
$request->session()->flash('success', 'This is a success message.');
|
||||
$request->session()->flash('info', 'This is an info message.');
|
||||
$request->session()->flash('warning', 'This is a warning.');
|
||||
$request->session()->flash('error', 'This is an error!');
|
||||
|
||||
return redirect(route('home'));
|
||||
}
|
||||
|
||||
private function generateTable(): string
|
||||
{
|
||||
// system information:
|
||||
@ -336,4 +322,19 @@ class DebugController extends Controller
|
||||
|
||||
return implode(' ', $flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash all types of messages.
|
||||
*
|
||||
* @return Redirector|RedirectResponse
|
||||
*/
|
||||
public function testFlash(Request $request)
|
||||
{
|
||||
$request->session()->flash('success', 'This is a success message.');
|
||||
$request->session()->flash('info', 'This is an info message.');
|
||||
$request->session()->flash('warning', 'This is a warning.');
|
||||
$request->session()->flash('error', 'This is an error!');
|
||||
|
||||
return redirect(route('home'));
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ class IndexController extends Controller
|
||||
public function export(): LaravelResponse|RedirectResponse
|
||||
{
|
||||
if (auth()->user()->hasRole('demo')) {
|
||||
session()->flash('info', (string) trans('firefly.demo_user_export'));
|
||||
session()->flash('info', (string)trans('firefly.demo_user_export'));
|
||||
|
||||
return redirect(route('export.index'));
|
||||
}
|
||||
|
@ -144,7 +144,6 @@ class JavascriptController extends Controller
|
||||
|
||||
return response()
|
||||
->view('v2.javascript.variables', $data)
|
||||
->header('Content-Type', 'text/javascript')
|
||||
;
|
||||
->header('Content-Type', 'text/javascript');
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ class BoxController extends Controller
|
||||
app('log')->debug('Left to spend is positive or zero!');
|
||||
$boxTitle = (string)trans('firefly.left_to_spend');
|
||||
$activeDaysLeft = $this->activeDaysLeft($start, $end); // see method description.
|
||||
$display = 1; // not overspent
|
||||
$display = 1; // not overspent
|
||||
$leftPerDayAmount = bcdiv($leftToSpendAmount, (string)$activeDaysLeft);
|
||||
app('log')->debug(sprintf('Left to spend per day is %s', $leftPerDayAmount));
|
||||
}
|
||||
|
@ -71,13 +71,13 @@ class RecurrenceController extends Controller
|
||||
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
|
||||
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
|
||||
$firstDate = Carbon::createFromFormat('Y-m-d', $request->get('first_date'));
|
||||
$endDate = '' !== (string) $request->get('end_date') ? Carbon::createFromFormat('Y-m-d', $request->get('end_date')) : null;
|
||||
$endsAt = (string) $request->get('ends');
|
||||
$endDate = '' !== (string)$request->get('end_date') ? Carbon::createFromFormat('Y-m-d', $request->get('end_date')) : null;
|
||||
$endsAt = (string)$request->get('ends');
|
||||
$repetitionType = explode(',', $request->get('type'))[0];
|
||||
$repetitions = (int) $request->get('reps');
|
||||
$weekend = (int) $request->get('weekend');
|
||||
$repetitions = (int)$request->get('reps');
|
||||
$weekend = (int)$request->get('weekend');
|
||||
$repetitionMoment = '';
|
||||
$skip = (int) $request->get('skip');
|
||||
$skip = (int)$request->get('skip');
|
||||
$skip = $skip < 0 || $skip > 31 ? 0 : $skip;
|
||||
$weekend = $weekend < 1 || $weekend > 4 ? 1 : $weekend;
|
||||
|
||||
@ -147,7 +147,7 @@ class RecurrenceController extends Controller
|
||||
*/
|
||||
public function suggest(Request $request): JsonResponse
|
||||
{
|
||||
$string = '' === (string) $request->get('date') ? date('Y-m-d') : (string) $request->get('date');
|
||||
$string = '' === (string)$request->get('date') ? date('Y-m-d') : (string)$request->get('date');
|
||||
$today = today(config('app.timezone'))->startOfDay();
|
||||
|
||||
try {
|
||||
@ -159,37 +159,37 @@ class RecurrenceController extends Controller
|
||||
return response()->json();
|
||||
}
|
||||
$date->startOfDay();
|
||||
$preSelected = (string) $request->get('pre_select');
|
||||
$preSelected = (string)$request->get('pre_select');
|
||||
$locale = app('steam')->getLocale();
|
||||
|
||||
app('log')->debug(sprintf('date = %s, today = %s. date > today? %s', $date->toAtomString(), $today->toAtomString(), var_export($date > $today, true)));
|
||||
app('log')->debug(sprintf('past = true? %s', var_export('true' === (string) $request->get('past'), true)));
|
||||
app('log')->debug(sprintf('past = true? %s', var_export('true' === (string)$request->get('past'), true)));
|
||||
|
||||
$result = [];
|
||||
if ($date > $today || 'true' === (string) $request->get('past')) {
|
||||
if ($date > $today || 'true' === (string)$request->get('past')) {
|
||||
app('log')->debug('Will fill dropdown.');
|
||||
$weekly = sprintf('weekly,%s', $date->dayOfWeekIso);
|
||||
$monthly = sprintf('monthly,%s', $date->day);
|
||||
$dayOfWeek = (string) trans(sprintf('config.dow_%s', $date->dayOfWeekIso));
|
||||
$dayOfWeek = (string)trans(sprintf('config.dow_%s', $date->dayOfWeekIso));
|
||||
$ndom = sprintf('ndom,%s,%s', $date->weekOfMonth, $date->dayOfWeekIso);
|
||||
$yearly = sprintf('yearly,%s', $date->format('Y-m-d'));
|
||||
$yearlyDate = $date->isoFormat((string) trans('config.month_and_day_no_year_js', [], $locale));
|
||||
$yearlyDate = $date->isoFormat((string)trans('config.month_and_day_no_year_js', [], $locale));
|
||||
$result = [
|
||||
'daily' => ['label' => (string) trans('firefly.recurring_daily'), 'selected' => str_starts_with($preSelected, 'daily')],
|
||||
'daily' => ['label' => (string)trans('firefly.recurring_daily'), 'selected' => str_starts_with($preSelected, 'daily')],
|
||||
$weekly => [
|
||||
'label' => (string) trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]),
|
||||
'label' => (string)trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]),
|
||||
'selected' => str_starts_with($preSelected, 'weekly'),
|
||||
],
|
||||
$monthly => [
|
||||
'label' => (string) trans('firefly.recurring_monthly', ['dayOfMonth' => $date->day]),
|
||||
'label' => (string)trans('firefly.recurring_monthly', ['dayOfMonth' => $date->day]),
|
||||
'selected' => str_starts_with($preSelected, 'monthly'),
|
||||
],
|
||||
$ndom => [
|
||||
'label' => (string) trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $date->weekOfMonth]),
|
||||
'label' => (string)trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $date->weekOfMonth]),
|
||||
'selected' => str_starts_with($preSelected, 'ndom'),
|
||||
],
|
||||
$yearly => [
|
||||
'label' => (string) trans('firefly.recurring_yearly', ['date' => $yearlyDate]),
|
||||
'label' => (string)trans('firefly.recurring_yearly', ['date' => $yearlyDate]),
|
||||
'selected' => str_starts_with($preSelected, 'yearly'),
|
||||
],
|
||||
];
|
||||
|
@ -141,24 +141,6 @@ class IndexController extends Controller
|
||||
return view('piggy-banks.index', compact('piggyBanks', 'accounts'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order of a piggy bank.
|
||||
*/
|
||||
public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse
|
||||
{
|
||||
$objectGroupTitle = (string)$request->get('objectGroupTitle');
|
||||
$newOrder = (int)$request->get('order');
|
||||
$this->piggyRepos->setOrder($piggyBank, $newOrder);
|
||||
if ('' !== $objectGroupTitle) {
|
||||
$this->piggyRepos->setObjectGroup($piggyBank, $objectGroupTitle);
|
||||
}
|
||||
if ('' === $objectGroupTitle) {
|
||||
$this->piggyRepos->removeObjectGroup($piggyBank);
|
||||
}
|
||||
|
||||
return response()->json(['data' => 'OK']);
|
||||
}
|
||||
|
||||
private function makeSums(array $piggyBanks): array
|
||||
{
|
||||
$sums = [];
|
||||
@ -193,4 +175,22 @@ class IndexController extends Controller
|
||||
|
||||
return $piggyBanks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order of a piggy bank.
|
||||
*/
|
||||
public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse
|
||||
{
|
||||
$objectGroupTitle = (string)$request->get('objectGroupTitle');
|
||||
$newOrder = (int)$request->get('order');
|
||||
$this->piggyRepos->setOrder($piggyBank, $newOrder);
|
||||
if ('' !== $objectGroupTitle) {
|
||||
$this->piggyRepos->setObjectGroup($piggyBank, $objectGroupTitle);
|
||||
}
|
||||
if ('' === $objectGroupTitle) {
|
||||
$this->piggyRepos->removeObjectGroup($piggyBank);
|
||||
}
|
||||
|
||||
return response()->json(['data' => 'OK']);
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ class PreferencesController extends Controller
|
||||
|
||||
$this->middleware(
|
||||
static function ($request, $next) {
|
||||
app('view')->share('title', (string) trans('firefly.preferences'));
|
||||
app('view')->share('title', (string)trans('firefly.preferences'));
|
||||
app('view')->share('mainTitleIcon', 'fa-gear');
|
||||
|
||||
return $next($request);
|
||||
@ -72,8 +72,8 @@ class PreferencesController extends Controller
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$type = $account->accountType->type;
|
||||
$role = sprintf('opt_group_%s', $repository->getMetaValue($account, 'account_role'));
|
||||
$type = $account->accountType->type;
|
||||
$role = sprintf('opt_group_%s', $repository->getMetaValue($account, 'account_role'));
|
||||
|
||||
if (in_array($type, [AccountType::MORTGAGE, AccountType::DEBT, AccountType::LOAN], true)) {
|
||||
$role = sprintf('opt_group_l_%s', $type);
|
||||
@ -82,7 +82,7 @@ class PreferencesController extends Controller
|
||||
if ('opt_group_' === $role) {
|
||||
$role = 'opt_group_defaultAsset';
|
||||
}
|
||||
$groupedAccounts[(string) trans(sprintf('firefly.%s', $role))][$account->id] = $account->name;
|
||||
$groupedAccounts[(string)trans(sprintf('firefly.%s', $role))][$account->id] = $account->name;
|
||||
}
|
||||
ksort($groupedAccounts);
|
||||
|
||||
@ -105,7 +105,7 @@ class PreferencesController extends Controller
|
||||
if (is_array($fiscalYearStartStr)) {
|
||||
$fiscalYearStartStr = '01-01';
|
||||
}
|
||||
$fiscalYearStart = sprintf('%s-%s', date('Y'), (string) $fiscalYearStartStr);
|
||||
$fiscalYearStart = sprintf('%s-%s', date('Y'), (string)$fiscalYearStartStr);
|
||||
$tjOptionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
|
||||
$availableDarkModes = config('firefly.available_dark_modes');
|
||||
|
||||
@ -120,12 +120,12 @@ class PreferencesController extends Controller
|
||||
// list of locales also has "equal" which makes it equal to whatever the language is.
|
||||
|
||||
try {
|
||||
$locales = json_decode((string) file_get_contents(resource_path(sprintf('lang/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR);
|
||||
$locales = json_decode((string)file_get_contents(resource_path(sprintf('lang/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
$locales = [];
|
||||
}
|
||||
$locales = ['equal' => (string) trans('firefly.equal_to_language')] + $locales;
|
||||
$locales = ['equal' => (string)trans('firefly.equal_to_language')] + $locales;
|
||||
// an important fallback is that the frontPageAccount array gets refilled automatically
|
||||
// when it turns up empty.
|
||||
if (0 === count($frontPageAccounts)) {
|
||||
@ -158,7 +158,7 @@ class PreferencesController extends Controller
|
||||
$frontPageAccounts = [];
|
||||
if (is_array($request->get('frontPageAccounts')) && count($request->get('frontPageAccounts')) > 0) {
|
||||
foreach ($request->get('frontPageAccounts') as $id) {
|
||||
$frontPageAccounts[] = (int) $id;
|
||||
$frontPageAccounts[] = (int)$id;
|
||||
}
|
||||
app('preferences')->set('frontPageAccounts', $frontPageAccounts);
|
||||
}
|
||||
@ -184,7 +184,7 @@ class PreferencesController extends Controller
|
||||
|
||||
// slack URL:
|
||||
if (!auth()->user()->hasRole('demo')) {
|
||||
$url = (string) $request->get('slackUrl');
|
||||
$url = (string)$request->get('slackUrl');
|
||||
if (UrlValidator::isValidWebhookURL($url)) {
|
||||
app('preferences')->set('slack_webhook_url', $url);
|
||||
}
|
||||
@ -194,8 +194,8 @@ class PreferencesController extends Controller
|
||||
}
|
||||
|
||||
// custom fiscal year
|
||||
$customFiscalYear = 1 === (int) $request->get('customFiscalYear');
|
||||
$string = strtotime((string) $request->get('fiscalYearStart'));
|
||||
$customFiscalYear = 1 === (int)$request->get('customFiscalYear');
|
||||
$string = strtotime((string)$request->get('fiscalYearStart'));
|
||||
if (false !== $string) {
|
||||
$fiscalYearStart = date('m-d', $string);
|
||||
app('preferences')->set('customFiscalYear', $customFiscalYear);
|
||||
@ -204,7 +204,7 @@ class PreferencesController extends Controller
|
||||
|
||||
// save page size:
|
||||
app('preferences')->set('listPageSize', 50);
|
||||
$listPageSize = (int) $request->get('listPageSize');
|
||||
$listPageSize = (int)$request->get('listPageSize');
|
||||
if ($listPageSize > 0 && $listPageSize < 1337) {
|
||||
app('preferences')->set('listPageSize', $listPageSize);
|
||||
}
|
||||
@ -252,7 +252,7 @@ class PreferencesController extends Controller
|
||||
app('preferences')->set('darkMode', $darkMode);
|
||||
}
|
||||
|
||||
session()->flash('success', (string) trans('firefly.saved_preferences'));
|
||||
session()->flash('success', (string)trans('firefly.saved_preferences'));
|
||||
app('preferences')->mark();
|
||||
|
||||
return redirect(route('preferences.index'));
|
||||
|
@ -497,6 +497,47 @@ class ProfileController extends Controller
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate code.
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function addToMFAHistory(string $mfaCode): void
|
||||
{
|
||||
/** @var array $mfaHistory */
|
||||
$mfaHistory = app('preferences')->get('mfa_history', [])->data;
|
||||
$entry = [
|
||||
'time' => time(),
|
||||
'code' => $mfaCode,
|
||||
];
|
||||
$mfaHistory[] = $entry;
|
||||
|
||||
app('preferences')->set('mfa_history', $mfaHistory);
|
||||
$this->filterMFAHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove old entries from the preferences array.
|
||||
*/
|
||||
private function filterMFAHistory(): void
|
||||
{
|
||||
/** @var array $mfaHistory */
|
||||
$mfaHistory = app('preferences')->get('mfa_history', [])->data;
|
||||
$newHistory = [];
|
||||
$now = time();
|
||||
foreach ($mfaHistory as $entry) {
|
||||
$time = $entry['time'];
|
||||
$code = $entry['code'];
|
||||
if ($now - $time <= 300) {
|
||||
$newHistory[] = [
|
||||
'time' => $time,
|
||||
'code' => $code,
|
||||
];
|
||||
}
|
||||
}
|
||||
app('preferences')->set('mfa_history', $newHistory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit delete account.
|
||||
*
|
||||
@ -631,45 +672,4 @@ class ProfileController extends Controller
|
||||
|
||||
return redirect(route('login'));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate code.
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function addToMFAHistory(string $mfaCode): void
|
||||
{
|
||||
/** @var array $mfaHistory */
|
||||
$mfaHistory = app('preferences')->get('mfa_history', [])->data;
|
||||
$entry = [
|
||||
'time' => time(),
|
||||
'code' => $mfaCode,
|
||||
];
|
||||
$mfaHistory[] = $entry;
|
||||
|
||||
app('preferences')->set('mfa_history', $mfaHistory);
|
||||
$this->filterMFAHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove old entries from the preferences array.
|
||||
*/
|
||||
private function filterMFAHistory(): void
|
||||
{
|
||||
/** @var array $mfaHistory */
|
||||
$mfaHistory = app('preferences')->get('mfa_history', [])->data;
|
||||
$newHistory = [];
|
||||
$now = time();
|
||||
foreach ($mfaHistory as $entry) {
|
||||
$time = $entry['time'];
|
||||
$code = $entry['code'];
|
||||
if ($now - $time <= 300) {
|
||||
$newHistory[] = [
|
||||
'time' => $time,
|
||||
'code' => $code,
|
||||
];
|
||||
}
|
||||
}
|
||||
app('preferences')->set('mfa_history', $newHistory);
|
||||
}
|
||||
}
|
||||
|
@ -283,8 +283,8 @@ class CategoryController extends Controller
|
||||
];
|
||||
++$result[$key]['transactions'];
|
||||
$result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
|
||||
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']);
|
||||
$result[$key]['avg_float'] = (float) $result[$key]['avg']; // intentional float
|
||||
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
|
||||
$result[$key]['avg_float'] = (float)$result[$key]['avg']; // intentional float
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -333,8 +333,8 @@ class CategoryController extends Controller
|
||||
];
|
||||
++$result[$key]['transactions'];
|
||||
$result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
|
||||
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']);
|
||||
$result[$key]['avg_float'] = (float) $result[$key]['avg'];
|
||||
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
|
||||
$result[$key]['avg_float'] = (float)$result[$key]['avg'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -664,7 +664,7 @@ class CategoryController extends Controller
|
||||
$result[] = [
|
||||
'description' => $journal['description'],
|
||||
'transaction_group_id' => $journal['transaction_group_id'],
|
||||
'amount_float' => (float) $journal['amount'],
|
||||
'amount_float' => (float)$journal['amount'],
|
||||
'amount' => $journal['amount'],
|
||||
'date' => $journal['date']->isoFormat($this->monthAndDayFormat),
|
||||
'date_sort' => $journal['date']->format('Y-m-d'),
|
||||
@ -712,7 +712,7 @@ class CategoryController extends Controller
|
||||
$result[] = [
|
||||
'description' => $journal['description'],
|
||||
'transaction_group_id' => $journal['transaction_group_id'],
|
||||
'amount_float' => (float) $journal['amount'],
|
||||
'amount_float' => (float)$journal['amount'],
|
||||
'amount' => $journal['amount'],
|
||||
'date' => $journal['date']->isoFormat($this->monthAndDayFormat),
|
||||
'date_sort' => $journal['date']->format('Y-m-d'),
|
||||
|
@ -41,7 +41,7 @@ class DoubleController extends Controller
|
||||
{
|
||||
use AugumentData;
|
||||
|
||||
protected AccountRepositoryInterface $accountRepository;
|
||||
protected AccountRepositoryInterface $accountRepository;
|
||||
private OperationsRepositoryInterface $opsRepository;
|
||||
|
||||
/**
|
||||
@ -278,6 +278,24 @@ class DoubleController extends Controller
|
||||
return view('reports.double.partials.accounts', compact('sums', 'report'));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO this method is duplicated.
|
||||
*/
|
||||
private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->name === $name && $account->id !== $id) {
|
||||
return $account->name;
|
||||
}
|
||||
if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) {
|
||||
return $account->iban;
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Factory|View
|
||||
*/
|
||||
@ -468,22 +486,4 @@ class DoubleController extends Controller
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO this method is duplicated.
|
||||
*/
|
||||
private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->name === $name && $account->id !== $id) {
|
||||
return $account->name;
|
||||
}
|
||||
if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) {
|
||||
return $account->iban;
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
@ -279,8 +279,8 @@ class TagController extends Controller
|
||||
];
|
||||
++$result[$key]['transactions'];
|
||||
$result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
|
||||
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']);
|
||||
$result[$key]['avg_float'] = (float) $result[$key]['avg'];
|
||||
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
|
||||
$result[$key]['avg_float'] = (float)$result[$key]['avg'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -329,8 +329,8 @@ class TagController extends Controller
|
||||
];
|
||||
++$result[$key]['transactions'];
|
||||
$result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
|
||||
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']);
|
||||
$result[$key]['avg_float'] = (float) $result[$key]['avg'];
|
||||
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
|
||||
$result[$key]['avg_float'] = (float)$result[$key]['avg'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -466,7 +466,7 @@ class TagController extends Controller
|
||||
$result[] = [
|
||||
'description' => $journal['description'],
|
||||
'transaction_group_id' => $journal['transaction_group_id'],
|
||||
'amount_float' => (float) $journal['amount'],
|
||||
'amount_float' => (float)$journal['amount'],
|
||||
'amount' => $journal['amount'],
|
||||
'date' => $journal['date']->isoFormat($this->monthAndDayFormat),
|
||||
'date_sort' => $journal['date']->format('Y-m-d'),
|
||||
@ -514,7 +514,7 @@ class TagController extends Controller
|
||||
$result[] = [
|
||||
'description' => $journal['description'],
|
||||
'transaction_group_id' => $journal['transaction_group_id'],
|
||||
'amount_float' => (float) $journal['amount'], // intentional float.
|
||||
'amount_float' => (float)$journal['amount'], // intentional float.
|
||||
'amount' => $journal['amount'],
|
||||
'date' => $journal['date']->isoFormat($this->monthAndDayFormat),
|
||||
'date_sort' => $journal['date']->format('Y-m-d'),
|
||||
|
@ -132,29 +132,6 @@ class EditController extends Controller
|
||||
return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the rule.
|
||||
*
|
||||
* @return Redirector|RedirectResponse
|
||||
*/
|
||||
public function update(RuleFormRequest $request, Rule $rule)
|
||||
{
|
||||
$data = $request->getRuleData();
|
||||
|
||||
$this->ruleRepos->update($rule, $data);
|
||||
|
||||
session()->flash('success', (string)trans('firefly.updated_rule', ['title' => $rule->title]));
|
||||
app('preferences')->mark();
|
||||
$redirect = redirect($this->getPreviousUrl('rules.edit.url'));
|
||||
if (1 === (int)$request->get('return_to_edit')) {
|
||||
session()->put('rules.edit.fromUpdate', true);
|
||||
|
||||
$redirect = redirect(route('rules.edit', [$rule->id]))->withInput(['return_to_edit' => 1]);
|
||||
}
|
||||
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
@ -196,4 +173,27 @@ class EditController extends Controller
|
||||
|
||||
return $renderedEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the rule.
|
||||
*
|
||||
* @return Redirector|RedirectResponse
|
||||
*/
|
||||
public function update(RuleFormRequest $request, Rule $rule)
|
||||
{
|
||||
$data = $request->getRuleData();
|
||||
|
||||
$this->ruleRepos->update($rule, $data);
|
||||
|
||||
session()->flash('success', (string)trans('firefly.updated_rule', ['title' => $rule->title]));
|
||||
app('preferences')->mark();
|
||||
$redirect = redirect($this->getPreviousUrl('rules.edit.url'));
|
||||
if (1 === (int)$request->get('return_to_edit')) {
|
||||
session()->put('rules.edit.fromUpdate', true);
|
||||
|
||||
$redirect = redirect(route('rules.edit', [$rule->id]))->withInput(['return_to_edit' => 1]);
|
||||
}
|
||||
|
||||
return $redirect;
|
||||
}
|
||||
}
|
||||
|
@ -129,26 +129,6 @@ class InstallController extends Controller
|
||||
return response()->json($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create specific RSA keys.
|
||||
*/
|
||||
public function keys(): void
|
||||
{
|
||||
$key = RSA::createKey(4096);
|
||||
|
||||
[$publicKey, $privateKey] = [
|
||||
Passport::keyPath('oauth-public.key'),
|
||||
Passport::keyPath('oauth-private.key'),
|
||||
];
|
||||
|
||||
if (file_exists($publicKey) || file_exists($privateKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
file_put_contents($publicKey, (string)$key->getPublicKey());
|
||||
file_put_contents($privateKey, $key->toString('PKCS1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
@ -173,4 +153,24 @@ class InstallController extends Controller
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create specific RSA keys.
|
||||
*/
|
||||
public function keys(): void
|
||||
{
|
||||
$key = RSA::createKey(4096);
|
||||
|
||||
[$publicKey, $privateKey] = [
|
||||
Passport::keyPath('oauth-public.key'),
|
||||
Passport::keyPath('oauth-private.key'),
|
||||
];
|
||||
|
||||
if (file_exists($publicKey) || file_exists($privateKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
file_put_contents($publicKey, (string)$key->getPublicKey());
|
||||
file_put_contents($privateKey, $key->toString('PKCS1'));
|
||||
}
|
||||
}
|
||||
|
@ -135,38 +135,6 @@ class ConvertController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the conversion.
|
||||
*
|
||||
* @return Redirector|RedirectResponse
|
||||
*/
|
||||
public function postIndex(Request $request, TransactionType $destinationType, TransactionGroup $group)
|
||||
{
|
||||
if (!$this->isEditableGroup($group)) {
|
||||
return $this->redirectGroupToAccount($group);
|
||||
}
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($group->transactionJournals as $journal) {
|
||||
// catch FF exception.
|
||||
try {
|
||||
$this->convertJournal($journal, $destinationType, $request->all());
|
||||
} catch (FireflyException $e) {
|
||||
session()->flash('error', $e->getMessage());
|
||||
|
||||
return redirect()->route('transactions.convert.index', [strtolower($destinationType->type), $group->id])->withInput();
|
||||
}
|
||||
}
|
||||
|
||||
// correct transfers:
|
||||
$group->refresh();
|
||||
|
||||
session()->flash('success', (string)trans('firefly.converted_to_'.$destinationType->type));
|
||||
event(new UpdatedTransactionGroup($group, true, true));
|
||||
|
||||
return redirect(route('transactions.show', [$group->id]));
|
||||
}
|
||||
|
||||
private function getValidDepositSources(): array
|
||||
{
|
||||
// make repositories
|
||||
@ -291,6 +259,38 @@ class ConvertController extends Controller
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the conversion.
|
||||
*
|
||||
* @return Redirector|RedirectResponse
|
||||
*/
|
||||
public function postIndex(Request $request, TransactionType $destinationType, TransactionGroup $group)
|
||||
{
|
||||
if (!$this->isEditableGroup($group)) {
|
||||
return $this->redirectGroupToAccount($group);
|
||||
}
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($group->transactionJournals as $journal) {
|
||||
// catch FF exception.
|
||||
try {
|
||||
$this->convertJournal($journal, $destinationType, $request->all());
|
||||
} catch (FireflyException $e) {
|
||||
session()->flash('error', $e->getMessage());
|
||||
|
||||
return redirect()->route('transactions.convert.index', [strtolower($destinationType->type), $group->id])->withInput();
|
||||
}
|
||||
}
|
||||
|
||||
// correct transfers:
|
||||
$group->refresh();
|
||||
|
||||
session()->flash('success', (string)trans('firefly.converted_to_'.$destinationType->type));
|
||||
event(new UpdatedTransactionGroup($group, true, true));
|
||||
|
||||
return redirect(route('transactions.show', [$group->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
|
@ -50,7 +50,7 @@ class CreateController extends Controller
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('title', (string) trans('firefly.transactions'));
|
||||
app('view')->share('title', (string)trans('firefly.transactions'));
|
||||
app('view')->share('mainTitleIcon', 'fa-exchange');
|
||||
$this->repository = app(TransactionGroupRepositoryInterface::class);
|
||||
|
||||
@ -61,7 +61,7 @@ class CreateController extends Controller
|
||||
|
||||
public function cloneGroup(Request $request): JsonResponse
|
||||
{
|
||||
$groupId = (int) $request->get('id');
|
||||
$groupId = (int)$request->get('id');
|
||||
if (0 !== $groupId) {
|
||||
$group = $this->repository->find($groupId);
|
||||
if (null !== $group) {
|
||||
@ -101,14 +101,14 @@ class CreateController extends Controller
|
||||
{
|
||||
app('preferences')->mark();
|
||||
|
||||
$sourceId = (int) request()->get('source');
|
||||
$destinationId = (int) request()->get('destination');
|
||||
$sourceId = (int)request()->get('source');
|
||||
$destinationId = (int)request()->get('destination');
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$cash = $accountRepository->getCashAccount();
|
||||
$preFilled = session()->has('preFilled') ? session('preFilled') : [];
|
||||
$subTitle = (string) trans(sprintf('breadcrumbs.create_%s', strtolower((string) $objectType)));
|
||||
$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');
|
||||
|
@ -51,7 +51,7 @@ class EditController extends Controller
|
||||
// translations:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('title', (string) trans('firefly.transactions'));
|
||||
app('view')->share('title', (string)trans('firefly.transactions'));
|
||||
app('view')->share('mainTitleIcon', 'fa-exchange');
|
||||
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
@ -79,7 +79,7 @@ class EditController extends Controller
|
||||
$expectedSourceTypes = config('firefly.expected_source_types');
|
||||
$allowedSourceDests = config('firefly.source_dests');
|
||||
$title = $transactionGroup->transactionJournals()->count() > 1 ? $transactionGroup->title : $transactionGroup->transactionJournals()->first()->description;
|
||||
$subTitle = (string) trans('firefly.edit_transaction_title', ['description' => $title]);
|
||||
$subTitle = (string)trans('firefly.edit_transaction_title', ['description' => $title]);
|
||||
$subTitleIcon = 'fa-plus';
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$cash = $repository->getCashAccount();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user