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::DeliveryNote;
pub const TABLE_NAME: &str = "inventory.delivery_notes";
pub struct DeliveryNoteRepository(
backbone_orm::GenericCrudRepository<DeliveryNote, backbone_orm::SoftDelete>,
);
impl std::ops::Deref for DeliveryNoteRepository {
type Target = backbone_orm::GenericCrudRepository<DeliveryNote, backbone_orm::SoftDelete>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DeliveryNoteRepository {
pub fn new(pool: PgPool) -> Self {
Self(backbone_orm::GenericCrudRepository::new(pool, TABLE_NAME))
}
}
pub struct NewDeliveryRow<'a> {
pub id: Uuid,
pub delivery_number: &'a str,
pub branch_id: Option<Uuid>,
pub customer_id: Uuid,
pub source_so_id: Option<Uuid>,
pub warehouse_id: Uuid,
pub posting_date: chrono::NaiveDate,
pub currency: &'a str,
pub cogs_account_id: Uuid,
pub inventory_account_id: Uuid,
}
pub struct DeliverySubmitHeaderRow {
pub delivery_number: String,
pub branch_id: Option<Uuid>,
pub warehouse_id: Uuid,
pub posting_date: chrono::NaiveDate,
pub source_so_id: Option<Uuid>,
pub currency: String,
pub status: String,
pub cogs_account_id: Uuid,
pub inventory_account_id: Uuid,
}
pub struct DeliveryRepostHeaderRow {
pub branch_id: Option<Uuid>,
pub delivery_number: String,
pub posting_date: chrono::NaiveDate,
pub currency: String,
pub total_cogs: Decimal,
pub cogs_account_id: Uuid,
pub inventory_account_id: Uuid,
pub warehouse_id: Uuid,
pub gl: GlSettlementState,
}
pub struct DeliveryCancelHeaderRow {
pub branch_id: Option<Uuid>,
pub warehouse_id: Uuid,
pub posting_date: chrono::NaiveDate,
pub currency: String,
pub delivery_number: String,
pub total_cogs: Decimal,
pub status: String,
pub cogs_account_id: Uuid,
pub inventory_account_id: Uuid,
pub gl: GlSettlementState,
pub reversal_journal_id: Option<Uuid>,
pub reversal_accounting_post_id: Option<Uuid>,
}
impl DeliveryNoteRepository {
pub async fn insert_draft(
&self,
conn: &mut sqlx::PgConnection,
d: &NewDeliveryRow<'_>,
) -> Result<(), sqlx::Error> {
sqlx::query(
r#"INSERT INTO inventory.delivery_notes
(id, delivery_number, branch_id, customer_id, source_so_id, warehouse_id,
posting_date, currency, total_cogs, cogs_account_id, inventory_account_id, status, posting_state)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,0,$9,$10,'draft'::doc_status,'pending'::gl_posting_state)"#,
)
.bind(d.id).bind(d.delivery_number).bind(d.branch_id).bind(d.customer_id)
.bind(d.source_so_id).bind(d.warehouse_id).bind(d.posting_date).bind(d.currency)
.bind(d.cogs_account_id).bind(d.inventory_account_id)
.execute(conn)
.await?;
Ok(())
}
pub async fn fetch_submit_header(
&self,
pool: &PgPool,
id: Uuid,
) -> Result<Option<DeliverySubmitHeaderRow>, sqlx::Error> {
let row = org_scope::fetch_optional_row_scoped(
pool,
sqlx::query(
r#"SELECT delivery_number, branch_id, warehouse_id, posting_date, source_so_id,
currency, status::text AS st, cogs_account_id, inventory_account_id
FROM inventory.delivery_notes WHERE id=$1 AND (metadata->>'deleted_at') IS NULL"#,
)
.bind(id),
)
.await?;
Ok(row.map(|h| DeliverySubmitHeaderRow {
delivery_number: h.get("delivery_number"),
branch_id: h.get("branch_id"), warehouse_id: h.get("warehouse_id"),
posting_date: h.get("posting_date"), source_so_id: h.get("source_so_id"),
currency: h.get("currency"),
status: h.get("st"), cogs_account_id: h.get("cogs_account_id"),
inventory_account_id: h.get("inventory_account_id"),
}))
}
pub async fn fetch_repost_header(
&self,
pool: &PgPool,
id: Uuid,
) -> Result<Option<DeliveryRepostHeaderRow>, sqlx::Error> {
let row = org_scope::fetch_optional_row_scoped(
pool,
sqlx::query(
r#"SELECT branch_id, delivery_number, posting_date, currency, total_cogs,
cogs_account_id, inventory_account_id, posting_state::text AS ps, journal_id, accounting_post_id,
warehouse_id
FROM inventory.delivery_notes WHERE id=$1 AND (metadata->>'deleted_at') IS NULL"#,
)
.bind(id),
)
.await?;
Ok(row.map(|h| DeliveryRepostHeaderRow {
branch_id: h.get("branch_id"),
delivery_number: h.get("delivery_number"), posting_date: h.get("posting_date"),
currency: h.get("currency"),
total_cogs: h.get("total_cogs"), cogs_account_id: h.get("cogs_account_id"),
inventory_account_id: h.get("inventory_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_with_cogs(
&self,
conn: &mut sqlx::PgConnection,
id: Uuid,
total_cogs: Decimal,
) -> Result<(), sqlx::Error> {
sqlx::query("UPDATE inventory.delivery_notes SET status='submitted'::doc_status, total_cogs=$2 WHERE id=$1")
.bind(id).bind(total_cogs)
.execute(conn)
.await?;
Ok(())
}
pub async fn mark_cancelled(
&self,
conn: &mut sqlx::PgConnection,
id: Uuid,
) -> Result<(), sqlx::Error> {
sqlx::query("UPDATE inventory.delivery_notes 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<DeliveryCancelHeaderRow>, sqlx::Error> {
let row = org_scope::fetch_optional_row_scoped(
pool,
sqlx::query(
r#"SELECT branch_id, warehouse_id, posting_date, currency, delivery_number,
total_cogs, status::text AS st, cogs_account_id, inventory_account_id,
posting_state::text AS ps, journal_id, accounting_post_id,
reversal_journal_id, reversal_accounting_post_id
FROM inventory.delivery_notes WHERE id=$1 AND (metadata->>'deleted_at') IS NULL"#,
)
.bind(id),
)
.await?;
Ok(row.map(|h| DeliveryCancelHeaderRow {
branch_id: h.get("branch_id"),
warehouse_id: h.get("warehouse_id"), posting_date: h.get("posting_date"),
currency: h.get("currency"), delivery_number: h.get("delivery_number"),
total_cogs: h.get("total_cogs"),
status: h.get("st"), cogs_account_id: h.get("cogs_account_id"),
inventory_account_id: h.get("inventory_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!(DeliveryNoteRepository, DeliveryNote, soft_delete);