Files
Novault-backend/internal/models/TRANSACTION_EXTENSION_IMPLEMENTATION.md
2026-01-25 21:59:00 +08:00

6.0 KiB

Transaction Model Extension Implementation

Overview

This document describes the implementation of Task 1.5: 扩展Transaction模型 (Extend Transaction Model) from the accounting-feature-upgrade specification.

Implementation Summary

1. Model Extensions

Extended the Transaction model in backend/internal/models/models.go with the following new fields:

Multi-Ledger Support

  • LedgerID (*uint): Associates transaction with a specific ledger
  • Validates: Requirements 3.10

Precise Time Recording

  • TransactionTime (*time.Time): Records precise transaction time (HH:mm:ss)
  • Validates: Requirements 5.2

Reimbursement Fields

  • ReimbursementStatus (string): Status of reimbursement (none, pending, completed)
  • ReimbursementAmount (*float64): Amount to be reimbursed
  • ReimbursementIncomeID (*uint): Links to the generated reimbursement income transaction
  • Validates: Requirements 8.4-8.9

Refund Fields

  • RefundStatus (string): Status of refund (none, partial, full)
  • RefundAmount (*float64): Amount refunded
  • RefundIncomeID (*uint): Links to the generated refund income transaction
  • Validates: Requirements 8.10-8.18
  • OriginalTransactionID (*uint): Links refund/reimbursement income back to original expense
  • IncomeType (string): Type of income (normal, refund, reimbursement)
  • Validates: Requirements 8.19-8.22

New Relationships

  • Ledger: Foreign key relationship to Ledger model
  • Images: One-to-many relationship with TransactionImage
  • OriginalTransaction: Self-referencing relationship for refund/reimbursement tracking

2. Database Migration

Created migration file: backend/migrations/003_extend_transaction_model.sql

The migration adds the following columns to the transactions table:

  • transaction_time (TIME)
  • reimbursement_status (VARCHAR(20), default: 'none')
  • reimbursement_amount (DECIMAL(15,2))
  • reimbursement_income_id (BIGINT UNSIGNED)
  • refund_status (VARCHAR(20), default: 'none')
  • refund_amount (DECIMAL(15,2))
  • refund_income_id (BIGINT UNSIGNED)
  • original_transaction_id (BIGINT UNSIGNED)
  • income_type (VARCHAR(20))

Indexes created:

  • idx_transactions_reimbursement_income_id
  • idx_transactions_refund_income_id
  • idx_transactions_original_transaction_id

3. Test Coverage

Created comprehensive test file: backend/internal/models/transaction_extension_test.go

Test functions:

  1. TestTransactionExtensionFields: Verifies all new fields are properly set
  2. TestTransactionReimbursementStatuses: Validates reimbursement status values
  3. TestTransactionRefundStatuses: Validates refund status values
  4. TestTransactionIncomeTypes: Validates income type values
  5. TestTransactionDefaultValues: Verifies default values for new fields
  6. TestTransactionRelationships: Verifies new relationship fields
  7. TestTransactionPreciseTime: Tests precise time recording functionality
  8. TestTransactionReimbursementFlow: Tests complete reimbursement workflow
  9. TestTransactionRefundFlow: Tests complete refund workflow (full and partial)
  10. TestTransactionOriginalLink: Tests original transaction linking
  11. TestTransactionLedgerAssociation: Tests ledger association

All tests pass successfully ✓

4. Migration Execution

The migration was successfully executed using:

go run cmd/migrate/main.go

Results:

  • ✓ All existing tables updated
  • ✓ New transaction_images table created
  • ✓ New user_settings table created
  • ✓ System categories initialized (refund, reimbursement)

Field Details

Reimbursement Status Values

  • none: No reimbursement requested
  • pending: Reimbursement requested, awaiting confirmation
  • completed: Reimbursement confirmed and income record created

Refund Status Values

  • none: No refund processed
  • partial: Partial refund (amount < original amount)
  • full: Full refund (amount = original amount)

Income Type Values

  • normal: Regular income transaction
  • refund: Income generated from a refund
  • reimbursement: Income generated from a reimbursement

Usage Examples

Creating a Transaction with Precise Time

transactionTime := time.Date(2024, 1, 15, 14, 30, 0, 0, time.UTC)
tx := Transaction{
    Amount:          100.00,
    Type:            TransactionTypeExpense,
    TransactionDate: transactionTime,
    TransactionTime: &transactionTime,
    LedgerID:        &ledgerID,
}

Applying for Reimbursement

reimbursementAmount := 80.00
expense.ReimbursementStatus = "pending"
expense.ReimbursementAmount = &reimbursementAmount

Processing a Refund

refundAmount := 50.00
incomeID := uint(200)
expense.RefundStatus = "partial"
expense.RefundAmount = &refundAmount
expense.RefundIncomeID = &incomeID

Creating Linked Income Record

refundIncome := Transaction{
    Type:                  TransactionTypeIncome,
    Amount:                50.00,
    IncomeType:            "refund",
    OriginalTransactionID: &originalExpenseID,
    LedgerID:              originalExpense.LedgerID, // Same ledger as original
}

Requirements Validation

This implementation validates the following requirements:

  • ✓ 3.10: Multi-ledger transaction association
  • ✓ 5.2: Precise time recording
  • ✓ 8.4-8.9: Reimbursement workflow
  • ✓ 8.10-8.18: Refund workflow
  • ✓ 8.19-8.22: Original transaction linking
  • ✓ 8.28: Ledger consistency for refund/reimbursement income

Next Steps

The Transaction model is now ready for:

  1. Backend API implementation for reimbursement operations (Task 5.1)
  2. Backend API implementation for refund operations (Task 5.2)
  3. Frontend integration with transaction forms
  4. Property-based testing for transaction workflows

Notes

  • All pointer fields (*uint, *float64, *time.Time) are optional and can be nil
  • Default values for status fields are set by GORM on database insert
  • Foreign key constraints are commented out in the migration to avoid circular reference issues
  • The implementation maintains backward compatibility with existing transactions