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:
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)?;
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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()]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user