Reformat various code.

This commit is contained in:
James Cole 2022-03-29 14:59:58 +02:00
parent 29bed2547c
commit d1a09ff33b
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
115 changed files with 2700 additions and 2699 deletions

View File

@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Jobs;
use Carbon\Carbon;
use FireflyIII\Events\RequestedReportOnJournals;
use FireflyIII\Events\WarnUserAboutBill;
use FireflyIII\Models\Bill;
use Illuminate\Bus\Queueable;

View File

@ -34,8 +34,8 @@ class BillWarningMail extends Mailable
use Queueable, SerializesModels;
public Bill $bill;
public string $field;
public int $diff;
public string $field;
/**
* ConfirmEmailChangeMail constructor.

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Mail;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
@ -60,7 +61,7 @@ class NewIPAddressWarningMail extends Mailable
$this->host = '';
try {
$hostName = gethostbyaddr($this->ipAddress);
} catch(\Exception $e) {
} catch (Exception $e) {
$hostName = $this->ipAddress;
}
if ($hostName !== $this->ipAddress) {

View File

@ -49,34 +49,34 @@ use Illuminate\Support\Carbon;
*/
class AccountType extends Model
{
/** @var string */
public const DEFAULT = 'Default account';
/** @var string */
public const CASH = 'Cash account';
/** @var string */
public const ASSET = 'Asset account';
/** @var string */
public const EXPENSE = 'Expense account';
/** @var string */
public const REVENUE = 'Revenue account';
/** @var string */
public const INITIAL_BALANCE = 'Initial balance account';
/** @var string */
public const BENEFICIARY = 'Beneficiary account';
/** @var string */
public const IMPORT = 'Import account';
/** @var string */
public const RECONCILIATION = 'Reconciliation account';
/** @var string */
public const LOAN = 'Loan';
/** @var string */
public const DEBT = 'Debt';
/** @var string */
public const MORTGAGE = 'Mortgage';
public const CASH = 'Cash account';
/** @var string */
public const CREDITCARD = 'Credit card';
/** @var string */
public const DEBT = 'Debt';
/** @var string */
public const DEFAULT = 'Default account';
/** @var string */
public const EXPENSE = 'Expense account';
/** @var string */
public const IMPORT = 'Import account';
/** @var string */
public const INITIAL_BALANCE = 'Initial balance account';
/** @var string */
public const LIABILITY_CREDIT = 'Liability credit account';
/** @var string */
public const LOAN = 'Loan';
/** @var string */
public const MORTGAGE = 'Mortgage';
/** @var string */
public const RECONCILIATION = 'Reconciliation account';
/** @var string */
public const REVENUE = 'Revenue account';
/**
* The attributes that should be casted to native types.
*

View File

@ -77,7 +77,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @property string $email
* @property int|null $user_group_id
* @method static \Illuminate\Database\Eloquent\Builder|Budget whereUserGroupId($value)
* @property-read Collection|\FireflyIII\Models\Note[] $notes
* @property-read Collection|Note[] $notes
* @property-read int|null $notes_count
*/
class Budget extends Model

View File

@ -58,20 +58,20 @@ class TransactionType extends Model
{
use SoftDeletes;
/** @var string */
public const WITHDRAWAL = 'Withdrawal';
/** @var string */
public const DEPOSIT = 'Deposit';
/** @var string */
public const TRANSFER = 'Transfer';
public const INVALID = 'Invalid';
/** @var string */
public const LIABILITY_CREDIT = 'Liability credit';
/** @var string */
public const OPENING_BALANCE = 'Opening balance';
/** @var string */
public const RECONCILIATION = 'Reconciliation';
/** @var string */
public const INVALID = 'Invalid';
public const TRANSFER = 'Transfer';
/** @var string */
public const LIABILITY_CREDIT = 'Liability credit';
public const WITHDRAWAL = 'Withdrawal';
/** @var string[] */
protected $casts
= [

View File

@ -53,14 +53,14 @@ use Illuminate\Support\Carbon;
*/
class UserRole extends Model
{
public const READ_ONLY = 'ro';
public const CHANGE_TRANSACTIONS = 'change_tx';
public const CHANGE_RULES = 'change_rules';
public const CHANGE_PIGGY_BANKS = 'change_piggies';
public const CHANGE_REPETITIONS = 'change_reps';
public const VIEW_REPORTS = 'view_reports';
public const CHANGE_RULES = 'change_rules';
public const CHANGE_TRANSACTIONS = 'change_tx';
public const FULL = 'full';
public const OWNER = 'owner';
public const READ_ONLY = 'ro';
public const VIEW_REPORTS = 'view_reports';
protected $fillable = ['title'];
/**

View File

@ -80,17 +80,17 @@ class Webhook extends Model
// dont forget to update the config in firefly.php
// triggers
public const TRIGGER_STORE_TRANSACTION = 100;
public const TRIGGER_UPDATE_TRANSACTION = 110;
public const TRIGGER_DESTROY_TRANSACTION = 120;
// actions
public const RESPONSE_TRANSACTIONS = 200;
public const DELIVERY_JSON = 300;
public const RESPONSE_ACCOUNTS = 210;
public const RESPONSE_NONE = 220;
// actions
public const RESPONSE_TRANSACTIONS = 200;
public const TRIGGER_DESTROY_TRANSACTION = 120;
public const TRIGGER_STORE_TRANSACTION = 100;
// delivery
public const DELIVERY_JSON = 300;
public const TRIGGER_UPDATE_TRANSACTION = 110;
protected $casts
= [
'active' => 'boolean',

View File

@ -48,7 +48,6 @@ use Laravel\Passport\Client;
use Laravel\Passport\Events\AccessTokenCreated;
use Log;
use Mail;
use Request;
use Session;
/**

View File

@ -198,21 +198,6 @@ class AccountRepository implements AccountRepositoryInterface
return $account;
}
/**
* @param Account $account
*
* @return TransactionCurrency|null
*/
public function getAccountCurrency(Account $account): ?TransactionCurrency
{
$currencyId = (int)$this->getMetaValue($account, 'currency_id');
if ($currencyId > 0) {
return TransactionCurrency::find($currencyId);
}
return null;
}
/**
* Return account type or null if not found.
*
@ -244,38 +229,6 @@ class AccountRepository implements AccountRepositoryInterface
return $query->get(['accounts.*']);
}
/**
* @param array $types
* @param array|null $sort
*
* @return Collection
*/
public function getAccountsByType(array $types, ?array $sort = []): Collection
{
$res = array_intersect([AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], $types);
$query = $this->user->accounts();
if (!empty($types)) {
$query->accountTypeIn($types);
}
// add sort parameters. At this point they're filtered to allowed fields to sort by:
if (!empty($sort)) {
foreach ($sort as $param) {
$query->orderBy($param[0], $param[1]);
}
}
if (empty($sort)) {
if (!empty($res)) {
$query->orderBy('accounts.order', 'ASC');
}
$query->orderBy('accounts.active', 'DESC');
$query->orderBy('accounts.name', 'ASC');
}
return $query->get(['accounts.*']);
}
/**
* @param array $types
*
@ -384,31 +337,6 @@ class AccountRepository implements AccountRepositoryInterface
return $account->locations()->first();
}
/**
* Return meta value for account. Null if not found.
*
* @param Account $account
* @param string $field
*
* @return null|string
*/
public function getMetaValue(Account $account, string $field): ?string
{
$result = $account->accountMeta->filter(
function (AccountMeta $meta) use ($field) {
return strtolower($meta->name) === strtolower($field);
}
);
if (0 === $result->count()) {
return null;
}
if (1 === $result->count()) {
return (string)$result->first()->data;
}
return null;
}
/**
* Get note text or null.
*
@ -427,20 +355,6 @@ class AccountRepository implements AccountRepositoryInterface
return $note->text;
}
/**
* @param Account $account
*
* @return TransactionJournal|null
*/
public function getOpeningBalance(Account $account): ?TransactionJournal
{
return TransactionJournal
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id)
->transactionTypes([TransactionType::OPENING_BALANCE])
->first(['transaction_journals.*']);
}
/**
* Returns the amount of the opening balance for this account.
*
@ -498,6 +412,20 @@ class AccountRepository implements AccountRepositoryInterface
return $journal?->transactionGroup;
}
/**
* @param Account $account
*
* @return TransactionJournal|null
*/
public function getOpeningBalance(Account $account): ?TransactionJournal
{
return TransactionJournal
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id)
->transactionTypes([TransactionType::OPENING_BALANCE])
->first(['transaction_journals.*']);
}
/**
* @param Account $account
*
@ -550,6 +478,46 @@ class AccountRepository implements AccountRepositoryInterface
return $factory->create($data);
}
/**
* @param Account $account
*
* @return TransactionCurrency|null
*/
public function getAccountCurrency(Account $account): ?TransactionCurrency
{
$currencyId = (int) $this->getMetaValue($account, 'currency_id');
if ($currencyId > 0) {
return TransactionCurrency::find($currencyId);
}
return null;
}
/**
* Return meta value for account. Null if not found.
*
* @param Account $account
* @param string $field
*
* @return null|string
*/
public function getMetaValue(Account $account, string $field): ?string
{
$result = $account->accountMeta->filter(
function (AccountMeta $meta) use ($field) {
return strtolower($meta->name) === strtolower($field);
}
);
if (0 === $result->count()) {
return null;
}
if (1 === $result->count()) {
return (string) $result->first()->data;
}
return null;
}
/**
* @inheritDoc
*/
@ -603,6 +571,52 @@ class AccountRepository implements AccountRepositoryInterface
return $order;
}
/**
* @param array $types
* @param array|null $sort
*
* @return Collection
*/
public function getAccountsByType(array $types, ?array $sort = []): Collection
{
$res = array_intersect([AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], $types);
$query = $this->user->accounts();
if (!empty($types)) {
$query->accountTypeIn($types);
}
// add sort parameters. At this point they're filtered to allowed fields to sort by:
if (!empty($sort)) {
foreach ($sort as $param) {
$query->orderBy($param[0], $param[1]);
}
}
if (empty($sort)) {
if (!empty($res)) {
$query->orderBy('accounts.order', 'ASC');
}
$query->orderBy('accounts.active', 'DESC');
$query->orderBy('accounts.name', 'ASC');
}
return $query->get(['accounts.*']);
}
/**
* Returns the date of the very first transaction in this account.
*
* @param Account $account
*
* @return Carbon|null
*/
public function oldestJournalDate(Account $account): ?Carbon
{
$journal = $this->oldestJournal($account);
return $journal?->date;
}
/**
* Returns the date of the very first transaction in this account.
*
@ -626,20 +640,6 @@ class AccountRepository implements AccountRepositoryInterface
return null;
}
/**
* Returns the date of the very first transaction in this account.
*
* @param Account $account
*
* @return Carbon|null
*/
public function oldestJournalDate(Account $account): ?Carbon
{
$journal = $this->oldestJournal($account);
return $journal?->date;
}
/**
* @inheritDoc
*/

View File

@ -146,46 +146,6 @@ class AccountTasker implements AccountTaskerInterface
return $report;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): array
{
// get all incomes for the given accounts in the given period!
// also transfers!
// get all transactions:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setDestinationAccounts($accounts)->setRange($start, $end);
$collector->excludeSourceAccounts($accounts);
$collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])->withAccountInformation();
$report = $this->groupIncomeBySource($collector->getExtractedJournals());
// sort the result
// Obtain a list of columns
$sum = [];
foreach ($report['accounts'] as $accountId => $row) {
$sum[$accountId] = (float)$row['sum'];
}
array_multisort($sum, SORT_DESC, $report['accounts']);
return $report;
}
/**
* @param User $user
*/
public function setUser(User $user): void
{
$this->user = $user;
}
/**
* @param array $array
*
@ -247,6 +207,38 @@ class AccountTasker implements AccountTaskerInterface
return $report;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): array
{
// get all incomes for the given accounts in the given period!
// also transfers!
// get all transactions:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setDestinationAccounts($accounts)->setRange($start, $end);
$collector->excludeSourceAccounts($accounts);
$collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])->withAccountInformation();
$report = $this->groupIncomeBySource($collector->getExtractedJournals());
// sort the result
// Obtain a list of columns
$sum = [];
foreach ($report['accounts'] as $accountId => $row) {
$sum[$accountId] = (float) $row['sum'];
}
array_multisort($sum, SORT_DESC, $report['accounts']);
return $report;
}
/**
* @param array $array
*
@ -306,4 +298,12 @@ class AccountTasker implements AccountTaskerInterface
return $report;
}
/**
* @param User $user
*/
public function setUser(User $user): void
{
$this->user = $user;
}
}

View File

@ -56,109 +56,6 @@ class OperationsRepository implements OperationsRepositoryInterface
return $this->sortByCurrency($journals, 'negative');
}
/**
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
* which have the specified accounts. It's grouped per currency, with as few details in the array
* as possible. Amounts are always positive.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection|null $accounts
*
* @return array
*/
public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array
{
$journals = $this->getTransactions($start, $end, $accounts, TransactionType::DEPOSIT);
return $this->sortByCurrency($journals, 'positive');
}
/**
* @param User $user
*/
public function setUser(User $user): void
{
$this->user = $user;
}
/**
* @inheritDoc
*/
public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByCurrency($journals, 'negative');
}
/**
* @inheritDoc
*/
public function sumExpensesByDestination(
Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByDirection($journals, 'destination', 'negative');
}
/**
* @inheritDoc
*/
public function sumExpensesBySource(
Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByDirection($journals, 'source', 'negative');
}
/**
* @inheritDoc
*/
public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByCurrency($journals, 'positive');
}
/**
* @inheritDoc
*/
public function sumIncomeByDestination(
Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByDirection($journals, 'destination', 'positive');
}
/**
* @inheritDoc
*/
public function sumIncomeBySource(
Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByDirection($journals, 'source', 'positive');
}
/**
* @inheritDoc
*/
public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array
{
$journals = $this->getTransactionsForSum(TransactionType::TRANSFER, $start, $end, $accounts, null, $currency);
return $this->groupByEither($journals);
}
/**
* Collect transactions with some parameters
*
@ -223,6 +120,45 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
* which have the specified accounts. It's grouped per currency, with as few details in the array
* as possible. Amounts are always positive.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection|null $accounts
*
* @return array
*/
public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array
{
$journals = $this->getTransactions($start, $end, $accounts, TransactionType::DEPOSIT);
return $this->sortByCurrency($journals, 'positive');
}
/**
* @param User $user
*/
public function setUser(User $user): void
{
$this->user = $user;
}
/**
* @inheritDoc
*/
public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null
): array
{
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByCurrency($journals, 'negative');
}
/**
* @param Carbon $start
* @param Carbon $end
@ -236,7 +172,8 @@ class OperationsRepository implements OperationsRepositoryInterface
private function getTransactionsForSum(
string $type, Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $opposing = null, ?TransactionCurrency $currency = null
): array {
): array
{
$start->startOfDay();
$end->endOfDay();
@ -343,6 +280,18 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* @inheritDoc
*/
public function sumExpensesByDestination(
Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null
): array
{
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByDirection($journals, 'destination', 'negative');
}
/**
* @param array $journals
* @param string $direction
@ -390,6 +339,63 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* @inheritDoc
*/
public function sumExpensesBySource(
Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null
): array
{
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByDirection($journals, 'source', 'negative');
}
/**
* @inheritDoc
*/
public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null
): array
{
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByCurrency($journals, 'positive');
}
/**
* @inheritDoc
*/
public function sumIncomeByDestination(
Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null
): array
{
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByDirection($journals, 'destination', 'positive');
}
/**
* @inheritDoc
*/
public function sumIncomeBySource(
Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null
): array
{
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByDirection($journals, 'source', 'positive');
}
/**
* @inheritDoc
*/
public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array
{
$journals = $this->getTransactionsForSum(TransactionType::TRANSFER, $start, $end, $accounts, null, $currency);
return $this->groupByEither($journals);
}
/**
* @param array $journals
*

View File

@ -33,7 +33,6 @@ use FireflyIII\User;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Log;
/**
* Class AttachmentRepository.

View File

@ -55,6 +55,36 @@ class BillRepository implements BillRepositoryInterface
private User $user;
/**
* @inheritDoc
*/
public function billEndsWith(string $query, int $limit): Collection
{
$search = $this->user->bills();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s', $query));
}
$search->orderBy('name', 'ASC')
->where('active', true);
return $search->take($limit)->get();
}
/**
* @inheritDoc
*/
public function billStartsWith(string $query, int $limit): Collection
{
$search = $this->user->bills();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%s%%', $query));
}
$search->orderBy('name', 'ASC')
->where('active', true);
return $search->take($limit)->get();
}
/**
* Correct order of piggies in case of issues.
*/
@ -95,18 +125,6 @@ class BillRepository implements BillRepositoryInterface
$this->user->bills()->delete();
}
/**
* Find a bill by ID.
*
* @param int $billId
*
* @return Bill|null
*/
public function find(int $billId): ?Bill
{
return $this->user->bills()->find($billId);
}
/**
* Find bill by parameters.
*
@ -138,6 +156,18 @@ class BillRepository implements BillRepositoryInterface
return null;
}
/**
* Find a bill by ID.
*
* @param int $billId
*
* @return Bill|null
*/
public function find(int $billId): ?Bill
{
return $this->user->bills()->find($billId);
}
/**
* Find a bill by name.
*
@ -150,17 +180,6 @@ class BillRepository implements BillRepositoryInterface
return $this->user->bills()->where('name', $name)->first(['bills.*']);
}
/**
* @return Collection
*/
public function getActiveBills(): Collection
{
return $this->user->bills()
->where('active', true)
->orderBy('bills.name', 'ASC')
->get(['bills.*', DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount'),]);
}
/**
* Get all attachments.
*
@ -258,6 +277,17 @@ class BillRepository implements BillRepositoryInterface
return $sum;
}
/**
* @return Collection
*/
public function getActiveBills(): Collection
{
return $this->user->bills()
->where('active', true)
->orderBy('bills.name', 'ASC')
->get(['bills.*', DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount'),]);
}
/**
* Get the total amount of money paid for the users active bills in the date range given,
* grouped per currency.
@ -320,6 +350,71 @@ class BillRepository implements BillRepositoryInterface
return $sum;
}
/**
* Between start and end, tells you on which date(s) the bill is expected to hit.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getPayDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection
{
$set = new Collection;
$currentStart = clone $start;
//Log::debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq));
//Log::debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d')));
while ($currentStart <= $end) {
//Log::debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d')));
$nextExpectedMatch = $this->nextDateMatch($bill, $currentStart);
//Log::debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d')));
if ($nextExpectedMatch > $end) {// If nextExpectedMatch is after end, we continue
break;
}
$set->push(clone $nextExpectedMatch);
//Log::debug(sprintf('Now %d dates in set.', $set->count()));
$nextExpectedMatch->addDay();
//Log::debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d')));
$currentStart = clone $nextExpectedMatch;
}
return $set;
}
/**
* Given a bill and a date, this method will tell you at which moment this bill expects its next
* transaction. Whether or not it is there already, is not relevant.
*
* @param Bill $bill
* @param Carbon $date
*
* @return Carbon
* @throws JsonException
*/
public function nextDateMatch(Bill $bill, Carbon $date): Carbon
{
$cache = new CacheProperties;
$cache->addProperty($bill->id);
$cache->addProperty('nextDateMatch');
$cache->addProperty($date);
if ($cache->has()) {
return $cache->get();
}
// find the most recent date for this bill NOT in the future. Cache this date:
$start = clone $bill->date;
while ($start < $date) {
$start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip);
}
$cache->store($start);
return $start;
}
/**
* Get the total amount of money due for the users active bills in the date range given.
*
@ -464,41 +559,6 @@ class BillRepository implements BillRepositoryInterface
);
}
/**
* Between start and end, tells you on which date(s) the bill is expected to hit.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getPayDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection
{
$set = new Collection;
$currentStart = clone $start;
//Log::debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq));
//Log::debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d')));
while ($currentStart <= $end) {
//Log::debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d')));
$nextExpectedMatch = $this->nextDateMatch($bill, $currentStart);
//Log::debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d')));
if ($nextExpectedMatch > $end) {// If nextExpectedMatch is after end, we continue
break;
}
$set->push(clone $nextExpectedMatch);
//Log::debug(sprintf('Now %d dates in set.', $set->count()));
$nextExpectedMatch->addDay();
//Log::debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d')));
$currentStart = clone $nextExpectedMatch;
}
return $set;
}
/**
* Return all rules for one bill
*
@ -616,36 +676,6 @@ class BillRepository implements BillRepositoryInterface
}
}
/**
* Given a bill and a date, this method will tell you at which moment this bill expects its next
* transaction. Whether or not it is there already, is not relevant.
*
* @param Bill $bill
* @param Carbon $date
*
* @return Carbon
* @throws JsonException
*/
public function nextDateMatch(Bill $bill, Carbon $date): Carbon
{
$cache = new CacheProperties;
$cache->addProperty($bill->id);
$cache->addProperty('nextDateMatch');
$cache->addProperty($date);
if ($cache->has()) {
return $cache->get();
}
// find the most recent date for this bill NOT in the future. Cache this date:
$start = clone $bill->date;
while ($start < $date) {
$start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip);
}
$cache->store($start);
return $start;
}
/**
* Given the date in $date, this method will return a moment in the future where the bill is expected to be paid.
*
@ -782,35 +812,4 @@ class BillRepository implements BillRepositoryInterface
return $service->update($bill, $data);
}
/**
* @inheritDoc
*/
public function billEndsWith(string $query, int $limit): Collection
{
$search = $this->user->bills();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s', $query));
}
$search->orderBy('name', 'ASC')
->where('active', true);
return $search->take($limit)->get();
}
/**
* @inheritDoc
*/
public function billStartsWith(string $query, int $limit): Collection
{
$search = $this->user->bills();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%s%%', $query));
}
$search->orderBy('name', 'ASC')
->where('active', true);
return $search->take($limit)->get();
}
}

View File

@ -35,11 +35,6 @@ use Illuminate\Support\Collection;
interface BillRepositoryInterface
{
/**
* Add correct order to bills.
*/
public function correctOrder(): void;
/**
* @param string $query
* @param int $limit
@ -56,6 +51,11 @@ interface BillRepositoryInterface
*/
public function billStartsWith(string $query, int $limit): Collection;
/**
* Add correct order to bills.
*/
public function correctOrder(): void;
/**
* @param Bill $bill
*

View File

@ -147,6 +147,22 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
->where('end_date', $end->format('Y-m-d'))->first();
}
/**
* @param TransactionCurrency $currency
* @param Carbon|null $start
* @param Carbon|null $end
*
* @return Collection
*/
public function getAllBudgetLimitsByCurrency(TransactionCurrency $currency, Carbon $start = null, Carbon $end = null): Collection
{
return $this->getAllBudgetLimits($start, $end)->filter(
static function (BudgetLimit $budgetLimit) use ($currency) {
return $budgetLimit->transaction_currency_id === $currency->id;
}
);
}
/**
* @param Carbon|null $start
* @param Carbon|null $end
@ -215,22 +231,6 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
)->get(['budget_limits.*']);
}
/**
* @param TransactionCurrency $currency
* @param Carbon|null $start
* @param Carbon|null $end
*
* @return Collection
*/
public function getAllBudgetLimitsByCurrency(TransactionCurrency $currency, Carbon $start = null, Carbon $end = null): Collection
{
return $this->getAllBudgetLimits($start, $end)->filter(
static function (BudgetLimit $budgetLimit) use ($currency) {
return $budgetLimit->transaction_currency_id === $currency->id;
}
);
}
/**
* @param Budget $budget
* @param Carbon|null $start

View File

@ -50,6 +50,36 @@ class BudgetRepository implements BudgetRepositoryInterface
{
private User $user;
/**
* @inheritDoc
*/
public function budgetEndsWith(string $query, int $limit): Collection
{
$search = $this->user->budgets();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s', $query));
}
$search->orderBy('order', 'ASC')
->orderBy('name', 'ASC')->where('active', true);
return $search->take($limit)->get();
}
/**
* @inheritDoc
*/
public function budgetStartsWith(string $query, int $limit): Collection
{
$search = $this->user->budgets();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%s%%', $query));
}
$search->orderBy('order', 'ASC')
->orderBy('name', 'ASC')->where('active', true);
return $search->take($limit)->get();
}
/**
* @return bool
*/
@ -76,6 +106,17 @@ class BudgetRepository implements BudgetRepositoryInterface
return true;
}
/**
* @return Collection
*/
public function getActiveBudgets(): Collection
{
return $this->user->budgets()->where('active', true)
->orderBy('order', 'ASC')
->orderBy('name', 'ASC')
->get();
}
/**
* @param Budget $budget
*
@ -106,6 +147,15 @@ class BudgetRepository implements BudgetRepositoryInterface
}
}
/**
* @return Collection
*/
public function getBudgets(): Collection
{
return $this->user->budgets()->orderBy('order', 'ASC')
->orderBy('name', 'ASC')->get();
}
/**
* @inheritDoc
*/
@ -117,18 +167,6 @@ class BudgetRepository implements BudgetRepositoryInterface
}
}
/**
* Find a budget or return NULL
*
* @param int|null $budgetId |null
*
* @return Budget|null
*/
public function find(int $budgetId = null): ?Budget
{
return $this->user->budgets()->find($budgetId);
}
/**
* @param int|null $budgetId
* @param string|null $budgetName
@ -152,6 +190,18 @@ class BudgetRepository implements BudgetRepositoryInterface
return $result;
}
/**
* Find a budget or return NULL
*
* @param int|null $budgetId |null
*
* @return Budget|null
*/
public function find(int $budgetId = null): ?Budget
{
return $this->user->budgets()->find($budgetId);
}
/**
* Find budget by name.
*
@ -187,17 +237,6 @@ class BudgetRepository implements BudgetRepositoryInterface
return null;
}
/**
* @return Collection
*/
public function getActiveBudgets(): Collection
{
return $this->user->budgets()->where('active', true)
->orderBy('order', 'ASC')
->orderBy('name', 'ASC')
->get();
}
/**
* @inheritDoc
*/
@ -219,23 +258,6 @@ class BudgetRepository implements BudgetRepositoryInterface
);
}
/**
* @inheritDoc
*/
public function getAutoBudget(Budget $budget): ?AutoBudget
{
return $budget->autoBudgets()->first();
}
/**
* @return Collection
*/
public function getBudgets(): Collection
{
return $this->user->budgets()->orderBy('order', 'ASC')
->orderBy('name', 'ASC')->get();
}
/**
* Get all budgets with these ID's.
*
@ -258,9 +280,17 @@ class BudgetRepository implements BudgetRepositoryInterface
->orderBy('name', 'ASC')->where('active', 0)->get();
}
public function getMaxOrder(): int
/**
* @inheritDoc
*/
public function getNoteText(Budget $budget): ?string
{
return (int)$this->user->budgets()->max('order');
$note = $budget->notes()->first();
if (null === $note) {
return null;
}
return $note->text;
}
/**
@ -387,6 +417,38 @@ class BudgetRepository implements BudgetRepositoryInterface
return $newBudget;
}
public function getMaxOrder(): int
{
return (int) $this->user->budgets()->max('order');
}
/**
* @param Budget $budget
* @param string $text
* @return void
*/
private function setNoteText(Budget $budget, string $text): void
{
$dbNote = $budget->notes()->first();
if ('' !== $text) {
if (null === $dbNote) {
$dbNote = new Note;
$dbNote->noteable()->associate($budget);
}
$dbNote->text = trim($text);
$dbNote->save();
return;
}
if (null !== $dbNote) {
try {
$dbNote->delete();
} catch (Exception $e) { // @phpstan-ignore-line
// @ignoreException
}
}
}
/**
* @param Budget $budget
* @param array $data
@ -476,6 +538,14 @@ class BudgetRepository implements BudgetRepositoryInterface
}
}
/**
* @inheritDoc
*/
public function getAutoBudget(Budget $budget): ?AutoBudget
{
return $budget->autoBudgets()->first();
}
/**
* @param Budget $budget
* @param array $data
@ -524,74 +594,4 @@ class BudgetRepository implements BudgetRepositoryInterface
$autoBudget->save();
}
/**
* @inheritDoc
*/
public function getNoteText(Budget $budget): ?string
{
$note = $budget->notes()->first();
if (null === $note) {
return null;
}
return $note->text;
}
/**
* @param Budget $budget
* @param string $text
* @return void
*/
private function setNoteText(Budget $budget, string $text): void
{
$dbNote = $budget->notes()->first();
if ('' !== $text) {
if (null === $dbNote) {
$dbNote = new Note;
$dbNote->noteable()->associate($budget);
}
$dbNote->text = trim($text);
$dbNote->save();
return;
}
if (null !== $dbNote) {
try {
$dbNote->delete();
} catch (Exception $e) { // @phpstan-ignore-line
// @ignoreException
}
}
}
/**
* @inheritDoc
*/
public function budgetEndsWith(string $query, int $limit): Collection
{
$search = $this->user->budgets();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s', $query));
}
$search->orderBy('order', 'ASC')
->orderBy('name', 'ASC')->where('active', true);
return $search->take($limit)->get();
}
/**
* @inheritDoc
*/
public function budgetStartsWith(string $query, int $limit): Collection
{
$search = $this->user->budgets();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%s%%', $query));
}
$search->orderBy('order', 'ASC')
->orderBy('name', 'ASC')->where('active', true);
return $search->take($limit)->get();
}
}

View File

@ -34,17 +34,27 @@ use Illuminate\Support\Collection;
*/
interface BudgetRepositoryInterface
{
/**
* @param string $query
* @param int $limit
*
* @return Collection
*/
public function budgetEndsWith(string $query, int $limit): Collection;
/**
* @param string $query
* @param int $limit
*
* @return Collection
*/
public function budgetStartsWith(string $query, int $limit): Collection;
/**
* @return bool
*/
public function cleanupBudgets(): bool;
/**
* @param Budget $budget
* @return string|null
*/
public function getNoteText(Budget $budget): ?string;
/**
* @param Budget $budget
*
@ -140,6 +150,12 @@ interface BudgetRepositoryInterface
*/
public function getMaxOrder(): int;
/**
* @param Budget $budget
* @return string|null
*/
public function getNoteText(Budget $budget): ?string;
/**
* @param string $query
* @param int $limit
@ -175,20 +191,4 @@ interface BudgetRepositoryInterface
*/
public function update(Budget $budget, array $data): Budget;
/**
* @param string $query
* @param int $limit
*
* @return Collection
*/
public function budgetEndsWith(string $query, int $limit): Collection;
/**
* @param string $query
* @param int $limit
*
* @return Collection
*/
public function budgetStartsWith(string $query, int $limit): Collection;
}

View File

@ -200,6 +200,19 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* @return Collection
*/
private function getBudgets(): Collection
{
/** @var BudgetRepositoryInterface $repos */
$repos = app(BudgetRepositoryInterface::class);
return $repos->getActiveBudgets();
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* @param User $user
*/
@ -208,8 +221,6 @@ class OperationsRepository implements OperationsRepositoryInterface
$this->user = $user;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* @param Collection $budgets
* @param Collection $accounts
@ -371,17 +382,6 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* @return Collection
*/
private function getBudgets(): Collection
{
/** @var BudgetRepositoryInterface $repos */
$repos = app(BudgetRepositoryInterface::class);
return $repos->getActiveBudgets();
}
/**
* For now, simply refer to whichever repository holds this function.
* See reference nr. 14

View File

@ -46,6 +46,32 @@ class CategoryRepository implements CategoryRepositoryInterface
{
private User $user;
/**
* @inheritDoc
*/
public function categoryEndsWith(string $query, int $limit): Collection
{
$search = $this->user->categories();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s', $query));
}
return $search->take($limit)->get();
}
/**
* @inheritDoc
*/
public function categoryStartsWith(string $query, int $limit): Collection
{
$search = $this->user->categories();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%s%%', $query));
}
return $search->take($limit)->get();
}
/**
* @param Category $category
*
@ -79,27 +105,13 @@ class CategoryRepository implements CategoryRepositoryInterface
}
/**
* Find a category or return NULL
* Returns a list of all the categories belonging to a user.
*
* @param int $categoryId
*
* @return Category|null
* @return Collection
*/
public function find(int $categoryId): ?Category
public function getCategories(): Collection
{
return $this->user->categories()->find($categoryId);
}
/**
* Find a category.
*
* @param string $name
*
* @return Category|null
*/
public function findByName(string $name): ?Category
{
return $this->user->categories()->where('name', $name)->first(['categories.*']);
return $this->user->categories()->with(['attachments'])->orderBy('name', 'ASC')->get();
}
/**
@ -130,6 +142,81 @@ class CategoryRepository implements CategoryRepositoryInterface
return $result;
}
/**
* Find a category or return NULL
*
* @param int $categoryId
*
* @return Category|null
*/
public function find(int $categoryId): ?Category
{
return $this->user->categories()->find($categoryId);
}
/**
* Find a category.
*
* @param string $name
*
* @return Category|null
*/
public function findByName(string $name): ?Category
{
return $this->user->categories()->where('name', $name)->first(['categories.*']);
}
/**
* @param array $data
*
* @return Category
* @throws FireflyException
*/
public function store(array $data): Category
{
/** @var CategoryFactory $factory */
$factory = app(CategoryFactory::class);
$factory->setUser($this->user);
$category = $factory->findOrCreate(null, $data['name']);
if (null === $category) {
throw new FireflyException(sprintf('400003: Could not store new category with name "%s"', $data['name']));
}
if (array_key_exists('notes', $data) && '' === $data['notes']) {
$this->removeNotes($category);
}
if (array_key_exists('notes', $data) && '' !== $data['notes']) {
$this->updateNotes($category, $data['notes']);
}
return $category;
}
/**
* @param Category $category
*/
public function removeNotes(Category $category): void
{
$category->notes()->delete();
}
/**
* @inheritDoc
*/
public function updateNotes(Category $category, string $notes): void
{
$dbNote = $category->notes()->first();
if (null === $dbNote) {
$dbNote = new Note;
$dbNote->noteable()->associate($category);
}
$dbNote->text = trim($notes);
$dbNote->save();
}
/**
* @param Category $category
*
@ -158,6 +245,43 @@ class CategoryRepository implements CategoryRepositoryInterface
return $firstJournalDate;
}
/**
* @param Category $category
*
* @return Carbon|null
*/
private function getFirstJournalDate(Category $category): ?Carbon
{
$query = $category->transactionJournals()->orderBy('date', 'ASC');
$result = $query->first(['transaction_journals.*']);
if (null !== $result) {
return $result->date;
}
return null;
}
/**
* @param Category $category
*
* @return Carbon|null
*/
private function getFirstTransactionDate(Category $category): ?Carbon
{
// check transactions:
$query = $category->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'ASC');
$lastTransaction = $query->first(['transaction_journals.*']);
if (null !== $lastTransaction) {
return new Carbon($lastTransaction->date);
}
return null;
}
/**
* @inheritDoc
*/
@ -191,16 +315,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $this->user->categories()->whereIn('id', $categoryIds)->get();
}
/**
* Returns a list of all the categories belonging to a user.
*
* @return Collection
*/
public function getCategories(): Collection
{
return $this->user->categories()->with(['attachments'])->orderBy('name', 'ASC')->get();
}
/**
* @inheritDoc
*/
@ -243,134 +357,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $lastJournalDate;
}
/**
* @param Category $category
*/
public function removeNotes(Category $category): void
{
$category->notes()->delete();
}
/**
* @param string $query
* @param int $limit
*
* @return Collection
*/
public function searchCategory(string $query, int $limit): Collection
{
$search = $this->user->categories();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
}
return $search->take($limit)->get();
}
/**
* @param User $user
*/
public function setUser(User $user): void
{
$this->user = $user;
}
/**
* @param array $data
*
* @return Category
* @throws FireflyException
*/
public function store(array $data): Category
{
/** @var CategoryFactory $factory */
$factory = app(CategoryFactory::class);
$factory->setUser($this->user);
$category = $factory->findOrCreate(null, $data['name']);
if (null === $category) {
throw new FireflyException(sprintf('400003: Could not store new category with name "%s"', $data['name']));
}
if (array_key_exists('notes', $data) && '' === $data['notes']) {
$this->removeNotes($category);
}
if (array_key_exists('notes', $data) && '' !== $data['notes']) {
$this->updateNotes($category, $data['notes']);
}
return $category;
}
/**
* @param Category $category
* @param array $data
*
* @return Category
* @throws Exception
*/
public function update(Category $category, array $data): Category
{
/** @var CategoryUpdateService $service */
$service = app(CategoryUpdateService::class);
$service->setUser($this->user);
return $service->update($category, $data);
}
/**
* @inheritDoc
*/
public function updateNotes(Category $category, string $notes): void
{
$dbNote = $category->notes()->first();
if (null === $dbNote) {
$dbNote = new Note;
$dbNote->noteable()->associate($category);
}
$dbNote->text = trim($notes);
$dbNote->save();
}
/**
* @param Category $category
*
* @return Carbon|null
*/
private function getFirstJournalDate(Category $category): ?Carbon
{
$query = $category->transactionJournals()->orderBy('date', 'ASC');
$result = $query->first(['transaction_journals.*']);
if (null !== $result) {
return $result->date;
}
return null;
}
/**
* @param Category $category
*
* @return Carbon|null
*/
private function getFirstTransactionDate(Category $category): ?Carbon
{
// check transactions:
$query = $category->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'ASC');
$lastTransaction = $query->first(['transaction_journals.*']);
if (null !== $lastTransaction) {
return new Carbon($lastTransaction->date);
}
return null;
}
/**
* @param Category $category
* @param Collection $accounts
@ -422,28 +408,42 @@ class CategoryRepository implements CategoryRepositoryInterface
}
/**
* @inheritDoc
* @param string $query
* @param int $limit
*
* @return Collection
*/
public function categoryEndsWith(string $query, int $limit): Collection
public function searchCategory(string $query, int $limit): Collection
{
$search = $this->user->categories();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s', $query));
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
}
return $search->take($limit)->get();
}
/**
* @inheritDoc
* @param User $user
*/
public function categoryStartsWith(string $query, int $limit): Collection
public function setUser(User $user): void
{
$search = $this->user->categories();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%s%%', $query));
$this->user = $user;
}
return $search->take($limit)->get();
/**
* @param Category $category
* @param array $data
*
* @return Category
* @throws Exception
*/
public function update(Category $category, array $data): Category
{
/** @var CategoryUpdateService $service */
$service = app(CategoryUpdateService::class);
$service->setUser($this->user);
return $service->update($category, $data);
}
}

View File

@ -34,6 +34,22 @@ use Illuminate\Support\Collection;
interface CategoryRepositoryInterface
{
/**
* @param string $query
* @param int $limit
*
* @return Collection
*/
public function categoryEndsWith(string $query, int $limit): Collection;
/**
* @param string $query
* @param int $limit
*
* @return Collection
*/
public function categoryStartsWith(string $query, int $limit): Collection;
/**
* @param Category $category
*
@ -134,22 +150,6 @@ interface CategoryRepositoryInterface
*/
public function searchCategory(string $query, int $limit): Collection;
/**
* @param string $query
* @param int $limit
*
* @return Collection
*/
public function categoryEndsWith(string $query, int $limit): Collection;
/**
* @param string $query
* @param int $limit
*
* @return Collection
*/
public function categoryStartsWith(string $query, int $limit): Collection;
/**
* @param User $user
*/

View File

@ -116,6 +116,16 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* Returns a list of all the categories belonging to a user.
*
* @return Collection
*/
private function getCategories(): Collection
{
return $this->user->categories()->get();
}
/**
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
* which have the specified category set to them. It's grouped per currency, with as few details in the array
@ -192,6 +202,138 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* @inheritDoc
*/
public function listTransferredIn(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])
->setDestinationAccounts($accounts)->excludeSourceAccounts($accounts);
if (null !== $categories && $categories->count() > 0) {
$collector->setCategories($categories);
}
if (null === $categories || (null !== $categories && 0 === $categories->count())) {
$collector->setCategories($this->getCategories());
}
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
$journals = $collector->getExtractedJournals();
$array = [];
foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id'];
$categoryId = (int) $journal['category_id'];
$categoryName = (string) $journal['category_name'];
// catch "no category" entries.
if (0 === $categoryId) {
continue;
}
// info about the currency:
$array[$currencyId] = $array[$currencyId] ?? [
'categories' => [],
'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'],
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_decimal_places' => $journal['currency_decimal_places'],
];
// info about the categories:
$array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [
'id' => $categoryId,
'name' => $categoryName,
'transaction_journals' => [],
];
// add journal to array:
// only a subset of the fields.
$journalId = (int) $journal['transaction_journal_id'];
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
'amount' => app('steam')->positive($journal['amount']),
'date' => $journal['date'],
'source_account_id' => $journal['source_account_id'],
'category_name' => $journal['category_name'],
'source_account_name' => $journal['source_account_name'],
'destination_account_id' => $journal['destination_account_id'],
'destination_account_name' => $journal['destination_account_name'],
'description' => $journal['description'],
'transaction_group_id' => $journal['transaction_group_id'],
];
}
return $array;
}
/**
* @inheritDoc
*/
public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])
->setSourceAccounts($accounts)->excludeDestinationAccounts($accounts);
if (null !== $categories && $categories->count() > 0) {
$collector->setCategories($categories);
}
if (null === $categories || (null !== $categories && 0 === $categories->count())) {
$collector->setCategories($this->getCategories());
}
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
$journals = $collector->getExtractedJournals();
$array = [];
foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id'];
$categoryId = (int) $journal['category_id'];
$categoryName = (string) $journal['category_name'];
// catch "no category" entries.
if (0 === $categoryId) {
continue;
}
// info about the currency:
$array[$currencyId] = $array[$currencyId] ?? [
'categories' => [],
'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'],
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_decimal_places' => $journal['currency_decimal_places'],
];
// info about the categories:
$array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [
'id' => $categoryId,
'name' => $categoryName,
'transaction_journals' => [],
];
// add journal to array:
// only a subset of the fields.
$journalId = (int) $journal['transaction_journal_id'];
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
'amount' => app('steam')->negative($journal['amount']),
'date' => $journal['date'],
'source_account_id' => $journal['source_account_id'],
'category_name' => $journal['category_name'],
'source_account_name' => $journal['source_account_name'],
'destination_account_id' => $journal['destination_account_id'],
'destination_account_name' => $journal['destination_account_name'],
'description' => $journal['description'],
'transaction_group_id' => $journal['transaction_group_id'],
];
}
return $array;
}
/**
* @param User $user
*/
@ -329,146 +471,4 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* Returns a list of all the categories belonging to a user.
*
* @return Collection
*/
private function getCategories(): Collection
{
return $this->user->categories()->get();
}
/**
* @inheritDoc
*/
public function listTransferredIn(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])
->setDestinationAccounts($accounts)->excludeSourceAccounts($accounts);
if (null !== $categories && $categories->count() > 0) {
$collector->setCategories($categories);
}
if (null === $categories || (null !== $categories && 0 === $categories->count())) {
$collector->setCategories($this->getCategories());
}
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
$journals = $collector->getExtractedJournals();
$array = [];
foreach ($journals as $journal) {
$currencyId = (int)$journal['currency_id'];
$categoryId = (int)$journal['category_id'];
$categoryName = (string)$journal['category_name'];
// catch "no category" entries.
if (0 === $categoryId) {
continue;
}
// info about the currency:
$array[$currencyId] = $array[$currencyId] ?? [
'categories' => [],
'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'],
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_decimal_places' => $journal['currency_decimal_places'],
];
// info about the categories:
$array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [
'id' => $categoryId,
'name' => $categoryName,
'transaction_journals' => [],
];
// add journal to array:
// only a subset of the fields.
$journalId = (int)$journal['transaction_journal_id'];
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
'amount' => app('steam')->positive($journal['amount']),
'date' => $journal['date'],
'source_account_id' => $journal['source_account_id'],
'category_name' => $journal['category_name'],
'source_account_name' => $journal['source_account_name'],
'destination_account_id' => $journal['destination_account_id'],
'destination_account_name' => $journal['destination_account_name'],
'description' => $journal['description'],
'transaction_group_id' => $journal['transaction_group_id'],
];
}
return $array;
}
/**
* @inheritDoc
*/
public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])
->setSourceAccounts($accounts)->excludeDestinationAccounts($accounts);
if (null !== $categories && $categories->count() > 0) {
$collector->setCategories($categories);
}
if (null === $categories || (null !== $categories && 0 === $categories->count())) {
$collector->setCategories($this->getCategories());
}
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
$journals = $collector->getExtractedJournals();
$array = [];
foreach ($journals as $journal) {
$currencyId = (int)$journal['currency_id'];
$categoryId = (int)$journal['category_id'];
$categoryName = (string)$journal['category_name'];
// catch "no category" entries.
if (0 === $categoryId) {
continue;
}
// info about the currency:
$array[$currencyId] = $array[$currencyId] ?? [
'categories' => [],
'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'],
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_decimal_places' => $journal['currency_decimal_places'],
];
// info about the categories:
$array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [
'id' => $categoryId,
'name' => $categoryName,
'transaction_journals' => [],
];
// add journal to array:
// only a subset of the fields.
$journalId = (int)$journal['transaction_journal_id'];
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
'amount' => app('steam')->negative($journal['amount']),
'date' => $journal['date'],
'source_account_id' => $journal['source_account_id'],
'category_name' => $journal['category_name'],
'source_account_name' => $journal['source_account_name'],
'destination_account_id' => $journal['destination_account_id'],
'destination_account_name' => $journal['destination_account_name'],
'description' => $journal['description'],
'transaction_group_id' => $journal['transaction_group_id'],
];
}
return $array;
}
}

View File

@ -47,6 +47,20 @@ interface OperationsRepositoryInterface
*/
public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array;
/**
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
* which have the specified category set to them. It's grouped per currency, with as few details in the array
* as possible. Amounts are always positive.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection|null $accounts
* @param Collection|null $categories
*
* @return array
*/
public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array;
/**
* This method returns a list of all the transfer transaction journals (as arrays) set in that period
* which have the specified category set to them, transferred INTO the listed accounts.
@ -77,20 +91,6 @@ interface OperationsRepositoryInterface
*/
public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array;
/**
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
* which have the specified category set to them. It's grouped per currency, with as few details in the array
* as possible. Amounts are always positive.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection|null $accounts
* @param Collection|null $categories
*
* @return array
*/
public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array;
/**
* @param User $user
*/

View File

@ -48,19 +48,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface
{
private User $user;
/**
* @param TransactionCurrency $currency
*
* @return int
*/
public function countJournals(TransactionCurrency $currency): int
{
$count = $currency->transactions()->whereNull('deleted_at')->count() + $currency->transactionJournals()->whereNull('deleted_at')->count();
// also count foreign:
return $count + Transaction::where('foreign_currency_id', $currency->id)->count();
}
/**
* @param TransactionCurrency $currency
*
@ -163,6 +150,27 @@ class CurrencyRepository implements CurrencyRepositoryInterface
return null;
}
/**
* @param TransactionCurrency $currency
*
* @return int
*/
public function countJournals(TransactionCurrency $currency): int
{
$count = $currency->transactions()->whereNull('deleted_at')->count() + $currency->transactionJournals()->whereNull('deleted_at')->count();
// also count foreign:
return $count + Transaction::where('foreign_currency_id', $currency->id)->count();
}
/**
* @return Collection
*/
public function getAll(): Collection
{
return TransactionCurrency::orderBy('code', 'ASC')->get();
}
/**
* @param TransactionCurrency $currency
*
@ -192,40 +200,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface
$currency->save();
}
/**
* @param TransactionCurrency $currency
* Enables a currency
*/
public function enable(TransactionCurrency $currency): void
{
$currency->enabled = true;
$currency->save();
}
/**
* Find by ID, return NULL if not found.
*
* @param int $currencyId
*
* @return TransactionCurrency|null
*/
public function find(int $currencyId): ?TransactionCurrency
{
return TransactionCurrency::find($currencyId);
}
/**
* Find by currency code, return NULL if unfound.
*
* @param string $currencyCode
*
* @return TransactionCurrency|null
*/
public function findByCode(string $currencyCode): ?TransactionCurrency
{
return TransactionCurrency::where('code', $currencyCode)->first();
}
/**
* Find by currency code, return NULL if unfound.
* Used in Import Currency!
@ -347,19 +321,45 @@ class CurrencyRepository implements CurrencyRepositoryInterface
}
/**
* @return Collection
* Find by ID, return NULL if not found.
*
* @param int $currencyId
*
* @return TransactionCurrency|null
*/
public function get(): Collection
public function find(int $currencyId): ?TransactionCurrency
{
return TransactionCurrency::where('enabled', true)->orderBy('code', 'ASC')->get();
return TransactionCurrency::find($currencyId);
}
/**
* Find by currency code, return NULL if unfound.
*
* @param string $currencyCode
*
* @return TransactionCurrency|null
*/
public function findByCode(string $currencyCode): ?TransactionCurrency
{
return TransactionCurrency::where('code', $currencyCode)->first();
}
/**
* @param TransactionCurrency $currency
* Enables a currency
*/
public function enable(TransactionCurrency $currency): void
{
$currency->enabled = true;
$currency->save();
}
/**
* @return Collection
*/
public function getAll(): Collection
public function get(): Collection
{
return TransactionCurrency::orderBy('code', 'ASC')->get();
return TransactionCurrency::where('enabled', true)->orderBy('code', 'ASC')->get();
}
/**

View File

@ -80,30 +80,6 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface
return true;
}
/**
* @param int $linkTypeId
*
* @return LinkType|null
*/
public function find(int $linkTypeId): ?LinkType
{
return LinkType::find($linkTypeId);
}
/**
* @param string|null $name
*
* @return LinkType|null
*/
public function findByName(string $name = null): ?LinkType
{
if (null === $name) {
return null;
}
return LinkType::where('name', $name)->first();
}
/**
* Check if link exists between journals.
*
@ -121,24 +97,6 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface
return $count + $opposingCount > 0;
}
/**
* See if such a link already exists (and get it).
*
* @param LinkType $linkType
* @param TransactionJournal $inward
* @param TransactionJournal $outward
*
* @return TransactionJournalLink|null
*/
public function findSpecificLink(LinkType $linkType, TransactionJournal $inward, TransactionJournal $outward): ?TransactionJournalLink
{
return TransactionJournalLink
::where('link_type_id', $linkType->id)
->where('source_id', $inward->id)
->where('destination_id', $outward->id)->first();
}
/**
* @return Collection
*/
@ -188,6 +146,16 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface
return $query->get(['journal_links.*']);
}
public function getLink(TransactionJournal $one, TransactionJournal $two): ?TransactionJournalLink
{
$left = TransactionJournalLink::whereDestinationId($one->id)->whereSourceId($two->id)->first();
if (null !== $left) {
return $left;
}
return TransactionJournalLink::whereDestinationId($two->id)->whereSourceId($one->id)->first();
}
/**
* Return list of existing connections.
*
@ -282,6 +250,91 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface
return $link;
}
/**
* @param int $linkTypeId
*
* @return LinkType|null
*/
public function find(int $linkTypeId): ?LinkType
{
return LinkType::find($linkTypeId);
}
/**
* @param string|null $name
*
* @return LinkType|null
*/
public function findByName(string $name = null): ?LinkType
{
if (null === $name) {
return null;
}
return LinkType::where('name', $name)->first();
}
/**
* See if such a link already exists (and get it).
*
* @param LinkType $linkType
* @param TransactionJournal $inward
* @param TransactionJournal $outward
*
* @return TransactionJournalLink|null
*/
public function findSpecificLink(LinkType $linkType, TransactionJournal $inward, TransactionJournal $outward): ?TransactionJournalLink
{
return TransactionJournalLink
::where('link_type_id', $linkType->id)
->where('source_id', $inward->id)
->where('destination_id', $outward->id)->first();
}
/**
* @param TransactionJournalLink $link
* @param string $text
*
* @throws Exception
*/
private function setNoteText(TransactionJournalLink $link, string $text): void
{
$dbNote = $link->notes()->first();
if ('' !== $text) {
if (null === $dbNote) {
$dbNote = new Note();
$dbNote->noteable()->associate($link);
}
$dbNote->text = trim($text);
$dbNote->save();
return;
}
if (null !== $dbNote && '' === $text) {
try {
$dbNote->delete();
} catch (Exception $e) { // @phpstan-ignore-line
// @ignoreException
}
}
}
/**
* @inheritDoc
*/
public function switchLinkById(int $linkId): bool
{
/** @var TransactionJournalLink $link */
$link = TransactionJournalLink::find($linkId);
if (null !== $link && $link->source->user->id === $this->user->id) {
$this->switchLink($link);
}
return true;
}
/**
* @param TransactionJournalLink $link
*
@ -350,57 +403,4 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface
return $journalLink;
}
/**
* @param TransactionJournalLink $link
* @param string $text
*
* @throws Exception
*/
private function setNoteText(TransactionJournalLink $link, string $text): void
{
$dbNote = $link->notes()->first();
if ('' !== $text) {
if (null === $dbNote) {
$dbNote = new Note();
$dbNote->noteable()->associate($link);
}
$dbNote->text = trim($text);
$dbNote->save();
return;
}
if (null !== $dbNote && '' === $text) {
try {
$dbNote->delete();
} catch (Exception $e) { // @phpstan-ignore-line
// @ignoreException
}
}
}
public function getLink(TransactionJournal $one, TransactionJournal $two): ?TransactionJournalLink
{
$left = TransactionJournalLink::whereDestinationId($one->id)->whereSourceId($two->id)->first();
if (null !== $left) {
return $left;
}
return TransactionJournalLink::whereDestinationId($two->id)->whereSourceId($one->id)->first();
}
/**
* @inheritDoc
*/
public function switchLinkById(int $linkId): bool
{
/** @var TransactionJournalLink $link */
$link = TransactionJournalLink::find($linkId);
if (null !== $link && $link->source->user->id === $this->user->id) {
$this->switchLink($link);
}
return true;
}
}

View File

@ -52,6 +52,17 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface
}
}
/**
* @inheritDoc
*/
public function get(): Collection
{
return $this->user->objectGroups()
->with(['piggyBanks', 'bills'])
->orderBy('order', 'ASC')
->orderBy('title', 'ASC')->get();
}
/**
* @inheritDoc
*/
@ -81,17 +92,6 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface
$objectGroup->delete();
}
/**
* @inheritDoc
*/
public function get(): Collection
{
return $this->user->objectGroups()
->with(['piggyBanks', 'bills'])
->orderBy('order', 'ASC')
->orderBy('title', 'ASC')->get();
}
/**
* @inheritDoc
*/
@ -151,6 +151,32 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface
return $dbQuery->take($limit)->get(['object_groups.*']);
}
/**
* @param User $user
*/
public function setUser(User $user): void
{
$this->user = $user;
}
/**
* @inheritDoc
*/
public function update(ObjectGroup $objectGroup, array $data): ObjectGroup
{
if (array_key_exists('title', $data)) {
$objectGroup->title = $data['title'];
}
if (array_key_exists('order', $data)) {
$this->setOrder($objectGroup, (int) $data['order']);
}
$objectGroup->save();
return $objectGroup;
}
/**
* @inheritDoc
*/
@ -179,30 +205,4 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface
return $objectGroup;
}
/**
* @param User $user
*/
public function setUser(User $user): void
{
$this->user = $user;
}
/**
* @inheritDoc
*/
public function update(ObjectGroup $objectGroup, array $data): ObjectGroup
{
if (array_key_exists('title', $data)) {
$objectGroup->title = $data['title'];
}
if (array_key_exists('order', $data)) {
$this->setOrder($objectGroup, (int)$data['order']);
}
$objectGroup->save();
return $objectGroup;
}
}

View File

@ -127,21 +127,6 @@ trait ModifiesPiggyBanks
return bccomp($amount, $savedSoFar) <= 0;
}
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return PiggyBankEvent
*/
public function createEvent(PiggyBank $piggyBank, string $amount): PiggyBankEvent
{
if (0 === bccomp('0', $amount)) {
return new PiggyBankEvent;
}
return PiggyBankEvent::create(['date' => Carbon::now(), 'amount' => $amount, 'piggy_bank_id' => $piggyBank->id]);
}
/**
* @param PiggyBank $piggyBank
* @param string $amount
@ -195,6 +180,21 @@ trait ModifiesPiggyBanks
return true;
}
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return PiggyBankEvent
*/
public function createEvent(PiggyBank $piggyBank, string $amount): PiggyBankEvent
{
if (0 === bccomp('0', $amount)) {
return new PiggyBankEvent;
}
return PiggyBankEvent::create(['date' => Carbon::now(), 'amount' => $amount, 'piggy_bank_id' => $piggyBank->id]);
}
/**
* @inheritDoc
*/
@ -205,23 +205,6 @@ trait ModifiesPiggyBanks
return $piggyBank;
}
/**
* Correct order of piggies in case of issues.
*/
public function resetOrder(): void
{
$set = $this->user->piggyBanks()->orderBy('piggy_banks.order', 'ASC')->get(['piggy_banks.*']);
$current = 1;
foreach ($set as $piggyBank) {
if ((int) $piggyBank->order !== $current) {
Log::debug(sprintf('Piggy bank #%d ("%s") was at place %d but should be on %d', $piggyBank->id, $piggyBank->name, $piggyBank->order, $current));
$piggyBank->order = $current;
$piggyBank->save();
}
$current++;
}
}
/**
* @param PiggyBank $piggyBank
* @param string $amount
@ -262,34 +245,6 @@ trait ModifiesPiggyBanks
}
/**
* @inheritDoc
*/
public function setOrder(PiggyBank $piggyBank, int $newOrder): bool
{
$oldOrder = (int) $piggyBank->order;
Log::debug(sprintf('Will move piggy bank #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder));
if ($newOrder > $oldOrder) {
$this->user->piggyBanks()->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder)
->where('piggy_banks.id', '!=', $piggyBank->id)
->decrement('piggy_banks.order');
$piggyBank->order = $newOrder;
Log::debug(sprintf('Order of piggy #%d ("%s") is now %d', $piggyBank->id, $piggyBank->name, $newOrder));
$piggyBank->save();
return true;
}
$this->user->piggyBanks()->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder)
->where('piggy_banks.id', '!=', $piggyBank->id)
->increment('piggy_banks.order');
$piggyBank->order = $newOrder;
Log::debug(sprintf('Order of piggy #%d ("%s") is now %d', $piggyBank->id, $piggyBank->name, $newOrder));
$piggyBank->save();
return true;
}
/**
* @param array $data
*
@ -349,6 +304,82 @@ trait ModifiesPiggyBanks
return $piggyBank;
}
/**
* Correct order of piggies in case of issues.
*/
public function resetOrder(): void
{
$set = $this->user->piggyBanks()->orderBy('piggy_banks.order', 'ASC')->get(['piggy_banks.*']);
$current = 1;
foreach ($set as $piggyBank) {
if ((int) $piggyBank->order !== $current) {
Log::debug(sprintf('Piggy bank #%d ("%s") was at place %d but should be on %d', $piggyBank->id, $piggyBank->name, $piggyBank->order, $current));
$piggyBank->order = $current;
$piggyBank->save();
}
$current++;
}
}
/**
* @inheritDoc
*/
public function setOrder(PiggyBank $piggyBank, int $newOrder): bool
{
$oldOrder = (int) $piggyBank->order;
Log::debug(sprintf('Will move piggy bank #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder));
if ($newOrder > $oldOrder) {
$this->user->piggyBanks()->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder)
->where('piggy_banks.id', '!=', $piggyBank->id)
->decrement('piggy_banks.order');
$piggyBank->order = $newOrder;
Log::debug(sprintf('Order of piggy #%d ("%s") is now %d', $piggyBank->id, $piggyBank->name, $newOrder));
$piggyBank->save();
return true;
}
$this->user->piggyBanks()->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder)
->where('piggy_banks.id', '!=', $piggyBank->id)
->increment('piggy_banks.order');
$piggyBank->order = $newOrder;
Log::debug(sprintf('Order of piggy #%d ("%s") is now %d', $piggyBank->id, $piggyBank->name, $newOrder));
$piggyBank->save();
return true;
}
/**
* @param PiggyBank $piggyBank
* @param string $note
*
* @return bool
*/
private function updateNote(PiggyBank $piggyBank, string $note): bool
{
if ('' === $note) {
$dbNote = $piggyBank->notes()->first();
if (null !== $dbNote) {
try {
$dbNote->delete();
} catch (Exception $e) { // @phpstan-ignore-line
// @ignoreException
}
}
return true;
}
$dbNote = $piggyBank->notes()->first();
if (null === $dbNote) {
$dbNote = new Note;
$dbNote->noteable()->associate($piggyBank);
}
$dbNote->text = trim($note);
$dbNote->save();
return true;
}
/**
* @param PiggyBank $piggyBank
* @param array $data
@ -416,37 +447,6 @@ trait ModifiesPiggyBanks
return $piggyBank;
}
/**
* @param PiggyBank $piggyBank
* @param string $note
*
* @return bool
*/
private function updateNote(PiggyBank $piggyBank, string $note): bool
{
if ('' === $note) {
$dbNote = $piggyBank->notes()->first();
if (null !== $dbNote) {
try {
$dbNote->delete();
} catch (Exception $e) { // @phpstan-ignore-line
// @ignoreException
}
}
return true;
}
$dbNote = $piggyBank->notes()->first();
if (null === $dbNote) {
$dbNote = new Note;
$dbNote->noteable()->associate($piggyBank);
}
$dbNote->text = trim($note);
$dbNote->save();
return true;
}
/**
* @param PiggyBank $piggyBank
* @param array $data

View File

@ -54,28 +54,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
$this->user->piggyBanks()->delete();
}
/**
* @param int $piggyBankId
*
* @return PiggyBank|null
*/
public function find(int $piggyBankId): ?PiggyBank
{
return $this->user->piggyBanks()->find($piggyBankId);
}
/**
* Find by name or return NULL.
*
* @param string $name
*
* @return PiggyBank|null
*/
public function findByName(string $name): ?PiggyBank
{
return $this->user->piggyBanks()->where('piggy_banks.name', $name)->first(['piggy_banks.*']);
}
/**
* @param int|null $piggyBankId
* @param string|null $piggyBankName
@ -107,6 +85,28 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return null;
}
/**
* @param int $piggyBankId
*
* @return PiggyBank|null
*/
public function find(int $piggyBankId): ?PiggyBank
{
return $this->user->piggyBanks()->find($piggyBankId);
}
/**
* Find by name or return NULL.
*
* @param string $name
*
* @return PiggyBank|null
*/
public function findByName(string $name): ?PiggyBank
{
return $this->user->piggyBanks()->where('piggy_banks.name', $name)->first(['piggy_banks.*']);
}
/**
* @inheritDoc
*/
@ -145,6 +145,16 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return (string) $rep->currentamount;
}
/**
* @param PiggyBank $piggyBank
*
* @return PiggyBankRepetition|null
*/
public function getRepetition(PiggyBank $piggyBank): ?PiggyBankRepetition
{
return $piggyBank->piggyBankRepetitions()->first();
}
/**
* @param PiggyBank $piggyBank
*
@ -277,14 +287,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return $note->text;
}
/**
* @return Collection
*/
public function getPiggyBanks(): Collection
{
return $this->user->piggyBanks()->with(['account', 'objectGroups'])->orderBy('order', 'ASC')->get();
}
/**
* Also add amount in name.
*
@ -307,13 +309,11 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
}
/**
* @param PiggyBank $piggyBank
*
* @return PiggyBankRepetition|null
* @return Collection
*/
public function getRepetition(PiggyBank $piggyBank): ?PiggyBankRepetition
public function getPiggyBanks(): Collection
{
return $piggyBank->piggyBankRepetitions()->first();
return $this->user->piggyBanks()->with(['account', 'objectGroups'])->orderBy('order', 'ASC')->get();
}
/**

View File

@ -107,6 +107,22 @@ class RecurringRepository implements RecurringRepositoryInterface
->get();
}
/**
* @inheritDoc
*/
public function getBillId(RecurrenceTransaction $recTransaction): ?int
{
$return = null;
/** @var RecurrenceTransactionMeta $meta */
foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
if ('bill_id' === $meta->name) {
$return = (int) $meta->value;
}
}
return $return;
}
/**
* Get the budget ID from a recurring transaction transaction.
*
@ -228,45 +244,6 @@ class RecurringRepository implements RecurringRepositoryInterface
return '';
}
/**
* Generate events in the date range.
*
* @param RecurrenceRepetition $repetition
* @param Carbon $start
* @param Carbon $end
*
* @return array
*
*/
public function getOccurrencesInRange(RecurrenceRepetition $repetition, Carbon $start, Carbon $end): array
{
$occurrences = [];
$mutator = clone $start;
$mutator->startOfDay();
$skipMod = $repetition->repetition_skip + 1;
Log::debug(sprintf('Calculating occurrences for rep type "%s"', $repetition->repetition_type));
Log::debug(sprintf('Mutator is now: %s', $mutator->format('Y-m-d')));
if ('daily' === $repetition->repetition_type) {
$occurrences = $this->getDailyInRange($mutator, $end, $skipMod);
}
if ('weekly' === $repetition->repetition_type) {
$occurrences = $this->getWeeklyInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
}
if ('monthly' === $repetition->repetition_type) {
$occurrences = $this->getMonthlyInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
}
if ('ndom' === $repetition->repetition_type) {
$occurrences = $this->getNdomInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
}
if ('yearly' === $repetition->repetition_type) {
$occurrences = $this->getYearlyInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
}
// filter out all the weekend days:
return $this->filterWeekends($repetition, $occurrences);
}
/**
* @param RecurrenceTransaction $transaction
*
@ -449,6 +426,27 @@ class RecurringRepository implements RecurringRepositoryInterface
return $this->filterMaxDate($repeatUntil, $occurrences);
}
/**
* @param Carbon|null $max
* @param array $occurrences
*
* @return array
*/
private function filterMaxDate(?Carbon $max, array $occurrences): array
{
if (null === $max) {
return $occurrences;
}
$filtered = [];
foreach ($occurrences as $date) {
if ($date->lte($max)) {
$filtered[] = $date;
}
}
return $filtered;
}
/**
* Parse the repetition in a string that is user readable.
*
@ -576,6 +574,45 @@ class RecurringRepository implements RecurringRepositoryInterface
return 0;
}
/**
* Generate events in the date range.
*
* @param RecurrenceRepetition $repetition
* @param Carbon $start
* @param Carbon $end
*
* @return array
*
*/
public function getOccurrencesInRange(RecurrenceRepetition $repetition, Carbon $start, Carbon $end): array
{
$occurrences = [];
$mutator = clone $start;
$mutator->startOfDay();
$skipMod = $repetition->repetition_skip + 1;
Log::debug(sprintf('Calculating occurrences for rep type "%s"', $repetition->repetition_type));
Log::debug(sprintf('Mutator is now: %s', $mutator->format('Y-m-d')));
if ('daily' === $repetition->repetition_type) {
$occurrences = $this->getDailyInRange($mutator, $end, $skipMod);
}
if ('weekly' === $repetition->repetition_type) {
$occurrences = $this->getWeeklyInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
}
if ('monthly' === $repetition->repetition_type) {
$occurrences = $this->getMonthlyInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
}
if ('ndom' === $repetition->repetition_type) {
$occurrences = $this->getNdomInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
}
if ('yearly' === $repetition->repetition_type) {
$occurrences = $this->getYearlyInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
}
// filter out all the weekend days:
return $this->filterWeekends($repetition, $occurrences);
}
/**
* Update a recurring transaction.
*
@ -592,41 +629,4 @@ class RecurringRepository implements RecurringRepositoryInterface
return $service->update($recurrence, $data);
}
/**
* @param Carbon|null $max
* @param array $occurrences
*
* @return array
*/
private function filterMaxDate(?Carbon $max, array $occurrences): array
{
if (null === $max) {
return $occurrences;
}
$filtered = [];
foreach ($occurrences as $date) {
if ($date->lte($max)) {
$filtered[] = $date;
}
}
return $filtered;
}
/**
* @inheritDoc
*/
public function getBillId(RecurrenceTransaction $recTransaction): ?int
{
$return = null;
/** @var RecurrenceTransactionMeta $meta */
foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
if ('bill_id' === $meta->name) {
$return = (int)$meta->value;
}
}
return $return;
}
}

View File

@ -64,6 +64,15 @@ interface RecurringRepositoryInterface
*/
public function getAll(): Collection;
/**
* Get the category from a recurring transaction transaction.
*
* @param RecurrenceTransaction $recTransaction
*
* @return null|int
*/
public function getBillId(RecurrenceTransaction $recTransaction): ?int;
/**
* Get the budget ID from a recurring transaction transaction.
*
@ -82,15 +91,6 @@ interface RecurringRepositoryInterface
*/
public function getCategoryId(RecurrenceTransaction $recTransaction): ?int;
/**
* Get the category from a recurring transaction transaction.
*
* @param RecurrenceTransaction $recTransaction
*
* @return null|int
*/
public function getBillId(RecurrenceTransaction $recTransaction): ?int;
/**
* Get the category from a recurring transaction transaction.
*

View File

@ -253,43 +253,6 @@ class RuleRepository implements RuleRepositoryInterface
return $filtered;
}
/**
* @inheritDoc
*/
public function maxOrder(RuleGroup $ruleGroup): int
{
return (int)$ruleGroup->rules()->max('order');
}
/**
* @inheritDoc
*/
public function moveRule(Rule $rule, RuleGroup $ruleGroup, int $order): Rule
{
if ($rule->rule_group_id !== $ruleGroup->id) {
$rule->rule_group_id = $ruleGroup->id;
}
$rule->save();
$rule->refresh();
$this->setOrder($rule, $order);
return $rule;
}
/**
* @param RuleGroup $ruleGroup
*
* @return bool
*/
public function resetRuleOrder(RuleGroup $ruleGroup): bool
{
$groupRepository = app(RuleGroupRepositoryInterface::class);
$groupRepository->setUser($ruleGroup->user);
$groupRepository->resetRuleOrder($ruleGroup);
return true;
}
/**
* @inheritDoc
*/
@ -305,42 +268,6 @@ class RuleRepository implements RuleRepositoryInterface
return $search->take($limit)->get(['id', 'title', 'description']);
}
/**
* @inheritDoc
*/
public function setOrder(Rule $rule, int $newOrder): void
{
$oldOrder = (int)$rule->order;
$groupId = (int)$rule->rule_group_id;
$maxOrder = $this->maxOrder($rule->ruleGroup);
$newOrder = $newOrder > $maxOrder ? $maxOrder + 1 : $newOrder;
Log::debug(sprintf('New order will be %d', $newOrder));
if ($newOrder > $oldOrder) {
$this->user->rules()
->where('rules.rule_group_id', $groupId)
->where('rules.order', '<=', $newOrder)
->where('rules.order', '>', $oldOrder)
->where('rules.id', '!=', $rule->id)
->decrement('rules.order');
$rule->order = $newOrder;
Log::debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder));
$rule->save();
return;
}
$this->user->rules()
->where('rules.rule_group_id', $groupId)
->where('rules.order', '>=', $newOrder)
->where('rules.order', '<', $oldOrder)
->where('rules.id', '!=', $rule->id)
->increment('rules.order');
$rule->order = $newOrder;
Log::debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder));
$rule->save();
}
/**
* @param User $user
*/
@ -404,23 +331,113 @@ class RuleRepository implements RuleRepositoryInterface
}
/**
* @param string $moment
* @param Rule $rule
* @param array $values
*
* @return RuleAction
*/
public function storeAction(Rule $rule, array $values): RuleAction
private function setRuleTrigger(string $moment, Rule $rule): void
{
$ruleAction = new RuleAction;
$ruleAction->rule()->associate($rule);
$ruleAction->order = $values['order'];
$ruleAction->active = $values['active'];
$ruleAction->stop_processing = $values['stop_processing'];
$ruleAction->action_type = $values['action'];
$ruleAction->action_value = $values['value'] ?? '';
$ruleAction->save();
/** @var RuleTrigger|null $trigger */
$trigger = $rule->ruleTriggers()->where('trigger_type', 'user_action')->first();
if (null !== $trigger) {
$trigger->trigger_value = $moment;
$trigger->save();
return $ruleAction;
return;
}
$trigger = new RuleTrigger;
$trigger->order = 0;
$trigger->trigger_type = 'user_action';
$trigger->trigger_value = $moment;
$trigger->rule_id = $rule->id;
$trigger->active = true;
$trigger->stop_processing = false;
$trigger->save();
}
/**
* @param RuleGroup $ruleGroup
*
* @return bool
*/
public function resetRuleOrder(RuleGroup $ruleGroup): bool
{
$groupRepository = app(RuleGroupRepositoryInterface::class);
$groupRepository->setUser($ruleGroup->user);
$groupRepository->resetRuleOrder($ruleGroup);
return true;
}
/**
* @inheritDoc
*/
public function setOrder(Rule $rule, int $newOrder): void
{
$oldOrder = (int) $rule->order;
$groupId = (int) $rule->rule_group_id;
$maxOrder = $this->maxOrder($rule->ruleGroup);
$newOrder = $newOrder > $maxOrder ? $maxOrder + 1 : $newOrder;
Log::debug(sprintf('New order will be %d', $newOrder));
if ($newOrder > $oldOrder) {
$this->user->rules()
->where('rules.rule_group_id', $groupId)
->where('rules.order', '<=', $newOrder)
->where('rules.order', '>', $oldOrder)
->where('rules.id', '!=', $rule->id)
->decrement('rules.order');
$rule->order = $newOrder;
Log::debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder));
$rule->save();
return;
}
$this->user->rules()
->where('rules.rule_group_id', $groupId)
->where('rules.order', '>=', $newOrder)
->where('rules.order', '<', $oldOrder)
->where('rules.id', '!=', $rule->id)
->increment('rules.order');
$rule->order = $newOrder;
Log::debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder));
$rule->save();
}
/**
* @inheritDoc
*/
public function maxOrder(RuleGroup $ruleGroup): int
{
return (int) $ruleGroup->rules()->max('order');
}
/**
* @param Rule $rule
* @param array $data
*
* @return bool
*/
private function storeTriggers(Rule $rule, array $data): bool
{
$order = 1;
foreach ($data['triggers'] as $trigger) {
$value = $trigger['value'] ?? '';
$stopProcessing = $trigger['stop_processing'] ?? false;
$active = $trigger['active'] ?? true;
$triggerValues = [
'action' => $trigger['type'],
'value' => $value,
'stop_processing' => $stopProcessing,
'order' => $order,
'active' => $active,
];
$this->storeTrigger($rule, $triggerValues);
++$order;
}
return true;
}
/**
@ -443,6 +460,53 @@ class RuleRepository implements RuleRepositoryInterface
return $ruleTrigger;
}
/**
* @param Rule $rule
* @param array $data
*
* @return bool
*/
private function storeActions(Rule $rule, array $data): bool
{
$order = 1;
foreach ($data['actions'] as $action) {
$value = $action['value'] ?? '';
$stopProcessing = $action['stop_processing'] ?? false;
$active = $action['active'] ?? true;
$actionValues = [
'action' => $action['type'],
'value' => $value,
'stop_processing' => $stopProcessing,
'order' => $order,
'active' => $active,
];
$this->storeAction($rule, $actionValues);
++$order;
}
return true;
}
/**
* @param Rule $rule
* @param array $values
*
* @return RuleAction
*/
public function storeAction(Rule $rule, array $values): RuleAction
{
$ruleAction = new RuleAction;
$ruleAction->rule()->associate($rule);
$ruleAction->order = $values['order'];
$ruleAction->active = $values['active'];
$ruleAction->stop_processing = $values['stop_processing'];
$ruleAction->action_type = $values['action'];
$ruleAction->action_value = $values['value'] ?? '';
$ruleAction->save();
return $ruleAction;
}
/**
* @param Rule $rule
* @param array $data
@ -502,81 +566,17 @@ class RuleRepository implements RuleRepositoryInterface
}
/**
* @param string $moment
* @param Rule $rule
* @inheritDoc
*/
private function setRuleTrigger(string $moment, Rule $rule): void
public function moveRule(Rule $rule, RuleGroup $ruleGroup, int $order): Rule
{
/** @var RuleTrigger|null $trigger */
$trigger = $rule->ruleTriggers()->where('trigger_type', 'user_action')->first();
if (null !== $trigger) {
$trigger->trigger_value = $moment;
$trigger->save();
if ($rule->rule_group_id !== $ruleGroup->id) {
$rule->rule_group_id = $ruleGroup->id;
}
$rule->save();
$rule->refresh();
$this->setOrder($rule, $order);
return;
}
$trigger = new RuleTrigger;
$trigger->order = 0;
$trigger->trigger_type = 'user_action';
$trigger->trigger_value = $moment;
$trigger->rule_id = $rule->id;
$trigger->active = true;
$trigger->stop_processing = false;
$trigger->save();
}
/**
* @param Rule $rule
* @param array $data
*
* @return bool
*/
private function storeTriggers(Rule $rule, array $data): bool
{
$order = 1;
foreach ($data['triggers'] as $trigger) {
$value = $trigger['value'] ?? '';
$stopProcessing = $trigger['stop_processing'] ?? false;
$active = $trigger['active'] ?? true;
$triggerValues = [
'action' => $trigger['type'],
'value' => $value,
'stop_processing' => $stopProcessing,
'order' => $order,
'active' => $active,
];
$this->storeTrigger($rule, $triggerValues);
++$order;
}
return true;
}
/**
* @param Rule $rule
* @param array $data
*
* @return bool
*/
private function storeActions(Rule $rule, array $data): bool
{
$order = 1;
foreach ($data['actions'] as $action) {
$value = $action['value'] ?? '';
$stopProcessing = $action['stop_processing'] ?? false;
$active = $action['active'] ?? true;
$actionValues = [
'action' => $action['type'],
'value' => $value,
'stop_processing' => $stopProcessing,
'order' => $order,
'active' => $active,
];
$this->storeAction($rule, $actionValues);
++$order;
}
return true;
return $rule;
}
}

View File

@ -99,6 +99,108 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return true;
}
/**
* @return bool
*/
public function resetOrder(): bool
{
$set = $this->user
->ruleGroups()
->whereNull('deleted_at')
->orderBy('order', 'ASC')
->orderBy('title', 'DESC')
->get();
$count = 1;
/** @var RuleGroup $entry */
foreach ($set as $entry) {
if ($entry->order !== $count) {
$entry->order = $count;
$entry->save();
}
// also update rules in group.
$this->resetRuleOrder($entry);
++$count;
}
return true;
}
/**
* @param RuleGroup $ruleGroup
*
* @return bool
*/
public function resetRuleOrder(RuleGroup $ruleGroup): bool
{
$set = $ruleGroup->rules()
->orderBy('order', 'ASC')
->orderBy('title', 'DESC')
->orderBy('updated_at', 'DESC')
->get(['rules.*']);
$count = 1;
/** @var Rule $entry */
foreach ($set as $entry) {
if ((int) $entry->order !== $count) {
Log::debug(sprintf('Rule #%d was on spot %d but must be on spot %d', $entry->id, $entry->order, $count));
$entry->order = $count;
$entry->save();
}
$this->resetRuleActionOrder($entry);
$this->resetRuleTriggerOrder($entry);
++$count;
}
return true;
}
/**
* @param Rule $rule
*/
private function resetRuleActionOrder(Rule $rule): void
{
$actions = $rule->ruleActions()
->orderBy('order', 'ASC')
->orderBy('active', 'DESC')
->orderBy('action_type', 'ASC')
->get();
$index = 1;
/** @var RuleAction $action */
foreach ($actions as $action) {
if ((int) $action->order !== $index) {
$action->order = $index;
$action->save();
Log::debug(sprintf('Rule action #%d was on spot %d but must be on spot %d', $action->id, $action->order, $index));
}
$index++;
}
}
/**
* @param Rule $rule
*/
private function resetRuleTriggerOrder(Rule $rule): void
{
$triggers = $rule->ruleTriggers()
->orderBy('order', 'ASC')
->orderBy('active', 'DESC')
->orderBy('trigger_type', 'ASC')
->get();
$index = 1;
/** @var RuleTrigger $trigger */
foreach ($triggers as $trigger) {
$order = (int) $trigger->order;
if ($order !== $index) {
$trigger->order = $index;
$trigger->save();
Log::debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index));
}
$index++;
}
}
/**
* @inheritDoc
*/
@ -112,6 +214,14 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
}
}
/**
* @return Collection
*/
public function get(): Collection
{
return $this->user->ruleGroups()->orderBy('order', 'ASC')->get();
}
/**
* @param int $ruleGroupId
*
@ -132,14 +242,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return $this->user->ruleGroups()->where('title', $title)->first();
}
/**
* @return Collection
*/
public function get(): Collection
{
return $this->user->ruleGroups()->orderBy('order', 'ASC')->get();
}
/**
* @return Collection
*/
@ -324,63 +426,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return (int) $this->user->ruleGroups()->where('active', true)->max('order');
}
/**
* @return bool
*/
public function resetOrder(): bool
{
$set = $this->user
->ruleGroups()
->whereNull('deleted_at')
->orderBy('order', 'ASC')
->orderBy('title', 'DESC')
->get();
$count = 1;
/** @var RuleGroup $entry */
foreach ($set as $entry) {
if ($entry->order !== $count) {
$entry->order = $count;
$entry->save();
}
// also update rules in group.
$this->resetRuleOrder($entry);
++$count;
}
return true;
}
/**
* @param RuleGroup $ruleGroup
*
* @return bool
*/
public function resetRuleOrder(RuleGroup $ruleGroup): bool
{
$set = $ruleGroup->rules()
->orderBy('order', 'ASC')
->orderBy('title', 'DESC')
->orderBy('updated_at', 'DESC')
->get(['rules.*']);
$count = 1;
/** @var Rule $entry */
foreach ($set as $entry) {
if ((int)$entry->order !== $count) {
Log::debug(sprintf('Rule #%d was on spot %d but must be on spot %d', $entry->id, $entry->order, $count));
$entry->order = $count;
$entry->save();
}
$this->resetRuleActionOrder($entry);
$this->resetRuleTriggerOrder($entry);
++$count;
}
return true;
}
/**
* @inheritDoc
*/
@ -396,32 +441,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return $search->take($limit)->get(['id', 'title', 'description']);
}
/**
* @inheritDoc
*/
public function setOrder(RuleGroup $ruleGroup, int $newOrder): void
{
$oldOrder = (int)$ruleGroup->order;
if ($newOrder > $oldOrder) {
$this->user->ruleGroups()->where('rule_groups.order', '<=', $newOrder)->where('rule_groups.order', '>', $oldOrder)
->where('rule_groups.id', '!=', $ruleGroup->id)
->decrement('order');
$ruleGroup->order = $newOrder;
Log::debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder));
$ruleGroup->save();
return;
}
$this->user->ruleGroups()->where('rule_groups.order', '>=', $newOrder)->where('rule_groups.order', '<', $oldOrder)
->where('rule_groups.id', '!=', $ruleGroup->id)
->increment('order');
$ruleGroup->order = $newOrder;
Log::debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder));
$ruleGroup->save();
}
/**
* @param User $user
*/
@ -455,6 +474,32 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return $newRuleGroup;
}
/**
* @inheritDoc
*/
public function setOrder(RuleGroup $ruleGroup, int $newOrder): void
{
$oldOrder = (int) $ruleGroup->order;
if ($newOrder > $oldOrder) {
$this->user->ruleGroups()->where('rule_groups.order', '<=', $newOrder)->where('rule_groups.order', '>', $oldOrder)
->where('rule_groups.id', '!=', $ruleGroup->id)
->decrement('order');
$ruleGroup->order = $newOrder;
Log::debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder));
$ruleGroup->save();
return;
}
$this->user->ruleGroups()->where('rule_groups.order', '>=', $newOrder)->where('rule_groups.order', '<', $oldOrder)
->where('rule_groups.id', '!=', $ruleGroup->id)
->increment('order');
$ruleGroup->order = $newOrder;
Log::debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder));
$ruleGroup->save();
}
/**
* @param RuleGroup $ruleGroup
* @param array $data
@ -483,49 +528,4 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return $ruleGroup;
}
/**
* @param Rule $rule
*/
private function resetRuleActionOrder(Rule $rule): void
{
$actions = $rule->ruleActions()
->orderBy('order', 'ASC')
->orderBy('active', 'DESC')
->orderBy('action_type', 'ASC')
->get();
$index = 1;
/** @var RuleAction $action */
foreach ($actions as $action) {
if ((int)$action->order !== $index) {
$action->order = $index;
$action->save();
Log::debug(sprintf('Rule action #%d was on spot %d but must be on spot %d', $action->id, $action->order, $index));
}
$index++;
}
}
/**
* @param Rule $rule
*/
private function resetRuleTriggerOrder(Rule $rule): void
{
$triggers = $rule->ruleTriggers()
->orderBy('order', 'ASC')
->orderBy('active', 'DESC')
->orderBy('trigger_type', 'ASC')
->get();
$index = 1;
/** @var RuleTrigger $trigger */
foreach ($triggers as $trigger) {
$order = (int)$trigger->order;
if ($order !== $index) {
$trigger->order = $index;
$trigger->save();
Log::debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index));
}
$index++;
}
}
}

View File

@ -114,6 +114,16 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* @return Collection
*/
private function getTags(): Collection
{
$repository = app(TagRepositoryInterface::class);
return $repository->get();
}
/**
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
* which have the specified tag(s) set to them. It's grouped per currency, with as few details in the array
@ -229,14 +239,4 @@ class OperationsRepository implements OperationsRepositoryInterface
{
throw new FireflyException(sprintf('%s is not yet implemented.', __METHOD__));
}
/**
* @return Collection
*/
private function getTags(): Collection
{
$repository = app(TagRepositoryInterface::class);
return $repository->get();
}
}

View File

@ -81,6 +81,14 @@ class TagRepository implements TagRepositoryInterface
}
}
/**
* @return Collection
*/
public function get(): Collection
{
return $this->user->tags()->orderBy('tag', 'ASC')->get();
}
/**
* @param Tag $tag
* @param Carbon $start
@ -134,14 +142,6 @@ class TagRepository implements TagRepositoryInterface
return null;
}
/**
* @return Collection
*/
public function get(): Collection
{
return $this->user->tags()->orderBy('tag', 'ASC')->get();
}
/**
* @inheritDoc
*/
@ -162,14 +162,6 @@ class TagRepository implements TagRepositoryInterface
);
}
/**
* @inheritDoc
*/
public function getLocation(Tag $tag): ?Location
{
return $tag->locations()->first();
}
/**
* @param int|null $year
*
@ -458,4 +450,12 @@ class TagRepository implements TagRepositoryInterface
return $tag;
}
/**
* @inheritDoc
*/
public function getLocation(Tag $tag): ?Location
{
return $tag->locations()->first();
}
}

View File

@ -56,6 +56,18 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
{
private User $user;
/**
* @inheritDoc
*/
public function countAttachments(int $journalId): int
{
/** @var TransactionJournal $journal */
$journal = $this->user->transactionJournals()->find($journalId);
return $journal->attachments()->count();
}
/**
* @param TransactionGroup $group
*/
@ -80,6 +92,56 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
return $result;
}
/**
* @param TransactionJournal $journal
*
* @return array
*/
private function expandJournal(TransactionJournal $journal): array
{
$array = $journal->toArray();
$array['transactions'] = [];
$array['meta'] = $journal->transactionJournalMeta->toArray();
$array['tags'] = $journal->tags->toArray();
$array['categories'] = $journal->categories->toArray();
$array['budgets'] = $journal->budgets->toArray();
$array['notes'] = $journal->notes->toArray();
$array['locations'] = [];
$array['attachments'] = $journal->attachments->toArray();
$array['links'] = [];
$array['piggy_bank_events'] = $journal->piggyBankEvents->toArray();
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
$array['transactions'][] = $this->expandTransaction($transaction);
}
return $array;
}
/**
* @param Transaction $transaction
*
* @return array
*/
private function expandTransaction(Transaction $transaction): array
{
$array = $transaction->toArray();
$array['account'] = $transaction->account->toArray();
$array['budgets'] = [];
$array['categories'] = [];
foreach ($transaction->categories as $category) {
$array['categories'][] = $category->toArray();
}
foreach ($transaction->budgets as $budget) {
$array['budgets'][] = $budget->toArray();
}
return $array;
}
/**
* Find a transaction group by its ID.
*
@ -182,6 +244,56 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
return $return;
}
/**
* @param TransactionJournal $journal
*
* @return string
*/
private function getFormattedAmount(TransactionJournal $journal): string
{
/** @var Transaction $transaction */
$transaction = $journal->transactions->first();
$currency = $transaction->transactionCurrency;
$type = $journal->transactionType->type;
$amount = app('steam')->positive($transaction->amount);
$return = '';
if (TransactionType::WITHDRAWAL === $type) {
$return = app('amount')->formatAnything($currency, app('steam')->negative($amount));
}
if (TransactionType::WITHDRAWAL !== $type) {
$return = app('amount')->formatAnything($currency, $amount);
}
return $return;
}
/**
* @param TransactionJournal $journal
*
* @return string
*/
private function getFormattedForeignAmount(TransactionJournal $journal): string
{
/** @var Transaction $transaction */
$transaction = $journal->transactions->first();
if (null === $transaction->foreign_amount) {
return '';
}
$currency = $transaction->foreignCurrency;
$type = $journal->transactionType->type;
$amount = app('steam')->positive($transaction->foreign_amount);
$return = '';
if (TransactionType::WITHDRAWAL === $type) {
$return = app('amount')->formatAnything($currency, app('steam')->negative($amount));
}
if (TransactionType::WITHDRAWAL !== $type) {
$return = app('amount')->formatAnything($currency, $amount);
}
return $return;
}
/**
* @inheritDoc
*/
@ -390,116 +502,4 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
return $service->update($transactionGroup, $data);
}
/**
* @param TransactionJournal $journal
*
* @return array
*/
private function expandJournal(TransactionJournal $journal): array
{
$array = $journal->toArray();
$array['transactions'] = [];
$array['meta'] = $journal->transactionJournalMeta->toArray();
$array['tags'] = $journal->tags->toArray();
$array['categories'] = $journal->categories->toArray();
$array['budgets'] = $journal->budgets->toArray();
$array['notes'] = $journal->notes->toArray();
$array['locations'] = [];
$array['attachments'] = $journal->attachments->toArray();
$array['links'] = [];
$array['piggy_bank_events'] = $journal->piggyBankEvents->toArray();
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
$array['transactions'][] = $this->expandTransaction($transaction);
}
return $array;
}
/**
* @param Transaction $transaction
*
* @return array
*/
private function expandTransaction(Transaction $transaction): array
{
$array = $transaction->toArray();
$array['account'] = $transaction->account->toArray();
$array['budgets'] = [];
$array['categories'] = [];
foreach ($transaction->categories as $category) {
$array['categories'][] = $category->toArray();
}
foreach ($transaction->budgets as $budget) {
$array['budgets'][] = $budget->toArray();
}
return $array;
}
/**
* @param TransactionJournal $journal
*
* @return string
*/
private function getFormattedAmount(TransactionJournal $journal): string
{
/** @var Transaction $transaction */
$transaction = $journal->transactions->first();
$currency = $transaction->transactionCurrency;
$type = $journal->transactionType->type;
$amount = app('steam')->positive($transaction->amount);
$return = '';
if (TransactionType::WITHDRAWAL === $type) {
$return = app('amount')->formatAnything($currency, app('steam')->negative($amount));
}
if (TransactionType::WITHDRAWAL !== $type) {
$return = app('amount')->formatAnything($currency, $amount);
}
return $return;
}
/**
* @param TransactionJournal $journal
*
* @return string
*/
private function getFormattedForeignAmount(TransactionJournal $journal): string
{
/** @var Transaction $transaction */
$transaction = $journal->transactions->first();
if (null === $transaction->foreign_amount) {
return '';
}
$currency = $transaction->foreignCurrency;
$type = $journal->transactionType->type;
$amount = app('steam')->positive($transaction->foreign_amount);
$return = '';
if (TransactionType::WITHDRAWAL === $type) {
$return = app('amount')->formatAnything($currency, app('steam')->negative($amount));
}
if (TransactionType::WITHDRAWAL !== $type) {
$return = app('amount')->formatAnything($currency, $amount);
}
return $return;
}
/**
* @inheritDoc
*/
public function countAttachments(int $journalId): int
{
/** @var TransactionJournal $journal */
$journal = $this->user->transactionJournals()->find($journalId);
return $journal->attachments()->count();
}
}

View File

@ -36,6 +36,13 @@ use Illuminate\Support\Collection;
*/
interface TransactionGroupRepositoryInterface
{
/**
* @param int $journalId
*
* @return int
*/
public function countAttachments(int $journalId): int;
/**
* @param TransactionGroup $group
*/
@ -86,13 +93,6 @@ interface TransactionGroupRepositoryInterface
*/
public function getLocation(int $journalId): ?Location;
/**
* @param int $journalId
*
* @return int
*/
public function countAttachments(int $journalId): int;
/**
* Return object with all found meta field things as Carbon objects.
*

View File

@ -33,18 +33,6 @@ use Log;
class TransactionTypeRepository implements TransactionTypeRepositoryInterface
{
/**
* @param string $type
*
* @return TransactionType|null
*/
public function findByType(string $type): ?TransactionType
{
$search = ucfirst($type);
return TransactionType::whereType($search)->first();
}
/**
* @param TransactionType|null $type
* @param string|null $typeString
@ -69,6 +57,18 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface
return $search;
}
/**
* @param string $type
*
* @return TransactionType|null
*/
public function findByType(string $type): ?TransactionType
{
$search = ucfirst($type);
return TransactionType::whereType($search)->first();
}
/**
* @param string $query
* @param int $limit

View File

@ -39,39 +39,6 @@ use Str;
*/
class UserRepository implements UserRepositoryInterface
{
/**
* @return Collection
*/
public function all(): Collection
{
return User::orderBy('id', 'DESC')->get(['users.*']);
}
/**
* @param User $user
* @param string $role
*
* @return bool
*/
public function attachRole(User $user, string $role): bool
{
$roleObject = Role::where('name', $role)->first();
if (null === $roleObject) {
Log::error(sprintf('Could not find role "%s" in attachRole()', $role));
return false;
}
try {
$user->roles()->attach($roleObject);
} catch (QueryException $e) {
// don't care
Log::error(sprintf('Query exception when giving user a role: %s', $e->getMessage()));
}
return true;
}
/**
* This updates the users email address and records some things so it can be confirmed or undone later.
* The user is blocked until the change is confirmed.
@ -144,6 +111,14 @@ class UserRepository implements UserRepositoryInterface
return $this->all()->count();
}
/**
* @return Collection
*/
public function all(): Collection
{
return User::orderBy('id', 'DESC')->get(['users.*']);
}
/**
* @param string $name
* @param string $displayName
@ -173,6 +148,22 @@ class UserRepository implements UserRepositoryInterface
return true;
}
/**
* @inheritDoc
*/
public function deleteEmptyGroups(): void
{
$groups = UserGroup::get();
/** @var UserGroup $group */
foreach ($groups as $group) {
$count = $group->groupMemberships()->count();
if (0 === $count) {
Log::info(sprintf('Deleted empty group #%d ("%s")', $group->id, $group->title));
$group->delete();
}
}
}
/**
* @param int $userId
*
@ -203,16 +194,6 @@ class UserRepository implements UserRepositoryInterface
return User::orderBy('id', 'ASC')->first();
}
/**
* @param string $role
*
* @return Role|null
*/
public function getRole(string $role): ?Role
{
return Role::where('name', $role)->first();
}
/**
* @param User $user
*
@ -284,21 +265,6 @@ class UserRepository implements UserRepositoryInterface
return false;
}
/**
* Remove any role the user has.
*
* @param User $user
* @param string $role
*/
public function removeRole(User $user, string $role): void
{
$roleObj = $this->getRole($role);
if (null === $roleObj) {
return;
}
$user->roles()->detach($roleObj->id);
}
/**
* Set MFA code.
*
@ -334,6 +300,31 @@ class UserRepository implements UserRepositoryInterface
return $user;
}
/**
* @param User $user
* @param string $role
*
* @return bool
*/
public function attachRole(User $user, string $role): bool
{
$roleObject = Role::where('name', $role)->first();
if (null === $roleObject) {
Log::error(sprintf('Could not find role "%s" in attachRole()', $role));
return false;
}
try {
$user->roles()->attach($roleObject);
} catch (QueryException $e) {
// don't care
Log::error(sprintf('Query exception when giving user a role: %s', $e->getMessage()));
}
return true;
}
/**
* @param User $user
*/
@ -402,18 +393,27 @@ class UserRepository implements UserRepositoryInterface
}
/**
* @inheritDoc
* Remove any role the user has.
*
* @param User $user
* @param string $role
*/
public function deleteEmptyGroups(): void
public function removeRole(User $user, string $role): void
{
$groups = UserGroup::get();
/** @var UserGroup $group */
foreach ($groups as $group) {
$count = $group->groupMemberships()->count();
if (0 === $count) {
Log::info(sprintf('Deleted empty group #%d ("%s")', $group->id, $group->title));
$group->delete();
}
}
$roleObj = $this->getRole($role);
if (null === $roleObj) {
return;
}
$user->roles()->detach($roleObj->id);
}
/**
* @param string $role
*
* @return Role|null
*/
public function getRole(string $role): ?Role
{
return Role::where('name', $role)->first();
}
}

View File

@ -39,11 +39,6 @@ interface UserRepositoryInterface
*/
public function all(): Collection;
/**
*
*/
public function deleteEmptyGroups(): void;
/**
* Gives a user a role.
*
@ -100,6 +95,11 @@ interface UserRepositoryInterface
*/
public function createRole(string $name, string $displayName, string $description): Role;
/**
*
*/
public function deleteEmptyGroups(): void;
/**
* @param User $user
*

View File

@ -512,6 +512,63 @@ trait AccountServiceTrait
return $group;
}
/**
* See reference nr. 99
*
* @param TransactionGroup $group
*
* @return TransactionJournal
* @throws FireflyException
*/
private function getObJournal(TransactionGroup $group): TransactionJournal
{
/** @var TransactionJournal $journal */
$journal = $group->transactionJournals()->first();
if (null === $journal) {
throw new FireflyException(sprintf('Group #%d has no OB journal', $group->id));
}
return $journal;
}
/**
* See reference nr. 98
*
* @param TransactionJournal $journal
* @param Account $account
*
* @return Transaction
* @throws FireflyException
*/
private function getOBTransaction(TransactionJournal $journal, Account $account): Transaction
{
/** @var Transaction $transaction */
$transaction = $journal->transactions()->where('account_id', '!=', $account->id)->first();
if (null === $transaction) {
throw new FireflyException(sprintf('Could not get OB transaction for journal #%d', $journal->id));
}
return $transaction;
}
/**
* @param TransactionJournal $journal
* @param Account $account
*
* @return Transaction
* @throws FireflyException
*/
private function getNotOBTransaction(TransactionJournal $journal, Account $account): Transaction
{
/** @var Transaction $transaction */
$transaction = $journal->transactions()->where('account_id', $account->id)->first();
if (null === $transaction) {
throw new FireflyException(sprintf('Could not get non-OB transaction for journal #%d', $journal->id));
}
return $transaction;
}
/**
* Update or create the opening balance group.
* Since opening balance and date can still be empty strings, it may fail.
@ -667,61 +724,4 @@ trait AccountServiceTrait
return $group;
}
/**
* See reference nr. 99
*
* @param TransactionGroup $group
*
* @return TransactionJournal
* @throws FireflyException
*/
private function getObJournal(TransactionGroup $group): TransactionJournal
{
/** @var TransactionJournal $journal */
$journal = $group->transactionJournals()->first();
if (null === $journal) {
throw new FireflyException(sprintf('Group #%d has no OB journal', $group->id));
}
return $journal;
}
/**
* See reference nr. 98
*
* @param TransactionJournal $journal
* @param Account $account
*
* @return Transaction
* @throws FireflyException
*/
private function getOBTransaction(TransactionJournal $journal, Account $account): Transaction
{
/** @var Transaction $transaction */
$transaction = $journal->transactions()->where('account_id', '!=', $account->id)->first();
if (null === $transaction) {
throw new FireflyException(sprintf('Could not get OB transaction for journal #%d', $journal->id));
}
return $transaction;
}
/**
* @param TransactionJournal $journal
* @param Account $account
*
* @return Transaction
* @throws FireflyException
*/
private function getNotOBTransaction(TransactionJournal $journal, Account $account): Transaction
{
/** @var Transaction $transaction */
$transaction = $journal->transactions()->where('account_id', $account->id)->first();
if (null === $transaction) {
throw new FireflyException(sprintf('Could not get non-OB transaction for journal #%d', $journal->id));
}
return $transaction;
}
}

View File

@ -109,32 +109,6 @@ trait JournalServiceTrait
return $search;
}
/**
* @param Account|null $account
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function findAccountByName(?Account $account, array $data, array $types): ?Account
{
// second attempt, find by name.
if (null === $account && null !== $data['name']) {
// find by preferred type.
$source = $this->accountRepository->findByName($data['name'], [$types[0]]);
// or any expected type.
$source = $source ?? $this->accountRepository->findByName($data['name'], $types);
if (null !== $source) {
Log::debug(sprintf('Found "account_name" object: #%d, %s', $source->id, $source->name));
$account = $source;
}
}
return $account;
}
/**
* @param Account|null $account
* @param array $data
@ -190,6 +164,32 @@ trait JournalServiceTrait
return $account;
}
/**
* @param Account|null $account
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function findAccountByName(?Account $account, array $data, array $types): ?Account
{
// second attempt, find by name.
if (null === $account && null !== $data['name']) {
// find by preferred type.
$source = $this->accountRepository->findByName($data['name'], [$types[0]]);
// or any expected type.
$source = $source ?? $this->accountRepository->findByName($data['name'], $types);
if (null !== $source) {
Log::debug(sprintf('Found "account_name" object: #%d, %s', $source->id, $source->name));
$account = $source;
}
}
return $account;
}
/**
* @param Account|null $account
* @param array $data

View File

@ -53,7 +53,7 @@ class EitherConfigKey
// triggers and actions:
'firefly.rule-actions',
'firefly.context-rule-actions',
'search.operators'
'search.operators',
];
/**

View File

@ -180,28 +180,6 @@ trait ConvertsDataTypes
return false;
}
/**
* Return date or NULL.
*
* @param string $field
*
* @return Carbon|null
*/
protected function getCarbonDate(string $field): ?Carbon
{
$result = null;
try {
$result = $this->get($field) ? new Carbon($this->get($field)) : null;
} catch (InvalidFormatException $e) {
// @ignoreException
}
if (null === $result) {
Log::debug(sprintf('Exception when parsing date "%s".', $this->get($field)));
}
return $result;
}
/**
* @param string|null $string
*
@ -269,6 +247,28 @@ trait ConvertsDataTypes
return $return;
}
/**
* Return date or NULL.
*
* @param string $field
*
* @return Carbon|null
*/
protected function getCarbonDate(string $field): ?Carbon
{
$result = null;
try {
$result = $this->get($field) ? new Carbon($this->get($field)) : null;
} catch (InvalidFormatException $e) {
// @ignoreException
}
if (null === $result) {
Log::debug(sprintf('Exception when parsing date "%s".', $this->get($field)));
}
return $result;
}
/**
* Parse to integer
*

View File

@ -37,14 +37,13 @@ class AccountSearch implements GenericSearchInterface
/** @var string */
public const SEARCH_ALL = 'all';
/** @var string */
public const SEARCH_NAME = 'name';
/** @var string */
public const SEARCH_IBAN = 'iban';
/** @var string */
public const SEARCH_NUMBER = 'number';
/** @var string */
public const SEARCH_ID = 'id';
/** @var string */
public const SEARCH_NAME = 'name';
/** @var string */
public const SEARCH_NUMBER = 'number';
private string $field;
private string $query;
private array $types;

View File

@ -170,73 +170,6 @@ class OperatorQuerySearch implements SearchInterface
$this->collector->setSearchWords($this->words);
}
/**
* @inheritDoc
* @codeCoverageIgnore
*/
public function searchTime(): float
{
return microtime(true) - $this->startTime;
}
/**
* @inheritDoc
*/
public function searchTransactions(): LengthAwarePaginator
{
if (empty($this->getWords()) && empty($this->getOperators())) {
return new LengthAwarePaginator([], 0, 5, 1);
}
return $this->collector->getPaginatedGroups();
}
/**
* @param Carbon $date
*/
public function setDate(Carbon $date): void
{
$this->date = $date;
}
/**
* @param int $limit
*/
public function setLimit(int $limit): void
{
$this->limit = $limit;
$this->collector->setLimit($this->limit);
}
/**
* @inheritDoc
* @codeCoverageIgnore
*/
public function setPage(int $page): void
{
$this->page = $page;
$this->collector->setPage($this->page);
}
/**
* @inheritDoc
* @codeCoverageIgnore
*/
public function setUser(User $user): void
{
$this->accountRepository->setUser($user);
$this->billRepository->setUser($user);
$this->categoryRepository->setUser($user);
$this->budgetRepository->setUser($user);
$this->tagRepository->setUser($user);
$this->collector = app(GroupCollectorInterface::class);
$this->collector->setUser($user);
$this->collector->withAccountInformation()->withCategoryInformation()->withBudgetInformation();
$this->setLimit((int) app('preferences')->getForUser($user, 'listPageSize', 50)->data);
}
/**
* @param Node $searchNode
*
@ -1152,14 +1085,6 @@ class OperatorQuerySearch implements SearchInterface
];
}
/**
* @return array
*/
public function getWords(): array
{
return $this->words;
}
/**
* @param array $range
*
@ -1199,46 +1124,6 @@ class OperatorQuerySearch implements SearchInterface
}
}
/**
* @param string $field
* @param array $range
* @return void
* @throws FireflyException
*/
private function setExactObjectDateParams(string $field,array $range): void
{
/**
* @var string $key
* @var Carbon|string $value
*/
foreach ($range as $key => $value) {
switch ($key) {
default:
throw new FireflyException(sprintf('Cannot handle key "%s" in setExactObjectDateParams()', $key));
case 'exact':
Log::debug(sprintf('Set %s_is_exact value "%s"',$field, $value->format('Y-m-d')));
$this->collector->setObjectRange($value, clone $value, $field);
$this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d'),]);
break;
case 'year':
Log::debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value));
$this->collector->objectYearIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value,]);
break;
case 'month':
Log::debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value));
$this->collector->objectMonthIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value,]);
break;
case 'day':
Log::debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value));
$this->collector->objectDayIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value,]);
break;
}
}
}
/**
* @param array $range
*
@ -1277,6 +1162,85 @@ class OperatorQuerySearch implements SearchInterface
}
}
/**
* @param array $range
*
* @throws FireflyException
*/
private function setDateAfterParams(array $range)
{
/**
* @var string $key
* @var Carbon|string $value
*/
foreach ($range as $key => $value) {
switch ($key) {
default:
throw new FireflyException(sprintf('Cannot handle key "%s" in setDateAfterParams()', $key));
case 'exact':
$this->collector->setAfter($value);
$this->operators->push(['type' => 'date_after', 'value' => $value->format('Y-m-d'),]);
break;
case 'year':
Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value));
$this->collector->yearAfter($value);
$this->operators->push(['type' => 'date_after_year', 'value' => $value,]);
break;
case 'month':
Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value));
$this->collector->monthAfter($value);
$this->operators->push(['type' => 'date_after_month', 'value' => $value,]);
break;
case 'day':
Log::debug(sprintf('Set date_is_after DAY value "%s"', $value));
$this->collector->dayAfter($value);
$this->operators->push(['type' => 'date_after_day', 'value' => $value,]);
break;
}
}
}
/**
* @param string $field
* @param array $range
* @return void
* @throws FireflyException
*/
private function setExactMetaDateParams(string $field, array $range): void
{
Log::debug('Now in setExactMetaDateParams()');
/**
* @var string $key
* @var Carbon|string $value
*/
foreach ($range as $key => $value) {
switch ($key) {
default:
throw new FireflyException(sprintf('Cannot handle key "%s" in setExactMetaDateParams()', $key));
case 'exact':
Log::debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d')));
$this->collector->setMetaDateRange($value, $value, $field);
$this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d'),]);
break;
case 'year':
Log::debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value));
$this->collector->metaYearIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value,]);
break;
case 'month':
Log::debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value));
$this->collector->metaMonthIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value,]);
break;
case 'day':
Log::debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value));
$this->collector->metaDayIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value,]);
break;
}
}
}
/**
* @param string $field
* @param array $range
@ -1356,11 +1320,12 @@ class OperatorQuerySearch implements SearchInterface
}
/**
* @param string $field
* @param array $range
*
* @return void
* @throws FireflyException
*/
private function setDateAfterParams(array $range)
private function setExactObjectDateParams(string $field, array $range): void
{
/**
* @var string $key
@ -1369,63 +1334,26 @@ class OperatorQuerySearch implements SearchInterface
foreach ($range as $key => $value) {
switch ($key) {
default:
throw new FireflyException(sprintf('Cannot handle key "%s" in setDateAfterParams()', $key));
throw new FireflyException(sprintf('Cannot handle key "%s" in setExactObjectDateParams()', $key));
case 'exact':
$this->collector->setAfter($value);
$this->operators->push(['type' => 'date_after', 'value' => $value->format('Y-m-d'),]);
Log::debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d')));
$this->collector->setObjectRange($value, clone $value, $field);
$this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d'),]);
break;
case 'year':
Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value));
$this->collector->yearAfter($value);
$this->operators->push(['type' => 'date_after_year', 'value' => $value,]);
Log::debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value));
$this->collector->objectYearIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value,]);
break;
case 'month':
Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value));
$this->collector->monthAfter($value);
$this->operators->push(['type' => 'date_after_month', 'value' => $value,]);
Log::debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value));
$this->collector->objectMonthIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value,]);
break;
case 'day':
Log::debug(sprintf('Set date_is_after DAY value "%s"', $value));
$this->collector->dayAfter($value);
$this->operators->push(['type' => 'date_after_day', 'value' => $value,]);
break;
}
}
}
/**
* @param array $range
*
* @throws FireflyException
*/
private function setObjectDateAfterParams(string $field, array $range)
{
/**
* @var string $key
* @var Carbon|string $value
*/
foreach ($range as $key => $value) {
switch ($key) {
default:
throw new FireflyException(sprintf('Cannot handle key "%s" in setDateAfterParams()', $key));
case 'exact':
$this->collector->setObjectAfter($value, $field);
$this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d'),]);
break;
case 'year':
Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value));
$this->collector->objectYearAfter($value, $field);
$this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value,]);
break;
case 'month':
Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value));
$this->collector->objectMonthAfter($value, $field);
$this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value,]);
break;
case 'day':
Log::debug(sprintf('Set date_is_after DAY value "%s"', $value));
$this->collector->objectDayAfter($value, $field);
$this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value,]);
Log::debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value));
$this->collector->objectDayIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value,]);
break;
}
}
@ -1470,14 +1398,12 @@ class OperatorQuerySearch implements SearchInterface
}
/**
* @param string $field
* @param array $range
* @return void
*
* @throws FireflyException
*/
private function setExactMetaDateParams(string $field, array $range): void
private function setObjectDateAfterParams(string $field, array $range)
{
Log::debug('Now in setExactMetaDateParams()');
/**
* @var string $key
* @var Carbon|string $value
@ -1485,28 +1411,102 @@ class OperatorQuerySearch implements SearchInterface
foreach ($range as $key => $value) {
switch ($key) {
default:
throw new FireflyException(sprintf('Cannot handle key "%s" in setExactMetaDateParams()', $key));
throw new FireflyException(sprintf('Cannot handle key "%s" in setDateAfterParams()', $key));
case 'exact':
Log::debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d')));
$this->collector->setMetaDateRange($value, $value, $field);
$this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d'),]);
$this->collector->setObjectAfter($value, $field);
$this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d'),]);
break;
case 'year':
Log::debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value));
$this->collector->metaYearIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value,]);
Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value));
$this->collector->objectYearAfter($value, $field);
$this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value,]);
break;
case 'month':
Log::debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value));
$this->collector->metaMonthIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value,]);
Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value));
$this->collector->objectMonthAfter($value, $field);
$this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value,]);
break;
case 'day':
Log::debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value));
$this->collector->metaDayIs($value, $field);
$this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value,]);
Log::debug(sprintf('Set date_is_after DAY value "%s"', $value));
$this->collector->objectDayAfter($value, $field);
$this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value,]);
break;
}
}
}
/**
* @inheritDoc
* @codeCoverageIgnore
*/
public function searchTime(): float
{
return microtime(true) - $this->startTime;
}
/**
* @inheritDoc
*/
public function searchTransactions(): LengthAwarePaginator
{
if (empty($this->getWords()) && empty($this->getOperators())) {
return new LengthAwarePaginator([], 0, 5, 1);
}
return $this->collector->getPaginatedGroups();
}
/**
* @return array
*/
public function getWords(): array
{
return $this->words;
}
/**
* @param Carbon $date
*/
public function setDate(Carbon $date): void
{
$this->date = $date;
}
/**
* @inheritDoc
* @codeCoverageIgnore
*/
public function setPage(int $page): void
{
$this->page = $page;
$this->collector->setPage($this->page);
}
/**
* @inheritDoc
* @codeCoverageIgnore
*/
public function setUser(User $user): void
{
$this->accountRepository->setUser($user);
$this->billRepository->setUser($user);
$this->categoryRepository->setUser($user);
$this->budgetRepository->setUser($user);
$this->tagRepository->setUser($user);
$this->collector = app(GroupCollectorInterface::class);
$this->collector->setUser($user);
$this->collector->withAccountInformation()->withCategoryInformation()->withBudgetInformation();
$this->setLimit((int) app('preferences')->getForUser($user, 'listPageSize', 50)->data);
}
/**
* @param int $limit
*/
public function setLimit(int $limit): void
{
$this->limit = $limit;
$this->collector->setLimit($this->limit);
}
}

View File

@ -43,53 +43,6 @@ use Str;
class Steam
{
/**
* Returns the previous URL but refuses to send you to specific URLs.
*
* - outside domain
* - to JS files, API or JSON routes
*
* Uses the session's previousUrl() function as inspired by GitHub user @z1r0-
*
* session()->previousUrl() uses getSafeUrl() so we can safely return it:
*
* @return string
*/
public function getSafePreviousUrl(): string
{
//Log::debug(sprintf('getSafePreviousUrl: "%s"', session()->previousUrl()));
return session()->previousUrl() ?? route('index');
}
/**
* Make sure URL is safe.
*
* @param string $unknownUrl
* @param string $safeUrl
*
* @return string
*/
public function getSafeUrl(string $unknownUrl, string $safeUrl): string
{
//Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl));
$returnUrl = $safeUrl;
$unknownHost = parse_url($unknownUrl, PHP_URL_HOST);
$safeHost = parse_url($safeUrl, PHP_URL_HOST);
if (null !== $unknownHost && $unknownHost === $safeHost) {
$returnUrl = $unknownUrl;
}
// URL must not lead to weird pages
$forbiddenWords = ['jscript', 'json', 'debug', 'serviceworker', 'offline', 'delete', '/login', '/attachments/view'];
if (Str::contains($returnUrl, $forbiddenWords)) {
$returnUrl = $safeUrl;
}
return $returnUrl;
}
/**
* @param Account $account
* @param Carbon $date
@ -391,6 +344,65 @@ class Steam
return $return;
}
/**
* @param string $string
*
* @return string
*/
public function filterSpaces(string $string): string
{
$search = [
"\u{0001}", // start of heading
"\u{0002}", // start of text
"\u{0003}", // end of text
"\u{0004}", // end of transmission
"\u{0005}", // enquiry
"\u{0006}", // ACK
"\u{0007}", // BEL
"\u{0008}", // backspace
"\u{000E}", // shift out
"\u{000F}", // shift in
"\u{0010}", // data link escape
"\u{0011}", // DC1
"\u{0012}", // DC2
"\u{0013}", // DC3
"\u{0014}", // DC4
"\u{0015}", // NAK
"\u{0016}", // SYN
"\u{0017}", // ETB
"\u{0018}", // CAN
"\u{0019}", // EM
"\u{001A}", // SUB
"\u{001B}", // escape
"\u{001C}", // file separator
"\u{001D}", // group separator
"\u{001E}", // record separator
"\u{001F}", // unit separator
"\u{007F}", // DEL
"\u{00A0}", // non-breaking space
"\u{1680}", // ogham space mark
"\u{180E}", // mongolian vowel separator
"\u{2000}", // en quad
"\u{2001}", // em quad
"\u{2002}", // en space
"\u{2003}", // em space
"\u{2004}", // three-per-em space
"\u{2005}", // four-per-em space
"\u{2006}", // six-per-em space
"\u{2007}", // figure space
"\u{2008}", // punctuation space
"\u{2009}", // thin space
"\u{200A}", // hair space
"\u{200B}", // zero width space
"\u{202F}", // narrow no-break space
"\u{3000}", // ideographic space
"\u{FEFF}", // zero width no -break space
"\x20", // plain old normal space
];
return str_replace($search, '', $string);
}
/**
* @param array $accounts
*
@ -463,6 +475,52 @@ class Steam
];
}
/**
* Returns the previous URL but refuses to send you to specific URLs.
*
* - outside domain
* - to JS files, API or JSON routes
*
* Uses the session's previousUrl() function as inspired by GitHub user @z1r0-
*
* session()->previousUrl() uses getSafeUrl() so we can safely return it:
*
* @return string
*/
public function getSafePreviousUrl(): string
{
//Log::debug(sprintf('getSafePreviousUrl: "%s"', session()->previousUrl()));
return session()->previousUrl() ?? route('index');
}
/**
* Make sure URL is safe.
*
* @param string $unknownUrl
* @param string $safeUrl
*
* @return string
*/
public function getSafeUrl(string $unknownUrl, string $safeUrl): string
{
//Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl));
$returnUrl = $safeUrl;
$unknownHost = parse_url($unknownUrl, PHP_URL_HOST);
$safeHost = parse_url($safeUrl, PHP_URL_HOST);
if (null !== $unknownHost && $unknownHost === $safeHost) {
$returnUrl = $unknownUrl;
}
// URL must not lead to weird pages
$forbiddenWords = ['jscript', 'json', 'debug', 'serviceworker', 'offline', 'delete', '/login', '/attachments/view'];
if (Str::contains($returnUrl, $forbiddenWords)) {
$returnUrl = $safeUrl;
}
return $returnUrl;
}
/**
* @param string $amount
*
@ -573,63 +631,4 @@ class Steam
return $amount;
}
/**
* @param string $string
*
* @return string
*/
public function filterSpaces(string $string): string
{
$search = [
"\u{0001}", // start of heading
"\u{0002}", // start of text
"\u{0003}", // end of text
"\u{0004}", // end of transmission
"\u{0005}", // enquiry
"\u{0006}", // ACK
"\u{0007}", // BEL
"\u{0008}", // backspace
"\u{000E}", // shift out
"\u{000F}", // shift in
"\u{0010}", // data link escape
"\u{0011}", // DC1
"\u{0012}", // DC2
"\u{0013}", // DC3
"\u{0014}", // DC4
"\u{0015}", // NAK
"\u{0016}", // SYN
"\u{0017}", // ETB
"\u{0018}", // CAN
"\u{0019}", // EM
"\u{001A}", // SUB
"\u{001B}", // escape
"\u{001C}", // file separator
"\u{001D}", // group separator
"\u{001E}", // record separator
"\u{001F}", // unit separator
"\u{007F}", // DEL
"\u{00A0}", // non-breaking space
"\u{1680}", // ogham space mark
"\u{180E}", // mongolian vowel separator
"\u{2000}", // en quad
"\u{2001}", // em quad
"\u{2002}", // en space
"\u{2003}", // em space
"\u{2004}", // three-per-em space
"\u{2005}", // four-per-em space
"\u{2006}", // six-per-em space
"\u{2007}", // figure space
"\u{2008}", // punctuation space
"\u{2009}", // thin space
"\u{200A}", // hair space
"\u{200B}", // zero width space
"\u{202F}", // narrow no-break space
"\u{3000}", // ideographic space
"\u{FEFF}", // zero width no -break space
"\x20", // plain old normal space
];
return str_replace($search, '', $string);
}
}