stateset-embedded 0.7.13

Embeddable commerce library - the SQLite of commerce operations
//! Quality Control operations
//!
//! Comprehensive quality management system supporting:
//! - Inspections (receiving, in-process, final, random)
//! - Non-conformance reports (NCRs)
//! - Quality holds on inventory
//! - Defect code management
//!
//! # Example
//!
//! ```rust,ignore
//! use stateset_embedded::{Commerce, CreateInspection, InspectionType};
//! use uuid::Uuid;
//!
//! let commerce = Commerce::new("./store.db")?;
//!
//! // Create an inspection for received goods
//! let inspection = commerce.quality().create_inspection(CreateInspection {
//!     inspection_type: InspectionType::Receiving,
//!     reference_type: "purchase_order".into(),
//!     reference_id: Uuid::new_v4(),
//!     ..Default::default()
//! })?;
//!
//! println!("Created inspection #{}", inspection.inspection_number);
//! # Ok::<(), stateset_embedded::CommerceError>(())
//! ```

use stateset_core::{
    CreateDefectCode, CreateInspection, CreateNonConformance, CreateQualityHold, DefectCode,
    Inspection, InspectionFilter, InspectionItem, InspectionStatus, NcrStatus, NonConformance,
    NonConformanceFilter, QualityHold, QualityHoldFilter, RecordInspectionResult,
    ReleaseQualityHold, Result, UpdateInspection, UpdateNonConformance,
};
use stateset_db::Database;
use std::sync::Arc;
use uuid::Uuid;

/// Quality control management interface.
pub struct Quality {
    db: Arc<dyn Database>,
}

impl std::fmt::Debug for Quality {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Quality").finish_non_exhaustive()
    }
}

impl Quality {
    pub(crate) fn new(db: Arc<dyn Database>) -> Self {
        Self { db }
    }

    // ========================================================================
    // Inspections
    // ========================================================================

    /// Create a new inspection.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// use stateset_embedded::{Commerce, CreateInspection, InspectionType};
    /// use uuid::Uuid;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// let inspection = commerce.quality().create_inspection(CreateInspection {
    ///     inspection_type: InspectionType::Receiving,
    ///     reference_type: "purchase_order".into(),
    ///     reference_id: Uuid::new_v4(),
    ///     ..Default::default()
    /// })?;
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn create_inspection(&self, input: CreateInspection) -> Result<Inspection> {
        self.db.quality().create_inspection(input)
    }

    /// Get an inspection by ID.
    pub fn get_inspection(&self, id: Uuid) -> Result<Option<Inspection>> {
        self.db.quality().get_inspection(id)
    }

    /// Get an inspection by its number.
    pub fn get_inspection_by_number(&self, number: &str) -> Result<Option<Inspection>> {
        self.db.quality().get_inspection_by_number(number)
    }

    /// List inspections with optional filtering.
    pub fn list_inspections(&self, filter: InspectionFilter) -> Result<Vec<Inspection>> {
        self.db.quality().list_inspections(filter)
    }

    /// Update an inspection.
    pub fn update_inspection(&self, id: Uuid, input: UpdateInspection) -> Result<Inspection> {
        self.db.quality().update_inspection(id, input)
    }

    /// Start an inspection (set status to `InProgress`).
    pub fn start_inspection(&self, id: Uuid) -> Result<Inspection> {
        self.db.quality().start_inspection(id)
    }

    /// Record inspection results for an item.
    pub fn record_inspection_result(
        &self,
        input: RecordInspectionResult,
    ) -> Result<InspectionItem> {
        self.db.quality().record_inspection_result(input)
    }

    /// Get inspection items for an inspection.
    pub fn get_inspection_items(&self, inspection_id: Uuid) -> Result<Vec<InspectionItem>> {
        self.db.quality().get_inspection_items(inspection_id)
    }

    /// Complete an inspection (mark as Passed or Failed based on results).
    pub fn complete_inspection(&self, id: Uuid) -> Result<Inspection> {
        self.db.quality().complete_inspection(id)
    }

    /// Delete an inspection (only if Pending).
    pub fn delete_inspection(&self, id: Uuid) -> Result<()> {
        self.db.quality().delete_inspection(id)
    }

    /// Count inspections matching filter.
    pub fn count_inspections(&self, filter: InspectionFilter) -> Result<u64> {
        self.db.quality().count_inspections(filter)
    }

    // ========================================================================
    // Non-Conformance Reports
    // ========================================================================

    /// Create a non-conformance report (NCR).
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// use stateset_embedded::{Commerce, CreateNonConformance, NonConformanceSource, Severity};
    /// use rust_decimal_macros::dec;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// let ncr = commerce.quality().create_ncr(CreateNonConformance {
    ///     source: NonConformanceSource::Inspection,
    ///     severity: Severity::Major,
    ///     sku: "SKU-001".into(),
    ///     quantity_affected: dec!(10),
    ///     description: "Parts do not meet specification".into(),
    ///     ..Default::default()
    /// })?;
    ///
    /// println!("Created NCR #{}", ncr.ncr_number);
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn create_ncr(&self, input: CreateNonConformance) -> Result<NonConformance> {
        self.db.quality().create_ncr(input)
    }

    /// Get a non-conformance report by ID.
    pub fn get_ncr(&self, id: Uuid) -> Result<Option<NonConformance>> {
        self.db.quality().get_ncr(id)
    }

    /// Get a non-conformance report by its number.
    pub fn get_ncr_by_number(&self, number: &str) -> Result<Option<NonConformance>> {
        self.db.quality().get_ncr_by_number(number)
    }

    /// List non-conformance reports with optional filtering.
    pub fn list_ncrs(&self, filter: NonConformanceFilter) -> Result<Vec<NonConformance>> {
        self.db.quality().list_ncrs(filter)
    }

    /// Update a non-conformance report.
    ///
    /// Use this to set root cause, corrective action, disposition, etc.
    pub fn update_ncr(&self, id: Uuid, input: UpdateNonConformance) -> Result<NonConformance> {
        self.db.quality().update_ncr(id, input)
    }

    /// Close an NCR.
    pub fn close_ncr(&self, id: Uuid) -> Result<NonConformance> {
        self.db.quality().close_ncr(id)
    }

    /// Cancel an NCR.
    pub fn cancel_ncr(&self, id: Uuid) -> Result<NonConformance> {
        self.db.quality().cancel_ncr(id)
    }

    /// Count NCRs matching filter.
    pub fn count_ncrs(&self, filter: NonConformanceFilter) -> Result<u64> {
        self.db.quality().count_ncrs(filter)
    }

    // ========================================================================
    // Quality Holds
    // ========================================================================

    /// Create a quality hold on inventory.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// use stateset_embedded::{Commerce, CreateQualityHold, HoldType};
    /// use rust_decimal_macros::dec;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// let hold = commerce.quality().create_hold(CreateQualityHold {
    ///     sku: "SKU-001".into(),
    ///     lot_number: Some("LOT-2025-001".into()),
    ///     quantity_held: dec!(50),
    ///     reason: "Pending quality inspection".into(),
    ///     hold_type: HoldType::QualityInspection,
    ///     placed_by: Some("QA Team".into()),
    ///     ..Default::default()
    /// })?;
    ///
    /// println!("Hold placed on {} units", hold.quantity_held);
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn create_hold(&self, input: CreateQualityHold) -> Result<QualityHold> {
        self.db.quality().create_hold(input)
    }

    /// Get a quality hold by ID.
    pub fn get_hold(&self, id: Uuid) -> Result<Option<QualityHold>> {
        self.db.quality().get_hold(id)
    }

    /// List quality holds with optional filtering.
    pub fn list_holds(&self, filter: QualityHoldFilter) -> Result<Vec<QualityHold>> {
        self.db.quality().list_holds(filter)
    }

    /// Release a quality hold.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// use stateset_embedded::{Commerce, ReleaseQualityHold};
    /// use uuid::Uuid;
    ///
    /// let commerce = Commerce::new(":memory:")?;
    ///
    /// commerce.quality().release_hold(Uuid::new_v4(), ReleaseQualityHold {
    ///     released_by: "QA Manager".into(),
    ///     notes: Some("Inspection passed".into()),
    /// })?;
    /// # Ok::<(), stateset_embedded::CommerceError>(())
    /// ```
    pub fn release_hold(&self, id: Uuid, input: ReleaseQualityHold) -> Result<QualityHold> {
        self.db.quality().release_hold(id, input)
    }

    /// Get active holds for a SKU.
    pub fn get_active_holds_for_sku(&self, sku: &str) -> Result<Vec<QualityHold>> {
        self.db.quality().get_active_holds_for_sku(sku)
    }

    /// Get active holds for a lot.
    pub fn get_active_holds_for_lot(&self, lot_number: &str) -> Result<Vec<QualityHold>> {
        self.db.quality().get_active_holds_for_lot(lot_number)
    }

    /// Count active holds.
    pub fn count_active_holds(&self) -> Result<u64> {
        self.db.quality().count_active_holds()
    }

    // ========================================================================
    // Defect Codes
    // ========================================================================

    /// Create a defect code.
    pub fn create_defect_code(&self, input: CreateDefectCode) -> Result<DefectCode> {
        self.db.quality().create_defect_code(input)
    }

    /// Get a defect code by code.
    pub fn get_defect_code(&self, code: &str) -> Result<Option<DefectCode>> {
        self.db.quality().get_defect_code(code)
    }

    /// List all defect codes, optionally filtered by category.
    pub fn list_defect_codes(&self, category: Option<&str>) -> Result<Vec<DefectCode>> {
        self.db.quality().list_defect_codes(category)
    }

    /// Deactivate a defect code.
    pub fn deactivate_defect_code(&self, id: Uuid) -> Result<()> {
        self.db.quality().deactivate_defect_code(id)
    }

    // ========================================================================
    // Convenience Methods
    // ========================================================================

    /// Get all pending inspections.
    pub fn get_pending_inspections(&self) -> Result<Vec<Inspection>> {
        self.list_inspections(InspectionFilter {
            status: Some(InspectionStatus::Pending),
            ..Default::default()
        })
    }

    /// Get all open NCRs.
    pub fn get_open_ncrs(&self) -> Result<Vec<NonConformance>> {
        self.list_ncrs(NonConformanceFilter { status: Some(NcrStatus::Open), ..Default::default() })
    }

    /// Get all active holds.
    pub fn get_active_holds(&self) -> Result<Vec<QualityHold>> {
        self.list_holds(QualityHoldFilter { active_only: Some(true), ..Default::default() })
    }
}