add: transfer refunds

This commit is contained in:
trisua 2025-08-09 14:00:46 -04:00
parent 95cb889080
commit fdaae8d977
10 changed files with 340 additions and 26 deletions

View file

@ -9,5 +9,6 @@ CREATE TABLE IF NOT EXISTS products (
price INT NOT NULL,
stock INT NOT NULL,
single_use INT NOT NULL,
data TEXT NOT NULL
data TEXT NOT NULL,
uploads TEXT NOT NULL
)

View file

@ -54,10 +54,14 @@ ADD COLUMN IF NOT EXISTS single_use INT DEFAULT 1;
ALTER TABLE transfers
ADD COLUMN IF NOT EXISTS source TEXT DEFAULT '"General"';
-- products single_use
-- products data
ALTER TABLE products
ADD COLUMN IF NOT EXISTS data TEXT DEFAULT '';
-- users applied_configurations
ALTER TABLE users
ADD COLUMN IF NOT EXISTS applied_configurations TEXT DEFAULT '[]';
-- products uploads
ALTER TABLE products
ADD COLUMN IF NOT EXISTS uploads TEXT DEFAULT '{}';

View file

@ -2,6 +2,7 @@ use crate::model::{
auth::User,
economy::{
CoinTransfer, CoinTransferMethod, CoinTransferSource, Product, ProductFulfillmentMethod,
ProductUploads,
},
mail::Letter,
permissions::FinePermission,
@ -25,6 +26,7 @@ impl DataManager {
stock: get!(x->8(i32)),
single_use: get!(x->9(i32)) as i8 == 1,
data: get!(x->10(String)),
uploads: serde_json::from_str(&get!(x->11(String))).unwrap(),
}
}
@ -107,7 +109,7 @@ impl DataManager {
let res = execute!(
&conn,
"INSERT INTO products VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
"INSERT INTO products VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
params![
&(data.id as i64),
&(data.created as i64),
@ -120,6 +122,7 @@ impl DataManager {
&(data.stock as i32),
&{ if data.single_use { 1 } else { 0 } },
&data.data,
&serde_json::to_string(&data.uploads).unwrap(),
]
);
@ -271,6 +274,7 @@ If your product is a purchase of goods or services, please be sure to fulfill th
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_data(&str)@get_product_by_id:FinePermission::MANAGE_USERS; -> "UPDATE products SET data = $1 WHERE id = $2" --cache-key-tmpl="atto.product:{}");
auto_method!(update_product_uploads(ProductUploads)@get_product_by_id:FinePermission::MANAGE_USERS; -> "UPDATE products SET uploads = $1 WHERE id = $2" --serde --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

@ -1,8 +1,8 @@
use std::collections::HashMap;
use crate::model::{
Error, Result,
economy::{CoinTransferMethod, Product, CoinTransfer},
auth::{Notification, User},
economy::{CoinTransfer, CoinTransferMethod, CoinTransferSource, Product},
Error, Result,
};
use crate::{auto_method, DataManager};
use oiseau::{cache::Cache, execute, get, params, query_row, query_rows, PostgresRow};
@ -28,16 +28,13 @@ impl DataManager {
pub async fn fill_transfers(
&self,
list: Vec<CoinTransfer>,
) -> Result<Vec<(usize, usize, i32, User, User, Option<Product>, bool)>> {
) -> Result<Vec<(User, User, Option<Product>, CoinTransfer)>> {
let mut out = Vec::new();
let mut seen_users: HashMap<usize, User> = HashMap::new();
let mut seen_products: HashMap<usize, Product> = HashMap::new();
for transfer in list {
out.push((
transfer.id,
transfer.created,
transfer.amount,
if let Some(user) = seen_users.get(&transfer.sender) {
user.to_owned()
} else {
@ -68,7 +65,7 @@ impl DataManager {
}
}
},
transfer.is_pending,
transfer,
));
}
@ -163,6 +160,16 @@ impl DataManager {
}
self.update_user_coins(receiver.id, receiver.coins).await?;
// handle refund notification
if data.source == CoinTransferSource::Refund {
self.create_notification(Notification::new(
"A coin refund has been issued to your account!".to_string(),
"You've been issued a refund for a prior purchase. The product will remain in your account, but your coins have been returned.".to_string(),
receiver.id,
))
.await?;
}
} else {
// we haven't applied the transfer, so this must be pending
data.is_pending = true;