diff --git a/assets/i18n/en/main.ftl b/assets/i18n/en/main.ftl index f64a9f1..b7ec7e0 100644 --- a/assets/i18n/en/main.ftl +++ b/assets/i18n/en/main.ftl @@ -211,6 +211,9 @@ price = Price sale-price = Sale price admin-discounts = Discounts admin-discounts-desc = Set discounted product prices. A discount shows up as a sale in the shop. +business-discount-desc = A baseline discount for all business accounts (off the regular price). Profiles and negotiated prices apply on top (lowest price wins). +audience-personal = Personal +audience-business = Business on-sale = On sale no-discount = No discount discount = Discount diff --git a/assets/i18n/sk/main.ftl b/assets/i18n/sk/main.ftl index e5c054e..2a96641 100644 --- a/assets/i18n/sk/main.ftl +++ b/assets/i18n/sk/main.ftl @@ -211,6 +211,9 @@ price = Cena sale-price = Zľavnená cena admin-discounts = Zľavy admin-discounts-desc = Nastavte zľavnené ceny produktov. Zľava sa v obchode zobrazí ako akcia. +business-discount-desc = Základná zľava pre všetky firemné účty (z bežnej ceny). Profily a dohodnuté ceny sa uplatnia navyše (platí najnižšia cena). +audience-personal = Osobné +audience-business = Firemné on-sale = V akcii no-discount = Bez zľavy discount = Zľava diff --git a/assets/views/admin/catalog/discount_form.html b/assets/views/admin/catalog/discount_form.html index d87f748..ae0b8aa 100644 --- a/assets/views/admin/catalog/discount_form.html +++ b/assets/views/admin/catalog/discount_form.html @@ -6,11 +6,16 @@ {% block content %}
+ {% if audience == "business" %}{{ t(key="audience-business", lang=lang | default(value='sk')) }}{% else %}{{ t(key="audience-personal", lang=lang | default(value='sk')) }}{% endif %} +
+{{ t(key="admin-discounts-desc", lang=lang | default(value='sk')) }}
++ {% if business %}{{ t(key="business-discount-desc", lang=lang | default(value='sk')) }}{% else %}{{ t(key="admin-discounts-desc", lang=lang | default(value='sk')) }}{% endif %} +
|
{{ product.name }}
|
{{ product.regular_price }} {{ product.currency }} | - {% if product.on_sale %} - {{ product.sale_price }} {{ product.currency }} - (−{{ product.percent_off }}%) + {% if on_sale %} + {{ sale_price }} {{ product.currency }} + (−{{ pct }}%) {% else %} — {% endif %} | - {% if product.on_sale %} + {% if on_sale %} {{ ui::badge(label=t(key="on-sale", lang=lang | default(value='sk')), variant="danger") }} {% else %} {{ ui::badge(label=t(key="no-discount", lang=lang | default(value='sk')), variant="neutral") }} @@ -48,9 +65,9 @@ |
- {{ ui::button(variant="outline-secondary", label=t(key="set-discount", lang=lang | default(value='sk')), href="/admin/catalog/discounts/" ~ product.id ~ "/edit", size="px-3 py-1.5 text-xs") }}
- {% if product.on_sale %}
-
diff --git a/migration/src/lib.rs b/migration/src/lib.rs
index e4ef5da..a7557f0 100644
--- a/migration/src/lib.rs
+++ b/migration/src/lib.rs
@@ -38,6 +38,7 @@ mod m20260620_000001_add_totp_to_users;
mod m20260621_000001_add_sale_price_to_products;
mod m20260621_000002_account_product_prices;
mod m20260621_000003_discount_profiles;
+mod m20260621_000004_add_business_sale_price_to_products;
pub struct Migrator;
#[async_trait::async_trait]
@@ -80,6 +81,7 @@ impl MigratorTrait for Migrator {
Box::new(m20260621_000001_add_sale_price_to_products::Migration),
Box::new(m20260621_000002_account_product_prices::Migration),
Box::new(m20260621_000003_discount_profiles::Migration),
+ Box::new(m20260621_000004_add_business_sale_price_to_products::Migration),
// inject-above (do not remove this comment)
]
}
diff --git a/migration/src/m20260621_000004_add_business_sale_price_to_products.rs b/migration/src/m20260621_000004_add_business_sale_price_to_products.rs
new file mode 100644
index 0000000..885e611
--- /dev/null
+++ b/migration/src/m20260621_000004_add_business_sale_price_to_products.rs
@@ -0,0 +1,20 @@
+use loco_rs::schema::*;
+use sea_orm_migration::prelude::*;
+
+#[derive(DeriveMigrationName)]
+pub struct Migration;
+
+#[async_trait::async_trait]
+impl MigrationTrait for Migration {
+ async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> {
+ // Optional per-product discounted price (minor units) shown to ALL
+ // business (company) accounts as a baseline, computed off the regular
+ // price like the personal sale. Per-company profiles/negotiated prices
+ // still layer on top (lowest price wins).
+ add_column(m, "products", "business_sale_price_cents", ColType::BigIntegerNull).await
+ }
+
+ async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
+ remove_column(m, "products", "business_sale_price_cents").await
+ }
+}
diff --git a/src/controllers/admin_discounts.rs b/src/controllers/admin_discounts.rs
index 867ddc9..804497d 100644
--- a/src/controllers/admin_discounts.rs
+++ b/src/controllers/admin_discounts.rs
@@ -1,9 +1,15 @@
-//! Admin management of per-product discounts.
+//! Admin management of per-product discounts, in a place of their own rather
+//! than on the product editor.
//!
-//! Discounts live on the product (`sale_price_cents`) but are set here, in a
-//! place of their own, rather than on the product editor: an admin picks a
-//! product, enters a discounted price, and the storefront then shows it on sale.
-//! Editing a product never touches its discount, and vice versa.
+//! Two audiences, switched by an `?audience=` tab:
+//! - **personal** (default): the public sale price (`products.sale_price_cents`)
+//! everyone sees.
+//! - **business**: a baseline discount for all company accounts
+//! (`products.business_sale_price_cents`). Per-company profiles/negotiated
+//! prices still layer on top (lowest price wins). Both are computed off the
+//! regular price.
+
+use std::collections::HashMap;
use axum_extra::extract::cookie::CookieJar;
use loco_rs::prelude::*;
@@ -20,6 +26,8 @@ use crate::{
},
};
+const BUSINESS: &str = "business";
+
#[derive(Debug, Deserialize)]
struct DiscountForm {
/// "fixed" (enter the new price) or "percent" (enter % off). Defaults to
@@ -29,8 +37,35 @@ struct DiscountForm {
percent: Option |