use anyhow::Result;
use rust_decimal::Decimal;
use sqlx::{PgPool, Row};
use uuid::Uuid;
use backbone_orm::org_scope::fetch_all_rows_scoped;
use crate::domain::entity::PosPayment;
pub const TABLE_NAME: &str = "pos.pos_payments";
pub struct PosPaymentRepository(
backbone_orm::GenericCrudRepository<PosPayment, backbone_orm::SoftDelete>,
);
impl std::ops::Deref for PosPaymentRepository {
type Target = backbone_orm::GenericCrudRepository<PosPayment, backbone_orm::SoftDelete>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl PosPaymentRepository {
pub fn new(pool: PgPool) -> Self {
Self(backbone_orm::GenericCrudRepository::new(pool, TABLE_NAME))
}
}
pub struct NewTenderRow<'a> {
pub id: Uuid,
pub pos_invoice_id: Uuid,
pub client_uuid: Option<Uuid>,
pub payment_method: &'a str,
pub amount: Decimal,
pub reference_no: Option<&'a str>,
}
pub struct MethodTotalRow {
pub method: String,
pub total: Decimal,
}
pub struct ReceiptTenderRow {
pub method: String,
pub amount: Decimal,
}
impl PosPaymentRepository {
pub async fn insert_tender(
&self,
conn: &mut sqlx::PgConnection,
t: &NewTenderRow<'_>,
) -> Result<(), sqlx::Error> {
sqlx::query(
r#"INSERT INTO pos.pos_payments (id, pos_invoice_id, client_uuid, payment_method, amount, reference_no)
VALUES ($1,$2,$3,$4::pos_payment_method,$5,$6)"#,
)
.bind(t.id).bind(t.pos_invoice_id).bind(t.client_uuid).bind(t.payment_method).bind(t.amount).bind(t.reference_no)
.execute(conn)
.await?;
Ok(())
}
pub async fn soft_delete_tenders_for_ticket(
&self,
conn: &mut sqlx::PgConnection,
pos_invoice_id: Uuid,
now: chrono::DateTime<chrono::Utc>,
) -> Result<(), sqlx::Error> {
sqlx::query(
r#"UPDATE pos.pos_payments SET metadata = jsonb_set(metadata, '{deleted_at}', to_jsonb($2::timestamptz))
WHERE pos_invoice_id=$1 AND (metadata->>'deleted_at') IS NULL"#,
)
.bind(pos_invoice_id).bind(now)
.execute(conn)
.await?;
Ok(())
}
pub async fn valid_methods(
&self,
conn: &mut sqlx::PgConnection,
) -> Result<Vec<String>, sqlx::Error> {
let rows = sqlx::query("SELECT unnest(enum_range(NULL::pos_payment_method))::text AS m")
.fetch_all(conn)
.await?;
Ok(rows.iter().map(|r| r.get::<String, _>("m")).collect())
}
pub async fn sum_paid_total_on(
&self,
conn: &mut sqlx::PgConnection,
pos_invoice_id: Uuid,
) -> Result<Decimal, sqlx::Error> {
sqlx::query_scalar(
"SELECT COALESCE(SUM(amount),0) FROM pos.pos_payments WHERE pos_invoice_id=$1 AND (metadata->>'deleted_at') IS NULL",
)
.bind(pos_invoice_id)
.fetch_one(conn)
.await
}
pub async fn sum_by_method_for_session(
&self,
pool: &PgPool,
opening_entry_id: Uuid,
) -> Result<Vec<MethodTotalRow>, sqlx::Error> {
let rows = fetch_all_rows_scoped(
pool,
sqlx::query(
r#"SELECT pay.payment_method::text AS method, COALESCE(SUM(pay.amount),0) AS total
FROM pos.pos_payments pay JOIN pos.pos_invoices i ON i.id = pay.pos_invoice_id
WHERE i.opening_entry_id=$1 AND i.status='paid'::pos_invoice_status AND (pay.metadata->>'deleted_at') IS NULL
GROUP BY pay.payment_method"#,
)
.bind(opening_entry_id),
)
.await?;
Ok(rows.iter().map(|r| MethodTotalRow {
method: r.get("method"), total: r.get("total"),
}).collect())
}
pub async fn fetch_receipt_tenders(
&self,
pool: &PgPool,
pos_invoice_id: Uuid,
) -> Result<Vec<ReceiptTenderRow>, sqlx::Error> {
let rows = fetch_all_rows_scoped(
pool,
sqlx::query(
"SELECT payment_method::text AS method, amount FROM pos.pos_payments
WHERE pos_invoice_id=$1 AND (metadata->>'deleted_at') IS NULL ORDER BY (metadata->>'created_at')",
)
.bind(pos_invoice_id),
)
.await?;
Ok(rows.iter().map(|r| ReceiptTenderRow {
method: r.get("method"), amount: r.get("amount"),
}).collect())
}
}
backbone_core::impl_crud_repository!(PosPaymentRepository, PosPayment, soft_delete);