effective price is only highlighted if changed
This commit is contained in:
@@ -31,6 +31,7 @@ use crate::{
|
||||
};
|
||||
|
||||
const COMPANY: &str = "company";
|
||||
const BUSINESS_AUDIENCE: &str = "business";
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct PriceForm {
|
||||
@@ -117,8 +118,6 @@ async fn show(
|
||||
.all(&ctx.db)
|
||||
.await?;
|
||||
let assigned = assigned_profile_ids(&ctx, company.id).await?;
|
||||
let profile_name: HashMap<i32, String> =
|
||||
all_profiles.iter().map(|p| (p.id, p.name.clone())).collect();
|
||||
let profiles_json: Vec<serde_json::Value> = all_profiles
|
||||
.iter()
|
||||
.map(|p| {
|
||||
@@ -136,32 +135,30 @@ async fn show(
|
||||
.order_by_asc(products::Column::Name)
|
||||
.all(&ctx.db)
|
||||
.await?;
|
||||
// Two prices per product:
|
||||
// - the generic business price a freshly-registered company sees (business
|
||||
// baseline + business-audience profiles, no per-company deals), and
|
||||
// - this company's effective price (its negotiated price + assigned profiles).
|
||||
// The effective price is highlighted only when it differs from the generic one.
|
||||
let business = pricing::audience_price_many(&ctx, &list, BUSINESS_AUDIENCE).await?;
|
||||
let details = pricing::detail_many(&ctx, &list, Some(&company)).await?;
|
||||
|
||||
let rows: Vec<serde_json::Value> = list
|
||||
.iter()
|
||||
.zip(business.iter())
|
||||
.zip(details.iter())
|
||||
.map(|(product, d)| {
|
||||
let covering: Vec<serde_json::Value> = d
|
||||
.covering_profile_ids
|
||||
.iter()
|
||||
.map(|pid| json!({ "id": pid, "name": profile_name.get(pid) }))
|
||||
.collect();
|
||||
.map(|((product, b), d)| {
|
||||
json!({
|
||||
"product_id": product.id,
|
||||
"name": product.name,
|
||||
"currency": product.currency,
|
||||
"regular_price": format_price(d.regular_cents),
|
||||
"public_price": format_price(d.public_cents),
|
||||
"on_public_sale": product.on_sale(),
|
||||
"manual_price": d.manual_cents.map(format_price),
|
||||
"auto_price": d.auto_cents.map(format_price),
|
||||
"auto_profile_name": d.auto_profile_id.and_then(|pid| profile_name.get(&pid)),
|
||||
"auto_profile_id": d.auto_profile_id,
|
||||
"business_price": format_price(b.price_cents),
|
||||
"business_reduced": b.price_cents < d.regular_cents,
|
||||
"has_negotiated": d.manual_cents.is_some(),
|
||||
"collision": d.collision,
|
||||
"covering": covering,
|
||||
"effective_price": format_price(d.price_cents),
|
||||
"is_business": d.is_business,
|
||||
"effective_differs": d.price_cents != b.price_cents,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
@@ -179,6 +176,75 @@ async fn show(
|
||||
)
|
||||
}
|
||||
|
||||
/// Dedicated per-product page for the negotiated price (and, when two assigned
|
||||
/// profiles collide, the resolution selector). Mirrors the catalog "Set discount"
|
||||
/// page but for a single company.
|
||||
#[debug_handler]
|
||||
async fn price_edit(
|
||||
auth: auth::JWT,
|
||||
jar: CookieJar,
|
||||
ViewEngine(v): ViewEngine<TeraView>,
|
||||
Path((id, product_id)): Path<(i32, i32)>,
|
||||
Query(params): Query<HashMap<String, String>>,
|
||||
State(ctx): State<AppContext>,
|
||||
) -> Result<Response> {
|
||||
guard::current_admin(auth, &ctx).await?;
|
||||
let company = company_by_id(&ctx, id).await?;
|
||||
let product = products::Entity::find_by_id(product_id)
|
||||
.one(&ctx.db)
|
||||
.await?
|
||||
.ok_or_else(|| Error::NotFound)?;
|
||||
|
||||
let business =
|
||||
pricing::audience_price_many(&ctx, std::slice::from_ref(&product), BUSINESS_AUDIENCE)
|
||||
.await?;
|
||||
let business_cents = business[0].price_cents;
|
||||
let detail = pricing::detail_many(&ctx, std::slice::from_ref(&product), Some(&company)).await?;
|
||||
let d = &detail[0];
|
||||
|
||||
// Names for the covering profiles, used by the collision resolution selector.
|
||||
let covering: Vec<serde_json::Value> = if d.covering_profile_ids.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
let profiles = discount_profiles::Entity::find()
|
||||
.filter(discount_profiles::Column::Id.is_in(d.covering_profile_ids.clone()))
|
||||
.all(&ctx.db)
|
||||
.await?;
|
||||
let name: HashMap<i32, String> =
|
||||
profiles.iter().map(|p| (p.id, p.name.clone())).collect();
|
||||
d.covering_profile_ids
|
||||
.iter()
|
||||
.map(|pid| json!({ "id": pid, "name": name.get(pid) }))
|
||||
.collect()
|
||||
};
|
||||
|
||||
format::view(
|
||||
&v,
|
||||
"admin/customers/price_form.html",
|
||||
json!({
|
||||
"customer": { "id": company.id, "name": company.name },
|
||||
"product": {
|
||||
"id": product.id,
|
||||
"name": product.name,
|
||||
"currency": product.currency,
|
||||
"regular_price": format_price(d.regular_cents),
|
||||
"regular_cents": d.regular_cents,
|
||||
"business_price": format_price(business_cents),
|
||||
"business_reduced": business_cents < d.regular_cents,
|
||||
"effective_price": format_price(d.price_cents),
|
||||
"effective_differs": d.price_cents != business_cents,
|
||||
},
|
||||
"negotiated": d.manual_cents.map(format_price).unwrap_or_default(),
|
||||
"has_negotiated": d.manual_cents.is_some(),
|
||||
"collision": d.collision,
|
||||
"covering": covering,
|
||||
"auto_profile_id": d.auto_profile_id,
|
||||
"error": params.get("error"),
|
||||
"lang": current_lang(&jar),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
async fn set_price(
|
||||
auth: auth::JWT,
|
||||
@@ -199,7 +265,7 @@ async fn set_price(
|
||||
Ok(cents) if cents > 0 => cents,
|
||||
_ => {
|
||||
return format::redirect(&format!(
|
||||
"/admin/customers/{id}?error=discount-must-be-positive"
|
||||
"/admin/customers/{id}/prices/{product_id}/edit?error=discount-must-be-positive"
|
||||
))
|
||||
}
|
||||
};
|
||||
@@ -288,6 +354,10 @@ pub fn routes() -> Routes {
|
||||
.add("/admin/customers", get(index))
|
||||
.add("/admin/customers/{id}", get(show))
|
||||
.add("/admin/customers/{id}/profiles", post(sync_profiles))
|
||||
.add(
|
||||
"/admin/customers/{id}/prices/{product_id}/edit",
|
||||
get(price_edit),
|
||||
)
|
||||
.add("/admin/customers/{id}/prices/{product_id}", post(set_price))
|
||||
.add(
|
||||
"/admin/customers/{id}/prices/{product_id}/remove",
|
||||
|
||||
Reference in New Issue
Block a user