# Real Estate Addon - Data Storage
## Overview
The Real Estate addon stores all its data in the **main Wealthfolio database** using the addon storage API. There are **no separate tables** for real estate data - everything is stored as JSON in the `app_settings` table.
## Storage Architecture
### Database Table: `app_settings`
```sql
CREATE TABLE app_settings (
setting_key TEXT PRIMARY KEY,
setting_value TEXT NOT NULL
);
```
### Storage Key Format
Real estate data is stored with a scoped key:
```
addon:real-estate:real-estate-data
```
The key format is: `addon:{addon_id}:{storage_key}`
### Data Structure
All real estate data is stored as a **single JSON object** with the following structure:
```typescript
interface RealEstateData {
properties: Property[];
loans: Loan[];
valuations: PropertyValuation[];
loanEvents: LoanEvent[];
version: string;
}
```
## Data Models
### Property
Represents a real estate property:
```typescript
interface Property {
id: string; // Unique identifier
name: string; // Property name/description
type: PropertyType; // residential, commercial, land, rental, vacation, mixed-use
address: string; // Street address
city?: string;
state?: string;
country: string;
postalCode?: string;
purchaseDate: string; // ISO date format
purchasePrice: number; // Original purchase price
currentValue: number; // Current market value
currency: string; // Currency code (USD, EUR, etc.)
notes?: string;
createdAt: string; // ISO timestamp
updatedAt: string; // ISO timestamp
}
```
### Loan
Represents a mortgage or loan associated with a property:
```typescript
interface Loan {
id: string;
propertyId: string; // Reference to parent property
name: string; // Loan name/description
type: LoanType; // fixed, variable, interest-only, adjustable, home-equity
lender: string; // Bank or lender name
originalAmount: number; // Original loan amount
currentBalance: number; // Current outstanding balance
interestRate: number; // Annual percentage rate
startDate: string; // ISO date - loan origination
endDate?: string; // ISO date - loan maturity
monthlyPayment?: number; // Monthly payment amount
currency: string;
notes?: string;
createdAt: string;
updatedAt: string;
}
```
### PropertyValuation
Tracks historical property value changes:
```typescript
interface PropertyValuation {
id: string;
propertyId: string;
value: number; // Property value at this date
date: string; // ISO date
source?: string; // "appraisal", "market", "manual"
notes?: string;
createdAt: string;
}
```
### LoanEvent
Records modifications to loan terms:
```typescript
interface LoanEvent {
id: string;
loanId: string;
type: LoanEventType; // postponement, early_reimbursement_duration,
// early_reimbursement_payment, rate_change, refinance
date: string; // ISO date when event occurs
amount?: number; // For early reimbursements
previousMonthlyPayment?: number;
newMonthlyPayment?: number;
previousEndDate?: string;
newEndDate?: string;
previousInterestRate?: number;
newInterestRate?: number;
postponementMonths?: number; // For payment postponements
notes?: string;
createdAt: string;
}
```
## Storage API
### Loading Data
```typescript
import { loadData } from './lib/storage';
const data: RealEstateData = await loadData(ctx);
// Returns all properties, loans, valuations, and loan events
```
### Saving Data
```typescript
import { saveData } from './lib/storage';
await saveData(ctx, data);
// Saves entire data structure as JSON
```
### Helper Functions
The storage module provides convenient functions:
```typescript
// Properties
await saveProperty(ctx, property);
await deleteProperty(ctx, propertyId); // Also deletes associated loans & valuations
// Loans
await saveLoan(ctx, loan);
await deleteLoan(ctx, loanId);
await getPropertyLoans(ctx, propertyId);
// Valuations
await addValuation(ctx, valuation);
await getPropertyValuations(ctx, propertyId);
// Loan Events
await addLoanEvent(ctx, loanEvent);
await updateLoanEvent(ctx, loanEvent);
await deleteLoanEvent(ctx, eventId);
await getLoanEvents(ctx, loanId);
```
## How It Works
### Backend (Rust)
1. **Tauri Commands** (src-tauri/src/commands/addon.rs):
```rust
#[tauri::command]
pub async fn get_addon_data(
addon_id: String,
key: String,
state: State<'_, Arc<ServiceContext>>,
) -> Result<String, String>
#[tauri::command]
pub async fn set_addon_data(
addon_id: String,
key: String,
value: String,
state: State<'_, Arc<ServiceContext>>,
) -> Result<(), String>
```
2. **Storage Implementation**:
- Uses `SettingsService` to store data
- Key format: `addon:{addon_id}:{key}`
- For real estate: `addon:real-estate:real-estate-data`
- Stored in `app_settings` table
### Frontend (TypeScript)
1. **Addon SDK** (packages/addon-sdk/src/host-api.ts):
```typescript
interface StorageAPI {
getData(key: string): Promise<string>;
setData(key: string, value: string): Promise<void>;
}
```
2. **Real Estate Storage** (addons/real-estate/src/lib/storage.ts):
```typescript
const STORAGE_KEY = "real-estate-data";
// Load from database
const data = await ctx.api.storage.getData(STORAGE_KEY);
const parsed = JSON.parse(data) as RealEstateData;
// Save to database
const serialized = JSON.stringify(data);
await ctx.api.storage.setData(STORAGE_KEY, serialized);
```
## Example: Database Query
To view real estate data directly in the database:
```sql
-- Get the real estate data JSON
SELECT setting_value
FROM app_settings
WHERE setting_key = 'addon:real-estate:real-estate-data';
-- Pretty print with SQLite JSON functions (if available)
SELECT json_pretty(setting_value)
FROM app_settings
WHERE setting_key = 'addon:real-estate:real-estate-data';
```
## Example: Raw Data Structure
```json
{
"version": "1.0.0",
"properties": [
{
"id": "1734014093629-kq1yx6j",
"name": "Appartement Paris",
"type": "Résidentiel",
"address": "110 Avenue Félix Faure, Paris",
"city": "Paris",
"country": "France",
"purchaseDate": "2020-01-15",
"purchasePrice": 750000,
"currentValue": 803000,
"currency": "EUR",
"createdAt": "2024-12-12T12:34:53.629Z",
"updatedAt": "2024-12-12T14:22:15.442Z"
},
{
"id": "1734014162345-abc123x",
"name": "1/6 rue propriété Bordeaux",
"type": "Résidentiel",
"address": "31 rue Sainte-Colombe, Bordeaux",
"city": "Bordeaux",
"country": "France",
"purchaseDate": "2018-06-20",
"purchasePrice": 100000,
"currentValue": 83333,
"currency": "EUR",
"createdAt": "2024-12-12T12:36:02.345Z",
"updatedAt": "2024-12-12T14:20:30.123Z"
}
],
"loans": [
{
"id": "1734014200000-loan001",
"propertyId": "1734014093629-kq1yx6j",
"name": "Prêt Immobilier Paris",
"type": "fixed",
"lender": "Crédit Agricole",
"originalAmount": 600000,
"currentBalance": 541708,
"interestRate": 1.5,
"startDate": "2020-01-15",
"endDate": "2040-01-15",
"monthlyPayment": 2789,
"currency": "EUR",
"createdAt": "2024-12-12T12:36:40.000Z",
"updatedAt": "2024-12-12T12:36:40.000Z"
}
],
"valuations": [
{
"id": "val-paris-2024",
"propertyId": "1734014093629-kq1yx6j",
"value": 803000,
"date": "2024-12-01",
"source": "market",
"createdAt": "2024-12-12T14:22:15.442Z"
},
{
"id": "val-bordeaux-2024",
"propertyId": "1734014162345-abc123x",
"value": 83333,
"date": "2024-12-01",
"source": "market",
"createdAt": "2024-12-12T14:20:30.123Z"
}
],
"loanEvents": []
}
```
## Backup & Migration
### Automatic Backup
✅ **Real estate data is automatically included in Wealthfolio backups** because it's stored in the main database (`app_settings` table).
### Manual Export
To manually export real estate data:
```sql
-- Export to JSON file
.mode json
.output real_estate_backup.json
SELECT setting_value
FROM app_settings
WHERE setting_key = 'addon:real-estate:real-estate-data';
.output stdout
```
### Manual Import
To manually import real estate data:
```sql
-- Import from JSON
UPDATE app_settings
SET setting_value = readfile('real_estate_backup.json')
WHERE setting_key = 'addon:real-estate:real-estate-data';
-- Or insert if doesn't exist
INSERT OR REPLACE INTO app_settings (setting_key, setting_value)
VALUES ('addon:real-estate:real-estate-data', readfile('real_estate_backup.json'));
```
## Data Migration
If the data structure changes in future versions, the addon includes version tracking:
```typescript
const CURRENT_VERSION = "1.0.0";
interface RealEstateData {
version: string; // Used for migration logic
// ... rest of data
}
```
Migration logic can be added in `storage.ts` to handle version upgrades.
## Security Considerations
1. **Scoped Storage**: Each addon can only access its own data via the scoped key format
2. **No Direct SQL**: Addons cannot execute arbitrary SQL queries
3. **Validation**: The addon validates all data before storing/loading
4. **Backup Integration**: Data is included in database backups
5. **No Sensitive Data**: API keys/secrets should use the separate Secrets API, not storage
## Performance
- **Single JSON Document**: All data stored as one JSON object
- **Load Once**: Data loaded once at startup, cached in memory
- **Save on Change**: Only writes to database when data changes
- **No Joins**: No complex queries needed - just key-value lookup
- **Small Footprint**: Typical real estate portfolio < 100KB
## Limitations
1. **No Relational Queries**: Cannot join with other Wealthfolio data directly
2. **Single Storage Key**: All data in one JSON object (could be split if needed)
3. **JSON Parsing**: Must parse entire object to access any data
4. **No Indexing**: Cannot create database indexes on nested JSON fields
## Future Enhancements
Potential improvements if real estate data grows significantly:
1. **Separate Tables**: Migrate to dedicated `properties`, `loans`, `valuations` tables
2. **Integration**: Link properties to Wealthfolio accounts as assets
3. **Cash Flow Tracking**: Integrate rental income with Wealthfolio activities
4. **Portfolio Metrics**: Include real estate equity in overall portfolio calculations