From b63bddebf4056e11a876246181dbd3b1e320a6f1 Mon Sep 17 00:00:00 2001 From: Staging-Devin AI <166158716+staging-devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 19:55:44 +0000 Subject: [PATCH] Add comprehensive documentation to code and README - Add PHPDoc comments to key Model files (Account, Transaction, Budget, Bill, Category, PiggyBank, TransactionJournal, Rule) - Add documentation to User.php explaining authentication and authorization - Add documentation to base Controller and HomeController - Expand README with technical architecture, development setup, project structure, API documentation, database schema, testing, and configuration sections Co-Authored-By: sam.fertig@cognition.ai --- app/Http/Controllers/Controller.php | 20 ++- app/Http/Controllers/HomeController.php | 18 ++- app/Models/Account.php | 202 +++++++++++++++++++++++- app/Models/Bill.php | 40 +++++ app/Models/Budget.php | 28 ++++ app/Models/Category.php | 26 +++ app/Models/PiggyBank.php | 33 ++++ app/Models/Rule.php | 38 +++++ app/Models/Transaction.php | 123 ++++++++++++++- app/Models/TransactionJournal.php | 42 +++++ app/User.php | 61 +++++++ readme.md | 120 +++++++++++++- 12 files changed, 733 insertions(+), 18 deletions(-) diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index dc282fbc074..4385290768d 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -42,7 +42,25 @@ use function Safe\ini_get; /** - * Class Controller. + * Class Controller + * + * Base controller class for all Firefly III web controllers. This abstract class + * provides common functionality shared across all controllers including view + * sharing, middleware configuration, and user session management. + * + * The controller sets up various view variables that are available across all + * pages including demo site configuration, version information, feature flags, + * authentication settings, and user preferences like dark mode and locale. + * + * Key responsibilities: + * - Sharing global view variables (version, demo mode, feature flags) + * - Setting up authentication guard configuration + * - Managing user locale and date format preferences + * - Handling webhook message sending via lottery system + * - Managing primary currency for the authenticated user + * + * All web controllers in Firefly III extend this base class to inherit + * common functionality and ensure consistent behavior across the application. * * @SuppressWarnings("PHPMD.CouplingBetweenObjects") * @SuppressWarnings("PHPMD.NumberOfChildren") diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index b8297243083..ac9ecf3cfd3 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -40,7 +40,23 @@ use Illuminate\Support\Facades\Log; /** - * Class HomeController. + * Class HomeController + * + * Handles the main dashboard and home page functionality for Firefly III. + * This controller is responsible for rendering the primary user interface + * that users see after logging in, including account summaries, recent + * transactions, and financial overview widgets. + * + * The controller supports two layout versions (v1 and v2) and automatically + * redirects new users to the setup wizard if they haven't created any accounts yet. + * + * Key responsibilities: + * - Rendering the main dashboard with account summaries + * - Handling date range selection for filtering displayed data + * - Triggering version check notifications for administrators + * - Supporting both legacy (v1) and modern (v2) UI layouts + * + * @see \FireflyIII\Http\Controllers\Controller Base controller class */ class HomeController extends Controller { diff --git a/app/Models/Account.php b/app/Models/Account.php index 683b4a29737..248246a2933 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -40,6 +40,37 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class Account + * + * Represents a financial account in Firefly III. Accounts are the core entity for tracking + * money flow and can be of various types including asset accounts (checking, savings), + * expense accounts (stores, vendors), revenue accounts (employers, clients), and + * liability accounts (loans, mortgages, debts). + * + * Each account belongs to a user and has an account type that determines its behavior + * in the double-entry bookkeeping system. Accounts can have associated metadata, + * attachments, notes, locations, and can be grouped for organizational purposes. + * + * Key features: + * - Supports virtual balance for credit card accounts + * - Can store IBAN for European bank accounts + * - Supports soft deletion for data preservation + * - Can be linked to piggy banks for savings goals + * + * @property int $id Primary key identifier + * @property int $user_id Foreign key to the owning user + * @property int $user_group_id Foreign key to the user group + * @property int $account_type_id Foreign key to the account type + * @property string $name Display name of the account + * @property bool $active Whether the account is currently active + * @property string|null $virtual_balance Virtual balance for credit accounts + * @property string|null $iban International Bank Account Number + * @property string|null $native_virtual_balance Virtual balance in native currency + * @property \Carbon\Carbon $created_at Timestamp of creation + * @property \Carbon\Carbon $updated_at Timestamp of last update + * @property \Carbon\Carbon|null $deleted_at Soft delete timestamp + */ class Account extends Model { use HasFactory; @@ -53,9 +84,15 @@ class Account extends Model private bool $joinedAccountTypes = false; /** - * Route binder. Converts the key in the URL to the specified object (or throw 404). + * Route binder for Laravel route model binding. + * + * Converts the account ID from the URL to an Account model instance. + * Ensures the authenticated user owns the requested account for security. + * Used by Laravel's implicit route model binding feature. * - * @throws NotFoundHttpException + * @param string $value The account ID from the URL + * @return self The Account model instance + * @throws NotFoundHttpException When account is not found or user is not authenticated */ public static function routeBinder(string $value): self { @@ -75,21 +112,50 @@ public static function routeBinder(string $value): self throw new NotFoundHttpException(); } + /** + * Get the user that owns this account. + * + * @return BelongsTo Relationship to the User model + */ public function user(): BelongsTo { return $this->belongsTo(User::class); } + /** + * Get all balance records for this account. + * + * Account balances track the historical balance of the account + * at different points in time for reporting and reconciliation. + * + * @return HasMany Relationship to AccountBalance models + */ public function accountBalances(): HasMany { return $this->hasMany(AccountBalance::class); } + /** + * Get the type of this account. + * + * Account types define the behavior and categorization of accounts + * (e.g., asset, expense, revenue, liability). + * + * @return BelongsTo Relationship to the AccountType model + */ public function accountType(): BelongsTo { return $this->belongsTo(AccountType::class); } + /** + * Get all attachments associated with this account. + * + * Attachments can include documents, receipts, or other files + * related to the account. + * + * @return MorphMany Polymorphic relationship to Attachment models + */ public function attachments(): MorphMany { return $this->morphMany(Attachment::class, 'attachable'); @@ -111,11 +177,29 @@ protected function accountNumber(): Attribute }); } + /** + * Get all metadata associated with this account. + * + * Account metadata stores additional key-value pairs such as + * account numbers, BIC codes, interest rates, and other + * account-specific information. + * + * @return HasMany Relationship to AccountMeta models + */ public function accountMeta(): HasMany { return $this->hasMany(AccountMeta::class); } + /** + * Get the editable name of the account. + * + * Returns an empty string for cash accounts since they + * don't have editable names. For all other account types, + * returns the account name. + * + * @return Attribute Accessor for the edit name + */ protected function editName(): Attribute { return Attribute::make(get: function () { @@ -128,13 +212,26 @@ protected function editName(): Attribute }); } + /** + * Get all geographic locations associated with this account. + * + * Locations can be used to track where transactions occur + * or where the account is physically located. + * + * @return MorphMany Polymorphic relationship to Location models + */ public function locations(): MorphMany { return $this->morphMany(Location::class, 'locatable'); } /** - * Get all the notes. + * Get all notes associated with this account. + * + * Notes allow users to add free-form text descriptions + * or reminders about the account. + * + * @return MorphMany Polymorphic relationship to Note models */ public function notes(): MorphMany { @@ -142,18 +239,42 @@ public function notes(): MorphMany } /** - * Get all the tags for the post. + * Get all object groups this account belongs to. + * + * Object groups allow users to organize accounts into + * custom categories for better organization and filtering. + * + * @return MorphToMany Polymorphic many-to-many relationship to ObjectGroup models */ public function objectGroups(): MorphToMany { return $this->morphToMany(ObjectGroup::class, 'object_groupable'); } + /** + * Get all piggy banks linked to this account. + * + * Piggy banks are savings goals that can be funded from + * asset accounts. Multiple piggy banks can be linked to + * a single account. + * + * @return BelongsToMany Many-to-many relationship to PiggyBank models + */ public function piggyBanks(): BelongsToMany { return $this->belongsToMany(PiggyBank::class); } + /** + * Query scope to filter accounts by their type. + * + * Joins the account_types table if not already joined and filters + * accounts to only include those matching the specified types. + * + * @param EloquentBuilder $query The query builder instance + * @param array $types Array of account type strings to filter by + * @return void + */ #[Scope] protected function accountTypeIn(EloquentBuilder $query, array $types): void { @@ -164,6 +285,16 @@ protected function accountTypeIn(EloquentBuilder $query, array $types): void $query->whereIn('account_types.type', $types); } + /** + * Set the virtual balance attribute. + * + * Converts the value to string and sets to null if empty. + * Virtual balance is used for credit card accounts to track + * the credit limit or expected balance. + * + * @param mixed $value The virtual balance value to set + * @return void + */ public function setVirtualBalanceAttribute(mixed $value): void { $value = (string) $value; @@ -173,16 +304,40 @@ public function setVirtualBalanceAttribute(mixed $value): void $this->attributes['virtual_balance'] = $value; } + /** + * Get all transactions associated with this account. + * + * Transactions represent individual money movements in or out + * of this account as part of the double-entry bookkeeping system. + * + * @return HasMany Relationship to Transaction models + */ public function transactions(): HasMany { return $this->hasMany(Transaction::class); } + /** + * Get the user group this account belongs to. + * + * User groups allow multiple users to share access to accounts + * and financial data for household or team budgeting. + * + * @return BelongsTo Relationship to the UserGroup model + */ public function userGroup(): BelongsTo { return $this->belongsTo(UserGroup::class); } + /** + * Get the account ID as an integer. + * + * Ensures the account ID is always returned as an integer type + * for consistent type handling throughout the application. + * + * @return Attribute Accessor for the account ID + */ protected function accountId(): Attribute { return Attribute::make( @@ -191,7 +346,12 @@ protected function accountId(): Attribute } /** - * Get the user ID + * Get the account type ID as an integer. + * + * Ensures the account type ID is always returned as an integer type + * for consistent type handling throughout the application. + * + * @return Attribute Accessor for the account type ID */ protected function accountTypeId(): Attribute { @@ -200,6 +360,14 @@ protected function accountTypeId(): Attribute ); } + /** + * Get the IBAN with spaces removed. + * + * Normalizes the IBAN by removing all spaces for consistent + * storage and comparison. Returns null if no IBAN is set. + * + * @return Attribute Accessor for the IBAN + */ protected function iban(): Attribute { return Attribute::make( @@ -207,6 +375,14 @@ protected function iban(): Attribute ); } + /** + * Get the display order as an integer. + * + * The order determines how accounts are sorted in lists + * and can be customized by the user. + * + * @return Attribute Accessor for the order + */ protected function order(): Attribute { return Attribute::make( @@ -215,7 +391,13 @@ protected function order(): Attribute } /** - * Get the virtual balance + * Get the virtual balance as a string. + * + * Virtual balance is used for credit card accounts to represent + * the credit limit or expected balance. Stored as string for + * precise decimal handling. + * + * @return Attribute Accessor for the virtual balance */ protected function virtualBalance(): Attribute { @@ -224,6 +406,14 @@ protected function virtualBalance(): Attribute ); } + /** + * Define the attribute casting for this model. + * + * Specifies how database columns should be cast to PHP types + * when retrieved from the database. + * + * @return array Array of attribute names to cast types + */ protected function casts(): array { return [ diff --git a/app/Models/Bill.php b/app/Models/Bill.php index a0f59bc9d47..c4c367e5da0 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -36,6 +36,46 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class Bill + * + * Represents a recurring bill or subscription that the user expects to pay regularly. + * Bills help users track expected expenses like rent, utilities, subscriptions, and + * other recurring payments. Firefly III can automatically match incoming transactions + * to bills based on configurable matching rules. + * + * Bills have a minimum and maximum expected amount range, allowing for variable + * recurring expenses. They also support various repeat frequencies (daily, weekly, + * monthly, quarterly, half-yearly, yearly) and can be configured to skip certain + * occurrences. + * + * Key features: + * - Automatic transaction matching based on description patterns + * - Flexible repeat frequencies for various billing cycles + * - Amount range support for variable bills + * - End date and extension date for time-limited bills + * - Timezone-aware date handling + * - Can be organized into object groups + * + * @property int $id Primary key identifier + * @property int $user_id Foreign key to the owning user + * @property int $user_group_id Foreign key to the user group + * @property string $name Display name of the bill + * @property string $match Matching pattern for automatic transaction linking + * @property string $amount_min Minimum expected amount + * @property string $amount_max Maximum expected amount + * @property \Carbon\Carbon $date Next expected date of the bill + * @property string $repeat_freq Repeat frequency (daily, weekly, monthly, etc.) + * @property int $skip Number of periods to skip between occurrences + * @property bool $automatch Whether to automatically match transactions + * @property bool $active Whether the bill is currently active + * @property int $transaction_currency_id Foreign key to the currency + * @property \Carbon\Carbon|null $end_date When the bill expires + * @property \Carbon\Carbon|null $extension_date Extended end date + * @property \Carbon\Carbon $created_at Timestamp of creation + * @property \Carbon\Carbon $updated_at Timestamp of last update + * @property \Carbon\Carbon|null $deleted_at Soft delete timestamp + */ class Bill extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/Budget.php b/app/Models/Budget.php index c5c1c29d465..642c90eb867 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -35,6 +35,34 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class Budget + * + * Represents a budget category for expense tracking and financial planning. + * Budgets help users allocate money for specific spending categories and + * track their expenses against those allocations. + * + * Budgets can have limits set for specific time periods through BudgetLimit + * records, and can be configured for automatic replenishment through AutoBudget. + * Transactions can be assigned to budgets to track spending against the budget. + * + * Key features: + * - Supports budget limits for time-based spending caps + * - Auto-budget functionality for automatic limit creation + * - Can be linked to transactions and transaction journals + * - Supports attachments and notes for additional context + * - Soft deletion for data preservation + * + * @property int $id Primary key identifier + * @property int $user_id Foreign key to the owning user + * @property int $user_group_id Foreign key to the user group + * @property string $name Display name of the budget + * @property bool $active Whether the budget is currently active + * @property int $order Display order for sorting + * @property \Carbon\Carbon $created_at Timestamp of creation + * @property \Carbon\Carbon $updated_at Timestamp of last update + * @property \Carbon\Carbon|null $deleted_at Soft delete timestamp + */ class Budget extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/Category.php b/app/Models/Category.php index 3e50f448c6c..ffe1b489c55 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -34,6 +34,32 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class Category + * + * Represents a category for classifying transactions. Categories provide a flexible + * way to organize and analyze spending patterns without the constraints of budgets. + * Unlike budgets which have spending limits, categories are purely organizational. + * + * Categories are typically used to classify the nature of a transaction (e.g., + * "Groceries", "Entertainment", "Transportation") and can be applied to any + * transaction type (withdrawals, deposits, or transfers). + * + * Key features: + * - Simple name-based classification system + * - Can be linked to multiple transactions + * - Supports attachments and notes for additional context + * - Soft deletion for data preservation + * - Shared across user groups for collaborative budgeting + * + * @property int $id Primary key identifier + * @property int $user_id Foreign key to the owning user + * @property int $user_group_id Foreign key to the user group + * @property string $name Display name of the category + * @property \Carbon\Carbon $created_at Timestamp of creation + * @property \Carbon\Carbon $updated_at Timestamp of last update + * @property \Carbon\Carbon|null $deleted_at Soft delete timestamp + */ class Category extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 08d5a2563d0..f50c295dfae 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -34,6 +34,39 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class PiggyBank + * + * Represents a savings goal that helps users save money for specific purposes. + * Piggy banks are virtual containers that track progress toward a target amount, + * allowing users to allocate portions of their account balance for specific goals + * like vacations, emergency funds, or large purchases. + * + * Piggy banks are linked to one or more asset accounts and track the current + * saved amount separately from the actual account balance. This allows users + * to mentally allocate funds without moving money between accounts. + * + * Key features: + * - Target amount and optional target date for goal tracking + * - Progress tracking with events for deposits and withdrawals + * - Can be linked to multiple accounts + * - Supports repetitions for recurring savings goals + * - Can be organized into object groups + * - Supports attachments and notes + * + * @property int $id Primary key identifier + * @property string $name Display name of the piggy bank + * @property int $order Display order for sorting + * @property string $target_amount Target savings amount + * @property \Carbon\Carbon|null $start_date When saving started + * @property \Carbon\Carbon|null $target_date Target date to reach the goal + * @property bool $active Whether the piggy bank is currently active + * @property int $transaction_currency_id Foreign key to the currency + * @property string|null $native_target_amount Target amount in native currency + * @property \Carbon\Carbon $created_at Timestamp of creation + * @property \Carbon\Carbon $updated_at Timestamp of last update + * @property \Carbon\Carbon|null $deleted_at Soft delete timestamp + */ class PiggyBank extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 5308eae4d51..e04f81f0f3f 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -33,6 +33,44 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class Rule + * + * Represents an automation rule that can automatically modify transactions based + * on configurable triggers and actions. Rules are a powerful feature that allows + * users to automate repetitive tasks like categorizing transactions, setting budgets, + * or adding tags based on transaction properties. + * + * Each rule consists of: + * - Triggers: Conditions that must be met for the rule to fire (e.g., description contains "grocery") + * - Actions: Operations to perform when triggers match (e.g., set category to "Food") + * + * Rules can operate in strict mode (all triggers must match) or non-strict mode + * (any trigger can match). They are organized into rule groups for better management + * and can be ordered to control execution priority. + * + * Key features: + * - Flexible trigger conditions based on transaction properties + * - Multiple action types for transaction modification + * - Strict/non-strict matching modes + * - Rule groups for organization + * - Execution order control + * - Can be enabled/disabled individually + * + * @property int $id Primary key identifier + * @property int $user_id Foreign key to the owning user + * @property int $user_group_id Foreign key to the user group + * @property int $rule_group_id Foreign key to the rule group + * @property int $order Execution order within the group + * @property bool $active Whether the rule is currently active + * @property string $title Display title of the rule + * @property string|null $description Description of what the rule does + * @property bool $strict Whether all triggers must match (true) or any (false) + * @property bool $stop_processing Whether to stop processing other rules after this one + * @property \Carbon\Carbon $created_at Timestamp of creation + * @property \Carbon\Carbon $updated_at Timestamp of last update + * @property \Carbon\Carbon|null $deleted_at Soft delete timestamp + */ class Rule extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index a563e3656ff..9064c45c305 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -34,6 +34,40 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\SoftDeletes; +/** + * Class Transaction + * + * Represents a single leg of a financial transaction in the double-entry bookkeeping system. + * In Firefly III, every transaction journal has at least two Transaction records: one with + * a positive amount (credit) and one with a negative amount (debit), ensuring the books + * always balance. + * + * Transactions are linked to accounts and can be categorized using budgets and categories. + * They support multiple currencies through the foreign_amount and foreign_currency_id fields, + * allowing for currency conversion tracking. + * + * Key concepts: + * - Each Transaction belongs to a TransactionJournal (the parent record) + * - Positive amounts represent money coming into an account + * - Negative amounts represent money leaving an account + * - The sum of all transactions in a journal should equal zero + * + * @property int $id Primary key identifier + * @property int $account_id Foreign key to the associated account + * @property int $transaction_journal_id Foreign key to the parent transaction journal + * @property string $description Description of this transaction leg + * @property string $amount The transaction amount (positive or negative) + * @property string|null $native_amount Amount in the user's native currency + * @property string|null $foreign_amount Amount in a foreign currency + * @property string|null $native_foreign_amount Foreign amount in native currency + * @property int $identifier Identifier for ordering within a journal + * @property int $transaction_currency_id Foreign key to the transaction currency + * @property int|null $foreign_currency_id Foreign key to the foreign currency + * @property bool $reconciled Whether this transaction has been reconciled + * @property \Carbon\Carbon $created_at Timestamp of creation + * @property \Carbon\Carbon $updated_at Timestamp of last update + * @property \Carbon\Carbon|null $deleted_at Soft delete timestamp + */ class Transaction extends Model { use HasFactory; @@ -58,7 +92,12 @@ class Transaction extends Model protected $hidden = ['encrypted']; /** - * Get the account this object belongs to. + * Get the account this transaction belongs to. + * + * Every transaction is associated with exactly one account, + * representing either the source or destination of funds. + * + * @return BelongsTo Relationship to the Account model */ public function account(): BelongsTo { @@ -66,7 +105,12 @@ public function account(): BelongsTo } /** - * Get the budget(s) this object belongs to. + * Get the budgets associated with this transaction. + * + * Transactions can be assigned to one or more budgets for + * expense tracking and budget management purposes. + * + * @return BelongsToMany Many-to-many relationship to Budget models */ public function budgets(): BelongsToMany { @@ -74,7 +118,12 @@ public function budgets(): BelongsToMany } /** - * Get the category(ies) this object belongs to. + * Get the categories associated with this transaction. + * + * Categories provide a way to classify transactions for + * reporting and analysis purposes. + * + * @return BelongsToMany Many-to-many relationship to Category models */ public function categories(): BelongsToMany { @@ -82,7 +131,12 @@ public function categories(): BelongsToMany } /** - * Get the currency this object belongs to. + * Get the foreign currency for this transaction. + * + * Used when the transaction involves a currency different + * from the account's primary currency. + * + * @return BelongsTo Relationship to the TransactionCurrency model */ public function foreignCurrency(): BelongsTo { @@ -90,7 +144,14 @@ public function foreignCurrency(): BelongsTo } /** - * Check for transactions AFTER a specified date. + * Query scope to filter transactions after a specified date. + * + * Joins the transaction_journals table if not already joined + * and filters to only include transactions on or after the given date. + * + * @param Builder $query The query builder instance + * @param Carbon $date The start date for filtering + * @return void */ #[Scope] protected function after(Builder $query, Carbon $date): void @@ -102,7 +163,13 @@ protected function after(Builder $query, Carbon $date): void } /** - * Check if a table is joined. + * Check if a table has already been joined in the query. + * + * Utility method to prevent duplicate joins which would cause SQL errors. + * + * @param Builder $query The query builder instance + * @param string $table The table name to check for + * @return bool True if the table is already joined, false otherwise */ public static function isJoined(Builder $query, string $table): bool { @@ -118,7 +185,14 @@ public static function isJoined(Builder $query, string $table): bool } /** - * Check for transactions BEFORE the specified date. + * Query scope to filter transactions before a specified date. + * + * Joins the transaction_journals table if not already joined + * and filters to only include transactions on or before the given date. + * + * @param Builder $query The query builder instance + * @param Carbon $date The end date for filtering + * @return void */ #[Scope] protected function before(Builder $query, Carbon $date): void @@ -129,6 +203,17 @@ protected function before(Builder $query, Carbon $date): void $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')); } + /** + * Query scope to filter transactions by their type. + * + * Joins the transaction_journals and transaction_types tables if not + * already joined and filters by the specified transaction types + * (e.g., withdrawal, deposit, transfer). + * + * @param Builder $query The query builder instance + * @param array $types Array of transaction type strings to filter by + * @return void + */ #[Scope] protected function transactionTypes(Builder $query, array $types): void { @@ -143,18 +228,40 @@ protected function transactionTypes(Builder $query, array $types): void } /** - * @param mixed $value + * Set the amount attribute as a string. + * + * Ensures amounts are stored as strings for precise decimal handling + * and to avoid floating-point precision issues. + * + * @param mixed $value The amount value to set + * @return void */ public function setAmountAttribute($value): void { $this->attributes['amount'] = (string) $value; } + /** + * Get the primary currency for this transaction. + * + * The transaction currency is the main currency in which + * the transaction amount is denominated. + * + * @return BelongsTo Relationship to the TransactionCurrency model + */ public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } + /** + * Get the transaction journal this transaction belongs to. + * + * The transaction journal is the parent record that groups + * related transactions together in the double-entry system. + * + * @return BelongsTo Relationship to the TransactionJournal model + */ public function transactionJournal(): BelongsTo { return $this->belongsTo(TransactionJournal::class); diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 609d7dc3534..3597c5f14ea 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -42,6 +42,48 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** + * Class TransactionJournal + * + * Represents a complete financial transaction in the double-entry bookkeeping system. + * A transaction journal is the parent record that groups together the individual + * Transaction records (debits and credits) that make up a complete financial event. + * + * In Firefly III's double-entry system, every transaction journal contains at least + * two Transaction records: one representing money leaving an account (negative amount) + * and one representing money entering another account (positive amount). The sum of + * all transactions in a journal always equals zero. + * + * Transaction journals can be of different types: + * - Withdrawal: Money leaving an asset account to an expense account + * - Deposit: Money entering an asset account from a revenue account + * - Transfer: Money moving between two asset accounts + * - Opening Balance: Initial balance when setting up an account + * - Reconciliation: Adjustments made during account reconciliation + * + * Key features: + * - Groups related transactions together + * - Supports multiple currencies through foreign amounts + * - Can be linked to bills for recurring expense tracking + * - Supports budgets, categories, and tags for organization + * - Can be linked to other journals for split transactions + * - Maintains audit log for change tracking + * - Timezone-aware date handling + * + * @property int $id Primary key identifier + * @property int $user_id Foreign key to the owning user + * @property int $user_group_id Foreign key to the user group + * @property int $transaction_type_id Foreign key to the transaction type + * @property int|null $bill_id Foreign key to associated bill + * @property int $tag_count Number of tags attached + * @property int $transaction_currency_id Foreign key to the currency + * @property string $description Description of the transaction + * @property bool $completed Whether the transaction is completed + * @property int $order Display order for sorting + * @property \Carbon\Carbon $date Date of the transaction + * @property \Carbon\Carbon $created_at Timestamp of creation + * @property \Carbon\Carbon $updated_at Timestamp of last update + * @property \Carbon\Carbon|null $deleted_at Soft delete timestamp + * * @method EloquentBuilder|static before() * @method EloquentBuilder|static after() * @method static EloquentBuilder|static query() diff --git a/app/User.php b/app/User.php index 3e2eccddf08..0510735e6cc 100644 --- a/app/User.php +++ b/app/User.php @@ -20,6 +20,20 @@ * along with this program. If not, see . */ +/** + * User Model + * + * This file contains the User model which is the central authentication and + * authorization entity in Firefly III. The User model extends Laravel's + * Authenticatable class and implements various traits for API tokens, + * notifications, and custom ID handling. + * + * Users own all financial data in the system including accounts, transactions, + * budgets, categories, bills, and rules. The model provides relationships to + * all these entities and handles user-specific functionality like password + * resets, notification routing, and role-based access control. + */ + declare(strict_types=1); namespace FireflyIII; @@ -69,13 +83,60 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Exception; +/** + * Class User + * + * Represents a user account in Firefly III. This is the central authentication + * and authorization entity that owns all financial data in the system. + * + * Users can have multiple roles (admin, demo, etc.) and can be members of + * user groups for shared financial management. Each user has their own set + * of accounts, transactions, budgets, categories, bills, rules, and other + * financial entities. + * + * Key features: + * - API token authentication via Laravel Passport + * - Multi-channel notifications (email, Slack, Pushover) + * - Role-based access control for admin functions + * - User group membership for shared finances + * - LDAP integration support (deprecated) + * - Two-factor authentication support + * + * @property int $id Primary key identifier + * @property string $email User's email address (unique) + * @property string $password Hashed password + * @property bool $blocked Whether the user account is blocked + * @property string|null $blocked_code Reason code for blocking + * @property int|null $user_group_id Current active user group + * @property string|null $remember_token Token for "remember me" functionality + * @property \Carbon\Carbon $created_at Timestamp of creation + * @property \Carbon\Carbon $updated_at Timestamp of last update + */ class User extends Authenticatable { use HasApiTokens; use Notifiable; use ReturnsIntegerIdTrait; + + /** + * The attributes that are mass assignable. + * + * @var array + */ protected $fillable = ['email', 'password', 'blocked', 'blocked_code', 'user_group_id']; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ protected $hidden = ['password', 'remember_token']; + + /** + * The database table used by the model. + * + * @var string + */ protected $table = 'users'; /** diff --git a/readme.md b/readme.md index 25a7d39e243..434a8dda7da 100644 --- a/readme.md +++ b/readme.md @@ -41,6 +41,14 @@ - [Do you need help, or do you want to get in touch?](#do-you-need-help-or-do-you-want-to-get-in-touch) - [Acknowledgements](#acknowledgements) +- [Technical Architecture](#technical-architecture) +- [Development Setup](#development-setup) +- [Project Structure](#project-structure) +- [API Documentation](#api-documentation) +- [Database Schema](#database-schema) +- [Testing](#testing) +- [Configuration](#configuration) + ## About Firefly III @@ -173,7 +181,115 @@ Over time, [many people have contributed to Firefly III](https://github.com/fire The Firefly III logo is made by the excellent Cherie Woo. -[packagist-shield]: https://img.shields.io/packagist/v/grumpydictator/firefly-iii.svg?style=flat-square +## Technical Architecture + +Firefly III is built on the Laravel PHP framework (version 12) and follows a clean, modular architecture based on established design patterns. + +### Technology Stack + +The application uses PHP 8.4+ with Laravel 12 as the core framework, supporting both MySQL and PostgreSQL databases. The frontend combines Twig templating with modern JavaScript, while Redis handles caching and session management. Authentication is managed through Laravel Passport for API tokens and Laravel's built-in authentication for web sessions. + +### Core Design Patterns + +The codebase implements the Repository Pattern to abstract data access, separating business logic from database operations. This is complemented by a Service Layer that encapsulates complex business logic, and an Event-Driven Architecture using Laravel's event system for decoupled components. The application follows Double-Entry Bookkeeping principles where every transaction creates balanced debit and credit entries. + +### Key Architectural Components + +Models represent the core data entities (Account, Transaction, Budget, etc.) with Eloquent ORM relationships. Repositories provide data access abstraction through interfaces like AccountRepositoryInterface. Services contain business logic for operations like transaction creation and rule processing. Controllers handle HTTP requests and delegate to services, while Transformers format data for API responses using the Fractal library. + +## Development Setup + +### Prerequisites + +Before setting up the development environment, ensure you have PHP 8.4 or higher with required extensions (bcmath, curl, fileinfo, intl, json, mbstring, openssl, pdo, session, xml), Composer for PHP dependency management, Node.js and npm for frontend assets, and either MySQL 8.0+ or PostgreSQL 12+. + +### Local Installation + +Clone the repository and install dependencies by running `composer install` followed by `npm install`. Copy the environment file with `cp .env.example .env` and generate an application key using `php artisan key:generate`. Configure your database connection in the .env file, then run migrations with `php artisan migrate --seed`. Finally, start the development server with `php artisan serve`. + +### Environment Configuration + +Key environment variables include APP_KEY (auto-generated application encryption key), DB_CONNECTION (database driver: mysql or pgsql), DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, and DB_PASSWORD for database configuration. MAIL_MAILER configures the email driver, and APP_URL sets the application URL for links. + +## Project Structure + +The application follows Laravel's standard directory structure with some Firefly III-specific additions. + +### Directory Overview + +The `app/` directory contains the core application code including Models (Eloquent models for all entities), Http/Controllers (web and API controllers), Repositories (data access layer implementations), Services (business logic services), Events and Listeners (event-driven components), Jobs (queued background tasks), and Transformers (API response formatters). + +The `config/` directory holds configuration files, with firefly.php containing application-specific settings. The `database/` directory includes migrations (database schema definitions) and seeders (initial data population). The `routes/` directory defines web.php for web routes, api.php for API endpoints, and console.php for Artisan commands. The `resources/` directory contains views (Twig templates), lang (translation files), and assets (CSS/JS source files). Finally, `tests/` contains Unit and Feature test suites. + +### Key Model Relationships + +Account is the central entity linked to transactions, with types including Asset, Expense, Revenue, and Liability. TransactionJournal groups related Transaction records in the double-entry system. Budget and Category provide transaction classification, while Bill tracks recurring expected expenses. Rule defines automation triggers and actions, and PiggyBank manages savings goals linked to accounts. + +## API Documentation + +Firefly III provides a comprehensive REST API for programmatic access to all features. + +### Authentication + +The API uses OAuth2 authentication via Laravel Passport. Generate a Personal Access Token through the web interface under Profile > OAuth, then include the token in requests using the Authorization header with Bearer token format. + +### API Versioning + +The current API version is 2.1.0. All endpoints are prefixed with `/api/v1/` for consistency. + +### Common Endpoints + +Account management is available at GET/POST `/api/v1/accounts` for listing and creating accounts, and GET/PUT/DELETE `/api/v1/accounts/{id}` for individual account operations. Transaction management uses GET/POST `/api/v1/transactions` and GET/PUT/DELETE `/api/v1/transactions/{id}`. Budget operations are at `/api/v1/budgets` endpoints, and category management at `/api/v1/categories`. + +### Response Format + +All API responses use JSON format with consistent structure. Successful responses include a "data" key with the requested resource(s), while error responses include "message" and "errors" keys with details. + +## Database Schema + +The database uses a normalized relational schema optimized for financial data integrity. + +### Core Tables + +The `users` table stores user accounts and authentication data. The `accounts` table contains financial accounts with foreign keys to account_types and users. The `transactions` table holds individual transaction legs with amounts and account references. The `transaction_journals` table groups transactions into complete financial events. The `budgets` and `categories` tables provide classification systems, while `bills` tracks recurring expected expenses. The `rules`, `rule_triggers`, and `rule_actions` tables define automation logic. + +### Key Relationships + +Users own all financial data through user_id foreign keys. Accounts belong to AccountTypes which define their behavior. TransactionJournals contain multiple Transactions (minimum 2 for double-entry). Budgets and Categories link to transactions through pivot tables, and Bills link to TransactionJournals for recurring expense tracking. + +## Testing + +The project includes comprehensive test suites for ensuring code quality. + +### Running Tests + +Execute the full test suite with `php artisan test` or `./vendor/bin/phpunit`. Run only unit tests using `composer unit-test` and integration tests with `composer integration-test`. Generate a coverage report using `composer coverage`. + +### Test Structure + +Unit tests in `tests/Unit/` test individual classes in isolation. Feature tests in `tests/Feature/` test complete features and API endpoints. The project uses PHPUnit 12 as the testing framework with Laravel's testing utilities. + +### Code Quality Tools + +PHPStan performs static analysis (run with `./vendor/bin/phpstan analyse`). Laravel Pint handles code style checking, and SonarQube integration is available for comprehensive code quality analysis. + +## Configuration + +Firefly III offers extensive configuration options through environment variables and the config files. + +### Feature Flags + +Located in `config/firefly.php`, feature flags control optional functionality. The `webhooks` flag enables webhook support for external integrations. The `export` flag enables data export functionality. The `handle_debts` flag enables liability/debt management features. + +### Security Settings + +AUTHENTICATION_GUARD configures the authentication method (default: web). DISABLE_FRAME_HEADER controls X-Frame-Options header. DISABLE_CSP_HEADER controls Content-Security-Policy header. ALLOW_WEBHOOKS enables/disables webhook functionality. + +### Localization + +The application supports 30+ languages configured in `config/firefly.php`. DEFAULT_LANGUAGE sets the default language (e.g., en_US). DEFAULT_LOCALE configures number and date formatting. Users can override these in their preferences. + +[packagist-shield]:https://img.shields.io/packagist/v/grumpydictator/firefly-iii.svg?style=flat-square [packagist-url]: https://packagist.org/packages/grumpydictator/firefly-iii [license-shield]: https://img.shields.io/github/license/firefly-iii/firefly-iii.svg?style=flat-square [license-url]: https://www.gnu.org/licenses/agpl-3.0.html @@ -189,4 +305,4 @@ The Firefly III logo is made by the excellent Cherie Woo. [sc-vuln-shield]: https://sonarcloud.io/api/project_badges/measure?project=firefly-iii_firefly-iii&metric=vulnerabilities [sc-project-url]: https://sonarcloud.io/dashboard?id=firefly-iii_firefly-iii [bp-badge]: https://bestpractices.coreinfrastructure.org/projects/6335/badge -[bp-url]: https://bestpractices.coreinfrastructure.org/projects/6335 +[bp-url]: https://bestpractices.coreinfrastructure.org/projects/6335