feat(review): Vier-Augen-Prüfung für geänderte Lieferscheine

Geänderte Lieferscheine (Artikel entfernt/teil-gutgeschrieben ODER
Geld-Gutschrift) sollen von der Fakturierung gegengeprüft werden, statt
die Menge im ERP zu reduzieren (was den Lagerbestand inkonsistent machte,
weil der Raw-Writeback die ERPframe-Engine umgeht).

- ERP-Writeback: kein Setzen der Belegzeilen-Menge mehr → ERP-Beleg bleibt
  im Original (kein Bestandskonflikt). Geld-Gutschrift wird weiter
  geschrieben (unkritisch, nur Vier-Augen). delivered-at/State/Zahlbed
  bleiben.
- Migration 0031: review_resolved_at/by/note auf deliveries. Der Status
  wird ABGELEITET (Abweichung via credited_quantity / aktueller Gutschrift
  vs. resolved_at), daher: Originalzustand wiederhergestellt ⇒ Flag weg;
  nach Bestätigung erneut geändert ⇒ wieder offen.
- ReviewRepository (Port + Pg-Impl) + Use Cases ListPendingReviews/
  ResolveReview.
- Endpoints: GET /admin/reviews (offene Prüfungen inkl. Änderungsdetails
  aus scan_audit/credit_audit) + POST /admin/reviews/{delivery_id}/resolve
  (Admin-Key).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-06-23 18:02:27 +02:00
parent 1e6dfb10b0
commit 2d364f3fb7
12 changed files with 503 additions and 32 deletions

View File

@ -24,6 +24,7 @@ pub mod mark_mail_sent;
pub mod payment_methods;
pub mod process_delivery_report;
pub mod push_completion_to_erp;
pub mod reviews;
pub mod services;
pub mod set_delivery_order;
pub mod sync_tour;
@ -54,6 +55,7 @@ pub use payment_methods::{
};
pub use process_delivery_report::ProcessDeliveryReportUseCase;
pub use push_completion_to_erp::PushCompletionToErpUseCase;
pub use reviews::{ListPendingReviewsUseCase, ResolveReviewUseCase};
pub use services::{
CreateServiceUseCase, DeleteDeliveryServiceUseCase, DeleteServiceUseCase,
ListServicesUseCase, SetDeliveryServiceUseCase, UpdateServiceUseCase,

View File

@ -0,0 +1,54 @@
//! Use Cases für die Vier-Augen-Prüfung geänderter Lieferscheine.
//!
//! Dünne Hüllen über [`ReviewRepository`]: die Fakturierung listet offene
//! Prüfungen und löst sie nach manueller Entscheidung auf.
use std::sync::Arc;
use uuid::Uuid;
use crate::error::ApplicationError;
use crate::ports::{PendingReview, ReviewRepository};
/// Listet alle offenen (noch nicht bestätigten) Prüfungen geänderter
/// Lieferscheine.
pub struct ListPendingReviewsUseCase {
repository: Arc<dyn ReviewRepository>,
}
impl ListPendingReviewsUseCase {
pub fn new(repository: Arc<dyn ReviewRepository>) -> Self {
Self { repository }
}
pub async fn execute(&self) -> Result<Vec<PendingReview>, ApplicationError> {
self.repository.list_pending().await
}
}
/// Löst die Prüfung einer Lieferung auf (Vier-Augen-Bestätigung).
pub struct ResolveReviewUseCase {
repository: Arc<dyn ReviewRepository>,
}
impl ResolveReviewUseCase {
pub fn new(repository: Arc<dyn ReviewRepository>) -> Self {
Self { repository }
}
pub async fn execute(
&self,
delivery_id: Uuid,
resolved_by: &str,
note: Option<&str>,
) -> Result<(), ApplicationError> {
let by = resolved_by.trim();
if by.is_empty() {
return Err(ApplicationError::Validation(
"resolved_by darf nicht leer sein".into(),
));
}
let note = note.map(str::trim).filter(|s| !s.is_empty());
self.repository.resolve(delivery_id, by, note).await
}
}