Fix order, add non negative constructor
This commit is contained in:
parent
5354d909b3
commit
388bfd634a
@ -3,11 +3,13 @@ use sqlx::PgPool;
|
||||
|
||||
pub use account_orders::*;
|
||||
pub use accounts::*;
|
||||
pub use order_items::*;
|
||||
pub use products::*;
|
||||
pub use stocks::*;
|
||||
|
||||
mod account_orders;
|
||||
mod accounts;
|
||||
mod order_items;
|
||||
mod products;
|
||||
mod stocks;
|
||||
|
||||
@ -37,6 +39,8 @@ pub enum Error {
|
||||
Product(products::Error),
|
||||
#[error("{0}")]
|
||||
Stock(stocks::Error),
|
||||
#[error("{0}")]
|
||||
OrderItem(order_items::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
@ -9,13 +9,13 @@ use super::Result;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Can't create account")]
|
||||
#[error("Can't create account order")]
|
||||
CantCreate,
|
||||
#[error("Can't find account does to lack of identity")]
|
||||
#[error("Can't find account order does to lack of identity")]
|
||||
NoIdentity,
|
||||
#[error("Account order does not exists")]
|
||||
NotExists,
|
||||
#[error("Failed to load all accounts")]
|
||||
#[error("Failed to load all account orders")]
|
||||
All,
|
||||
}
|
||||
|
||||
|
93
api/src/actors/database/order_items.rs
Normal file
93
api/src/actors/database/order_items.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use crate::async_handler;
|
||||
use actix::{Handler, ResponseActFuture, WrapFuture};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::model::*;
|
||||
|
||||
use super::Result;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Can't create order item")]
|
||||
CantCreate,
|
||||
#[error("Can't find order item does to lack of identity")]
|
||||
NoIdentity,
|
||||
#[error("Order item does not exists")]
|
||||
NotExists,
|
||||
#[error("Failed to load all order items")]
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<Vec<OrderItem>>")]
|
||||
pub struct AllOrderItems;
|
||||
|
||||
async_handler!(AllOrderItems, all_order_items, Vec<OrderItem>);
|
||||
|
||||
pub async fn all_order_items(_msg: AllOrderItems, pool: PgPool) -> Result<Vec<OrderItem>> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT id, buyer_id, status
|
||||
FROM order_items
|
||||
"#,
|
||||
)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
super::Error::OrderItem(Error::All)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<OrderItem>")]
|
||||
pub struct CreateOrderItem {
|
||||
pub buyer_id: AccountId,
|
||||
pub status: OrderStatus,
|
||||
}
|
||||
|
||||
async_handler!(CreateOrderItem, create_order_item, OrderItem);
|
||||
|
||||
async fn create_order_item(msg: CreateOrderItem, db: PgPool) -> Result<OrderItem> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
INSERT INTO order_items (buyer_id, status)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id, buyer_id, status
|
||||
"#,
|
||||
)
|
||||
.bind(msg.buyer_id)
|
||||
.bind(msg.status)
|
||||
.fetch_one(&db)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
super::Error::OrderItem(Error::CantCreate)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<OrderItem>")]
|
||||
pub struct FindOrderItem {
|
||||
pub id: OrderItemId,
|
||||
}
|
||||
|
||||
async_handler!(FindOrderItem, find_order_item, OrderItem);
|
||||
|
||||
async fn find_order_item(msg: FindOrderItem, db: PgPool) -> Result<OrderItem> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT id, buyer_id, status
|
||||
FROM order_items
|
||||
WHERE id = $1
|
||||
"#,
|
||||
)
|
||||
.bind(msg.id)
|
||||
.fetch_one(&db)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
super::Error::OrderItem(Error::NotExists)
|
||||
})
|
||||
}
|
@ -8,13 +8,13 @@ use crate::{database, model};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Unable to load all products")]
|
||||
#[error("Unable to load all stocks")]
|
||||
All,
|
||||
#[error("Unable to create product")]
|
||||
#[error("Unable to create stock")]
|
||||
Create,
|
||||
#[error("Unable to update product")]
|
||||
#[error("Unable to update stock")]
|
||||
Update,
|
||||
#[error("Unable to delete product")]
|
||||
#[error("Unable to delete stock")]
|
||||
Delete,
|
||||
}
|
||||
|
||||
@ -22,9 +22,9 @@ pub enum Error {
|
||||
#[rtype(result = "Result<Vec<model::Stock>>")]
|
||||
pub struct AllStocks;
|
||||
|
||||
crate::async_handler!(AllStocks, all, Vec<Stock>);
|
||||
crate::async_handler!(AllStocks, all_stocks, Vec<Stock>);
|
||||
|
||||
async fn all(_msg: AllStocks, pool: PgPool) -> Result<Vec<model::Stock>> {
|
||||
async fn all_stocks(_msg: AllStocks, pool: PgPool) -> Result<Vec<model::Stock>> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT id, product_id, quantity, quantity_unit
|
||||
@ -47,9 +47,9 @@ pub struct CreateStock {
|
||||
pub quantity_unit: QuantityUnit,
|
||||
}
|
||||
|
||||
crate::async_handler!(CreateStock, create_product, Stock);
|
||||
crate::async_handler!(CreateStock, create_stock, Stock);
|
||||
|
||||
async fn create_product(msg: CreateStock, pool: PgPool) -> Result<model::Stock> {
|
||||
async fn create_stock(msg: CreateStock, pool: PgPool) -> Result<model::Stock> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
INSERT INTO stocks (product_id, quantity)
|
||||
@ -77,9 +77,9 @@ pub struct UpdateStock {
|
||||
pub quantity_unit: QuantityUnit,
|
||||
}
|
||||
|
||||
crate::async_handler!(UpdateStock, update_product, Stock);
|
||||
crate::async_handler!(UpdateStock, update_stock, Stock);
|
||||
|
||||
async fn update_product(msg: UpdateStock, pool: PgPool) -> Result<model::Stock> {
|
||||
async fn update_stock(msg: UpdateStock, pool: PgPool) -> Result<model::Stock> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
UPDATE stocks
|
||||
@ -108,9 +108,9 @@ pub struct DeleteStock {
|
||||
pub stock_id: StockId,
|
||||
}
|
||||
|
||||
crate::async_handler!(DeleteStock, delete_product, Option<model::Stock>);
|
||||
crate::async_handler!(DeleteStock, delete_stock, Option<model::Stock>);
|
||||
|
||||
async fn delete_product(msg: DeleteStock, pool: PgPool) -> Result<Option<Stock>> {
|
||||
async fn delete_stock(msg: DeleteStock, pool: PgPool) -> Result<Option<Stock>> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
DELETE FROM stocks
|
||||
|
@ -5,7 +5,7 @@ use actix_session::{storage::RedisActorSessionStore, SessionMiddleware};
|
||||
use actix_web::cookie::Key;
|
||||
use actix_web::middleware::Logger;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, App, HttpResponse, HttpServer};
|
||||
use actix_web::{App, HttpServer};
|
||||
use gumdrop::Options;
|
||||
use password_hash::SaltString;
|
||||
use validator::{validate_email, validate_length};
|
||||
|
@ -4,6 +4,12 @@ use derive_more::{Deref, Display};
|
||||
use serde::de::{Error, Visitor};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TransformError {
|
||||
#[error("Given value is below minimal value")]
|
||||
BelowMinimal,
|
||||
}
|
||||
|
||||
pub type RecordId = i32;
|
||||
|
||||
#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize)]
|
||||
@ -41,6 +47,13 @@ pub enum QuantityUnit {
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize)]
|
||||
#[sqlx(rename_all = "lowercase")]
|
||||
pub enum PaymentMethod {
|
||||
PayU,
|
||||
PaymentOnTheSpot,
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
|
||||
#[sqlx(transparent)]
|
||||
#[serde(transparent)]
|
||||
@ -56,6 +69,22 @@ pub struct PriceMinor(NonNegative);
|
||||
#[serde(transparent)]
|
||||
pub struct Quantity(NonNegative);
|
||||
|
||||
impl TryFrom<NonNegative> for Quantity {
|
||||
type Error = TransformError;
|
||||
|
||||
fn try_from(value: NonNegative) -> Result<Self, Self::Error> {
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<i32> for Quantity {
|
||||
type Error = TransformError;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
Ok(Self(value.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Deserialize, Serialize, Deref, Debug)]
|
||||
#[sqlx(transparent)]
|
||||
#[serde(transparent)]
|
||||
@ -98,7 +127,19 @@ impl<'de> serde::Deserialize<'de> for Email {
|
||||
#[derive(sqlx::Type, Serialize, Deref)]
|
||||
#[sqlx(transparent)]
|
||||
#[serde(transparent)]
|
||||
pub struct NonNegative(pub i32);
|
||||
pub struct NonNegative(i32);
|
||||
|
||||
impl TryFrom<i32> for NonNegative {
|
||||
type Error = TransformError;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
if value < 0 {
|
||||
return Err(TransformError::BelowMinimal);
|
||||
} else {
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for NonNegative {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
@ -227,7 +268,7 @@ pub struct Stock {
|
||||
pub quantity_unit: QuantityUnit,
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Serialize, Deserialize)]
|
||||
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
|
||||
#[sqlx(transparent)]
|
||||
#[serde(transparent)]
|
||||
pub struct AccountOrderId(pub RecordId);
|
||||
@ -238,3 +279,43 @@ pub struct AccountOrder {
|
||||
pub buyer_id: AccountId,
|
||||
pub status: OrderStatus,
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
|
||||
#[sqlx(transparent)]
|
||||
#[serde(transparent)]
|
||||
pub struct OrderItemId(pub RecordId);
|
||||
|
||||
#[derive(sqlx::FromRow, Serialize, Deserialize)]
|
||||
pub struct OrderItem {
|
||||
pub id: OrderItemId,
|
||||
pub product_id: ProductId,
|
||||
pub order_id: OrderItemId,
|
||||
pub quantity: Quantity,
|
||||
pub quantity_unit: QuantityUnit,
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
|
||||
#[sqlx(transparent)]
|
||||
#[serde(transparent)]
|
||||
pub struct ShoppingCartId(pub RecordId);
|
||||
|
||||
#[derive(sqlx::FromRow, Serialize, Deserialize)]
|
||||
pub struct ShoppingCart {
|
||||
pub id: ShoppingCartId,
|
||||
pub buyer_id: AccountId,
|
||||
pub payment_method: PaymentMethod,
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
|
||||
#[sqlx(transparent)]
|
||||
#[serde(transparent)]
|
||||
pub struct ShoppingCartItemId(pub RecordId);
|
||||
|
||||
#[derive(sqlx::FromRow, Serialize, Deserialize)]
|
||||
pub struct ShoppingCartItem {
|
||||
pub id: ShoppingCartId,
|
||||
pub product_id: ProductId,
|
||||
pub shopping_cart_id: ShoppingCartId,
|
||||
pub quantity: Quantity,
|
||||
pub quantity_unit: QuantityUnit,
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ CREATE TABLE order_items
|
||||
CONSTRAINT positive_quantity check ( quantity >= 0 )
|
||||
);
|
||||
|
||||
CREATE TABLE statistics
|
||||
CREATE TABLE "statistics"
|
||||
(
|
||||
id serial not null primary key,
|
||||
url varchar not null,
|
||||
|
@ -1,5 +1,5 @@
|
||||
CREATE TYPE "PaymentMethod" AS ENUM (
|
||||
'payu',
|
||||
'pay_u',
|
||||
'payment_on_the_spot'
|
||||
);
|
||||
|
||||
@ -14,7 +14,7 @@ CREATE TABLE shopping_cart_items
|
||||
(
|
||||
id serial not null primary key,
|
||||
product_id int references products (id) not null,
|
||||
order_id int references shopping_carts (id),
|
||||
shopping_cart_id int references shopping_carts (id),
|
||||
quantity int not null default 0,
|
||||
quantity_unit "QuantityUnit" not null,
|
||||
CONSTRAINT positive_quantity check ( quantity >= 0 )
|
||||
|
Loading…
Reference in New Issue
Block a user