adjustments on the net rev pool

This commit is contained in:
aecsocket
2026-08-26 15:08:56 +01:00
parent 9b85f3bcdf
commit d513c6c756
22 changed files with 203 additions and 111 deletions
@@ -76,12 +76,10 @@
"name": "delphi_severity",
"kind": {
"Enum": [
"hidden",
"low",
"medium",
"high",
"severe",
"malware"
"severe"
]
}
}
@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "\n\t\t\t\tINSERT INTO payout_periods (\n\t\t\t\t\tperiod,\n\t\t\t\t\traw_actual_aditude_revenue_usd,\n\t\t\t\t\trevenue_adjustments\n\t\t\t\t)\n\t\t\t\tSELECT period, 0, '[]'::jsonb\n\t\t\t\tFROM UNNEST($1::date[]) AS period\n\t\t\t\t",
"describe": {
"columns": [],
"parameters": {
"Left": [
"DateArray"
]
},
"nullable": []
},
"hash": "093388c38a65cd30c1ad75b265cd70d17d47cf60ac42bf55855fda8053fcdf49"
}
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n\t\t\tSELECT\n\t\t\t\tpayout_periods.period,\n\t\t\t\tpayout_periods.raw_actual_aditude_revenue_usd,\n\t\t\t\tpayout_periods.adjustments AS \"adjustments: Json<Vec<Adjustment>>\",\n\t\t\t\tactive_run.payload AS \"active_run_payload: Json<PayoutRunPayload>\",\n\t\t\t\tactive_run.execute_at AS active_run_execute_at,\n\t\t\t\tEXISTS (\n\t\t\t\t\tSELECT 1\n\t\t\t\t\tFROM payout_runs\n\t\t\t\t\tWHERE payout_runs.period = payout_periods.period\n\t\t\t\t\t\tAND payout_runs.status = 'succeeded'\n\t\t\t\t) AS \"has_succeeded_run!\"\n\t\t\tFROM payout_periods\n\t\t\tLEFT JOIN payout_runs active_run\n\t\t\t\tON active_run.period = payout_periods.period\n\t\t\t\tAND active_run.status IN ('scheduled', 'running')\n\t\t\tWHERE payout_periods.period = ANY($1)\n\t\t\t",
"query": "\n\t\t\tSELECT\n\t\t\t\tpayout_periods.period,\n\t\t\t\tpayout_periods.raw_actual_aditude_revenue_usd,\n\t\t\t\tpayout_periods.revenue_adjustments AS \"revenue_adjustments: Json<Vec<RevenueAdjustment>>\",\n\t\t\t\tactive_run.payload AS \"active_run_payload: Json<PayoutRunPayload>\",\n\t\t\t\tactive_run.execute_at AS active_run_execute_at,\n\t\t\t\tEXISTS (\n\t\t\t\t\tSELECT 1\n\t\t\t\t\tFROM payout_runs\n\t\t\t\t\tWHERE payout_runs.period = payout_periods.period\n\t\t\t\t\t\tAND payout_runs.status = 'succeeded'\n\t\t\t\t) AS \"has_succeeded_run!\"\n\t\t\tFROM payout_periods\n\t\t\tLEFT JOIN payout_runs active_run\n\t\t\t\tON active_run.period = payout_periods.period\n\t\t\t\tAND active_run.status IN ('scheduled', 'running')\n\t\t\tWHERE payout_periods.period = ANY($1)\n\t\t\t",
"describe": {
"columns": [
{
@@ -15,7 +15,7 @@
},
{
"ordinal": 2,
"name": "adjustments: Json<Vec<Adjustment>>",
"name": "revenue_adjustments: Json<Vec<RevenueAdjustment>>",
"type_info": "Jsonb"
},
{
@@ -48,5 +48,5 @@
null
]
},
"hash": "56e3d8b2961562ef5a8be80f71814743f0cd95a7c26e4d0dd5b354d2ac3bbaf1"
"hash": "0f8bb9a3ade9d28ee2d91e10b3d06a03bccfe57a196c62b27b5334525f32e0ca"
}
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO payout_periods (\n period,\n raw_actual_aditude_revenue_usd,\n adjustments\n )\n VALUES ($1, 100, '[]'::jsonb)\n ",
"query": "\n INSERT INTO payout_periods (\n period,\n raw_actual_aditude_revenue_usd,\n revenue_adjustments\n )\n VALUES ($1, 100, '[]'::jsonb)\n ",
"describe": {
"columns": [],
"parameters": {
@@ -10,5 +10,5 @@
},
"nullable": []
},
"hash": "291b7a70925bdd5d748048fa08dea1901ce8c2b584ec86921efe6c4d2d279b29"
"hash": "12c7d361e554ea8d9216c420cdf6bb79c460a4dc4217a656ab4d2e11c714ea56"
}
@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO payout_periods (\n period,\n raw_actual_aditude_revenue_usd,\n revenue_adjustments\n )\n VALUES ($1, $2, '[]'::jsonb)\n ON CONFLICT (period) DO UPDATE SET\n raw_actual_aditude_revenue_usd =\n EXCLUDED.raw_actual_aditude_revenue_usd\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Date",
"Numeric"
]
},
"nullable": []
},
"hash": "1437d67c3b497ae0ae5d3229ae4cda5dbe8c2d451fe264b7392c8d8cbec73a58"
}
@@ -76,12 +76,10 @@
"name": "delphi_severity",
"kind": {
"Enum": [
"hidden",
"low",
"medium",
"high",
"severe",
"malware"
"severe"
]
}
}
@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO payout_periods (\n period,\n raw_actual_aditude_revenue_usd,\n adjustments\n )\n VALUES ($1, $2, '[]'::jsonb)\n ON CONFLICT (period) DO UPDATE SET\n raw_actual_aditude_revenue_usd =\n EXCLUDED.raw_actual_aditude_revenue_usd\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Date",
"Numeric"
]
},
"nullable": []
},
"hash": "4798f2780f28c4329d96af43e4902d97972573a8f4954ceab284b8d8989641a7"
}
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO payout_runs (\n id,\n period,\n payload,\n status,\n started_at,\n started_by,\n execute_at,\n processing_started_at,\n finished_at\n )\n VALUES (\n $1,\n $2,\n '{\"raw_actual_revenue_usd\":100,\"adjustments\":[]}'::jsonb,\n 'succeeded',\n $3,\n $4,\n $5,\n $5,\n $6\n )\n ",
"query": "\n INSERT INTO payout_runs (\n id,\n period,\n payload,\n status,\n started_at,\n started_by,\n execute_at,\n processing_started_at,\n finished_at\n )\n VALUES (\n $1,\n $2,\n '{\"raw_actual_revenue_usd\":100,\"revenue_adjustments\":[]}'::jsonb,\n 'succeeded',\n $3,\n $4,\n $5,\n $5,\n $6\n )\n ",
"describe": {
"columns": [],
"parameters": {
@@ -15,5 +15,5 @@
},
"nullable": []
},
"hash": "c30e4f1aad62a3a8c278340f051e91476a4fbe3d7774ef9fed712929a5816ecb"
"hash": "49e243559bf389d86da849dcd20108c1cb8cc0633a6c90ab662543295af068c9"
}
@@ -41,12 +41,10 @@
"name": "delphi_severity",
"kind": {
"Enum": [
"hidden",
"low",
"medium",
"high",
"severe",
"malware"
"severe"
]
}
}
@@ -26,12 +26,10 @@
"name": "delphi_severity",
"kind": {
"Enum": [
"hidden",
"low",
"medium",
"high",
"severe",
"malware"
"severe"
]
}
}
@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n\t\t\t\tINSERT INTO payout_periods (\n\t\t\t\t\tperiod,\n\t\t\t\t\traw_actual_aditude_revenue_usd,\n\t\t\t\t\tadjustments\n\t\t\t\t)\n\t\t\t\tSELECT period, 0, '[]'::jsonb\n\t\t\t\tFROM UNNEST($1::date[]) AS period\n\t\t\t\t",
"describe": {
"columns": [],
"parameters": {
"Left": [
"DateArray"
]
},
"nullable": []
},
"hash": "8d6cbbb4963ff2d0cc36c2c91e147fda4c6a6d69ec96bc4e531dc695e4e92f62"
}
@@ -22,12 +22,10 @@
"name": "delphi_severity",
"kind": {
"Enum": [
"hidden",
"low",
"medium",
"high",
"severe",
"malware"
"severe"
]
}
}
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n\t\tINSERT INTO payout_runs (\n\t\t\tid,\n\t\t\tperiod,\n\t\t\tpayload,\n\t\t\tstatus,\n\t\t\tstarted_at,\n\t\t\tstarted_by,\n\t\t\texecute_at\n\t\t)\n\t\tVALUES (\n\t\t\t$1,\n\t\t\t$2,\n\t\t\t'{\"raw_actual_revenue_usd\": 0, \"adjustments\": []}'::jsonb,\n\t\t\t$3,\n\t\t\tNOW(),\n\t\t\t$4,\n\t\t\tNOW()\n\t\t)\n\t\t",
"query": "\n\t\tINSERT INTO payout_runs (\n\t\t\tid,\n\t\t\tperiod,\n\t\t\tpayload,\n\t\t\tstatus,\n\t\t\tstarted_at,\n\t\t\tstarted_by,\n\t\t\texecute_at\n\t\t)\n\t\tVALUES (\n\t\t\t$1,\n\t\t\t$2,\n\t\t\t'{\"raw_actual_revenue_usd\": 0, \"revenue_adjustments\": []}'::jsonb,\n\t\t\t$3,\n\t\t\tNOW(),\n\t\t\t$4,\n\t\t\tNOW()\n\t\t)\n\t\t",
"describe": {
"columns": [],
"parameters": {
@@ -13,5 +13,5 @@
},
"nullable": []
},
"hash": "0f4cefdd003516ad3260cd7b471fef35cf224e053933f4aec75f377fbdffbbcb"
"hash": "b40a175bcc4465b7c2793572528de6bd1f8e4923f879149d8559b9e620054c96"
}
@@ -19,12 +19,10 @@
"name": "delphi_severity",
"kind": {
"Enum": [
"hidden",
"low",
"medium",
"high",
"severe",
"malware"
"severe"
]
}
}
@@ -3,7 +3,7 @@ CREATE TABLE payout_periods (
CHECK (EXTRACT(DAY FROM period) = 1),
raw_actual_aditude_revenue_usd NUMERIC(40, 20) NOT NULL
CHECK (raw_actual_aditude_revenue_usd >= 0),
adjustments JSONB NOT NULL
revenue_adjustments JSONB NOT NULL
);
CREATE TABLE payout_period_days (
@@ -5,13 +5,13 @@ use rust_decimal::Decimal;
use sqlx::types::Json;
use super::DatabaseError;
use crate::queue::payout_run::{Adjustment, PayoutRunPayload};
use crate::queue::payout_run::{PayoutRunPayload, RevenueAdjustment};
#[derive(Debug, Clone)]
pub struct DBPayoutPeriod {
pub period: NaiveDate,
pub raw_actual_aditude_revenue_usd: Decimal,
pub adjustments: Vec<Adjustment>,
pub revenue_adjustments: Vec<RevenueAdjustment>,
pub active_run_payload: Option<PayoutRunPayload>,
pub active_run_execute_at: Option<DateTime<Utc>>,
pub days: Vec<DBPayoutPeriodDay>,
@@ -39,7 +39,7 @@ impl DBPayoutPeriod {
SELECT
payout_periods.period,
payout_periods.raw_actual_aditude_revenue_usd,
payout_periods.adjustments AS "adjustments: Json<Vec<Adjustment>>",
payout_periods.revenue_adjustments AS "revenue_adjustments: Json<Vec<RevenueAdjustment>>",
active_run.payload AS "active_run_payload: Json<PayoutRunPayload>",
active_run.execute_at AS active_run_execute_at,
EXISTS (
@@ -68,7 +68,7 @@ impl DBPayoutPeriod {
period: row.period,
raw_actual_aditude_revenue_usd: row
.raw_actual_aditude_revenue_usd,
adjustments: row.adjustments.0,
revenue_adjustments: row.revenue_adjustments.0,
active_run_payload: row
.active_run_payload
.map(|payload| payload.0),
+111 -17
View File
@@ -14,8 +14,9 @@
//! money, so this is closer to NET 75. During this period, the month (payout
//! period) is in an _in review_ state.
//! - Once we receive the money from the provider, an admin enters the total
//! amount we've received into the web UI, adds any manual adjustments (for
//! campaigns outside of our ad provider's), and starts a payout run.
//! amount we've received into the web UI, adds any manual revenue
//! adjustments (for campaigns outside of our ad provider's), and starts a
//! payout run.
//! - The payout run is not immediately executed; there is a period of time in
//! which it can still be cancelled.
//! - Once the payout run is executed, we calculate the exact revenue
@@ -50,11 +51,12 @@
//! ```
//! - (fees stay the same, since they're based on impressions, not revenue)
//! - (variance is ignored, since that's purely an estimation value)
//! - `actual.net_revenue_usd`: raw actual revenue - fees
//! - Revenue adjustments are split evenly across the days in the period.
//! - `actual.net_revenue_usd`: raw actual revenue - fees + revenue adjustment
//! - `actual.(platform|creator)_net_revenue_usd`: same logic as estimated,
//! but using the net actual revenue
//! - Manual adjustments are stored separately on the payout period and applied
//! on top of its actual distribution.
//! - Manual revenue adjustments are stored on the payout period and applied to
//! its actual distribution before the platform/creator split.
//!
//! ## Variance
//!
@@ -63,10 +65,12 @@
//! epoch date)
//! - the decimal fraction of variance to apply
use chrono::NaiveDate;
use chrono::{Months, NaiveDate};
use rust_decimal::{Decimal, dec};
use serde::{Deserialize, Serialize};
use crate::util::time::YearMonth;
mod estimate;
pub use estimate::*;
@@ -77,13 +81,13 @@ pub struct PayoutRunPayload {
/// Actual raw revenue received from the ad provider for the period.
#[serde(with = "rust_decimal::serde::float")]
pub raw_actual_revenue_usd: Decimal,
/// Manual adjustments to apply on top of actual revenue.
pub adjustments: Vec<Adjustment>,
/// Manual revenue adjustments to apply on top of actual revenue.
pub revenue_adjustments: Vec<RevenueAdjustment>,
}
/// Manual admin-input adjustment to a payout period.
/// Manual admin-input revenue adjustment to a payout period.
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct Adjustment {
pub struct RevenueAdjustment {
/// Total value of the adjustment.
#[serde(with = "rust_decimal::serde::float")]
pub amount_usd: Decimal,
@@ -113,8 +117,14 @@ pub struct DayDistribution {
/// For non-estimates (actual revenue values), this is zero.
#[serde(with = "rust_decimal::serde::float")]
pub variance_usd: Decimal,
/// Revenue added after fees and variance, before the platform/creator
/// split.
///
/// This is zero for estimated revenue.
#[serde(with = "rust_decimal::serde::float")]
pub revenue_adjustment_usd: Decimal,
/// Total net revenue that we earned;
/// `raw_revenue - fees - variance`.
/// `raw_revenue - fees - variance + revenue_adjustment`.
#[serde(with = "rust_decimal::serde::float")]
pub net_revenue_usd: Decimal,
/// How much of the net revenue goes to the platform.
@@ -153,6 +163,7 @@ pub fn distribution_for_day(
date: NaiveDate,
raw_revenue_usd: Decimal,
impressions: u128,
revenue_adjustment_usd: Decimal,
variances: &PayoutVariances,
) -> DayDistribution {
let fees_usd = {
@@ -168,16 +179,17 @@ pub fn distribution_for_day(
.unwrap_or(variances.default_frac);
let variance_usd = raw_revenue_usd * variance_frac;
let net_estimated_revenue_usd = raw_revenue_usd - fees_usd - variance_usd;
let net_revenue_usd =
raw_revenue_usd - fees_usd - variance_usd + revenue_adjustment_usd;
DayDistribution {
raw_revenue_usd,
fees_usd,
variance_usd,
net_revenue_usd: net_estimated_revenue_usd,
platform_net_revenue_usd: net_estimated_revenue_usd
* PLATFORM_REVENUE_SPLIT,
creator_net_revenue_usd: net_estimated_revenue_usd
revenue_adjustment_usd,
net_revenue_usd,
platform_net_revenue_usd: net_revenue_usd * PLATFORM_REVENUE_SPLIT,
creator_net_revenue_usd: net_revenue_usd
* (dec!(1) - PLATFORM_REVENUE_SPLIT),
}
}
@@ -186,6 +198,9 @@ pub fn distribution_for_day(
#[derive(Debug, Clone, Copy)]
pub struct ActualDistributionFlow {
share: Decimal,
period: YearMonth,
daily_revenue_adjustment_usd: Decimal,
final_day_revenue_adjustment_usd: Decimal,
}
/// Start a flow for computing the actual revenue distribution of a payout
@@ -202,12 +217,17 @@ pub struct ActualDistributionFlow {
/// ```
///
/// If the period's estimated revenue is zero, the share is `1`.
/// Revenue adjustments are split evenly across every calendar day. Any
/// decimal rounding remainder is placed on the final day so the daily values
/// sum exactly to the period adjustment.
///
/// We use a type-state-ish pattern here to ensure that the same flow is used
/// for each day in a period.
pub fn compute_actual_distribution_flow(
period: YearMonth,
raw_estimated_revenue_usd: Decimal,
raw_actual_revenue_usd: Decimal,
revenue_adjustment_usd: Decimal,
) -> ActualDistributionFlow {
let share = if raw_estimated_revenue_usd.is_zero() {
Decimal::ONE
@@ -215,7 +235,21 @@ pub fn compute_actual_distribution_flow(
raw_actual_revenue_usd / raw_estimated_revenue_usd
};
ActualDistributionFlow { share }
let next_period = period
.date()
.checked_add_months(Months::new(1))
.expect("a payout period must have a following month");
let day_count = Decimal::from((next_period - period.date()).num_days());
let daily_revenue_adjustment_usd = revenue_adjustment_usd / day_count;
let final_day_revenue_adjustment_usd = revenue_adjustment_usd
- daily_revenue_adjustment_usd * (day_count - Decimal::ONE);
ActualDistributionFlow {
share,
period,
daily_revenue_adjustment_usd,
final_day_revenue_adjustment_usd,
}
}
impl ActualDistributionFlow {
@@ -226,12 +260,72 @@ impl ActualDistributionFlow {
raw_estimated_revenue_usd: Decimal,
impressions: u128,
) -> DayDistribution {
debug_assert_eq!(YearMonth::from_day1(date), self.period);
let next_period = self
.period
.date()
.checked_add_months(Months::new(1))
.expect("a payout period must have a following month");
let revenue_adjustment_usd = if date.succ_opt() == Some(next_period) {
self.final_day_revenue_adjustment_usd
} else {
self.daily_revenue_adjustment_usd
};
distribution_for_day(
date,
raw_estimated_revenue_usd * self.share,
impressions,
revenue_adjustment_usd,
// actual rev distribution always has no variance
&PayoutVariances::ZERO,
)
}
}
#[cfg(test)]
mod tests {
use chrono::Days;
use super::*;
#[test]
fn splits_revenue_adjustment_across_period_exactly() {
let period = YearMonth::from_year_month(2026, 4).unwrap();
let adjustment = dec!(500);
let flow = compute_actual_distribution_flow(
period,
dec!(30),
dec!(30),
adjustment,
);
let mut date = period.date();
let mut distributions = Vec::new();
for _ in 0..30 {
distributions.push(flow.distribution_for_day(date, dec!(1), 0));
date = date.checked_add_days(Days::new(1)).unwrap();
}
assert_eq!(
distributions
.iter()
.map(|day| day.revenue_adjustment_usd)
.sum::<Decimal>(),
adjustment,
);
assert_eq!(
distributions
.iter()
.map(|day| day.net_revenue_usd)
.sum::<Decimal>(),
dec!(530),
);
assert!(
distributions[..29]
.iter()
.all(|day| day.revenue_adjustment_usd
== distributions[0].revenue_adjustment_usd),
);
}
}
@@ -4,7 +4,7 @@ use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use xredis::RedisPool;
use super::{Adjustment, PayoutRunDay, PayoutRunPayload};
use super::{PayoutRunDay, PayoutRunPayload, RevenueAdjustment};
use crate::{
auth::{
AuthenticationError, get_user_from_headers, two_factor::verify_2fa_code,
@@ -45,7 +45,7 @@ pub struct StartPayoutRun {
pub ignore_totp: bool,
#[serde(with = "rust_decimal::serde::float")]
pub raw_actual_revenue_usd: Decimal,
pub adjustments: Vec<Adjustment>,
pub revenue_adjustments: Vec<RevenueAdjustment>,
}
#[derive(Debug, Serialize, utoipa::ToSchema)]
@@ -58,10 +58,11 @@ pub struct StartPayoutRunResponse {
pub struct CalculatePayoutRunResponse {
pub period: YearMonth,
pub days: Vec<PayoutRunDay>,
/// Sum of all adjustments that would be applied on top of actual revenue.
/// Sum of all revenue adjustments that would be applied on top of actual
/// revenue.
#[serde(with = "rust_decimal::serde::float")]
pub total_adjustments: Decimal,
pub adjustments: Vec<Adjustment>,
pub total_revenue_adjustment_usd: Decimal,
pub revenue_adjustments: Vec<RevenueAdjustment>,
}
/// Calculate a payout run without scheduling it.
@@ -148,9 +149,16 @@ pub async fn calculate_run(
.iter()
.map(|day| day.raw_estimated_revenue_usd)
.sum();
let total_revenue_adjustment_usd = body
.revenue_adjustments
.iter()
.map(|adjustment| adjustment.amount_usd)
.sum();
let actual_flow = compute_actual_distribution_flow(
body.period,
total_estimated_revenue_usd,
body.raw_actual_revenue_usd,
total_revenue_adjustment_usd,
);
let days = estimate
.days
@@ -161,6 +169,7 @@ pub async fn calculate_run(
day.date,
day.raw_estimated_revenue_usd,
day.impressions,
Decimal::ZERO,
&variances,
),
actual: Some(actual_flow.distribution_for_day(
@@ -170,17 +179,12 @@ pub async fn calculate_run(
)),
})
.collect();
let total_adjustments = body
.adjustments
.iter()
.map(|adjustment| adjustment.amount_usd)
.sum();
Ok(web::Json(CalculatePayoutRunResponse {
period: body.period,
days,
total_adjustments,
adjustments: body.adjustments,
total_revenue_adjustment_usd,
revenue_adjustments: body.revenue_adjustments,
}))
}
@@ -290,7 +294,7 @@ pub async fn start_run(
.wrap_failed_dependency_err("validating payout estimate")?;
let payload = PayoutRunPayload {
raw_actual_revenue_usd: body.raw_actual_revenue_usd,
adjustments: body.adjustments.clone(),
revenue_adjustments: body.revenue_adjustments.clone(),
};
let mut transaction = pool
@@ -303,7 +307,7 @@ pub async fn start_run(
INSERT INTO payout_periods (
period,
raw_actual_aditude_revenue_usd,
adjustments
revenue_adjustments
)
VALUES ($1, $2, '[]'::jsonb)
ON CONFLICT (period) DO UPDATE SET
@@ -6,7 +6,7 @@ use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use xredis::RedisPool;
use super::Adjustment;
use super::RevenueAdjustment;
use crate::{
auth::get_user_from_headers,
database::{
@@ -43,14 +43,15 @@ pub struct PayoutRunPeriod {
/// When the active payout run is scheduled to begin executing.
pub runs_at: Option<DateTime<Utc>>,
pub days: Vec<PayoutRunDay>,
/// Sum of all adjustments applied on top of actual revenue.
/// Sum of all revenue adjustments applied on top of actual revenue.
#[serde(with = "rust_decimal::serde::float")]
pub total_adjustments: Decimal,
/// Individual adjustments, including their admin-provided descriptions.
pub total_revenue_adjustment_usd: Decimal,
/// Individual revenue adjustments, including their admin-provided
/// descriptions.
///
/// Only visible to admins.
#[serde(skip_serializing_if = "Option::is_none")]
pub adjustments: Option<Vec<Adjustment>>,
pub revenue_adjustments: Option<Vec<RevenueAdjustment>>,
}
/// Has revenue been distributed for a specific payout period month yet?
@@ -200,25 +201,28 @@ pub async fn get_runs(
.get(&requested_period.date())
.filter(|period| !period.days.is_empty())
{
let period_adjustments =
let period_revenue_adjustments =
if let Some(payload) = &period.active_run_payload {
&payload.adjustments
&payload.revenue_adjustments
} else {
&period.adjustments
&period.revenue_adjustments
};
let total_adjustments = period_adjustments
let total_revenue_adjustment_usd = period_revenue_adjustments
.iter()
.map(|adjustment| adjustment.amount_usd)
.sum();
let adjustments = is_admin.then(|| period_adjustments.clone());
let revenue_adjustments =
is_admin.then(|| period_revenue_adjustments.clone());
let total_estimated_revenue_usd = period
.days
.iter()
.map(|day| day.raw_estimated_aditude_revenue_usd)
.sum();
let actual_flow = compute_actual_distribution_flow(
requested_period,
total_estimated_revenue_usd,
period.raw_actual_aditude_revenue_usd,
total_revenue_adjustment_usd,
);
let days = period
.days
@@ -235,6 +239,7 @@ pub async fn get_runs(
day.date,
day.raw_estimated_aditude_revenue_usd,
impressions,
Decimal::ZERO,
&variances,
),
actual: Some(actual_flow.distribution_for_day(
@@ -258,8 +263,8 @@ pub async fn get_runs(
status,
runs_at: period.active_run_execute_at,
days,
total_adjustments,
adjustments,
total_revenue_adjustment_usd,
revenue_adjustments,
})
} else {
let estimate = live_estimates
@@ -282,6 +287,7 @@ pub async fn get_runs(
day.date,
day.raw_estimated_revenue_usd,
day.impressions,
Decimal::ZERO,
&variances,
),
actual: None,
@@ -293,8 +299,8 @@ pub async fn get_runs(
status,
runs_at: None,
days,
total_adjustments: Decimal::ZERO,
adjustments: is_admin.then(Vec::new),
total_revenue_adjustment_usd: Decimal::ZERO,
revenue_adjustments: is_admin.then(Vec::new),
})
}
})
@@ -1,7 +1,7 @@
mod admin;
mod fetch;
pub use crate::queue::payout_run::{Adjustment, PayoutRunPayload};
pub use crate::queue::payout_run::{PayoutRunPayload, RevenueAdjustment};
pub use admin::*;
pub use fetch::*;
+2 -2
View File
@@ -54,7 +54,7 @@ async fn insert_succeeded_period(
INSERT INTO payout_periods (
period,
raw_actual_aditude_revenue_usd,
adjustments
revenue_adjustments
)
VALUES ($1, 100, '[]'::jsonb)
"#,
@@ -81,7 +81,7 @@ async fn insert_succeeded_period(
VALUES (
$1,
$2,
'{"raw_actual_revenue_usd":100,"adjustments":[]}'::jsonb,
'{"raw_actual_revenue_usd":100,"revenue_adjustments":[]}'::jsonb,
'succeeded',
$3,
$4,
+2 -2
View File
@@ -31,7 +31,7 @@ async fn insert_run(
VALUES (
$1,
$2,
'{"raw_actual_revenue_usd": 0, "adjustments": []}'::jsonb,
'{"raw_actual_revenue_usd": 0, "revenue_adjustments": []}'::jsonb,
$3,
NOW(),
$4,
@@ -118,7 +118,7 @@ async fn payout_run_lineage_and_active_run_constraints() {
INSERT INTO payout_periods (
period,
raw_actual_aditude_revenue_usd,
adjustments
revenue_adjustments
)
SELECT period, 0, '[]'::jsonb
FROM UNNEST($1::date[]) AS period