feat(abschluss): interne Notiz des Fahrers beim Abschluss
- CompleteDeliveryAcknowledgements.internalNote (optional, getrimmt, max. 2000 Zeichen) wird atomar mit dem Abschluss gespeichert (delivery_completions.internal_note) - Bewusst keine delivery_notes-Zeile: loest weder die Kunden-Bestaetigung der Notizen aus noch erscheint sie im Notizen-Step der App - Lieferbericht: eigener Abschnitt "Interne Notiz" mit Zeitpunkt und Fahrer - Bericht: Betrag in "Betrag erhalten" ans Zeilenende (Helvetica setzt nach dem Euro-Zeichen zu eng) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@ -37,4 +37,9 @@ pub struct CompleteDeliveryAcknowledgements {
|
||||
/// Fahrzeug des Akteurs (Audit-Spur). Muss zum Account gehören.
|
||||
#[serde(default)]
|
||||
pub author_car_id: Option<Uuid>,
|
||||
/// Optionale interne Notiz des Fahrers, abgefragt nach beiden
|
||||
/// Unterschriften. Nur für interne Zwecke (Lieferbericht), nicht Teil
|
||||
/// der Kunden-Bestätigung. Leer/Whitespace = keine Notiz; max. 2000 Zeichen.
|
||||
#[serde(default)]
|
||||
pub internal_note: Option<String>,
|
||||
}
|
||||
|
||||
@ -111,6 +111,8 @@ pub struct ReportCompletion {
|
||||
/// Die beim Abschluss gültige protokollierte Zahlung (None = ohne
|
||||
/// Zahlungs-Step abgeschlossen, z. B. nichts offen oder alte App).
|
||||
pub payment_id: Option<Uuid>,
|
||||
/// Interne Notiz des Fahrers beim Abschluss (`None` = keine).
|
||||
pub internal_note: Option<String>,
|
||||
}
|
||||
|
||||
/// Ein Eintrag im Zahlungsprotokoll („Abkassieren").
|
||||
|
||||
@ -76,6 +76,8 @@ pub struct CompleteDeliveryInput {
|
||||
pub payment_method_id: Option<Uuid>,
|
||||
pub completed_by_personalnummer: i64,
|
||||
pub completed_by_car_id: Option<Uuid>,
|
||||
/// Interne Notiz des Fahrers (bereits getrimmt, `None` = keine).
|
||||
pub internal_note: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@ -21,6 +21,10 @@ use crate::usecases::PushCompletionToErpUseCase;
|
||||
/// Lock prüft). Schlägt das Repo-Gate fehl, bleiben höchstens die beiden
|
||||
/// deterministisch benannten PNG-Dateien liegen — ein erneuter Versuch
|
||||
/// überschreibt sie, es entsteht kein Müll.
|
||||
/// Obergrenze der internen Abschluss-Notiz (Zeichen) — deckungsgleich mit
|
||||
/// dem DB-CHECK in `0033_completion_internal_note.sql`.
|
||||
const MAX_INTERNAL_NOTE_CHARS: usize = 2000;
|
||||
|
||||
pub struct CompleteDeliveryUseCase {
|
||||
repository: Arc<dyn DeliveryCompletionRepository>,
|
||||
signatures: Arc<dyn SignatureStorage>,
|
||||
@ -69,6 +73,20 @@ impl CompleteDeliveryUseCase {
|
||||
"driver signature is required".into(),
|
||||
));
|
||||
}
|
||||
let internal_note = acknowledgements
|
||||
.internal_note
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|n| !n.is_empty())
|
||||
.map(str::to_owned);
|
||||
if internal_note
|
||||
.as_ref()
|
||||
.is_some_and(|n| n.chars().count() > MAX_INTERNAL_NOTE_CHARS)
|
||||
{
|
||||
return Err(ApplicationError::Validation(format!(
|
||||
"internal note must not exceed {MAX_INTERNAL_NOTE_CHARS} characters"
|
||||
)));
|
||||
}
|
||||
if let Some(car_id) = acknowledgements.author_car_id {
|
||||
self.cars
|
||||
.assert_owned_by_account(&[car_id], author_personalnummer)
|
||||
@ -102,6 +120,7 @@ impl CompleteDeliveryUseCase {
|
||||
payment_method_id: acknowledgements.payment_method_id,
|
||||
completed_by_personalnummer: author_personalnummer,
|
||||
completed_by_car_id: acknowledgements.author_car_id,
|
||||
internal_note,
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user