1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
use chrono::NaiveDate;
use serde_json::Value as JsonValue;

#[derive(Debug, PartialEq, Clone, sqlx::FromRow)]
pub struct Invoice {
    pub invoice_id: i64,
    pub order_id: i64,
    pub invoice_date: NaiveDate,
    pub payment_method_name: String,
    pub payment_transaction_id: String,
    pub payment_instructions: Option<String>,
    pub shipment: Option<JsonValue>,
}

#[derive(Debug)]
pub struct NewInvoice<'a> {
    pub order_id: i64,
    pub invoice_date: NaiveDate,
    pub payment_method_name: &'a str,
    pub payment_transaction_id: &'a str,
    pub payment_instructions: Option<&'a str>,
    pub shipment: Option<&'a JsonValue>,
}

pub async fn fetch<'e>(
    id: i64,
    conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Option<Invoice>, sqlx::Error> {
    sqlx::query_as("SELECT * FROM invoices WHERE invoice_id = $1")
        .bind(id)
        .fetch_optional(conn)
        .await
}

pub async fn fetch_for_update<'e>(
    id: i64,
    conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Option<Invoice>, sqlx::Error> {
    sqlx::query_as("SELECT * FROM invoices WHERE invoice_id = $1 FOR UPDATE")
        .bind(id)
        .fetch_optional(conn)
        .await
}

pub async fn by_order_id<'e>(
    order_id: i64,
    conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Option<Invoice>, sqlx::Error> {
    sqlx::query_as("SELECT * FROM invoices WHERE order_id = $1")
        .bind(order_id)
        .fetch_optional(conn)
        .await
}

pub async fn insert<'e>(
    invoice: &NewInvoice<'_>,
    conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Invoice, sqlx::Error> {
    // deconstruct to not miss new properties in the future
    let NewInvoice {
        order_id,
        invoice_date,
        payment_method_name,
        payment_transaction_id,
        payment_instructions,
        shipment,
    } = invoice;
    sqlx::query_as(
        "
            INSERT INTO invoices (
                order_id,
                invoice_date,
                payment_method_name,
                payment_transaction_id,
                payment_instructions,
                shipment
            ) VALUES (
                $1, $2, $3, $4, $5, $6
            ) RETURNING *
        ",
    )
    .bind(order_id)
    .bind(invoice_date)
    .bind(payment_method_name)
    .bind(payment_transaction_id)
    .bind(payment_instructions)
    .bind(shipment)
    .fetch_one(conn)
    .await
}

impl Invoice {
    pub async fn update_shipment(
        &mut self,
        new_shipment: JsonValue,
        conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
    ) -> Result<(), sqlx::Error> {
        sqlx::query("UPDATE invoices SET shipment = $2 WHERE invoice_id = $1")
            .bind(self.invoice_id)
            .bind(&new_shipment)
            .execute(conn)
            .await?;
        self.shipment = Some(new_shipment);
        Ok(())
    }
}