backbone_maintenance/application/service/maintenance_ports.rs
1//! Maintenance's inventory seam (hand-authored, user-owned).
2//!
3//! Maintenance owns no stock. A visit's parts are issued out of `backbone-inventory` on completion, and
4//! inventory VALUES them at its moving-average — the parts cost is inventory's number, not a made-up one.
5//! Maintenance holds only the `InventoryPort` trait; a composing service (and the seam test) wires it over
6//! the real inventory write path. **Zero normal Cargo edge** to inventory.
7
8use rust_decimal::Decimal;
9use serde::{Deserialize, Serialize};
10use uuid::Uuid;
11
12/// A request to issue a visit's parts out of a warehouse.
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14pub struct PartsIssue {
15 /// Legacy twin (ADR-0029): the inventory seam's wire shape still carries the tenant (the
16 /// composing adapter decides what it binds); filled from the ambient org scope's company echo.
17 pub company_id: Uuid,
18 pub visit_id: Uuid,
19 pub warehouse_id: Uuid,
20 /// Stable dedup key. A retry of the SAME issue (a crash/error mid-completion) must NOT move stock
21 /// twice — the inventory implementation returns the prior result for a repeated key.
22 pub idempotency_key: String,
23 pub lines: Vec<IssueLine>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
27pub struct IssueLine {
28 pub item_id: Uuid,
29 pub quantity: Decimal,
30}
31
32/// The valued result of an issue — inventory reports the rate + value it removed for each line.
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34pub struct IssueAck {
35 pub total_value: Decimal,
36 pub lines: Vec<IssuedLineValue>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
40pub struct IssuedLineValue {
41 pub item_id: Uuid,
42 pub quantity: Decimal,
43 pub rate: Decimal,
44 pub value: Decimal,
45}
46
47/// Inventory's rejection of an issue (e.g. insufficient stock).
48#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
49pub struct InventoryRejected {
50 pub code: String,
51 pub message: String,
52}
53
54/// The inventory seam maintenance drives.
55#[async_trait::async_trait]
56pub trait InventoryPort: Send + Sync {
57 /// Remove a visit's parts from stock and return what they were worth.
58 async fn issue_parts(&self, req: &PartsIssue) -> Result<IssueAck, InventoryRejected>;
59}