mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-26 13:36:15 +00:00
Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
This commit is contained in:
@@ -31,7 +31,6 @@ use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
||||
@@ -252,7 +251,7 @@ class BudgetController extends Controller
|
||||
/** @var array $journal */
|
||||
foreach ($currentBudgetArray['transaction_journals'] as $journal) {
|
||||
/** @var numeric-string $amount */
|
||||
$amount= (string)$journal['amount'];
|
||||
$amount = (string)$journal['amount'];
|
||||
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $amount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ class StoreByDateRequest extends FormRequest
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function rules(): array
|
||||
@@ -85,6 +86,7 @@ class StoreByDateRequest extends FormRequest
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$to = Amount::getTransactionCurrencyByCode((string)$key);
|
||||
} catch (FireflyException) {
|
||||
|
||||
@@ -40,9 +40,9 @@ class UpgradesCurrencyPreferences extends Command
|
||||
|
||||
public const string CONFIG_NAME = '610_upgrade_currency_prefs';
|
||||
|
||||
protected $description = 'Upgrade user currency preferences';
|
||||
protected $description = 'Upgrade user currency preferences';
|
||||
|
||||
protected $signature = 'upgrade:610-currency-preferences {--F|force : Force the execution of this command.}';
|
||||
protected $signature = 'upgrade:610-currency-preferences {--F|force : Force the execution of this command.}';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
@@ -82,7 +82,7 @@ class UpgradesCurrencyPreferences extends Command
|
||||
$this->upgradeGroupPreferences($group);
|
||||
}
|
||||
|
||||
$users = User::get();
|
||||
$users = User::get();
|
||||
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
@@ -119,6 +119,7 @@ class UpgradesCurrencyPreferences extends Command
|
||||
|
||||
// set the default currency for the user and for the group:
|
||||
$preference = $this->getPreference($user);
|
||||
|
||||
try {
|
||||
$primaryCurrency = Amount::getTransactionCurrencyByCode($preference);
|
||||
} catch (FireflyException) {
|
||||
|
||||
@@ -47,6 +47,7 @@ use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Override;
|
||||
|
||||
use function Safe\json_decode;
|
||||
|
||||
/**
|
||||
@@ -66,15 +67,15 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->sorting = [];
|
||||
$this->postFilters = [];
|
||||
$this->tags = [];
|
||||
$this->user = null;
|
||||
$this->userGroup = null;
|
||||
$this->limit = null;
|
||||
$this->page = null;
|
||||
$this->startRow = null;
|
||||
$this->endRow = null;
|
||||
$this->sorting = [];
|
||||
$this->postFilters = [];
|
||||
$this->tags = [];
|
||||
$this->user = null;
|
||||
$this->userGroup = null;
|
||||
$this->limit = null;
|
||||
$this->page = null;
|
||||
$this->startRow = null;
|
||||
$this->endRow = null;
|
||||
|
||||
$this->hasAccountInfo = false;
|
||||
$this->hasCatInformation = false;
|
||||
@@ -304,7 +305,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
if (is_int($param)) {
|
||||
$replace = (string)$param;
|
||||
}
|
||||
$pos = strpos($query, '?');
|
||||
$pos = strpos($query, '?');
|
||||
if (false !== $pos) {
|
||||
$query = substr_replace($query, $replace, $pos, 1);
|
||||
}
|
||||
@@ -476,15 +477,15 @@ class GroupCollector implements GroupCollectorInterface
|
||||
// add to query:
|
||||
$this->query->orWhereIn('transaction_journals.transaction_group_id', $groupIds);
|
||||
}
|
||||
$result = $this->query->get($this->fields);
|
||||
$result = $this->query->get($this->fields);
|
||||
// now to parse this into an array.
|
||||
$collection = $this->parseArray($result);
|
||||
$collection = $this->parseArray($result);
|
||||
|
||||
// filter the array using all available post filters:
|
||||
$collection = $this->postFilterCollection($collection);
|
||||
$collection = $this->postFilterCollection($collection);
|
||||
|
||||
// sort the collection, if sort instructions are present.
|
||||
$collection = $this->sortCollection($collection);
|
||||
$collection = $this->sortCollection($collection);
|
||||
|
||||
// count it and continue:
|
||||
$this->total = $collection->count();
|
||||
@@ -517,12 +518,12 @@ class GroupCollector implements GroupCollectorInterface
|
||||
|
||||
/** @var TransactionJournal $augumentedJournal */
|
||||
foreach ($collection as $augumentedJournal) {
|
||||
$groupId = (int)$augumentedJournal->transaction_group_id;
|
||||
$groupId = (int)$augumentedJournal->transaction_group_id;
|
||||
|
||||
if (!array_key_exists($groupId, $groups)) {
|
||||
// make new array
|
||||
$parsedGroup = $this->parseAugmentedJournal($augumentedJournal);
|
||||
$groupArray = [
|
||||
$parsedGroup = $this->parseAugmentedJournal($augumentedJournal);
|
||||
$groupArray = [
|
||||
'id' => (int)$augumentedJournal->transaction_group_id,
|
||||
'user_id' => $augumentedJournal->user_id,
|
||||
'user_group_id' => $augumentedJournal->user_group_id,
|
||||
@@ -593,7 +594,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
}
|
||||
|
||||
// try to process meta date value (if present)
|
||||
$dates = ['interest_date', 'payment_date', 'invoice_date', 'book_date', 'due_date', 'process_date'];
|
||||
$dates = ['interest_date', 'payment_date', 'invoice_date', 'book_date', 'due_date', 'process_date'];
|
||||
if (array_key_exists('meta_name', $result) && in_array($result['meta_name'], $dates, true)) {
|
||||
$name = $result['meta_name'];
|
||||
if (array_key_exists('meta_data', $result) && '' !== (string)$result['meta_data']) {
|
||||
@@ -602,18 +603,18 @@ class GroupCollector implements GroupCollectorInterface
|
||||
}
|
||||
|
||||
// convert values to integers:
|
||||
$result = $this->convertToInteger($result);
|
||||
$result = $this->convertToInteger($result);
|
||||
|
||||
// convert to boolean
|
||||
$result = $this->convertToBoolean($result);
|
||||
$result = $this->convertToBoolean($result);
|
||||
|
||||
// convert back to strings because SQLite is dumb like that.
|
||||
$result = $this->convertToStrings($result);
|
||||
$result = $this->convertToStrings($result);
|
||||
|
||||
$result['reconciled'] = 1 === (int)$result['reconciled'];
|
||||
$result['reconciled'] = 1 === (int)$result['reconciled'];
|
||||
if (array_key_exists('tag_id', $result) && null !== $result['tag_id']) { // assume the other fields are present as well.
|
||||
$tagId = (int)$augumentedJournal['tag_id'];
|
||||
$tagDate = null;
|
||||
$tagId = (int)$augumentedJournal['tag_id'];
|
||||
$tagDate = null;
|
||||
|
||||
try {
|
||||
$tagDate = Carbon::parse($augumentedJournal['tag_date']);
|
||||
@@ -686,9 +687,9 @@ class GroupCollector implements GroupCollectorInterface
|
||||
{
|
||||
$newArray = $newJournal->toArray();
|
||||
if (array_key_exists('tag_id', $newArray)) { // assume the other fields are present as well.
|
||||
$tagId = (int)$newJournal['tag_id'];
|
||||
$tagId = (int)$newJournal['tag_id'];
|
||||
|
||||
$tagDate = null;
|
||||
$tagDate = null;
|
||||
|
||||
try {
|
||||
$tagDate = Carbon::parse($newArray['tag_date']);
|
||||
@@ -711,7 +712,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
{
|
||||
$newArray = $newJournal->toArray();
|
||||
if (array_key_exists('attachment_id', $newArray)) {
|
||||
$attachmentId = (int)$newJournal['attachment_id'];
|
||||
$attachmentId = (int)$newJournal['attachment_id'];
|
||||
|
||||
$existingJournal['attachments'][$attachmentId] = [
|
||||
'id' => $attachmentId,
|
||||
@@ -724,19 +725,19 @@ class GroupCollector implements GroupCollectorInterface
|
||||
private function parseSums(array $groups): array
|
||||
{
|
||||
/**
|
||||
* @var int $groudId
|
||||
* @var int $groudId
|
||||
* @var array $group
|
||||
*/
|
||||
foreach ($groups as $groudId => $group) {
|
||||
/** @var array $transaction */
|
||||
foreach ($group['transactions'] as $transaction) {
|
||||
$currencyId = (int)$transaction['currency_id'];
|
||||
$currencyId = (int)$transaction['currency_id'];
|
||||
if (null === $transaction['amount']) {
|
||||
throw new FireflyException(sprintf('Amount is NULL for a transaction in group #%d, please investigate.', $groudId));
|
||||
}
|
||||
$pcAmount = (string)('' === $transaction['pc_amount'] ? '0' : $transaction['pc_amount']);
|
||||
$pcForeignAmount = (string)('' === $transaction['pc_foreign_amount'] ? '0' : $transaction['pc_foreign_amount']);
|
||||
$foreignAmount = (string)('' === $transaction['foreign_amount'] ? '0' : $transaction['foreign_amount']);
|
||||
$pcAmount = (string)('' === $transaction['pc_amount'] ? '0' : $transaction['pc_amount']);
|
||||
$pcForeignAmount = (string)('' === $transaction['pc_foreign_amount'] ? '0' : $transaction['pc_foreign_amount']);
|
||||
$foreignAmount = (string)('' === $transaction['foreign_amount'] ? '0' : $transaction['foreign_amount']);
|
||||
|
||||
// set default:
|
||||
if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) {
|
||||
@@ -751,7 +752,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = bcadd((string)$groups[$groudId]['sums'][$currencyId]['pc_amount'], $pcAmount);
|
||||
|
||||
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
|
||||
$currencyId = (int)$transaction['foreign_currency_id'];
|
||||
$currencyId = (int)$transaction['foreign_currency_id'];
|
||||
|
||||
// set default:
|
||||
if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) {
|
||||
@@ -786,7 +787,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
foreach ($this->postFilters as $function) {
|
||||
app('log')->debug('Applying filter...');
|
||||
$nextCollection = new Collection();
|
||||
$nextCollection = new Collection();
|
||||
|
||||
// loop everything in the current collection
|
||||
// and save it (or not) in the new collection.
|
||||
@@ -850,7 +851,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
public function getPaginatedGroups(): LengthAwarePaginator
|
||||
{
|
||||
$set = $this->getGroups();
|
||||
$set = $this->getGroups();
|
||||
if (0 === $this->limit) {
|
||||
$this->setLimit(50);
|
||||
}
|
||||
@@ -1074,7 +1075,8 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transactions as source',
|
||||
static function (JoinClause $join): void {
|
||||
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('source.amount', '<', 0);
|
||||
->where('source.amount', '<', 0)
|
||||
;
|
||||
}
|
||||
)
|
||||
// join destination transaction
|
||||
@@ -1082,7 +1084,8 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transactions as destination',
|
||||
static function (JoinClause $join): void {
|
||||
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('destination.amount', '>', 0);
|
||||
->where('destination.amount', '>', 0)
|
||||
;
|
||||
}
|
||||
)
|
||||
// left join transaction type.
|
||||
@@ -1101,7 +1104,8 @@ class GroupCollector implements GroupCollectorInterface
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC')
|
||||
->orderBy('transaction_journals.description', 'DESC')
|
||||
->orderBy('source.amount', 'DESC');
|
||||
->orderBy('source.amount', 'DESC')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1132,7 +1136,8 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transactions as source',
|
||||
static function (JoinClause $join): void {
|
||||
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('source.amount', '<', 0);
|
||||
->where('source.amount', '<', 0)
|
||||
;
|
||||
}
|
||||
)
|
||||
// join destination transaction
|
||||
@@ -1140,7 +1145,8 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transactions as destination',
|
||||
static function (JoinClause $join): void {
|
||||
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('destination.amount', '>', 0);
|
||||
->where('destination.amount', '>', 0)
|
||||
;
|
||||
}
|
||||
)
|
||||
// left join transaction type.
|
||||
@@ -1155,7 +1161,8 @@ class GroupCollector implements GroupCollectorInterface
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC')
|
||||
->orderBy('transaction_journals.description', 'DESC')
|
||||
->orderBy('source.amount', 'DESC');
|
||||
->orderBy('source.amount', 'DESC')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1166,13 +1173,12 @@ class GroupCollector implements GroupCollectorInterface
|
||||
// include source + destination account name and type.
|
||||
$this->withAccountInformation()
|
||||
// include category ID + name (if any)
|
||||
->withCategoryInformation()
|
||||
->withCategoryInformation()
|
||||
// include budget ID + name (if any)
|
||||
->withBudgetInformation()
|
||||
->withBudgetInformation()
|
||||
// include bill ID + name (if any)
|
||||
->withBillInformation();
|
||||
->withBillInformation();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -104,20 +104,20 @@ class InterestingMessage
|
||||
|
||||
// send message about newly created transaction group.
|
||||
/** @var null|TransactionGroup $group */
|
||||
$group = auth()->user()->transactionGroups()->with(['transactionJournals', 'transactionJournals.transactionType'])->find((int)$transactionGroupId);
|
||||
$group = auth()->user()->transactionGroups()->with(['transactionJournals', 'transactionJournals.transactionType'])->find((int)$transactionGroupId);
|
||||
|
||||
if (null === $group) {
|
||||
return;
|
||||
}
|
||||
|
||||
$count = $group->transactionJournals->count();
|
||||
$count = $group->transactionJournals->count();
|
||||
|
||||
/** @var null|TransactionJournal $journal */
|
||||
$journal = $group->transactionJournals->first();
|
||||
$journal = $group->transactionJournals->first();
|
||||
if (null === $journal) {
|
||||
return;
|
||||
}
|
||||
$title = $count > 1 ? $group->title : $journal->description;
|
||||
$title = $count > 1 ? $group->title : $journal->description;
|
||||
if ('created' === $message) {
|
||||
session()->flash('success_url', route('transactions.show', [$transactionGroupId]));
|
||||
session()->flash('success', (string)trans('firefly.stored_journal', ['description' => $title]));
|
||||
@@ -150,7 +150,7 @@ class InterestingMessage
|
||||
$message = $request->get('message');
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$user = auth()->user();
|
||||
|
||||
/** @var null|UserGroup $userGroup */
|
||||
$userGroup = UserGroup::find($userGroupId);
|
||||
@@ -197,10 +197,10 @@ class InterestingMessage
|
||||
$message = $request->get('message');
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$user = auth()->user();
|
||||
|
||||
/** @var null|Account $account */
|
||||
$account = $user->accounts()->withTrashed()->find($accountId);
|
||||
$account = $user->accounts()->withTrashed()->find($accountId);
|
||||
|
||||
if (null === $account) {
|
||||
return;
|
||||
@@ -232,7 +232,7 @@ class InterestingMessage
|
||||
$message = $request->get('message');
|
||||
|
||||
/** @var null|Bill $bill */
|
||||
$bill = auth()->user()->bills()->withTrashed()->find($billId);
|
||||
$bill = auth()->user()->bills()->withTrashed()->find($billId);
|
||||
|
||||
if (null === $bill) {
|
||||
return;
|
||||
@@ -261,7 +261,7 @@ class InterestingMessage
|
||||
$message = $request->get('message');
|
||||
|
||||
/** @var null|Webhook $webhook */
|
||||
$webhook = auth()->user()->webhooks()->withTrashed()->find($webhookId);
|
||||
$webhook = auth()->user()->webhooks()->withTrashed()->find($webhookId);
|
||||
|
||||
if (null === $webhook) {
|
||||
return;
|
||||
@@ -290,8 +290,9 @@ class InterestingMessage
|
||||
{
|
||||
// params:
|
||||
// get parameters from request.
|
||||
$code = (string) $request->get('code');
|
||||
$message = (string) $request->get('message');
|
||||
$code = (string) $request->get('code');
|
||||
$message = (string) $request->get('message');
|
||||
|
||||
try {
|
||||
$currency = Amount::getTransactionCurrencyByCode($code);
|
||||
} catch (FireflyException) {
|
||||
|
||||
@@ -107,7 +107,7 @@ class PiggyBankStoreRequest extends FormRequest
|
||||
if ($accountCurrency->id !== $currency->id && 'true' !== $isMultiCurrency) {
|
||||
$validator->errors()->add('accounts', trans('validation.invalid_account_currency'));
|
||||
}
|
||||
$type = $account->accountType->type;
|
||||
$type = $account->accountType->type;
|
||||
if (!in_array($type, $types, true)) {
|
||||
$validator->errors()->add('accounts', trans('validation.invalid_account_type'));
|
||||
}
|
||||
@@ -125,6 +125,7 @@ class PiggyBankStoreRequest extends FormRequest
|
||||
private function getCurrencyFromData(array $data): TransactionCurrency
|
||||
{
|
||||
$currencyId = (int)($data['transaction_currency_id'] ?? 0);
|
||||
|
||||
try {
|
||||
$currency = Amount::getTransactionCurrencyById($currencyId);
|
||||
} catch (FireflyException) {
|
||||
|
||||
@@ -130,6 +130,7 @@ class PiggyBankUpdateRequest extends FormRequest
|
||||
private function getCurrencyFromData(array $data): TransactionCurrency
|
||||
{
|
||||
$currencyId = (int) ($data['transaction_currency_id'] ?? 0);
|
||||
|
||||
try {
|
||||
$currency = Amount::getTransactionCurrencyById($currencyId);
|
||||
} catch (FireflyException) {
|
||||
|
||||
@@ -71,9 +71,9 @@ trait ModifiesPiggyBanks
|
||||
$pivot->native_current_amount = null;
|
||||
|
||||
// also update native_current_amount.
|
||||
$userCurrency = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup);
|
||||
$userCurrency = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup);
|
||||
if ($userCurrency->id !== $piggyBank->transaction_currency_id) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setIgnoreSettings(true);
|
||||
$pivot->native_current_amount = $converter->convert($piggyBank->transactionCurrency, $userCurrency, today(), $pivot->current_amount);
|
||||
}
|
||||
@@ -94,9 +94,9 @@ trait ModifiesPiggyBanks
|
||||
$pivot->native_current_amount = null;
|
||||
|
||||
// also update native_current_amount.
|
||||
$userCurrency = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup);
|
||||
$userCurrency = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup);
|
||||
if ($userCurrency->id !== $piggyBank->transaction_currency_id) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setIgnoreSettings(true);
|
||||
$pivot->native_current_amount = $converter->convert($piggyBank->transactionCurrency, $userCurrency, today(), $pivot->current_amount);
|
||||
}
|
||||
@@ -128,8 +128,8 @@ trait ModifiesPiggyBanks
|
||||
Log::debug(sprintf('Maximum amount: %s', $maxAmount));
|
||||
}
|
||||
|
||||
$compare = bccomp($amount, $maxAmount);
|
||||
$result = $compare <= 0;
|
||||
$compare = bccomp($amount, $maxAmount);
|
||||
$result = $compare <= 0;
|
||||
|
||||
Log::debug(sprintf('Compare <= 0? %d, so canAddAmount is %s', $compare, var_export($result, true)));
|
||||
|
||||
@@ -163,11 +163,11 @@ trait ModifiesPiggyBanks
|
||||
|
||||
public function setCurrentAmount(PiggyBank $piggyBank, string $amount): PiggyBank
|
||||
{
|
||||
$repetition = $this->getRepetition($piggyBank);
|
||||
$repetition = $this->getRepetition($piggyBank);
|
||||
if (null === $repetition) {
|
||||
return $piggyBank;
|
||||
}
|
||||
$max = $piggyBank->target_amount;
|
||||
$max = $piggyBank->target_amount;
|
||||
if (1 === bccomp($amount, $max) && 0 !== bccomp($piggyBank->target_amount, '0')) {
|
||||
$amount = $max;
|
||||
}
|
||||
@@ -210,14 +210,14 @@ trait ModifiesPiggyBanks
|
||||
|
||||
public function update(PiggyBank $piggyBank, array $data): PiggyBank
|
||||
{
|
||||
$piggyBank = $this->updateProperties($piggyBank, $data);
|
||||
$piggyBank = $this->updateProperties($piggyBank, $data);
|
||||
if (array_key_exists('notes', $data)) {
|
||||
$this->updateNote($piggyBank, (string)$data['notes']);
|
||||
}
|
||||
|
||||
// update the order of the piggy bank:
|
||||
$oldOrder = $piggyBank->order;
|
||||
$newOrder = (int)($data['order'] ?? $oldOrder);
|
||||
$oldOrder = $piggyBank->order;
|
||||
$newOrder = (int)($data['order'] ?? $oldOrder);
|
||||
if ($oldOrder !== $newOrder) {
|
||||
$this->setOrder($piggyBank, $newOrder);
|
||||
}
|
||||
@@ -318,7 +318,7 @@ trait ModifiesPiggyBanks
|
||||
|
||||
return;
|
||||
}
|
||||
$dbNote = $piggyBank->notes()->first();
|
||||
$dbNote = $piggyBank->notes()->first();
|
||||
if (null === $dbNote) {
|
||||
$dbNote = new Note();
|
||||
$dbNote->noteable()->associate($piggyBank);
|
||||
@@ -329,15 +329,16 @@ trait ModifiesPiggyBanks
|
||||
|
||||
public function setOrder(PiggyBank $piggyBank, int $newOrder): bool
|
||||
{
|
||||
$oldOrder = $piggyBank->order;
|
||||
$oldOrder = $piggyBank->order;
|
||||
// Log::debug(sprintf('Will move piggy bank #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder));
|
||||
if ($newOrder > $oldOrder) {
|
||||
PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
|
||||
->where('accounts.user_id', $this->user->id)
|
||||
->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder)
|
||||
->where('piggy_banks.id', '!=', $piggyBank->id)
|
||||
->distinct()->decrement('piggy_banks.order');
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
|
||||
->where('accounts.user_id', $this->user->id)
|
||||
->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder)
|
||||
->where('piggy_banks.id', '!=', $piggyBank->id)
|
||||
->distinct()->decrement('piggy_banks.order')
|
||||
;
|
||||
|
||||
$piggyBank->order = $newOrder;
|
||||
Log::debug(sprintf('[1] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder));
|
||||
@@ -346,11 +347,12 @@ trait ModifiesPiggyBanks
|
||||
return true;
|
||||
}
|
||||
PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
|
||||
->where('accounts.user_id', $this->user->id)
|
||||
->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder)
|
||||
->where('piggy_banks.id', '!=', $piggyBank->id)
|
||||
->distinct()->increment('piggy_banks.order');
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
|
||||
->where('accounts.user_id', $this->user->id)
|
||||
->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder)
|
||||
->where('piggy_banks.id', '!=', $piggyBank->id)
|
||||
->distinct()->increment('piggy_banks.order')
|
||||
;
|
||||
|
||||
$piggyBank->order = $newOrder;
|
||||
Log::debug(sprintf('[2] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder));
|
||||
@@ -371,7 +373,7 @@ trait ModifiesPiggyBanks
|
||||
}
|
||||
// if this account contains less than the amount, remove the current amount, update the amount and continue.
|
||||
$this->removeAmount($piggyBank, $account, $current);
|
||||
$amount = bcsub($amount, (string)$current);
|
||||
$amount = bcsub($amount, (string)$current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ use FireflyIII\Models\Location;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionJournalLink;
|
||||
|
||||
@@ -60,15 +60,15 @@ class Amount
|
||||
*/
|
||||
public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string
|
||||
{
|
||||
$locale = Steam::getLocale();
|
||||
$rounded = Steam::bcround($amount, $decimalPlaces);
|
||||
$locale = Steam::getLocale();
|
||||
$rounded = Steam::bcround($amount, $decimalPlaces);
|
||||
$coloured ??= true;
|
||||
|
||||
$fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY);
|
||||
$fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY);
|
||||
$fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol);
|
||||
$fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces);
|
||||
$fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces);
|
||||
$result = (string)$fmt->format((float)$rounded); // intentional float
|
||||
$result = (string)$fmt->format((float)$rounded); // intentional float
|
||||
|
||||
if (true === $coloured) {
|
||||
if (1 === bccomp($rounded, '0')) {
|
||||
@@ -120,8 +120,9 @@ class Amount
|
||||
{
|
||||
$instance = PreferencesSingleton::getInstance();
|
||||
$key = sprintf('transaction_currency_%d', $currencyId);
|
||||
/** @var TransactionCurrency|null $pref */
|
||||
$pref = $instance->getPreference($key);
|
||||
|
||||
/** @var null|TransactionCurrency $pref */
|
||||
$pref = $instance->getPreference($key);
|
||||
if (null !== $pref) {
|
||||
return $pref;
|
||||
}
|
||||
@@ -129,9 +130,11 @@ class Amount
|
||||
if (null === $currency) {
|
||||
$message = sprintf('Could not find a transaction currency with ID #%d', $currencyId);
|
||||
Log::error($message);
|
||||
|
||||
throw new FireflyException($message);
|
||||
}
|
||||
$instance->setPreference($key, $currency);
|
||||
|
||||
return $currency;
|
||||
}
|
||||
|
||||
@@ -139,8 +142,9 @@ class Amount
|
||||
{
|
||||
$instance = PreferencesSingleton::getInstance();
|
||||
$key = sprintf('transaction_currency_%s', $code);
|
||||
/** @var TransactionCurrency|null $pref */
|
||||
$pref = $instance->getPreference($key);
|
||||
|
||||
/** @var null|TransactionCurrency $pref */
|
||||
$pref = $instance->getPreference($key);
|
||||
if (null !== $pref) {
|
||||
return $pref;
|
||||
}
|
||||
@@ -148,9 +152,11 @@ class Amount
|
||||
if (null === $currency) {
|
||||
$message = sprintf('Could not find a transaction currency with code "%s"', $code);
|
||||
Log::error($message);
|
||||
|
||||
throw new FireflyException($message);
|
||||
}
|
||||
$instance->setPreference($key, $currency);
|
||||
|
||||
return $currency;
|
||||
}
|
||||
|
||||
@@ -168,8 +174,8 @@ class Amount
|
||||
|
||||
return $pref;
|
||||
}
|
||||
$key = sprintf('convert_to_primary_%d', $user->id);
|
||||
$pref = $instance->getPreference($key);
|
||||
$key = sprintf('convert_to_primary_%d', $user->id);
|
||||
$pref = $instance->getPreference($key);
|
||||
if (null === $pref) {
|
||||
$res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled');
|
||||
$instance->setPreference($key, $res);
|
||||
@@ -195,7 +201,7 @@ class Amount
|
||||
|
||||
public function getPrimaryCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency
|
||||
{
|
||||
$cache = new CacheProperties();
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty('getPrimaryCurrencyByGroup');
|
||||
$cache->addProperty($userGroup->id);
|
||||
if ($cache->has()) {
|
||||
@@ -225,16 +231,16 @@ class Amount
|
||||
*/
|
||||
public function getAmountFromJournalObject(TransactionJournal $journal): string
|
||||
{
|
||||
$convertToPrimary = $this->convertToPrimary();
|
||||
$currency = $this->getPrimaryCurrency();
|
||||
$field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount';
|
||||
$convertToPrimary = $this->convertToPrimary();
|
||||
$currency = $this->getPrimaryCurrency();
|
||||
$field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount';
|
||||
|
||||
/** @var null|Transaction $sourceTransaction */
|
||||
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
|
||||
if (null === $sourceTransaction) {
|
||||
return '0';
|
||||
}
|
||||
$amount = $sourceTransaction->{$field} ?? '0';
|
||||
$amount = $sourceTransaction->{$field} ?? '0';
|
||||
if ((int)$sourceTransaction->foreign_currency_id === $currency->id) {
|
||||
// use foreign amount instead!
|
||||
$amount = (string)$sourceTransaction->foreign_amount; // hard coded to be foreign amount.
|
||||
@@ -282,20 +288,20 @@ class Amount
|
||||
private function getLocaleInfo(): array
|
||||
{
|
||||
// get config from preference, not from translation:
|
||||
$locale = Steam::getLocale();
|
||||
$array = Steam::getLocaleArray($locale);
|
||||
$locale = Steam::getLocale();
|
||||
$array = Steam::getLocaleArray($locale);
|
||||
|
||||
setlocale(LC_MONETARY, $array);
|
||||
$info = localeconv();
|
||||
$info = localeconv();
|
||||
|
||||
// correct variables
|
||||
$info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes');
|
||||
$info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes');
|
||||
$info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes');
|
||||
$info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes');
|
||||
|
||||
$info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space');
|
||||
$info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space');
|
||||
$info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space');
|
||||
$info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space');
|
||||
|
||||
$fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY);
|
||||
$fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY);
|
||||
|
||||
$info['mon_decimal_point'] = $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL);
|
||||
$info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL);
|
||||
@@ -327,11 +333,11 @@ class Amount
|
||||
|
||||
// there are five possible positions for the "+" or "-" sign (if it is even used)
|
||||
// pos_a and pos_e could be the ( and ) symbol.
|
||||
$posA = ''; // before everything
|
||||
$posB = ''; // before currency symbol
|
||||
$posC = ''; // after currency symbol
|
||||
$posD = ''; // before amount
|
||||
$posE = ''; // after everything
|
||||
$posA = ''; // before everything
|
||||
$posB = ''; // before currency symbol
|
||||
$posC = ''; // after currency symbol
|
||||
$posD = ''; // before amount
|
||||
$posE = ''; // after everything
|
||||
|
||||
// format would be (currency before amount)
|
||||
// AB%sC_D%vE
|
||||
@@ -373,9 +379,9 @@ class Amount
|
||||
}
|
||||
|
||||
if ($csPrecedes) {
|
||||
return $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE;
|
||||
return $posA.$posB.'%s'.$posC.$space.$posD.'%v'.$posE;
|
||||
}
|
||||
|
||||
return $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE;
|
||||
return $posA.$posD.'%v'.$space.$posB.'%s'.$posC.$posE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,10 @@ class CurrencyCode implements BinderInterface
|
||||
if (auth()->check()) {
|
||||
try {
|
||||
$currency = Amount::getTransactionCurrencyByCode(trim($value));
|
||||
} catch(FireflyException) {
|
||||
} catch (FireflyException) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
return $currency;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,14 +72,15 @@ class AccountBalanceCalculator
|
||||
$balances = [];
|
||||
$count = 0;
|
||||
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
// this order is the same as GroupCollector, but in the exact reverse.
|
||||
->orderBy('transaction_journals.date', 'asc')
|
||||
->orderBy('transaction_journals.order', 'desc')
|
||||
->orderBy('transaction_journals.id', 'asc')
|
||||
->orderBy('transaction_journals.description', 'asc')
|
||||
->orderBy('transactions.amount', 'asc');
|
||||
->orderBy('transaction_journals.date', 'asc')
|
||||
->orderBy('transaction_journals.order', 'desc')
|
||||
->orderBy('transaction_journals.id', 'asc')
|
||||
->orderBy('transaction_journals.description', 'asc')
|
||||
->orderBy('transactions.amount', 'asc')
|
||||
;
|
||||
if ($accounts->count() > 0) {
|
||||
$query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray());
|
||||
}
|
||||
@@ -88,7 +89,7 @@ class AccountBalanceCalculator
|
||||
$query->where('transaction_journals.date', '>=', $notBefore);
|
||||
}
|
||||
|
||||
$set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']);
|
||||
$set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']);
|
||||
Log::debug(sprintf('Counted %d transaction(s)', $set->count()));
|
||||
|
||||
// the balance value is an array.
|
||||
@@ -101,8 +102,8 @@ class AccountBalanceCalculator
|
||||
$balances[$entry->account_id][$entry->transaction_currency_id] ??= [$this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore), null];
|
||||
|
||||
// before and after are easy:
|
||||
$before = $balances[$entry->account_id][$entry->transaction_currency_id][0];
|
||||
$after = bcadd($before, (string)$entry->amount);
|
||||
$before = $balances[$entry->account_id][$entry->transaction_currency_id][0];
|
||||
$after = bcadd($before, (string)$entry->amount);
|
||||
if (true === $entry->balance_dirty || $accounts->count() > 0) {
|
||||
// update the transaction:
|
||||
$entry->balance_before = $before;
|
||||
@@ -128,17 +129,18 @@ class AccountBalanceCalculator
|
||||
return '0';
|
||||
}
|
||||
Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d')));
|
||||
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->where('transaction_journals.transaction_currency_id', $currencyId)
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->where('transaction_journals.transaction_currency_id', $currencyId)
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
// this order is the same as GroupCollector
|
||||
->orderBy('transaction_journals.date', 'DESC')
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC')
|
||||
->orderBy('transaction_journals.description', 'DESC')
|
||||
->orderBy('transactions.amount', 'DESC')
|
||||
->where('transactions.account_id', $accountId);
|
||||
->orderBy('transaction_journals.date', 'DESC')
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC')
|
||||
->orderBy('transaction_journals.description', 'DESC')
|
||||
->orderBy('transactions.amount', 'DESC')
|
||||
->where('transactions.account_id', $accountId)
|
||||
;
|
||||
$notBefore->startOfDay();
|
||||
$query->where('transaction_journals.date', '<', $notBefore);
|
||||
|
||||
@@ -152,7 +154,7 @@ class AccountBalanceCalculator
|
||||
private function storeAccountBalances(array $balances): void
|
||||
{
|
||||
/**
|
||||
* @var int $accountId
|
||||
* @var int $accountId
|
||||
* @var array $currencies
|
||||
*/
|
||||
foreach ($balances as $accountId => $currencies) {
|
||||
@@ -165,7 +167,7 @@ class AccountBalanceCalculator
|
||||
}
|
||||
|
||||
/**
|
||||
* @var int $currencyId
|
||||
* @var int $currencyId
|
||||
* @var array $balance
|
||||
*/
|
||||
foreach ($currencies as $currencyId => $balance) {
|
||||
@@ -198,9 +200,9 @@ class AccountBalanceCalculator
|
||||
public static function recalculateForJournal(TransactionJournal $transactionJournal): void
|
||||
{
|
||||
Log::debug(__METHOD__);
|
||||
$object = new self();
|
||||
$object = new self();
|
||||
|
||||
$set = [];
|
||||
$set = [];
|
||||
foreach ($transactionJournal->transactions as $transaction) {
|
||||
$set[$transaction->account_id] = $transaction->account;
|
||||
}
|
||||
|
||||
@@ -524,6 +524,7 @@ class Steam
|
||||
$singleton = PreferencesSingleton::getInstance();
|
||||
foreach ($others as $key => $amount) {
|
||||
$preference = $singleton->getPreference($key);
|
||||
|
||||
try {
|
||||
$currency = $preference ?? Amount::getTransactionCurrencyByCode($key);
|
||||
} catch (FireflyException) {
|
||||
|
||||
@@ -164,7 +164,7 @@ class AmountFormat extends AbstractExtension
|
||||
|
||||
try {
|
||||
$currency = Amount::getTransactionCurrencyByCode($code);
|
||||
} catch(FireflyException) {
|
||||
} catch (FireflyException) {
|
||||
Log::error(sprintf('Could not find currency with code "%s". Fallback to primary currency.', $code));
|
||||
$currency = Amount::getPrimaryCurrency();
|
||||
Log::error(sprintf('Fallback currency is "%s".', $currency->code));
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace FireflyIII\Support\Twig;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
|
||||
Reference in New Issue
Block a user