PHPStorm can order methods by alphabet, who knew.

This commit is contained in:
James Cole 2024-02-22 20:11:09 +01:00
parent f9d4a43e05
commit 68c9c4ec3c
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
221 changed files with 5840 additions and 5843 deletions

View File

@ -77,7 +77,7 @@ class AccountController extends Controller
$query = $data['query']; $query = $data['query'];
$date = $data['date'] ?? today(config('app.timezone')); $date = $data['date'] ?? today(config('app.timezone'));
$return = []; $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. // TODO this code is duplicated in the V2 Autocomplete controller, which means this code is due to be deprecated.
$defaultCurrency = app('amount')->getDefaultCurrency(); $defaultCurrency = app('amount')->getDefaultCurrency();
@ -97,11 +97,11 @@ class AccountController extends Controller
} }
$return[] = [ $return[] = [
'id' => (string) $account->id, 'id' => (string)$account->id,
'name' => $account->name, 'name' => $account->name,
'name_with_balance' => $nameWithBalance, 'name_with_balance' => $nameWithBalance,
'type' => $account->accountType->type, 'type' => $account->accountType->type,
'currency_id' => (string) $currency->id, 'currency_id' => (string)$currency->id,
'currency_name' => $currency->name, 'currency_name' => $currency->name,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
@ -114,8 +114,8 @@ class AccountController extends Controller
$return, $return,
static function (array $left, array $right) { static function (array $left, array $right) {
$order = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE]; $order = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE];
$posA = (int) array_search($left['type'], $order, true); $posA = (int)array_search($left['type'], $order, true);
$posB = (int) array_search($right['type'], $order, true); $posB = (int)array_search($right['type'], $order, true);
return $posA - $posB; return $posA - $posB;
} }

View File

@ -66,7 +66,7 @@ class BillController extends Controller
$filtered = $result->map( $filtered = $result->map(
static function (Bill $item) { static function (Bill $item) {
return [ return [
'id' => (string) $item->id, 'id' => (string)$item->id,
'name' => $item->name, 'name' => $item->name,
'active' => $item->active, 'active' => $item->active,
]; ];

View File

@ -66,7 +66,7 @@ class BudgetController extends Controller
$filtered = $result->map( $filtered = $result->map(
static function (Budget $item) { static function (Budget $item) {
return [ return [
'id' => (string) $item->id, 'id' => (string)$item->id,
'name' => $item->name, 'name' => $item->name,
]; ];
} }

View File

@ -66,7 +66,7 @@ class CategoryController extends Controller
$filtered = $result->map( $filtered = $result->map(
static function (Category $item) { static function (Category $item) {
return [ return [
'id' => (string) $item->id, 'id' => (string)$item->id,
'name' => $item->name, 'name' => $item->name,
]; ];
} }

View File

@ -68,7 +68,7 @@ class CurrencyController extends Controller
/** @var TransactionCurrency $currency */ /** @var TransactionCurrency $currency */
foreach ($collection as $currency) { foreach ($collection as $currency) {
$result[] = [ $result[] = [
'id' => (string) $currency->id, 'id' => (string)$currency->id,
'name' => $currency->name, 'name' => $currency->name,
'code' => $currency->code, 'code' => $currency->code,
'symbol' => $currency->symbol, 'symbol' => $currency->symbol,
@ -94,7 +94,7 @@ class CurrencyController extends Controller
/** @var TransactionCurrency $currency */ /** @var TransactionCurrency $currency */
foreach ($collection as $currency) { foreach ($collection as $currency) {
$result[] = [ $result[] = [
'id' => (string) $currency->id, 'id' => (string)$currency->id,
'name' => sprintf('%s (%s)', $currency->name, $currency->code), 'name' => sprintf('%s (%s)', $currency->name, $currency->code),
'code' => $currency->code, 'code' => $currency->code,
'symbol' => $currency->symbol, 'symbol' => $currency->symbol,

View File

@ -68,7 +68,7 @@ class ObjectGroupController extends Controller
/** @var ObjectGroup $objectGroup */ /** @var ObjectGroup $objectGroup */
foreach ($result as $objectGroup) { foreach ($result as $objectGroup) {
$return[] = [ $return[] = [
'id' => (string) $objectGroup->id, 'id' => (string)$objectGroup->id,
'name' => $objectGroup->title, 'name' => $objectGroup->title,
'title' => $objectGroup->title, 'title' => $objectGroup->title,
]; ];

View File

@ -75,14 +75,14 @@ class PiggyBankController extends Controller
$currency = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency; $currency = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency;
$objectGroup = $piggy->objectGroups()->first(); $objectGroup = $piggy->objectGroups()->first();
$response[] = [ $response[] = [
'id' => (string) $piggy->id, 'id' => (string)$piggy->id,
'name' => $piggy->name, 'name' => $piggy->name,
'currency_id' => (string) $currency->id, 'currency_id' => (string)$currency->id,
'currency_name' => $currency->name, 'currency_name' => $currency->name,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, '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, 'object_group_title' => $objectGroup?->title,
]; ];
} }
@ -107,7 +107,7 @@ class PiggyBankController extends Controller
$currentAmount = $this->piggyRepository->getRepetition($piggy)->currentamount ?? '0'; $currentAmount = $this->piggyRepository->getRepetition($piggy)->currentamount ?? '0';
$objectGroup = $piggy->objectGroups()->first(); $objectGroup = $piggy->objectGroups()->first();
$response[] = [ $response[] = [
'id' => (string) $piggy->id, 'id' => (string)$piggy->id,
'name' => $piggy->name, 'name' => $piggy->name,
'name_with_balance' => sprintf( 'name_with_balance' => sprintf(
'%s (%s / %s)', '%s (%s / %s)',
@ -115,12 +115,12 @@ class PiggyBankController extends Controller
app('amount')->formatAnything($currency, $currentAmount, false), app('amount')->formatAnything($currency, $currentAmount, false),
app('amount')->formatAnything($currency, $piggy->targetamount, false), app('amount')->formatAnything($currency, $piggy->targetamount, false),
), ),
'currency_id' => (string) $currency->id, 'currency_id' => (string)$currency->id,
'currency_name' => $currency->name, 'currency_name' => $currency->name,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, '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, 'object_group_title' => $objectGroup?->title,
]; ];
} }

View File

@ -66,7 +66,7 @@ class RecurrenceController extends Controller
/** @var Recurrence $recurrence */ /** @var Recurrence $recurrence */
foreach ($recurrences as $recurrence) { foreach ($recurrences as $recurrence) {
$response[] = [ $response[] = [
'id' => (string) $recurrence->id, 'id' => (string)$recurrence->id,
'name' => $recurrence->title, 'name' => $recurrence->title,
'description' => $recurrence->description, 'description' => $recurrence->description,
]; ];

View File

@ -65,7 +65,7 @@ class RuleController extends Controller
/** @var Rule $rule */ /** @var Rule $rule */
foreach ($rules as $rule) { foreach ($rules as $rule) {
$response[] = [ $response[] = [
'id' => (string) $rule->id, 'id' => (string)$rule->id,
'name' => $rule->title, 'name' => $rule->title,
'description' => $rule->description, 'description' => $rule->description,
]; ];

View File

@ -65,7 +65,7 @@ class RuleGroupController extends Controller
/** @var RuleGroup $group */ /** @var RuleGroup $group */
foreach ($groups as $group) { foreach ($groups as $group) {
$response[] = [ $response[] = [
'id' => (string) $group->id, 'id' => (string)$group->id,
'name' => $group->title, 'name' => $group->title,
'description' => $group->description, 'description' => $group->description,
]; ];

View File

@ -68,7 +68,7 @@ class TagController extends Controller
/** @var Tag $tag */ /** @var Tag $tag */
foreach ($result as $tag) { foreach ($result as $tag) {
$array[] = [ $array[] = [
'id' => (string) $tag->id, 'id' => (string)$tag->id,
'name' => $tag->tag, 'name' => $tag->tag,
'tag' => $tag->tag, 'tag' => $tag->tag,
]; ];

View File

@ -76,8 +76,8 @@ class TransactionController extends Controller
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($filtered as $journal) { foreach ($filtered as $journal) {
$array[] = [ $array[] = [
'id' => (string) $journal->id, 'id' => (string)$journal->id,
'transaction_group_id' => (string) $journal->transaction_group_id, 'transaction_group_id' => (string)$journal->transaction_group_id,
'name' => $journal->description, 'name' => $journal->description,
'description' => $journal->description, 'description' => $journal->description,
]; ];
@ -96,7 +96,7 @@ class TransactionController extends Controller
$result = new Collection(); $result = new Collection();
if (is_numeric($data['query'])) { if (is_numeric($data['query'])) {
// search for group, not journal. // search for group, not journal.
$firstResult = $this->groupRepository->find((int) $data['query']); $firstResult = $this->groupRepository->find((int)$data['query']);
if (null !== $firstResult) { if (null !== $firstResult) {
// group may contain multiple journals, each a result: // group may contain multiple journals, each a result:
foreach ($firstResult->transactionJournals as $journal) { foreach ($firstResult->transactionJournals as $journal) {
@ -114,8 +114,8 @@ class TransactionController extends Controller
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($result as $journal) { foreach ($result as $journal) {
$array[] = [ $array[] = [
'id' => (string) $journal->id, 'id' => (string)$journal->id,
'transaction_group_id' => (string) $journal->transaction_group_id, 'transaction_group_id' => (string)$journal->transaction_group_id,
'name' => sprintf('#%d: %s', $journal->transaction_group_id, $journal->description), 'name' => sprintf('#%d: %s', $journal->transaction_group_id, $journal->description),
'description' => sprintf('#%d: %s', $journal->transaction_group_id, $journal->description), 'description' => sprintf('#%d: %s', $journal->transaction_group_id, $journal->description),
]; ];

View File

@ -65,7 +65,7 @@ class TransactionTypeController extends Controller
foreach ($types as $type) { foreach ($types as $type) {
// different key for consistency. // different key for consistency.
$array[] = [ $array[] = [
'id' => (string) $type->id, 'id' => (string)$type->id,
'name' => $type->type, 'name' => $type->type,
'type' => $type->type, 'type' => $type->type,
]; ];

View File

@ -104,7 +104,7 @@ class AccountController extends Controller
} }
$currentSet = [ $currentSet = [
'label' => $account->name, 'label' => $account->name,
'currency_id' => (string) $currency->id, 'currency_id' => (string)$currency->id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $currency->decimal_places,

View File

@ -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. * Method to grab all parameters from the URL.
*/ */
@ -216,4 +184,36 @@ abstract class Controller extends BaseController
return $bag; 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;
}
} }

View File

@ -70,8 +70,8 @@ class TransactionController extends Controller
// to respond to what is in the $query. // to respond to what is in the $query.
// this is OK because only one thing can be in the query at the moment. // this is OK because only one thing can be in the query at the moment.
if ($this->isUpdateTransactionAccount($params)) { if ($this->isUpdateTransactionAccount($params)) {
$original = $this->repository->find((int) $params['where']['account_id']); $original = $this->repository->find((int)$params['where']['account_id']);
$destination = $this->repository->find((int) $params['update']['account_id']); $destination = $this->repository->find((int)$params['update']['account_id']);
/** @var AccountDestroyService $service */ /** @var AccountDestroyService $service */
$service = app(AccountDestroyService::class); $service = app(AccountDestroyService::class);

View File

@ -68,6 +68,32 @@ class ExportController extends Controller
return $this->returnExport('accounts'); 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: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBills * 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'); 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;
}
} }

View File

@ -75,28 +75,28 @@ class TagController extends Controller
$genericSet = $collector->getExtractedJournals(); $genericSet = $collector->getExtractedJournals();
foreach ($genericSet as $journal) { foreach ($genericSet as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int)$journal['currency_id'];
$foreignCurrencyId = (int) $journal['foreign_currency_id']; $foreignCurrencyId = (int)$journal['foreign_currency_id'];
if (0 !== $currencyId) { if (0 !== $currencyId) {
$response[$currencyId] ??= [ $response[$currencyId] ??= [
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $currencyId, 'currency_id' => (string)$currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $journal['currency_code'],
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], $journal['amount']); $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) { if (0 !== $foreignCurrencyId) {
$response[$foreignCurrencyId] ??= [ $response[$foreignCurrencyId] ??= [
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $foreignCurrencyId, 'currency_id' => (string)$foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'], 'currency_code' => $journal['foreign_currency_code'],
]; ];
$response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], $journal['foreign_amount']); $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 */ /** @var array $journal */
foreach ($genericSet as $journal) { foreach ($genericSet as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int)$journal['currency_id'];
$foreignCurrencyId = (int) $journal['foreign_currency_id']; $foreignCurrencyId = (int)$journal['foreign_currency_id'];
/** @var array $tag */ /** @var array $tag */
foreach ($journal['tags'] as $tag) { foreach ($journal['tags'] as $tag) {
@ -142,15 +142,15 @@ class TagController extends Controller
// on currency ID // on currency ID
if (0 !== $currencyId) { if (0 !== $currencyId) {
$response[$key] ??= [ $response[$key] ??= [
'id' => (string) $tagId, 'id' => (string)$tagId,
'name' => $tag['name'], 'name' => $tag['name'],
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $currencyId, 'currency_id' => (string)$currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $journal['currency_code'],
]; ];
$response[$key]['difference'] = bcadd($response[$key]['difference'], $journal['amount']); $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 // on foreign ID
@ -158,11 +158,11 @@ class TagController extends Controller
$response[$foreignKey] = $journal[$foreignKey] ?? [ $response[$foreignKey] = $journal[$foreignKey] ?? [
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $foreignCurrencyId, 'currency_id' => (string)$foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'], 'currency_code' => $journal['foreign_currency_code'],
]; ];
$response[$foreignKey]['difference'] = bcadd($response[$foreignKey]['difference'], $journal['foreign_amount']); $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.
} }
} }
} }

View File

@ -118,23 +118,6 @@ class BasicController extends Controller
return response()->json($return); 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 private function getBalanceInformation(Carbon $start, Carbon $end): array
{ {
// prep some arrays: // prep some arrays:
@ -152,7 +135,7 @@ class BasicController extends Controller
/** @var array $transactionJournal */ /** @var array $transactionJournal */
foreach ($set as $transactionJournal) { foreach ($set as $transactionJournal) {
$currencyId = (int) $transactionJournal['currency_id']; $currencyId = (int)$transactionJournal['currency_id'];
$incomes[$currencyId] ??= '0'; $incomes[$currencyId] ??= '0';
$incomes[$currencyId] = bcadd( $incomes[$currencyId] = bcadd(
$incomes[$currencyId], $incomes[$currencyId],
@ -170,7 +153,7 @@ class BasicController extends Controller
/** @var array $transactionJournal */ /** @var array $transactionJournal */
foreach ($set as $transactionJournal) { foreach ($set as $transactionJournal) {
$currencyId = (int) $transactionJournal['currency_id']; $currencyId = (int)$transactionJournal['currency_id'];
$expenses[$currencyId] ??= '0'; $expenses[$currencyId] ??= '0';
$expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']); $expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']);
$sums[$currencyId] ??= '0'; $sums[$currencyId] ??= '0';
@ -189,7 +172,7 @@ class BasicController extends Controller
'key' => sprintf('balance-in-%s', $currency->code), 'key' => sprintf('balance-in-%s', $currency->code),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]), 'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $sums[$currencyId] ?? '0', 'monetary_value' => $sums[$currencyId] ?? '0',
'currency_id' => (string) $currency->id, 'currency_id' => (string)$currency->id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $currency->decimal_places,
@ -202,7 +185,7 @@ class BasicController extends Controller
'key' => sprintf('spent-in-%s', $currency->code), 'key' => sprintf('spent-in-%s', $currency->code),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]), 'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $expenses[$currencyId] ?? '0', 'monetary_value' => $expenses[$currencyId] ?? '0',
'currency_id' => (string) $currency->id, 'currency_id' => (string)$currency->id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $currency->decimal_places,
@ -214,7 +197,7 @@ class BasicController extends Controller
'key' => sprintf('earned-in-%s', $currency->code), 'key' => sprintf('earned-in-%s', $currency->code),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]), 'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $incomes[$currencyId] ?? '0', 'monetary_value' => $incomes[$currencyId] ?? '0',
'currency_id' => (string) $currency->id, 'currency_id' => (string)$currency->id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $currency->decimal_places,
@ -248,7 +231,7 @@ class BasicController extends Controller
'key' => sprintf('bills-paid-in-%s', $info['code']), 'key' => sprintf('bills-paid-in-%s', $info['code']),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $info['symbol']]), 'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $info['symbol']]),
'monetary_value' => $amount, 'monetary_value' => $amount,
'currency_id' => (string) $info['id'], 'currency_id' => (string)$info['id'],
'currency_code' => $info['code'], 'currency_code' => $info['code'],
'currency_symbol' => $info['symbol'], 'currency_symbol' => $info['symbol'],
'currency_decimal_places' => $info['decimal_places'], 'currency_decimal_places' => $info['decimal_places'],
@ -267,7 +250,7 @@ class BasicController extends Controller
'key' => sprintf('bills-unpaid-in-%s', $info['code']), 'key' => sprintf('bills-unpaid-in-%s', $info['code']),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $info['symbol']]), 'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $info['symbol']]),
'monetary_value' => $amount, 'monetary_value' => $amount,
'currency_id' => (string) $info['id'], 'currency_id' => (string)$info['id'],
'currency_code' => $info['code'], 'currency_code' => $info['code'],
'currency_symbol' => $info['symbol'], 'currency_symbol' => $info['symbol'],
'currency_decimal_places' => $info['decimal_places'], 'currency_decimal_places' => $info['decimal_places'],
@ -294,21 +277,21 @@ class BasicController extends Controller
foreach ($spent as $row) { foreach ($spent as $row) {
// either an amount was budgeted or 0 is available. // 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']; $spentInCurrency = $row['sum'];
$leftToSpend = bcadd($amount, $spentInCurrency); $leftToSpend = bcadd($amount, $spentInCurrency);
$days = $today->diffInDays($end) + 1; $days = $today->diffInDays($end) + 1;
$perDay = '0'; $perDay = '0';
if (0 !== $days && bccomp($leftToSpend, '0') > -1) { if (0 !== $days && bccomp($leftToSpend, '0') > -1) {
$perDay = bcdiv($leftToSpend, (string) $days); $perDay = bcdiv($leftToSpend, (string)$days);
} }
$return[] = [ $return[] = [
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']), 'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]), 'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]),
'monetary_value' => $leftToSpend, 'monetary_value' => $leftToSpend,
'currency_id' => (string) $row['currency_id'], 'currency_id' => (string)$row['currency_id'],
'currency_code' => $row['currency_code'], 'currency_code' => $row['currency_code'],
'currency_symbol' => $row['currency_symbol'], 'currency_symbol' => $row['currency_symbol'],
'currency_decimal_places' => $row['currency_decimal_places'], 'currency_decimal_places' => $row['currency_decimal_places'],
@ -368,7 +351,7 @@ class BasicController extends Controller
'key' => sprintf('net-worth-in-%s', $data['currency_code']), 'key' => sprintf('net-worth-in-%s', $data['currency_code']),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $data['currency_symbol']]), 'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $data['currency_symbol']]),
'monetary_value' => $amount, 'monetary_value' => $amount,
'currency_id' => (string) $data['currency_id'], 'currency_id' => (string)$data['currency_id'],
'currency_code' => $data['currency_code'], 'currency_code' => $data['currency_code'],
'currency_symbol' => $data['currency_symbol'], 'currency_symbol' => $data['currency_symbol'],
'currency_decimal_places' => $data['currency_decimal_places'], 'currency_decimal_places' => $data['currency_decimal_places'],
@ -380,4 +363,21 @@ class BasicController extends Controller
return $return; 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;
}
} }

View File

@ -88,6 +88,35 @@ class ConfigurationController extends Controller
return response()->json($return); 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: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/configuration/getSingleConfiguration * 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); 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;
}
} }

View File

@ -83,12 +83,12 @@ class MoveTransactionsRequest extends FormRequest
$data = $validator->getData(); $data = $validator->getData();
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$repository->setUser(auth()->user()); $repository->setUser(auth()->user());
$original = $repository->find((int) $data['original_account']); $original = $repository->find((int)$data['original_account']);
$destination = $repository->find((int) $data['destination_account']); $destination = $repository->find((int)$data['destination_account']);
// not the same type: // not the same type:
if ($original->accountType->type !== $destination->accountType->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; return;
} }
@ -98,7 +98,7 @@ class MoveTransactionsRequest extends FormRequest
// check different scenario's. // check different scenario's.
if (null === $originalCurrency xor null === $destinationCurrency) { 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; return;
} }
@ -107,7 +107,7 @@ class MoveTransactionsRequest extends FormRequest
return; return;
} }
if ($originalCurrency->code !== $destinationCurrency->code) { 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'));
} }
} }
} }

View File

@ -78,6 +78,25 @@ class GenericRequest extends FormRequest
return $return; 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 public function getBills(): Collection
{ {
$this->parseBills(); $this->parseBills();
@ -85,6 +104,25 @@ class GenericRequest extends FormRequest
return $this->bills; 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 public function getBudgets(): Collection
{ {
$this->parseBudgets(); $this->parseBudgets();
@ -92,6 +130,25 @@ class GenericRequest extends FormRequest
return $this->budgets; 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 public function getCategories(): Collection
{ {
$this->parseCategories(); $this->parseCategories();
@ -99,6 +156,25 @@ class GenericRequest extends FormRequest
return $this->categories; 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 public function getEnd(): Carbon
{ {
$date = $this->getCarbonDate('end'); $date = $this->getCarbonDate('end');
@ -154,100 +230,6 @@ class GenericRequest extends FormRequest
return $this->tags; 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 private function parseTags(): void
{ {
if (0 !== $this->tags->count()) { 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',
];
}
} }

View File

@ -83,7 +83,7 @@ class UpdateRequest extends FormRequest
$start = new Carbon($data['start']); $start = new Carbon($data['start']);
$end = new Carbon($data['end']); $end = new Carbon($data['end']);
if ($end->isBefore($start)) { if ($end->isBefore($start)) {
$validator->errors()->add('end', (string) trans('validation.date_after')); $validator->errors()->add('end', (string)trans('validation.date_after'));
} }
} }
} }

View File

@ -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. * 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()); 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;
}
} }

View File

@ -78,6 +78,70 @@ class UpdateRequest extends FormRequest
return $return; 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. * 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()); 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;
}
} }

View File

@ -64,6 +64,42 @@ class StoreRequest extends FormRequest
return $data; 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. * 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')); $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;
}
} }

View File

@ -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 private function getPage(): int
{ {
return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page'); return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page');
@ -81,4 +71,14 @@ class TestRequest extends FormRequest
{ {
return $this->get('accounts'); 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',
];
}
} }

View File

@ -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 private function getDate(string $field): ?Carbon
{ {
$value = $this->query($field); $value = $this->query($field);
@ -75,4 +65,14 @@ class TriggerRequest extends FormRequest
{ {
return $this->get('accounts') ?? []; return $this->get('accounts') ?? [];
} }
public function rules(): array
{
return [
'start' => 'date',
'end' => 'date|after_or_equal:start',
'accounts' => '',
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
];
}
} }

View File

@ -70,6 +70,50 @@ class UpdateRequest extends FormRequest
return $return; 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. * 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')); $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;
}
} }

View File

@ -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 private function getDate(string $field): ?Carbon
{ {
$value = $this->query($field); $value = $this->query($field);
@ -75,4 +65,14 @@ class TestRequest extends FormRequest
{ {
return $this->get('accounts'); return $this->get('accounts');
} }
public function rules(): array
{
return [
'start' => 'date',
'end' => 'date|after_or_equal:start',
'accounts' => '',
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
];
}
} }

View File

@ -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 private function getDate(string $field): ?Carbon
{ {
$value = $this->query($field); $value = $this->query($field);
@ -77,4 +69,12 @@ class TriggerRequest extends FormRequest
return $this->get('accounts'); return $this->get('accounts');
} }
public function rules(): array
{
return [
'start' => 'date',
'end' => 'date|after_or_equal:start',
];
}
} }

View File

@ -69,6 +69,101 @@ class StoreRequest extends FormRequest
// TODO include location and ability to process it. // TODO include location and ability to process it.
} }
/**
* Get transaction data.
*/
private function getTransactionData(): array
{
$return = [];
/**
* @var array $transaction
*/
foreach ($this->get('transactions') as $transaction) {
$object = new NullArrayObject($transaction);
$return[] = [
'type' => $this->clearString($object['type']),
'date' => $this->dateFromValue($object['date']),
'order' => $this->integerFromValue((string)$object['order']),
'currency_id' => $this->integerFromValue((string)$object['currency_id']),
'currency_code' => $this->clearString((string)$object['currency_code']),
// foreign currency info:
'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']),
'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']),
// amount and foreign amount. Cannot be 0.
'amount' => $this->clearString((string)$object['amount']),
'foreign_amount' => $this->clearString((string)$object['foreign_amount']),
// description.
'description' => $this->clearString($object['description']),
// source of transaction. If everything is null, assume cash account.
'source_id' => $this->integerFromValue((string)$object['source_id']),
'source_name' => $this->clearString((string)$object['source_name']),
'source_iban' => $this->clearString((string)$object['source_iban']),
'source_number' => $this->clearString((string)$object['source_number']),
'source_bic' => $this->clearString((string)$object['source_bic']),
// destination of transaction. If everything is null, assume cash account.
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
'destination_name' => $this->clearString((string)$object['destination_name']),
'destination_iban' => $this->clearString((string)$object['destination_iban']),
'destination_number' => $this->clearString((string)$object['destination_number']),
'destination_bic' => $this->clearString((string)$object['destination_bic']),
// budget info
'budget_id' => $this->integerFromValue((string)$object['budget_id']),
'budget_name' => $this->clearString((string)$object['budget_name']),
// category info
'category_id' => $this->integerFromValue((string)$object['category_id']),
'category_name' => $this->clearString((string)$object['category_name']),
// journal bill reference. Optional. Will only work for withdrawals
'bill_id' => $this->integerFromValue((string)$object['bill_id']),
'bill_name' => $this->clearString((string)$object['bill_name']),
// piggy bank reference. Optional. Will only work for transfers
'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']),
'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']),
// some other interesting properties
'reconciled' => $this->convertBoolean((string)$object['reconciled']),
'notes' => $this->clearStringKeepNewlines((string)$object['notes']),
'tags' => $this->arrayFromValue($object['tags']),
// all custom fields:
'internal_reference' => $this->clearString((string)$object['internal_reference']),
'external_id' => $this->clearString((string)$object['external_id']),
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']),
'external_url' => $this->clearString((string)$object['external_url']),
'sepa_cc' => $this->clearString((string)$object['sepa_cc']),
'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']),
'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']),
'sepa_db' => $this->clearString((string)$object['sepa_db']),
'sepa_country' => $this->clearString((string)$object['sepa_country']),
'sepa_ep' => $this->clearString((string)$object['sepa_ep']),
'sepa_ci' => $this->clearString((string)$object['sepa_ci']),
'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']),
// custom date fields. Must be Carbon objects. Presence is optional.
'interest_date' => $this->dateFromValue($object['interest_date']),
'book_date' => $this->dateFromValue($object['book_date']),
'process_date' => $this->dateFromValue($object['process_date']),
'due_date' => $this->dateFromValue($object['due_date']),
'payment_date' => $this->dateFromValue($object['payment_date']),
'invoice_date' => $this->dateFromValue($object['invoice_date']),
];
}
return $return;
}
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
*/ */
@ -196,99 +291,4 @@ class StoreRequest extends FormRequest
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
} }
} }
/**
* Get transaction data.
*/
private function getTransactionData(): array
{
$return = [];
/**
* @var array $transaction
*/
foreach ($this->get('transactions') as $transaction) {
$object = new NullArrayObject($transaction);
$return[] = [
'type' => $this->clearString($object['type']),
'date' => $this->dateFromValue($object['date']),
'order' => $this->integerFromValue((string)$object['order']),
'currency_id' => $this->integerFromValue((string)$object['currency_id']),
'currency_code' => $this->clearString((string)$object['currency_code']),
// foreign currency info:
'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']),
'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']),
// amount and foreign amount. Cannot be 0.
'amount' => $this->clearString((string)$object['amount']),
'foreign_amount' => $this->clearString((string)$object['foreign_amount']),
// description.
'description' => $this->clearString($object['description']),
// source of transaction. If everything is null, assume cash account.
'source_id' => $this->integerFromValue((string)$object['source_id']),
'source_name' => $this->clearString((string)$object['source_name']),
'source_iban' => $this->clearString((string)$object['source_iban']),
'source_number' => $this->clearString((string)$object['source_number']),
'source_bic' => $this->clearString((string)$object['source_bic']),
// destination of transaction. If everything is null, assume cash account.
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
'destination_name' => $this->clearString((string)$object['destination_name']),
'destination_iban' => $this->clearString((string)$object['destination_iban']),
'destination_number' => $this->clearString((string)$object['destination_number']),
'destination_bic' => $this->clearString((string)$object['destination_bic']),
// budget info
'budget_id' => $this->integerFromValue((string)$object['budget_id']),
'budget_name' => $this->clearString((string)$object['budget_name']),
// category info
'category_id' => $this->integerFromValue((string)$object['category_id']),
'category_name' => $this->clearString((string)$object['category_name']),
// journal bill reference. Optional. Will only work for withdrawals
'bill_id' => $this->integerFromValue((string)$object['bill_id']),
'bill_name' => $this->clearString((string)$object['bill_name']),
// piggy bank reference. Optional. Will only work for transfers
'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']),
'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']),
// some other interesting properties
'reconciled' => $this->convertBoolean((string)$object['reconciled']),
'notes' => $this->clearStringKeepNewlines((string)$object['notes']),
'tags' => $this->arrayFromValue($object['tags']),
// all custom fields:
'internal_reference' => $this->clearString((string)$object['internal_reference']),
'external_id' => $this->clearString((string)$object['external_id']),
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']),
'external_url' => $this->clearString((string)$object['external_url']),
'sepa_cc' => $this->clearString((string)$object['sepa_cc']),
'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']),
'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']),
'sepa_db' => $this->clearString((string)$object['sepa_db']),
'sepa_country' => $this->clearString((string)$object['sepa_country']),
'sepa_ep' => $this->clearString((string)$object['sepa_ep']),
'sepa_ci' => $this->clearString((string)$object['sepa_ci']),
'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']),
// custom date fields. Must be Carbon objects. Presence is optional.
'interest_date' => $this->dateFromValue($object['interest_date']),
'book_date' => $this->dateFromValue($object['book_date']),
'process_date' => $this->dateFromValue($object['process_date']),
'due_date' => $this->dateFromValue($object['due_date']),
'payment_date' => $this->dateFromValue($object['payment_date']),
'invoice_date' => $this->dateFromValue($object['invoice_date']),
];
}
return $return;
}
} }

View File

@ -90,6 +90,158 @@ class UpdateRequest extends FormRequest
return $data; return $data;
} }
/**
* Get transaction data.
*
* @throws FireflyException
*/
private function getTransactionData(): array
{
app('log')->debug(sprintf('Now in %s', __METHOD__));
$return = [];
/** @var null|array $transactions */
$transactions = $this->get('transactions');
if (!is_countable($transactions)) {
return $return;
}
/** @var null|array $transaction */
foreach ($transactions as $transaction) {
if (!is_array($transaction)) {
throw new FireflyException('Invalid data submitted: transaction is not array.');
}
// default response is to update nothing in the transaction:
$current = [];
$current = $this->getIntegerData($current, $transaction);
$current = $this->getStringData($current, $transaction);
$current = $this->getNlStringData($current, $transaction);
$current = $this->getDateData($current, $transaction);
$current = $this->getBooleanData($current, $transaction);
$current = $this->getArrayData($current, $transaction);
$current = $this->getFloatData($current, $transaction);
$return[] = $current;
}
return $return;
}
/**
* For each field, add it to the array if a reference is present in the request:
*
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getIntegerData(array $current, array $transaction): array
{
foreach ($this->integerFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->integerFromValue((string)$transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getStringData(array $current, array $transaction): array
{
foreach ($this->stringFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->clearString((string)$transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getNlStringData(array $current, array $transaction): array
{
foreach ($this->textareaFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->clearStringKeepNewlines((string)$transaction[$fieldName]); // keep newlines
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getDateData(array $current, array $transaction): array
{
foreach ($this->dateFields as $fieldName) {
app('log')->debug(sprintf('Now at date field %s', $fieldName));
if (array_key_exists($fieldName, $transaction)) {
app('log')->debug(sprintf('New value: "%s"', (string)$transaction[$fieldName]));
$current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getBooleanData(array $current, array $transaction): array
{
foreach ($this->booleanFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->convertBoolean((string)$transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getArrayData(array $current, array $transaction): array
{
foreach ($this->arrayFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getFloatData(array $current, array $transaction): array
{
foreach ($this->floatFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$value = $transaction[$fieldName];
if (is_float($value)) {
$current[$fieldName] = sprintf('%.12f', $value);
}
if (!is_float($value)) {
$current[$fieldName] = (string)$value;
}
}
}
return $current;
}
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
*/ */
@ -210,156 +362,4 @@ class UpdateRequest extends FormRequest
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
} }
} }
/**
* Get transaction data.
*
* @throws FireflyException
*/
private function getTransactionData(): array
{
app('log')->debug(sprintf('Now in %s', __METHOD__));
$return = [];
/** @var null|array $transactions */
$transactions = $this->get('transactions');
if (!is_countable($transactions)) {
return $return;
}
/** @var null|array $transaction */
foreach ($transactions as $transaction) {
if (!is_array($transaction)) {
throw new FireflyException('Invalid data submitted: transaction is not array.');
}
// default response is to update nothing in the transaction:
$current = [];
$current = $this->getIntegerData($current, $transaction);
$current = $this->getStringData($current, $transaction);
$current = $this->getNlStringData($current, $transaction);
$current = $this->getDateData($current, $transaction);
$current = $this->getBooleanData($current, $transaction);
$current = $this->getArrayData($current, $transaction);
$current = $this->getFloatData($current, $transaction);
$return[] = $current;
}
return $return;
}
/**
* For each field, add it to the array if a reference is present in the request:
*
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getIntegerData(array $current, array $transaction): array
{
foreach ($this->integerFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->integerFromValue((string) $transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getStringData(array $current, array $transaction): array
{
foreach ($this->stringFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->clearString((string) $transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getNlStringData(array $current, array $transaction): array
{
foreach ($this->textareaFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->clearStringKeepNewlines((string) $transaction[$fieldName]); // keep newlines
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getDateData(array $current, array $transaction): array
{
foreach ($this->dateFields as $fieldName) {
app('log')->debug(sprintf('Now at date field %s', $fieldName));
if (array_key_exists($fieldName, $transaction)) {
app('log')->debug(sprintf('New value: "%s"', (string) $transaction[$fieldName]));
$current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getBooleanData(array $current, array $transaction): array
{
foreach ($this->booleanFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->convertBoolean((string) $transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getArrayData(array $current, array $transaction): array
{
foreach ($this->arrayFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getFloatData(array $current, array $transaction): array
{
foreach ($this->floatFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$value = $transaction[$fieldName];
if (is_float($value)) {
$current[$fieldName] = sprintf('%.12f', $value);
}
if (!is_float($value)) {
$current[$fieldName] = (string) $value;
}
}
}
return $current;
}
} }

View File

@ -72,7 +72,7 @@ class CategoryController extends Controller
$filtered = $result->map( $filtered = $result->map(
static function (Category $item) { static function (Category $item) {
return [ return [
'id' => (string) $item->id, 'id' => (string)$item->id,
'name' => $item->name, 'name' => $item->name,
]; ];
} }

View File

@ -72,9 +72,9 @@ class TagController extends Controller
$filtered = $result->map( $filtered = $result->map(
static function (Tag $item) { static function (Tag $item) {
return [ return [
'id' => (string) $item->id, 'id' => (string)$item->id,
'name' => $item->tag, 'name' => $item->tag,
'value' => (string) $item->id, 'value' => (string)$item->id,
'label' => $item->tag, 'label' => $item->tag,
]; ];
} }

View File

@ -124,11 +124,11 @@ class BudgetController extends Controller
foreach ($rows as $row) { foreach ($rows as $row) {
$current = [ $current = [
'label' => $budget->name, 'label' => $budget->name,
'currency_id' => (string) $row['currency_id'], 'currency_id' => (string)$row['currency_id'],
'currency_code' => $row['currency_code'], 'currency_code' => $row['currency_code'],
'currency_name' => $row['currency_name'], 'currency_name' => $row['currency_name'],
'currency_decimal_places' => $row['currency_decimal_places'], '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_code' => $row['native_currency_code'],
'native_currency_name' => $row['native_currency_name'], 'native_currency_name' => $row['native_currency_name'],
'native_currency_decimal_places' => $row['native_currency_decimal_places'], 'native_currency_decimal_places' => $row['native_currency_decimal_places'],
@ -189,12 +189,12 @@ class BudgetController extends Controller
foreach ($array as $currencyId => $block) { foreach ($array as $currencyId => $block) {
$this->currencies[$currencyId] ??= TransactionCurrency::find($currencyId); $this->currencies[$currencyId] ??= TransactionCurrency::find($currencyId);
$return[$currencyId] ??= [ $return[$currencyId] ??= [
'currency_id' => (string) $currencyId, 'currency_id' => (string)$currencyId,
'currency_code' => $block['currency_code'], 'currency_code' => $block['currency_code'],
'currency_name' => $block['currency_name'], 'currency_name' => $block['currency_name'],
'currency_symbol' => $block['currency_symbol'], 'currency_symbol' => $block['currency_symbol'],
'currency_decimal_places' => (int) $block['currency_decimal_places'], 'currency_decimal_places' => (int)$block['currency_decimal_places'],
'native_currency_id' => (string) $this->currency->id, 'native_currency_id' => (string)$this->currency->id,
'native_currency_code' => $this->currency->code, 'native_currency_code' => $this->currency->code,
'native_currency_name' => $this->currency->name, 'native_currency_name' => $this->currency->name,
'native_currency_symbol' => $this->currency->symbol, 'native_currency_symbol' => $this->currency->symbol,

View File

@ -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 * TODO duplicate from V1 controller
* Method to grab all parameters from the URL. * Method to grab all parameters from the URL.
@ -184,4 +147,41 @@ class Controller extends BaseController
return $bag; 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();
}
} }

View File

@ -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 * 2023-10-29 removed the cerSum reference, not sure where this is used atm
* so removed from api.php. Also applies to "spent" method. * so removed from api.php. Also applies to "spent" method.
@ -80,6 +66,20 @@ class ShowController extends Controller
return response()->json($result); 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: * This endpoint is documented at:
* TODO add URL * TODO add URL

View File

@ -114,23 +114,6 @@ class BasicController extends Controller
return response()->json($total); 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 * @throws FireflyException
*/ */
@ -411,4 +394,21 @@ class BasicController extends Controller
return $return; 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;
}
} }

View File

@ -35,43 +35,6 @@ use Illuminate\Http\JsonResponse;
*/ */
class TransactionController extends Controller 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 public function list(ListRequest $request): JsonResponse
{ {
// collect transactions: // collect transactions:
@ -115,4 +78,40 @@ class TransactionController extends Controller
->header('Content-Type', self::CONTENT_TYPE) ->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);
}
} }

View File

@ -57,13 +57,6 @@ class ListByCountRequest extends FormRequest
return http_build_query($array); return http_build_query($array);
} }
public function getPage(): int
{
$page = $this->convertInteger('page');
return 0 === $page || $page > 65536 ? 1 : $page;
}
public function getStartRow(): int public function getStartRow(): int
{ {
$startRow = $this->convertInteger('start_row'); $startRow = $this->convertInteger('start_row');
@ -88,6 +81,13 @@ class ListByCountRequest extends FormRequest
return $this->getCarbonDate('end'); return $this->getCarbonDate('end');
} }
public function getPage(): int
{
$page = $this->convertInteger('page');
return 0 === $page || $page > 65536 ? 1 : $page;
}
public function getTransactionTypes(): array public function getTransactionTypes(): array
{ {
$type = (string)$this->get('type', 'default'); $type = (string)$this->get('type', 'default');

View File

@ -78,6 +78,103 @@ class StoreRequest extends FormRequest
]; ];
} }
/**
* Get transaction data.
*/
private function getTransactionData(): array
{
$return = [];
/**
* @var array $transaction
*/
foreach ($this->get('transactions') as $transaction) {
$object = new NullArrayObject($transaction);
$result = [
'type' => $this->clearString($object['type']),
'date' => $this->dateFromValue($object['date']),
'order' => $this->integerFromValue((string)$object['order']),
'currency_id' => $this->integerFromValue((string)$object['currency_id']),
'currency_code' => $this->clearString((string)$object['currency_code']),
// foreign currency info:
'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']),
'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']),
// amount and foreign amount. Cannot be 0.
'amount' => $this->clearString((string)$object['amount']),
'foreign_amount' => $this->clearString((string)$object['foreign_amount']),
// description.
'description' => $this->clearString($object['description']),
// source of transaction. If everything is null, assume cash account.
'source_id' => $this->integerFromValue((string)$object['source_id']),
'source_name' => $this->clearString((string)$object['source_name']),
'source_iban' => $this->clearString((string)$object['source_iban']),
'source_number' => $this->clearString((string)$object['source_number']),
'source_bic' => $this->clearString((string)$object['source_bic']),
// destination of transaction. If everything is null, assume cash account.
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
'destination_name' => $this->clearString((string)$object['destination_name']),
'destination_iban' => $this->clearString((string)$object['destination_iban']),
'destination_number' => $this->clearString((string)$object['destination_number']),
'destination_bic' => $this->clearString((string)$object['destination_bic']),
// budget info
'budget_id' => $this->integerFromValue((string)$object['budget_id']),
'budget_name' => $this->clearString((string)$object['budget_name']),
// category info
'category_id' => $this->integerFromValue((string)$object['category_id']),
'category_name' => $this->clearString((string)$object['category_name']),
// journal bill reference. Optional. Will only work for withdrawals
'bill_id' => $this->integerFromValue((string)$object['bill_id']),
'bill_name' => $this->clearString((string)$object['bill_name']),
// piggy bank reference. Optional. Will only work for transfers
'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']),
'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']),
// some other interesting properties
'reconciled' => $this->convertBoolean((string)$object['reconciled']),
'notes' => $this->clearStringKeepNewlines((string)$object['notes']),
'tags' => $this->arrayFromValue($object['tags']),
// all custom fields:
'internal_reference' => $this->clearString((string)$object['internal_reference']),
'external_id' => $this->clearString((string)$object['external_id']),
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']),
'external_url' => $this->clearString((string)$object['external_url']),
'sepa_cc' => $this->clearString((string)$object['sepa_cc']),
'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']),
'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']),
'sepa_db' => $this->clearString((string)$object['sepa_db']),
'sepa_country' => $this->clearString((string)$object['sepa_country']),
'sepa_ep' => $this->clearString((string)$object['sepa_ep']),
'sepa_ci' => $this->clearString((string)$object['sepa_ci']),
'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']),
// custom date fields. Must be Carbon objects. Presence is optional.
'interest_date' => $this->dateFromValue($object['interest_date']),
'book_date' => $this->dateFromValue($object['book_date']),
'process_date' => $this->dateFromValue($object['process_date']),
'due_date' => $this->dateFromValue($object['due_date']),
'payment_date' => $this->dateFromValue($object['payment_date']),
'invoice_date' => $this->dateFromValue($object['invoice_date']),
];
$result = $this->addFromromTransactionStore($transaction, $result);
$return[] = $result;
}
return $return;
}
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
*/ */
@ -216,101 +313,4 @@ class StoreRequest extends FormRequest
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
} }
} }
/**
* Get transaction data.
*/
private function getTransactionData(): array
{
$return = [];
/**
* @var array $transaction
*/
foreach ($this->get('transactions') as $transaction) {
$object = new NullArrayObject($transaction);
$result = [
'type' => $this->clearString($object['type']),
'date' => $this->dateFromValue($object['date']),
'order' => $this->integerFromValue((string)$object['order']),
'currency_id' => $this->integerFromValue((string)$object['currency_id']),
'currency_code' => $this->clearString((string)$object['currency_code']),
// foreign currency info:
'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']),
'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']),
// amount and foreign amount. Cannot be 0.
'amount' => $this->clearString((string)$object['amount']),
'foreign_amount' => $this->clearString((string)$object['foreign_amount']),
// description.
'description' => $this->clearString($object['description']),
// source of transaction. If everything is null, assume cash account.
'source_id' => $this->integerFromValue((string)$object['source_id']),
'source_name' => $this->clearString((string)$object['source_name']),
'source_iban' => $this->clearString((string)$object['source_iban']),
'source_number' => $this->clearString((string)$object['source_number']),
'source_bic' => $this->clearString((string)$object['source_bic']),
// destination of transaction. If everything is null, assume cash account.
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
'destination_name' => $this->clearString((string)$object['destination_name']),
'destination_iban' => $this->clearString((string)$object['destination_iban']),
'destination_number' => $this->clearString((string)$object['destination_number']),
'destination_bic' => $this->clearString((string)$object['destination_bic']),
// budget info
'budget_id' => $this->integerFromValue((string)$object['budget_id']),
'budget_name' => $this->clearString((string)$object['budget_name']),
// category info
'category_id' => $this->integerFromValue((string)$object['category_id']),
'category_name' => $this->clearString((string)$object['category_name']),
// journal bill reference. Optional. Will only work for withdrawals
'bill_id' => $this->integerFromValue((string)$object['bill_id']),
'bill_name' => $this->clearString((string)$object['bill_name']),
// piggy bank reference. Optional. Will only work for transfers
'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']),
'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']),
// some other interesting properties
'reconciled' => $this->convertBoolean((string)$object['reconciled']),
'notes' => $this->clearStringKeepNewlines((string)$object['notes']),
'tags' => $this->arrayFromValue($object['tags']),
// all custom fields:
'internal_reference' => $this->clearString((string)$object['internal_reference']),
'external_id' => $this->clearString((string)$object['external_id']),
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']),
'external_url' => $this->clearString((string)$object['external_url']),
'sepa_cc' => $this->clearString((string)$object['sepa_cc']),
'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']),
'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']),
'sepa_db' => $this->clearString((string)$object['sepa_db']),
'sepa_country' => $this->clearString((string)$object['sepa_country']),
'sepa_ep' => $this->clearString((string)$object['sepa_ep']),
'sepa_ci' => $this->clearString((string)$object['sepa_ci']),
'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']),
// custom date fields. Must be Carbon objects. Presence is optional.
'interest_date' => $this->dateFromValue($object['interest_date']),
'book_date' => $this->dateFromValue($object['book_date']),
'process_date' => $this->dateFromValue($object['process_date']),
'due_date' => $this->dateFromValue($object['due_date']),
'payment_date' => $this->dateFromValue($object['payment_date']),
'invoice_date' => $this->dateFromValue($object['invoice_date']),
];
$result = $this->addFromromTransactionStore($transaction, $result);
$return[] = $result;
}
return $return;
}
} }

View File

@ -91,6 +91,158 @@ class UpdateRequest extends Request
return $data; return $data;
} }
/**
* Get transaction data.
*
* @throws FireflyException
*/
private function getTransactionData(): array
{
app('log')->debug(sprintf('Now in %s', __METHOD__));
$return = [];
/** @var null|array $transactions */
$transactions = $this->get('transactions');
if (!is_countable($transactions)) {
return $return;
}
/** @var null|array $transaction */
foreach ($transactions as $transaction) {
if (!is_array($transaction)) {
throw new FireflyException('Invalid data submitted: transaction is not array.');
}
// default response is to update nothing in the transaction:
$current = [];
$current = $this->getIntegerData($current, $transaction);
$current = $this->getStringData($current, $transaction);
$current = $this->getNlStringData($current, $transaction);
$current = $this->getDateData($current, $transaction);
$current = $this->getBooleanData($current, $transaction);
$current = $this->getArrayData($current, $transaction);
$current = $this->getFloatData($current, $transaction);
$return[] = $current;
}
return $return;
}
/**
* For each field, add it to the array if a reference is present in the request:
*
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getIntegerData(array $current, array $transaction): array
{
foreach ($this->integerFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->integerFromValue((string)$transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getStringData(array $current, array $transaction): array
{
foreach ($this->stringFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->clearString((string)$transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getNlStringData(array $current, array $transaction): array
{
foreach ($this->textareaFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->clearStringKeepNewlines((string)$transaction[$fieldName]); // keep newlines
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getDateData(array $current, array $transaction): array
{
foreach ($this->dateFields as $fieldName) {
app('log')->debug(sprintf('Now at date field %s', $fieldName));
if (array_key_exists($fieldName, $transaction)) {
app('log')->debug(sprintf('New value: "%s"', (string)$transaction[$fieldName]));
$current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getBooleanData(array $current, array $transaction): array
{
foreach ($this->booleanFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->convertBoolean((string)$transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getArrayData(array $current, array $transaction): array
{
foreach ($this->arrayFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getFloatData(array $current, array $transaction): array
{
foreach ($this->floatFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$value = $transaction[$fieldName];
if (is_float($value)) {
$current[$fieldName] = sprintf('%.12f', $value);
}
if (!is_float($value)) {
$current[$fieldName] = (string)$value;
}
}
}
return $current;
}
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
*/ */
@ -211,156 +363,4 @@ class UpdateRequest extends Request
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
} }
} }
/**
* Get transaction data.
*
* @throws FireflyException
*/
private function getTransactionData(): array
{
app('log')->debug(sprintf('Now in %s', __METHOD__));
$return = [];
/** @var null|array $transactions */
$transactions = $this->get('transactions');
if (!is_countable($transactions)) {
return $return;
}
/** @var null|array $transaction */
foreach ($transactions as $transaction) {
if (!is_array($transaction)) {
throw new FireflyException('Invalid data submitted: transaction is not array.');
}
// default response is to update nothing in the transaction:
$current = [];
$current = $this->getIntegerData($current, $transaction);
$current = $this->getStringData($current, $transaction);
$current = $this->getNlStringData($current, $transaction);
$current = $this->getDateData($current, $transaction);
$current = $this->getBooleanData($current, $transaction);
$current = $this->getArrayData($current, $transaction);
$current = $this->getFloatData($current, $transaction);
$return[] = $current;
}
return $return;
}
/**
* For each field, add it to the array if a reference is present in the request:
*
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getIntegerData(array $current, array $transaction): array
{
foreach ($this->integerFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->integerFromValue((string) $transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getStringData(array $current, array $transaction): array
{
foreach ($this->stringFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->clearString((string) $transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getNlStringData(array $current, array $transaction): array
{
foreach ($this->textareaFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->clearStringKeepNewlines((string) $transaction[$fieldName]); // keep newlines
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getDateData(array $current, array $transaction): array
{
foreach ($this->dateFields as $fieldName) {
app('log')->debug(sprintf('Now at date field %s', $fieldName));
if (array_key_exists($fieldName, $transaction)) {
app('log')->debug(sprintf('New value: "%s"', (string) $transaction[$fieldName]));
$current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getBooleanData(array $current, array $transaction): array
{
foreach ($this->booleanFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->convertBoolean((string) $transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getArrayData(array $current, array $transaction): array
{
foreach ($this->arrayFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]);
}
}
return $current;
}
/**
* @param array<string, string> $current
* @param array<string, mixed> $transaction
*/
private function getFloatData(array $current, array $transaction): array
{
foreach ($this->floatFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$value = $transaction[$fieldName];
if (is_float($value)) {
$current[$fieldName] = sprintf('%.12f', $value);
}
if (!is_float($value)) {
$current[$fieldName] = (string) $value;
}
}
}
return $current;
}
} }

View File

@ -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 private function shouldBeTransfer(string $transactionType, string $sourceType, string $destinationType): bool
{ {
return TransactionType::TRANSFER === $transactionType && AccountType::ASSET === $sourceType && $this->isLiability($destinationType); 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; return AccountType::LOAN === $destinationType || AccountType::DEBT === $destinationType || AccountType::MORTGAGE === $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;
} }
private function makeTransfer(TransactionJournal $journal): void private function makeTransfer(TransactionJournal $journal): void
@ -286,6 +271,11 @@ class FixAccountTypes extends Command
$this->inspectJournal($journal); $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 private function makeDeposit(TransactionJournal $journal): void
{ {
// from a liability to an asset should be a deposit. // from a liability to an asset should be a deposit.
@ -299,6 +289,11 @@ class FixAccountTypes extends Command
$this->inspectJournal($journal); $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 private function makeExpenseDestination(TransactionJournal $journal, Transaction $destination): void
{ {
// withdrawals with a revenue account as destination instead of an expense account. // withdrawals with a revenue account as destination instead of an expense account.
@ -320,6 +315,11 @@ class FixAccountTypes extends Command
$this->inspectJournal($journal); $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 private function makeRevenueSource(TransactionJournal $journal, Transaction $source): void
{ {
// deposits with an expense account as source instead of a revenue account. // 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); 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 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)); 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); $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 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)); app('log')->debug(sprintf('An account of type "%s" could be a valid destination.', AccountTypeEnum::EXPENSE->value));

View File

@ -57,6 +57,19 @@ class CreateGroupMemberships extends Command
return 0; 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. * TODO move to helper.
* *
@ -93,17 +106,4 @@ class CreateGroupMemberships extends Command
$user->save(); $user->save();
} }
} }
/**
* @throws FireflyException
*/
private function createGroupMemberships(): void
{
$users = User::get();
/** @var User $user */
foreach ($users as $user) {
self::createGroupMembership($user);
}
}
} }

View File

@ -71,7 +71,7 @@ class MigrateRecurrenceMeta extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; return false;

View File

@ -104,7 +104,7 @@ class MigrateToGroups extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; 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) * @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 private function getTransactionBudget(Transaction $left, Transaction $right): ?int
{ {
app('log')->debug('Now in getTransactionBudget()'); app('log')->debug('Now in getTransactionBudget()');

View File

@ -128,7 +128,7 @@ class Handler extends ExceptionHandler
$errorCode = 500; $errorCode = 500;
$errorCode = $e instanceof MethodNotAllowedHttpException ? 405 : $errorCode; $errorCode = $e instanceof MethodNotAllowedHttpException ? 405 : $errorCode;
$isDebug = (bool) config('app.debug', false); $isDebug = (bool)config('app.debug', false);
if ($isDebug) { if ($isDebug) {
app('log')->debug(sprintf('Return JSON %s with debug.', get_class($e))); 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 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) { if ($this->shouldntReportLocal($e) || !$doMailError) {
parent::report($e); parent::report($e);
@ -221,12 +221,22 @@ class Handler extends ExceptionHandler
// create job that will mail. // create job that will mail.
$ipAddress = request()->ip() ?? '0.0.0.0'; $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); dispatch($job);
parent::report($e); 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. * 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. * Only return the redirectTo property from the exception if it is a valid URL. Return NULL otherwise.
*/ */

View File

@ -121,21 +121,6 @@ class AccountFactory
return $return; 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 * @throws FireflyException
*/ */
@ -169,6 +154,15 @@ class AccountFactory
return $result; 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 * @throws FireflyException
*/ */
@ -365,4 +359,10 @@ class AccountFactory
$updateService->setUser($account->user); $updateService->setUser($account->user);
$updateService->update($account, ['order' => $order]); $updateService->update($account, ['order' => $order]);
} }
public function setUser(User $user): void
{
$this->user = $user;
$this->accountRepository->setUser($user);
}
} }

View File

@ -79,7 +79,7 @@ class RecurrenceFactory
$firstDate = $data['recurrence']['first_date']; $firstDate = $data['recurrence']['first_date'];
} }
if (array_key_exists('nr_of_repetitions', $data['recurrence'])) { 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'])) { if (array_key_exists('repeat_until', $data['recurrence'])) {
$repeatUntil = $data['recurrence']['repeat_until']; $repeatUntil = $data['recurrence']['repeat_until'];
@ -116,7 +116,7 @@ class RecurrenceFactory
$recurrence->save(); $recurrence->save();
if (array_key_exists('notes', $data['recurrence'])) { 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'] ?? []); $this->createRepetitions($recurrence, $data['repetitions'] ?? []);

View File

@ -72,64 +72,6 @@ class TransactionFactory
return $this->create(app('steam')->negative($amount), $foreignAmount); 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 * @throws FireflyException
*/ */
@ -224,4 +166,62 @@ class TransactionFactory
$service = app(AccountUpdateService::class); $service = app(AccountUpdateService::class);
$service->update($this->account, ['iban' => $this->accountInformation['iban']]); $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.
}
} }

View File

@ -141,49 +141,6 @@ class TransactionJournalFactory
return $collection; 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 * TODO typeOverrule: the account validator may have another opinion on the transaction type. not sure what to do
* with this. * with this.
@ -203,11 +160,11 @@ class TransactionJournalFactory
$type = $this->typeRepository->findTransactionType(null, $row['type']); $type = $this->typeRepository->findTransactionType(null, $row['type']);
$carbon = $row['date'] ?? today(config('app.timezone')); $carbon = $row['date'] ?? today(config('app.timezone'));
$order = $row['order'] ?? 0; $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']); $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; $billId = TransactionType::WITHDRAWAL === $type->type && null !== $bill ? $bill->id : null;
$description = (string) $row['description']; $description = (string)$row['description'];
// Manipulate basic fields // Manipulate basic fields
$carbon->setTimezone(config('app.timezone')); $carbon->setTimezone(config('app.timezone'));
@ -286,7 +243,7 @@ class TransactionJournalFactory
$transactionFactory->setReconciled($row['reconciled'] ?? false); $transactionFactory->setReconciled($row['reconciled'] ?? false);
try { try {
$negative = $transactionFactory->createNegative((string) $row['amount'], (string) $row['foreign_amount']); $negative = $transactionFactory->createNegative((string)$row['amount'], (string)$row['foreign_amount']);
} catch (FireflyException $e) { } catch (FireflyException $e) {
app('log')->error(sprintf('Exception creating negative transaction: %s', $e->getMessage())); app('log')->error(sprintf('Exception creating negative transaction: %s', $e->getMessage()));
$this->forceDeleteOnError(new Collection([$journal])); $this->forceDeleteOnError(new Collection([$journal]));
@ -305,7 +262,7 @@ class TransactionJournalFactory
$transactionFactory->setReconciled($row['reconciled'] ?? false); $transactionFactory->setReconciled($row['reconciled'] ?? false);
try { try {
$transactionFactory->createPositive((string) $row['amount'], (string) $row['foreign_amount']); $transactionFactory->createPositive((string)$row['amount'], (string)$row['foreign_amount']);
} catch (FireflyException $e) { } catch (FireflyException $e) {
app('log')->error(sprintf('Exception creating positive transaction: %s', $e->getMessage())); app('log')->error(sprintf('Exception creating positive transaction: %s', $e->getMessage()));
$this->forceTrDelete($negative); $this->forceTrDelete($negative);
@ -326,18 +283,6 @@ class TransactionJournalFactory
return $journal; 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 private function hashArray(NullArrayObject $row): string
{ {
$dataRow = $row->getArrayCopy(); $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)); app('log')->warning(sprintf('Found a duplicate in errorIfDuplicate because hash %s is not unique!', $hash));
$journal = $result->transactionJournal()->withTrashed()->first(); $journal = $result->transactionJournal()->withTrashed()->first();
$group = $journal?->transactionGroup()->withTrashed()->first(); $group = $journal?->transactionGroup()->withTrashed()->first();
$groupId = (int) $group?->id; $groupId = (int)$group?->id;
throw new DuplicateTransactionException(sprintf('Duplicate of transaction #%d.', $groupId)); throw new DuplicateTransactionException(sprintf('Duplicate of transaction #%d.', $groupId));
} }
@ -400,10 +345,10 @@ class TransactionJournalFactory
// validate source account. // validate source account.
$array = [ $array = [
'id' => null !== $data['source_id'] ? (int) $data['source_id'] : null, 'id' => null !== $data['source_id'] ? (int)$data['source_id'] : null,
'name' => null !== $data['source_name'] ? (string) $data['source_name'] : null, 'name' => null !== $data['source_name'] ? (string)$data['source_name'] : null,
'iban' => null !== $data['source_iban'] ? (string) $data['source_iban'] : null, 'iban' => null !== $data['source_iban'] ? (string)$data['source_iban'] : null,
'number' => null !== $data['source_number'] ? (string) $data['source_number'] : null, 'number' => null !== $data['source_number'] ? (string)$data['source_number'] : null,
]; ];
$validSource = $this->accountValidator->validateSource($array); $validSource = $this->accountValidator->validateSource($array);
@ -415,10 +360,10 @@ class TransactionJournalFactory
// validate destination account // validate destination account
$array = [ $array = [
'id' => null !== $data['destination_id'] ? (int) $data['destination_id'] : null, 'id' => null !== $data['destination_id'] ? (int)$data['destination_id'] : null,
'name' => null !== $data['destination_name'] ? (string) $data['destination_name'] : null, 'name' => null !== $data['destination_name'] ? (string)$data['destination_name'] : null,
'iban' => null !== $data['destination_iban'] ? (string) $data['destination_iban'] : null, 'iban' => null !== $data['destination_iban'] ? (string)$data['destination_iban'] : null,
'number' => null !== $data['destination_number'] ? (string) $data['destination_number'] : null, 'number' => null !== $data['destination_number'] ? (string)$data['destination_number'] : null,
]; ];
$validDestination = $this->accountValidator->validateDestination($array); $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 private function reconciliationSanityCheck(?Account $sourceAccount, ?Account $destinationAccount): array
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); app('log')->debug(sprintf('Now in %s', __METHOD__));
@ -550,7 +510,7 @@ class TransactionJournalFactory
{ {
app('log')->debug('Will now store piggy event.'); 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) { if (null !== $piggyBank) {
$this->piggyEventFactory->create($journal, $piggyBank); $this->piggyEventFactory->create($journal, $piggyBank);
@ -567,4 +527,44 @@ class TransactionJournalFactory
$this->storeMeta($journal, $transaction, $field); $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.');
}
}
} }

View File

@ -67,6 +67,14 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $result; return $result;
} }
/**
* Return the preferred period.
*/
protected function preferredPeriod(): string
{
return 'day';
}
/** /**
* Set accounts. * Set accounts.
*/ */
@ -130,12 +138,4 @@ class MonthReportGenerator implements ReportGeneratorInterface
{ {
return $this; return $this;
} }
/**
* Return the preferred period.
*/
protected function preferredPeriod(): string
{
return 'day';
}
} }

View File

@ -125,26 +125,6 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this; 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. * Get the expenses.
*/ */
@ -170,4 +150,24 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $journals; 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;
}
} }

View File

@ -124,26 +124,6 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this; 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. * Get the expenses for this report.
*/ */
@ -168,6 +148,26 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $transactions; 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. * Get the income for this report.
*/ */

View File

@ -49,20 +49,6 @@ class BudgetLimitHandler
$this->updateAvailableBudget($event->budgetLimit); $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 private function updateAvailableBudget(BudgetLimit $budgetLimit): void
{ {
app('log')->debug(sprintf('Now in updateAvailableBudget(#%d)', $budgetLimit->id)); app('log')->debug(sprintf('Now in updateAvailableBudget(#%d)', $budgetLimit->id));
@ -248,4 +234,18 @@ class BudgetLimitHandler
return $amount; 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);
}
} }

View File

@ -220,7 +220,7 @@ class UserEventHandler
public function sendAdminRegistrationNotification(RegisteredUser $event): void 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) { if ($sendMail) {
/** @var UserRepositoryInterface $repository */ /** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class); $repository = app(UserRepositoryInterface::class);
@ -285,7 +285,7 @@ class UserEventHandler
$oldEmail = $event->oldEmail; $oldEmail = $event->oldEmail;
$user = $event->user; $user = $event->user;
$token = app('preferences')->getForUser($user, 'email_change_undo_token', 'invalid'); $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]); $url = route('profile.undo-email-change', [$token->data, $hashed]);
try { try {
@ -347,7 +347,7 @@ class UserEventHandler
*/ */
public function sendRegistrationMail(RegisteredUser $event): void 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) { if ($sendMail) {
try { try {
Notification::send($event->user, new UserRegistrationNotification()); Notification::send($event->user, new UserRegistrationNotification());

View File

@ -81,6 +81,27 @@ trait AttachmentCollection
return $this; 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 public function withAttachmentInformation(): GroupCollectorInterface
{ {
$this->fields[] = 'attachments.id as attachment_id'; $this->fields[] = 'attachments.id as attachment_id';
@ -511,25 +532,4 @@ trait AttachmentCollection
return $this; 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');
}
)
;
}
}
} }

View File

@ -34,6 +34,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
trait CollectorProperties trait CollectorProperties
{ {
public const string TEST = 'Test'; public const string TEST = 'Test';
private ?int $endRow;
private bool $expandGroupSearch; private bool $expandGroupSearch;
private array $fields; private array $fields;
private bool $hasAccountInfo; private bool $hasAccountInfo;
@ -49,10 +50,8 @@ trait CollectorProperties
private ?int $page; private ?int $page;
private array $postFilters; private array $postFilters;
private HasMany $query; private HasMany $query;
private array $stringFields;
private ?int $startRow; 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 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. * This way the user can call 'setTags' multiple times and get a joined result.

View File

@ -171,6 +171,19 @@ trait MetaCollection
return $this; 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 public function excludeExternalUrl(string $url): GroupCollectorInterface
{ {
$this->joinMetaDataTables(); $this->joinMetaDataTables();
@ -369,6 +382,19 @@ trait MetaCollection
return $this; 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 public function internalReferenceContains(string $internalReference): GroupCollectorInterface
{ {
$internalReference = (string)json_encode($internalReference); $internalReference = (string)json_encode($internalReference);
@ -539,6 +565,63 @@ trait MetaCollection
return $this; 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. * Limit the search to a specific bill.
*/ */
@ -669,63 +752,6 @@ trait MetaCollection
return $this; 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. * Limit results to any of the tags in the list.
*/ */
@ -938,30 +964,4 @@ trait MetaCollection
return $this; 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');
}
}
} }

View File

@ -25,6 +25,7 @@ namespace FireflyIII\Helpers\Collector;
use Carbon\Carbon; use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException; use Carbon\Exceptions\InvalidFormatException;
use Exception;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\Extensions\AccountCollection; use FireflyIII\Helpers\Collector\Extensions\AccountCollection;
use FireflyIII\Helpers\Collector\Extensions\AmountCollection; use FireflyIII\Helpers\Collector\Extensions\AmountCollection;
@ -483,226 +484,6 @@ class GroupCollector implements GroupCollectorInterface
return $collection; 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 private function getCollectedGroupIds(): array
{ {
return $this->query->get(['transaction_journals.transaction_group_id'])->pluck('transaction_group_id')->toArray(); return $this->query->get(['transaction_journals.transaction_group_id'])->pluck('transaction_group_id')->toArray();
@ -998,6 +779,195 @@ class GroupCollector implements GroupCollectorInterface
return $currentCollection; 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. * 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. * Build the query.
*/ */
@ -1087,4 +1070,21 @@ class GroupCollector implements GroupCollectorInterface
->orderBy('source.amount', 'DESC') ->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;
}
} }

View File

@ -401,6 +401,11 @@ interface GroupCollectorInterface
*/ */
public function setAfter(Carbon $date): self; 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. * Collect transactions before a specific date.
*/ */
@ -461,6 +466,11 @@ interface GroupCollectorInterface
*/ */
public function setEnd(Carbon $end): self; public function setEnd(Carbon $end): self;
/**
* Set the page to get.
*/
public function setEndRow(int $endRow): self;
public function setExpandGroupSearch(bool $expandGroupSearch): self; public function setExpandGroupSearch(bool $expandGroupSearch): self;
/** /**
@ -526,16 +536,6 @@ interface GroupCollectorInterface
*/ */
public function setPage(int $page): self; 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. * Set the start and end time of the results to return.
*/ */
@ -563,6 +563,11 @@ interface GroupCollectorInterface
*/ */
public function setStart(Carbon $start): self; public function setStart(Carbon $start): self;
/**
* Set the page to get.
*/
public function setStartRow(int $startRow): self;
/** /**
* Limit results to a specific tag. * Limit results to a specific tag.
*/ */
@ -573,11 +578,6 @@ interface GroupCollectorInterface
*/ */
public function setTags(Collection $tags): self; 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. * Limit the search to one specific transaction group.
*/ */

View File

@ -121,12 +121,12 @@ class NetWorth implements NetWorthInterface
$netWorth[$currencyCode] ??= [ $netWorth[$currencyCode] ??= [
'balance' => '0', 'balance' => '0',
'native_balance' => '0', 'native_balance' => '0',
'currency_id' => (string) $currency->id, 'currency_id' => (string)$currency->id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_name' => $currency->name, 'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, '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_code' => $default->code,
'native_currency_name' => $default->name, 'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol, 'native_currency_symbol' => $default->symbol,
@ -144,6 +144,15 @@ class NetWorth implements NetWorthInterface
return $netWorth; 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 public function setUser(null|Authenticatable|User $user): void
{ {
if (!$user instanceof User) { if (!$user instanceof User) {
@ -189,7 +198,7 @@ class NetWorth implements NetWorthInterface
} }
$return[$currency->id] ??= [ $return[$currency->id] ??= [
'id' => (string) $currency->id, 'id' => (string)$currency->id,
'name' => $currency->name, 'name' => $currency->name,
'symbol' => $currency->symbol, 'symbol' => $currency->symbol,
'code' => $currency->code, 'code' => $currency->code,
@ -202,15 +211,6 @@ class NetWorth implements NetWorthInterface
return $return; return $return;
} }
private function getRepository(): AccountRepositoryInterface|AdminAccountRepositoryInterface
{
if (null === $this->userGroup) {
return $this->accountRepository;
}
return $this->adminAccountRepository;
}
private function getAccounts(): Collection private function getAccounts(): Collection
{ {
$accounts = $this->getRepository()->getAccountsByType( $accounts = $this->getRepository()->getAccountsByType(
@ -220,7 +220,7 @@ class NetWorth implements NetWorthInterface
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $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); $filtered->push($account);
} }
} }

View File

@ -58,7 +58,7 @@ class CreateController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-credit-card'); 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->repository = app(AccountRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class); $this->attachments = app(AttachmentHelperInterface::class);
@ -77,7 +77,7 @@ class CreateController extends Controller
{ {
$defaultCurrency = app('amount')->getDefaultCurrency(); $defaultCurrency = app('amount')->getDefaultCurrency();
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType)); $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(); $roles = $this->getRoles();
$liabilityTypes = $this->getLiabilityTypes(); $liabilityTypes = $this->getLiabilityTypes();
$hasOldInput = null !== $request->old('_token'); $hasOldInput = null !== $request->old('_token');
@ -96,9 +96,9 @@ class CreateController extends Controller
// interest calculation periods: // interest calculation periods:
$interestPeriods = [ $interestPeriods = [
'daily' => (string) trans('firefly.interest_calc_daily'), 'daily' => (string)trans('firefly.interest_calc_daily'),
'monthly' => (string) trans('firefly.interest_calc_monthly'), 'monthly' => (string)trans('firefly.interest_calc_monthly'),
'yearly' => (string) trans('firefly.interest_calc_yearly'), 'yearly' => (string)trans('firefly.interest_calc_yearly'),
]; ];
// pre fill some data // pre fill some data
@ -106,7 +106,7 @@ class CreateController extends Controller
'preFilled', 'preFilled',
[ [
'currency_id' => $defaultCurrency->id, '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 // issue #8321
@ -139,7 +139,7 @@ class CreateController extends Controller
{ {
$data = $request->getAccountData(); $data = $request->getAccountData();
$account = $this->repository->store($data); $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(); app('preferences')->mark();
Log::channel('audit')->info('Stored new account.', $data); Log::channel('audit')->info('Stored new account.', $data);
@ -162,7 +162,7 @@ class CreateController extends Controller
} }
if (null !== $files && auth()->user()->hasRole('demo')) { if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string) trans('firefly.no_att_demo_user')); session()->flash('info', (string)trans('firefly.no_att_demo_user'));
} }
if (count($this->attachments->getMessages()->get('attachments')) > 0) { if (count($this->attachments->getMessages()->get('attachments')) > 0) {
@ -171,7 +171,7 @@ class CreateController extends Controller
// redirect to previous URL. // redirect to previous URL.
$redirect = redirect($this->getPreviousUrl('accounts.create.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: // set value so create routine will not overwrite URL:
$request->session()->put('accounts.create.fromStore', true); $request->session()->put('accounts.create.fromStore', true);

View File

@ -57,7 +57,7 @@ class EditController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-credit-card'); 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->repository = app(AccountRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class); $this->attachments = app(AttachmentHelperInterface::class);
@ -81,7 +81,7 @@ class EditController extends Controller
} }
$objectType = config('firefly.shortNamesByFullName')[$account->accountType->type]; $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)); $subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
$roles = $this->getRoles(); $roles = $this->getRoles();
$liabilityTypes = $this->getLiabilityTypes(); $liabilityTypes = $this->getLiabilityTypes();
@ -106,9 +106,9 @@ class EditController extends Controller
// interest calculation periods: // interest calculation periods:
$interestPeriods = [ $interestPeriods = [
'daily' => (string) trans('firefly.interest_calc_daily'), 'daily' => (string)trans('firefly.interest_calc_daily'),
'monthly' => (string) trans('firefly.interest_calc_monthly'), 'monthly' => (string)trans('firefly.interest_calc_monthly'),
'yearly' => (string) trans('firefly.interest_calc_yearly'), 'yearly' => (string)trans('firefly.interest_calc_yearly'),
]; ];
// put previous url in session if not redirect from store (not "return_to_edit"). // 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'); $request->session()->forget('accounts.edit.fromUpdate');
$openingBalanceAmount = (string) $repository->getOpeningBalanceAmount($account); $openingBalanceAmount = (string)$repository->getOpeningBalanceAmount($account);
if ('0' === $openingBalanceAmount) { if ('0' === $openingBalanceAmount) {
$openingBalanceAmount = ''; $openingBalanceAmount = '';
} }
@ -143,17 +143,17 @@ class EditController extends Controller
'cc_type' => $repository->getMetaValue($account, 'cc_type'), 'cc_type' => $repository->getMetaValue($account, 'cc_type'),
'cc_monthly_payment_date' => $repository->getMetaValue($account, 'cc_monthly_payment_date'), 'cc_monthly_payment_date' => $repository->getMetaValue($account, 'cc_monthly_payment_date'),
'BIC' => $repository->getMetaValue($account, 'BIC'), '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, 'liability_type_id' => $account->account_type_id,
'opening_balance' => app('steam')->bcround($openingBalanceAmount, $currency->decimal_places), 'opening_balance' => app('steam')->bcround($openingBalanceAmount, $currency->decimal_places),
'liability_direction' => $this->repository->getMetaValue($account, 'liability_direction'), 'liability_direction' => $this->repository->getMetaValue($account, 'liability_direction'),
'virtual_balance' => app('steam')->bcround($virtualBalance, $currency->decimal_places), 'virtual_balance' => app('steam')->bcround($virtualBalance, $currency->decimal_places),
'currency_id' => $currency->id, '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' => $repository->getMetaValue($account, 'interest'),
'interest_period' => $repository->getMetaValue($account, 'interest_period'), 'interest_period' => $repository->getMetaValue($account, 'interest_period'),
'notes' => $this->repository->getNoteText($account), 'notes' => $this->repository->getNoteText($account),
'active' => $hasOldInput ? (bool) $request->old('active') : $account->active, 'active' => $hasOldInput ? (bool)$request->old('active') : $account->active,
]; ];
if ('' === $openingBalanceAmount) { if ('' === $openingBalanceAmount) {
$preFilled['opening_balance'] = ''; $preFilled['opening_balance'] = '';
@ -178,7 +178,7 @@ class EditController extends Controller
$data = $request->getAccountData(); $data = $request->getAccountData();
$this->repository->update($account, $data); $this->repository->update($account, $data);
Log::channel('audit')->info(sprintf('Updated account #%d.', $account->id), $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): // store new attachment(s):
/** @var null|array $files */ /** @var null|array $files */
@ -188,7 +188,7 @@ class EditController extends Controller
} }
if (null !== $files && auth()->user()->hasRole('demo')) { if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string) trans('firefly.no_att_demo_user')); session()->flash('info', (string)trans('firefly.no_att_demo_user'));
} }
if (count($this->attachments->getMessages()->get('attachments')) > 0) { if (count($this->attachments->getMessages()->get('attachments')) > 0) {
@ -197,7 +197,7 @@ class EditController extends Controller
// redirect // redirect
$redirect = redirect($this->getPreviousUrl('accounts.edit.url')); $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: // set value so edit routine will not overwrite URL:
$request->session()->put('accounts.edit.fromUpdate', true); $request->session()->put('accounts.edit.fromUpdate', true);

View File

@ -95,6 +95,21 @@ class ForgotPasswordController extends Controller
return back()->with('status', trans($response)); 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. * Show form for email recovery.
* *
@ -121,19 +136,4 @@ class ForgotPasswordController extends Controller
return view('auth.passwords.email')->with(compact('allowRegistration', 'pageTitle')); 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');
}
}
} }

View File

@ -132,6 +132,25 @@ class LoginController extends Controller
return $this->username; 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. * 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')); 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;
}
} }

View File

@ -109,6 +109,31 @@ class RegisterController extends Controller
return redirect($this->redirectPath()); 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. * 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')); 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;
}
} }

View File

@ -31,7 +31,6 @@ use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Routing\Redirector; use Illuminate\Routing\Redirector;
use PragmaRX\Google2FALaravel\Support\Authenticator; use PragmaRX\Google2FALaravel\Support\Authenticator;
use Preferences;
/** /**
* Class TwoFactorController. * Class TwoFactorController.

View File

@ -52,7 +52,7 @@ class EditController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { 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'); app('view')->share('mainTitleIcon', 'fa-calendar-o');
$this->attachments = app(AttachmentHelperInterface::class); $this->attachments = app(AttachmentHelperInterface::class);
$this->repository = app(BillRepositoryInterface::class); $this->repository = app(BillRepositoryInterface::class);
@ -75,10 +75,10 @@ class EditController extends Controller
$billPeriods = config('firefly.bill_periods'); $billPeriods = config('firefly.bill_periods');
foreach ($billPeriods as $current) { 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"). // put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('bills.edit.fromUpdate')) { if (true !== session('bills.edit.fromUpdate')) {
@ -99,7 +99,7 @@ class EditController extends Controller
'extension_date' => $bill->extension_date, 'extension_date' => $bill->extension_date,
'notes' => $this->repository->getNoteText($bill), 'notes' => $this->repository->getNoteText($bill),
'transaction_currency_id' => $bill->transaction_currency_id, '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 : '', '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); 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(); app('preferences')->mark();
/** @var null|array $files */ /** @var null|array $files */
@ -129,7 +129,7 @@ class EditController extends Controller
} }
if (null !== $files && auth()->user()->hasRole('demo')) { if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string) trans('firefly.no_att_demo_user')); session()->flash('info', (string)trans('firefly.no_att_demo_user'));
} }
// flash messages // flash messages
@ -138,7 +138,7 @@ class EditController extends Controller
} }
$redirect = redirect($this->getPreviousUrl('bills.edit.url')); $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); $request->session()->put('bills.edit.fromUpdate', true);
$redirect = redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]); $redirect = redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);

View File

@ -134,24 +134,6 @@ class IndexController extends Controller
return view('bills.index', compact('bills', 'sums', 'total', 'totals', 'today')); 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 * @throws FireflyException
*/ */
@ -268,4 +250,22 @@ class IndexController extends Controller
return $totals; 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']);
}
} }

View File

@ -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 private function getAllAvailableBudgets(Carbon $start, Carbon $end): array
{ {
// get all available budgets. // get all available budgets.
@ -316,4 +298,22 @@ class IndexController extends Controller
return $sums; 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']);
}
} }

View File

@ -409,6 +409,58 @@ class AccountController extends Controller
return response()->json($data); 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. * Shows the balances for a given set of dates and accounts.
* *
@ -512,56 +564,4 @@ class AccountController extends Controller
return response()->json($data); 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;
}
} }

View File

@ -195,6 +195,23 @@ class BudgetReportController extends Controller
return response()->json($data); 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. * Chart that groups expenses by the account.
*/ */
@ -224,21 +241,4 @@ class BudgetReportController extends Controller
return response()->json($data); 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;
}
} }

View File

@ -94,6 +94,11 @@ class CategoryController extends Controller
return response()->json($data); return response()->json($data);
} }
private function getDate(): Carbon
{
return today(config('app.timezone'));
}
/** /**
* Shows the category chart on the front page. * Shows the category chart on the front page.
* TODO test method for category refactor. * TODO test method for category refactor.
@ -141,66 +146,6 @@ class CategoryController extends Controller
return response()->json($data); return response()->json($data);
} }
/**
* Chart for period for transactions without a category.
* TODO test me.
*/
public function reportPeriodNoCategory(Collection $accounts, Carbon $start, Carbon $end): JsonResponse
{
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('chart.category.period.no-cat');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return response()->json($cache->get());
}
$data = $this->reportPeriodChart($accounts, $start, $end, null);
$cache->store($data);
return response()->json($data);
}
/**
* Chart for a specific period.
* TODO test me, for category refactor.
*
* @throws FireflyException
*/
public function specificPeriod(Category $category, Carbon $date): JsonResponse
{
$range = app('navigation')->getViewRange(false);
$start = app('navigation')->startOfPeriod($date, $range);
$end = session()->get('end');
if ($end < $start) {
[$end, $start] = [$start, $end];
}
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($category->id);
$cache->addProperty('chart.category.period-chart');
if ($cache->has()) {
return response()->json($cache->get());
}
/** @var WholePeriodChartGenerator $chartGenerator */
$chartGenerator = app(WholePeriodChartGenerator::class);
$chartData = $chartGenerator->generate($category, $start, $end);
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return response()->json($data);
}
private function getDate(): Carbon
{
return today(config('app.timezone'));
}
/** /**
* Generate report chart for either with or without category. * Generate report chart for either with or without category.
*/ */
@ -279,4 +224,59 @@ class CategoryController extends Controller
return $this->generator->multiSet($chartData); return $this->generator->multiSet($chartData);
} }
/**
* Chart for period for transactions without a category.
* TODO test me.
*/
public function reportPeriodNoCategory(Collection $accounts, Carbon $start, Carbon $end): JsonResponse
{
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('chart.category.period.no-cat');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return response()->json($cache->get());
}
$data = $this->reportPeriodChart($accounts, $start, $end, null);
$cache->store($data);
return response()->json($data);
}
/**
* Chart for a specific period.
* TODO test me, for category refactor.
*
* @throws FireflyException
*/
public function specificPeriod(Category $category, Carbon $date): JsonResponse
{
$range = app('navigation')->getViewRange(false);
$start = app('navigation')->startOfPeriod($date, $range);
$end = session()->get('end');
if ($end < $start) {
[$end, $start] = [$start, $end];
}
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($category->id);
$cache->addProperty('chart.category.period-chart');
if ($cache->has()) {
return response()->json($cache->get());
}
/** @var WholePeriodChartGenerator $chartGenerator */
$chartGenerator = app(WholePeriodChartGenerator::class);
$chartData = $chartGenerator->generate($category, $start, $end);
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return response()->json($data);
}
} }

View File

@ -269,6 +269,26 @@ class CategoryReportController extends Controller
return response()->json($data); 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 public function sourceExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
{ {
$result = []; $result = [];
@ -324,24 +344,4 @@ class CategoryReportController extends Controller
return response()->json($data); 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;
}
} }

View File

@ -212,6 +212,44 @@ class DoubleReportController extends Controller
return response()->json($data); 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 public function tagExpense(Collection $accounts, Collection $others, Carbon $start, Carbon $end): JsonResponse
{ {
$result = []; $result = [];
@ -317,42 +355,4 @@ class DoubleReportController extends Controller
return response()->json($data); 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;
}
} }

View File

@ -102,27 +102,27 @@ class ExpenseReportController extends Controller
/** @var Account $exp */ /** @var Account $exp */
$exp = $combination->first(); $exp = $combination->first();
$chartData[$exp->id.'-in'] = [ $chartData[$exp->id.'-in'] = [
'label' => sprintf('%s (%s)', $name, (string) trans('firefly.income')), 'label' => sprintf('%s (%s)', $name, (string)trans('firefly.income')),
'type' => 'bar', 'type' => 'bar',
'yAxisID' => 'y-axis-0', 'yAxisID' => 'y-axis-0',
'entries' => [], 'entries' => [],
]; ];
$chartData[$exp->id.'-out'] = [ $chartData[$exp->id.'-out'] = [
'label' => sprintf('%s (%s)', $name, (string) trans('firefly.expenses')), 'label' => sprintf('%s (%s)', $name, (string)trans('firefly.expenses')),
'type' => 'bar', 'type' => 'bar',
'yAxisID' => 'y-axis-0', 'yAxisID' => 'y-axis-0',
'entries' => [], 'entries' => [],
]; ];
// total in, total out: // total in, total out:
$chartData[$exp->id.'-total-in'] = [ $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', 'type' => 'line',
'fill' => false, 'fill' => false,
'yAxisID' => 'y-axis-1', 'yAxisID' => 'y-axis-1',
'entries' => [], 'entries' => [],
]; ];
$chartData[$exp->id.'-total-out'] = [ $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', 'type' => 'line',
'fill' => false, 'fill' => false,
'yAxisID' => 'y-axis-1', 'yAxisID' => 'y-axis-1',

View File

@ -274,6 +274,26 @@ class TagReportController extends Controller
return response()->json($data); 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 public function sourceExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
{ {
$result = []; $result = [];
@ -381,24 +401,4 @@ class TagReportController extends Controller
return response()->json($data); 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;
}
} }

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Middleware\IsDemoUser; use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
@ -140,21 +141,6 @@ class DebugController extends Controller
return view('debug', compact('table', 'now', 'logContent')); 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 private function generateTable(): string
{ {
// system information: // system information:
@ -336,4 +322,19 @@ class DebugController extends Controller
return implode(' ', $flags); 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'));
}
} }

View File

@ -67,7 +67,7 @@ class IndexController extends Controller
public function export(): LaravelResponse|RedirectResponse public function export(): LaravelResponse|RedirectResponse
{ {
if (auth()->user()->hasRole('demo')) { 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')); return redirect(route('export.index'));
} }

View File

@ -144,7 +144,6 @@ class JavascriptController extends Controller
return response() return response()
->view('v2.javascript.variables', $data) ->view('v2.javascript.variables', $data)
->header('Content-Type', 'text/javascript') ->header('Content-Type', 'text/javascript');
;
} }
} }

View File

@ -71,13 +71,13 @@ class RecurrenceController extends Controller
$start = Carbon::createFromFormat('Y-m-d', $request->get('start')); $start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end')); $end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$firstDate = Carbon::createFromFormat('Y-m-d', $request->get('first_date')); $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; $endDate = '' !== (string)$request->get('end_date') ? Carbon::createFromFormat('Y-m-d', $request->get('end_date')) : null;
$endsAt = (string) $request->get('ends'); $endsAt = (string)$request->get('ends');
$repetitionType = explode(',', $request->get('type'))[0]; $repetitionType = explode(',', $request->get('type'))[0];
$repetitions = (int) $request->get('reps'); $repetitions = (int)$request->get('reps');
$weekend = (int) $request->get('weekend'); $weekend = (int)$request->get('weekend');
$repetitionMoment = ''; $repetitionMoment = '';
$skip = (int) $request->get('skip'); $skip = (int)$request->get('skip');
$skip = $skip < 0 || $skip > 31 ? 0 : $skip; $skip = $skip < 0 || $skip > 31 ? 0 : $skip;
$weekend = $weekend < 1 || $weekend > 4 ? 1 : $weekend; $weekend = $weekend < 1 || $weekend > 4 ? 1 : $weekend;
@ -147,7 +147,7 @@ class RecurrenceController extends Controller
*/ */
public function suggest(Request $request): JsonResponse 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(); $today = today(config('app.timezone'))->startOfDay();
try { try {
@ -159,37 +159,37 @@ class RecurrenceController extends Controller
return response()->json(); return response()->json();
} }
$date->startOfDay(); $date->startOfDay();
$preSelected = (string) $request->get('pre_select'); $preSelected = (string)$request->get('pre_select');
$locale = app('steam')->getLocale(); $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('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 = []; $result = [];
if ($date > $today || 'true' === (string) $request->get('past')) { if ($date > $today || 'true' === (string)$request->get('past')) {
app('log')->debug('Will fill dropdown.'); app('log')->debug('Will fill dropdown.');
$weekly = sprintf('weekly,%s', $date->dayOfWeekIso); $weekly = sprintf('weekly,%s', $date->dayOfWeekIso);
$monthly = sprintf('monthly,%s', $date->day); $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); $ndom = sprintf('ndom,%s,%s', $date->weekOfMonth, $date->dayOfWeekIso);
$yearly = sprintf('yearly,%s', $date->format('Y-m-d')); $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 = [ $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 => [ $weekly => [
'label' => (string) trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]), 'label' => (string)trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]),
'selected' => str_starts_with($preSelected, 'weekly'), 'selected' => str_starts_with($preSelected, 'weekly'),
], ],
$monthly => [ $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'), 'selected' => str_starts_with($preSelected, 'monthly'),
], ],
$ndom => [ $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'), 'selected' => str_starts_with($preSelected, 'ndom'),
], ],
$yearly => [ $yearly => [
'label' => (string) trans('firefly.recurring_yearly', ['date' => $yearlyDate]), 'label' => (string)trans('firefly.recurring_yearly', ['date' => $yearlyDate]),
'selected' => str_starts_with($preSelected, 'yearly'), 'selected' => str_starts_with($preSelected, 'yearly'),
], ],
]; ];

View File

@ -141,24 +141,6 @@ class IndexController extends Controller
return view('piggy-banks.index', compact('piggyBanks', 'accounts')); 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 private function makeSums(array $piggyBanks): array
{ {
$sums = []; $sums = [];
@ -193,4 +175,22 @@ class IndexController extends Controller
return $piggyBanks; 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']);
}
} }

View File

@ -49,7 +49,7 @@ class PreferencesController extends Controller
$this->middleware( $this->middleware(
static function ($request, $next) { 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'); app('view')->share('mainTitleIcon', 'fa-gear');
return $next($request); return $next($request);
@ -82,7 +82,7 @@ class PreferencesController extends Controller
if ('opt_group_' === $role) { if ('opt_group_' === $role) {
$role = 'opt_group_defaultAsset'; $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); ksort($groupedAccounts);
@ -105,7 +105,7 @@ class PreferencesController extends Controller
if (is_array($fiscalYearStartStr)) { if (is_array($fiscalYearStartStr)) {
$fiscalYearStartStr = '01-01'; $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; $tjOptionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
$availableDarkModes = config('firefly.available_dark_modes'); $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. // list of locales also has "equal" which makes it equal to whatever the language is.
try { 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) { } catch (\JsonException $e) {
app('log')->error($e->getMessage()); app('log')->error($e->getMessage());
$locales = []; $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 // an important fallback is that the frontPageAccount array gets refilled automatically
// when it turns up empty. // when it turns up empty.
if (0 === count($frontPageAccounts)) { if (0 === count($frontPageAccounts)) {
@ -158,7 +158,7 @@ class PreferencesController extends Controller
$frontPageAccounts = []; $frontPageAccounts = [];
if (is_array($request->get('frontPageAccounts')) && count($request->get('frontPageAccounts')) > 0) { if (is_array($request->get('frontPageAccounts')) && count($request->get('frontPageAccounts')) > 0) {
foreach ($request->get('frontPageAccounts') as $id) { foreach ($request->get('frontPageAccounts') as $id) {
$frontPageAccounts[] = (int) $id; $frontPageAccounts[] = (int)$id;
} }
app('preferences')->set('frontPageAccounts', $frontPageAccounts); app('preferences')->set('frontPageAccounts', $frontPageAccounts);
} }
@ -184,7 +184,7 @@ class PreferencesController extends Controller
// slack URL: // slack URL:
if (!auth()->user()->hasRole('demo')) { if (!auth()->user()->hasRole('demo')) {
$url = (string) $request->get('slackUrl'); $url = (string)$request->get('slackUrl');
if (UrlValidator::isValidWebhookURL($url)) { if (UrlValidator::isValidWebhookURL($url)) {
app('preferences')->set('slack_webhook_url', $url); app('preferences')->set('slack_webhook_url', $url);
} }
@ -194,8 +194,8 @@ class PreferencesController extends Controller
} }
// custom fiscal year // custom fiscal year
$customFiscalYear = 1 === (int) $request->get('customFiscalYear'); $customFiscalYear = 1 === (int)$request->get('customFiscalYear');
$string = strtotime((string) $request->get('fiscalYearStart')); $string = strtotime((string)$request->get('fiscalYearStart'));
if (false !== $string) { if (false !== $string) {
$fiscalYearStart = date('m-d', $string); $fiscalYearStart = date('m-d', $string);
app('preferences')->set('customFiscalYear', $customFiscalYear); app('preferences')->set('customFiscalYear', $customFiscalYear);
@ -204,7 +204,7 @@ class PreferencesController extends Controller
// save page size: // save page size:
app('preferences')->set('listPageSize', 50); app('preferences')->set('listPageSize', 50);
$listPageSize = (int) $request->get('listPageSize'); $listPageSize = (int)$request->get('listPageSize');
if ($listPageSize > 0 && $listPageSize < 1337) { if ($listPageSize > 0 && $listPageSize < 1337) {
app('preferences')->set('listPageSize', $listPageSize); app('preferences')->set('listPageSize', $listPageSize);
} }
@ -252,7 +252,7 @@ class PreferencesController extends Controller
app('preferences')->set('darkMode', $darkMode); app('preferences')->set('darkMode', $darkMode);
} }
session()->flash('success', (string) trans('firefly.saved_preferences')); session()->flash('success', (string)trans('firefly.saved_preferences'));
app('preferences')->mark(); app('preferences')->mark();
return redirect(route('preferences.index')); return redirect(route('preferences.index'));

View File

@ -497,6 +497,47 @@ class ProfileController extends Controller
return redirect(route('profile.index')); 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. * Submit delete account.
* *
@ -631,45 +672,4 @@ class ProfileController extends Controller
return redirect(route('login')); 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);
}
} }

View File

@ -283,8 +283,8 @@ class CategoryController extends Controller
]; ];
++$result[$key]['transactions']; ++$result[$key]['transactions'];
$result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']); $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
$result[$key]['avg_float'] = (float) $result[$key]['avg']; // intentional float $result[$key]['avg_float'] = (float)$result[$key]['avg']; // intentional float
} }
} }
} }
@ -333,8 +333,8 @@ class CategoryController extends Controller
]; ];
++$result[$key]['transactions']; ++$result[$key]['transactions'];
$result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']); $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
$result[$key]['avg_float'] = (float) $result[$key]['avg']; $result[$key]['avg_float'] = (float)$result[$key]['avg'];
} }
} }
} }
@ -664,7 +664,7 @@ class CategoryController extends Controller
$result[] = [ $result[] = [
'description' => $journal['description'], 'description' => $journal['description'],
'transaction_group_id' => $journal['transaction_group_id'], 'transaction_group_id' => $journal['transaction_group_id'],
'amount_float' => (float) $journal['amount'], 'amount_float' => (float)$journal['amount'],
'amount' => $journal['amount'], 'amount' => $journal['amount'],
'date' => $journal['date']->isoFormat($this->monthAndDayFormat), 'date' => $journal['date']->isoFormat($this->monthAndDayFormat),
'date_sort' => $journal['date']->format('Y-m-d'), 'date_sort' => $journal['date']->format('Y-m-d'),
@ -712,7 +712,7 @@ class CategoryController extends Controller
$result[] = [ $result[] = [
'description' => $journal['description'], 'description' => $journal['description'],
'transaction_group_id' => $journal['transaction_group_id'], 'transaction_group_id' => $journal['transaction_group_id'],
'amount_float' => (float) $journal['amount'], 'amount_float' => (float)$journal['amount'],
'amount' => $journal['amount'], 'amount' => $journal['amount'],
'date' => $journal['date']->isoFormat($this->monthAndDayFormat), 'date' => $journal['date']->isoFormat($this->monthAndDayFormat),
'date_sort' => $journal['date']->format('Y-m-d'), 'date_sort' => $journal['date']->format('Y-m-d'),

View File

@ -278,6 +278,24 @@ class DoubleController extends Controller
return view('reports.double.partials.accounts', compact('sums', 'report')); 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 * @return Factory|View
*/ */
@ -468,22 +486,4 @@ class DoubleController extends Controller
return $result; 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;
}
} }

View File

@ -279,8 +279,8 @@ class TagController extends Controller
]; ];
++$result[$key]['transactions']; ++$result[$key]['transactions'];
$result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']); $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
$result[$key]['avg_float'] = (float) $result[$key]['avg']; $result[$key]['avg_float'] = (float)$result[$key]['avg'];
} }
} }
} }
@ -329,8 +329,8 @@ class TagController extends Controller
]; ];
++$result[$key]['transactions']; ++$result[$key]['transactions'];
$result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
$result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']); $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
$result[$key]['avg_float'] = (float) $result[$key]['avg']; $result[$key]['avg_float'] = (float)$result[$key]['avg'];
} }
} }
} }
@ -466,7 +466,7 @@ class TagController extends Controller
$result[] = [ $result[] = [
'description' => $journal['description'], 'description' => $journal['description'],
'transaction_group_id' => $journal['transaction_group_id'], 'transaction_group_id' => $journal['transaction_group_id'],
'amount_float' => (float) $journal['amount'], 'amount_float' => (float)$journal['amount'],
'amount' => $journal['amount'], 'amount' => $journal['amount'],
'date' => $journal['date']->isoFormat($this->monthAndDayFormat), 'date' => $journal['date']->isoFormat($this->monthAndDayFormat),
'date_sort' => $journal['date']->format('Y-m-d'), 'date_sort' => $journal['date']->format('Y-m-d'),
@ -514,7 +514,7 @@ class TagController extends Controller
$result[] = [ $result[] = [
'description' => $journal['description'], 'description' => $journal['description'],
'transaction_group_id' => $journal['transaction_group_id'], 'transaction_group_id' => $journal['transaction_group_id'],
'amount_float' => (float) $journal['amount'], // intentional float. 'amount_float' => (float)$journal['amount'], // intentional float.
'amount' => $journal['amount'], 'amount' => $journal['amount'],
'date' => $journal['date']->isoFormat($this->monthAndDayFormat), 'date' => $journal['date']->isoFormat($this->monthAndDayFormat),
'date_sort' => $journal['date']->format('Y-m-d'), 'date_sort' => $journal['date']->format('Y-m-d'),

View File

@ -132,29 +132,6 @@ class EditController extends Controller
return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount')); 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 * @throws FireflyException
*/ */
@ -196,4 +173,27 @@ class EditController extends Controller
return $renderedEntries; 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;
}
} }

View File

@ -129,26 +129,6 @@ class InstallController extends Controller
return response()->json($response); 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 * @throws FireflyException
*/ */
@ -173,4 +153,24 @@ class InstallController extends Controller
return true; 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'));
}
} }

View File

@ -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 private function getValidDepositSources(): array
{ {
// make repositories // make repositories
@ -291,6 +259,38 @@ class ConvertController extends Controller
return $grouped; 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 * @throws FireflyException
*/ */

View File

@ -50,7 +50,7 @@ class CreateController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { 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'); app('view')->share('mainTitleIcon', 'fa-exchange');
$this->repository = app(TransactionGroupRepositoryInterface::class); $this->repository = app(TransactionGroupRepositoryInterface::class);
@ -61,7 +61,7 @@ class CreateController extends Controller
public function cloneGroup(Request $request): JsonResponse public function cloneGroup(Request $request): JsonResponse
{ {
$groupId = (int) $request->get('id'); $groupId = (int)$request->get('id');
if (0 !== $groupId) { if (0 !== $groupId) {
$group = $this->repository->find($groupId); $group = $this->repository->find($groupId);
if (null !== $group) { if (null !== $group) {
@ -101,14 +101,14 @@ class CreateController extends Controller
{ {
app('preferences')->mark(); app('preferences')->mark();
$sourceId = (int) request()->get('source'); $sourceId = (int)request()->get('source');
$destinationId = (int) request()->get('destination'); $destinationId = (int)request()->get('destination');
/** @var AccountRepositoryInterface $accountRepository */ /** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class); $accountRepository = app(AccountRepositoryInterface::class);
$cash = $accountRepository->getCashAccount(); $cash = $accountRepository->getCashAccount();
$preFilled = session()->has('preFilled') ? session('preFilled') : []; $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'; $subTitleIcon = 'fa-plus';
$optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data; $optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
$allowedOpposingTypes = config('firefly.allowed_opposing_types'); $allowedOpposingTypes = config('firefly.allowed_opposing_types');

View File

@ -51,7 +51,7 @@ class EditController extends Controller
// translations: // translations:
$this->middleware( $this->middleware(
function ($request, $next) { 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'); app('view')->share('mainTitleIcon', 'fa-exchange');
$this->repository = app(JournalRepositoryInterface::class); $this->repository = app(JournalRepositoryInterface::class);
@ -79,7 +79,7 @@ class EditController extends Controller
$expectedSourceTypes = config('firefly.expected_source_types'); $expectedSourceTypes = config('firefly.expected_source_types');
$allowedSourceDests = config('firefly.source_dests'); $allowedSourceDests = config('firefly.source_dests');
$title = $transactionGroup->transactionJournals()->count() > 1 ? $transactionGroup->title : $transactionGroup->transactionJournals()->first()->description; $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'; $subTitleIcon = 'fa-plus';
$defaultCurrency = app('amount')->getDefaultCurrency(); $defaultCurrency = app('amount')->getDefaultCurrency();
$cash = $repository->getCashAccount(); $cash = $repository->getCashAccount();

View File

@ -73,22 +73,6 @@ class Installer
return $next($request); return $next($request);
} }
/**
* Is access denied error.
*/
protected function isAccessDenied(string $message): bool
{
return false !== stripos($message, 'Access denied');
}
/**
* Is no tables exist error.
*/
protected function noTablesExist(string $message): bool
{
return false !== stripos($message, 'Base table or view not found');
}
/** /**
* Check if the tables are created and accounted for. * Check if the tables are created and accounted for.
* *
@ -125,6 +109,22 @@ class Installer
return false; return false;
} }
/**
* Is access denied error.
*/
protected function isAccessDenied(string $message): bool
{
return false !== stripos($message, 'Access denied');
}
/**
* Is no tables exist error.
*/
protected function noTablesExist(string $message): bool
{
return false !== stripos($message, 'Base table or view not found');
}
/** /**
* Check if the "db_version" variable is correct. * Check if the "db_version" variable is correct.
*/ */

View File

@ -46,7 +46,7 @@ class JournalLinkRequest extends FormRequest
$return = []; $return = [];
$linkType = $this->get('link_type'); $linkType = $this->get('link_type');
$parts = explode('_', $linkType); $parts = explode('_', $linkType);
$return['link_type_id'] = (int) $parts[0]; $return['link_type_id'] = (int)$parts[0];
$return['transaction_journal_id'] = $this->convertInteger('opposing'); $return['transaction_journal_id'] = $this->convertInteger('opposing');
$return['notes'] = $this->convertString('notes'); $return['notes'] = $this->convertString('notes');
$return['direction'] = $parts[1]; $return['direction'] = $parts[1];

View File

@ -150,6 +150,38 @@ class RecurrenceFormRequest extends FormRequest
return $return; return $return;
} }
/**
* Parses repetition data.
*/
private function parseRepetitionData(): array
{
$value = $this->convertString('repetition_type');
$return = [
'type' => '',
'moment' => '',
];
if ('daily' === $value) {
$return['type'] = $value;
}
// monthly,17
// ndom,3,7
if (in_array(substr($value, 0, 6), ['yearly', 'weekly'], true)) {
$return['type'] = substr($value, 0, 6);
$return['moment'] = substr($value, 7);
}
if (str_starts_with($value, 'monthly')) {
$return['type'] = substr($value, 0, 7);
$return['moment'] = substr($value, 8);
}
if (str_starts_with($value, 'ndom')) {
$return['type'] = substr($value, 0, 4);
$return['moment'] = substr($value, 5);
}
return $return;
}
/** /**
* The rules for this request. * The rules for this request.
*/ */
@ -278,18 +310,18 @@ class RecurrenceFormRequest extends FormRequest
$throwError = true; $throwError = true;
if ('withdrawal' === $type) { if ('withdrawal' === $type) {
$throwError = false; $throwError = false;
$sourceId = (int) $data['source_id']; $sourceId = (int)$data['source_id'];
$destinationId = (int) $data['withdrawal_destination_id']; $destinationId = (int)$data['withdrawal_destination_id'];
} }
if ('deposit' === $type) { if ('deposit' === $type) {
$throwError = false; $throwError = false;
$sourceId = (int) $data['deposit_source_id']; $sourceId = (int)$data['deposit_source_id'];
$destinationId = (int) ($data['destination_id'] ?? 0); $destinationId = (int)($data['destination_id'] ?? 0);
} }
if ('transfer' === $type) { if ('transfer' === $type) {
$throwError = false; $throwError = false;
$sourceId = (int) $data['source_id']; $sourceId = (int)$data['source_id'];
$destinationId = (int) ($data['destination_id'] ?? 0); $destinationId = (int)($data['destination_id'] ?? 0);
} }
if (true === $throwError) { if (true === $throwError) {
throw new FireflyException(sprintf('Cannot handle transaction type "%s"', $this->convertString('transaction_type'))); throw new FireflyException(sprintf('Cannot handle transaction type "%s"', $this->convertString('transaction_type')));
@ -300,7 +332,7 @@ class RecurrenceFormRequest extends FormRequest
// do something with result: // do something with result:
if (false === $validSource) { if (false === $validSource) {
$message = (string) trans('validation.generic_invalid_source'); $message = (string)trans('validation.generic_invalid_source');
$validator->errors()->add('source_id', $message); $validator->errors()->add('source_id', $message);
$validator->errors()->add('deposit_source_id', $message); $validator->errors()->add('deposit_source_id', $message);
@ -311,41 +343,9 @@ class RecurrenceFormRequest extends FormRequest
$validDestination = $accountValidator->validateDestination(['id' => $destinationId]); $validDestination = $accountValidator->validateDestination(['id' => $destinationId]);
// do something with result: // do something with result:
if (false === $validDestination) { if (false === $validDestination) {
$message = (string) trans('validation.generic_invalid_destination'); $message = (string)trans('validation.generic_invalid_destination');
$validator->errors()->add('destination_id', $message); $validator->errors()->add('destination_id', $message);
$validator->errors()->add('withdrawal_destination_id', $message); $validator->errors()->add('withdrawal_destination_id', $message);
} }
} }
/**
* Parses repetition data.
*/
private function parseRepetitionData(): array
{
$value = $this->convertString('repetition_type');
$return = [
'type' => '',
'moment' => '',
];
if ('daily' === $value) {
$return['type'] = $value;
}
// monthly,17
// ndom,3,7
if (in_array(substr($value, 0, 6), ['yearly', 'weekly'], true)) {
$return['type'] = substr($value, 0, 6);
$return['moment'] = substr($value, 7);
}
if (str_starts_with($value, 'monthly')) {
$return['type'] = substr($value, 0, 7);
$return['moment'] = substr($value, 8);
}
if (str_starts_with($value, 'ndom')) {
$return['type'] = substr($value, 0, 4);
$return['moment'] = substr($value, 5);
}
return $return;
}
} }

View File

@ -55,7 +55,7 @@ class ReportFormRequest extends FormRequest
$collection = new Collection(); $collection = new Collection();
if (is_array($set)) { if (is_array($set)) {
foreach ($set as $accountId) { foreach ($set as $accountId) {
$account = $repository->find((int) $accountId); $account = $repository->find((int)$accountId);
if (null !== $account) { if (null !== $account) {
$collection->push($account); $collection->push($account);
} }
@ -76,7 +76,7 @@ class ReportFormRequest extends FormRequest
$collection = new Collection(); $collection = new Collection();
if (is_array($set)) { if (is_array($set)) {
foreach ($set as $budgetId) { foreach ($set as $budgetId) {
$budget = $repository->find((int) $budgetId); $budget = $repository->find((int)$budgetId);
if (null !== $budget) { if (null !== $budget) {
$collection->push($budget); $collection->push($budget);
} }
@ -97,7 +97,7 @@ class ReportFormRequest extends FormRequest
$collection = new Collection(); $collection = new Collection();
if (is_array($set)) { if (is_array($set)) {
foreach ($set as $categoryId) { foreach ($set as $categoryId) {
$category = $repository->find((int) $categoryId); $category = $repository->find((int)$categoryId);
if (null !== $category) { if (null !== $category) {
$collection->push($category); $collection->push($category);
} }
@ -118,7 +118,7 @@ class ReportFormRequest extends FormRequest
$collection = new Collection(); $collection = new Collection();
if (is_array($set)) { if (is_array($set)) {
foreach ($set as $accountId) { foreach ($set as $accountId) {
$account = $repository->find((int) $accountId); $account = $repository->find((int)$accountId);
if (null !== $account) { if (null !== $account) {
$collection->push($account); $collection->push($account);
} }
@ -137,7 +137,7 @@ class ReportFormRequest extends FormRequest
{ {
$date = today(config('app.timezone')); $date = today(config('app.timezone'));
$range = $this->get('daterange'); $range = $this->get('daterange');
$parts = explode(' - ', (string) $range); $parts = explode(' - ', (string)$range);
if (2 === count($parts)) { if (2 === count($parts)) {
$string = $parts[1]; $string = $parts[1];
// validate as date // validate as date
@ -175,7 +175,7 @@ class ReportFormRequest extends FormRequest
{ {
$date = today(config('app.timezone')); $date = today(config('app.timezone'));
$range = $this->get('daterange'); $range = $this->get('daterange');
$parts = explode(' - ', (string) $range); $parts = explode(' - ', (string)$range);
if (2 === count($parts)) { if (2 === count($parts)) {
$string = $parts[0]; $string = $parts[0];
// validate as date // validate as date
@ -229,7 +229,7 @@ class ReportFormRequest extends FormRequest
continue; continue;
} }
$tag = $repository->find((int) $tagTag); $tag = $repository->find((int)$tagTag);
if (null !== $tag) { if (null !== $tag) {
$collection->push($tag); $collection->push($tag);
} }

View File

@ -58,6 +58,28 @@ class RuleFormRequest extends FormRequest
]; ];
} }
private function getRuleTriggerData(): array
{
$return = [];
$triggerData = $this->get('triggers');
if (is_array($triggerData)) {
foreach ($triggerData as $trigger) {
$stopProcessing = $trigger['stop_processing'] ?? '0';
$prohibited = $trigger['prohibited'] ?? '0';
$set = [
'type' => $trigger['type'] ?? 'invalid',
'value' => $trigger['value'] ?? '',
'stop_processing' => 1 === (int)$stopProcessing,
'prohibited' => 1 === (int)$prohibited,
];
$set = self::replaceAmountTrigger($set);
$return[] = $set;
}
}
return $return;
}
public static function replaceAmountTrigger(array $array): array public static function replaceAmountTrigger(array $array): array
{ {
// do some sneaky search and replace. // do some sneaky search and replace.
@ -83,6 +105,24 @@ class RuleFormRequest extends FormRequest
return $array; return $array;
} }
private function getRuleActionData(): array
{
$return = [];
$actionData = $this->get('actions');
if (is_array($actionData)) {
foreach ($actionData as $action) {
$stopProcessing = $action['stop_processing'] ?? '0';
$return[] = [
'type' => $action['type'] ?? 'invalid',
'value' => $action['value'] ?? '',
'stop_processing' => 1 === (int)$stopProcessing,
];
}
}
return $return;
}
/** /**
* Rules for this request. * Rules for this request.
*/ */
@ -127,44 +167,4 @@ class RuleFormRequest extends FormRequest
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
} }
} }
private function getRuleTriggerData(): array
{
$return = [];
$triggerData = $this->get('triggers');
if (is_array($triggerData)) {
foreach ($triggerData as $trigger) {
$stopProcessing = $trigger['stop_processing'] ?? '0';
$prohibited = $trigger['prohibited'] ?? '0';
$set = [
'type' => $trigger['type'] ?? 'invalid',
'value' => $trigger['value'] ?? '',
'stop_processing' => 1 === (int) $stopProcessing,
'prohibited' => 1 === (int) $prohibited,
];
$set = self::replaceAmountTrigger($set);
$return[] = $set;
}
}
return $return;
}
private function getRuleActionData(): array
{
$return = [];
$actionData = $this->get('actions');
if (is_array($actionData)) {
foreach ($actionData as $action) {
$stopProcessing = $action['stop_processing'] ?? '0';
$return[] = [
'type' => $action['type'] ?? 'invalid',
'value' => $action['value'] ?? '',
'stop_processing' => 1 === (int) $stopProcessing,
];
}
}
return $return;
}
} }

View File

@ -77,13 +77,6 @@ class CreateAutoBudgetLimits implements ShouldQueue
} }
} }
public function setDate(Carbon $date): void
{
$newDate = clone $date;
$newDate->startOfDay();
$this->date = $newDate;
}
/** /**
* @throws FireflyException * @throws FireflyException
*/ */
@ -362,4 +355,11 @@ class CreateAutoBudgetLimits implements ShouldQueue
} }
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
} }
public function setDate(Carbon $date): void
{
$newDate = clone $date;
$newDate->startOfDay();
$this->date = $newDate;
}
} }

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