feat: backend MVP (auth/rbac, catalog, orders, fulfillment, invoices) + specs + scaffolds

This commit is contained in:
Chengdong Zhang
2026-09-17 12:43:22 +08:00
commit dc9fd31c5e
96 changed files with 17550 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
use rust_decimal::prelude::*;
use rust_decimal::Decimal;
use crate::error::ApiError;
use crate::models::Currency;
/// Convert integer minor units between currencies via base rates.
/// rate_to_base = units of currency per 1 unit of base. Rounds half-up
/// to whole minor units of the target currency.
pub fn convert_minor(amount_minor: i64, from: &Currency, to: &Currency) -> Result<i64, ApiError> {
if amount_minor < 0 {
return Err(ApiError::BadRequest("amount_minor must be >= 0".into()));
}
let amount = Decimal::from(amount_minor);
let from_scale = Decimal::from(10i64.pow(from.exponent as u32));
let to_scale = Decimal::from(10i64.pow(to.exponent as u32));
// minor(from) -> major(from) -> major(base) -> minor(to)
let base_major = amount / from_scale / from.rate_to_base;
let target = (base_major * to.rate_to_base * to_scale).round_dp(0);
target
.to_i64()
.ok_or_else(|| ApiError::BadRequest("amount out of range".into()))
}