use anyhow::Result;
use rust_decimal::Decimal;
use sqlx::{PgPool, Row};
use uuid::Uuid;
use backbone_orm::org_scope;
use super::gl_voucher_repository::GlSettlementState;
use crate::domain::entity::PurchaseReceipt;
pub const TABLE_NAME: &str = "inventory.purchase_receipts";
pub struct PurchaseReceiptRepository(
backbone_orm::GenericCrudRepository<PurchaseReceipt, backbone_orm::SoftDelete>,
);
impl std::ops::Deref for PurchaseReceiptRepository {
type Target = backbone_orm::GenericCrudRepository<PurchaseReceipt, backbone_orm::SoftDelete>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl PurchaseReceiptRepository {
pub fn new(pool: PgPool) -> Self {
Self(backbone_orm::GenericCrudRepository::new(pool, TABLE_NAME))
}
}
pub struct NewReceiptRow<'a> {
pub id: Uuid,
pub receipt_number: &'a str,
pub branch_id: Option<Uuid>,
pub supplier_id: Uuid,
pub source_po_id: Option<Uuid>,
pub warehouse_id: Uuid,
pub posting_date: chrono::NaiveDate,
pub currency: &'a str,
pub total_value: Decimal,
pub inventory_account_id: Uuid,
pub grir_account_id: Uuid,
}
pub struct ReceiptSubmitHeaderRow {
pub receipt_number: String,
pub branch_id: Option<Uuid>,
pub warehouse_id: Uuid,
pub posting_date: chrono::NaiveDate,
pub source_po_id: Option<Uuid>,
pub currency: String,
pub status: String,
pub inventory_account_id: Uuid,
pub grir_account_id: Uuid,
}
pub struct ReceiptRepostHeaderRow {
pub branch_id: Option<Uuid>,
pub receipt_number: String,
pub posting_date: chrono::NaiveDate,
pub currency: String,
pub total_value: Decimal,
pub inventory_account_id: Uuid,
pub grir_account_id: Uuid,
pub warehouse_id: Uuid,
pub gl: GlSettlementState,
}
pub struct ReceiptCancelHeaderRow {
pub branch_id: Option<Uuid>,
pub warehouse_id: Uuid,
pub posting_date: chrono::NaiveDate,
pub currency: String,
pub receipt_number: String,
pub total_value: Decimal,
pub status: String,
pub inventory_account_id: Uuid,
pub grir_account_id: Uuid,
pub gl: GlSettlementState,
pub reversal_journal_id: Option<Uuid>,
pub reversal_accounting_post_id: Option<Uuid>,
}
impl PurchaseReceiptRepository {
pub async fn insert_draft(
&self,
conn: &mut sqlx::PgConnection,
r: &NewReceiptRow<'_>,
) -> Result<(), sqlx::Error> {
sqlx::query(
r#"INSERT INTO inventory.purchase_receipts
(id, receipt_number, branch_id, supplier_id, source_po_id, warehouse_id,
posting_date, currency, total_value, inventory_account_id, grir_account_id,
status, posting_state)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,'draft'::doc_status,'pending'::gl_posting_state)"#,
)
.bind(r.id).bind(r.receipt_number).bind(r.branch_id).bind(r.supplier_id)
.bind(r.source_po_id).bind(r.warehouse_id).bind(r.posting_date).bind(r.currency).bind(r.total_value)
.bind(r.inventory_account_id).bind(r.grir_account_id)
.execute(conn)
.await?;
Ok(())
}
pub async fn fetch_submit_header(
&self,
pool: &PgPool,
id: Uuid,
) -> Result<Option<ReceiptSubmitHeaderRow>, sqlx::Error> {
let row = org_scope::fetch_optional_row_scoped(
pool,
sqlx::query(
r#"SELECT receipt_number, branch_id, warehouse_id, posting_date, source_po_id,
currency, status::text AS st, inventory_account_id, grir_account_id
FROM inventory.purchase_receipts WHERE id=$1 AND (metadata->>'deleted_at') IS NULL"#,
)
.bind(id),
)
.await?;
Ok(row.map(|h| ReceiptSubmitHeaderRow {
receipt_number: h.get("receipt_number"),
branch_id: h.get("branch_id"), warehouse_id: h.get("warehouse_id"),
posting_date: h.get("posting_date"), source_po_id: h.get("source_po_id"),
currency: h.get("currency"),
status: h.get("st"), inventory_account_id: h.get("inventory_account_id"),
grir_account_id: h.get("grir_account_id"),
}))
}
pub async fn fetch_repost_header(
&self,
pool: &PgPool,
id: Uuid,
) -> Result<Option<ReceiptRepostHeaderRow>, sqlx::Error> {
let row = org_scope::fetch_optional_row_scoped(
pool,
sqlx::query(
r#"SELECT branch_id, receipt_number, posting_date, currency, total_value,
inventory_account_id, grir_account_id, posting_state::text AS ps, journal_id, accounting_post_id,
warehouse_id
FROM inventory.purchase_receipts WHERE id=$1 AND (metadata->>'deleted_at') IS NULL"#,
)
.bind(id),
)
.await?;
Ok(row.map(|h| ReceiptRepostHeaderRow {
branch_id: h.get("branch_id"),
receipt_number: h.get("receipt_number"), posting_date: h.get("posting_date"),
currency: h.get("currency"),
total_value: h.get("total_value"), inventory_account_id: h.get("inventory_account_id"),
grir_account_id: h.get("grir_account_id"),
warehouse_id: h.get("warehouse_id"),
gl: GlSettlementState {
posting_state: h.get("ps"), journal_id: h.get("journal_id"),
accounting_post_id: h.get("accounting_post_id"),
},
}))
}
pub async fn mark_submitted(
&self,
conn: &mut sqlx::PgConnection,
id: Uuid,
) -> Result<(), sqlx::Error> {
sqlx::query("UPDATE inventory.purchase_receipts SET status='submitted'::doc_status WHERE id=$1")
.bind(id)
.execute(conn)
.await?;
Ok(())
}
pub async fn mark_cancelled(
&self,
conn: &mut sqlx::PgConnection,
id: Uuid,
) -> Result<(), sqlx::Error> {
sqlx::query("UPDATE inventory.purchase_receipts SET status='cancelled'::doc_status WHERE id=$1 AND status='submitted'::doc_status")
.bind(id)
.execute(conn)
.await?;
Ok(())
}
pub async fn fetch_cancel_header(
&self,
pool: &PgPool,
id: Uuid,
) -> Result<Option<ReceiptCancelHeaderRow>, sqlx::Error> {
let row = org_scope::fetch_optional_row_scoped(
pool,
sqlx::query(
r#"SELECT branch_id, warehouse_id, posting_date, currency, receipt_number,
total_value, status::text AS st, inventory_account_id, grir_account_id,
posting_state::text AS ps, journal_id, accounting_post_id,
reversal_journal_id, reversal_accounting_post_id
FROM inventory.purchase_receipts WHERE id=$1 AND (metadata->>'deleted_at') IS NULL"#,
)
.bind(id),
)
.await?;
Ok(row.map(|h| ReceiptCancelHeaderRow {
branch_id: h.get("branch_id"),
warehouse_id: h.get("warehouse_id"), posting_date: h.get("posting_date"),
currency: h.get("currency"), receipt_number: h.get("receipt_number"),
total_value: h.get("total_value"),
status: h.get("st"), inventory_account_id: h.get("inventory_account_id"),
grir_account_id: h.get("grir_account_id"),
reversal_journal_id: h.get("reversal_journal_id"),
reversal_accounting_post_id: h.get("reversal_accounting_post_id"),
gl: GlSettlementState {
posting_state: h.get("ps"), journal_id: h.get("journal_id"),
accounting_post_id: h.get("accounting_post_id"),
},
}))
}
}
backbone_core::impl_crud_repository!(PurchaseReceiptRepository, PurchaseReceipt, soft_delete);