add: single_use products

This commit is contained in:
trisua 2025-08-08 14:17:40 -04:00
parent 7fbc732290
commit e5e6d5cddb
12 changed files with 149 additions and 7 deletions

View file

@ -7,5 +7,6 @@ CREATE TABLE IF NOT EXISTS products (
method TEXT NOT NULL,
on_sale INT NOT NULL,
price INT NOT NULL,
stock INT NOT NULL
stock INT NOT NULL,
single_use INT NOT NULL
)

View file

@ -45,3 +45,7 @@ ADD COLUMN IF NOT EXISTS data TEXT DEFAULT '"Null"';
-- users checkouts
ALTER TABLE users
ADD COLUMN IF NOT EXISTS checkouts TEXT DEFAULT '[]';
-- products single_use
ALTER TABLE products
ADD COLUMN IF NOT EXISTS single_use INT DEFAULT 1;

View file

@ -21,6 +21,7 @@ impl DataManager {
on_sale: get!(x->6(i32)) as i8 == 1,
price: get!(x->7(i32)),
stock: get!(x->8(i32)),
single_use: get!(x->9(i32)) as i8 == 1,
}
}
@ -103,7 +104,7 @@ impl DataManager {
let res = execute!(
&conn,
"INSERT INTO products VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
"INSERT INTO products VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
params![
&(data.id as i64),
&(data.created as i64),
@ -113,7 +114,8 @@ impl DataManager {
&serde_json::to_string(&data.method).unwrap(),
&{ if data.on_sale { 1 } else { 0 } },
&data.price,
&(data.stock as i32)
&(data.stock as i32),
&{ if data.single_use { 1 } else { 0 } },
]
);
@ -131,6 +133,22 @@ impl DataManager {
customer: &mut User,
) -> Result<CoinTransfer> {
let product = self.get_product_by_id(product).await?;
// handle single_use product
if product.single_use {
if self
.get_transfer_by_sender_method(
customer.id,
CoinTransferMethod::Purchase(product.id),
)
.await
.is_ok()
{
return Err(Error::MiscError("You already own this product".to_string()));
}
}
// ...
let mut transfer = CoinTransfer::new(
customer.id,
product.owner,
@ -231,6 +249,7 @@ If your product is a purchase of goods or services, please be sure to fulfill th
auto_method!(update_product_price(i32)@get_product_by_id:FinePermission::MANAGE_USERS; -> "UPDATE products SET price = $1 WHERE id = $2" --cache-key-tmpl="atto.product:{}");
auto_method!(update_product_on_sale(i32)@get_product_by_id:FinePermission::MANAGE_USERS; -> "UPDATE products SET on_sale = $1 WHERE id = $2" --cache-key-tmpl="atto.product:{}");
auto_method!(update_product_method(ProductFulfillmentMethod)@get_product_by_id:FinePermission::MANAGE_USERS; -> "UPDATE products SET method = $1 WHERE id = $2" --serde --cache-key-tmpl="atto.product:{}");
auto_method!(update_product_single_use(i32)@get_product_by_id:FinePermission::MANAGE_USERS; -> "UPDATE products SET single_use = $1 WHERE id = $2" --cache-key-tmpl="atto.product:{}");
auto_method!(update_product_stock(i32)@get_product_by_id:FinePermission::MANAGE_USERS; -> "UPDATE products SET stock = $1 WHERE id = $2" --cache-key-tmpl="atto.product:{}");
auto_method!(incr_product_stock() -> "UPDATE products SET stock = stock + 1 WHERE id = $1" --cache-key-tmpl="atto.product:{}" --incr);

View file

@ -5,7 +5,7 @@ use crate::model::{
auth::{Notification, User},
};
use crate::{auto_method, DataManager};
use oiseau::{cache::Cache, execute, get, params, query_rows, PostgresRow};
use oiseau::{cache::Cache, execute, get, params, query_row, query_rows, PostgresRow};
impl DataManager {
/// Get a [`CoinTransfer`] from an SQL row.
@ -101,6 +101,35 @@ impl DataManager {
Ok(res.unwrap())
}
/// Get a transfer by user and method.
///
/// # Arguments
/// * `id` - the ID of the user to fetch transfers for
/// * `method` - the transfer method
pub async fn get_transfer_by_sender_method(
&self,
id: usize,
method: CoinTransferMethod,
) -> Result<CoinTransfer> {
let conn = match self.0.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
let res = query_row!(
&conn,
"SELECT * FROM transfers WHERE sender = $1 AND method = $2 LIMIT 1",
params![&(id as i64), &serde_json::to_string(&method).unwrap()],
|x| { Ok(Self::get_transfer_from_row(x)) }
);
if res.is_err() {
return Err(Error::GeneralNotFound("transfer".to_string()));
}
Ok(res.unwrap())
}
/// Create a new transfer in the database.
///
/// # Arguments