Skip to main content

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}