WIP: Place order for guess and account.
This commit is contained in:
parent
28e9736562
commit
c1c97061eb
@ -90,7 +90,7 @@ impl actix::Actor for AccountManager {
|
|||||||
|
|
||||||
pub struct MeResult {
|
pub struct MeResult {
|
||||||
pub account: FullAccount,
|
pub account: FullAccount,
|
||||||
pub addresses: Vec<model::Address>,
|
pub addresses: Vec<model::AccountAddress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message, Debug)]
|
#[derive(actix::Message, Debug)]
|
||||||
|
@ -9,7 +9,7 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<Vec<model::Address>>")]
|
#[rtype(result = "Result<Vec<model::AccountAddress>>")]
|
||||||
pub struct AccountAddresses {
|
pub struct AccountAddresses {
|
||||||
pub account_id: model::AccountId,
|
pub account_id: model::AccountId,
|
||||||
}
|
}
|
||||||
@ -17,17 +17,17 @@ pub struct AccountAddresses {
|
|||||||
db_async_handler!(
|
db_async_handler!(
|
||||||
AccountAddresses,
|
AccountAddresses,
|
||||||
account_addresses,
|
account_addresses,
|
||||||
Vec<model::Address>,
|
Vec<model::AccountAddress>,
|
||||||
inner_account_addresses
|
inner_account_addresses
|
||||||
);
|
);
|
||||||
|
|
||||||
pub(crate) async fn account_addresses(
|
pub(crate) async fn account_addresses(
|
||||||
msg: AccountAddresses,
|
msg: AccountAddresses,
|
||||||
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
) -> Result<Vec<model::Address>> {
|
) -> Result<Vec<model::AccountAddress>> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, name, email, street, city, country, zip, account_id
|
SELECT id, name, email, street, city, country, zip, account_id, is_default
|
||||||
FROM account_addresses
|
FROM account_addresses
|
||||||
WHERE account_id = $1
|
WHERE account_id = $1
|
||||||
"#,
|
"#,
|
||||||
@ -39,7 +39,37 @@ WHERE account_id = $1
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<model::Address>")]
|
#[rtype(result = "Result<model::AccountAddress>")]
|
||||||
|
pub struct DefaultAccountAddress {
|
||||||
|
pub account_id: model::AccountId,
|
||||||
|
}
|
||||||
|
|
||||||
|
db_async_handler!(
|
||||||
|
DefaultAccountAddress,
|
||||||
|
default_account_address,
|
||||||
|
model::AccountAddress,
|
||||||
|
inner_default_account_address
|
||||||
|
);
|
||||||
|
|
||||||
|
pub(crate) async fn default_account_address(
|
||||||
|
msg: DefaultAccountAddress,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<model::AccountAddress> {
|
||||||
|
sqlx::query_as(
|
||||||
|
r#"
|
||||||
|
SELECT id, name, email, street, city, country, zip, account_id, is_default
|
||||||
|
FROM account_addresses
|
||||||
|
WHERE account_id = $1 AND is_default
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(msg.account_id)
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::AccountAddresses.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(actix::Message)]
|
||||||
|
#[rtype(result = "Result<model::AccountAddress>")]
|
||||||
pub struct CreateAccountAddress {
|
pub struct CreateAccountAddress {
|
||||||
pub name: model::Name,
|
pub name: model::Name,
|
||||||
pub email: model::Email,
|
pub email: model::Email,
|
||||||
@ -47,25 +77,41 @@ pub struct CreateAccountAddress {
|
|||||||
pub city: model::City,
|
pub city: model::City,
|
||||||
pub country: model::Country,
|
pub country: model::Country,
|
||||||
pub zip: model::Zip,
|
pub zip: model::Zip,
|
||||||
pub account_id: model::AccountId,
|
pub account_id: Option<model::AccountId>,
|
||||||
|
pub is_default: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(
|
db_async_handler!(
|
||||||
CreateAccountAddress,
|
CreateAccountAddress,
|
||||||
create_address,
|
create_address,
|
||||||
model::Address,
|
model::AccountAddress,
|
||||||
inner_create_address
|
inner_create_address
|
||||||
);
|
);
|
||||||
|
|
||||||
pub(crate) async fn create_address(
|
pub(crate) async fn create_address(
|
||||||
msg: CreateAccountAddress,
|
msg: CreateAccountAddress,
|
||||||
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
) -> Result<model::Address> {
|
) -> Result<model::AccountAddress> {
|
||||||
|
if msg.is_default {
|
||||||
|
if let Err(e) = sqlx::query(
|
||||||
|
r#"
|
||||||
|
UPDATE account_addresses
|
||||||
|
SET is_default = FALSE
|
||||||
|
WHERE account_id = $1
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(msg.account_id)
|
||||||
|
.fetch_all(&mut *pool)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
log::error!("{}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO account_addresses ( name, email, street, city, country, zip, account_id )
|
INSERT INTO account_addresses ( name, email, street, city, country, zip, account_id )
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
RETURNING id, name, email, street, city, country, zip, account_id
|
RETURNING id, name, email, street, city, country, zip, account_id, is_default
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.name)
|
.bind(msg.name)
|
||||||
@ -81,7 +127,7 @@ RETURNING id, name, email, street, city, country, zip, account_id
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<model::Address>")]
|
#[rtype(result = "Result<model::AccountAddress>")]
|
||||||
pub struct UpdateAccountAddress {
|
pub struct UpdateAccountAddress {
|
||||||
pub id: model::AddressId,
|
pub id: model::AddressId,
|
||||||
pub name: model::Name,
|
pub name: model::Name,
|
||||||
@ -91,25 +137,26 @@ pub struct UpdateAccountAddress {
|
|||||||
pub country: model::Country,
|
pub country: model::Country,
|
||||||
pub zip: model::Zip,
|
pub zip: model::Zip,
|
||||||
pub account_id: model::AccountId,
|
pub account_id: model::AccountId,
|
||||||
|
pub is_default: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(
|
db_async_handler!(
|
||||||
UpdateAccountAddress,
|
UpdateAccountAddress,
|
||||||
update_account_address,
|
update_account_address,
|
||||||
model::Address,
|
model::AccountAddress,
|
||||||
inner_update_account_address
|
inner_update_account_address
|
||||||
);
|
);
|
||||||
|
|
||||||
pub(crate) async fn update_account_address(
|
pub(crate) async fn update_account_address(
|
||||||
msg: UpdateAccountAddress,
|
msg: UpdateAccountAddress,
|
||||||
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
) -> Result<model::Address> {
|
) -> Result<model::AccountAddress> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
UPDATE account_addresses
|
UPDATE account_addresses
|
||||||
SET name = $2, email = $3, street = $4, city = $5, country = $6, zip = $7, account_id = $8
|
SET name = $2, email = $3, street = $4, city = $5, country = $6, zip = $7, account_id = $8, is_default = $9
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, name, email, street, city, country, zip, account_id
|
RETURNING id, name, email, street, city, country, zip, account_id, is_default
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.id)
|
.bind(msg.id)
|
||||||
@ -120,6 +167,7 @@ RETURNING id, name, email, street, city, country, zip, account_id
|
|||||||
.bind(msg.country)
|
.bind(msg.country)
|
||||||
.bind(msg.zip)
|
.bind(msg.zip)
|
||||||
.bind(msg.account_id)
|
.bind(msg.account_id)
|
||||||
|
.bind(msg.is_default)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::CreateAccountAddress.into())
|
.map_err(|_| Error::CreateAccountAddress.into())
|
@ -3,10 +3,11 @@ use config::SharedAppConfig;
|
|||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use sqlx_core::arguments::Arguments;
|
use sqlx_core::arguments::Arguments;
|
||||||
|
|
||||||
pub use crate::account_orders::*;
|
pub use crate::account_addresses::*;
|
||||||
pub use crate::accounts::*;
|
pub use crate::accounts::*;
|
||||||
pub use crate::addresses::*;
|
pub use crate::order_addresses::*;
|
||||||
pub use crate::order_items::*;
|
pub use crate::order_items::*;
|
||||||
|
pub use crate::orders::*;
|
||||||
pub use crate::photos::*;
|
pub use crate::photos::*;
|
||||||
pub use crate::product_photos::*;
|
pub use crate::product_photos::*;
|
||||||
pub use crate::products::*;
|
pub use crate::products::*;
|
||||||
@ -15,10 +16,11 @@ pub use crate::shopping_carts::*;
|
|||||||
pub use crate::stocks::*;
|
pub use crate::stocks::*;
|
||||||
pub use crate::tokens::*;
|
pub use crate::tokens::*;
|
||||||
|
|
||||||
pub mod account_orders;
|
pub mod account_addresses;
|
||||||
pub mod accounts;
|
pub mod accounts;
|
||||||
pub mod addresses;
|
pub mod order_addresses;
|
||||||
pub mod order_items;
|
pub mod order_items;
|
||||||
|
pub mod orders;
|
||||||
pub mod photos;
|
pub mod photos;
|
||||||
pub mod product_photos;
|
pub mod product_photos;
|
||||||
pub mod products;
|
pub mod products;
|
||||||
@ -128,7 +130,7 @@ pub enum Error {
|
|||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Account(#[from] accounts::Error),
|
Account(#[from] accounts::Error),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
AccountOrder(#[from] account_orders::Error),
|
AccountOrder(#[from] orders::Error),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Product(#[from] products::Error),
|
Product(#[from] products::Error),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
@ -146,7 +148,9 @@ pub enum Error {
|
|||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
ProductPhoto(#[from] product_photos::Error),
|
ProductPhoto(#[from] product_photos::Error),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
AccountAddress(#[from] addresses::Error),
|
AccountAddress(#[from] account_addresses::Error),
|
||||||
|
#[error("{0}")]
|
||||||
|
OrderAddress(#[from] order_addresses::Error),
|
||||||
#[error("Failed to start or finish transaction")]
|
#[error("Failed to start or finish transaction")]
|
||||||
TransactionFailed,
|
TransactionFailed,
|
||||||
}
|
}
|
||||||
|
130
actors/database_manager/src/order_addresses.rs
Normal file
130
actors/database_manager/src/order_addresses.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
use crate::{db_async_handler, Result};
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, serde::Serialize, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Can't load account addresses")]
|
||||||
|
OrderAddress,
|
||||||
|
#[error("Failed to save account address")]
|
||||||
|
CreateOrderAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(actix::Message)]
|
||||||
|
#[rtype(result = "Result<model::OrderAddress>")]
|
||||||
|
pub struct OrderAddress {
|
||||||
|
pub order_id: model::OrderId,
|
||||||
|
}
|
||||||
|
|
||||||
|
db_async_handler!(
|
||||||
|
OrderAddress,
|
||||||
|
order_address,
|
||||||
|
model::OrderAddress,
|
||||||
|
inner_order_address
|
||||||
|
);
|
||||||
|
|
||||||
|
pub(crate) async fn order_address(
|
||||||
|
msg: OrderAddress,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<model::OrderAddress> {
|
||||||
|
sqlx::query_as(
|
||||||
|
r#"
|
||||||
|
SELECT
|
||||||
|
order_addresses.id,
|
||||||
|
order_addresses.name,
|
||||||
|
order_addresses.email,
|
||||||
|
order_addresses.street,
|
||||||
|
order_addresses.city,
|
||||||
|
order_addresses.country,
|
||||||
|
order_addresses.zip
|
||||||
|
FROM order_addresses
|
||||||
|
INNER JOIN orders ON orders.address_id = order_addresses.id
|
||||||
|
WHERE orders.id = $1
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(msg.order_id)
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::OrderAddress.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(actix::Message)]
|
||||||
|
#[rtype(result = "Result<model::OrderAddress>")]
|
||||||
|
pub struct CreateOrderAddress {
|
||||||
|
pub name: model::Name,
|
||||||
|
pub email: model::Email,
|
||||||
|
pub street: model::Street,
|
||||||
|
pub city: model::City,
|
||||||
|
pub country: model::Country,
|
||||||
|
pub zip: model::Zip,
|
||||||
|
}
|
||||||
|
|
||||||
|
db_async_handler!(
|
||||||
|
CreateOrderAddress,
|
||||||
|
create_order_address,
|
||||||
|
model::OrderAddress,
|
||||||
|
inner_create_order_address
|
||||||
|
);
|
||||||
|
|
||||||
|
pub(crate) async fn create_order_address(
|
||||||
|
msg: CreateOrderAddress,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<model::OrderAddress> {
|
||||||
|
sqlx::query_as(
|
||||||
|
r#"
|
||||||
|
INSERT INTO order_addresses ( name, email, street, city, country, zip )
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6)
|
||||||
|
RETURNING id, name, email, street, city, country, zip
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(msg.name)
|
||||||
|
.bind(msg.email)
|
||||||
|
.bind(msg.street)
|
||||||
|
.bind(msg.city)
|
||||||
|
.bind(msg.country)
|
||||||
|
.bind(msg.zip)
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::CreateOrderAddress.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(actix::Message)]
|
||||||
|
#[rtype(result = "Result<model::OrderAddress>")]
|
||||||
|
pub struct UpdateOrderAddress {
|
||||||
|
pub id: model::OrderAddressId,
|
||||||
|
pub name: model::Name,
|
||||||
|
pub email: model::Email,
|
||||||
|
pub street: model::Street,
|
||||||
|
pub city: model::City,
|
||||||
|
pub country: model::Country,
|
||||||
|
pub zip: model::Zip,
|
||||||
|
}
|
||||||
|
|
||||||
|
db_async_handler!(
|
||||||
|
UpdateOrderAddress,
|
||||||
|
update_account_address,
|
||||||
|
model::OrderAddress,
|
||||||
|
inner_update_account_address
|
||||||
|
);
|
||||||
|
|
||||||
|
pub(crate) async fn update_account_address(
|
||||||
|
msg: UpdateOrderAddress,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<model::OrderAddress> {
|
||||||
|
sqlx::query_as(
|
||||||
|
r#"
|
||||||
|
UPDATE order_addresses
|
||||||
|
SET name = $2, email = $3, street = $4, city = $5, country = $6, zip = $7
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING id, name, email, street, city, country, zip
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(msg.id)
|
||||||
|
.bind(msg.name)
|
||||||
|
.bind(msg.email)
|
||||||
|
.bind(msg.street)
|
||||||
|
.bind(msg.city)
|
||||||
|
.bind(msg.country)
|
||||||
|
.bind(msg.zip)
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::CreateOrderAddress.into())
|
||||||
|
}
|
@ -47,7 +47,7 @@ ORDER BY id DESC
|
|||||||
#[rtype(result = "Result<OrderItem>")]
|
#[rtype(result = "Result<OrderItem>")]
|
||||||
pub struct CreateOrderItem {
|
pub struct CreateOrderItem {
|
||||||
pub product_id: ProductId,
|
pub product_id: ProductId,
|
||||||
pub order_id: AccountOrderId,
|
pub order_id: OrderId,
|
||||||
pub quantity: Quantity,
|
pub quantity: Quantity,
|
||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
}
|
}
|
||||||
@ -109,7 +109,7 @@ WHERE id = $1
|
|||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<Vec<OrderItem>>")]
|
#[rtype(result = "Result<Vec<OrderItem>>")]
|
||||||
pub struct OrderItems {
|
pub struct OrderItems {
|
||||||
pub order_id: model::AccountOrderId,
|
pub order_id: model::OrderId,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(OrderItems, order_items, Vec<OrderItem>);
|
db_async_handler!(OrderItems, order_items, Vec<OrderItem>);
|
||||||
|
@ -21,19 +21,16 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<Vec<AccountOrder>>")]
|
#[rtype(result = "Result<Vec<Order>>")]
|
||||||
pub struct AllAccountOrders;
|
pub struct AllAccountOrders;
|
||||||
|
|
||||||
db_async_handler!(AllAccountOrders, all_account_orders, Vec<AccountOrder>);
|
db_async_handler!(AllAccountOrders, all_orders, Vec<Order>);
|
||||||
|
|
||||||
pub(crate) async fn all_account_orders(
|
pub(crate) async fn all_orders(_msg: AllAccountOrders, pool: PgPool) -> Result<Vec<Order>> {
|
||||||
_msg: AllAccountOrders,
|
|
||||||
pool: PgPool,
|
|
||||||
) -> Result<Vec<AccountOrder>> {
|
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, buyer_id, status, order_ext_id, service_order_id, checkout_notes
|
SELECT id, buyer_id, status, order_ext_id, service_order_id, checkout_notes, address_id
|
||||||
FROM account_orders
|
FROM orders
|
||||||
ORDER BY id DESC
|
ORDER BY id DESC
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
@ -56,7 +53,7 @@ pub mod create_order {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<AccountOrder>")]
|
#[rtype(result = "Result<Order>")]
|
||||||
pub struct CreateAccountOrder {
|
pub struct CreateAccountOrder {
|
||||||
pub buyer_id: AccountId,
|
pub buyer_id: AccountId,
|
||||||
pub items: Vec<create_order::OrderItem>,
|
pub items: Vec<create_order::OrderItem>,
|
||||||
@ -67,19 +64,19 @@ pub struct CreateAccountOrder {
|
|||||||
db_async_handler!(
|
db_async_handler!(
|
||||||
CreateAccountOrder,
|
CreateAccountOrder,
|
||||||
create_account_order,
|
create_account_order,
|
||||||
AccountOrder,
|
Order,
|
||||||
inner_create_account_order
|
inner_create_account_order
|
||||||
);
|
);
|
||||||
|
|
||||||
pub(crate) async fn create_account_order(
|
pub(crate) async fn create_account_order(
|
||||||
msg: CreateAccountOrder,
|
msg: CreateAccountOrder,
|
||||||
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
) -> Result<AccountOrder> {
|
) -> Result<Order> {
|
||||||
let order: AccountOrder = match sqlx::query_as(
|
let order: Order = match sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO account_orders (buyer_id, status)
|
INSERT INTO orders (buyer_id, status)
|
||||||
VALUES ($1, $2, $3)
|
VALUES ($1, $2, $3)
|
||||||
RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes
|
RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes, address_id
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.buyer_id)
|
.bind(msg.buyer_id)
|
||||||
@ -130,26 +127,23 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<AccountOrder>")]
|
#[rtype(result = "Result<Order>")]
|
||||||
pub struct UpdateAccountOrder {
|
pub struct UpdateAccountOrder {
|
||||||
pub id: AccountOrderId,
|
pub id: OrderId,
|
||||||
pub buyer_id: AccountId,
|
pub buyer_id: AccountId,
|
||||||
pub status: OrderStatus,
|
pub status: OrderStatus,
|
||||||
pub order_id: Option<OrderId>,
|
pub order_id: Option<ExtOrderId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(UpdateAccountOrder, update_account_order, AccountOrder);
|
db_async_handler!(UpdateAccountOrder, update_account_order, Order);
|
||||||
|
|
||||||
pub(crate) async fn update_account_order(
|
pub(crate) async fn update_account_order(msg: UpdateAccountOrder, db: PgPool) -> Result<Order> {
|
||||||
msg: UpdateAccountOrder,
|
|
||||||
db: PgPool,
|
|
||||||
) -> Result<AccountOrder> {
|
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
UPDATE account_orders
|
UPDATE orders
|
||||||
SET buyer_id = $2 AND status = $3 AND order_id = $4
|
SET buyer_id = $2 AND status = $3 AND order_id = $4
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes
|
RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes, address_id
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.id)
|
.bind(msg.id)
|
||||||
@ -165,28 +159,24 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<AccountOrder>")]
|
#[rtype(result = "Result<Order>")]
|
||||||
pub struct UpdateAccountOrderByExt {
|
pub struct UpdateAccountOrderByExt {
|
||||||
pub order_ext_id: String,
|
pub order_ext_id: String,
|
||||||
pub status: OrderStatus,
|
pub status: OrderStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(
|
db_async_handler!(UpdateAccountOrderByExt, update_account_order_by_ext, Order);
|
||||||
UpdateAccountOrderByExt,
|
|
||||||
update_account_order_by_ext,
|
|
||||||
AccountOrder
|
|
||||||
);
|
|
||||||
|
|
||||||
pub(crate) async fn update_account_order_by_ext(
|
pub(crate) async fn update_account_order_by_ext(
|
||||||
msg: UpdateAccountOrderByExt,
|
msg: UpdateAccountOrderByExt,
|
||||||
db: PgPool,
|
db: PgPool,
|
||||||
) -> Result<AccountOrder> {
|
) -> Result<Order> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
UPDATE account_orders
|
UPDATE orders
|
||||||
SET status = $2
|
SET status = $2
|
||||||
WHERE order_ext_id = $1
|
WHERE order_ext_id = $1
|
||||||
RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes
|
RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes, address_id
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.order_ext_id)
|
.bind(msg.order_ext_id)
|
||||||
@ -200,18 +190,18 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<AccountOrder>")]
|
#[rtype(result = "Result<Order>")]
|
||||||
pub struct FindAccountOrder {
|
pub struct FindAccountOrder {
|
||||||
pub id: AccountOrderId,
|
pub id: OrderId,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(FindAccountOrder, find_account_order, AccountOrder);
|
db_async_handler!(FindAccountOrder, find_account_order, Order);
|
||||||
|
|
||||||
pub(crate) async fn find_account_order(msg: FindAccountOrder, db: PgPool) -> Result<AccountOrder> {
|
pub(crate) async fn find_account_order(msg: FindAccountOrder, db: PgPool) -> Result<Order> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, buyer_id, status, order_ext_id, service_order_id, checkout_notes
|
SELECT id, buyer_id, status, order_ext_id, service_order_id, checkout_notes, address_id
|
||||||
FROM account_orders
|
FROM orders
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
@ -225,24 +215,21 @@ WHERE id = $1
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<AccountOrder>")]
|
#[rtype(result = "Result<Order>")]
|
||||||
pub struct SetOrderServiceId {
|
pub struct SetOrderServiceId {
|
||||||
pub id: AccountOrderId,
|
pub id: OrderId,
|
||||||
pub service_order_id: String,
|
pub service_order_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(SetOrderServiceId, set_order_service_id, AccountOrder);
|
db_async_handler!(SetOrderServiceId, set_order_service_id, Order);
|
||||||
|
|
||||||
pub(crate) async fn set_order_service_id(
|
pub(crate) async fn set_order_service_id(msg: SetOrderServiceId, db: PgPool) -> Result<Order> {
|
||||||
msg: SetOrderServiceId,
|
|
||||||
db: PgPool,
|
|
||||||
) -> Result<AccountOrder> {
|
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
UPDATE account_orders
|
UPDATE orders
|
||||||
SET service_order_id = $2
|
SET service_order_id = $2
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes
|
RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes, address_id
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.id)
|
.bind(msg.id)
|
@ -1,7 +1,7 @@
|
|||||||
use actix::Message;
|
use actix::Message;
|
||||||
use config::SharedAppConfig;
|
use config::SharedAppConfig;
|
||||||
use database_manager::{query_db, SharedDatabase};
|
use database_manager::{query_db, SharedDatabase};
|
||||||
use model::{AccountId, AccountOrder, OrderStatus, ShoppingCart, ShoppingCartId, ShoppingCartItem};
|
use model::{AccountId, Order, OrderStatus, ShoppingCart, ShoppingCartItem};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! order_async_handler {
|
macro_rules! order_async_handler {
|
||||||
@ -19,6 +19,27 @@ macro_rules! order_async_handler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! query_order {
|
||||||
|
($order_manager: expr, $msg: expr, $fail: expr) => {
|
||||||
|
$crate::query_order!($order_manager, $msg, $fail, $fail)
|
||||||
|
};
|
||||||
|
|
||||||
|
($order_manager: expr, $msg: expr, $db_fail: expr, $act_fail: expr) => {
|
||||||
|
match $order_manager.send($msg).await {
|
||||||
|
Ok(Ok(r)) => Ok(r),
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
log::error!("{e}");
|
||||||
|
Err($db_fail)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("{e:?}");
|
||||||
|
Err($act_fail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, serde::Serialize, thiserror::Error)]
|
#[derive(Debug, Copy, Clone, serde::Serialize, thiserror::Error)]
|
||||||
#[serde(rename_all = "kebab-case", tag = "order")]
|
#[serde(rename_all = "kebab-case", tag = "order")]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -28,6 +49,12 @@ pub enum Error {
|
|||||||
ShoppingCart,
|
ShoppingCart,
|
||||||
#[error("Failed to create account order")]
|
#[error("Failed to create account order")]
|
||||||
CreateAccountOrder,
|
CreateAccountOrder,
|
||||||
|
#[error("Account does not have address")]
|
||||||
|
NoAddress,
|
||||||
|
#[error("Invalid account address")]
|
||||||
|
InvalidAccountAddress,
|
||||||
|
#[error("Invalid order address")]
|
||||||
|
InvalidOrderAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@ -47,24 +74,34 @@ impl OrderManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Message, Debug)]
|
#[derive(Debug)]
|
||||||
#[rtype(result = "Result<AccountOrder>")]
|
pub struct CreateOrderAddress {
|
||||||
pub struct CreateOrder {
|
pub name: model::Name,
|
||||||
pub account_id: AccountId,
|
pub email: model::Email,
|
||||||
pub shopping_cart_id: ShoppingCartId,
|
pub street: model::Street,
|
||||||
|
pub city: model::City,
|
||||||
|
pub country: model::Country,
|
||||||
|
pub zip: model::Zip,
|
||||||
}
|
}
|
||||||
|
|
||||||
order_async_handler!(CreateOrder, create_order, AccountOrder);
|
#[derive(Message, Debug)]
|
||||||
|
#[rtype(result = "Result<Order>")]
|
||||||
|
pub struct CreateAccountOrder {
|
||||||
|
pub account_id: AccountId,
|
||||||
|
pub create_address: Option<CreateOrderAddress>,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn create_order(
|
order_async_handler!(CreateAccountOrder, create_account_order, Order);
|
||||||
msg: CreateOrder,
|
|
||||||
|
pub(crate) async fn create_account_order(
|
||||||
|
msg: CreateAccountOrder,
|
||||||
db: SharedDatabase,
|
db: SharedDatabase,
|
||||||
_config: SharedAppConfig,
|
_config: SharedAppConfig,
|
||||||
) -> Result<AccountOrder> {
|
) -> Result<Order> {
|
||||||
let cart: ShoppingCart = query_db!(
|
let cart: ShoppingCart = query_db!(
|
||||||
db,
|
db,
|
||||||
database_manager::FindShoppingCart {
|
database_manager::EnsureActiveShoppingCart {
|
||||||
id: msg.shopping_cart_id,
|
buyer_id: msg.account_id
|
||||||
},
|
},
|
||||||
Error::ShoppingCart,
|
Error::ShoppingCart,
|
||||||
Error::DatabaseInternal
|
Error::DatabaseInternal
|
||||||
@ -78,6 +115,44 @@ pub(crate) async fn create_order(
|
|||||||
Error::ShoppingCart,
|
Error::ShoppingCart,
|
||||||
Error::DatabaseInternal
|
Error::DatabaseInternal
|
||||||
);
|
);
|
||||||
|
let address: model::AccountAddress = if let Some(input) = msg.create_address {
|
||||||
|
query_db!(
|
||||||
|
db,
|
||||||
|
database_manager::CreateAccountAddress {
|
||||||
|
name: input.name,
|
||||||
|
email: input.email,
|
||||||
|
street: input.street,
|
||||||
|
city: input.city,
|
||||||
|
country: input.country,
|
||||||
|
zip: input.zip,
|
||||||
|
account_id: None,
|
||||||
|
is_default: true,
|
||||||
|
},
|
||||||
|
Error::InvalidAccountAddress
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
query_db!(
|
||||||
|
db,
|
||||||
|
database_manager::DefaultAccountAddress {
|
||||||
|
account_id: cart.buyer_id
|
||||||
|
},
|
||||||
|
Error::NoAddress
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
query_db!(
|
||||||
|
db,
|
||||||
|
database_manager::CreateOrderAddress {
|
||||||
|
name: address.name,
|
||||||
|
email: address.email,
|
||||||
|
street: address.street,
|
||||||
|
city: address.city,
|
||||||
|
country: address.country,
|
||||||
|
zip: address.zip,
|
||||||
|
},
|
||||||
|
Error::InvalidOrderAddress
|
||||||
|
);
|
||||||
|
|
||||||
let order = query_db!(
|
let order = query_db!(
|
||||||
db,
|
db,
|
||||||
database_manager::CreateAccountOrder {
|
database_manager::CreateAccountOrder {
|
||||||
|
@ -151,7 +151,7 @@ impl From<Product> for pay_u::Product {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct CreatePaymentResult {
|
pub struct CreatePaymentResult {
|
||||||
pub order: model::AccountOrder,
|
pub order: model::Order,
|
||||||
pub items: Vec<model::OrderItem>,
|
pub items: Vec<model::OrderItem>,
|
||||||
pub redirect_uri: String,
|
pub redirect_uri: String,
|
||||||
}
|
}
|
||||||
@ -215,7 +215,7 @@ pub(crate) async fn request_payment(
|
|||||||
Error::UnavailableShoppingCart
|
Error::UnavailableShoppingCart
|
||||||
);
|
);
|
||||||
|
|
||||||
let db_order: model::AccountOrder = query_db!(
|
let db_order: model::Order = query_db!(
|
||||||
db,
|
db,
|
||||||
database_manager::CreateAccountOrder {
|
database_manager::CreateAccountOrder {
|
||||||
buyer_id: msg.buyer_id,
|
buyer_id: msg.buyer_id,
|
||||||
|
@ -4,7 +4,7 @@ use actix_web::{get, patch, post, HttpResponse};
|
|||||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
use config::SharedAppConfig;
|
use config::SharedAppConfig;
|
||||||
use database_manager::Database;
|
use database_manager::Database;
|
||||||
use model::{AccountId, AccountState, Address, Encrypt, PasswordConfirmation};
|
use model::{AccountAddress, AccountId, AccountState, Encrypt, PasswordConfirmation};
|
||||||
use token_manager::TokenManager;
|
use token_manager::TokenManager;
|
||||||
|
|
||||||
use crate::routes::admin::Error;
|
use crate::routes::admin::Error;
|
||||||
@ -112,7 +112,7 @@ pub async fn create_account(
|
|||||||
role: payload.role,
|
role: payload.role,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
let addresses: Vec<Address> = admin_send_db!(
|
let addresses: Vec<AccountAddress> = admin_send_db!(
|
||||||
db,
|
db,
|
||||||
database_manager::AccountAddresses {
|
database_manager::AccountAddresses {
|
||||||
account_id: account.id
|
account_id: account.id
|
||||||
|
@ -3,7 +3,7 @@ use actix_web::get;
|
|||||||
use actix_web::web::{Data, Json, ServiceConfig};
|
use actix_web::web::{Data, Json, ServiceConfig};
|
||||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
use database_manager::Database;
|
use database_manager::Database;
|
||||||
use model::api::AccountOrders;
|
use model::api::Orders;
|
||||||
use token_manager::TokenManager;
|
use token_manager::TokenManager;
|
||||||
|
|
||||||
use crate::routes::RequireUser;
|
use crate::routes::RequireUser;
|
||||||
@ -14,10 +14,10 @@ async fn orders(
|
|||||||
credentials: BearerAuth,
|
credentials: BearerAuth,
|
||||||
tm: Data<Addr<TokenManager>>,
|
tm: Data<Addr<TokenManager>>,
|
||||||
db: Data<Addr<Database>>,
|
db: Data<Addr<Database>>,
|
||||||
) -> routes::Result<Json<AccountOrders>> {
|
) -> routes::Result<Json<Orders>> {
|
||||||
credentials.require_admin(tm.into_inner()).await?;
|
credentials.require_admin(tm.into_inner()).await?;
|
||||||
|
|
||||||
let orders: Vec<model::AccountOrder> = admin_send_db!(&db, database_manager::AllAccountOrders);
|
let orders: Vec<model::Order> = admin_send_db!(&db, database_manager::AllAccountOrders);
|
||||||
let items: Vec<model::OrderItem> = admin_send_db!(db, database_manager::AllOrderItems);
|
let items: Vec<model::OrderItem> = admin_send_db!(db, database_manager::AllOrderItems);
|
||||||
|
|
||||||
Ok(Json((orders, items).into()))
|
Ok(Json((orders, items).into()))
|
||||||
|
@ -6,6 +6,7 @@ use actix_web_httpauth::extractors::bearer::BearerAuth;
|
|||||||
use cart_manager::{query_cart, CartManager};
|
use cart_manager::{query_cart, CartManager};
|
||||||
use database_manager::{query_db, Database};
|
use database_manager::{query_db, Database};
|
||||||
use model::api;
|
use model::api;
|
||||||
|
use order_manager::{query_order, OrderManager};
|
||||||
use payment_manager::{query_pay, PaymentManager};
|
use payment_manager::{query_pay, PaymentManager};
|
||||||
use token_manager::TokenManager;
|
use token_manager::TokenManager;
|
||||||
|
|
||||||
@ -234,6 +235,7 @@ pub(crate) async fn create_order(
|
|||||||
tm: Data<Addr<TokenManager>>,
|
tm: Data<Addr<TokenManager>>,
|
||||||
credentials: BearerAuth,
|
credentials: BearerAuth,
|
||||||
payment: Data<Addr<PaymentManager>>,
|
payment: Data<Addr<PaymentManager>>,
|
||||||
|
order: Data<Addr<OrderManager>>,
|
||||||
) -> routes::Result<HttpResponse> {
|
) -> routes::Result<HttpResponse> {
|
||||||
let account_id = credentials
|
let account_id = credentials
|
||||||
.require_user(tm.into_inner())
|
.require_user(tm.into_inner())
|
||||||
@ -248,6 +250,7 @@ pub(crate) async fn create_order(
|
|||||||
language,
|
language,
|
||||||
charge_client,
|
charge_client,
|
||||||
currency,
|
currency,
|
||||||
|
address,
|
||||||
} = payload;
|
} = payload;
|
||||||
let ip = match req.peer_addr() {
|
let ip = match req.peer_addr() {
|
||||||
Some(ip) => ip,
|
Some(ip) => ip,
|
||||||
@ -272,6 +275,32 @@ pub(crate) async fn create_order(
|
|||||||
routes::Error::Public(PublicError::DatabaseConnection)
|
routes::Error::Public(PublicError::DatabaseConnection)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
query_order!(
|
||||||
|
order,
|
||||||
|
order_manager::CreateAccountOrder {
|
||||||
|
account_id,
|
||||||
|
create_address: address.map(
|
||||||
|
|model::api::CreateOrderAddress {
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
street,
|
||||||
|
city,
|
||||||
|
country,
|
||||||
|
zip,
|
||||||
|
}| order_manager::CreateOrderAddress {
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
street,
|
||||||
|
city,
|
||||||
|
country,
|
||||||
|
zip,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
PublicError::PlaceOrder,
|
||||||
|
PublicError::DatabaseConnection
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(HttpResponse::SeeOther()
|
Ok(HttpResponse::SeeOther()
|
||||||
.append_header(("Location", redirect_uri.as_str()))
|
.append_header(("Location", redirect_uri.as_str()))
|
||||||
.body(format!(
|
.body(format!(
|
||||||
|
@ -48,6 +48,8 @@ macro_rules! public_send_db {
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
ApiV1(#[from] api_v1::Error),
|
ApiV1(#[from] api_v1::Error),
|
||||||
|
#[error("Failed to place order")]
|
||||||
|
PlaceOrder,
|
||||||
#[error("Internal server error")]
|
#[error("Internal server error")]
|
||||||
DatabaseConnection,
|
DatabaseConnection,
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
|
19
migrations/20220523090806_change_orders.sql
Normal file
19
migrations/20220523090806_change_orders.sql
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
ALTER TABLE account_orders
|
||||||
|
ALTER COLUMN buyer_id DROP NOT NULL;
|
||||||
|
|
||||||
|
CREATE TABLE order_addresses
|
||||||
|
(
|
||||||
|
id serial not null primary key unique,
|
||||||
|
name text not null,
|
||||||
|
email text not null,
|
||||||
|
street text not null,
|
||||||
|
city text not null,
|
||||||
|
country text not null,
|
||||||
|
zip text not null
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE account_orders
|
||||||
|
ADD COLUMN address_id INT REFERENCES order_addresses (id);
|
||||||
|
|
||||||
|
ALTER TABLE account_orders
|
||||||
|
RENAME TO orders;
|
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE account_addresses
|
||||||
|
ADD COLUMN is_default BOOLEAN NOT NULL DEFAULT false;
|
@ -28,10 +28,10 @@ pub struct Account {
|
|||||||
pub role: Role,
|
pub role: Role,
|
||||||
pub customer_id: uuid::Uuid,
|
pub customer_id: uuid::Uuid,
|
||||||
pub state: AccountState,
|
pub state: AccountState,
|
||||||
pub addresses: Vec<Address>,
|
pub addresses: Vec<AccountAddress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(FullAccount, Vec<crate::Address>)> for Account {
|
impl From<(FullAccount, Vec<crate::AccountAddress>)> for Account {
|
||||||
fn from(
|
fn from(
|
||||||
(
|
(
|
||||||
FullAccount {
|
FullAccount {
|
||||||
@ -44,7 +44,7 @@ impl From<(FullAccount, Vec<crate::Address>)> for Account {
|
|||||||
state,
|
state,
|
||||||
},
|
},
|
||||||
addresses,
|
addresses,
|
||||||
): (FullAccount, Vec<crate::Address>),
|
): (FullAccount, Vec<crate::AccountAddress>),
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
@ -59,7 +59,7 @@ impl From<(FullAccount, Vec<crate::Address>)> for Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Address {
|
pub struct AccountAddress {
|
||||||
pub id: AddressId,
|
pub id: AddressId,
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub email: Email,
|
pub email: Email,
|
||||||
@ -68,11 +68,12 @@ pub struct Address {
|
|||||||
pub country: Country,
|
pub country: Country,
|
||||||
pub zip: Zip,
|
pub zip: Zip,
|
||||||
pub account_id: AccountId,
|
pub account_id: AccountId,
|
||||||
|
pub is_default: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<crate::Address> for Address {
|
impl From<crate::AccountAddress> for AccountAddress {
|
||||||
fn from(
|
fn from(
|
||||||
crate::Address {
|
crate::AccountAddress {
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
@ -81,7 +82,8 @@ impl From<crate::Address> for Address {
|
|||||||
country,
|
country,
|
||||||
zip,
|
zip,
|
||||||
account_id,
|
account_id,
|
||||||
}: crate::Address,
|
is_default,
|
||||||
|
}: crate::AccountAddress,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
@ -92,6 +94,7 @@ impl From<crate::Address> for Address {
|
|||||||
country,
|
country,
|
||||||
zip,
|
zip,
|
||||||
account_id,
|
account_id,
|
||||||
|
is_default,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,15 +102,15 @@ impl From<crate::Address> for Address {
|
|||||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct AccountOrders(pub Vec<AccountOrder>);
|
pub struct Orders(pub Vec<Order>);
|
||||||
|
|
||||||
impl From<(Vec<crate::AccountOrder>, Vec<crate::OrderItem>)> for AccountOrders {
|
impl From<(Vec<crate::Order>, Vec<crate::OrderItem>)> for Orders {
|
||||||
fn from((orders, mut items): (Vec<crate::AccountOrder>, Vec<crate::OrderItem>)) -> Self {
|
fn from((orders, mut items): (Vec<crate::Order>, Vec<crate::OrderItem>)) -> Self {
|
||||||
Self(
|
Self(
|
||||||
orders
|
orders
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(
|
.map(
|
||||||
|crate::AccountOrder {
|
|crate::Order {
|
||||||
id,
|
id,
|
||||||
buyer_id,
|
buyer_id,
|
||||||
status,
|
status,
|
||||||
@ -115,14 +118,16 @@ impl From<(Vec<crate::AccountOrder>, Vec<crate::OrderItem>)> for AccountOrders {
|
|||||||
order_ext_id: _,
|
order_ext_id: _,
|
||||||
service_order_id: _,
|
service_order_id: _,
|
||||||
checkout_notes,
|
checkout_notes,
|
||||||
|
address_id,
|
||||||
}| {
|
}| {
|
||||||
AccountOrder {
|
Order {
|
||||||
id,
|
id,
|
||||||
buyer_id,
|
buyer_id,
|
||||||
status,
|
status,
|
||||||
order_id,
|
order_id,
|
||||||
items: items.drain_filter(|item| item.order_id == id).collect(),
|
items: items.drain_filter(|item| item.order_id == id).collect(),
|
||||||
checkout_notes,
|
checkout_notes,
|
||||||
|
address_id,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -131,10 +136,10 @@ impl From<(Vec<crate::AccountOrder>, Vec<crate::OrderItem>)> for AccountOrders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(crate::AccountOrder, Vec<crate::OrderItem>)> for AccountOrder {
|
impl From<(crate::Order, Vec<crate::OrderItem>)> for Order {
|
||||||
fn from(
|
fn from(
|
||||||
(
|
(
|
||||||
crate::AccountOrder {
|
crate::Order {
|
||||||
id,
|
id,
|
||||||
buyer_id,
|
buyer_id,
|
||||||
status,
|
status,
|
||||||
@ -142,30 +147,33 @@ impl From<(crate::AccountOrder, Vec<crate::OrderItem>)> for AccountOrder {
|
|||||||
order_ext_id: _,
|
order_ext_id: _,
|
||||||
service_order_id: _,
|
service_order_id: _,
|
||||||
checkout_notes,
|
checkout_notes,
|
||||||
|
address_id,
|
||||||
},
|
},
|
||||||
mut items,
|
mut items,
|
||||||
): (crate::AccountOrder, Vec<crate::OrderItem>),
|
): (crate::Order, Vec<crate::OrderItem>),
|
||||||
) -> Self {
|
) -> Self {
|
||||||
AccountOrder {
|
Order {
|
||||||
id,
|
id,
|
||||||
buyer_id,
|
buyer_id,
|
||||||
status,
|
status,
|
||||||
order_id,
|
order_id,
|
||||||
items: items.drain_filter(|item| item.order_id == id).collect(),
|
items: items.drain_filter(|item| item.order_id == id).collect(),
|
||||||
checkout_notes,
|
checkout_notes,
|
||||||
|
address_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct AccountOrder {
|
pub struct Order {
|
||||||
pub id: crate::AccountOrderId,
|
pub id: crate::OrderId,
|
||||||
pub buyer_id: crate::AccountId,
|
pub buyer_id: crate::AccountId,
|
||||||
pub status: crate::OrderStatus,
|
pub status: crate::OrderStatus,
|
||||||
pub order_id: Option<crate::OrderId>,
|
pub order_id: Option<crate::ExtOrderId>,
|
||||||
pub items: Vec<crate::OrderItem>,
|
pub items: Vec<crate::OrderItem>,
|
||||||
pub checkout_notes: Option<String>,
|
pub checkout_notes: Option<String>,
|
||||||
|
pub address_id: OrderAddressId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||||
@ -436,6 +444,7 @@ pub struct CreateOrderInput {
|
|||||||
pub charge_client: bool,
|
pub charge_client: bool,
|
||||||
/// User currency
|
/// User currency
|
||||||
pub currency: String,
|
pub currency: String,
|
||||||
|
pub address: Option<CreateOrderAddress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -482,6 +491,27 @@ pub struct SearchRequest {
|
|||||||
pub lang: String,
|
pub lang: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct CreateOrderAddress {
|
||||||
|
pub name: Name,
|
||||||
|
pub email: Email,
|
||||||
|
pub street: Street,
|
||||||
|
pub city: City,
|
||||||
|
pub country: Country,
|
||||||
|
pub zip: Zip,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct UpdateOrderAddress {
|
||||||
|
pub id: OrderAddressId,
|
||||||
|
pub name: Name,
|
||||||
|
pub email: Email,
|
||||||
|
pub street: Street,
|
||||||
|
pub city: City,
|
||||||
|
pub country: Country,
|
||||||
|
pub zip: Zip,
|
||||||
|
}
|
||||||
|
|
||||||
pub mod admin {
|
pub mod admin {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -342,7 +342,7 @@ impl Login {
|
|||||||
|
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||||
#[derive(Serialize, Debug, Clone, Deref, DerefMut, From, Display)]
|
#[derive(Serialize, Debug, Clone, Default, Deref, DerefMut, From, Display)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Email(String);
|
pub struct Email(String);
|
||||||
|
|
||||||
@ -843,42 +843,51 @@ pub struct Stock {
|
|||||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Display, Deref)]
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Display, Deref)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct AccountOrderId(RecordId);
|
pub struct OrderAddressId(RecordId);
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||||
|
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||||
|
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Display, Deref)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct OrderId(RecordId);
|
||||||
|
|
||||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Display, Deref)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Display, Deref)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct OrderId(String);
|
pub struct ExtOrderId(String);
|
||||||
|
|
||||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct AccountOrder {
|
pub struct Order {
|
||||||
pub id: AccountOrderId,
|
pub id: OrderId,
|
||||||
pub buyer_id: AccountId,
|
pub buyer_id: AccountId,
|
||||||
pub status: OrderStatus,
|
pub status: OrderStatus,
|
||||||
pub order_id: Option<OrderId>,
|
pub order_id: Option<ExtOrderId>,
|
||||||
pub order_ext_id: uuid::Uuid,
|
pub order_ext_id: uuid::Uuid,
|
||||||
pub service_order_id: Option<String>,
|
pub service_order_id: Option<String>,
|
||||||
pub checkout_notes: Option<String>,
|
pub checkout_notes: Option<String>,
|
||||||
|
pub address_id: OrderAddressId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct PublicAccountOrder {
|
pub struct PublicAccountOrder {
|
||||||
pub id: AccountOrderId,
|
pub id: OrderId,
|
||||||
pub buyer_id: AccountId,
|
pub buyer_id: AccountId,
|
||||||
pub status: OrderStatus,
|
pub status: OrderStatus,
|
||||||
pub order_id: Option<OrderId>,
|
pub order_id: Option<ExtOrderId>,
|
||||||
pub checkout_notes: String,
|
pub checkout_notes: String,
|
||||||
|
pub address_id: OrderAddressId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AccountOrder> for PublicAccountOrder {
|
impl From<Order> for PublicAccountOrder {
|
||||||
fn from(
|
fn from(
|
||||||
AccountOrder {
|
Order {
|
||||||
id,
|
id,
|
||||||
buyer_id,
|
buyer_id,
|
||||||
status,
|
status,
|
||||||
@ -886,7 +895,8 @@ impl From<AccountOrder> for PublicAccountOrder {
|
|||||||
order_ext_id: _,
|
order_ext_id: _,
|
||||||
service_order_id: _,
|
service_order_id: _,
|
||||||
checkout_notes,
|
checkout_notes,
|
||||||
}: AccountOrder,
|
address_id,
|
||||||
|
}: Order,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
@ -894,6 +904,7 @@ impl From<AccountOrder> for PublicAccountOrder {
|
|||||||
status,
|
status,
|
||||||
order_id,
|
order_id,
|
||||||
checkout_notes: checkout_notes.unwrap_or_default(),
|
checkout_notes: checkout_notes.unwrap_or_default(),
|
||||||
|
address_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -910,7 +921,7 @@ pub struct OrderItemId(pub RecordId);
|
|||||||
pub struct OrderItem {
|
pub struct OrderItem {
|
||||||
pub id: OrderItemId,
|
pub id: OrderItemId,
|
||||||
pub product_id: ProductId,
|
pub product_id: ProductId,
|
||||||
pub order_id: AccountOrderId,
|
pub order_id: OrderId,
|
||||||
pub quantity: Quantity,
|
pub quantity: Quantity,
|
||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
}
|
}
|
||||||
@ -1128,7 +1139,7 @@ pub struct AddressId(RecordId);
|
|||||||
|
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Deref, DerefMut, From, Display)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, Deref, DerefMut, From, Display)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Name(String);
|
pub struct Name(String);
|
||||||
|
|
||||||
@ -1140,7 +1151,7 @@ impl Name {
|
|||||||
|
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Deref, DerefMut, From, Display)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, Deref, DerefMut, From, Display)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Street(String);
|
pub struct Street(String);
|
||||||
|
|
||||||
@ -1152,7 +1163,7 @@ impl Street {
|
|||||||
|
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Deref, DerefMut, From, Display)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, Deref, DerefMut, From, Display)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct City(String);
|
pub struct City(String);
|
||||||
|
|
||||||
@ -1164,7 +1175,7 @@ impl City {
|
|||||||
|
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Deref, DerefMut, From, Display)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, Deref, DerefMut, From, Display)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Country(String);
|
pub struct Country(String);
|
||||||
|
|
||||||
@ -1176,7 +1187,7 @@ impl Country {
|
|||||||
|
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Deref, DerefMut, From, Display)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, Deref, DerefMut, From, Display)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Zip(String);
|
pub struct Zip(String);
|
||||||
|
|
||||||
@ -1189,7 +1200,7 @@ impl Zip {
|
|||||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Address {
|
pub struct AccountAddress {
|
||||||
pub id: AddressId,
|
pub id: AddressId,
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub email: Email,
|
pub email: Email,
|
||||||
@ -1198,4 +1209,18 @@ pub struct Address {
|
|||||||
pub country: Country,
|
pub country: Country,
|
||||||
pub zip: Zip,
|
pub zip: Zip,
|
||||||
pub account_id: AccountId,
|
pub account_id: AccountId,
|
||||||
|
pub is_default: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||||
|
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct OrderAddress {
|
||||||
|
pub id: OrderAddressId,
|
||||||
|
pub name: Name,
|
||||||
|
pub email: Email,
|
||||||
|
pub street: Street,
|
||||||
|
pub city: City,
|
||||||
|
pub country: Country,
|
||||||
|
pub zip: Zip,
|
||||||
}
|
}
|
||||||
|
@ -117,3 +117,24 @@ pub async fn update_cart(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn place_order(access_token: AccessTokenString) -> NetRes<String> {
|
||||||
|
let input = model::api::CreateOrderInput {
|
||||||
|
email: "".to_string(),
|
||||||
|
phone: "".to_string(),
|
||||||
|
first_name: "".to_string(),
|
||||||
|
last_name: "".to_string(),
|
||||||
|
language: "".to_string(),
|
||||||
|
charge_client: false,
|
||||||
|
currency: "".to_string(),
|
||||||
|
address: None,
|
||||||
|
};
|
||||||
|
perform(
|
||||||
|
Request::new("/api/v1/order")
|
||||||
|
.method(Method::Post)
|
||||||
|
.header(Header::bearer(access_token.as_str()))
|
||||||
|
.json(&input)
|
||||||
|
.map_err(NetRes::Http)?,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use seed::prelude::*;
|
use seed::prelude::*;
|
||||||
use seed::*;
|
use seed::*;
|
||||||
|
|
||||||
@ -7,11 +9,28 @@ use crate::NetRes;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CheckoutMsg {
|
pub enum CheckoutMsg {
|
||||||
ProductsFetched(NetRes<model::api::Products>),
|
ProductsFetched(NetRes<model::api::Products>),
|
||||||
|
AddressNameChanged(String),
|
||||||
|
AddressEmailChanged(String),
|
||||||
|
AddressStreetChanged(String),
|
||||||
|
AddressCityChanged(String),
|
||||||
|
AddressCountryChanged(String),
|
||||||
|
AddressZipChanged(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct AddressForm {
|
||||||
|
pub name: model::Name,
|
||||||
|
pub email: model::Email,
|
||||||
|
pub street: model::Street,
|
||||||
|
pub city: model::City,
|
||||||
|
pub country: model::Country,
|
||||||
|
pub zip: model::Zip,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CheckoutPage {
|
pub struct CheckoutPage {
|
||||||
pub products: Products,
|
pub products: Products,
|
||||||
|
pub address: AddressForm,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(_url: Url, orders: &mut impl Orders<crate::Msg>) -> CheckoutPage {
|
pub fn init(_url: Url, orders: &mut impl Orders<crate::Msg>) -> CheckoutPage {
|
||||||
@ -22,6 +41,7 @@ pub fn init(_url: Url, orders: &mut impl Orders<crate::Msg>) -> CheckoutPage {
|
|||||||
});
|
});
|
||||||
CheckoutPage {
|
CheckoutPage {
|
||||||
products: Default::default(),
|
products: Default::default(),
|
||||||
|
address: AddressForm::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,6 +58,26 @@ pub fn update(msg: CheckoutMsg, model: &mut CheckoutPage, _orders: &mut impl Ord
|
|||||||
CheckoutMsg::ProductsFetched(NetRes::Http(e)) => {
|
CheckoutMsg::ProductsFetched(NetRes::Http(e)) => {
|
||||||
seed::error!("fetch product http", e);
|
seed::error!("fetch product http", e);
|
||||||
}
|
}
|
||||||
|
CheckoutMsg::AddressNameChanged(value) => {
|
||||||
|
model.address.name = model::Name::new(value);
|
||||||
|
}
|
||||||
|
CheckoutMsg::AddressEmailChanged(value) => {
|
||||||
|
if let Ok(value) = model::Email::from_str(&value) {
|
||||||
|
model.address.email = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CheckoutMsg::AddressStreetChanged(value) => {
|
||||||
|
model.address.street = model::Street::new(value);
|
||||||
|
}
|
||||||
|
CheckoutMsg::AddressCityChanged(value) => {
|
||||||
|
model.address.city = model::City::new(value);
|
||||||
|
}
|
||||||
|
CheckoutMsg::AddressCountryChanged(value) => {
|
||||||
|
model.address.country = model::Country::new(value);
|
||||||
|
}
|
||||||
|
CheckoutMsg::AddressZipChanged(value) => {
|
||||||
|
model.address.zip = model::Zip::new(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +222,8 @@ mod right_side {
|
|||||||
use seed::prelude::*;
|
use seed::prelude::*;
|
||||||
use seed::*;
|
use seed::*;
|
||||||
|
|
||||||
use crate::pages::public::checkout::CheckoutPage;
|
use crate::pages::public::checkout::{CheckoutMsg, CheckoutPage};
|
||||||
|
use crate::pages::public::sign_up::RegisterMsg;
|
||||||
use crate::shopping_cart::CartMsg;
|
use crate::shopping_cart::CartMsg;
|
||||||
use crate::Msg;
|
use crate::Msg;
|
||||||
|
|
||||||
@ -204,11 +245,15 @@ mod right_side {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contact(model: &crate::Model, _page: &CheckoutPage) -> Node<Msg> {
|
fn contact(model: &crate::Model, page: &CheckoutPage) -> Node<Msg> {
|
||||||
if model.shared.me.is_some() {
|
match &model.shared.me {
|
||||||
// TODO: Display user addresses
|
Some(me) if me.addresses.is_empty() => contact_form(model, page),
|
||||||
return empty![];
|
Some(_me) => empty![],
|
||||||
|
None => contact_form(model, page),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contact_form(model: &crate::Model, _page: &CheckoutPage) -> Node<Msg> {
|
||||||
div![
|
div![
|
||||||
C!["w-full mx-auto rounded-lg bg-white border border-gray-200 p-3 text-gray-800 font-light mb-6"],
|
C!["w-full mx-auto rounded-lg bg-white border border-gray-200 p-3 text-gray-800 font-light mb-6"],
|
||||||
form![
|
form![
|
||||||
@ -219,19 +264,13 @@ mod right_side {
|
|||||||
div![
|
div![
|
||||||
C!["mb-3"],
|
C!["mb-3"],
|
||||||
div![
|
div![
|
||||||
input![
|
address_input(model, "client-name", "text", "Name", CheckoutMsg::AddressNameChanged),
|
||||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
|
||||||
attrs![At::Id => "client-name", At::Placeholder => model.i18n.t("Name")]
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
div![
|
div![
|
||||||
C!["mb-3"],
|
C!["mb-3"],
|
||||||
div![
|
div![
|
||||||
input![
|
address_input(model, "client-email", "email", "E-Mail", CheckoutMsg::AddressEmailChanged),
|
||||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
|
||||||
attrs![At::Id => "client-email", At::Type => "email", At::Placeholder => model.i18n.t("E-Mail")]
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
div![
|
div![
|
||||||
@ -240,36 +279,47 @@ mod right_side {
|
|||||||
],
|
],
|
||||||
div![
|
div![
|
||||||
C!["mb-3"],
|
C!["mb-3"],
|
||||||
input![
|
address_input(model, "client-street", "text", "Street", CheckoutMsg::AddressStreetChanged),
|
||||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
|
||||||
attrs![At::Id => "client-street", At::Placeholder => model.i18n.t("Street")]
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
div![
|
div![
|
||||||
C!["mb-3"],
|
C!["mb-3"],
|
||||||
input![
|
address_input(model, "client-city", "text", "City", CheckoutMsg::AddressCityChanged),
|
||||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
|
||||||
attrs![At::Id => "client-city", At::Placeholder => model.i18n.t("City")]
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
div![
|
div![
|
||||||
C!["mb-3 inline-block w-1/2 pr-1"],
|
C!["mb-3 inline-block w-1/2 pr-1"],
|
||||||
input![
|
address_input(model, "client-country", "text", "Country", CheckoutMsg::AddressCountryChanged),
|
||||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
|
||||||
attrs![At::Id => "client-country", At::Placeholder => model.i18n.t("Country")]
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
div![
|
div![
|
||||||
C!["mb-3 inline-block -mx-1 pl-1 w-1/2"],
|
C!["mb-3 inline-block -mx-1 pl-1 w-1/2"],
|
||||||
input![
|
address_input(model, "client-zip", "text", "Zip", CheckoutMsg::AddressZipChanged),
|
||||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
|
||||||
attrs![At::Id => "client-zip", At::Placeholder => model.i18n.t("Zip")]
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn address_input<F>(
|
||||||
|
model: &crate::Model,
|
||||||
|
id: &str,
|
||||||
|
ty: &str,
|
||||||
|
label: &'static str,
|
||||||
|
msg: F,
|
||||||
|
) -> Node<Msg>
|
||||||
|
where
|
||||||
|
F: Clone + 'static + Fn(String) -> CheckoutMsg,
|
||||||
|
{
|
||||||
|
let handler = ev(Ev::Change, move |ev| {
|
||||||
|
ev.prevent_default();
|
||||||
|
let target = ev.target()?;
|
||||||
|
let input = seed::to_input(&target);
|
||||||
|
Some(crate::Msg::from(msg(input.value())))
|
||||||
|
});
|
||||||
|
input![
|
||||||
|
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
||||||
|
attrs![At::Id => id, At::Type => ty, At::Placeholder => model.i18n.t(label), At::Required => true],
|
||||||
|
handler,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
fn pay_now(model: &crate::Model) -> Node<Msg> {
|
fn pay_now(model: &crate::Model) -> Node<Msg> {
|
||||||
div![
|
div![
|
||||||
button![
|
button![
|
||||||
@ -292,13 +342,7 @@ mod right_side {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn pay_u(model: &crate::Model) -> Node<Msg> {
|
fn pay_u(model: &crate::Model) -> Node<Msg> {
|
||||||
payment_input(
|
payment_input(model, PaymentMethod::PayU, "pay_u", "PayU", pay_u_icon())
|
||||||
model,
|
|
||||||
model::PaymentMethod::PayU,
|
|
||||||
"pay_u",
|
|
||||||
"PayU",
|
|
||||||
pay_u_icon(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pay_u_icon() -> Node<Msg> {
|
fn pay_u_icon() -> Node<Msg> {
|
||||||
@ -329,7 +373,7 @@ mod right_side {
|
|||||||
fn pay_on_spot(model: &crate::Model) -> Node<Msg> {
|
fn pay_on_spot(model: &crate::Model) -> Node<Msg> {
|
||||||
payment_input(
|
payment_input(
|
||||||
model,
|
model,
|
||||||
model::PaymentMethod::PaymentOnTheSpot,
|
PaymentMethod::PaymentOnTheSpot,
|
||||||
"pay_on_spot",
|
"pay_on_spot",
|
||||||
"Pay on spot",
|
"Pay on spot",
|
||||||
pay_in_spot_icon(),
|
pay_in_spot_icon(),
|
||||||
@ -397,7 +441,7 @@ mod right_side {
|
|||||||
|
|
||||||
fn payment_input(
|
fn payment_input(
|
||||||
model: &crate::Model,
|
model: &crate::Model,
|
||||||
method: model::PaymentMethod,
|
method: PaymentMethod,
|
||||||
name: &str,
|
name: &str,
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
icon: Node<Msg>,
|
icon: Node<Msg>,
|
||||||
|
@ -56,7 +56,6 @@ pub fn update(msg: RegisterMsg, model: &mut SignUpPage, orders: &mut impl Orders
|
|||||||
let email = model.email.clone();
|
let email = model.email.clone();
|
||||||
let login = model.login.clone();
|
let login = model.login.clone();
|
||||||
let password = model.password.clone();
|
let password = model.password.clone();
|
||||||
let password_confirmation = model.password_confirmation.clone();
|
|
||||||
|
|
||||||
orders.perform_cmd(async move {
|
orders.perform_cmd(async move {
|
||||||
crate::Msg::Public(
|
crate::Msg::Public(
|
||||||
@ -65,7 +64,6 @@ pub fn update(msg: RegisterMsg, model: &mut SignUpPage, orders: &mut impl Orders
|
|||||||
email,
|
email,
|
||||||
login,
|
login,
|
||||||
password,
|
password,
|
||||||
password_confirmation,
|
|
||||||
})
|
})
|
||||||
.await,
|
.await,
|
||||||
)
|
)
|
||||||
@ -84,16 +82,15 @@ pub fn update(msg: RegisterMsg, model: &mut SignUpPage, orders: &mut impl Orders
|
|||||||
|
|
||||||
pub fn view(model: &crate::Model, page: &SignUpPage) -> Node<crate::Msg> {
|
pub fn view(model: &crate::Model, page: &SignUpPage) -> Node<crate::Msg> {
|
||||||
let home = Urls::new(&model.url).home();
|
let home = Urls::new(&model.url).home();
|
||||||
let logo = model
|
let logo = model.logo.as_deref().map_or_else(
|
||||||
.logo
|
|| a![attrs![At::Href => home], "Logo"],
|
||||||
.as_deref()
|
|src| {
|
||||||
.map(|src| {
|
|
||||||
a![
|
a![
|
||||||
attrs![At::Href => home],
|
attrs![At::Href => home],
|
||||||
img![attrs![At::Src => src], C!["m-auto"]]
|
img![attrs![At::Src => src], C!["m-auto"]]
|
||||||
]
|
]
|
||||||
})
|
},
|
||||||
.unwrap_or_else(|| a![attrs![At::Href => home], "Logo"]);
|
);
|
||||||
|
|
||||||
let content = div![
|
let content = div![
|
||||||
C!["relative flex flex-col justify-center overflow-hidden"],
|
C!["relative flex flex-col justify-center overflow-hidden"],
|
||||||
@ -129,51 +126,54 @@ fn sign_up_form(model: &crate::Model, _page: &SignUpPage) -> Node<RegisterMsg> {
|
|||||||
RegisterMsg::Submit
|
RegisterMsg::Submit
|
||||||
}),
|
}),
|
||||||
div![
|
div![
|
||||||
label![attrs!["for" => "email"], C!["block text-sm text-indigo-800"], model.i18n.t("E-Mail")],
|
label![attrs![At::For => "email"], C!["block text-sm text-indigo-800"], model.i18n.t("E-Mail")],
|
||||||
input![
|
input![
|
||||||
attrs!["type" => "email", "id" => "email"],
|
attrs![At::Type => "email", At::Id => "email"],
|
||||||
C!["block w-full px-4 py-2 mt-2 text-indigo-700 bg-white border rounded-md focus:border-indigo-400 focus:ring-indigo-300 focus:outline-none focus:ring focus:ring-opacity-40"],
|
C!["block w-full px-4 py-2 mt-2 text-indigo-700 bg-white border rounded-md focus:border-indigo-400 focus:ring-indigo-300 focus:outline-none focus:ring focus:ring-opacity-40"],
|
||||||
ev(Ev::Change, |ev| {
|
ev(Ev::Change, |ev| {
|
||||||
ev.stop_propagation();
|
ev.stop_propagation();
|
||||||
ev.target().map(|target| seed::to_input(&target).value()).map(RegisterMsg::EmailChanged)
|
ev.target().map(|target| seed::to_input(&target).value()).map(RegisterMsg::EmailChanged)
|
||||||
})
|
}),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
div![
|
div![
|
||||||
label![attrs!["for" => "login"], C!["block text-sm text-indigo-800"], model.i18n.t("Login")],
|
label![attrs![At::For => "login"], C!["block text-sm text-indigo-800"], model.i18n.t("Login")],
|
||||||
input![
|
input![
|
||||||
attrs!["type" => "text", "id" => "login"],
|
attrs![At::Type => "text", At::Id => "login"],
|
||||||
C!["block w-full px-4 py-2 mt-2 text-indigo-700 bg-white border rounded-md focus:border-indigo-400 focus:ring-indigo-300 focus:outline-none focus:ring focus:ring-opacity-40"],
|
C!["block w-full px-4 py-2 mt-2 text-indigo-700 bg-white border rounded-md focus:border-indigo-400 focus:ring-indigo-300 focus:outline-none focus:ring focus:ring-opacity-40"],
|
||||||
ev(Ev::Change, |ev| {
|
ev(Ev::Change, |ev| {
|
||||||
ev.stop_propagation();
|
ev.stop_propagation();
|
||||||
ev.target().map(|target| seed::to_input(&target).value()).map(RegisterMsg::LoginChanged)
|
ev.target().map(|target| seed::to_input(&target).value()).map(RegisterMsg::LoginChanged)
|
||||||
})
|
}),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
div![
|
div![
|
||||||
C!["mt-4"],
|
C!["mt-4"],
|
||||||
div![
|
div![
|
||||||
label![attrs!["for" => "password"], C!["block text-sm text-indigo-800"], model.i18n.t("Password")],
|
label![attrs![At::For => "password"], C!["block text-sm text-indigo-800"], model.i18n.t("Password")],
|
||||||
input![attrs!["type" => "password", "id" => "password"], C!["block w-full px-4 py-2 mt-2 text-indigo-700 bg-white border rounded-md focus:border-indigo-400 focus:ring-indigo-300 focus:outline-none focus:ring focus:ring-opacity-40"]],
|
input![
|
||||||
|
attrs![At::Type => "password", At::Id => "password"],
|
||||||
|
C!["block w-full px-4 py-2 mt-2 text-indigo-700 bg-white border rounded-md focus:border-indigo-400 focus:ring-indigo-300 focus:outline-none focus:ring focus:ring-opacity-40"],
|
||||||
ev(Ev::Change, |ev| {
|
ev(Ev::Change, |ev| {
|
||||||
ev.stop_propagation();
|
ev.stop_propagation();
|
||||||
ev.target().map(|target| seed::to_input(&target).value()).map(RegisterMsg::PasswordChanged)
|
ev.target().map(|target| seed::to_input(&target).value()).map(RegisterMsg::PasswordChanged)
|
||||||
})
|
})
|
||||||
|
]
|
||||||
],
|
],
|
||||||
div![
|
div![
|
||||||
label![
|
label![
|
||||||
attrs!["for" => "password-confirmation"],
|
attrs![At::For => "password-confirmation"],
|
||||||
C!["block text-sm text-indigo-800"],
|
C!["block text-sm text-indigo-800"],
|
||||||
model.i18n.t("Password confirmation")
|
model.i18n.t("Password confirmation")
|
||||||
],
|
],
|
||||||
input![
|
input![
|
||||||
attrs!["type" => "password", "id" => "password-confirmation"],
|
attrs![At::Type => "password", At::Id => "password-confirmation"],
|
||||||
C!["block w-full px-4 py-2 mt-2 text-indigo-700 bg-white border rounded-md focus:border-indigo-400 focus:ring-indigo-300 focus:outline-none focus:ring focus:ring-opacity-40"]
|
C!["block w-full px-4 py-2 mt-2 text-indigo-700 bg-white border rounded-md focus:border-indigo-400 focus:ring-indigo-300 focus:outline-none focus:ring focus:ring-opacity-40"],
|
||||||
],
|
|
||||||
ev(Ev::Change, |ev| {
|
ev(Ev::Change, |ev| {
|
||||||
ev.stop_propagation();
|
ev.stop_propagation();
|
||||||
ev.target().map(|target| seed::to_input(&target).value()).map(RegisterMsg::PasswordConfirmationChanged)
|
ev.target().map(|target| seed::to_input(&target).value()).map(RegisterMsg::PasswordConfirmationChanged)
|
||||||
})
|
}),
|
||||||
|
]
|
||||||
],
|
],
|
||||||
div![
|
div![
|
||||||
C!["mt-6"],
|
C!["mt-6"],
|
||||||
|
Loading…
Reference in New Issue
Block a user