//! StockMove DTOs (Data Transfer Objects)
//!
//! Generated by metaphor-schema. Do not edit manually.
//!
//! DTOs provide a clean separation between domain entities and API
//! representations, with validation and OpenAPI documentation support.
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
#[cfg(feature = "openapi")]
#[cfg(feature = "openapi")]
use utoipa::ToSchema;
#[cfg(feature = "validation")]
use validator::Validate;
use crate::domain::entity::StockMove;
use crate::domain::entity::AuditMetadata;
use crate::domain::entity::GlPostingState;
use crate::domain::entity::MoveState;
use crate::domain::entity::Priority;
use crate::domain::entity::ProcureMethod;
// =============================================================================
// Create DTO
// =============================================================================
/// Request DTO for creating a new StockMove
///
/// Used in POST requests to create new entities.
/// Excludes auto-generated fields (id, created_at, updated_at, deleted_at).
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[cfg_attr(feature = "validation", derive(Validate))]
#[serde(rename_all = "camelCase")]
pub struct CreateStockMoveDto {
#[cfg_attr(feature = "validation", validate(length(max = 400)))]
#[cfg_attr(feature = "openapi", schema(example = "example"))]
pub name: String,
pub state: MoveState,
#[serde(alias = "posting_state")]
pub posting_state: GlPostingState,
pub priority: Priority,
#[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
#[serde(alias = "create_date")]
pub create_date: DateTime<Utc>,
#[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
pub date: DateTime<Utc>,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
#[serde(alias = "item_id")]
pub item_id: Uuid,
#[serde(alias = "demand_qty")]
pub demand_qty: Decimal,
pub quantity: Decimal,
#[serde(alias = "price_unit")]
pub price_unit: Decimal,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "forced_value")]
pub forced_value: Option<Decimal>,
#[serde(alias = "procure_method")]
pub procure_method: ProcureMethod,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "picking_id")]
pub picking_id: Option<Uuid>,
#[cfg_attr(feature = "validation", validate(length(max = 200)))]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub origin: Option<String>,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
#[serde(alias = "location_id")]
pub location_id: Uuid,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
#[serde(alias = "location_dest_id")]
pub location_dest_id: Uuid,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "partner_id")]
pub partner_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "rule_id")]
pub rule_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "warehouse_id")]
pub warehouse_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "orderpoint_id")]
pub orderpoint_id: Option<Uuid>,
#[serde(alias = "move_orig_ids")]
pub move_orig_ids: Vec<Uuid>,
#[serde(alias = "move_dest_ids")]
pub move_dest_ids: Vec<Uuid>,
#[cfg_attr(feature = "openapi", schema(example = true))]
#[serde(alias = "is_inventory")]
pub is_inventory: bool,
#[cfg_attr(feature = "openapi", schema(example = true))]
pub scrapped: bool,
#[cfg_attr(feature = "openapi", schema(example = true))]
#[serde(alias = "propagate_cancel")]
pub propagate_cancel: bool,
}
// =============================================================================
// Update DTO
// =============================================================================
/// Request DTO for full update of a StockMove
///
/// Used in PUT requests for full entity replacement.
/// All fields are required (except auto-generated ones).
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[cfg_attr(feature = "validation", derive(Validate))]
#[serde(rename_all = "camelCase")]
pub struct UpdateStockMoveDto {
#[cfg_attr(feature = "validation", validate(length(max = 400)))]
#[cfg_attr(feature = "openapi", schema(example = "example"))]
pub name: String,
pub state: MoveState,
#[serde(alias = "posting_state")]
pub posting_state: GlPostingState,
pub priority: Priority,
#[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
#[serde(alias = "create_date")]
pub create_date: DateTime<Utc>,
#[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
pub date: DateTime<Utc>,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
#[serde(alias = "item_id")]
pub item_id: Uuid,
#[serde(alias = "demand_qty")]
pub demand_qty: Decimal,
pub quantity: Decimal,
#[serde(alias = "price_unit")]
pub price_unit: Decimal,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "forced_value")]
pub forced_value: Option<Decimal>,
#[serde(alias = "procure_method")]
pub procure_method: ProcureMethod,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "picking_id")]
pub picking_id: Option<Uuid>,
#[cfg_attr(feature = "validation", validate(length(max = 200)))]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub origin: Option<String>,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
#[serde(alias = "location_id")]
pub location_id: Uuid,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
#[serde(alias = "location_dest_id")]
pub location_dest_id: Uuid,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "partner_id")]
pub partner_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "rule_id")]
pub rule_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "warehouse_id")]
pub warehouse_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "orderpoint_id")]
pub orderpoint_id: Option<Uuid>,
#[serde(alias = "move_orig_ids")]
pub move_orig_ids: Vec<Uuid>,
#[serde(alias = "move_dest_ids")]
pub move_dest_ids: Vec<Uuid>,
#[cfg_attr(feature = "openapi", schema(example = true))]
#[serde(alias = "is_inventory")]
pub is_inventory: bool,
#[cfg_attr(feature = "openapi", schema(example = true))]
pub scrapped: bool,
#[cfg_attr(feature = "openapi", schema(example = true))]
#[serde(alias = "propagate_cancel")]
pub propagate_cancel: bool,
}
// =============================================================================
// Patch DTO
// =============================================================================
/// Request DTO for partial update of a StockMove
///
/// Used in PATCH requests for partial updates.
/// All fields are optional - only provided fields will be updated.
#[derive(Debug, Clone, Default, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[cfg_attr(feature = "validation", derive(Validate))]
#[serde(rename_all = "camelCase")]
pub struct PatchStockMoveDto {
#[cfg_attr(feature = "validation", validate(length(max = 400)))]
#[cfg_attr(feature = "openapi", schema(example = "example"))]
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<MoveState>,
#[serde(skip_serializing_if = "Option::is_none", alias = "posting_state")]
pub posting_state: Option<GlPostingState>,
#[serde(skip_serializing_if = "Option::is_none")]
pub priority: Option<Priority>,
#[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
#[serde(skip_serializing_if = "Option::is_none", alias = "create_date")]
pub create_date: Option<DateTime<Utc>>,
#[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
#[serde(skip_serializing_if = "Option::is_none")]
pub date: Option<DateTime<Utc>>,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
#[serde(skip_serializing_if = "Option::is_none", alias = "item_id")]
pub item_id: Option<Uuid>,
#[serde(skip_serializing_if = "Option::is_none", alias = "demand_qty")]
pub demand_qty: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quantity: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none", alias = "price_unit")]
pub price_unit: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none", alias = "forced_value")]
pub forced_value: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none", alias = "procure_method")]
pub procure_method: Option<ProcureMethod>,
#[serde(skip_serializing_if = "Option::is_none", alias = "picking_id")]
pub picking_id: Option<Uuid>,
#[cfg_attr(feature = "validation", validate(length(max = 200)))]
#[serde(skip_serializing_if = "Option::is_none")]
pub origin: Option<String>,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
#[serde(skip_serializing_if = "Option::is_none", alias = "location_id")]
pub location_id: Option<Uuid>,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
#[serde(skip_serializing_if = "Option::is_none", alias = "location_dest_id")]
pub location_dest_id: Option<Uuid>,
#[serde(skip_serializing_if = "Option::is_none", alias = "partner_id")]
pub partner_id: Option<Uuid>,
#[serde(skip_serializing_if = "Option::is_none", alias = "rule_id")]
pub rule_id: Option<Uuid>,
#[serde(skip_serializing_if = "Option::is_none", alias = "warehouse_id")]
pub warehouse_id: Option<Uuid>,
#[serde(skip_serializing_if = "Option::is_none", alias = "orderpoint_id")]
pub orderpoint_id: Option<Uuid>,
#[serde(skip_serializing_if = "Option::is_none", alias = "move_orig_ids")]
pub move_orig_ids: Option<Vec<Uuid>>,
#[serde(skip_serializing_if = "Option::is_none", alias = "move_dest_ids")]
pub move_dest_ids: Option<Vec<Uuid>>,
#[cfg_attr(feature = "openapi", schema(example = true))]
#[serde(skip_serializing_if = "Option::is_none", alias = "is_inventory")]
pub is_inventory: Option<bool>,
#[cfg_attr(feature = "openapi", schema(example = true))]
#[serde(skip_serializing_if = "Option::is_none")]
pub scrapped: Option<bool>,
#[cfg_attr(feature = "openapi", schema(example = true))]
#[serde(skip_serializing_if = "Option::is_none", alias = "propagate_cancel")]
pub propagate_cancel: Option<bool>,
}
impl PatchStockMoveDto {
/// Check if any field is set
pub fn has_changes(&self) -> bool {
self.name.is_some() || self.state.is_some() || self.posting_state.is_some() || self.priority.is_some() || self.create_date.is_some() || self.date.is_some() || self.item_id.is_some() || self.demand_qty.is_some() || self.quantity.is_some() || self.price_unit.is_some() || self.forced_value.is_some() || self.procure_method.is_some() || self.picking_id.is_some() || self.origin.is_some() || self.location_id.is_some() || self.location_dest_id.is_some() || self.partner_id.is_some() || self.rule_id.is_some() || self.warehouse_id.is_some() || self.orderpoint_id.is_some() || self.move_orig_ids.is_some() || self.move_dest_ids.is_some() || self.is_inventory.is_some() || self.scrapped.is_some() || self.propagate_cancel.is_some()
}
}
// =============================================================================
// Response DTO
// =============================================================================
/// Response DTO for StockMove entity
///
/// Used in API responses for single entity.
/// Includes all fields including metadata.
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct StockMoveResponseDto {
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
pub id: Uuid,
#[cfg_attr(feature = "openapi", schema(example = "example"))]
pub name: String,
pub state: MoveState,
pub posting_state: GlPostingState,
pub priority: Priority,
#[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
pub create_date: DateTime<Utc>,
#[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
pub date: DateTime<Utc>,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
pub item_id: Uuid,
pub demand_qty: Decimal,
pub quantity: Decimal,
pub price_unit: Decimal,
pub forced_value: Option<Decimal>,
pub procure_method: ProcureMethod,
pub picking_id: Option<Uuid>,
pub origin: Option<String>,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
pub location_id: Uuid,
#[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
pub location_dest_id: Uuid,
pub partner_id: Option<Uuid>,
pub rule_id: Option<Uuid>,
pub warehouse_id: Option<Uuid>,
pub orderpoint_id: Option<Uuid>,
pub move_orig_ids: Vec<Uuid>,
pub move_dest_ids: Vec<Uuid>,
#[cfg_attr(feature = "openapi", schema(example = true))]
pub is_inventory: bool,
#[cfg_attr(feature = "openapi", schema(example = true))]
pub scrapped: bool,
#[cfg_attr(feature = "openapi", schema(example = true))]
pub propagate_cancel: bool,
pub metadata: AuditMetadata,
}
// =============================================================================
// List Response DTO
// =============================================================================
/// Paginated list response for StockMove entities
///
/// Used in API responses for list endpoints.
/// Includes pagination metadata.
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct StockMoveListResponseDto {
/// List of items
pub items: Vec<StockMoveResponseDto>,
/// Total number of items (across all pages)
pub total: u64,
/// Current page number (1-indexed)
pub page: u32,
/// Number of items per page
pub per_page: u32,
/// Total number of pages
pub total_pages: u32,
/// Whether there is a next page
pub has_next: bool,
/// Whether there is a previous page
pub has_prev: bool,
}
impl StockMoveListResponseDto {
/// Create a new list response from items and pagination info
pub fn new(items: Vec<StockMoveResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
let total_pages = if per_page > 0 {
((total as f64) / (per_page as f64)).ceil() as u32
} else {
0
};
Self {
items,
total,
page,
per_page,
total_pages,
has_next: page < total_pages,
has_prev: page > 1,
}
}
}
/// Summary DTO for list views (compact version)
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct StockMoveSummaryDto {
pub id: Uuid,
pub name: String,
pub state: MoveState,
pub posting_state: GlPostingState,
pub created_at: Option<DateTime<Utc>>,
}
// =============================================================================
// Conversions
// =============================================================================
impl From<StockMove> for StockMoveResponseDto {
fn from(entity: StockMove) -> Self {
Self {
id: entity.id,
name: entity.name,
state: entity.state,
posting_state: entity.posting_state,
priority: entity.priority,
create_date: entity.create_date,
date: entity.date,
item_id: entity.item_id,
demand_qty: entity.demand_qty,
quantity: entity.quantity,
price_unit: entity.price_unit,
forced_value: entity.forced_value,
procure_method: entity.procure_method,
picking_id: entity.picking_id,
origin: entity.origin,
location_id: entity.location_id,
location_dest_id: entity.location_dest_id,
partner_id: entity.partner_id,
rule_id: entity.rule_id,
warehouse_id: entity.warehouse_id,
orderpoint_id: entity.orderpoint_id,
move_orig_ids: entity.move_orig_ids,
move_dest_ids: entity.move_dest_ids,
is_inventory: entity.is_inventory,
scrapped: entity.scrapped,
propagate_cancel: entity.propagate_cancel,
metadata: entity.metadata,
}
}
}
impl From<StockMove> for StockMoveSummaryDto {
fn from(entity: StockMove) -> Self {
let created_at = backbone_core::PersistentEntity::created_at(&entity);
Self {
id: entity.id,
name: entity.name,
state: entity.state,
posting_state: entity.posting_state,
created_at,
}
}
}
impl From<CreateStockMoveDto> for StockMove {
fn from(dto: CreateStockMoveDto) -> Self {
Self {
id: Uuid::new_v4(),
name: dto.name,
state: dto.state,
posting_state: dto.posting_state,
priority: dto.priority,
create_date: dto.create_date,
date: dto.date,
item_id: dto.item_id,
demand_qty: dto.demand_qty,
quantity: dto.quantity,
price_unit: dto.price_unit,
forced_value: dto.forced_value,
procure_method: dto.procure_method,
picking_id: dto.picking_id,
origin: dto.origin,
location_id: dto.location_id,
location_dest_id: dto.location_dest_id,
partner_id: dto.partner_id,
rule_id: dto.rule_id,
warehouse_id: dto.warehouse_id,
orderpoint_id: dto.orderpoint_id,
move_orig_ids: dto.move_orig_ids,
move_dest_ids: dto.move_dest_ids,
is_inventory: dto.is_inventory,
scrapped: dto.scrapped,
propagate_cancel: dto.propagate_cancel,
metadata: AuditMetadata::default(),
}
}
}
impl From<&StockMove> for StockMoveResponseDto {
fn from(entity: &StockMove) -> Self {
Self {
id: entity.id.clone(),
name: entity.name.clone(),
state: entity.state.clone(),
posting_state: entity.posting_state.clone(),
priority: entity.priority.clone(),
create_date: entity.create_date.clone(),
date: entity.date.clone(),
item_id: entity.item_id.clone(),
demand_qty: entity.demand_qty.clone(),
quantity: entity.quantity.clone(),
price_unit: entity.price_unit.clone(),
forced_value: entity.forced_value.clone(),
procure_method: entity.procure_method.clone(),
picking_id: entity.picking_id.clone(),
origin: entity.origin.clone(),
location_id: entity.location_id.clone(),
location_dest_id: entity.location_dest_id.clone(),
partner_id: entity.partner_id.clone(),
rule_id: entity.rule_id.clone(),
warehouse_id: entity.warehouse_id.clone(),
orderpoint_id: entity.orderpoint_id.clone(),
move_orig_ids: entity.move_orig_ids.clone(),
move_dest_ids: entity.move_dest_ids.clone(),
is_inventory: entity.is_inventory.clone(),
scrapped: entity.scrapped.clone(),
propagate_cancel: entity.propagate_cancel.clone(),
metadata: entity.metadata.clone(),
}
}
}
impl backbone_core::FromCreateDto<CreateStockMoveDto> for StockMove {
fn from_create_dto(dto: CreateStockMoveDto) -> backbone_core::ServiceResult<Self> {
Ok(StockMove::from(dto))
}
}
impl backbone_core::ApplyUpdateDto<UpdateStockMoveDto> for StockMove {
fn apply_update(mut self, dto: UpdateStockMoveDto) -> backbone_core::ServiceResult<Self> {
self.name = dto.name;
self.state = dto.state;
self.posting_state = dto.posting_state;
self.priority = dto.priority;
self.create_date = dto.create_date;
self.date = dto.date;
self.item_id = dto.item_id;
self.demand_qty = dto.demand_qty;
self.quantity = dto.quantity;
self.price_unit = dto.price_unit;
self.forced_value = dto.forced_value;
self.procure_method = dto.procure_method;
self.picking_id = dto.picking_id;
self.origin = dto.origin;
self.location_id = dto.location_id;
self.location_dest_id = dto.location_dest_id;
self.partner_id = dto.partner_id;
self.rule_id = dto.rule_id;
self.warehouse_id = dto.warehouse_id;
self.orderpoint_id = dto.orderpoint_id;
self.move_orig_ids = dto.move_orig_ids;
self.move_dest_ids = dto.move_dest_ids;
self.is_inventory = dto.is_inventory;
self.scrapped = dto.scrapped;
self.propagate_cancel = dto.propagate_cancel;
Ok(self)
}
}
// =============================================================================
// Custom DTOs
// =============================================================================
// <<< CUSTOM DTOs
// Add custom DTOs specific to StockMove here.
// This section will be preserved during regeneration.
// >>> END CUSTOM DTOs