use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, PgPool}; use uuid::Uuid; #[derive(Debug, Serialize, Deserialize, FromRow)] pub struct Requirement { pub id: Uuid, pub customer_id: Uuid, pub profession_key: String, pub title: String, pub description: String, pub location: String, pub budget: Option, pub preferred_date: Option, pub extra_data_json: Option, pub status: String, // DRAFT, PENDING_APPROVAL, OPEN, CLOSED, EXPIRED, REJECTED pub rejection_reason: Option, pub request_count: i32, pub accepted_count: i32, pub expires_at: Option>, pub approved_at: Option>, pub approved_by: Option, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Serialize, Deserialize)] pub struct CreateRequirementPayload { pub customer_id: Uuid, pub profession_key: String, pub title: String, pub description: String, pub location: String, pub budget: Option, pub preferred_date: Option, pub extra_data_json: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct UpdateRequirementPayload { pub title: Option, pub description: Option, pub location: Option, pub budget: Option, pub preferred_date: Option, pub extra_data_json: Option, } pub struct RequirementRepository; impl RequirementRepository { pub async fn create( pool: &PgPool, payload: CreateRequirementPayload, ) -> Result { let req = sqlx::query_as::<_, Requirement>( r#" INSERT INTO requirements ( customer_id, profession_key, title, description, location, budget, preferred_date, extra_data_json ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING * "#, ) .bind(payload.customer_id) .bind(payload.profession_key) .bind(payload.title) .bind(payload.description) .bind(payload.location) .bind(payload.budget) .bind(payload.preferred_date) .bind(payload.extra_data_json) .fetch_one(pool) .await?; Ok(req) } pub async fn get_by_id(pool: &PgPool, id: Uuid) -> Result, sqlx::Error> { sqlx::query_as::<_, Requirement>("SELECT * FROM requirements WHERE id = $1") .bind(id) .fetch_optional(pool) .await } pub async fn list_by_customer_id( pool: &PgPool, customer_id: Uuid, page: i64, limit: i64, ) -> Result, sqlx::Error> { let offset = (page - 1) * limit; let reqs = sqlx::query_as::<_, Requirement>( r#" SELECT * FROM requirements WHERE customer_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3 "#, ) .bind(customer_id) .bind(limit) .bind(offset) .fetch_all(pool) .await?; Ok(reqs) } pub async fn update( pool: &PgPool, id: Uuid, payload: UpdateRequirementPayload, ) -> Result { let req = sqlx::query_as::<_, Requirement>( r#" UPDATE requirements SET title = COALESCE($1, title), description = COALESCE($2, description), location = COALESCE($3, location), budget = COALESCE($4, budget), preferred_date = COALESCE($5, preferred_date), extra_data_json = COALESCE($6, extra_data_json), updated_at = NOW() WHERE id = $7 RETURNING * "#, ) .bind(payload.title) .bind(payload.description) .bind(payload.location) .bind(payload.budget) .bind(payload.preferred_date) .bind(payload.extra_data_json) .bind(id) .fetch_one(pool) .await?; Ok(req) } pub async fn update_status( pool: &PgPool, id: Uuid, status: &str, ) -> Result { let req = sqlx::query_as::<_, Requirement>( "UPDATE requirements SET status = $1, updated_at = NOW() WHERE id = $2 RETURNING *", ) .bind(status) .bind(id) .fetch_one(pool) .await?; Ok(req) } pub async fn increment_request_count(pool: &PgPool, id: Uuid) -> Result<(), sqlx::Error> { sqlx::query("UPDATE requirements SET request_count = request_count + 1 WHERE id = $1") .bind(id) .execute(pool) .await?; Ok(()) } pub async fn increment_accepted_count(pool: &PgPool, id: Uuid) -> Result<(), sqlx::Error> { sqlx::query( "UPDATE requirements SET accepted_count = accepted_count + 1 WHERE id = $1", ) .bind(id) .execute(pool) .await?; Ok(()) } pub async fn increment_accepted_count_and_get( pool: &PgPool, id: Uuid, ) -> Result { sqlx::query_as::<_, Requirement>( r#" UPDATE requirements SET accepted_count = accepted_count + 1, updated_at = NOW() WHERE id = $1 RETURNING * "#, ) .bind(id) .fetch_one(pool) .await } pub async fn approve( pool: &PgPool, id: Uuid, admin_user_id: Uuid, ) -> Result { sqlx::query_as::<_, Requirement>( r#" UPDATE requirements SET status = 'OPEN', approved_at = NOW(), approved_by = $1, rejection_reason = NULL, updated_at = NOW() WHERE id = $2 RETURNING * "#, ) .bind(admin_user_id) .bind(id) .fetch_one(pool) .await } pub async fn reject( pool: &PgPool, id: Uuid, reason: Option, ) -> Result { sqlx::query_as::<_, Requirement>( r#" UPDATE requirements SET status = 'REJECTED', rejection_reason = $1, approved_at = NULL, approved_by = NULL, updated_at = NOW() WHERE id = $2 RETURNING * "#, ) .bind(reason) .bind(id) .fetch_one(pool) .await } }