Skip to main content

nfe_web/db/
postgres.rs

1//! Módulo PostgreSQL
2
3use sqlx::{PgPool, postgres::PgPoolOptions};
4use super::models::*;
5
6/// Cliente PostgreSQL
7pub struct PostgresClient {
8    pool: PgPool,
9}
10
11impl PostgresClient {
12    /// Conecta ao PostgreSQL
13    pub async fn connect(database_url: &str) -> Result<Self, sqlx::Error> {
14        let pool = PgPoolOptions::new()
15            .max_connections(5)
16            .connect(database_url)
17            .await?;
18
19        Ok(Self { pool })
20    }
21
22    /// Cria a tabela se não existir
23    pub async fn create_table(&self) -> Result<(), sqlx::Error> {
24        sqlx::query(POSTGRES_CREATE_TABLE)
25            .execute(&self.pool)
26            .await?;
27        Ok(())
28    }
29
30    /// Insere uma NF-e
31    pub async fn insert(&self, record: &NfeRecord) -> Result<(), sqlx::Error> {
32        sqlx::query(POSTGRES_INSERT)
33            .bind(&record.id)
34            .bind(&record.chave_acesso)
35            .bind(record.numero)
36            .bind(record.serie)
37            .bind(record.data_emissao)
38            .bind(&record.emit_cnpj)
39            .bind(&record.emit_razao_social)
40            .bind(&record.dest_cnpj)
41            .bind(&record.dest_razao_social)
42            .bind(record.valor_total)
43            .bind(&record.xml)
44            .bind(&record.json_data)
45            .execute(&self.pool)
46            .await?;
47        Ok(())
48    }
49
50    /// Busca NF-e por chave de acesso
51    pub async fn find_by_chave(&self, chave: &str) -> Result<Option<NfeRecord>, sqlx::Error> {
52        let row = sqlx::query_as::<_, (String, String, i32, i16, chrono::DateTime<chrono::Utc>, String, String, Option<String>, Option<String>, f64, String, String, chrono::DateTime<chrono::Utc>)>(
53            "SELECT id, chave_acesso, numero, serie, data_emissao, emit_cnpj, emit_razao_social, dest_cnpj, dest_razao_social, valor_total::float8, xml, json_data::text, created_at FROM nfe WHERE chave_acesso = $1"
54        )
55            .bind(chave)
56            .fetch_optional(&self.pool)
57            .await?;
58
59        Ok(row.map(|r| NfeRecord {
60            id: r.0,
61            chave_acesso: r.1,
62            numero: r.2,
63            serie: r.3,
64            data_emissao: r.4,
65            emit_cnpj: r.5,
66            emit_razao_social: r.6,
67            dest_cnpj: r.7,
68            dest_razao_social: r.8,
69            valor_total: r.9,
70            xml: r.10,
71            json_data: r.11,
72            created_at: r.12,
73        }))
74    }
75
76    /// Lista NF-e com paginação
77    pub async fn list(&self, limit: i64, offset: i64) -> Result<Vec<NfeRecord>, sqlx::Error> {
78        let rows = sqlx::query_as::<_, (String, String, i32, i16, chrono::DateTime<chrono::Utc>, String, String, Option<String>, Option<String>, f64, String, String, chrono::DateTime<chrono::Utc>)>(
79            "SELECT id, chave_acesso, numero, serie, data_emissao, emit_cnpj, emit_razao_social, dest_cnpj, dest_razao_social, valor_total::float8, xml, json_data::text, created_at FROM nfe ORDER BY created_at DESC LIMIT $1 OFFSET $2"
80        )
81            .bind(limit)
82            .bind(offset)
83            .fetch_all(&self.pool)
84            .await?;
85
86        Ok(rows.into_iter().map(|r| NfeRecord {
87            id: r.0,
88            chave_acesso: r.1,
89            numero: r.2,
90            serie: r.3,
91            data_emissao: r.4,
92            emit_cnpj: r.5,
93            emit_razao_social: r.6,
94            dest_cnpj: r.7,
95            dest_razao_social: r.8,
96            valor_total: r.9,
97            xml: r.10,
98            json_data: r.11,
99            created_at: r.12,
100        }).collect())
101    }
102}