1use chrono::NaiveDate;
8use rust_decimal::Decimal;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12use super::graph_properties::{GraphPropertyValue, ToNodeProperties};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
16#[serde(rename_all = "snake_case")]
17pub enum MovementType {
18 #[default]
20 GoodsReceipt,
21 GoodsIssue,
23 Transfer,
25 Return,
27 Scrap,
29 Adjustment,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct InventoryMovement {
36 pub id: String,
38 pub entity_code: String,
40 pub material_code: String,
42 pub material_description: String,
44 pub movement_date: NaiveDate,
46 pub period: String,
48 pub movement_type: MovementType,
50 #[serde(with = "rust_decimal::serde::str")]
52 pub quantity: Decimal,
53 pub unit: String,
55 #[serde(with = "rust_decimal::serde::str")]
57 pub value: Decimal,
58 pub currency: String,
60 pub storage_location: String,
62 pub reference_doc: String,
64}
65
66impl InventoryMovement {
67 #[allow(clippy::too_many_arguments)]
69 pub fn new(
70 id: impl Into<String>,
71 entity_code: impl Into<String>,
72 material_code: impl Into<String>,
73 material_description: impl Into<String>,
74 movement_date: NaiveDate,
75 period: impl Into<String>,
76 movement_type: MovementType,
77 quantity: Decimal,
78 unit: impl Into<String>,
79 value: Decimal,
80 currency: impl Into<String>,
81 storage_location: impl Into<String>,
82 reference_doc: impl Into<String>,
83 ) -> Self {
84 Self {
85 id: id.into(),
86 entity_code: entity_code.into(),
87 material_code: material_code.into(),
88 material_description: material_description.into(),
89 movement_date,
90 period: period.into(),
91 movement_type,
92 quantity,
93 unit: unit.into(),
94 value,
95 currency: currency.into(),
96 storage_location: storage_location.into(),
97 reference_doc: reference_doc.into(),
98 }
99 }
100}
101
102impl ToNodeProperties for InventoryMovement {
103 fn node_type_name(&self) -> &'static str {
104 "inventory_movement"
105 }
106 fn node_type_code(&self) -> u16 {
107 105
108 }
109 fn to_node_properties(&self) -> HashMap<String, GraphPropertyValue> {
110 let mut p = HashMap::new();
111 p.insert(
112 "entityCode".into(),
113 GraphPropertyValue::String(self.entity_code.clone()),
114 );
115 p.insert(
116 "materialCode".into(),
117 GraphPropertyValue::String(self.material_code.clone()),
118 );
119 p.insert(
120 "materialDescription".into(),
121 GraphPropertyValue::String(self.material_description.clone()),
122 );
123 p.insert(
124 "movementDate".into(),
125 GraphPropertyValue::Date(self.movement_date),
126 );
127 p.insert(
128 "period".into(),
129 GraphPropertyValue::String(self.period.clone()),
130 );
131 p.insert(
132 "movementType".into(),
133 GraphPropertyValue::String(format!("{:?}", self.movement_type)),
134 );
135 p.insert(
136 "quantity".into(),
137 GraphPropertyValue::Decimal(self.quantity),
138 );
139 p.insert("unit".into(), GraphPropertyValue::String(self.unit.clone()));
140 p.insert("value".into(), GraphPropertyValue::Decimal(self.value));
141 p.insert(
142 "currency".into(),
143 GraphPropertyValue::String(self.currency.clone()),
144 );
145 p.insert(
146 "storageLocation".into(),
147 GraphPropertyValue::String(self.storage_location.clone()),
148 );
149 p.insert(
150 "referenceDoc".into(),
151 GraphPropertyValue::String(self.reference_doc.clone()),
152 );
153 p
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_inventory_movement_properties() {
163 let mv = InventoryMovement::new(
164 "MV-001",
165 "C001",
166 "MAT-100",
167 "Widget A",
168 NaiveDate::from_ymd_opt(2024, 6, 15).unwrap(),
169 "2024-06",
170 MovementType::GoodsReceipt,
171 Decimal::new(100, 0),
172 "EA",
173 Decimal::new(5000, 0),
174 "USD",
175 "WH01",
176 "PO-12345",
177 );
178 let props = mv.to_node_properties();
179 assert_eq!(mv.node_type_name(), "inventory_movement");
180 assert_eq!(mv.node_type_code(), 105);
181 assert!(props.contains_key("movementType"));
182 assert!(props.contains_key("storageLocation"));
183 assert_eq!(
184 props["quantity"],
185 GraphPropertyValue::Decimal(Decimal::new(100, 0))
186 );
187 }
188}