use crate::cents::Cents;
use chrono::{DateTime, Utc};
use serde_json::Value as JsonValue;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, sqlx::Type)]
#[sqlx(rename = "order_state")]
pub enum OrderState {
Created,
Pending,
Payed,
Failed,
Canceled,
}
#[derive(Debug, PartialEq, Clone, sqlx::FromRow)]
pub struct Order {
pub id: i64,
pub uuid: Uuid,
pub origin: String,
pub locale: String,
pub state: OrderState,
pub custom_status: String,
pub date: DateTime<Utc>,
pub payment_method: String,
pub payment_id: String,
pub payment_meta: JsonValue,
pub currency: String,
pub shipping_method: String,
pub shipping_method_name: String,
pub coupon: Option<String>,
pub shipping: Cents,
pub shipping_taxrate: i32,
pub tax: Cents,
pub discount: Cents,
pub total: Cents,
pub email: String,
pub token: String,
pub prioritize: bool,
pub note: Option<String>,
pub invoice_address_id: i64,
pub shipping_address_id: i64,
pub updated_at: DateTime<Utc>,
}
pub async fn fetch<'e>(
id: i64,
conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Option<Order>, sqlx::Error> {
sqlx::query_as("SELECT * FROM orders WHERE 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<Order>, sqlx::Error> {
sqlx::query_as("SELECT * FROM orders WHERE id = $1 FOR UPDATE")
.bind(id)
.fetch_optional(conn)
.await
}
pub async fn by_payment_for_update<'e>(
payment_method: &str,
payment_id: &str,
conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Option<Order>, sqlx::Error> {
sqlx::query_as("SELECT * FROM orders WHERE payment_method = $1 AND payment_id = $2 FOR UPDATE")
.bind(payment_method)
.bind(payment_id)
.fetch_optional(conn)
.await
}
pub async fn by_uuid<'e>(
uuid: uuid::Uuid,
conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Option<Order>, sqlx::Error> {
sqlx::query_as("SELECT * FROM orders WHERE uuid = $1")
.bind(uuid)
.fetch_optional(conn)
.await
}
#[derive(Debug)]
pub struct NewOrder<'a> {
pub uuid: Uuid,
pub origin: &'a str,
pub locale: &'a str,
pub state: OrderState,
pub custom_status: &'a str,
pub date: DateTime<Utc>,
pub payment_method: &'a str,
pub payment_id: &'a str,
pub payment_meta: &'a JsonValue,
pub currency: &'a str,
pub shipping_method: &'a str,
pub shipping_method_name: &'a str,
pub coupon: Option<&'a str>,
pub shipping_taxrate: i32,
pub shipping: Cents,
pub tax: Cents,
pub discount: Cents,
pub total: Cents,
pub email: &'a str,
pub token: &'a str,
pub prioritize: bool,
pub note: Option<&'a str>,
pub invoice_address_id: i64,
pub shipping_address_id: i64,
}
pub async fn insert<'e>(
order: &NewOrder<'_>,
conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Order, sqlx::Error> {
let NewOrder {
uuid,
origin,
locale,
state,
custom_status,
date,
payment_method,
payment_id,
payment_meta,
currency,
shipping_method,
shipping_method_name,
coupon,
shipping_taxrate,
shipping,
tax,
discount,
total,
email,
token,
prioritize,
note,
invoice_address_id,
shipping_address_id,
} = order;
sqlx::query_as(
"
INSERT INTO orders (
uuid,
origin,
locale,
state,
custom_status,
date,
payment_method,
payment_id,
payment_meta,
currency,
shipping_method,
shipping_method_name,
coupon,
shipping_taxrate,
shipping,
tax,
discount,
total,
email,
token,
prioritize,
note,
invoice_address_id,
shipping_address_id
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,
$19, $20, $21, $22, $23, $24
)
ON CONFLICT(payment_method, payment_id) DO NOTHING
RETURNING *
",
)
.bind(uuid)
.bind(origin)
.bind(locale)
.bind(state)
.bind(custom_status)
.bind(date)
.bind(payment_method)
.bind(payment_id)
.bind(payment_meta)
.bind(currency)
.bind(shipping_method)
.bind(shipping_method_name)
.bind(coupon)
.bind(shipping_taxrate)
.bind(shipping)
.bind(tax)
.bind(discount)
.bind(total)
.bind(email)
.bind(token)
.bind(prioritize)
.bind(note)
.bind(invoice_address_id)
.bind(shipping_address_id)
.fetch_one(conn)
.await
}
impl Order {
pub async fn update_state(
&mut self,
new_state: OrderState,
new_custom_status: &str,
conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<(), sqlx::Error> {
sqlx::query("UPDATE orders SET state = $2, custom_status = $3 WHERE id = $1")
.bind(self.id)
.bind(new_state)
.bind(new_custom_status)
.execute(conn)
.await?;
self.state = new_state;
self.custom_status = new_custom_status.to_string();
Ok(())
}
pub async fn update_payment(
&mut self,
new_payment_id: &str,
new_payment_meta: JsonValue,
conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<(), sqlx::Error> {
sqlx::query("UPDATE orders SET payment_id = $2, payment_meta = $3 WHERE id = $1")
.bind(self.id)
.bind(new_payment_id)
.bind(&new_payment_meta)
.execute(conn)
.await?;
self.payment_id = new_payment_id.to_string();
self.payment_meta = new_payment_meta;
Ok(())
}
}