+ {% if order.residence_address %}
+
+
{{ t(key="checkout-residence-address", lang=lang | default(value='sk')) }}
+
{{ order.residence_address }}
+
{{ order.residence_zip }} {{ order.residence_city }}{% if order.residence_country %}, {{ order.residence_country }}{% endif %}
+
+ {% endif %}
+
+
{{ t(key="checkout-shipping", lang=lang | default(value='sk')) }}
+ {% if order.address %}
{{ order.address }}
{% endif %}
+
{{ order.zip }} {{ order.city }}{% if order.country %}, {{ order.country }}{% endif %}
+
+
{{ t(key="payment-bank-instructions", lang=lang | default(value='sk')) }}
diff --git a/migration/src/lib.rs b/migration/src/lib.rs
index 150971c..d8eb988 100644
--- a/migration/src/lib.rs
+++ b/migration/src/lib.rs
@@ -50,6 +50,7 @@ mod m20260623_000002_strip_html_from_product_search;
mod m20260623_000003_drop_currency;
mod m20260623_000004_currencies;
mod m20260625_000001_add_avatar_to_users;
+mod m20260627_000001_order_residence_address;
pub struct Migrator;
#[async_trait::async_trait]
@@ -104,7 +105,8 @@ impl MigratorTrait for Migrator {
Box::new(m20260623_000003_drop_currency::Migration),
Box::new(m20260623_000004_currencies::Migration),
Box::new(m20260625_000001_add_avatar_to_users::Migration),
+ Box::new(m20260627_000001_order_residence_address::Migration),
// inject-above (do not remove this comment)
]
}
-}
\ No newline at end of file
+}
diff --git a/migration/src/m20260627_000001_order_residence_address.rs b/migration/src/m20260627_000001_order_residence_address.rs
new file mode 100644
index 0000000..6099d1d
--- /dev/null
+++ b/migration/src/m20260627_000001_order_residence_address.rs
@@ -0,0 +1,22 @@
+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> {
+ add_column(m, "orders", "residence_address", ColType::StringNull).await?;
+ add_column(m, "orders", "residence_city", ColType::StringNull).await?;
+ add_column(m, "orders", "residence_zip", ColType::StringNull).await?;
+ add_column(m, "orders", "residence_country", ColType::StringNull).await
+ }
+
+ async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
+ remove_column(m, "orders", "residence_country").await?;
+ remove_column(m, "orders", "residence_zip").await?;
+ remove_column(m, "orders", "residence_city").await?;
+ remove_column(m, "orders", "residence_address").await
+ }
+}
diff --git a/src/controllers/checkout.rs b/src/controllers/checkout.rs
index 5be217b..52895f1 100644
--- a/src/controllers/checkout.rs
+++ b/src/controllers/checkout.rs
@@ -35,10 +35,15 @@ struct CheckoutForm {
company_id: Option
,
tax_id: Option,
vat_id: Option,
- address: String,
- city: String,
- zip: String,
- country: String,
+ residence_address: String,
+ residence_city: String,
+ residence_zip: String,
+ residence_country: String,
+ delivery_same_as_residence: Option,
+ address: Option,
+ city: Option,
+ zip: Option,
+ country: Option,
note: Option,
payment_method: String,
carrier_code: String,
@@ -110,7 +115,7 @@ async fn checkout_page(
let p = |get: fn(&customer_profiles::Model) -> Option| {
profile.as_ref().and_then(get)
};
- // Whether the customer already has a shipping address on file. When they do,
+ // Whether the customer already has a residence address on file. When they do,
// the "save this address to my profile" opt-in is pointless (the profile was
// filled in advance), so it's hidden and the existing profile is left alone.
let profile_filled = profile
@@ -147,10 +152,10 @@ async fn checkout_page(
"prefill_vat_id": p(|x| x.vat_id.clone()),
"prefill_phone_prefix": p(|x| x.phone_prefix.clone()),
"prefill_phone": p(|x| x.phone.clone()),
- "prefill_address": p(|x| x.address.clone()),
- "prefill_city": p(|x| x.city.clone()),
- "prefill_zip": p(|x| x.zip.clone()),
- "prefill_country": p(|x| x.country.clone()),
+ "prefill_residence_address": p(|x| x.address.clone()),
+ "prefill_residence_city": p(|x| x.city.clone()),
+ "prefill_residence_zip": p(|x| x.zip.clone()),
+ "prefill_residence_country": p(|x| x.country.clone()),
"lang": current_lang(&jar),
}),
)
@@ -177,16 +182,37 @@ async fn place_order(
None => number.clone(),
};
- // Contact and shipping-address fields are mandatory (also enforced in the
+ // Contact and residence-address fields are mandatory (also enforced in the
// browser via `required`).
let require = |value: &str, field: &str| -> Result {
trimmed(value).ok_or_else(|| Error::BadRequest(format!("{field} is required")))
};
+ let require_opt = |value: Option<&str>, field: &str| -> Result {
+ value
+ .and_then(trimmed)
+ .ok_or_else(|| Error::BadRequest(format!("{field} is required")))
+ };
let customer_name = require(&form.customer_name, "name")?;
- let address = require(&form.address, "address")?;
- let city = require(&form.city, "city")?;
- let zip = require(&form.zip, "zip")?;
- let country = require(&form.country, "country")?;
+ let residence_address = require(&form.residence_address, "residence address")?;
+ let residence_city = require(&form.residence_city, "residence city")?;
+ let residence_zip = require(&form.residence_zip, "residence zip")?;
+ let residence_country = require(&form.residence_country, "residence country")?;
+ let same_address = form.delivery_same_as_residence.is_some();
+ let (address, city, zip, country) = if same_address {
+ (
+ residence_address.clone(),
+ residence_city.clone(),
+ residence_zip.clone(),
+ residence_country.clone(),
+ )
+ } else {
+ (
+ require_opt(form.address.as_deref(), "delivery address")?,
+ require_opt(form.city.as_deref(), "delivery city")?,
+ require_opt(form.zip.as_deref(), "delivery zip")?,
+ require_opt(form.country.as_deref(), "delivery country")?,
+ )
+ };
// The account type is fixed for a logged-in customer (taken from their
// account, never the form); a guest picks it on the form. Admins are treated
@@ -246,10 +272,10 @@ async fn place_order(
vat_id: vat_id.clone(),
phone_prefix: trimmed(&form.phone_prefix),
phone: Some(number.clone()),
- address: Some(address.clone()),
- city: Some(city.clone()),
- zip: Some(zip.clone()),
- country: Some(country.clone()),
+ address: Some(residence_address.clone()),
+ city: Some(residence_city.clone()),
+ zip: Some(residence_zip.clone()),
+ country: Some(residence_country.clone()),
};
// Resolve the account that will own this order. A logged-in customer always
@@ -318,6 +344,10 @@ async fn place_order(
company_id,
tax_id,
vat_id,
+ residence_address: Some(residence_address),
+ residence_city: Some(residence_city),
+ residence_zip: Some(residence_zip),
+ residence_country: Some(residence_country),
address: Some(address),
city: Some(city),
zip: Some(zip),
diff --git a/src/models/_entities/orders.rs b/src/models/_entities/orders.rs
index 531dd08..42545e4 100644
--- a/src/models/_entities/orders.rs
+++ b/src/models/_entities/orders.rs
@@ -38,6 +38,10 @@ pub struct Model {
pub tax_id: Option,
pub vat_id: Option,
pub user_id: Option,
+ pub residence_address: Option,
+ pub residence_city: Option,
+ pub residence_zip: Option,
+ pub residence_country: Option,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
diff --git a/src/models/orders.rs b/src/models/orders.rs
index d821e95..d934e16 100644
--- a/src/models/orders.rs
+++ b/src/models/orders.rs
@@ -24,6 +24,10 @@ pub struct Checkout {
pub company_id: Option,
pub tax_id: Option,
pub vat_id: Option,
+ pub residence_address: Option,
+ pub residence_city: Option,
+ pub residence_zip: Option,
+ pub residence_country: Option,
pub address: Option,
pub city: Option,
pub zip: Option,
@@ -102,6 +106,10 @@ pub async fn place(
company_id: Set(details.company_id),
tax_id: Set(details.tax_id),
vat_id: Set(details.vat_id),
+ residence_address: Set(details.residence_address),
+ residence_city: Set(details.residence_city),
+ residence_zip: Set(details.residence_zip),
+ residence_country: Set(details.residence_country),
address: Set(details.address),
city: Set(details.city),
zip: Set(details.zip),
diff --git a/src/views/checkout.rs b/src/views/checkout.rs
index b135fe8..00d5a8d 100644
--- a/src/views/checkout.rs
+++ b/src/views/checkout.rs
@@ -40,6 +40,10 @@ pub fn detail(order: &orders::Model, bank_iban: &str, bank_account_name: &str) -
"subtotal": format_price(order.total_cents - order.shipping_cents),
"shipping": format_price(order.shipping_cents),
"total": format_price(order.total_cents),
+ "residence_address": order.residence_address,
+ "residence_city": order.residence_city,
+ "residence_zip": order.residence_zip,
+ "residence_country": order.residence_country,
"address": order.address,
"city": order.city,
"zip": order.zip,