diff --git a/internal/model/budget.go b/internal/model/budget.go deleted file mode 100644 index 8f5f220..0000000 --- a/internal/model/budget.go +++ /dev/null @@ -1,54 +0,0 @@ -package model - -import ( - "strings" - "time" - - "github.com/shopspring/decimal" -) - -type BudgetPeriod string - -const ( - BudgetPeriodWeekly BudgetPeriod = "weekly" - BudgetPeriodMonthly BudgetPeriod = "monthly" - BudgetPeriodYearly BudgetPeriod = "yearly" -) - -type BudgetStatus string - -const ( - BudgetStatusOnTrack BudgetStatus = "on_track" - BudgetStatusWarning BudgetStatus = "warning" - BudgetStatusOver BudgetStatus = "over" -) - -type Budget struct { - ID string `db:"id"` - SpaceID string `db:"space_id"` - Amount decimal.Decimal `db:"amount"` - AmountCents int `db:"amount_cents"` // deprecated: kept for SELECT * compatibility - Period BudgetPeriod `db:"period"` - StartDate time.Time `db:"start_date"` - EndDate *time.Time `db:"end_date"` - IsActive bool `db:"is_active"` - CreatedBy string `db:"created_by"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -type BudgetWithSpent struct { - Budget - Tags []*Tag - Spent decimal.Decimal - Percentage float64 - Status BudgetStatus -} - -func (b *BudgetWithSpent) TagNames() string { - names := make([]string, len(b.Tags)) - for i, t := range b.Tags { - names[i] = t.Name - } - return strings.Join(names, ", ") -} diff --git a/internal/model/expense.go b/internal/model/expense.go deleted file mode 100644 index 2adef6e..0000000 --- a/internal/model/expense.go +++ /dev/null @@ -1,57 +0,0 @@ -package model - -import ( - "time" - - "github.com/shopspring/decimal" -) - -type ExpenseType string - -const ( - ExpenseTypeExpense ExpenseType = "expense" - ExpenseTypeTopup ExpenseType = "topup" -) - -type Expense struct { - ID string `db:"id"` - SpaceID string `db:"space_id"` - CreatedBy string `db:"created_by"` - Description string `db:"description"` - Amount decimal.Decimal `db:"amount"` - AmountCents int `db:"amount_cents"` // deprecated: kept for SELECT * compatibility - Type ExpenseType `db:"type"` - Date time.Time `db:"date"` - PaymentMethodID *string `db:"payment_method_id"` - RecurringExpenseID *string `db:"recurring_expense_id"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -type ExpenseWithTags struct { - Expense - Tags []*Tag -} - -type ExpenseWithTagsAndMethod struct { - Expense - Tags []*Tag - PaymentMethod *PaymentMethod -} - -type ExpenseTag struct { - ExpenseID string `db:"expense_id"` - TagID string `db:"tag_id"` -} - -type ExpenseItem struct { - ExpenseID string `db:"expense_id"` - ItemID string `db:"item_id"` -} - -type TagExpenseSummary struct { - TagID string `db:"tag_id"` - TagName string `db:"tag_name"` - TagColor *string `db:"tag_color"` - TotalAmount decimal.Decimal `db:"total_amount"` -} diff --git a/internal/model/file.go b/internal/model/file.go deleted file mode 100644 index 9087082..0000000 --- a/internal/model/file.go +++ /dev/null @@ -1,23 +0,0 @@ -package model - -import ( - "time" -) - -const ( - FileTypeAvatar = "avatar" -) - -type File struct { - ID string `db:"id"` - UserID string `db:"user_id"` // Who owns/created this file - OwnerType string `db:"owner_type"` // "user", "profile", etc. - the entity that owns the file - OwnerID string `db:"owner_id"` // Polymorphic FK - Type string `db:"type"` - Filename string `db:"filename"` - OriginalName string `db:"original_name"` - MimeType string `db:"mime_type"` - Size int64 `db:"size"` - StoragePath string `db:"storage_path"` - CreatedAt time.Time `db:"created_at"` -} diff --git a/internal/model/financial_management.go b/internal/model/financial_management.go new file mode 100644 index 0000000..56668f9 --- /dev/null +++ b/internal/model/financial_management.go @@ -0,0 +1,41 @@ +package model + +import ( + "time" + + "github.com/shopspring/decimal" +) + +type Account struct { + ID string `db:"id"` + Name string `db:"name"` + SpaceID string `db:"space_id"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` +} + +type TransactionType string + +const ( + TransactionTypeDeposit TransactionType = "deposit" + TransactionTypeWithdrawal TransactionType = "withdrawal" +) + +type Transaction struct { + ID string `db:"id"` + Value decimal.Decimal `db:"value"` + Type TransactionType `db:"type"` + AccountID string `db:"account_id"` + Description *string `db:"description"` + RelatedTransactionID *string `db:"related_transaction_id"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` +} + +type Tag struct { + ID string `db:"id"` + Name string `db:"name"` + SpaceID string `db:"space_id"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` +} diff --git a/internal/model/token.go b/internal/model/identity_access.go similarity index 59% rename from internal/model/token.go rename to internal/model/identity_access.go index 0746c7d..c61f9d9 100644 --- a/internal/model/token.go +++ b/internal/model/identity_access.go @@ -1,8 +1,22 @@ package model -import ( - "time" -) +import "time" + +type User struct { + ID string `db:"id"` + Email string `db:"email"` + Name *string `db:"name"` + // Allow null for passwordless users + PasswordHash *string `db:"password_hash"` + PendingEmail *string `db:"pending_email"` + EmailVerifiedAt *time.Time `db:"email_verified_at"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` +} + +func (u *User) HasPassword() bool { + return u.PasswordHash != nil && *u.PasswordHash != "" +} type Token struct { ID string `db:"id"` diff --git a/internal/model/invitation.go b/internal/model/invitation.go deleted file mode 100644 index 09b5069..0000000 --- a/internal/model/invitation.go +++ /dev/null @@ -1,22 +0,0 @@ -package model - -import "time" - -type InvitationStatus string - -const ( - InvitationStatusPending InvitationStatus = "pending" - InvitationStatusAccepted InvitationStatus = "accepted" - InvitationStatusExpired InvitationStatus = "expired" -) - -type SpaceInvitation struct { - Token string `db:"token"` - SpaceID string `db:"space_id"` - InviterID string `db:"inviter_id"` - Email string `db:"email"` - Status InvitationStatus `db:"status"` - ExpiresAt time.Time `db:"expires_at"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} diff --git a/internal/model/loan.go b/internal/model/loan.go deleted file mode 100644 index 31fff9b..0000000 --- a/internal/model/loan.go +++ /dev/null @@ -1,30 +0,0 @@ -package model - -import ( - "time" - - "github.com/shopspring/decimal" -) - -type Loan struct { - ID string `db:"id"` - SpaceID string `db:"space_id"` - Name string `db:"name"` - Description string `db:"description"` - OriginalAmount decimal.Decimal `db:"original_amount"` - OriginalAmountCents int `db:"original_amount_cents"` // deprecated: kept for SELECT * compatibility - InterestRateBps int `db:"interest_rate_bps"` - StartDate time.Time `db:"start_date"` - EndDate *time.Time `db:"end_date"` - IsPaidOff bool `db:"is_paid_off"` - CreatedBy string `db:"created_by"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -type LoanWithPaymentSummary struct { - Loan - TotalPaid decimal.Decimal - Remaining decimal.Decimal - ReceiptCount int -} diff --git a/internal/model/money.go b/internal/model/money.go deleted file mode 100644 index 426c5ac..0000000 --- a/internal/model/money.go +++ /dev/null @@ -1,17 +0,0 @@ -package model - -import ( - "fmt" - - "github.com/shopspring/decimal" -) - -// FormatMoney formats a decimal as a dollar string like "$12.50" -func FormatMoney(d decimal.Decimal) string { - return fmt.Sprintf("$%s", d.StringFixed(2)) -} - -// FormatDecimal formats a decimal for form input values like "12.50" -func FormatDecimal(d decimal.Decimal) string { - return d.StringFixed(2) -} diff --git a/internal/model/money_account.go b/internal/model/money_account.go deleted file mode 100644 index bdbf209..0000000 --- a/internal/model/money_account.go +++ /dev/null @@ -1,45 +0,0 @@ -package model - -import ( - "time" - - "github.com/shopspring/decimal" -) - -type TransferDirection string - -const ( - TransferDirectionDeposit TransferDirection = "deposit" - TransferDirectionWithdrawal TransferDirection = "withdrawal" -) - -type MoneyAccount struct { - ID string `db:"id"` - SpaceID string `db:"space_id"` - Name string `db:"name"` - CreatedBy string `db:"created_by"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -type AccountTransfer struct { - ID string `db:"id"` - AccountID string `db:"account_id"` - Amount decimal.Decimal `db:"amount"` - AmountCents int `db:"amount_cents"` // deprecated: kept for SELECT * compatibility - Direction TransferDirection `db:"direction"` - Note string `db:"note"` - RecurringDepositID *string `db:"recurring_deposit_id"` - CreatedBy string `db:"created_by"` - CreatedAt time.Time `db:"created_at"` -} - -type MoneyAccountWithBalance struct { - MoneyAccount - Balance decimal.Decimal -} - -type AccountTransferWithAccount struct { - AccountTransfer - AccountName string `db:"account_name"` -} diff --git a/internal/model/payment_method.go b/internal/model/payment_method.go deleted file mode 100644 index bc36ab4..0000000 --- a/internal/model/payment_method.go +++ /dev/null @@ -1,21 +0,0 @@ -package model - -import "time" - -type PaymentMethodType string - -const ( - PaymentMethodTypeCredit PaymentMethodType = "credit" - PaymentMethodTypeDebit PaymentMethodType = "debit" -) - -type PaymentMethod struct { - ID string `db:"id"` - SpaceID string `db:"space_id"` - Name string `db:"name"` - Type PaymentMethodType `db:"type"` - LastFour *string `db:"last_four"` - CreatedBy string `db:"created_by"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} diff --git a/internal/model/profile.go b/internal/model/profile.go deleted file mode 100644 index 1df43ce..0000000 --- a/internal/model/profile.go +++ /dev/null @@ -1,25 +0,0 @@ -package model - -import "time" - -type Profile struct { - ID string `db:"id"` - UserID string `db:"user_id"` - Name string `db:"name"` - Timezone *string `db:"timezone"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -// Location returns the *time.Location for this profile's timezone. -// Returns UTC if timezone is nil or invalid. -func (p *Profile) Location() *time.Location { - if p.Timezone == nil || *p.Timezone == "" { - return time.UTC - } - loc, err := time.LoadLocation(*p.Timezone) - if err != nil { - return time.UTC - } - return loc -} diff --git a/internal/model/receipt.go b/internal/model/receipt.go deleted file mode 100644 index cc4544d..0000000 --- a/internal/model/receipt.go +++ /dev/null @@ -1,54 +0,0 @@ -package model - -import ( - "time" - - "github.com/shopspring/decimal" -) - -type FundingSourceType string - -const ( - FundingSourceBalance FundingSourceType = "balance" - FundingSourceAccount FundingSourceType = "account" -) - -type Receipt struct { - ID string `db:"id"` - LoanID string `db:"loan_id"` - SpaceID string `db:"space_id"` - Description string `db:"description"` - TotalAmount decimal.Decimal `db:"total_amount"` - TotalAmountCents int `db:"total_amount_cents"` // deprecated: kept for SELECT * compatibility - Date time.Time `db:"date"` - RecurringReceiptID *string `db:"recurring_receipt_id"` - CreatedBy string `db:"created_by"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -type ReceiptFundingSource struct { - ID string `db:"id"` - ReceiptID string `db:"receipt_id"` - SourceType FundingSourceType `db:"source_type"` - AccountID *string `db:"account_id"` - Amount decimal.Decimal `db:"amount"` - AmountCents int `db:"amount_cents"` // deprecated: kept for SELECT * compatibility - LinkedExpenseID *string `db:"linked_expense_id"` - LinkedTransferID *string `db:"linked_transfer_id"` -} - -type ReceiptWithSources struct { - Receipt - Sources []ReceiptFundingSource -} - -type ReceiptFundingSourceWithAccount struct { - ReceiptFundingSource - AccountName string -} - -type ReceiptWithSourcesAndAccounts struct { - Receipt - Sources []ReceiptFundingSourceWithAccount -} diff --git a/internal/model/recurring_expense.go b/internal/model/recurring_expense.go deleted file mode 100644 index 4fd7bca..0000000 --- a/internal/model/recurring_expense.go +++ /dev/null @@ -1,46 +0,0 @@ -package model - -import ( - "time" - - "github.com/shopspring/decimal" -) - -type Frequency string - -const ( - FrequencyDaily Frequency = "daily" - FrequencyWeekly Frequency = "weekly" - FrequencyBiweekly Frequency = "biweekly" - FrequencyMonthly Frequency = "monthly" - FrequencyYearly Frequency = "yearly" -) - -type RecurringExpense struct { - ID string `db:"id"` - SpaceID string `db:"space_id"` - CreatedBy string `db:"created_by"` - Description string `db:"description"` - Amount decimal.Decimal `db:"amount"` - AmountCents int `db:"amount_cents"` // deprecated: kept for SELECT * compatibility - Type ExpenseType `db:"type"` - PaymentMethodID *string `db:"payment_method_id"` - Frequency Frequency `db:"frequency"` - StartDate time.Time `db:"start_date"` - EndDate *time.Time `db:"end_date"` - NextOccurrence time.Time `db:"next_occurrence"` - IsActive bool `db:"is_active"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -type RecurringExpenseWithTags struct { - RecurringExpense - Tags []*Tag -} - -type RecurringExpenseWithTagsAndMethod struct { - RecurringExpense - Tags []*Tag - PaymentMethod *PaymentMethod -} diff --git a/internal/model/recurring_receipt.go b/internal/model/recurring_receipt.go deleted file mode 100644 index 10fa1bd..0000000 --- a/internal/model/recurring_receipt.go +++ /dev/null @@ -1,44 +0,0 @@ -package model - -import ( - "time" - - "github.com/shopspring/decimal" -) - -type RecurringReceipt struct { - ID string `db:"id"` - LoanID string `db:"loan_id"` - SpaceID string `db:"space_id"` - Description string `db:"description"` - TotalAmount decimal.Decimal `db:"total_amount"` - TotalAmountCents int `db:"total_amount_cents"` // deprecated: kept for SELECT * compatibility - Frequency Frequency `db:"frequency"` - StartDate time.Time `db:"start_date"` - EndDate *time.Time `db:"end_date"` - NextOccurrence time.Time `db:"next_occurrence"` - IsActive bool `db:"is_active"` - CreatedBy string `db:"created_by"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -type RecurringReceiptSource struct { - ID string `db:"id"` - RecurringReceiptID string `db:"recurring_receipt_id"` - SourceType FundingSourceType `db:"source_type"` - AccountID *string `db:"account_id"` - Amount decimal.Decimal `db:"amount"` - AmountCents int `db:"amount_cents"` // deprecated: kept for SELECT * compatibility -} - -type RecurringReceiptWithSources struct { - RecurringReceipt - Sources []RecurringReceiptSource -} - -type RecurringReceiptWithLoan struct { - RecurringReceipt - LoanName string - Sources []RecurringReceiptSource -} diff --git a/internal/model/report.go b/internal/model/report.go deleted file mode 100644 index 8586c99..0000000 --- a/internal/model/report.go +++ /dev/null @@ -1,35 +0,0 @@ -package model - -import ( - "time" - - "github.com/shopspring/decimal" -) - -type DailySpending struct { - Date time.Time `db:"date"` - Total decimal.Decimal `db:"total"` -} - -type MonthlySpending struct { - Month string `db:"month"` - Total decimal.Decimal `db:"total"` -} - -type PaymentMethodExpenseSummary struct { - PaymentMethodID string `db:"payment_method_id"` - PaymentMethodName string `db:"payment_method_name"` - PaymentMethodType string `db:"payment_method_type"` - TotalAmount decimal.Decimal `db:"total_amount"` -} - -type SpendingReport struct { - ByTag []*TagExpenseSummary - ByPaymentMethod []*PaymentMethodExpenseSummary - DailySpending []*DailySpending - MonthlySpending []*MonthlySpending - TopExpenses []*ExpenseWithTagsAndMethod - TotalIncome decimal.Decimal - TotalExpenses decimal.Decimal - NetBalance decimal.Decimal -} diff --git a/internal/model/shopping_list.go b/internal/model/shopping_list.go deleted file mode 100644 index f0a230f..0000000 --- a/internal/model/shopping_list.go +++ /dev/null @@ -1,33 +0,0 @@ -package model - -import "time" - -type ShoppingList struct { - ID string `db:"id"` - SpaceID string `db:"space_id"` - Name string `db:"name"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -type ListItem struct { - ID string `db:"id"` - ListID string `db:"list_id"` - Name string `db:"name"` - IsChecked bool `db:"is_checked"` - CreatedBy string `db:"created_by"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -type ListWithUncheckedItems struct { - List *ShoppingList - Items []*ListItem -} - -type ListCardData struct { - List *ShoppingList - Items []*ListItem - CurrentPage int - TotalPages int -} diff --git a/internal/model/space.go b/internal/model/space.go deleted file mode 100644 index 5cebf4e..0000000 --- a/internal/model/space.go +++ /dev/null @@ -1,48 +0,0 @@ -package model - -import "time" - -type Role string - -const ( - RoleOwner Role = "owner" - RoleMember Role = "member" -) - -type Space struct { - ID string `db:"id"` - Name string `db:"name"` - OwnerID string `db:"owner_id"` - Timezone *string `db:"timezone"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} - -// Location returns the *time.Location for this space's timezone. -// Returns nil if timezone is not set, so callers can distinguish "not set" from "UTC". -func (s *Space) Location() *time.Location { - if s.Timezone == nil || *s.Timezone == "" { - return nil - } - loc, err := time.LoadLocation(*s.Timezone) - if err != nil { - return nil - } - return loc -} - -type SpaceMember struct { - SpaceID string `db:"space_id"` - UserID string `db:"user_id"` - Role Role `db:"role"` - JoinedAt time.Time `db:"joined_at"` -} - -type SpaceMemberWithProfile struct { - SpaceID string `db:"space_id"` - UserID string `db:"user_id"` - Role Role `db:"role"` - JoinedAt time.Time `db:"joined_at"` - Name string `db:"name"` - Email string `db:"email"` -} diff --git a/internal/model/tag.go b/internal/model/tag.go deleted file mode 100644 index 0b358fa..0000000 --- a/internal/model/tag.go +++ /dev/null @@ -1,12 +0,0 @@ -package model - -import "time" - -type Tag struct { - ID string `db:"id"` - SpaceID string `db:"space_id"` - Name string `db:"name"` - Color *string `db:"color"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` -} diff --git a/internal/model/user.go b/internal/model/user.go deleted file mode 100644 index a823efd..0000000 --- a/internal/model/user.go +++ /dev/null @@ -1,17 +0,0 @@ -package model - -import "time" - -type User struct { - ID string `db:"id"` - Email string `db:"email"` - // Allow null for passwordless users - PasswordHash *string `db:"password_hash"` - PendingEmail *string `db:"pending_email"` - EmailVerifiedAt *time.Time `db:"email_verified_at"` - CreatedAt time.Time `db:"created_at"` -} - -func (u *User) HasPassword() bool { - return u.PasswordHash != nil && *u.PasswordHash != "" -} diff --git a/internal/model/workspace_collaboration.go b/internal/model/workspace_collaboration.go new file mode 100644 index 0000000..e22e009 --- /dev/null +++ b/internal/model/workspace_collaboration.go @@ -0,0 +1,33 @@ +package model + +import "time" + +type Role string + +const ( + RoleOwner Role = "owner" + RoleMember Role = "member" +) + +type Space struct { + ID string `db:"id"` + Name string `db:"name"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` +} + +type SpaceMember struct { + SpaceID string `db:"space_id"` + UserID string `db:"user_id"` + Role Role `db:"role"` + JoinedAt time.Time `db:"joined_at"` +} + +type SpaceInvitation struct { + Token string `db:"token"` + SpaceID string `db:"space_id"` + InviterID string `db:"inviter_id"` + InviteeEmail string `db:"invitee_email"` + ExpiresAt time.Time `db:"expires_at"` + CreatedAt time.Time `db:"created_at"` +}