fix(sync): Lieferadresse nicht mehr feldweise mischen, Adresszusatz statt Land

- Sync nimmt die Lieferadresse als Ganzes: mit eigener LieferAdressId
  alle Felder aus ihr, sonst alle aus der Belegadresse. Das bisherige
  COALESCE je Feld hat leere Felder mit Werten des Bestellers gefüllt
  (z. B. dessen Ortsteil an eine fremde Anschrift gehängt).
- ERP-Freitext `Adressen.Land` (praktisch Ortsteil/Etage/Hinweis) wird
  zum Adresszusatz `Address.addition`; `country` kommt jetzt aus dem
  Länderstamm (`LandID` → `Laender.Land`). Deutschland-Kürzel und reine
  Landnamen sind kein Zusatz.
- Migration 0034: `customers.address_addition`, `deliveries.snap_addition`;
  Bestandstext aus `country` wandert in den Zusatz.
- Lieferbericht und Beleg-Ansicht: Land nur außerhalb Deutschlands,
  Zusatz als eigene Zeile „Zusatz: …".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-09-25 16:13:13 +02:00
parent af6d522290
commit c9d0ccfdef
11 changed files with 231 additions and 89 deletions

View File

@ -23,7 +23,7 @@ use holzleitner_application::dto::{
};
use holzleitner_application::error::ApplicationError;
use holzleitner_application::ports::ErpDeliverySource;
use holzleitner_domain::{Address, ContactKind, ContactRole};
use holzleitner_domain::{is_germany, Address, ContactKind, ContactRole};
/// Verbindungsparameter zur ERPframe-MSSQL.
#[derive(Debug, Clone)]
@ -141,12 +141,18 @@ SELECT
adr.Hausnummer AS custHouseNumber,
LTRIM(RTRIM(adr.PLZ)) AS custPostalCode,
adr.Ort AS custCity,
adr.Land AS custCountry,
COALESCE(dadr.Strasse, adr.Strasse) AS delivStreet,
COALESCE(dadr.Hausnummer, adr.Hausnummer) AS delivHouseNumber,
LTRIM(RTRIM(COALESCE(dadr.PLZ, adr.PLZ))) AS delivPostalCode,
COALESCE(dadr.Ort, adr.Ort) AS delivCity,
COALESCE(dadr.Land, adr.Land) AS delivCountry,
LTRIM(RTRIM(hland.Land)) AS custCountry,
adr.Land AS custAddition,
-- Lieferadresse als Ganzes: gibt es eine eigene, kommen ALLE Felder aus
-- ihr, sonst alle aus der Belegadresse. Kein Feld-für-Feld-COALESCE —
-- das hat leere Felder der Lieferadresse mit Werten des Bestellers
-- aufgefüllt (z. B. dessen Ortsteil an eine fremde Anschrift gehängt).
CASE WHEN dadr.ROW_ID IS NOT NULL THEN dadr.Strasse ELSE adr.Strasse END AS delivStreet,
CASE WHEN dadr.ROW_ID IS NOT NULL THEN dadr.Hausnummer ELSE adr.Hausnummer END AS delivHouseNumber,
LTRIM(RTRIM(CASE WHEN dadr.ROW_ID IS NOT NULL THEN dadr.PLZ ELSE adr.PLZ END)) AS delivPostalCode,
CASE WHEN dadr.ROW_ID IS NOT NULL THEN dadr.Ort ELSE adr.Ort END AS delivCity,
LTRIM(RTRIM(CASE WHEN dadr.ROW_ID IS NOT NULL THEN dland.Land ELSE hland.Land END)) AS delivCountry,
CASE WHEN dadr.ROW_ID IS NOT NULL THEN dadr.Land ELSE adr.Land END AS delivAddition,
LTRIM(RTRIM(CAST(bk._Uhrzeit_Txt AS varchar(200)))) AS desiredTime,
LTRIM(RTRIM(CAST(bk.Kopftext AS varchar(MAX)))) AS specialAgreements,
CAST(ISNULL(bk._Anz_Bestellung, 0) AS FLOAT) AS prepaidAmount,
@ -174,6 +180,8 @@ SELECT
FROM Belegkopf bk
JOIN Adressen adr ON bk.AdressId = adr.ROW_ID
LEFT JOIN Adressen dadr ON bk.LieferAdressId = dadr.ROW_ID
LEFT JOIN Laender hland ON hland.ROW_ID = adr.LandID
LEFT JOIN Laender dland ON dland.ROW_ID = dadr.LandID
LEFT JOIN Adressen radr ON bk.RechnungsAdressId = radr.ROW_ID
LEFT JOIN Adressen kadr ON bk.AnsprechpartnerId = kadr.ROW_ID
JOIN Belegzeilen bz ON bk.row_id = bz.ParentID
@ -215,12 +223,18 @@ SELECT
adr.Hausnummer AS custHouseNumber,
LTRIM(RTRIM(adr.PLZ)) AS custPostalCode,
adr.Ort AS custCity,
adr.Land AS custCountry,
COALESCE(dadr.Strasse, adr.Strasse) AS delivStreet,
COALESCE(dadr.Hausnummer, adr.Hausnummer) AS delivHouseNumber,
LTRIM(RTRIM(COALESCE(dadr.PLZ, adr.PLZ))) AS delivPostalCode,
COALESCE(dadr.Ort, adr.Ort) AS delivCity,
COALESCE(dadr.Land, adr.Land) AS delivCountry,
LTRIM(RTRIM(hland.Land)) AS custCountry,
adr.Land AS custAddition,
-- Lieferadresse als Ganzes: gibt es eine eigene, kommen ALLE Felder aus
-- ihr, sonst alle aus der Belegadresse. Kein Feld-für-Feld-COALESCE —
-- das hat leere Felder der Lieferadresse mit Werten des Bestellers
-- aufgefüllt (z. B. dessen Ortsteil an eine fremde Anschrift gehängt).
CASE WHEN dadr.ROW_ID IS NOT NULL THEN dadr.Strasse ELSE adr.Strasse END AS delivStreet,
CASE WHEN dadr.ROW_ID IS NOT NULL THEN dadr.Hausnummer ELSE adr.Hausnummer END AS delivHouseNumber,
LTRIM(RTRIM(CASE WHEN dadr.ROW_ID IS NOT NULL THEN dadr.PLZ ELSE adr.PLZ END)) AS delivPostalCode,
CASE WHEN dadr.ROW_ID IS NOT NULL THEN dadr.Ort ELSE adr.Ort END AS delivCity,
LTRIM(RTRIM(CASE WHEN dadr.ROW_ID IS NOT NULL THEN dland.Land ELSE hland.Land END)) AS delivCountry,
CASE WHEN dadr.ROW_ID IS NOT NULL THEN dadr.Land ELSE adr.Land END AS delivAddition,
LTRIM(RTRIM(CAST(bk._Uhrzeit_Txt AS varchar(200)))) AS desiredTime,
LTRIM(RTRIM(CAST(bk.Kopftext AS varchar(MAX)))) AS specialAgreements,
CAST(ISNULL(bk._Anz_Bestellung, 0) AS FLOAT) AS prepaidAmount,
@ -243,6 +257,8 @@ SELECT
FROM Belegkopf bk
JOIN Adressen adr ON bk.AdressId = adr.ROW_ID
LEFT JOIN Adressen dadr ON bk.LieferAdressId = dadr.ROW_ID
LEFT JOIN Laender hland ON hland.ROW_ID = adr.LandID
LEFT JOIN Laender dland ON dland.ROW_ID = dadr.LandID
LEFT JOIN Adressen radr ON bk.RechnungsAdressId = radr.ROW_ID
LEFT JOIN Adressen kadr ON bk.AnsprechpartnerId = kadr.ROW_ID
JOIN Belegzeilen bz ON bk.row_id = bz.ParentID
@ -412,6 +428,14 @@ fn read_all_contact_sources(row: &tiberius::Row) -> Vec<SyncContactSource> {
.collect()
}
/// Adresszusatz aus dem ERP-Freitextfeld `Adressen.Land`. Das Feld wird
/// praktisch für Ortsteil/Etage/Hinweise genutzt; steht dort nur das Land
/// (Deutschland-Kürzel oder derselbe Name wie im Länderstamm), ist es kein
/// Zusatz.
fn address_addition(free_text: Option<String>, country: &str) -> Option<String> {
free_text.filter(|t| !is_germany(t) && !t.eq_ignore_ascii_case(country.trim()))
}
fn map_row(row: &tiberius::Row) -> ErpRow {
ErpRow {
driver_personalnummer: i64c(row, "driverPersonalnummer"),
@ -427,6 +451,7 @@ fn map_row(row: &tiberius::Row) -> ErpRow {
postal_code: s(row, "custPostalCode"),
city: s(row, "custCity"),
country: s(row, "custCountry"),
addition: address_addition(opt_s(row, "custAddition"), &s(row, "custCountry")),
},
deliv: Address {
street: s(row, "delivStreet"),
@ -434,6 +459,7 @@ fn map_row(row: &tiberius::Row) -> ErpRow {
postal_code: s(row, "delivPostalCode"),
city: s(row, "delivCity"),
country: s(row, "delivCountry"),
addition: address_addition(opt_s(row, "delivAddition"), &s(row, "delivCountry")),
},
desired_time: opt_s(row, "desiredTime"),
special_agreements: opt_s(row, "specialAgreements"),
@ -584,3 +610,23 @@ impl ErpDeliverySource for MssqlErpDeliverySource {
Ok(tours)
}
}
#[cfg(test)]
mod address_addition_tests {
use super::address_addition;
#[test]
fn ortsteil_und_hinweise_sind_zusatz() {
assert_eq!(address_addition(Some("Holzheim".into()), "Deutschland").as_deref(), Some("Holzheim"));
assert_eq!(address_addition(Some("3 Etage".into()), "").as_deref(), Some("3 Etage"));
}
#[test]
fn reines_land_ist_kein_zusatz() {
for t in ["D", "DEU", "Deutschland"] {
assert_eq!(address_addition(Some(t.into()), ""), None, "{t}");
}
assert_eq!(address_addition(Some("belgien".into()), "Belgien"), None);
assert_eq!(address_addition(None, "Deutschland"), None);
}
}

View File

@ -48,6 +48,7 @@ struct DeliveryRow {
snap_postal_code: String,
snap_city: String,
snap_country: String,
snap_addition: Option<String>,
assigned_car_id: Option<Uuid>,
desired_time: Option<String>,
special_agreements: Option<String>,
@ -68,7 +69,7 @@ async fn lock_delivery(
r#"
SELECT
id, tour_id, erp_belegart_id, erp_belegnummer, customer_id,
snap_street, snap_house_number, snap_postal_code, snap_city, snap_country,
snap_street, snap_house_number, snap_postal_code, snap_city, snap_country, snap_addition,
assigned_car_id, desired_time, special_agreements,
state, prepaid_amount, payment_method_id
FROM deliveries
@ -109,6 +110,7 @@ fn build_delivery(row: DeliveryRow, state: DeliveryState, state_reason: Option<S
postal_code: row.snap_postal_code,
city: row.snap_city,
country: row.snap_country,
addition: row.snap_addition,
},
assigned_car_id: row.assigned_car_id,
contact_person_ids: contacts,

View File

@ -39,6 +39,7 @@ struct DeliveryRow {
snap_postal_code: String,
snap_city: String,
snap_country: String,
snap_addition: Option<String>,
assigned_car_id: Option<Uuid>,
desired_time: Option<String>,
special_agreements: Option<String>,
@ -112,7 +113,7 @@ async fn lock_delivery(
r#"
SELECT
id, tour_id, erp_belegart_id, erp_belegnummer, customer_id,
snap_street, snap_house_number, snap_postal_code, snap_city, snap_country,
snap_street, snap_house_number, snap_postal_code, snap_city, snap_country, snap_addition,
assigned_car_id, desired_time, special_agreements,
state, prepaid_amount, payment_method_id
FROM deliveries
@ -193,6 +194,7 @@ impl DeliveryRepository for PgDeliveryRepository {
postal_code: row.snap_postal_code,
city: row.snap_city,
country: row.snap_country,
addition: row.snap_addition,
},
assigned_car_id: row.assigned_car_id,
contact_person_ids,
@ -250,6 +252,7 @@ impl DeliveryRepository for PgDeliveryRepository {
postal_code: row.snap_postal_code,
city: row.snap_city,
country: row.snap_country,
addition: row.snap_addition,
},
assigned_car_id: car_id,
contact_person_ids,

View File

@ -75,6 +75,7 @@ struct DeliveryRow {
snap_postal_code: String,
snap_city: String,
snap_country: String,
snap_addition: Option<String>,
assigned_car_id: Option<Uuid>,
desired_time: Option<String>,
special_agreements: Option<String>,
@ -113,6 +114,7 @@ struct CustomerRow {
postal_code: String,
city: String,
country: String,
address_addition: Option<String>,
}
#[derive(sqlx::FromRow)]
@ -251,6 +253,7 @@ fn map_customer(row: CustomerRow) -> Customer {
postal_code: row.postal_code,
city: row.city,
country: row.country,
addition: row.address_addition,
},
}
}
@ -413,6 +416,7 @@ fn map_delivery(
postal_code: row.snap_postal_code,
city: row.snap_city,
country: row.snap_country,
addition: row.snap_addition,
},
assigned_car_id: row.assigned_car_id,
contact_person_ids,
@ -490,6 +494,7 @@ impl TourRepository for PgTourRepository {
SELECT
id, tour_id, erp_belegart_id, erp_belegnummer, customer_id,
snap_street, snap_house_number, snap_postal_code, snap_city, snap_country,
snap_addition,
assigned_car_id, desired_time, special_agreements,
state, state_reason, sort_order,
prepaid_amount, payment_method_id
@ -578,7 +583,8 @@ impl TourRepository for PgTourRepository {
let customer_ids_vec: Vec<Uuid> = customer_ids.into_iter().collect();
let customers = sqlx::query_as::<_, CustomerRow>(
r#"
SELECT id, erp_customer_id, name, street, house_number, postal_code, city, country
SELECT id, erp_customer_id, name, street, house_number, postal_code, city, country,
address_addition
FROM customers
WHERE id = ANY($1)
ORDER BY name
@ -1105,15 +1111,17 @@ async fn upsert_customer(
let id: Uuid = sqlx::query_scalar(
r#"
INSERT INTO customers (
erp_customer_id, name, street, house_number, postal_code, city, country
) VALUES ($1, $2, $3, $4, $5, $6, $7)
erp_customer_id, name, street, house_number, postal_code, city, country,
address_addition
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT (erp_customer_id) DO UPDATE SET
name = EXCLUDED.name,
street = EXCLUDED.street,
house_number = EXCLUDED.house_number,
postal_code = EXCLUDED.postal_code,
city = EXCLUDED.city,
country = EXCLUDED.country
country = EXCLUDED.country,
address_addition = EXCLUDED.address_addition
RETURNING id
"#,
)
@ -1124,6 +1132,7 @@ async fn upsert_customer(
.bind(&delivery.customer_address.postal_code)
.bind(&delivery.customer_address.city)
.bind(&delivery.customer_address.country)
.bind(delivery.customer_address.addition.as_deref())
.fetch_one(&mut **tx)
.await
.map_err(db)?;
@ -1163,8 +1172,8 @@ async fn upsert_delivery(
erp_belegnummer, customer_id,
snap_street, snap_house_number, snap_postal_code, snap_city, snap_country,
sort_order, desired_time, special_agreements,
prepaid_amount, payment_method_id
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
prepaid_amount, payment_method_id, snap_addition
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
ON CONFLICT (erp_belegart_id, erp_belegnummer) DO UPDATE SET
tour_id = EXCLUDED.tour_id,
erp_belegart_code = EXCLUDED.erp_belegart_code,
@ -1175,6 +1184,7 @@ async fn upsert_delivery(
snap_postal_code = EXCLUDED.snap_postal_code,
snap_city = EXCLUDED.snap_city,
snap_country = EXCLUDED.snap_country,
snap_addition = EXCLUDED.snap_addition,
sort_order = EXCLUDED.sort_order,
desired_time = EXCLUDED.desired_time,
special_agreements = EXCLUDED.special_agreements,
@ -1199,6 +1209,7 @@ async fn upsert_delivery(
.bind(delivery.special_agreements.as_deref())
.bind(delivery.prepaid_amount)
.bind(payment_method_id)
.bind(delivery.delivery_address.addition.as_deref())
.fetch_one(&mut **tx)
.await
.map_err(db)?;

View File

@ -482,11 +482,17 @@ impl DeliveryReportRenderer for PdfDeliveryReportRenderer {
);
p.kv("Besteller", &format!("{} (Nr. {})", d.customer_name, d.customer_number));
p.kv("Adresse Besteller", &r.orderer_address);
if let Some(z) = &r.orderer_address_addition {
p.kv("Zusatz Besteller", z);
}
if !r.orderer_phones.is_empty() {
p.kv("Tel. Besteller", &r.orderer_phones.join(" · "));
}
p.kv("Empfänger", &r.name);
p.kv("Lieferadresse", &d.address);
if let Some(z) = &d.address_addition {
p.kv("Zusatz", z);
}
if !r.phones.is_empty() {
p.kv("Tel. Empfänger", &r.phones.join(" · "));
}
@ -494,6 +500,9 @@ impl DeliveryReportRenderer for PdfDeliveryReportRenderer {
None => {
p.kv("Kunde", &format!("{} (Nr. {})", d.customer_name, d.customer_number));
p.kv("Adresse", &d.address);
if let Some(z) = &d.address_addition {
p.kv("Zusatz", z);
}
}
}
p.kv("Wunschzeit", opt(&d.desired_time));

View File

@ -15,6 +15,7 @@ use holzleitner_application::dto::{
};
use holzleitner_application::error::ApplicationError;
use holzleitner_application::ports::DeliveryReportRepository;
use holzleitner_domain::Address;
pub struct PgDeliveryReportRepository {
pool: PgPool,
@ -49,11 +50,13 @@ struct HeadRow {
cust_postal_code: String,
cust_city: String,
cust_country: String,
cust_addition: Option<String>,
snap_street: Option<String>,
snap_house_number: Option<String>,
snap_postal_code: Option<String>,
snap_city: Option<String>,
snap_country: Option<String>,
snap_addition: Option<String>,
desired_time: Option<String>,
special_agreements: Option<String>,
prepaid_amount: f64,
@ -185,32 +188,6 @@ struct AttachmentRow {
uploaded_by: i64,
}
fn one_line_address(
street: Option<String>,
house: Option<String>,
plz: Option<String>,
city: Option<String>,
country: Option<String>,
) -> String {
let line1 = [street, house]
.into_iter()
.flatten()
.filter(|s| !s.trim().is_empty())
.collect::<Vec<_>>()
.join(" ");
let line2 = [plz, city]
.into_iter()
.flatten()
.filter(|s| !s.trim().is_empty())
.collect::<Vec<_>>()
.join(" ");
[line1, line2, country.unwrap_or_default()]
.into_iter()
.filter(|s| !s.trim().is_empty())
.collect::<Vec<_>>()
.join(", ")
}
#[async_trait]
impl DeliveryReportRepository for PgDeliveryReportRepository {
async fn load(
@ -229,9 +206,9 @@ impl DeliveryReportRepository for PgDeliveryReportRepository {
c.erp_customer_id, c.name AS customer_name,
c.street AS cust_street, c.house_number AS cust_house_number,
c.postal_code AS cust_postal_code, c.city AS cust_city,
c.country AS cust_country,
c.country AS cust_country, c.address_addition AS cust_addition,
d.snap_street, d.snap_house_number, d.snap_postal_code,
d.snap_city, d.snap_country,
d.snap_city, d.snap_country, d.snap_addition,
d.desired_time, d.special_agreements, d.prepaid_amount
FROM deliveries d
JOIN tours t ON t.id = d.tour_id
@ -432,19 +409,21 @@ impl DeliveryReportRepository for PgDeliveryReportRepository {
})
.unwrap_or(0);
let snap = AddressParts {
let snap = Address {
street: head.snap_street.unwrap_or_default(),
house_number: head.snap_house_number.unwrap_or_default(),
postal_code: head.snap_postal_code.unwrap_or_default(),
city: head.snap_city.unwrap_or_default(),
country: head.snap_country.unwrap_or_default(),
addition: head.snap_addition,
};
let orderer = AddressParts {
let orderer = Address {
street: head.cust_street,
house_number: head.cust_house_number,
postal_code: head.cust_postal_code,
city: head.cust_city,
country: head.cust_country,
addition: head.cust_addition,
};
let different_recipient = different_recipient(
&head.customer_name,
@ -454,6 +433,7 @@ impl DeliveryReportRepository for PgDeliveryReportRepository {
sources.iter().find(|s| s.role == "delivery"),
);
let address = snap.one_line();
let address_addition = snap.addition.clone();
Ok(Some(DeliveryReportData {
generated_at: Utc::now(),
@ -470,6 +450,7 @@ impl DeliveryReportRepository for PgDeliveryReportRepository {
customer_number: head.erp_customer_id,
customer_name: head.customer_name,
address,
address_addition,
different_recipient,
desired_time: head.desired_time,
special_agreements: head.special_agreements,
@ -597,34 +578,13 @@ impl DeliveryReportRepository for PgDeliveryReportRepository {
}
}
/// Anschrift in Einzelteilen, wie sie am Kunden bzw. als Beleg-Snapshot liegt.
struct AddressParts {
street: String,
house_number: String,
postal_code: String,
city: String,
country: String,
}
impl AddressParts {
fn one_line(&self) -> String {
one_line_address(
Some(self.street.clone()),
Some(self.house_number.clone()),
Some(self.postal_code.clone()),
Some(self.city.clone()),
Some(self.country.clone()),
)
}
/// Gleiche Anschrift, unabhängig von Groß-/Kleinschreibung und
/// Leerzeichen. Das Land zählt nicht (im ERP oft uneinheitlich gepflegt).
fn same_place(&self, other: &Self) -> bool {
normalized(&self.street) == normalized(&other.street)
&& normalized(&self.house_number) == normalized(&other.house_number)
&& normalized(&self.postal_code) == normalized(&other.postal_code)
&& normalized(&self.city) == normalized(&other.city)
}
/// Gleiche Anschrift, unabhängig von Groß-/Kleinschreibung und Leerzeichen.
/// Land und Zusatz zählen nicht (im ERP uneinheitlich gepflegt).
fn same_place(a: &Address, b: &Address) -> bool {
normalized(&a.street) == normalized(&b.street)
&& normalized(&a.house_number) == normalized(&b.house_number)
&& normalized(&a.postal_code) == normalized(&b.postal_code)
&& normalized(&a.city) == normalized(&b.city)
}
fn normalized(value: &str) -> String {
@ -637,8 +597,8 @@ fn normalized(value: &str) -> String {
/// Kundennamen).
fn different_recipient(
customer_name: &str,
orderer: &AddressParts,
delivery_address: &AddressParts,
orderer: &Address,
delivery_address: &Address,
header: Option<&ContactSourceRow>,
delivery: Option<&ContactSourceRow>,
) -> Option<ReportRecipient> {
@ -647,7 +607,7 @@ fn different_recipient(
.unwrap_or_else(|| customer_name.to_string());
let recipient_name = delivery.and_then(ContactSourceRow::full_name);
let address_differs = !orderer.same_place(delivery_address);
let address_differs = !same_place(orderer, delivery_address);
let name_differs = recipient_name
.as_deref()
.is_some_and(|n| normalized(n) != normalized(&orderer_name));
@ -659,6 +619,7 @@ fn different_recipient(
name: recipient_name.unwrap_or_else(|| customer_name.to_string()),
phones: delivery.map(|s| s.phones.clone()).unwrap_or_default(),
orderer_address: orderer.one_line(),
orderer_address_addition: orderer.addition.clone(),
orderer_phones: header.map(|s| s.phones.clone()).unwrap_or_default(),
})
}
@ -667,13 +628,14 @@ fn different_recipient(
mod different_recipient_tests {
use super::*;
fn addr(street: &str, city: &str) -> AddressParts {
AddressParts {
fn addr(street: &str, city: &str) -> Address {
Address {
street: street.into(),
house_number: "1".into(),
postal_code: "41460".into(),
city: city.into(),
country: "DE".into(),
country: "Deutschland".into(),
addition: None,
}
}
@ -714,7 +676,7 @@ mod different_recipient_tests {
)
.expect("abweichend");
assert_eq!(r.name, "Musterfrau Erika");
assert_eq!(r.orderer_address, "Am Sandberg 1, 41460 Neuss, DE");
assert_eq!(r.orderer_address, "Am Sandberg 1, 41460 Neuss");
assert_eq!(r.phones, vec!["0151 1".to_string()]);
}