From ca8a65af60147500778ee722a320b6dedb6b5a55 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 2 Oct 2022 14:37:50 +0200 Subject: [PATCH] Display audit logs --- app/Handlers/Events/AuditEventHandler.php | 18 ++- .../Transaction/ShowController.php | 14 +- app/Models/AuditLogEntry.php | 4 +- app/Providers/FireflyServiceProvider.php | 4 + .../AuditLogEntry/ALERepository.php | 56 +++++++ .../AuditLogEntry/ALERepositoryInterface.php | 44 ++++++ .../Actions/AppendDescription.php | 2 +- app/TransactionRules/Actions/ClearBudget.php | 2 +- .../Actions/ClearCategory.php | 2 +- app/TransactionRules/Actions/ClearNotes.php | 2 +- .../Actions/ConvertToDeposit.php | 4 +- .../Actions/ConvertToTransfer.php | 4 +- .../Actions/ConvertToWithdrawal.php | 4 +- app/TransactionRules/Actions/LinkToBill.php | 10 +- .../Actions/MoveNotesToDescription.php | 2 +- .../Actions/RemoveAllTags.php | 9 +- app/TransactionRules/Actions/RemoveTag.php | 7 +- .../Actions/UpdatePiggybank.php | 6 +- resources/lang/en_US/firefly.php | 147 ++++++++++-------- resources/views/list/ale.twig | 89 +++++++++++ resources/views/transactions/show.twig | 30 ++-- 21 files changed, 360 insertions(+), 100 deletions(-) create mode 100644 app/Repositories/AuditLogEntry/ALERepository.php create mode 100644 app/Repositories/AuditLogEntry/ALERepositoryInterface.php create mode 100644 resources/views/list/ale.twig diff --git a/app/Handlers/Events/AuditEventHandler.php b/app/Handlers/Events/AuditEventHandler.php index 43ee76a222..e0249e12e7 100644 --- a/app/Handlers/Events/AuditEventHandler.php +++ b/app/Handlers/Events/AuditEventHandler.php @@ -23,6 +23,7 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\AuditLogEntry; +use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface; class AuditEventHandler { @@ -33,13 +34,16 @@ class AuditEventHandler */ public function storeAuditEvent(TriggeredAuditLog $event) { - $auditLogEntry = new AuditLogEntry; - $auditLogEntry->auditable()->associate($event->auditable); - $auditLogEntry->changer()->associate($event->changer); - $auditLogEntry->action = $event->field; - $auditLogEntry->before = $event->before; - $auditLogEntry->after = $event->after; - $auditLogEntry->save(); + $array = [ + 'auditable' => $event->auditable, + 'changer' => $event->changer, + 'action' => $event->field, + 'before' => $event->before, + 'after' => $event->after, + ]; + /** @var ALERepositoryInterface $repository */ + $repository = app(ALERepositoryInterface::class); + $repository->store($array); } } diff --git a/app/Http/Controllers/Transaction/ShowController.php b/app/Http/Controllers/Transaction/ShowController.php index 4454121bb8..219af41d40 100644 --- a/app/Http/Controllers/Transaction/ShowController.php +++ b/app/Http/Controllers/Transaction/ShowController.php @@ -27,12 +27,12 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; use FireflyIII\Transformers\TransactionGroupTransformer; use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Symfony\Component\HttpFoundation\ParameterBag; @@ -43,6 +43,7 @@ use Symfony\Component\HttpFoundation\ParameterBag; class ShowController extends Controller { private TransactionGroupRepositoryInterface $repository; + private ALERepositoryInterface $ALERepository; /** * ShowController constructor. @@ -54,7 +55,8 @@ class ShowController extends Controller // some useful repositories: $this->middleware( function ($request, $next) { - $this->repository = app(TransactionGroupRepositoryInterface::class); + $this->repository = app(TransactionGroupRepositoryInterface::class); + $this->ALERepository = app(ALERepositoryInterface::class); app('view')->share('title', (string) trans('firefly.transactions')); app('view')->share('mainTitleIcon', 'fa-exchange'); @@ -108,10 +110,17 @@ class ShowController extends Controller $groupArray['transactions'][$index]['tags'] = $this->repository->getTagObjects($groupArray['transactions'][$index]['transaction_journal_id']); } + // get audit log entries: + $logEntries = []; + foreach($transactionGroup->transactionJournals as $journal) { + $logEntries[$journal->id] = $this->ALERepository->getForObject($journal); + } + $events = $this->repository->getPiggyEvents($transactionGroup); $attachments = $this->repository->getAttachments($transactionGroup); $links = $this->repository->getLinks($transactionGroup); + return view( 'transactions.show', compact( @@ -119,6 +128,7 @@ class ShowController extends Controller 'amounts', 'first', 'type', + 'logEntries', 'subTitle', 'splits', 'groupArray', diff --git a/app/Models/AuditLogEntry.php b/app/Models/AuditLogEntry.php index 4cdcaedaa1..4b6ace614e 100644 --- a/app/Models/AuditLogEntry.php +++ b/app/Models/AuditLogEntry.php @@ -33,8 +33,8 @@ class AuditLogEntry extends Model use SoftDeletes; protected $casts = [ - 'before' => 'array', - 'after' => 'array', + 'before' => 'array', + 'after' => 'array', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index fcd2200ca6..d1f3d0c174 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -40,6 +40,8 @@ use FireflyIII\Helpers\Report\ReportHelper; use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Helpers\Webhook\Sha3SignatureGenerator; use FireflyIII\Helpers\Webhook\SignatureGeneratorInterface; +use FireflyIII\Repositories\AuditLogEntry\ALERepository; +use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface; use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepository; use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface; use FireflyIII\Repositories\TransactionType\TransactionTypeRepository; @@ -171,6 +173,8 @@ class FireflyServiceProvider extends ServiceProvider $this->app->bind(TransactionTypeRepositoryInterface::class, TransactionTypeRepository::class); $this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class); + $this->app->bind(ALERepositoryInterface::class, ALERepository::class); + $this->app->bind( ObjectGroupRepositoryInterface::class, static function (Application $app) { diff --git a/app/Repositories/AuditLogEntry/ALERepository.php b/app/Repositories/AuditLogEntry/ALERepository.php new file mode 100644 index 0000000000..7b39d0b342 --- /dev/null +++ b/app/Repositories/AuditLogEntry/ALERepository.php @@ -0,0 +1,56 @@ +. + */ + +namespace FireflyIII\Repositories\AuditLogEntry; + +use FireflyIII\Models\AuditLogEntry; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Collection; + +/** + * Class ALERepository + */ +class ALERepository implements ALERepositoryInterface +{ + /** + * @inheritDoc + */ + public function store(array $data): AuditLogEntry + { + $auditLogEntry = new AuditLogEntry; + + $auditLogEntry->auditable()->associate($data['auditable']); + $auditLogEntry->changer()->associate($data['changer']); + $auditLogEntry->action = $data['field']; + $auditLogEntry->before = $data['before']; + $auditLogEntry->after = $data['after']; + $auditLogEntry->save(); + return $auditLogEntry; + } + + /** + * @inheritDoc + */ + public function getForObject(Model $model): Collection + { + return AuditLogEntry::where('auditable_id', $model->id)->where('auditable_type', get_class($model))->get(); + } +} diff --git a/app/Repositories/AuditLogEntry/ALERepositoryInterface.php b/app/Repositories/AuditLogEntry/ALERepositoryInterface.php new file mode 100644 index 0000000000..dbaa73c1ae --- /dev/null +++ b/app/Repositories/AuditLogEntry/ALERepositoryInterface.php @@ -0,0 +1,44 @@ +. + */ + +namespace FireflyIII\Repositories\AuditLogEntry; + +use FireflyIII\Models\AuditLogEntry; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Collection; + +/** + * Interface ALERepositoryInterface + */ +interface ALERepositoryInterface +{ + /** + * @param array $data + * @return AuditLogEntry + */ + public function store(array $data): AuditLogEntry; + + /** + * @param Model $model + * @return Collection + */ + public function getForObject(Model $model): Collection; +} diff --git a/app/TransactionRules/Actions/AppendDescription.php b/app/TransactionRules/Actions/AppendDescription.php index f348f38ff7..e2108cdcde 100644 --- a/app/TransactionRules/Actions/AppendDescription.php +++ b/app/TransactionRules/Actions/AppendDescription.php @@ -55,7 +55,7 @@ class AppendDescription implements ActionInterface // event for audit log entry /** @var TransactionJournal $journal */ $journal = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - event(new TriggeredAuditLog($this->action->rule, $journal, 'update_description', null, $description)); + event(new TriggeredAuditLog($this->action->rule, $journal, 'update_description', $journal['description'], $description)); return true; } diff --git a/app/TransactionRules/Actions/ClearBudget.php b/app/TransactionRules/Actions/ClearBudget.php index 0fa34dec8c..ac5219a7fb 100644 --- a/app/TransactionRules/Actions/ClearBudget.php +++ b/app/TransactionRules/Actions/ClearBudget.php @@ -61,7 +61,7 @@ class ClearBudget implements ActionInterface DB::table('budget_transaction_journal')->where('transaction_journal_id', '=', $journal['transaction_journal_id'])->delete(); - event(new TriggeredAuditLog($this->action->rule, $journal, 'remove_budget', $budget->name, null)); + event(new TriggeredAuditLog($this->action->rule, $journal, 'clear_budget', $budget->name, null)); Log::debug(sprintf('RuleAction ClearBudget removed all budgets from journal #%d.', $journal['transaction_journal_id'])); diff --git a/app/TransactionRules/Actions/ClearCategory.php b/app/TransactionRules/Actions/ClearCategory.php index 251ab7d156..0c26840676 100644 --- a/app/TransactionRules/Actions/ClearCategory.php +++ b/app/TransactionRules/Actions/ClearCategory.php @@ -60,7 +60,7 @@ class ClearCategory implements ActionInterface DB::table('category_transaction_journal')->where('transaction_journal_id', '=', $journal['transaction_journal_id'])->delete(); - event(new TriggeredAuditLog($this->action->rule, $journal, 'removed_category', $category->name, null)); + event(new TriggeredAuditLog($this->action->rule, $journal, 'clear_category', $category->name, null)); Log::debug(sprintf('RuleAction ClearCategory removed all categories from journal #%d.', $journal['transaction_journal_id'])); diff --git a/app/TransactionRules/Actions/ClearNotes.php b/app/TransactionRules/Actions/ClearNotes.php index b066dd39eb..d64fee9c19 100644 --- a/app/TransactionRules/Actions/ClearNotes.php +++ b/app/TransactionRules/Actions/ClearNotes.php @@ -64,7 +64,7 @@ class ClearNotes implements ActionInterface ->delete(); Log::debug(sprintf('RuleAction ClearNotes removed all notes from journal #%d.', $journal['transaction_journal_id'])); - event(new TriggeredAuditLog($this->action->rule, $journal, 'remove_notes', $before, null)); + event(new TriggeredAuditLog($this->action->rule, $journal, 'clear_notes', $before, null)); return true; } diff --git a/app/TransactionRules/Actions/ConvertToDeposit.php b/app/TransactionRules/Actions/ConvertToDeposit.php index c8bf83d4ed..1b05ea053e 100644 --- a/app/TransactionRules/Actions/ConvertToDeposit.php +++ b/app/TransactionRules/Actions/ConvertToDeposit.php @@ -75,13 +75,13 @@ class ConvertToDeposit implements ActionInterface if (TransactionType::WITHDRAWAL === $type) { Log::debug('Going to transform a withdrawal to a deposit.'); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - event(new TriggeredAuditLog($this->action->rule, $object, 'change_transaction_type', TransactionType::WITHDRAWAL, TransactionType::DEPOSIT)); + event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::WITHDRAWAL, TransactionType::DEPOSIT)); return $this->convertWithdrawalArray($journal); } if (TransactionType::TRANSFER === $type) { $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - event(new TriggeredAuditLog($this->action->rule, $object, 'change_transaction_type', TransactionType::TRANSFER, TransactionType::DEPOSIT)); + event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::DEPOSIT)); Log::debug('Going to transform a transfer to a deposit.'); return $this->convertTransferArray($journal); diff --git a/app/TransactionRules/Actions/ConvertToTransfer.php b/app/TransactionRules/Actions/ConvertToTransfer.php index 5fd893f977..9e3533c322 100644 --- a/app/TransactionRules/Actions/ConvertToTransfer.php +++ b/app/TransactionRules/Actions/ConvertToTransfer.php @@ -92,7 +92,7 @@ class ConvertToTransfer implements ActionInterface if (TransactionType::WITHDRAWAL === $type) { Log::debug('Going to transform a withdrawal to a transfer.'); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - event(new TriggeredAuditLog($this->action->rule, $object, 'change_transaction_type', TransactionType::WITHDRAWAL, TransactionType::TRANSFER)); + event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::WITHDRAWAL, TransactionType::TRANSFER)); return $this->convertWithdrawalArray($journal, $asset); } @@ -100,7 +100,7 @@ class ConvertToTransfer implements ActionInterface Log::debug('Going to transform a deposit to a transfer.'); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - event(new TriggeredAuditLog($this->action->rule, $object, 'change_transaction_type', TransactionType::DEPOSIT, TransactionType::TRANSFER)); + event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::TRANSFER)); return $this->convertDepositArray($journal, $asset); } diff --git a/app/TransactionRules/Actions/ConvertToWithdrawal.php b/app/TransactionRules/Actions/ConvertToWithdrawal.php index 8f2c41d5aa..2f172f895e 100644 --- a/app/TransactionRules/Actions/ConvertToWithdrawal.php +++ b/app/TransactionRules/Actions/ConvertToWithdrawal.php @@ -73,14 +73,14 @@ class ConvertToWithdrawal implements ActionInterface if (TransactionType::DEPOSIT === $type) { Log::debug('Going to transform a deposit to a withdrawal.'); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - event(new TriggeredAuditLog($this->action->rule, $object, 'change_transaction_type', TransactionType::DEPOSIT, TransactionType::WITHDRAWAL)); + event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::WITHDRAWAL)); return $this->convertDepositArray($journal); } if (TransactionType::TRANSFER === $type) { Log::debug('Going to transform a transfer to a withdrawal.'); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - event(new TriggeredAuditLog($this->action->rule, $object, 'change_transaction_type', TransactionType::TRANSFER, TransactionType::WITHDRAWAL)); + event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::WITHDRAWAL)); return $this->convertTransferArray($journal); } diff --git a/app/TransactionRules/Actions/LinkToBill.php b/app/TransactionRules/Actions/LinkToBill.php index 584e4b8407..76fe89c98e 100644 --- a/app/TransactionRules/Actions/LinkToBill.php +++ b/app/TransactionRules/Actions/LinkToBill.php @@ -63,6 +63,14 @@ class LinkToBill implements ActionInterface $bill = $repository->findByName($billName); if (null !== $bill && $journal['transaction_type_type'] === TransactionType::WITHDRAWAL) { + $count = DB::table('transaction_journals')->where('id', '=', $journal['transaction_journal_id']) + ->where('bill_id', $bill->id)->count(); + if (0 !== $count) { + Log::error(sprintf('RuleAction LinkToBill could not set the bill of journal #%d to bill "%s": already set.', $journal['transaction_journal_id'], $billName)); + return false; + } + + DB::table('transaction_journals') ->where('id', '=', $journal['transaction_journal_id']) ->update(['bill_id' => $bill->id]); @@ -71,7 +79,7 @@ class LinkToBill implements ActionInterface ); $journal = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - event(new TriggeredAuditLog($this->action->rule, $journal, 'change_bill', null, $bill->id)); + event(new TriggeredAuditLog($this->action->rule, $journal, 'set_bill', null, $bill->name)); return true; } diff --git a/app/TransactionRules/Actions/MoveNotesToDescription.php b/app/TransactionRules/Actions/MoveNotesToDescription.php index 3058b1d70f..9ffb20e3de 100644 --- a/app/TransactionRules/Actions/MoveNotesToDescription.php +++ b/app/TransactionRules/Actions/MoveNotesToDescription.php @@ -74,7 +74,7 @@ class MoveNotesToDescription implements ActionInterface $note->delete(); event(new TriggeredAuditLog($this->action->rule, $journal, 'update_description', $before, $journal->description)); - event(new TriggeredAuditLog($this->action->rule, $journal, 'remove_notes', $beforeNote, null)); + event(new TriggeredAuditLog($this->action->rule, $journal, 'clear_notes', $beforeNote, null)); return true; } diff --git a/app/TransactionRules/Actions/RemoveAllTags.php b/app/TransactionRules/Actions/RemoveAllTags.php index 6abdc259a3..395a8fe180 100644 --- a/app/TransactionRules/Actions/RemoveAllTags.php +++ b/app/TransactionRules/Actions/RemoveAllTags.php @@ -50,14 +50,19 @@ class RemoveAllTags implements ActionInterface */ public function actOnArray(array $journal): bool { - Log::debug(sprintf('RuleAction ClearCategory removed all tags from journal %d.', $journal['transaction_journal_id'])); DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->delete(); + $count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->count(); + if (0 === $count) { + Log::debug(sprintf('RuleAction RemoveAllTags, journal #%d has no tags.', $journal['transaction_journal_id'])); + return false; + } + Log::debug(sprintf('RuleAction RemoveAllTags removed all tags from journal %d.', $journal['transaction_journal_id'])); /** @var TransactionJournal $journal */ $journal = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); // audit log - event(new TriggeredAuditLog($this->action->rule, $journal, 'remove_all_tags', null, null)); + event(new TriggeredAuditLog($this->action->rule, $journal, 'clear_all_tags', null, null)); return true; } diff --git a/app/TransactionRules/Actions/RemoveTag.php b/app/TransactionRules/Actions/RemoveTag.php index 5a796aa8fe..6594fa171d 100644 --- a/app/TransactionRules/Actions/RemoveTag.php +++ b/app/TransactionRules/Actions/RemoveTag.php @@ -62,6 +62,11 @@ class RemoveTag implements ActionInterface ); return false; } + $count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->where('tag_id', $tag->id)->count(); + if(0 === $count) { + Log::debug(sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag is linked.', $name, $journal['transaction_journal_id'])); + return false; + } Log::debug(sprintf('RuleAction RemoveTag removed tag #%d ("%s") from journal #%d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); DB::table('tag_transaction_journal') @@ -71,7 +76,7 @@ class RemoveTag implements ActionInterface /** @var TransactionJournal $journal */ $journal = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - event(new TriggeredAuditLog($this->action->rule, $journal, 'remove_tag', $tag->tag, null)); + event(new TriggeredAuditLog($this->action->rule, $journal, 'clear_tag', $tag->tag, null)); return true; } diff --git a/app/TransactionRules/Actions/UpdatePiggybank.php b/app/TransactionRules/Actions/UpdatePiggybank.php index 4b55f205e9..2fc4de30b0 100644 --- a/app/TransactionRules/Actions/UpdatePiggybank.php +++ b/app/TransactionRules/Actions/UpdatePiggybank.php @@ -28,6 +28,7 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\RuleAction; use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\User; @@ -61,6 +62,7 @@ class UpdatePiggybank implements ActionInterface // refresh the transaction type. $user = User::find($journal['user_id']); + /** @var TransactionJournal $journalObj */ $journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']); $type = TransactionType::find((int) $journalObj->transaction_type_id); $journal['transaction_type_type'] = $type->type; @@ -91,7 +93,7 @@ class UpdatePiggybank implements ActionInterface Log::debug('Piggy bank account is linked to source, so remove amount.'); $this->removeAmount($journal, $piggyBank, $destination->amount); - event(new TriggeredAuditLog($this->action->rule, $journalObj, 'remove_from_piggy', null, ['amount' => $destination->amount, 'piggy' => $piggyBank->name])); + event(new TriggeredAuditLog($this->action->rule, $journalObj, 'remove_from_piggy', null, ['currency_symbol' => $journalObj->transactionCurrency->symbol, 'decimal_places' => $journalObj->transactionCurrency->decimal_places, 'amount' => $destination->amount, 'piggy' => $piggyBank->name])); return true; } @@ -99,7 +101,7 @@ class UpdatePiggybank implements ActionInterface Log::debug('Piggy bank account is linked to source, so add amount.'); $this->addAmount($journal, $piggyBank, $destination->amount); - event(new TriggeredAuditLog($this->action->rule, $journalObj, 'add_to_piggy', null, ['amount' => $destination->amount, 'piggy' => $piggyBank->name])); + event(new TriggeredAuditLog($this->action->rule, $journalObj, 'add_to_piggy', null, ['currency_symbol' => $journalObj->transactionCurrency->symbol, 'decimal_places' => $journalObj->transactionCurrency->decimal_places, 'amount' => $destination->amount, 'piggy' => $piggyBank->name])); return true; } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 9698373633..33e455bd3f 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -2467,76 +2467,97 @@ return [ 'recurring_never_cron' => 'It seems the cron job that is necessary to support recurring transactions has never run. This is of course normal when you have just installed Firefly III, but this should be something to set up as soon as possible. Please check out the help-pages using the (?)-icon in the top right corner of the page.', 'recurring_cron_long_ago' => 'It looks like it has been more than 36 hours since the cron job to support recurring transactions has fired for the last time. Are you sure it has been set up correctly? Please check out the help-pages using the (?)-icon in the top right corner of the page.', - 'create_new_recurrence' => 'Create new recurring transaction', - 'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.', - 'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.', - 'no_currency' => '(no currency)', - 'mandatory_for_recurring' => 'Mandatory recurrence information', - 'mandatory_for_transaction' => 'Mandatory transaction information', - 'optional_for_recurring' => 'Optional recurrence information', - 'optional_for_transaction' => 'Optional transaction information', - 'change_date_other_options' => 'Change the "first date" to see more options.', - 'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created', - 'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.', - 'repeat_forever' => 'Repeat forever', - 'repeat_until_date' => 'Repeat until date', - 'repeat_times' => 'Repeat a number of times', - 'recurring_skips_one' => 'Every other', - 'recurring_skips_more' => 'Skips :count occurrences', - 'store_new_recurrence' => 'Store recurring transaction', - 'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.', - 'edit_recurrence' => 'Edit recurring transaction ":title"', - 'recurring_repeats_until' => 'Repeats until :date', - 'recurring_repeats_forever' => 'Repeats forever', - 'recurring_repeats_x_times' => 'Repeats :count time|Repeats :count times', - 'update_recurrence' => 'Update recurring transaction', - 'updated_recurrence' => 'Updated recurring transaction ":title"', - 'recurrence_is_inactive' => 'This recurring transaction is not active and will not generate new transactions.', - 'delete_recurring' => 'Delete recurring transaction ":title"', - 'new_recurring_transaction' => 'New recurring transaction', - 'help_weekend' => 'What should Firefly III do when the recurring transaction falls on a Saturday or Sunday?', - 'do_nothing' => 'Just create the transaction', - 'skip_transaction' => 'Skip the occurrence', - 'jump_to_friday' => 'Create the transaction on the previous Friday instead', - 'jump_to_monday' => 'Create the transaction on the next Monday instead', - 'will_jump_friday' => 'Will be created on Friday instead of the weekends.', - 'will_jump_monday' => 'Will be created on Monday instead of the weekends.', - 'except_weekends' => 'Except weekends', - 'recurrence_deleted' => 'Recurring transaction ":title" deleted', + 'create_new_recurrence' => 'Create new recurring transaction', + 'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.', + 'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.', + 'no_currency' => '(no currency)', + 'mandatory_for_recurring' => 'Mandatory recurrence information', + 'mandatory_for_transaction' => 'Mandatory transaction information', + 'optional_for_recurring' => 'Optional recurrence information', + 'optional_for_transaction' => 'Optional transaction information', + 'change_date_other_options' => 'Change the "first date" to see more options.', + 'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created', + 'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.', + 'repeat_forever' => 'Repeat forever', + 'repeat_until_date' => 'Repeat until date', + 'repeat_times' => 'Repeat a number of times', + 'recurring_skips_one' => 'Every other', + 'recurring_skips_more' => 'Skips :count occurrences', + 'store_new_recurrence' => 'Store recurring transaction', + 'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.', + 'edit_recurrence' => 'Edit recurring transaction ":title"', + 'recurring_repeats_until' => 'Repeats until :date', + 'recurring_repeats_forever' => 'Repeats forever', + 'recurring_repeats_x_times' => 'Repeats :count time|Repeats :count times', + 'update_recurrence' => 'Update recurring transaction', + 'updated_recurrence' => 'Updated recurring transaction ":title"', + 'recurrence_is_inactive' => 'This recurring transaction is not active and will not generate new transactions.', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'new_recurring_transaction' => 'New recurring transaction', + 'help_weekend' => 'What should Firefly III do when the recurring transaction falls on a Saturday or Sunday?', + 'do_nothing' => 'Just create the transaction', + 'skip_transaction' => 'Skip the occurrence', + 'jump_to_friday' => 'Create the transaction on the previous Friday instead', + 'jump_to_monday' => 'Create the transaction on the next Monday instead', + 'will_jump_friday' => 'Will be created on Friday instead of the weekends.', + 'will_jump_monday' => 'Will be created on Monday instead of the weekends.', + 'except_weekends' => 'Except weekends', + 'recurrence_deleted' => 'Recurring transaction ":title" deleted', // new lines for summary controller. - 'box_balance_in_currency' => 'Balance (:currency)', - 'box_spent_in_currency' => 'Spent (:currency)', - 'box_earned_in_currency' => 'Earned (:currency)', - 'box_budgeted_in_currency' => 'Budgeted (:currency)', - 'box_bill_paid_in_currency' => 'Bills paid (:currency)', - 'box_bill_unpaid_in_currency' => 'Bills unpaid (:currency)', - 'box_left_to_spend_in_currency' => 'Left to spend (:currency)', - 'box_net_worth_in_currency' => 'Net worth (:currency)', - 'box_spend_per_day' => 'Left to spend per day: :amount', + 'box_balance_in_currency' => 'Balance (:currency)', + 'box_spent_in_currency' => 'Spent (:currency)', + 'box_earned_in_currency' => 'Earned (:currency)', + 'box_budgeted_in_currency' => 'Budgeted (:currency)', + 'box_bill_paid_in_currency' => 'Bills paid (:currency)', + 'box_bill_unpaid_in_currency' => 'Bills unpaid (:currency)', + 'box_left_to_spend_in_currency' => 'Left to spend (:currency)', + 'box_net_worth_in_currency' => 'Net worth (:currency)', + 'box_spend_per_day' => 'Left to spend per day: :amount', // debug page - 'debug_page' => 'Debug page', - 'debug_submit_instructions' => 'If you are running into problems, you can use the information in this box as debug information. Please copy-and-paste into a new or existing GitHub issue. It will generate a beautiful table that can be used to quickly diagnose your problem.', - 'debug_pretty_table' => 'If you copy/paste the box below into a GitHub issue it will generate a table. Please do not surround this text with backticks or quotes.', - 'debug_additional_data' => 'You may also share the content of the box below. You can also copy-and-paste this into a new or existing GitHub issue. However, the content of this box may contain private information such as account names, transaction details or email addresses.', + 'debug_page' => 'Debug page', + 'debug_submit_instructions' => 'If you are running into problems, you can use the information in this box as debug information. Please copy-and-paste into a new or existing GitHub issue. It will generate a beautiful table that can be used to quickly diagnose your problem.', + 'debug_pretty_table' => 'If you copy/paste the box below into a GitHub issue it will generate a table. Please do not surround this text with backticks or quotes.', + 'debug_additional_data' => 'You may also share the content of the box below. You can also copy-and-paste this into a new or existing GitHub issue. However, the content of this box may contain private information such as account names, transaction details or email addresses.', // object groups - 'object_groups_menu_bar' => 'Groups', - 'object_groups_page_title' => 'Groups', - 'object_groups_breadcrumb' => 'Groups', - 'object_groups_index' => 'Overview', - 'object_groups' => 'Groups', - 'object_groups_empty_explain' => 'Some things in Firefly III can be divided into groups. Piggy banks for example, feature a "Group" field in the edit and create screens. When you set this field, you can edit the names and the order of the groups on this page. For more information, check out the help-pages in the top right corner, under the (?)-icon.', - 'object_group_title' => 'Title', - 'edit_object_group' => 'Edit group ":title"', - 'delete_object_group' => 'Delete group ":title"', - 'update_object_group' => 'Update group', - 'updated_object_group' => 'Successfully updated group ":title"', - 'deleted_object_group' => 'Successfully deleted group ":title"', - 'object_group' => 'Group', + 'object_groups_menu_bar' => 'Groups', + 'object_groups_page_title' => 'Groups', + 'object_groups_breadcrumb' => 'Groups', + 'object_groups_index' => 'Overview', + 'object_groups' => 'Groups', + 'object_groups_empty_explain' => 'Some things in Firefly III can be divided into groups. Piggy banks for example, feature a "Group" field in the edit and create screens. When you set this field, you can edit the names and the order of the groups on this page. For more information, check out the help-pages in the top right corner, under the (?)-icon.', + 'object_group_title' => 'Title', + 'edit_object_group' => 'Edit group ":title"', + 'delete_object_group' => 'Delete group ":title"', + 'update_object_group' => 'Update group', + 'updated_object_group' => 'Successfully updated group ":title"', + 'deleted_object_group' => 'Successfully deleted group ":title"', + 'object_group' => 'Group', // other stuff - 'placeholder' => '[Placeholder]', + 'placeholder' => '[Placeholder]', + + // audit log entries + 'audit_log_entries' => 'Audit log entries', + 'ale_action_log_add' => 'Add :amount to piggy bank ":name"', + 'ale_action_log_remove' => 'Remove :amount from piggy bank ":name"', + 'ale_action_clear_budget' => 'Removed from budget', + 'ale_action_clear_category' => 'Removed from category', + 'ale_action_clear_notes' => 'Removed notes', + 'ale_action_clear_tag' => 'Cleared tag', + 'ale_action_clear_all_tags' => 'Cleared all tags', + 'ale_action_set_bill' => 'Linked to bill', + 'ale_action_set_budget' => 'Set budget', + 'ale_action_set_category' => 'Set category', + 'ale_action_set_source' => 'Set source account', + 'ale_action_set_destination' => 'Set destination account', + 'ale_action_update_transaction_type' => 'Changed transaction type', + 'ale_action_update_notes' => 'Changed notes', + 'ale_action_update_description' => 'Changed description', + 'ale_action_add_to_piggy' => 'Piggy bank', + 'ale_action_remove_from_piggy' => 'Piggy bank', + 'ale_action_add_tag' => 'Added tag', ]; diff --git a/resources/views/list/ale.twig b/resources/views/list/ale.twig new file mode 100644 index 0000000000..6145385cee --- /dev/null +++ b/resources/views/list/ale.twig @@ -0,0 +1,89 @@ + + {% for logEntry in logEntries %} + + + + + + + + {% endfor %} +
+ {# link to object: #} + {% if 'FireflyIII\\Models\\Rule' == logEntry.changer_type %} + + {% endif %} + {{ logEntry.changer_type|replace({"FireflyIII\\Models\\": ""}) }} + #{{ logEntry.changer_id }} + + + {{ trans('firefly.ale_action_'~logEntry.action) }} + + {# display depends on action #} + {% if 'add_tag' == logEntry.action %} + {{ logEntry.after }} + {% endif %} + {% if 'clear_budget' == logEntry.action %} + {{ logEntry.before }} + {% endif %} + {% if 'clear_category' == logEntry.action %} + {{ logEntry.before }} + {% endif %} + {% if 'clear_tag' == logEntry.action %} + {{ logEntry.before }} + {% endif %} + + {% if 'clear_notes' == logEntry.action %} + {% if logEntry.before|length > 25 %} + {{ logEntry.before|slice(0,25) }}... + {% else %} + {{ logEntry.before }} + {% endif %} + {% endif %} + + {% if 'set_bill' == logEntry.action %} + {{ logEntry.after }} + {% endif %} + {% if 'set_budget' == logEntry.action %} + {{ logEntry.after }} + {% endif %} + {% if 'set_category' == logEntry.action %} + {{ logEntry.after }} + {% endif %} + {% if 'set_source' == logEntry.action %} + {{ logEntry.after }} + {% endif %} + {% if 'set_destination' == logEntry.action %} + {{ logEntry.after }} + {% endif %} + {% if 'update_transaction_type' == logEntry.action %} + {{ trans('firefly.'~logEntry.before) }} → {{ trans('firefly.'~logEntry.after) }} + {% endif %} + + {% if 'update_notes' == logEntry.action %} + {% if logEntry.before|length > 25 %} + {{ logEntry.before|slice(0,25) }}... + {% else %} + {{ logEntry.before }} + {% endif %} + + → + {% if logEntry.after|length > 25 %} + {{ logEntry.after|slice(0,25) }}... + {% else %} + {{ logEntry.after }} + {% endif %} + {% endif %} + + {% if 'update_description' == logEntry.action %} + {{ logEntry.before }} + → + {{ logEntry.after }} + {% endif %} + {% if 'add_to_piggy' == logEntry.action %} + {{ trans('firefly.ale_action_log_add', {amount: formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true), name: logEntry.after.name})|raw }} + {% endif %} + {% if 'remove_from_piggy' == logEntry.action %} + {{ trans('firefly.ale_action_log_remove', {amount: formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true), name: logEntry.after.name})|raw }} + {% endif %} +
diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index 48e0394a24..fbab2cbf1a 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -46,7 +46,7 @@ - + @@ -80,7 +80,7 @@ {% if first.transactiontype.type != 'Withdrawal' or splits == 1 %} - - + {% endif %} {% if null != journal.bill_id and first.transactiontype.type == 'Withdrawal' %} - + {% endif %} @@ -284,7 +284,7 @@ {% for dateField in ['interest_date','book_date','process_date','due_date','payment_date','invoice_date'] %} {% if journalHasMeta(journal.transaction_journal_id, dateField) %} - + {% endif %} @@ -293,7 +293,7 @@ {% if journalHasMeta(journal.transaction_journal_id, metaField) %} - - + {% endif %} @@ -323,13 +323,13 @@ {% set recurringTotal = '∞' %} {% endif %} - + {% endif %} {% if journal.tags|length > 0 %} - +
{{ trans('list.type') }}{{ trans('list.type') }} {{ first.transactiontype.type|_ }}
+ {{ trans_choice('firefly.source_accounts', accounts['source']|length ) }} @@ -270,13 +270,13 @@ {% endif %} {% if null != journal.budget_id and first.transactiontype.type == 'Withdrawal' %}
{{ 'budget'|_ }}{{ 'budget'|_ }} {{ journal.budget_name }}
{{ 'bill'|_ }}{{ 'bill'|_ }} {{ journal.bill_name }}
{{ trans('list.'~dateField) }}{{ trans('list.'~dateField) }} {{ journalGetMetaDate(journal.transaction_journal_id, dateField).isoFormat(monthAndDayFormat) }}
{{ trans('list.'~metaField) }} + {% if 'external_url' == metaField %} {% set url = journalGetMetaField(journal.transaction_journal_id, metaField) %} @@ -313,7 +313,7 @@ {% endfor %} {% if null != journal.notes and '' != journal.notes %}
{{ trans('list.notes') }}{{ trans('list.notes') }} {{ journal.notes|default('')|markdown }}
{{ trans('list.recurring_transaction') }}{{ trans('list.recurring_transaction') }} {{ trans('firefly.recurring_info', {total: recurringTotal, count: journalGetMetaField(journal.transaction_journal_id, 'recurring_count') }) }}
{{ 'tags'|_ }}{{ 'tags'|_ }} {% for tag in journal.tags %}

@@ -416,6 +416,18 @@ {% endif %} + {% if logEntries[journal.transaction_journal_id]|length > 0 %} +
+
+

+ {{ 'audit_log_entries'|_ }} +

+
+
+ {% include 'list.ale' with {logEntries: logEntries[journal.transaction_journal_id]} %} +
+
+ {% endif %} {% endfor %}