| 1 | //! Allowances and the usage entry that consumes them commit together. |
| 2 | use worker::Result; |
| 3 | use crate::{Billing, credits::Eligible}; |
| 4 | |
| 5 | pub(crate) const PLAN: &str = "INSERT INTO allowance_use (kind, scope, month, used) |
| 6 | SELECT 'plan_credit', workspace, ?2, credit_micros FROM ledger WHERE id=?1 AND credit_micros > 0 |
| 7 | ON CONFLICT(kind, scope, month) DO UPDATE SET used=used+excluded.used"; |
| 8 | pub(crate) const TRIAL: &str = "UPDATE trial_grants SET used_micros=used_micros+ |
| 9 | (SELECT trial_micros FROM ledger WHERE id=?1) WHERE workspace=(SELECT workspace FROM ledger WHERE id=?1)"; |
| 10 | pub(crate) const GIFT: &str = "INSERT INTO allowance_use (kind, scope, month, used) |
| 11 | SELECT 'complimentary', workspace, ?2, gift_micros FROM ledger WHERE id=?1 AND gift_micros > 0 |
| 12 | ON CONFLICT(kind, scope, month) DO UPDATE SET used=used+excluded.used"; |
| 13 | pub(crate) const OSS_POOL: &str = "INSERT INTO allowance_use (kind, scope, month, used) |
| 14 | SELECT 'oss_pool', '', ?2, oss_micros FROM ledger WHERE id=?1 AND oss_micros > 0 |
| 15 | ON CONFLICT(kind, scope, month) DO UPDATE SET used=used+excluded.used"; |
| 16 | pub(crate) const OSS_REPO: &str = "INSERT INTO allowance_use (kind, scope, month, used) |
| 17 | SELECT 'oss_repo', ?3, ?2, oss_micros FROM ledger WHERE id=?1 AND oss_micros > 0 |
| 18 | ON CONFLICT(kind, scope, month) DO UPDATE SET used=used+excluded.used"; |
| 19 | |
| 20 | impl Billing { |
| 21 | pub(crate) async fn posted_allocation(&self, reference: &str) -> Result<(i64, crate::credits::Drawn)> { |
| 22 | #[derive(serde::Deserialize)] |
| 23 | struct Row { amount_micros: i64, credit_micros: i64, gift_micros: i64, trial_micros: i64, oss_micros: i64, given_micros: i64 } |
| 24 | let row = self.db.prepare("SELECT amount_micros, credit_micros, gift_micros, trial_micros, oss_micros, given_micros FROM ledger WHERE reference = ?") |
| 25 | .bind(&[reference.into()])?.first::<Row>(None).await? |
| 26 | .ok_or_else(|| worker::Error::RustError("Posted usage is missing".into()))?; |
| 27 | Ok((-row.amount_micros, crate::credits::Drawn { |
| 28 | credit: row.credit_micros, gift: row.gift_micros, trial: row.trial_micros, oss: row.oss_micros, given: row.given_micros, |
| 29 | })) |
| 30 | } |
| 31 | |
| 32 | pub(crate) async fn allocation_statements(&self, id: &str, workspace: &str, month: &str, eligible: &Eligible, plan: bool) |
| 33 | -> Result<Vec<worker::D1PreparedStatement>> { |
| 34 | let repo = eligible.repo.as_deref().unwrap_or("").to_lowercase(); |
| 35 | let public = !repo.is_empty() && self.is_public(&repo).await; |
| 36 | let repo_cap = if public { self.oss_repo_cap(workspace).await? } else { 0 }; |
| 37 | Ok(vec![ |
| 38 | self.db.prepare(include_str!("allocate_usage.sql")).bind(&[ |
| 39 | id.into(), workspace.into(), month.into(), |
| 40 | (if plan { self.plans.plan_included_micros } else { 0 } as f64).into(), |
| 41 | i32::from(eligible.trial).into(), repo.as_str().into(), |
| 42 | (if public { self.plans.oss_pool_micros } else { 0 } as f64).into(), (repo_cap as f64).into(), |
| 43 | i32::from(eligible.cover_rest && !plan).into(), |
| 44 | ])?, |
| 45 | self.db.prepare(PLAN).bind(&[id.into(), month.into()])?, |
| 46 | self.db.prepare(GIFT).bind(&[id.into(), month.into()])?, |
| 47 | self.db.prepare(TRIAL).bind(&[id.into()])?, |
| 48 | self.db.prepare(OSS_POOL).bind(&[id.into(), month.into()])?, |
| 49 | self.db.prepare(OSS_REPO).bind(&[id.into(), month.into(), repo.into()])?, |
| 50 | ]) |
| 51 | } |
| 52 | } |