Implements a feature that allows users to pay their outstanding balance via Lightning. The changes include: - Adds the UI elements for invoice generation and display, including QR code. - Integrates backend endpoints to generate and record payments. - Adds polling mechanism to track payments and update balance. - Creates new database models to support manual payment requests.
155 lines
4.4 KiB
Python
155 lines
4.4 KiB
Python
from datetime import datetime
|
|
from enum import Enum
|
|
from typing import Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class AccountType(str, Enum):
|
|
ASSET = "asset"
|
|
LIABILITY = "liability"
|
|
EQUITY = "equity"
|
|
REVENUE = "revenue"
|
|
EXPENSE = "expense"
|
|
|
|
|
|
class Account(BaseModel):
|
|
id: str
|
|
name: str
|
|
account_type: AccountType
|
|
description: Optional[str] = None
|
|
user_id: Optional[str] = None # For user-specific accounts
|
|
created_at: datetime
|
|
|
|
|
|
class CreateAccount(BaseModel):
|
|
name: str
|
|
account_type: AccountType
|
|
description: Optional[str] = None
|
|
user_id: Optional[str] = None
|
|
|
|
|
|
class EntryLine(BaseModel):
|
|
id: str
|
|
journal_entry_id: str
|
|
account_id: str
|
|
debit: int = 0 # in satoshis
|
|
credit: int = 0 # in satoshis
|
|
description: Optional[str] = None
|
|
metadata: dict = {} # Stores currency info: fiat_currency, fiat_amount, fiat_rate, etc.
|
|
|
|
|
|
class CreateEntryLine(BaseModel):
|
|
account_id: str
|
|
debit: int = 0
|
|
credit: int = 0
|
|
description: Optional[str] = None
|
|
metadata: dict = {} # Stores currency info
|
|
|
|
|
|
class JournalEntry(BaseModel):
|
|
id: str
|
|
description: str
|
|
entry_date: datetime
|
|
created_by: str # wallet ID of user who created it
|
|
created_at: datetime
|
|
reference: Optional[str] = None # Invoice ID or reference number
|
|
lines: list[EntryLine] = []
|
|
|
|
|
|
class CreateJournalEntry(BaseModel):
|
|
description: str
|
|
entry_date: Optional[datetime] = None
|
|
reference: Optional[str] = None
|
|
lines: list[CreateEntryLine]
|
|
|
|
|
|
class UserBalance(BaseModel):
|
|
user_id: str
|
|
balance: int # positive = castle owes user, negative = user owes castle
|
|
accounts: list[Account] = []
|
|
|
|
|
|
class ExpenseEntry(BaseModel):
|
|
"""Helper model for creating expense entries"""
|
|
|
|
description: str
|
|
amount: float # Amount in the specified currency (or satoshis if currency is None)
|
|
expense_account: str # account name or ID
|
|
is_equity: bool = False # True = equity contribution, False = liability (castle owes user)
|
|
user_wallet: str
|
|
reference: Optional[str] = None
|
|
currency: Optional[str] = None # If None, amount is in satoshis. Otherwise, fiat currency code (EUR, USD, etc.)
|
|
|
|
|
|
class ReceivableEntry(BaseModel):
|
|
"""Helper model for creating accounts receivable entries"""
|
|
|
|
description: str
|
|
amount: float # Amount in the specified currency (or satoshis if currency is None)
|
|
revenue_account: str # account name or ID
|
|
user_id: str # The user_id (not wallet_id) of the user who owes the castle
|
|
reference: Optional[str] = None
|
|
currency: Optional[str] = None # If None, amount is in satoshis. Otherwise, fiat currency code
|
|
|
|
|
|
class RevenueEntry(BaseModel):
|
|
"""Helper model for creating revenue entries"""
|
|
|
|
description: str
|
|
amount: float # Amount in the specified currency (or satoshis if currency is None)
|
|
revenue_account: str
|
|
payment_method_account: str # e.g., "Cash", "Bank", "Lightning"
|
|
reference: Optional[str] = None
|
|
currency: Optional[str] = None # If None, amount is in satoshis. Otherwise, fiat currency code
|
|
|
|
|
|
class CastleSettings(BaseModel):
|
|
"""Settings for the Castle extension"""
|
|
|
|
castle_wallet_id: Optional[str] = None # The wallet ID that represents the Castle
|
|
updated_at: datetime = Field(default_factory=lambda: datetime.now())
|
|
|
|
@classmethod
|
|
def is_admin_only(cls) -> bool:
|
|
return True
|
|
|
|
|
|
class UserCastleSettings(CastleSettings):
|
|
"""User-specific settings (stored with user_id)"""
|
|
|
|
id: str
|
|
|
|
|
|
class UserWalletSettings(BaseModel):
|
|
"""Per-user wallet settings"""
|
|
|
|
user_wallet_id: Optional[str] = None # The wallet ID for this specific user
|
|
updated_at: datetime = Field(default_factory=lambda: datetime.now())
|
|
|
|
|
|
class StoredUserWalletSettings(UserWalletSettings):
|
|
"""Stored user wallet settings with user ID"""
|
|
|
|
id: str # user_id
|
|
|
|
|
|
class ManualPaymentRequest(BaseModel):
|
|
"""Manual payment request from user to castle"""
|
|
|
|
id: str
|
|
user_id: str
|
|
amount: int # in satoshis
|
|
description: str
|
|
status: str = "pending" # pending, approved, rejected
|
|
created_at: datetime
|
|
reviewed_at: Optional[datetime] = None
|
|
reviewed_by: Optional[str] = None # user_id of castle admin who reviewed
|
|
journal_entry_id: Optional[str] = None # set when approved
|
|
|
|
|
|
class CreateManualPaymentRequest(BaseModel):
|
|
"""Create a manual payment request"""
|
|
|
|
amount: int
|
|
description: str
|