fix: portfolio_items/services tables never existed; wallet ledger read used stale column names

Continuing the migration-chain audit: professional portfolio/services
management (PortfolioPage.tsx -> /api/{profession}/portfolio/me,
/api/{profession}/services) was broken the same way as lead_requests —
portfolio_items and services were only ever defined in a disabled .skip
migration (keyed differently: user_id + profession_key, vs. the
user_role_profile_id every live query in
crates/db/src/models/professional.rs actually uses). Self-created both
tables with the schema the live code needs, in the same already-pushed
migration (20260317195000_profession_specific_profiles.up.sql) that was
unconditionally ALTERing them.

Also fixed TracecoinLedgerEntry (professional.rs) — its `type`/`reason`
fields didn't match the `transaction_type`/`reference_type` columns
TracecoinWalletRepository actually writes (see previous commit), so
GET /api/{profession}/wallet/ledger would have failed to deserialize
every row. professional.rs has its own duplicate, unreachable
try_reserve/debit/release_tracecoins using the old type/reason names —
left alone since nothing calls them (send_lead_request and friends all
go through TracecoinWalletRepository).
This commit is contained in:
Ashwin Kumar Sivakumar 2026-07-21 02:21:52 +05:30
parent 94a8a1096a
commit 442dac8c04
2 changed files with 38 additions and 2 deletions

View file

@ -190,6 +190,42 @@ CREATE TABLE IF NOT EXISTS catering_service_profiles (
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Shared: portfolio_items/services — neither was ever created by any active
-- migration (only in the disabled portfolio_payments .skip migration, keyed
-- differently to boot). ProfessionalRepository (crates/db/src/models/
-- professional.rs) exclusively queries these by user_role_profile_id, so
-- that's the schema self-created here; the legacy user_id/profession_key
-- columns below are added for backwards compatibility with this comment's
-- original (unused) design but aren't required by any current code path.
CREATE TABLE IF NOT EXISTS portfolio_items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_role_profile_id UUID NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT,
tags TEXT[] DEFAULT '{}',
display_order INTEGER DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS services (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_role_profile_id UUID NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
price INTEGER NOT NULL DEFAULT 0,
duration_minutes INTEGER,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
ALTER TABLE portfolio_items ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
ALTER TABLE services ADD COLUMN IF NOT EXISTS user_role_profile_id UUID;
CREATE INDEX IF NOT EXISTS idx_portfolio_items_user_role_profile_id ON portfolio_items(user_role_profile_id);
CREATE INDEX IF NOT EXISTS idx_services_user_role_profile_id ON services(user_role_profile_id);
-- Shared: portfolio_items now uses user_id + profession_key (no foreign key to professionals)
-- Drop the professionals-table FK if it was added before
ALTER TABLE portfolio_items

View file

@ -53,9 +53,9 @@ pub struct Wallet {
pub struct TracecoinLedgerEntry {
pub id: Uuid,
pub wallet_id: Uuid,
pub r#type: String,
pub transaction_type: String,
pub amount: i32,
pub reason: String,
pub reference_type: Option<String>,
pub reference_id: Option<Uuid>,
pub created_at: DateTime<Utc>,
}