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:
@ -301,8 +301,13 @@ fn address_lines(a: &Address) -> Vec<String> {
|
|||||||
if !city.is_empty() {
|
if !city.is_empty() {
|
||||||
lines.push(city);
|
lines.push(city);
|
||||||
}
|
}
|
||||||
if !a.country.trim().is_empty() {
|
// Land nur außerhalb Deutschlands; der Adresszusatz (Ortsteil, Etage,
|
||||||
lines.push(a.country.clone());
|
// Hinweis) als eigene, gekennzeichnete Zeile.
|
||||||
|
if let Some(country) = a.foreign_country() {
|
||||||
|
lines.push(country.to_string());
|
||||||
|
}
|
||||||
|
if let Some(addition) = a.addition.as_deref().filter(|z| !z.trim().is_empty()) {
|
||||||
|
lines.push(format!("Zusatz: {addition}"));
|
||||||
}
|
}
|
||||||
lines
|
lines
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,8 @@ pub struct DeliveryReportData {
|
|||||||
pub customer_name: String,
|
pub customer_name: String,
|
||||||
/// Lieferadresse (Snapshot am Beleg), einzeilig.
|
/// Lieferadresse (Snapshot am Beleg), einzeilig.
|
||||||
pub address: String,
|
pub address: String,
|
||||||
|
/// Adresszusatz der Lieferadresse (Ortsteil, Etage, Hinweis).
|
||||||
|
pub address_addition: Option<String>,
|
||||||
/// Nur gesetzt, wenn Empfänger oder Lieferadresse vom Besteller
|
/// Nur gesetzt, wenn Empfänger oder Lieferadresse vom Besteller
|
||||||
/// abweichen (ERP-`LieferAdressId`). Dann ist `customer_*` der Besteller
|
/// abweichen (ERP-`LieferAdressId`). Dann ist `customer_*` der Besteller
|
||||||
/// und `address` die abweichende Lieferadresse.
|
/// und `address` die abweichende Lieferadresse.
|
||||||
@ -60,6 +62,7 @@ pub struct ReportRecipient {
|
|||||||
pub phones: Vec<String>,
|
pub phones: Vec<String>,
|
||||||
/// Anschrift des Bestellers (Kundenstamm), einzeilig.
|
/// Anschrift des Bestellers (Kundenstamm), einzeilig.
|
||||||
pub orderer_address: String,
|
pub orderer_address: String,
|
||||||
|
pub orderer_address_addition: Option<String>,
|
||||||
pub orderer_phones: Vec<String>,
|
pub orderer_phones: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,5 +16,74 @@ pub struct Address {
|
|||||||
pub house_number: String,
|
pub house_number: String,
|
||||||
pub postal_code: String,
|
pub postal_code: String,
|
||||||
pub city: String,
|
pub city: String,
|
||||||
|
/// Land laut ERP-Länderstamm (`Adressen.LandID` → `Laender.Land`), z. B.
|
||||||
|
/// „Deutschland" oder „Belgien". Leer, wenn im ERP kein Land verknüpft ist.
|
||||||
pub country: String,
|
pub country: String,
|
||||||
|
/// Freier Adresszusatz. Kommt aus dem ERP-Textfeld `Adressen.Land`, das
|
||||||
|
/// die Sachbearbeitung praktisch für Ortsteil, Etage oder Hinweise nutzt
|
||||||
|
/// (z. B. „Holzheim", „3. Etage", „Parterre"). `None`, wenn leer.
|
||||||
|
#[serde(default)]
|
||||||
|
pub addition: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Address {
|
||||||
|
/// Einzeilige Anschrift „Straße Nr, PLZ Ort" — das Land nur, wenn es
|
||||||
|
/// nicht Deutschland ist. Der Adresszusatz gehört nicht dazu (eigene
|
||||||
|
/// Zeile in der Anzeige).
|
||||||
|
pub fn one_line(&self) -> String {
|
||||||
|
let line1 = join_non_empty(&[&self.street, &self.house_number], " ");
|
||||||
|
let line2 = join_non_empty(&[&self.postal_code, &self.city], " ");
|
||||||
|
let country = self.foreign_country().unwrap_or_default();
|
||||||
|
join_non_empty(&[&line1, &line2, country], ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Land für die Anzeige: `None` für Deutschland (Heimatmarkt) oder leer.
|
||||||
|
pub fn foreign_country(&self) -> Option<&str> {
|
||||||
|
let c = self.country.trim();
|
||||||
|
(!c.is_empty() && !is_germany(c)).then_some(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `true` für die im ERP vorkommenden Schreibweisen von Deutschland.
|
||||||
|
pub fn is_germany(value: &str) -> bool {
|
||||||
|
matches!(
|
||||||
|
value.trim().to_lowercase().as_str(),
|
||||||
|
"d" | "de" | "deu" | "deutschland" | "germany" | "brd"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn join_non_empty(parts: &[&str], sep: &str) -> String {
|
||||||
|
parts
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.trim())
|
||||||
|
.filter(|p| !p.is_empty())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(sep)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod address_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn address(country: &str) -> Address {
|
||||||
|
Address {
|
||||||
|
street: "Am Sandberg".into(),
|
||||||
|
house_number: "15".into(),
|
||||||
|
postal_code: "41472".into(),
|
||||||
|
city: "Neuss".into(),
|
||||||
|
country: country.into(),
|
||||||
|
addition: Some("Holzheim".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deutschland_und_zusatz_stehen_nicht_in_der_zeile() {
|
||||||
|
assert_eq!(address("Deutschland").one_line(), "Am Sandberg 15, 41472 Neuss");
|
||||||
|
assert_eq!(address("").one_line(), "Am Sandberg 15, 41472 Neuss");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ausland_wird_angehaengt() {
|
||||||
|
assert_eq!(address("Belgien").one_line(), "Am Sandberg 15, 41472 Neuss, Belgien");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ pub use account::Account;
|
|||||||
pub use article::Article;
|
pub use article::Article;
|
||||||
pub use audit::{AuditAction, ScanAuditEntry};
|
pub use audit::{AuditAction, ScanAuditEntry};
|
||||||
pub use car::Car;
|
pub use car::Car;
|
||||||
pub use common::Address;
|
pub use common::{is_germany, Address};
|
||||||
pub use contact::{ContactChannel, ContactKind, ContactRole, ContactSource};
|
pub use contact::{ContactChannel, ContactKind, ContactRole, ContactSource};
|
||||||
pub use customer::{Customer, CustomerContact};
|
pub use customer::{Customer, CustomerContact};
|
||||||
pub use delivery::{
|
pub use delivery::{
|
||||||
|
|||||||
@ -23,7 +23,7 @@ use holzleitner_application::dto::{
|
|||||||
};
|
};
|
||||||
use holzleitner_application::error::ApplicationError;
|
use holzleitner_application::error::ApplicationError;
|
||||||
use holzleitner_application::ports::ErpDeliverySource;
|
use holzleitner_application::ports::ErpDeliverySource;
|
||||||
use holzleitner_domain::{Address, ContactKind, ContactRole};
|
use holzleitner_domain::{is_germany, Address, ContactKind, ContactRole};
|
||||||
|
|
||||||
/// Verbindungsparameter zur ERPframe-MSSQL.
|
/// Verbindungsparameter zur ERPframe-MSSQL.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -141,12 +141,18 @@ SELECT
|
|||||||
adr.Hausnummer AS custHouseNumber,
|
adr.Hausnummer AS custHouseNumber,
|
||||||
LTRIM(RTRIM(adr.PLZ)) AS custPostalCode,
|
LTRIM(RTRIM(adr.PLZ)) AS custPostalCode,
|
||||||
adr.Ort AS custCity,
|
adr.Ort AS custCity,
|
||||||
adr.Land AS custCountry,
|
LTRIM(RTRIM(hland.Land)) AS custCountry,
|
||||||
COALESCE(dadr.Strasse, adr.Strasse) AS delivStreet,
|
adr.Land AS custAddition,
|
||||||
COALESCE(dadr.Hausnummer, adr.Hausnummer) AS delivHouseNumber,
|
-- Lieferadresse als Ganzes: gibt es eine eigene, kommen ALLE Felder aus
|
||||||
LTRIM(RTRIM(COALESCE(dadr.PLZ, adr.PLZ))) AS delivPostalCode,
|
-- ihr, sonst alle aus der Belegadresse. Kein Feld-für-Feld-COALESCE —
|
||||||
COALESCE(dadr.Ort, adr.Ort) AS delivCity,
|
-- das hat leere Felder der Lieferadresse mit Werten des Bestellers
|
||||||
COALESCE(dadr.Land, adr.Land) AS delivCountry,
|
-- 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._Uhrzeit_Txt AS varchar(200)))) AS desiredTime,
|
||||||
LTRIM(RTRIM(CAST(bk.Kopftext AS varchar(MAX)))) AS specialAgreements,
|
LTRIM(RTRIM(CAST(bk.Kopftext AS varchar(MAX)))) AS specialAgreements,
|
||||||
CAST(ISNULL(bk._Anz_Bestellung, 0) AS FLOAT) AS prepaidAmount,
|
CAST(ISNULL(bk._Anz_Bestellung, 0) AS FLOAT) AS prepaidAmount,
|
||||||
@ -174,6 +180,8 @@ SELECT
|
|||||||
FROM Belegkopf bk
|
FROM Belegkopf bk
|
||||||
JOIN Adressen adr ON bk.AdressId = adr.ROW_ID
|
JOIN Adressen adr ON bk.AdressId = adr.ROW_ID
|
||||||
LEFT JOIN Adressen dadr ON bk.LieferAdressId = dadr.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 radr ON bk.RechnungsAdressId = radr.ROW_ID
|
||||||
LEFT JOIN Adressen kadr ON bk.AnsprechpartnerId = kadr.ROW_ID
|
LEFT JOIN Adressen kadr ON bk.AnsprechpartnerId = kadr.ROW_ID
|
||||||
JOIN Belegzeilen bz ON bk.row_id = bz.ParentID
|
JOIN Belegzeilen bz ON bk.row_id = bz.ParentID
|
||||||
@ -215,12 +223,18 @@ SELECT
|
|||||||
adr.Hausnummer AS custHouseNumber,
|
adr.Hausnummer AS custHouseNumber,
|
||||||
LTRIM(RTRIM(adr.PLZ)) AS custPostalCode,
|
LTRIM(RTRIM(adr.PLZ)) AS custPostalCode,
|
||||||
adr.Ort AS custCity,
|
adr.Ort AS custCity,
|
||||||
adr.Land AS custCountry,
|
LTRIM(RTRIM(hland.Land)) AS custCountry,
|
||||||
COALESCE(dadr.Strasse, adr.Strasse) AS delivStreet,
|
adr.Land AS custAddition,
|
||||||
COALESCE(dadr.Hausnummer, adr.Hausnummer) AS delivHouseNumber,
|
-- Lieferadresse als Ganzes: gibt es eine eigene, kommen ALLE Felder aus
|
||||||
LTRIM(RTRIM(COALESCE(dadr.PLZ, adr.PLZ))) AS delivPostalCode,
|
-- ihr, sonst alle aus der Belegadresse. Kein Feld-für-Feld-COALESCE —
|
||||||
COALESCE(dadr.Ort, adr.Ort) AS delivCity,
|
-- das hat leere Felder der Lieferadresse mit Werten des Bestellers
|
||||||
COALESCE(dadr.Land, adr.Land) AS delivCountry,
|
-- 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._Uhrzeit_Txt AS varchar(200)))) AS desiredTime,
|
||||||
LTRIM(RTRIM(CAST(bk.Kopftext AS varchar(MAX)))) AS specialAgreements,
|
LTRIM(RTRIM(CAST(bk.Kopftext AS varchar(MAX)))) AS specialAgreements,
|
||||||
CAST(ISNULL(bk._Anz_Bestellung, 0) AS FLOAT) AS prepaidAmount,
|
CAST(ISNULL(bk._Anz_Bestellung, 0) AS FLOAT) AS prepaidAmount,
|
||||||
@ -243,6 +257,8 @@ SELECT
|
|||||||
FROM Belegkopf bk
|
FROM Belegkopf bk
|
||||||
JOIN Adressen adr ON bk.AdressId = adr.ROW_ID
|
JOIN Adressen adr ON bk.AdressId = adr.ROW_ID
|
||||||
LEFT JOIN Adressen dadr ON bk.LieferAdressId = dadr.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 radr ON bk.RechnungsAdressId = radr.ROW_ID
|
||||||
LEFT JOIN Adressen kadr ON bk.AnsprechpartnerId = kadr.ROW_ID
|
LEFT JOIN Adressen kadr ON bk.AnsprechpartnerId = kadr.ROW_ID
|
||||||
JOIN Belegzeilen bz ON bk.row_id = bz.ParentID
|
JOIN Belegzeilen bz ON bk.row_id = bz.ParentID
|
||||||
@ -412,6 +428,14 @@ fn read_all_contact_sources(row: &tiberius::Row) -> Vec<SyncContactSource> {
|
|||||||
.collect()
|
.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 {
|
fn map_row(row: &tiberius::Row) -> ErpRow {
|
||||||
ErpRow {
|
ErpRow {
|
||||||
driver_personalnummer: i64c(row, "driverPersonalnummer"),
|
driver_personalnummer: i64c(row, "driverPersonalnummer"),
|
||||||
@ -427,6 +451,7 @@ fn map_row(row: &tiberius::Row) -> ErpRow {
|
|||||||
postal_code: s(row, "custPostalCode"),
|
postal_code: s(row, "custPostalCode"),
|
||||||
city: s(row, "custCity"),
|
city: s(row, "custCity"),
|
||||||
country: s(row, "custCountry"),
|
country: s(row, "custCountry"),
|
||||||
|
addition: address_addition(opt_s(row, "custAddition"), &s(row, "custCountry")),
|
||||||
},
|
},
|
||||||
deliv: Address {
|
deliv: Address {
|
||||||
street: s(row, "delivStreet"),
|
street: s(row, "delivStreet"),
|
||||||
@ -434,6 +459,7 @@ fn map_row(row: &tiberius::Row) -> ErpRow {
|
|||||||
postal_code: s(row, "delivPostalCode"),
|
postal_code: s(row, "delivPostalCode"),
|
||||||
city: s(row, "delivCity"),
|
city: s(row, "delivCity"),
|
||||||
country: s(row, "delivCountry"),
|
country: s(row, "delivCountry"),
|
||||||
|
addition: address_addition(opt_s(row, "delivAddition"), &s(row, "delivCountry")),
|
||||||
},
|
},
|
||||||
desired_time: opt_s(row, "desiredTime"),
|
desired_time: opt_s(row, "desiredTime"),
|
||||||
special_agreements: opt_s(row, "specialAgreements"),
|
special_agreements: opt_s(row, "specialAgreements"),
|
||||||
@ -584,3 +610,23 @@ impl ErpDeliverySource for MssqlErpDeliverySource {
|
|||||||
Ok(tours)
|
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_postal_code: String,
|
||||||
snap_city: String,
|
snap_city: String,
|
||||||
snap_country: String,
|
snap_country: String,
|
||||||
|
snap_addition: Option<String>,
|
||||||
assigned_car_id: Option<Uuid>,
|
assigned_car_id: Option<Uuid>,
|
||||||
desired_time: Option<String>,
|
desired_time: Option<String>,
|
||||||
special_agreements: Option<String>,
|
special_agreements: Option<String>,
|
||||||
@ -68,7 +69,7 @@ async fn lock_delivery(
|
|||||||
r#"
|
r#"
|
||||||
SELECT
|
SELECT
|
||||||
id, tour_id, erp_belegart_id, erp_belegnummer, customer_id,
|
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,
|
assigned_car_id, desired_time, special_agreements,
|
||||||
state, prepaid_amount, payment_method_id
|
state, prepaid_amount, payment_method_id
|
||||||
FROM deliveries
|
FROM deliveries
|
||||||
@ -109,6 +110,7 @@ fn build_delivery(row: DeliveryRow, state: DeliveryState, state_reason: Option<S
|
|||||||
postal_code: row.snap_postal_code,
|
postal_code: row.snap_postal_code,
|
||||||
city: row.snap_city,
|
city: row.snap_city,
|
||||||
country: row.snap_country,
|
country: row.snap_country,
|
||||||
|
addition: row.snap_addition,
|
||||||
},
|
},
|
||||||
assigned_car_id: row.assigned_car_id,
|
assigned_car_id: row.assigned_car_id,
|
||||||
contact_person_ids: contacts,
|
contact_person_ids: contacts,
|
||||||
|
|||||||
@ -39,6 +39,7 @@ struct DeliveryRow {
|
|||||||
snap_postal_code: String,
|
snap_postal_code: String,
|
||||||
snap_city: String,
|
snap_city: String,
|
||||||
snap_country: String,
|
snap_country: String,
|
||||||
|
snap_addition: Option<String>,
|
||||||
assigned_car_id: Option<Uuid>,
|
assigned_car_id: Option<Uuid>,
|
||||||
desired_time: Option<String>,
|
desired_time: Option<String>,
|
||||||
special_agreements: Option<String>,
|
special_agreements: Option<String>,
|
||||||
@ -112,7 +113,7 @@ async fn lock_delivery(
|
|||||||
r#"
|
r#"
|
||||||
SELECT
|
SELECT
|
||||||
id, tour_id, erp_belegart_id, erp_belegnummer, customer_id,
|
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,
|
assigned_car_id, desired_time, special_agreements,
|
||||||
state, prepaid_amount, payment_method_id
|
state, prepaid_amount, payment_method_id
|
||||||
FROM deliveries
|
FROM deliveries
|
||||||
@ -193,6 +194,7 @@ impl DeliveryRepository for PgDeliveryRepository {
|
|||||||
postal_code: row.snap_postal_code,
|
postal_code: row.snap_postal_code,
|
||||||
city: row.snap_city,
|
city: row.snap_city,
|
||||||
country: row.snap_country,
|
country: row.snap_country,
|
||||||
|
addition: row.snap_addition,
|
||||||
},
|
},
|
||||||
assigned_car_id: row.assigned_car_id,
|
assigned_car_id: row.assigned_car_id,
|
||||||
contact_person_ids,
|
contact_person_ids,
|
||||||
@ -250,6 +252,7 @@ impl DeliveryRepository for PgDeliveryRepository {
|
|||||||
postal_code: row.snap_postal_code,
|
postal_code: row.snap_postal_code,
|
||||||
city: row.snap_city,
|
city: row.snap_city,
|
||||||
country: row.snap_country,
|
country: row.snap_country,
|
||||||
|
addition: row.snap_addition,
|
||||||
},
|
},
|
||||||
assigned_car_id: car_id,
|
assigned_car_id: car_id,
|
||||||
contact_person_ids,
|
contact_person_ids,
|
||||||
|
|||||||
@ -75,6 +75,7 @@ struct DeliveryRow {
|
|||||||
snap_postal_code: String,
|
snap_postal_code: String,
|
||||||
snap_city: String,
|
snap_city: String,
|
||||||
snap_country: String,
|
snap_country: String,
|
||||||
|
snap_addition: Option<String>,
|
||||||
assigned_car_id: Option<Uuid>,
|
assigned_car_id: Option<Uuid>,
|
||||||
desired_time: Option<String>,
|
desired_time: Option<String>,
|
||||||
special_agreements: Option<String>,
|
special_agreements: Option<String>,
|
||||||
@ -113,6 +114,7 @@ struct CustomerRow {
|
|||||||
postal_code: String,
|
postal_code: String,
|
||||||
city: String,
|
city: String,
|
||||||
country: String,
|
country: String,
|
||||||
|
address_addition: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
@ -251,6 +253,7 @@ fn map_customer(row: CustomerRow) -> Customer {
|
|||||||
postal_code: row.postal_code,
|
postal_code: row.postal_code,
|
||||||
city: row.city,
|
city: row.city,
|
||||||
country: row.country,
|
country: row.country,
|
||||||
|
addition: row.address_addition,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,6 +416,7 @@ fn map_delivery(
|
|||||||
postal_code: row.snap_postal_code,
|
postal_code: row.snap_postal_code,
|
||||||
city: row.snap_city,
|
city: row.snap_city,
|
||||||
country: row.snap_country,
|
country: row.snap_country,
|
||||||
|
addition: row.snap_addition,
|
||||||
},
|
},
|
||||||
assigned_car_id: row.assigned_car_id,
|
assigned_car_id: row.assigned_car_id,
|
||||||
contact_person_ids,
|
contact_person_ids,
|
||||||
@ -490,6 +494,7 @@ impl TourRepository for PgTourRepository {
|
|||||||
SELECT
|
SELECT
|
||||||
id, tour_id, erp_belegart_id, erp_belegnummer, customer_id,
|
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,
|
assigned_car_id, desired_time, special_agreements,
|
||||||
state, state_reason, sort_order,
|
state, state_reason, sort_order,
|
||||||
prepaid_amount, payment_method_id
|
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 customer_ids_vec: Vec<Uuid> = customer_ids.into_iter().collect();
|
||||||
let customers = sqlx::query_as::<_, CustomerRow>(
|
let customers = sqlx::query_as::<_, CustomerRow>(
|
||||||
r#"
|
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
|
FROM customers
|
||||||
WHERE id = ANY($1)
|
WHERE id = ANY($1)
|
||||||
ORDER BY name
|
ORDER BY name
|
||||||
@ -1105,15 +1111,17 @@ async fn upsert_customer(
|
|||||||
let id: Uuid = sqlx::query_scalar(
|
let id: Uuid = sqlx::query_scalar(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO customers (
|
INSERT INTO customers (
|
||||||
erp_customer_id, name, street, house_number, postal_code, city, country
|
erp_customer_id, name, street, house_number, postal_code, city, country,
|
||||||
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
address_addition
|
||||||
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||||
ON CONFLICT (erp_customer_id) DO UPDATE SET
|
ON CONFLICT (erp_customer_id) DO UPDATE SET
|
||||||
name = EXCLUDED.name,
|
name = EXCLUDED.name,
|
||||||
street = EXCLUDED.street,
|
street = EXCLUDED.street,
|
||||||
house_number = EXCLUDED.house_number,
|
house_number = EXCLUDED.house_number,
|
||||||
postal_code = EXCLUDED.postal_code,
|
postal_code = EXCLUDED.postal_code,
|
||||||
city = EXCLUDED.city,
|
city = EXCLUDED.city,
|
||||||
country = EXCLUDED.country
|
country = EXCLUDED.country,
|
||||||
|
address_addition = EXCLUDED.address_addition
|
||||||
RETURNING id
|
RETURNING id
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
@ -1124,6 +1132,7 @@ async fn upsert_customer(
|
|||||||
.bind(&delivery.customer_address.postal_code)
|
.bind(&delivery.customer_address.postal_code)
|
||||||
.bind(&delivery.customer_address.city)
|
.bind(&delivery.customer_address.city)
|
||||||
.bind(&delivery.customer_address.country)
|
.bind(&delivery.customer_address.country)
|
||||||
|
.bind(delivery.customer_address.addition.as_deref())
|
||||||
.fetch_one(&mut **tx)
|
.fetch_one(&mut **tx)
|
||||||
.await
|
.await
|
||||||
.map_err(db)?;
|
.map_err(db)?;
|
||||||
@ -1163,8 +1172,8 @@ async fn upsert_delivery(
|
|||||||
erp_belegnummer, customer_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,
|
||||||
sort_order, desired_time, special_agreements,
|
sort_order, desired_time, special_agreements,
|
||||||
prepaid_amount, payment_method_id
|
prepaid_amount, payment_method_id, snap_addition
|
||||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
) 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
|
ON CONFLICT (erp_belegart_id, erp_belegnummer) DO UPDATE SET
|
||||||
tour_id = EXCLUDED.tour_id,
|
tour_id = EXCLUDED.tour_id,
|
||||||
erp_belegart_code = EXCLUDED.erp_belegart_code,
|
erp_belegart_code = EXCLUDED.erp_belegart_code,
|
||||||
@ -1175,6 +1184,7 @@ async fn upsert_delivery(
|
|||||||
snap_postal_code = EXCLUDED.snap_postal_code,
|
snap_postal_code = EXCLUDED.snap_postal_code,
|
||||||
snap_city = EXCLUDED.snap_city,
|
snap_city = EXCLUDED.snap_city,
|
||||||
snap_country = EXCLUDED.snap_country,
|
snap_country = EXCLUDED.snap_country,
|
||||||
|
snap_addition = EXCLUDED.snap_addition,
|
||||||
sort_order = EXCLUDED.sort_order,
|
sort_order = EXCLUDED.sort_order,
|
||||||
desired_time = EXCLUDED.desired_time,
|
desired_time = EXCLUDED.desired_time,
|
||||||
special_agreements = EXCLUDED.special_agreements,
|
special_agreements = EXCLUDED.special_agreements,
|
||||||
@ -1199,6 +1209,7 @@ async fn upsert_delivery(
|
|||||||
.bind(delivery.special_agreements.as_deref())
|
.bind(delivery.special_agreements.as_deref())
|
||||||
.bind(delivery.prepaid_amount)
|
.bind(delivery.prepaid_amount)
|
||||||
.bind(payment_method_id)
|
.bind(payment_method_id)
|
||||||
|
.bind(delivery.delivery_address.addition.as_deref())
|
||||||
.fetch_one(&mut **tx)
|
.fetch_one(&mut **tx)
|
||||||
.await
|
.await
|
||||||
.map_err(db)?;
|
.map_err(db)?;
|
||||||
|
|||||||
@ -482,11 +482,17 @@ impl DeliveryReportRenderer for PdfDeliveryReportRenderer {
|
|||||||
);
|
);
|
||||||
p.kv("Besteller", &format!("{} (Nr. {})", d.customer_name, d.customer_number));
|
p.kv("Besteller", &format!("{} (Nr. {})", d.customer_name, d.customer_number));
|
||||||
p.kv("Adresse Besteller", &r.orderer_address);
|
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() {
|
if !r.orderer_phones.is_empty() {
|
||||||
p.kv("Tel. Besteller", &r.orderer_phones.join(" · "));
|
p.kv("Tel. Besteller", &r.orderer_phones.join(" · "));
|
||||||
}
|
}
|
||||||
p.kv("Empfänger", &r.name);
|
p.kv("Empfänger", &r.name);
|
||||||
p.kv("Lieferadresse", &d.address);
|
p.kv("Lieferadresse", &d.address);
|
||||||
|
if let Some(z) = &d.address_addition {
|
||||||
|
p.kv("Zusatz", z);
|
||||||
|
}
|
||||||
if !r.phones.is_empty() {
|
if !r.phones.is_empty() {
|
||||||
p.kv("Tel. Empfänger", &r.phones.join(" · "));
|
p.kv("Tel. Empfänger", &r.phones.join(" · "));
|
||||||
}
|
}
|
||||||
@ -494,6 +500,9 @@ impl DeliveryReportRenderer for PdfDeliveryReportRenderer {
|
|||||||
None => {
|
None => {
|
||||||
p.kv("Kunde", &format!("{} (Nr. {})", d.customer_name, d.customer_number));
|
p.kv("Kunde", &format!("{} (Nr. {})", d.customer_name, d.customer_number));
|
||||||
p.kv("Adresse", &d.address);
|
p.kv("Adresse", &d.address);
|
||||||
|
if let Some(z) = &d.address_addition {
|
||||||
|
p.kv("Zusatz", z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.kv("Wunschzeit", opt(&d.desired_time));
|
p.kv("Wunschzeit", opt(&d.desired_time));
|
||||||
|
|||||||
@ -15,6 +15,7 @@ use holzleitner_application::dto::{
|
|||||||
};
|
};
|
||||||
use holzleitner_application::error::ApplicationError;
|
use holzleitner_application::error::ApplicationError;
|
||||||
use holzleitner_application::ports::DeliveryReportRepository;
|
use holzleitner_application::ports::DeliveryReportRepository;
|
||||||
|
use holzleitner_domain::Address;
|
||||||
|
|
||||||
pub struct PgDeliveryReportRepository {
|
pub struct PgDeliveryReportRepository {
|
||||||
pool: PgPool,
|
pool: PgPool,
|
||||||
@ -49,11 +50,13 @@ struct HeadRow {
|
|||||||
cust_postal_code: String,
|
cust_postal_code: String,
|
||||||
cust_city: String,
|
cust_city: String,
|
||||||
cust_country: String,
|
cust_country: String,
|
||||||
|
cust_addition: Option<String>,
|
||||||
snap_street: Option<String>,
|
snap_street: Option<String>,
|
||||||
snap_house_number: Option<String>,
|
snap_house_number: Option<String>,
|
||||||
snap_postal_code: Option<String>,
|
snap_postal_code: Option<String>,
|
||||||
snap_city: Option<String>,
|
snap_city: Option<String>,
|
||||||
snap_country: Option<String>,
|
snap_country: Option<String>,
|
||||||
|
snap_addition: Option<String>,
|
||||||
desired_time: Option<String>,
|
desired_time: Option<String>,
|
||||||
special_agreements: Option<String>,
|
special_agreements: Option<String>,
|
||||||
prepaid_amount: f64,
|
prepaid_amount: f64,
|
||||||
@ -185,32 +188,6 @@ struct AttachmentRow {
|
|||||||
uploaded_by: i64,
|
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]
|
#[async_trait]
|
||||||
impl DeliveryReportRepository for PgDeliveryReportRepository {
|
impl DeliveryReportRepository for PgDeliveryReportRepository {
|
||||||
async fn load(
|
async fn load(
|
||||||
@ -229,9 +206,9 @@ impl DeliveryReportRepository for PgDeliveryReportRepository {
|
|||||||
c.erp_customer_id, c.name AS customer_name,
|
c.erp_customer_id, c.name AS customer_name,
|
||||||
c.street AS cust_street, c.house_number AS cust_house_number,
|
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.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_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
|
d.desired_time, d.special_agreements, d.prepaid_amount
|
||||||
FROM deliveries d
|
FROM deliveries d
|
||||||
JOIN tours t ON t.id = d.tour_id
|
JOIN tours t ON t.id = d.tour_id
|
||||||
@ -432,19 +409,21 @@ impl DeliveryReportRepository for PgDeliveryReportRepository {
|
|||||||
})
|
})
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let snap = AddressParts {
|
let snap = Address {
|
||||||
street: head.snap_street.unwrap_or_default(),
|
street: head.snap_street.unwrap_or_default(),
|
||||||
house_number: head.snap_house_number.unwrap_or_default(),
|
house_number: head.snap_house_number.unwrap_or_default(),
|
||||||
postal_code: head.snap_postal_code.unwrap_or_default(),
|
postal_code: head.snap_postal_code.unwrap_or_default(),
|
||||||
city: head.snap_city.unwrap_or_default(),
|
city: head.snap_city.unwrap_or_default(),
|
||||||
country: head.snap_country.unwrap_or_default(),
|
country: head.snap_country.unwrap_or_default(),
|
||||||
|
addition: head.snap_addition,
|
||||||
};
|
};
|
||||||
let orderer = AddressParts {
|
let orderer = Address {
|
||||||
street: head.cust_street,
|
street: head.cust_street,
|
||||||
house_number: head.cust_house_number,
|
house_number: head.cust_house_number,
|
||||||
postal_code: head.cust_postal_code,
|
postal_code: head.cust_postal_code,
|
||||||
city: head.cust_city,
|
city: head.cust_city,
|
||||||
country: head.cust_country,
|
country: head.cust_country,
|
||||||
|
addition: head.cust_addition,
|
||||||
};
|
};
|
||||||
let different_recipient = different_recipient(
|
let different_recipient = different_recipient(
|
||||||
&head.customer_name,
|
&head.customer_name,
|
||||||
@ -454,6 +433,7 @@ impl DeliveryReportRepository for PgDeliveryReportRepository {
|
|||||||
sources.iter().find(|s| s.role == "delivery"),
|
sources.iter().find(|s| s.role == "delivery"),
|
||||||
);
|
);
|
||||||
let address = snap.one_line();
|
let address = snap.one_line();
|
||||||
|
let address_addition = snap.addition.clone();
|
||||||
|
|
||||||
Ok(Some(DeliveryReportData {
|
Ok(Some(DeliveryReportData {
|
||||||
generated_at: Utc::now(),
|
generated_at: Utc::now(),
|
||||||
@ -470,6 +450,7 @@ impl DeliveryReportRepository for PgDeliveryReportRepository {
|
|||||||
customer_number: head.erp_customer_id,
|
customer_number: head.erp_customer_id,
|
||||||
customer_name: head.customer_name,
|
customer_name: head.customer_name,
|
||||||
address,
|
address,
|
||||||
|
address_addition,
|
||||||
different_recipient,
|
different_recipient,
|
||||||
desired_time: head.desired_time,
|
desired_time: head.desired_time,
|
||||||
special_agreements: head.special_agreements,
|
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.
|
/// Gleiche Anschrift, unabhängig von Groß-/Kleinschreibung und Leerzeichen.
|
||||||
struct AddressParts {
|
/// Land und Zusatz zählen nicht (im ERP uneinheitlich gepflegt).
|
||||||
street: String,
|
fn same_place(a: &Address, b: &Address) -> bool {
|
||||||
house_number: String,
|
normalized(&a.street) == normalized(&b.street)
|
||||||
postal_code: String,
|
&& normalized(&a.house_number) == normalized(&b.house_number)
|
||||||
city: String,
|
&& normalized(&a.postal_code) == normalized(&b.postal_code)
|
||||||
country: String,
|
&& normalized(&a.city) == normalized(&b.city)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalized(value: &str) -> String {
|
fn normalized(value: &str) -> String {
|
||||||
@ -637,8 +597,8 @@ fn normalized(value: &str) -> String {
|
|||||||
/// Kundennamen).
|
/// Kundennamen).
|
||||||
fn different_recipient(
|
fn different_recipient(
|
||||||
customer_name: &str,
|
customer_name: &str,
|
||||||
orderer: &AddressParts,
|
orderer: &Address,
|
||||||
delivery_address: &AddressParts,
|
delivery_address: &Address,
|
||||||
header: Option<&ContactSourceRow>,
|
header: Option<&ContactSourceRow>,
|
||||||
delivery: Option<&ContactSourceRow>,
|
delivery: Option<&ContactSourceRow>,
|
||||||
) -> Option<ReportRecipient> {
|
) -> Option<ReportRecipient> {
|
||||||
@ -647,7 +607,7 @@ fn different_recipient(
|
|||||||
.unwrap_or_else(|| customer_name.to_string());
|
.unwrap_or_else(|| customer_name.to_string());
|
||||||
let recipient_name = delivery.and_then(ContactSourceRow::full_name);
|
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
|
let name_differs = recipient_name
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.is_some_and(|n| normalized(n) != normalized(&orderer_name));
|
.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()),
|
name: recipient_name.unwrap_or_else(|| customer_name.to_string()),
|
||||||
phones: delivery.map(|s| s.phones.clone()).unwrap_or_default(),
|
phones: delivery.map(|s| s.phones.clone()).unwrap_or_default(),
|
||||||
orderer_address: orderer.one_line(),
|
orderer_address: orderer.one_line(),
|
||||||
|
orderer_address_addition: orderer.addition.clone(),
|
||||||
orderer_phones: header.map(|s| s.phones.clone()).unwrap_or_default(),
|
orderer_phones: header.map(|s| s.phones.clone()).unwrap_or_default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -667,13 +628,14 @@ fn different_recipient(
|
|||||||
mod different_recipient_tests {
|
mod different_recipient_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn addr(street: &str, city: &str) -> AddressParts {
|
fn addr(street: &str, city: &str) -> Address {
|
||||||
AddressParts {
|
Address {
|
||||||
street: street.into(),
|
street: street.into(),
|
||||||
house_number: "1".into(),
|
house_number: "1".into(),
|
||||||
postal_code: "41460".into(),
|
postal_code: "41460".into(),
|
||||||
city: city.into(),
|
city: city.into(),
|
||||||
country: "DE".into(),
|
country: "Deutschland".into(),
|
||||||
|
addition: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -714,7 +676,7 @@ mod different_recipient_tests {
|
|||||||
)
|
)
|
||||||
.expect("abweichend");
|
.expect("abweichend");
|
||||||
assert_eq!(r.name, "Musterfrau Erika");
|
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()]);
|
assert_eq!(r.phones, vec!["0151 1".to_string()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
32
migrations/0034_address_addition.sql
Normal file
32
migrations/0034_address_addition.sql
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
-- Adresszusatz getrennt vom Land.
|
||||||
|
--
|
||||||
|
-- Das ERP-Textfeld `Adressen.Land` wird von der Sachbearbeitung praktisch
|
||||||
|
-- nie für das Land genutzt, sondern für Ortsteil, Etage oder Hinweise
|
||||||
|
-- („Holzheim", „3 Etage", „Parterre"). Bisher landete es in `country` und
|
||||||
|
-- erschien im Lieferbericht als vermeintliches Land. Ab jetzt:
|
||||||
|
-- * `*_addition` = dieser freie Zusatz,
|
||||||
|
-- * `country` = echtes Land aus dem ERP-Länderstamm (`LandID`).
|
||||||
|
--
|
||||||
|
-- Bestandsdaten: der bisherige `country`-Text war immer dieses Freitextfeld
|
||||||
|
-- → in den Zusatz verschieben. Reine Deutschland-Kürzel („D", „DEU", …)
|
||||||
|
-- sind kein Zusatz und werden verworfen. Der nächste Sync setzt `country`
|
||||||
|
-- wieder korrekt aus dem Länderstamm.
|
||||||
|
|
||||||
|
ALTER TABLE customers ADD COLUMN address_addition TEXT;
|
||||||
|
ALTER TABLE deliveries ADD COLUMN snap_addition TEXT;
|
||||||
|
|
||||||
|
UPDATE customers
|
||||||
|
SET address_addition = CASE
|
||||||
|
WHEN lower(btrim(country)) IN ('', 'd', 'de', 'deu', 'deutschland', 'germany', 'brd')
|
||||||
|
THEN NULL
|
||||||
|
ELSE btrim(country)
|
||||||
|
END,
|
||||||
|
country = '';
|
||||||
|
|
||||||
|
UPDATE deliveries
|
||||||
|
SET snap_addition = CASE
|
||||||
|
WHEN lower(btrim(snap_country)) IN ('', 'd', 'de', 'deu', 'deutschland', 'germany', 'brd')
|
||||||
|
THEN NULL
|
||||||
|
ELSE btrim(snap_country)
|
||||||
|
END,
|
||||||
|
snap_country = '';
|
||||||
Reference in New Issue
Block a user