package models import ( "time" "gorm.io/gorm" ) // BaseModel contains common fields for all models type BaseModel struct { ID uint `gorm:"primarykey" json:"id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` } // TransactionType represents the type of transaction type TransactionType string const ( TransactionTypeIncome TransactionType = "income" TransactionTypeExpense TransactionType = "expense" TransactionTypeTransfer TransactionType = "transfer" ) // AccountType represents the type of account type AccountType string const ( AccountTypeCash AccountType = "cash" AccountTypeDebitCard AccountType = "debit_card" AccountTypeCreditCard AccountType = "credit_card" AccountTypeEWallet AccountType = "e_wallet" AccountTypeCreditLine AccountType = "credit_line" // 花呗、白�? AccountTypeInvestment AccountType = "investment" ) // FrequencyType represents the frequency of recurring transactions type FrequencyType string const ( FrequencyDaily FrequencyType = "daily" FrequencyWeekly FrequencyType = "weekly" FrequencyMonthly FrequencyType = "monthly" FrequencyYearly FrequencyType = "yearly" ) // SubAccountType represents the type of sub-account // Feature: financial-core-upgrade // Validates: Requirements 1.2 type SubAccountType string const ( SubAccountTypeSavingsPot SubAccountType = "savings_pot" // 存钱罐,冻结资金 SubAccountTypeMoneyFund SubAccountType = "money_fund" // 货币基金(如余额宝),支持利�? SubAccountTypeInvestment SubAccountType = "investment" // 投资账户(如股票/基金�? ) // TransactionSubType represents the sub-type of transaction // Feature: financial-core-upgrade // Validates: Requirements 3.2 type TransactionSubType string const ( TransactionSubTypeInterest TransactionSubType = "interest" // 利息收入 TransactionSubTypeTransferIn TransactionSubType = "transfer_in" // 转入 TransactionSubTypeTransferOut TransactionSubType = "transfer_out" // 转出 TransactionSubTypeSavingsDeposit TransactionSubType = "savings_deposit" // 存钱罐存�? TransactionSubTypeSavingsWithdraw TransactionSubType = "savings_withdraw" // 存钱罐取�? ) // PeriodType represents the period type for budgets type PeriodType string const ( PeriodTypeDaily PeriodType = "daily" PeriodTypeWeekly PeriodType = "weekly" PeriodTypeMonthly PeriodType = "monthly" PeriodTypeYearly PeriodType = "yearly" ) // PiggyBankType represents the type of piggy bank type PiggyBankType string const ( PiggyBankTypeManual PiggyBankType = "manual" PiggyBankTypeAuto PiggyBankType = "auto" PiggyBankTypeFixedDeposit PiggyBankType = "fixed_deposit" PiggyBankTypeWeek52 PiggyBankType = "week_52" ) // Currency represents supported currencies type Currency string const ( // Major currencies CurrencyCNY Currency = "CNY" CurrencyUSD Currency = "USD" CurrencyEUR Currency = "EUR" CurrencyJPY Currency = "JPY" CurrencyGBP Currency = "GBP" CurrencyHKD Currency = "HKD" // Asia Pacific CurrencyAUD Currency = "AUD" CurrencyNZD Currency = "NZD" CurrencySGD Currency = "SGD" CurrencyKRW Currency = "KRW" CurrencyTHB Currency = "THB" CurrencyTWD Currency = "TWD" CurrencyMOP Currency = "MOP" CurrencyPHP Currency = "PHP" CurrencyIDR Currency = "IDR" CurrencyINR Currency = "INR" CurrencyVND Currency = "VND" CurrencyMNT Currency = "MNT" CurrencyKHR Currency = "KHR" CurrencyNPR Currency = "NPR" CurrencyPKR Currency = "PKR" CurrencyBND Currency = "BND" // Europe CurrencyCHF Currency = "CHF" CurrencySEK Currency = "SEK" CurrencyNOK Currency = "NOK" CurrencyDKK Currency = "DKK" CurrencyCZK Currency = "CZK" CurrencyHUF Currency = "HUF" CurrencyRUB Currency = "RUB" CurrencyTRY Currency = "TRY" // Americas CurrencyCAD Currency = "CAD" CurrencyMXN Currency = "MXN" CurrencyBRL Currency = "BRL" // Middle East & Africa CurrencyAED Currency = "AED" CurrencySAR Currency = "SAR" CurrencyQAR Currency = "QAR" CurrencyKWD Currency = "KWD" CurrencyILS Currency = "ILS" CurrencyZAR Currency = "ZAR" ) // SupportedCurrencies returns a list of all supported currencies func SupportedCurrencies() []Currency { return []Currency{ // Major currencies CurrencyCNY, CurrencyUSD, CurrencyEUR, CurrencyJPY, CurrencyGBP, CurrencyHKD, // Asia Pacific CurrencyAUD, CurrencyNZD, CurrencySGD, CurrencyKRW, CurrencyTHB, CurrencyTWD, CurrencyMOP, CurrencyPHP, CurrencyIDR, CurrencyINR, CurrencyVND, CurrencyMNT, CurrencyKHR, CurrencyNPR, CurrencyPKR, CurrencyBND, // Europe CurrencyCHF, CurrencySEK, CurrencyNOK, CurrencyDKK, CurrencyCZK, CurrencyHUF, CurrencyRUB, CurrencyTRY, // Americas CurrencyCAD, CurrencyMXN, CurrencyBRL, // Middle East & Africa CurrencyAED, CurrencySAR, CurrencyQAR, CurrencyKWD, CurrencyILS, CurrencyZAR, } } // CategoryType represents whether a category is for income or expense type CategoryType string const ( CategoryTypeIncome CategoryType = "income" CategoryTypeExpense CategoryType = "expense" ) // TriggerType represents the trigger type for allocation rules type TriggerType string const ( TriggerTypeIncome TriggerType = "income" TriggerTypeManual TriggerType = "manual" ) // TargetType represents the target type for allocation type TargetType string const ( TargetTypeAccount TargetType = "account" TargetTypePiggyBank TargetType = "piggy_bank" ) // ======================================== // Database Models // ======================================== // Account represents a financial account (cash, bank card, credit card, etc.) type Account struct { BaseModel UserID uint `gorm:"not null;index" json:"user_id"` Name string `gorm:"size:100;not null" json:"name"` Type AccountType `gorm:"size:20;not null" json:"type"` Balance float64 `gorm:"type:decimal(15,2);default:0" json:"balance"` Currency Currency `gorm:"size:10;not null;default:'CNY'" json:"currency"` Icon string `gorm:"size:50" json:"icon"` BillingDate *int `gorm:"type:integer" json:"billing_date,omitempty"` // Day of month for credit card billing PaymentDate *int `gorm:"type:integer" json:"payment_date,omitempty"` // Day of month for credit card payment IsCredit bool `gorm:"default:false" json:"is_credit"` // Asset management enhancements // Feature: accounting-feature-upgrade // Validates: Requirements 1.2-1.10 SortOrder int `gorm:"default:0" json:"sort_order"` // Display order for account list WarningThreshold *float64 `gorm:"type:decimal(15,2)" json:"warning_threshold,omitempty"` // Balance warning threshold LastSyncTime *time.Time `json:"last_sync_time,omitempty"` // Last synchronization time AccountCode string `gorm:"size:50" json:"account_code,omitempty"` // Account identifier (e.g., Alipay, Wechat) AccountType string `gorm:"size:20;default:'asset'" json:"account_type"` // asset or liability // Sub-account fields // Feature: financial-core-upgrade // Validates: Requirements 1.1, 1.3, 2.7 ParentAccountID *uint `gorm:"index" json:"parent_account_id,omitempty"` SubAccountType *SubAccountType `gorm:"size:20" json:"sub_account_type,omitempty"` // Balance management for sub-accounts // Feature: financial-core-upgrade // Validates: Requirements 2.1-2.6 FrozenBalance float64 `gorm:"type:decimal(15,2);default:0" json:"frozen_balance"` AvailableBalance float64 `gorm:"type:decimal(15,2);default:0" json:"available_balance"` // Savings pot fields // Feature: financial-core-upgrade // Validates: Requirements 2.7 TargetAmount *float64 `gorm:"type:decimal(15,2)" json:"target_amount,omitempty"` TargetDate *time.Time `gorm:"type:date" json:"target_date,omitempty"` // Interest fields // Feature: financial-core-upgrade // Validates: Requirements 3.1 AnnualRate *float64 `gorm:"type:decimal(5,4)" json:"annual_rate,omitempty"` InterestEnabled bool `gorm:"default:false" json:"interest_enabled"` // Relationships Transactions []Transaction `gorm:"foreignKey:AccountID" json:"-"` RecurringTransactions []RecurringTransaction `gorm:"foreignKey:AccountID" json:"-"` Budgets []Budget `gorm:"foreignKey:AccountID" json:"-"` PiggyBanks []PiggyBank `gorm:"foreignKey:LinkedAccountID" json:"-"` ParentAccount *Account `gorm:"foreignKey:ParentAccountID" json:"parent_account,omitempty"` SubAccounts []Account `gorm:"foreignKey:ParentAccountID" json:"sub_accounts,omitempty"` } // TableName specifies the table name for Account func (Account) TableName() string { return "accounts" } // TotalBalance calculates the total balance including sub-accounts // Feature: financial-core-upgrade // Validates: Requirements 1.3 func (a *Account) TotalBalance() float64 { total := a.AvailableBalance + a.FrozenBalance for _, sub := range a.SubAccounts { if sub.SubAccountType != nil && *sub.SubAccountType != SubAccountTypeSavingsPot { total += sub.Balance } } return total } // Category represents a transaction category with optional parent-child hierarchy type Category struct { ID uint `gorm:"primarykey" json:"id"` UserID uint `gorm:"not null;index" json:"user_id"` Name string `gorm:"size:50;not null" json:"name"` Icon string `gorm:"size:50" json:"icon"` Type CategoryType `gorm:"size:20;not null" json:"type"` // income or expense ParentID *uint `gorm:"index" json:"parent_id,omitempty"` SortOrder int `gorm:"default:0" json:"sort_order"` CreatedAt time.Time `json:"created_at"` // Relationships Parent *Category `gorm:"foreignKey:ParentID" json:"parent,omitempty"` Children []Category `gorm:"foreignKey:ParentID" json:"children,omitempty"` Transactions []Transaction `gorm:"foreignKey:CategoryID" json:"-"` Budgets []Budget `gorm:"foreignKey:CategoryID" json:"-"` } // TableName specifies the table name for Category func (Category) TableName() string { return "categories" } // Tag represents a label that can be attached to transactions type Tag struct { ID uint `gorm:"primarykey" json:"id"` UserID uint `gorm:"not null;index" json:"user_id"` Name string `gorm:"size:50;not null" json:"name"` Color string `gorm:"size:20" json:"color"` CreatedAt time.Time `json:"created_at"` // Relationships Transactions []Transaction `gorm:"many2many:transaction_tags;" json:"-"` } // TableName specifies the table name for Tag func (Tag) TableName() string { return "tags" } // Transaction represents a single financial transaction type Transaction struct { BaseModel UserID uint `gorm:"not null;index" json:"user_id"` Amount float64 `gorm:"type:decimal(15,2);not null" json:"amount"` Type TransactionType `gorm:"size:20;not null" json:"type"` CategoryID uint `gorm:"not null;index" json:"category_id"` AccountID uint `gorm:"not null;index" json:"account_id"` Currency Currency `gorm:"size:10;not null;default:'CNY'" json:"currency"` TransactionDate time.Time `gorm:"type:date;not null;index" json:"transaction_date"` Note string `gorm:"size:500" json:"note,omitempty"` ImagePath string `gorm:"size:255" json:"image_path,omitempty"` RecurringID *uint `gorm:"index" json:"recurring_id,omitempty"` // For transfer transactions ToAccountID *uint `gorm:"index" json:"to_account_id,omitempty"` // Multi-ledger support // Feature: accounting-feature-upgrade // Validates: Requirements 3.10 LedgerID *uint `gorm:"index" json:"ledger_id,omitempty"` // Precise time recording // Feature: accounting-feature-upgrade // Validates: Requirements 5.2 TransactionTime *time.Time `gorm:"type:time" json:"transaction_time,omitempty"` // Transaction sub-type for special transactions // Feature: financial-core-upgrade // Validates: Requirements 3.2 SubType *TransactionSubType `gorm:"size:20" json:"sub_type,omitempty"` // interest, transfer_in, transfer_out, savings_deposit, savings_withdraw // Reimbursement related fields // Feature: accounting-feature-upgrade // Validates: Requirements 8.4-8.9 ReimbursementStatus string `gorm:"size:20;default:'none'" json:"reimbursement_status"` // none, pending, completed ReimbursementAmount *float64 `gorm:"type:decimal(15,2)" json:"reimbursement_amount,omitempty"` ReimbursementIncomeID *uint `gorm:"index" json:"reimbursement_income_id,omitempty"` // Refund related fields // Feature: accounting-feature-upgrade // Validates: Requirements 8.10-8.18 RefundStatus string `gorm:"size:20;default:'none'" json:"refund_status"` // none, partial, full RefundAmount *float64 `gorm:"type:decimal(15,2)" json:"refund_amount,omitempty"` RefundIncomeID *uint `gorm:"index" json:"refund_income_id,omitempty"` // Link to original transaction (for refund/reimbursement income records) // Feature: accounting-feature-upgrade // Validates: Requirements 8.19-8.22 OriginalTransactionID *uint `gorm:"index" json:"original_transaction_id,omitempty"` IncomeType string `gorm:"size:20" json:"income_type,omitempty"` // normal, refund, reimbursement // Relationships Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"` Account Account `gorm:"foreignKey:AccountID" json:"account,omitempty"` ToAccount *Account `gorm:"foreignKey:ToAccountID" json:"to_account,omitempty"` Recurring *RecurringTransaction `gorm:"foreignKey:RecurringID" json:"recurring,omitempty"` Tags []Tag `gorm:"many2many:transaction_tags;" json:"tags,omitempty"` Ledger *Ledger `gorm:"foreignKey:LedgerID" json:"ledger,omitempty"` Images []TransactionImage `gorm:"foreignKey:TransactionID" json:"images,omitempty"` OriginalTransaction *Transaction `gorm:"foreignKey:OriginalTransactionID" json:"original_transaction,omitempty"` } // TableName specifies the table name for Transaction func (Transaction) TableName() string { return "transactions" } // TransactionTag represents the many-to-many relationship between transactions and tags type TransactionTag struct { TransactionID uint `gorm:"primaryKey" json:"transaction_id"` TagID uint `gorm:"primaryKey" json:"tag_id"` } // TableName specifies the table name for TransactionTag func (TransactionTag) TableName() string { return "transaction_tags" } // Budget represents a spending budget for a category or account type Budget struct { BaseModel UserID uint `gorm:"not null;index" json:"user_id"` Name string `gorm:"size:100;not null" json:"name"` Amount float64 `gorm:"type:decimal(15,2);not null" json:"amount"` PeriodType PeriodType `gorm:"size:20;not null" json:"period_type"` CategoryID *uint `gorm:"index" json:"category_id,omitempty"` AccountID *uint `gorm:"index" json:"account_id,omitempty"` IsRolling bool `gorm:"default:false" json:"is_rolling"` StartDate time.Time `gorm:"type:date;not null" json:"start_date"` EndDate *time.Time `gorm:"type:date" json:"end_date,omitempty"` // Relationships Category *Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"` Account *Account `gorm:"foreignKey:AccountID" json:"account,omitempty"` } // TableName specifies the table name for Budget func (Budget) TableName() string { return "budgets" } // PiggyBank represents a savings goal type PiggyBank struct { BaseModel UserID uint `gorm:"not null;index" json:"user_id"` Name string `gorm:"size:100;not null" json:"name"` TargetAmount float64 `gorm:"type:decimal(15,2);not null" json:"target_amount"` CurrentAmount float64 `gorm:"type:decimal(15,2);default:0" json:"current_amount"` Type PiggyBankType `gorm:"size:20;not null" json:"type"` TargetDate *time.Time `gorm:"type:date" json:"target_date,omitempty"` LinkedAccountID *uint `gorm:"index" json:"linked_account_id,omitempty"` AutoRule string `gorm:"size:255" json:"auto_rule,omitempty"` // JSON string for auto deposit rules // Relationships LinkedAccount *Account `gorm:"foreignKey:LinkedAccountID" json:"linked_account,omitempty"` } // TableName specifies the table name for PiggyBank func (PiggyBank) TableName() string { return "piggy_banks" } // RecurringTransaction represents a template for recurring transactions type RecurringTransaction struct { BaseModel UserID uint `gorm:"not null;index" json:"user_id"` Amount float64 `gorm:"type:decimal(15,2);not null" json:"amount"` Type TransactionType `gorm:"size:20;not null" json:"type"` CategoryID uint `gorm:"not null;index" json:"category_id"` AccountID uint `gorm:"not null;index" json:"account_id"` Currency Currency `gorm:"size:10;not null;default:'CNY'" json:"currency"` Note string `gorm:"size:500" json:"note,omitempty"` Frequency FrequencyType `gorm:"size:20;not null" json:"frequency"` StartDate time.Time `gorm:"type:date;not null" json:"start_date"` EndDate *time.Time `gorm:"type:date" json:"end_date,omitempty"` NextOccurrence time.Time `gorm:"type:date;not null" json:"next_occurrence"` IsActive bool `gorm:"default:true" json:"is_active"` // Relationships Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"` Account Account `gorm:"foreignKey:AccountID" json:"account,omitempty"` Transactions []Transaction `gorm:"foreignKey:RecurringID" json:"-"` } // TableName specifies the table name for RecurringTransaction func (RecurringTransaction) TableName() string { return "recurring_transactions" } // AllocationRule represents a rule for automatically allocating income type AllocationRule struct { BaseModel UserID uint `gorm:"not null;index" json:"user_id"` Name string `gorm:"size:100;not null" json:"name"` TriggerType TriggerType `gorm:"size:20;not null" json:"trigger_type"` SourceAccountID *uint `gorm:"index" json:"source_account_id,omitempty"` // 触发分配的源账户,为空则匹配所有账户 IsActive bool `gorm:"default:true" json:"is_active"` // Relationships SourceAccount *Account `gorm:"foreignKey:SourceAccountID" json:"source_account,omitempty"` Targets []AllocationTarget `gorm:"foreignKey:RuleID" json:"targets,omitempty"` } // TableName specifies the table name for AllocationRule func (AllocationRule) TableName() string { return "allocation_rules" } // AllocationTarget represents a target for income allocation type AllocationTarget struct { ID uint `gorm:"primarykey" json:"id"` RuleID uint `gorm:"not null;index" json:"rule_id"` TargetType TargetType `gorm:"size:20;not null" json:"target_type"` TargetID uint `gorm:"not null" json:"target_id"` // Account ID or PiggyBank ID Percentage *float64 `gorm:"type:decimal(5,2)" json:"percentage,omitempty"` FixedAmount *float64 `gorm:"type:decimal(15,2)" json:"fixed_amount,omitempty"` // Relationships Rule AllocationRule `gorm:"foreignKey:RuleID" json:"-"` } // TableName specifies the table name for AllocationTarget func (AllocationTarget) TableName() string { return "allocation_targets" } // AllocationRecord represents a historical record of an allocation execution // This is a duplicate definition - the correct one is below at line 627 // Keeping this comment for reference but removing the duplicate struct // ExchangeRate represents currency exchange rates type ExchangeRate struct { ID uint `gorm:"primarykey" json:"id"` FromCurrency Currency `gorm:"size:10;not null;index:idx_currency_pair" json:"from_currency"` ToCurrency Currency `gorm:"size:10;not null;index:idx_currency_pair" json:"to_currency"` Rate float64 `gorm:"type:decimal(15,6);not null" json:"rate"` EffectiveDate time.Time `gorm:"type:date;not null;index" json:"effective_date"` } // TableName specifies the table name for ExchangeRate func (ExchangeRate) TableName() string { return "exchange_rates" } // ClassificationRule represents a rule for smart category classification type ClassificationRule struct { ID uint `gorm:"primarykey" json:"id"` UserID uint `gorm:"not null;index" json:"user_id"` Keyword string `gorm:"size:100;not null;index" json:"keyword"` CategoryID uint `gorm:"not null;index" json:"category_id"` MinAmount *float64 `gorm:"type:decimal(15,2)" json:"min_amount,omitempty"` MaxAmount *float64 `gorm:"type:decimal(15,2)" json:"max_amount,omitempty"` HitCount int `gorm:"default:0" json:"hit_count"` // Relationships Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"` } // TableName specifies the table name for ClassificationRule func (ClassificationRule) TableName() string { return "classification_rules" } // BillStatus represents the status of a credit card bill type BillStatus string const ( BillStatusPending BillStatus = "pending" // Bill generated, not yet paid BillStatusPaid BillStatus = "paid" // Bill fully paid BillStatusOverdue BillStatus = "overdue" // Payment date passed, not paid ) // CreditCardBill represents a credit card billing cycle statement type CreditCardBill struct { BaseModel UserID uint `gorm:"not null;index" json:"user_id"` AccountID uint `gorm:"not null;index" json:"account_id"` BillingDate time.Time `gorm:"type:date;not null;index" json:"billing_date"` // Statement date PaymentDueDate time.Time `gorm:"type:date;not null;index" json:"payment_due_date"` // Payment due date PreviousBalance float64 `gorm:"type:decimal(15,2);default:0" json:"previous_balance"` // Balance from previous bill TotalSpending float64 `gorm:"type:decimal(15,2);default:0" json:"total_spending"` // Total spending in this cycle TotalPayment float64 `gorm:"type:decimal(15,2);default:0" json:"total_payment"` // Total payments made in this cycle CurrentBalance float64 `gorm:"type:decimal(15,2);default:0" json:"current_balance"` // Outstanding balance MinimumPayment float64 `gorm:"type:decimal(15,2);default:0" json:"minimum_payment"` // Minimum payment required Status BillStatus `gorm:"size:20;not null;default:'pending'" json:"status"` PaidAmount float64 `gorm:"type:decimal(15,2);default:0" json:"paid_amount"` // Amount paid towards this bill PaidAt *time.Time `gorm:"type:datetime" json:"paid_at,omitempty"` // Relationships Account Account `gorm:"foreignKey:AccountID" json:"account,omitempty"` RepaymentPlan *RepaymentPlan `gorm:"foreignKey:BillID" json:"repayment_plan,omitempty"` } // TableName specifies the table name for CreditCardBill func (CreditCardBill) TableName() string { return "credit_card_bills" } // RepaymentPlanStatus represents the status of a repayment plan type RepaymentPlanStatus string const ( RepaymentPlanStatusActive RepaymentPlanStatus = "active" // Plan is active RepaymentPlanStatusCompleted RepaymentPlanStatus = "completed" // Plan completed RepaymentPlanStatusCancelled RepaymentPlanStatus = "cancelled" // Plan cancelled ) // RepaymentPlan represents a plan for repaying a credit card bill in installments type RepaymentPlan struct { BaseModel UserID uint `gorm:"not null;index" json:"user_id"` BillID uint `gorm:"not null;uniqueIndex" json:"bill_id"` // One plan per bill TotalAmount float64 `gorm:"type:decimal(15,2);not null" json:"total_amount"` RemainingAmount float64 `gorm:"type:decimal(15,2);not null" json:"remaining_amount"` InstallmentCount int `gorm:"not null" json:"installment_count"` InstallmentAmount float64 `gorm:"type:decimal(15,2);not null" json:"installment_amount"` Status RepaymentPlanStatus `gorm:"size:20;not null;default:'active'" json:"status"` // Relationships Bill CreditCardBill `gorm:"foreignKey:BillID" json:"bill,omitempty"` Installments []RepaymentInstallment `gorm:"foreignKey:PlanID" json:"installments,omitempty"` } // TableName specifies the table name for RepaymentPlan func (RepaymentPlan) TableName() string { return "repayment_plans" } // RepaymentInstallmentStatus represents the status of a repayment installment type RepaymentInstallmentStatus string const ( RepaymentInstallmentStatusPending RepaymentInstallmentStatus = "pending" // Not yet paid RepaymentInstallmentStatusPaid RepaymentInstallmentStatus = "paid" // Paid RepaymentInstallmentStatusOverdue RepaymentInstallmentStatus = "overdue" // Past due date ) // RepaymentInstallment represents a single installment in a repayment plan type RepaymentInstallment struct { BaseModel PlanID uint `gorm:"not null;index" json:"plan_id"` DueDate time.Time `gorm:"type:date;not null;index" json:"due_date"` Amount float64 `gorm:"type:decimal(15,2);not null" json:"amount"` PaidAmount float64 `gorm:"type:decimal(15,2);default:0" json:"paid_amount"` Status RepaymentInstallmentStatus `gorm:"size:20;not null;default:'pending'" json:"status"` PaidAt *time.Time `gorm:"type:datetime" json:"paid_at,omitempty"` Sequence int `gorm:"not null" json:"sequence"` // Installment number (1, 2, 3, ...) // Relationships Plan RepaymentPlan `gorm:"foreignKey:PlanID" json:"-"` } // TableName specifies the table name for RepaymentInstallment func (RepaymentInstallment) TableName() string { return "repayment_installments" } // PaymentReminder represents a reminder for upcoming payments type PaymentReminder struct { ID uint `gorm:"primarykey" json:"id"` BillID uint `gorm:"not null;index" json:"bill_id"` InstallmentID *uint `gorm:"index" json:"installment_id,omitempty"` // Optional, for installment reminders ReminderDate time.Time `gorm:"type:date;not null;index" json:"reminder_date"` Message string `gorm:"size:500;not null" json:"message"` IsRead bool `gorm:"default:false" json:"is_read"` CreatedAt time.Time `json:"created_at"` // Relationships Bill CreditCardBill `gorm:"foreignKey:BillID" json:"bill,omitempty"` Installment *RepaymentInstallment `gorm:"foreignKey:InstallmentID" json:"installment,omitempty"` } // TableName specifies the table name for PaymentReminder func (PaymentReminder) TableName() string { return "payment_reminders" } // AppLock represents the application lock settings type AppLock struct { ID uint `gorm:"primarykey" json:"id"` UserID uint `gorm:"not null;uniqueIndex" json:"user_id"` PasswordHash string `gorm:"size:255;not null" json:"-"` // bcrypt hash of password IsEnabled bool `gorm:"default:false" json:"is_enabled"` FailedAttempts int `gorm:"default:0" json:"failed_attempts"` LockedUntil *time.Time `gorm:"type:datetime" json:"locked_until,omitempty"` LastFailedAttempt *time.Time `gorm:"type:datetime" json:"last_failed_attempt,omitempty"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // User represents a user account for authentication // Feature: api-interface-optimization // Validates: Requirements 12, 13 type User struct { ID uint `gorm:"primarykey" json:"id"` Email string `gorm:"size:255;uniqueIndex" json:"email"` PasswordHash string `gorm:"size:255" json:"-"` Username string `gorm:"size:100" json:"username"` Avatar string `gorm:"size:500" json:"avatar,omitempty"` IsActive bool `gorm:"default:true" json:"is_active"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // Relationships OAuthAccounts []OAuthAccount `gorm:"foreignKey:UserID" json:"oauth_accounts,omitempty"` } // TableName specifies the table name for User func (User) TableName() string { return "users" } // OAuthAccount represents an OAuth provider account linked to a user // Feature: api-interface-optimization // Validates: Requirements 13 type OAuthAccount struct { ID uint `gorm:"primarykey" json:"id"` UserID uint `gorm:"index" json:"user_id"` Provider string `gorm:"size:50;index" json:"provider"` // github, google, etc. ProviderID string `gorm:"size:255;index" json:"provider_id"` AccessToken string `gorm:"size:500" json:"-"` CreatedAt time.Time `json:"created_at"` // Relationships User User `gorm:"foreignKey:UserID" json:"-"` } // TableName specifies the table name for OAuthAccount func (OAuthAccount) TableName() string { return "oauth_accounts" } // TransactionTemplate represents a quick transaction template // Feature: api-interface-optimization // Validates: Requirements 15.1, 15.2 type TransactionTemplate struct { ID uint `gorm:"primarykey" json:"id"` UserID *uint `gorm:"index" json:"user_id,omitempty"` Name string `gorm:"size:100;not null" json:"name"` Amount float64 `gorm:"type:decimal(15,2)" json:"amount"` Type TransactionType `gorm:"size:20;not null" json:"type"` CategoryID uint `gorm:"not null" json:"category_id"` AccountID uint `gorm:"not null" json:"account_id"` Currency Currency `gorm:"size:10;not null;default:'CNY'" json:"currency"` Note string `gorm:"size:500" json:"note,omitempty"` SortOrder int `gorm:"default:0" json:"sort_order"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` // Relationships Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"` Account Account `gorm:"foreignKey:AccountID" json:"account,omitempty"` } // TableName specifies the table name for TransactionTemplate func (TransactionTemplate) TableName() string { return "transaction_templates" } // UserPreference represents user preferences for quick entry // Feature: api-interface-optimization // Validates: Requirements 15.4 type UserPreference struct { ID uint `gorm:"primarykey" json:"id"` UserID *uint `gorm:"uniqueIndex" json:"user_id,omitempty"` LastAccountID *uint `gorm:"index" json:"last_account_id,omitempty"` LastCategoryID *uint `gorm:"index" json:"last_category_id,omitempty"` FrequentAccounts string `gorm:"size:500" json:"frequent_accounts,omitempty"` // JSON array of account IDs FrequentCategories string `gorm:"size:500" json:"frequent_categories,omitempty"` // JSON array of category IDs CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // TableName specifies the table name for UserPreference func (UserPreference) TableName() string { return "user_preferences" } // TableName specifies the table name for AppLock func (AppLock) TableName() string { return "app_locks" } // IsLocked returns true if the app is currently locked due to failed attempts func (a *AppLock) IsLocked() bool { if a.LockedUntil == nil { return false } return time.Now().Before(*a.LockedUntil) } // AllocationRecord represents a record of income allocation execution type AllocationRecord struct { ID uint `gorm:"primarykey" json:"id"` UserID uint `gorm:"not null;index" json:"user_id"` RuleID uint `gorm:"not null;index" json:"rule_id"` RuleName string `gorm:"size:100;not null" json:"rule_name"` SourceAccountID uint `gorm:"not null;index" json:"source_account_id"` TotalAmount float64 `gorm:"type:decimal(15,2);not null" json:"total_amount"` AllocatedAmount float64 `gorm:"type:decimal(15,2);not null" json:"allocated_amount"` RemainingAmount float64 `gorm:"type:decimal(15,2);not null" json:"remaining_amount"` Note string `gorm:"size:500" json:"note,omitempty"` CreatedAt time.Time `json:"created_at"` // Relationships Rule AllocationRule `gorm:"foreignKey:RuleID" json:"rule,omitempty"` SourceAccount Account `gorm:"foreignKey:SourceAccountID" json:"source_account,omitempty"` Details []AllocationRecordDetail `gorm:"foreignKey:RecordID" json:"details,omitempty"` } // TableName specifies the table name for AllocationRecord func (AllocationRecord) TableName() string { return "allocation_records" } // AllocationRecordDetail represents a single allocation detail in a record type AllocationRecordDetail struct { ID uint `gorm:"primarykey" json:"id"` RecordID uint `gorm:"not null;index" json:"record_id"` TargetType TargetType `gorm:"size:20;not null" json:"target_type"` TargetID uint `gorm:"not null" json:"target_id"` TargetName string `gorm:"size:100;not null" json:"target_name"` Amount float64 `gorm:"type:decimal(15,2);not null" json:"amount"` Percentage *float64 `gorm:"type:decimal(5,2)" json:"percentage,omitempty"` FixedAmount *float64 `gorm:"type:decimal(15,2)" json:"fixed_amount,omitempty"` // Relationships Record AllocationRecord `gorm:"foreignKey:RecordID" json:"-"` } // TableName specifies the table name for AllocationRecordDetail func (AllocationRecordDetail) TableName() string { return "allocation_record_details" } // AllModels returns all models for database migration func AllModels() []interface{} { return []interface{}{ &Account{}, &Category{}, &Tag{}, &Transaction{}, &TransactionTag{}, // Explicit join table for many-to-many relationship &Budget{}, &PiggyBank{}, &RecurringTransaction{}, &AllocationRule{}, &AllocationTarget{}, &AllocationRecord{}, &AllocationRecordDetail{}, &ExchangeRate{}, &ClassificationRule{}, &CreditCardBill{}, &RepaymentPlan{}, &RepaymentInstallment{}, &PaymentReminder{}, &AppLock{}, &User{}, &OAuthAccount{}, &TransactionTemplate{}, &UserPreference{}, &Ledger{}, // Feature: accounting-feature-upgrade &SystemCategory{}, // Feature: accounting-feature-upgrade &TransactionImage{}, // Feature: accounting-feature-upgrade &UserSettings{}, // Feature: accounting-feature-upgrade } } // IsCreditAccountType returns true if the account type supports negative balance func IsCreditAccountType(accountType AccountType) bool { return accountType == AccountTypeCreditCard || accountType == AccountTypeCreditLine } // CurrencyInfo contains display information for a currency type CurrencyInfo struct { Code Currency `json:"code"` Name string `json:"name"` Symbol string `json:"symbol"` } // GetCurrencyInfo returns display information for all supported currencies func GetCurrencyInfo() []CurrencyInfo { return []CurrencyInfo{ // Major currencies {Code: CurrencyCNY, Name: "人民币", Symbol: "¥"}, {Code: CurrencyUSD, Name: "美元", Symbol: "$"}, {Code: CurrencyEUR, Name: "欧元", Symbol: "€"}, {Code: CurrencyJPY, Name: "日元", Symbol: "¥"}, {Code: CurrencyGBP, Name: "英镑", Symbol: "£"}, {Code: CurrencyHKD, Name: "港币", Symbol: "HK$"}, // Asia Pacific {Code: CurrencyAUD, Name: "澳元", Symbol: "A$"}, {Code: CurrencyNZD, Name: "新西兰元", Symbol: "NZ$"}, {Code: CurrencySGD, Name: "新加坡元", Symbol: "S$"}, {Code: CurrencyKRW, Name: "韩元", Symbol: "₩"}, {Code: CurrencyTHB, Name: "泰铢", Symbol: "฿"}, {Code: CurrencyTWD, Name: "新台币", Symbol: "NT$"}, {Code: CurrencyMOP, Name: "澳门元", Symbol: "MOP$"}, {Code: CurrencyPHP, Name: "菲律宾比索", Symbol: "₱"}, {Code: CurrencyIDR, Name: "印尼盾", Symbol: "Rp"}, {Code: CurrencyINR, Name: "印度卢比", Symbol: "₹"}, {Code: CurrencyVND, Name: "越南盾", Symbol: "₫"}, {Code: CurrencyMNT, Name: "蒙古图格里克", Symbol: "₮"}, {Code: CurrencyKHR, Name: "柬埔寨瑞尔", Symbol: "៛"}, {Code: CurrencyNPR, Name: "尼泊尔卢比", Symbol: "₨"}, {Code: CurrencyPKR, Name: "巴基斯坦卢比", Symbol: "₨"}, {Code: CurrencyBND, Name: "文莱元", Symbol: "B$"}, // Europe {Code: CurrencyCHF, Name: "瑞士法郎", Symbol: "CHF"}, {Code: CurrencySEK, Name: "瑞典克朗", Symbol: "kr"}, {Code: CurrencyNOK, Name: "挪威克朗", Symbol: "kr"}, {Code: CurrencyDKK, Name: "丹麦克朗", Symbol: "kr"}, {Code: CurrencyCZK, Name: "捷克克朗", Symbol: "Kč"}, {Code: CurrencyHUF, Name: "匈牙利福林", Symbol: "Ft"}, {Code: CurrencyRUB, Name: "俄罗斯卢布", Symbol: "₽"}, {Code: CurrencyTRY, Name: "土耳其里拉", Symbol: "₺"}, // Americas {Code: CurrencyCAD, Name: "加元", Symbol: "C$"}, {Code: CurrencyMXN, Name: "墨西哥比索", Symbol: "Mex$"}, {Code: CurrencyBRL, Name: "巴西雷亚尔", Symbol: "R$"}, // Middle East & Africa {Code: CurrencyAED, Name: "阿联酋迪拉姆", Symbol: "د.إ"}, {Code: CurrencySAR, Name: "沙特里亚尔", Symbol: "﷼"}, {Code: CurrencyQAR, Name: "卡塔尔里亚尔", Symbol: "﷼"}, {Code: CurrencyKWD, Name: "科威特第纳尔", Symbol: "د.ك"}, {Code: CurrencyILS, Name: "以色列新谢克尔", Symbol: "₪"}, {Code: CurrencyZAR, Name: "南非兰特", Symbol: "R"}, } }