backbone-pos 0.7.1

Point of sale — cashier session, ticket + multi-tender; orchestrates billing (revenue) + payment (settlement), posts no GL directly
Documentation
//! Pos Module
//!
//! Generated by metaphor-schema. Enhanced with runtime implementations.
//!
//! This module provides:
//! - Domain entities and repositories
//! - Application services
//! - HTTP and gRPC handlers
//! - Route configuration
//! - State machine enforcement
//! - Validation rules runtime
//! - RBAC middleware
//! - Trigger execution system
//! - Computed fields
//! - Workflow orchestrator

#![recursion_limit = "1024"]
#![allow(unused_imports)]

// Generated modules
pub mod domain;
pub mod infrastructure;
pub mod application;
pub mod presentation;
pub mod seeders;
pub mod exports;
// <<< CUSTOM MODULES
/// Stable integration surface for composing services (the deliberate public API consumers depend on).
pub mod integration;
// The hand-owned request-pool shim (the composing service's tenant pool
// resolution): a generated-tree declaration the regenerator drops, so it
// lives in the preserved block (#447 cause-2 class).
pub mod request_pool;
// END CUSTOM

// Re-exports for convenience - Domain entities
pub use domain::entity::*;

// Re-exports - Infrastructure
pub use infrastructure::persistence::*;

// Re-exports - Application services
pub use application::service::PosClosingEntryService;
pub use application::service::PosInvoiceService;
pub use application::service::PosInvoiceItemService;
pub use application::service::PosPaymentService;
pub use application::service::PosProfileService;
pub use application::service::PosOpeningEntryService;
pub use application::service::PosCashMovementService;
pub use application::service::PosDiscountService;
pub use application::service::PosManagerPinService;
pub use application::service::PosFloorPlanService;
pub use application::service::PosTableService;

// Re-exports - Validation
pub use application::validator::{ValidationError, ValidationResult};

use std::sync::Arc;
use axum::Router;
use sqlx::PgPool;

/// Pos module configuration
///
/// Use the builder pattern to configure and register this module:
///
/// ```text
/// let pos = PosModule::builder()
///     .with_database(pool.clone())
///     .build()?;
///
/// // Unguarded full CRUD (trusted/admin); compose a guarded router for production.
/// let router = pos.all_crud_routes();
/// ```
pub struct PosModule {
    pub(crate) pos_closing_entry_service: Arc<PosClosingEntryService>,
    pub(crate) pos_invoice_service: Arc<PosInvoiceService>,
    pub(crate) pos_invoice_item_service: Arc<PosInvoiceItemService>,
    pub(crate) pos_payment_service: Arc<PosPaymentService>,
    pub(crate) pos_profile_service: Arc<PosProfileService>,
    pub(crate) pos_opening_entry_service: Arc<PosOpeningEntryService>,
    pub(crate) pos_cash_movement_service: Arc<PosCashMovementService>,
    pub(crate) pos_discount_service: Arc<PosDiscountService>,
    pub(crate) pos_manager_pin_service: Arc<PosManagerPinService>,
    pub(crate) pos_floor_plan_service: Arc<PosFloorPlanService>,
    pub(crate) pos_table_service: Arc<PosTableService>,
    // <<< CUSTOM FIELDS
    // END CUSTOM
}

impl PosModule {
    /// Create a new module builder
    pub fn builder() -> PosModuleBuilder {
        PosModuleBuilder::new()
    }

    /// Mount ALL generated CRUD endpoints (12 per entity) with NO domain
    /// validation — the fully **unguarded** surface. A well-formed request can
    /// create invalid rows or soft-delete a referenced master out from under its
    /// dependents. Prefer a guarded composition (read + validated writes) for any
    /// real deployment; use this only in trusted/admin/seeding contexts.
    pub fn all_crud_routes(&self) -> Router {
        use presentation::http::{
            create_pos_closing_entry_routes,
            create_pos_invoice_routes,
            create_pos_invoice_item_routes,
            create_pos_payment_routes,
            create_pos_profile_routes,
            create_pos_opening_entry_routes,
            create_pos_cash_movement_routes,
            create_pos_discount_routes,
            create_pos_manager_pin_routes,
            create_pos_floor_plan_routes,
            create_pos_table_routes,
        };

        Router::new()
            .merge(create_pos_closing_entry_routes(self.pos_closing_entry_service.clone()))
            .merge(create_pos_invoice_routes(self.pos_invoice_service.clone()))
            .merge(create_pos_invoice_item_routes(self.pos_invoice_item_service.clone()))
            .merge(create_pos_payment_routes(self.pos_payment_service.clone()))
            .merge(create_pos_profile_routes(self.pos_profile_service.clone()))
            .merge(create_pos_opening_entry_routes(self.pos_opening_entry_service.clone()))
            .merge(create_pos_cash_movement_routes(self.pos_cash_movement_service.clone()))
            .merge(create_pos_discount_routes(self.pos_discount_service.clone()))
            .merge(create_pos_manager_pin_routes(self.pos_manager_pin_service.clone()))
            .merge(create_pos_floor_plan_routes(self.pos_floor_plan_service.clone()))
            .merge(create_pos_table_routes(self.pos_table_service.clone()))
    }

    /// Deprecated alias for [`Self::all_crud_routes`]. `routes()` reads like
    /// "the routes" but mounts UNVALIDATED generic CRUD on every entity — a naive
    /// mount exposes unguarded writes. Compose a guarded router (read + validated
    /// writes) for production, or call `all_crud_routes()` to opt into the full
    /// unguarded surface explicitly.
    #[deprecated(note = "mounts unvalidated generic CRUD; prefer readonly_routes() + validated writes, or all_crud_routes() for the full/unguarded surface")]
    pub fn routes(&self) -> Router {
        self.all_crud_routes()
    }

    /// Read-only routes for every entity (GET endpoints only) — the safe base.
    ///
    /// Generic mutation can't reach here, so this surface cannot bypass a
    /// validated write service's invariants. Use this as the production base and
    /// merge validated write routes (or a write service's HTTP layer) onto it.
    pub fn readonly_routes(&self) -> Router {
        use presentation::http::{
            create_pos_closing_entry_read_routes,
            create_pos_invoice_read_routes,
            create_pos_invoice_item_read_routes,
            create_pos_payment_read_routes,
            create_pos_profile_read_routes,
            create_pos_opening_entry_read_routes,
            create_pos_cash_movement_read_routes,
            create_pos_discount_read_routes,
            create_pos_manager_pin_read_routes,
            create_pos_floor_plan_read_routes,
            create_pos_table_read_routes,
        };

        Router::new()
            .merge(create_pos_closing_entry_read_routes(self.pos_closing_entry_service.clone()))
            .merge(create_pos_invoice_read_routes(self.pos_invoice_service.clone()))
            .merge(create_pos_invoice_item_read_routes(self.pos_invoice_item_service.clone()))
            .merge(create_pos_payment_read_routes(self.pos_payment_service.clone()))
            .merge(create_pos_profile_read_routes(self.pos_profile_service.clone()))
            .merge(create_pos_opening_entry_read_routes(self.pos_opening_entry_service.clone()))
            .merge(create_pos_cash_movement_read_routes(self.pos_cash_movement_service.clone()))
            .merge(create_pos_discount_read_routes(self.pos_discount_service.clone()))
            .merge(create_pos_manager_pin_read_routes(self.pos_manager_pin_service.clone()))
            .merge(create_pos_floor_plan_read_routes(self.pos_floor_plan_service.clone()))
            .merge(create_pos_table_read_routes(self.pos_table_service.clone()))
    }

    // <<< CUSTOM METHODS
    // END CUSTOM
}

/// Builder for PosModule
pub struct PosModuleBuilder {
    db_pool: Option<PgPool>,
    // <<< CUSTOM BUILDER FIELDS
    // END CUSTOM
}

impl PosModuleBuilder {
    /// Create a new builder
    pub fn new() -> Self {
        Self {
            db_pool: None,
            // <<< CUSTOM BUILDER DEFAULTS
            // END CUSTOM
        }
    }

    /// Set the database connection pool
    pub fn with_database(mut self, pool: PgPool) -> Self {
        self.db_pool = Some(pool);
        self
    }

    // <<< CUSTOM - custom builder methods
    // END CUSTOM

    /// Build the module with configured dependencies
    pub fn build(self) -> anyhow::Result<PosModule> {
        let db_pool = self.db_pool
            .ok_or_else(|| anyhow::anyhow!("Database pool not configured"))?;

        // PosClosingEntry service
        let pos_closing_entry_repository = Arc::new(PosClosingEntryRepository::new(db_pool.clone()));
        let pos_closing_entry_service = Arc::new(PosClosingEntryService::with_repository(pos_closing_entry_repository.clone()));

        // PosInvoice service
        let pos_invoice_repository = Arc::new(PosInvoiceRepository::new(db_pool.clone()));
        let pos_invoice_service = Arc::new(PosInvoiceService::with_repository(pos_invoice_repository.clone()));

        // PosInvoiceItem service
        let pos_invoice_item_repository = Arc::new(PosInvoiceItemRepository::new(db_pool.clone()));
        let pos_invoice_item_service = Arc::new(PosInvoiceItemService::with_repository(pos_invoice_item_repository.clone()));

        // PosPayment service
        let pos_payment_repository = Arc::new(PosPaymentRepository::new(db_pool.clone()));
        let pos_payment_service = Arc::new(PosPaymentService::with_repository(pos_payment_repository.clone()));

        // PosProfile service
        let pos_profile_repository = Arc::new(PosProfileRepository::new(db_pool.clone()));
        let pos_profile_service = Arc::new(PosProfileService::with_repository(pos_profile_repository.clone()));

        // PosOpeningEntry service
        let pos_opening_entry_repository = Arc::new(PosOpeningEntryRepository::new(db_pool.clone()));
        let pos_opening_entry_service = Arc::new(PosOpeningEntryService::with_repository(pos_opening_entry_repository.clone()));

        // PosCashMovement service
        let pos_cash_movement_repository = Arc::new(PosCashMovementRepository::new(db_pool.clone()));
        let pos_cash_movement_service = Arc::new(PosCashMovementService::with_repository(pos_cash_movement_repository.clone()));

        // PosDiscount service
        let pos_discount_repository = Arc::new(PosDiscountRepository::new(db_pool.clone()));
        let pos_discount_service = Arc::new(PosDiscountService::with_repository(pos_discount_repository.clone()));

        // PosManagerPin service
        let pos_manager_pin_repository = Arc::new(PosManagerPinRepository::new(db_pool.clone()));
        let pos_manager_pin_service = Arc::new(PosManagerPinService::with_repository(pos_manager_pin_repository.clone()));

        // PosFloorPlan service
        let pos_floor_plan_repository = Arc::new(PosFloorPlanRepository::new(db_pool.clone()));
        let pos_floor_plan_service = Arc::new(PosFloorPlanService::with_repository(pos_floor_plan_repository.clone()));

        // PosTable service
        let pos_table_repository = Arc::new(PosTableRepository::new(db_pool.clone()));
        let pos_table_service = Arc::new(PosTableService::with_repository(pos_table_repository.clone()));

        // <<< CUSTOM
        // END CUSTOM

        Ok(PosModule {
            pos_closing_entry_service,
            pos_invoice_service,
            pos_invoice_item_service,
            pos_payment_service,
            pos_profile_service,
            pos_opening_entry_service,
            pos_cash_movement_service,
            pos_discount_service,
            pos_manager_pin_service,
            pos_floor_plan_service,
            pos_table_service,
            // <<< CUSTOM
            // END CUSTOM
        })
    }
}

impl Default for PosModuleBuilder {
    fn default() -> Self {
        Self::new()
    }
}