1use magnus::{
17 DataTypeFunctions, Error, RHash, Ruby, Symbol, TypedData, Value, class, define_module,
18 exception, function, method, prelude::*,
19};
20use rust_decimal::Decimal;
21use rust_decimal::prelude::ToPrimitive;
22use stateset_embedded::Commerce as RustCommerce;
23use std::sync::{Arc, Mutex};
24
25macro_rules! lock_commerce {
30 ($commerce:expr) => {
31 $commerce
32 .lock()
33 .map_err(|e| Error::new(exception::runtime_error(), format!("Lock error: {}", e)))?
34 };
35}
36
37macro_rules! parse_uuid {
38 ($id:expr, $name:expr) => {
39 $id.parse()
40 .map_err(|_| Error::new(exception::arg_error(), format!("Invalid {} UUID", $name)))?
41 };
42}
43
44fn to_f64_or_nan<T>(value: T) -> f64
45where
46 T: TryInto<f64>,
47 <T as TryInto<f64>>::Error: std::fmt::Display,
48{
49 match value.try_into() {
50 Ok(converted) => converted,
51 Err(err) => {
52 eprintln!("stateset-embedded: failed to convert to f64: {}", err);
53 f64::NAN
54 }
55 }
56}
57
58#[derive(Clone)]
64#[magnus::wrap(class = "StateSet::Commerce", free_immediately, size)]
65pub struct Commerce {
66 inner: Arc<Mutex<RustCommerce>>,
67}
68
69impl Commerce {
70 fn new(db_path: String) -> Result<Self, Error> {
71 let commerce = RustCommerce::new(&db_path).map_err(|e| {
72 Error::new(exception::runtime_error(), format!("Failed to initialize commerce: {}", e))
73 })?;
74
75 Ok(Self { inner: Arc::new(Mutex::new(commerce)) })
76 }
77
78 fn customers(&self) -> Customers {
79 Customers { commerce: self.inner.clone() }
80 }
81
82 fn orders(&self) -> Orders {
83 Orders { commerce: self.inner.clone() }
84 }
85
86 fn products(&self) -> Products {
87 Products { commerce: self.inner.clone() }
88 }
89
90 fn inventory(&self) -> Inventory {
91 Inventory { commerce: self.inner.clone() }
92 }
93
94 fn returns(&self) -> Returns {
95 Returns { commerce: self.inner.clone() }
96 }
97
98 fn payments(&self) -> Payments {
99 Payments { commerce: self.inner.clone() }
100 }
101
102 fn shipments(&self) -> Shipments {
103 Shipments { commerce: self.inner.clone() }
104 }
105
106 fn warranties(&self) -> Warranties {
107 Warranties { commerce: self.inner.clone() }
108 }
109
110 fn purchase_orders(&self) -> PurchaseOrders {
111 PurchaseOrders { commerce: self.inner.clone() }
112 }
113
114 fn invoices(&self) -> Invoices {
115 Invoices { commerce: self.inner.clone() }
116 }
117
118 fn bom(&self) -> BomApi {
119 BomApi { commerce: self.inner.clone() }
120 }
121
122 fn work_orders(&self) -> WorkOrders {
123 WorkOrders { commerce: self.inner.clone() }
124 }
125
126 fn carts(&self) -> Carts {
127 Carts { commerce: self.inner.clone() }
128 }
129
130 fn analytics(&self) -> Analytics {
131 Analytics { commerce: self.inner.clone() }
132 }
133
134 fn currency(&self) -> CurrencyOps {
135 CurrencyOps { commerce: self.inner.clone() }
136 }
137
138 fn subscriptions(&self) -> Subscriptions {
139 Subscriptions { commerce: self.inner.clone() }
140 }
141
142 fn promotions(&self) -> Promotions {
143 Promotions { commerce: self.inner.clone() }
144 }
145
146 fn tax(&self) -> Tax {
147 Tax { commerce: self.inner.clone() }
148 }
149}
150
151#[derive(Clone)]
156#[magnus::wrap(class = "StateSet::Customer", free_immediately, size)]
157pub struct Customer {
158 id: String,
159 email: String,
160 first_name: String,
161 last_name: String,
162 phone: Option<String>,
163 status: String,
164 accepts_marketing: bool,
165 created_at: String,
166 updated_at: String,
167}
168
169impl Customer {
170 fn id(&self) -> String {
171 self.id.clone()
172 }
173
174 fn email(&self) -> String {
175 self.email.clone()
176 }
177
178 fn first_name(&self) -> String {
179 self.first_name.clone()
180 }
181
182 fn last_name(&self) -> String {
183 self.last_name.clone()
184 }
185
186 fn phone(&self) -> Option<String> {
187 self.phone.clone()
188 }
189
190 fn status(&self) -> String {
191 self.status.clone()
192 }
193
194 fn accepts_marketing(&self) -> bool {
195 self.accepts_marketing
196 }
197
198 fn created_at(&self) -> String {
199 self.created_at.clone()
200 }
201
202 fn updated_at(&self) -> String {
203 self.updated_at.clone()
204 }
205
206 fn full_name(&self) -> String {
207 format!("{} {}", self.first_name, self.last_name)
208 }
209
210 fn inspect(&self) -> String {
211 format!(
212 "#<StateSet::Customer id=\"{}\" email=\"{}\" name=\"{} {}\">",
213 self.id, self.email, self.first_name, self.last_name
214 )
215 }
216}
217
218impl From<stateset_core::Customer> for Customer {
219 fn from(c: stateset_core::Customer) -> Self {
220 Self {
221 id: c.id.to_string(),
222 email: c.email,
223 first_name: c.first_name,
224 last_name: c.last_name,
225 phone: c.phone,
226 status: format!("{}", c.status),
227 accepts_marketing: c.accepts_marketing,
228 created_at: c.created_at.to_rfc3339(),
229 updated_at: c.updated_at.to_rfc3339(),
230 }
231 }
232}
233
234#[derive(Clone)]
239#[magnus::wrap(class = "StateSet::Customers", free_immediately, size)]
240pub struct Customers {
241 commerce: Arc<Mutex<RustCommerce>>,
242}
243
244impl Customers {
245 fn create(
246 &self,
247 email: String,
248 first_name: String,
249 last_name: String,
250 phone: Option<String>,
251 accepts_marketing: Option<bool>,
252 ) -> Result<Customer, Error> {
253 let commerce = lock_commerce!(self.commerce);
254
255 let customer = commerce
256 .customers()
257 .create(stateset_core::CreateCustomer {
258 email,
259 first_name,
260 last_name,
261 phone,
262 accepts_marketing,
263 ..Default::default()
264 })
265 .map_err(|e| {
266 Error::new(exception::runtime_error(), format!("Failed to create customer: {}", e))
267 })?;
268
269 Ok(customer.into())
270 }
271
272 fn get(&self, id: String) -> Result<Option<Customer>, Error> {
273 let commerce = lock_commerce!(self.commerce);
274 let uuid = parse_uuid!(id, "customer");
275
276 let customer = commerce.customers().get(uuid).map_err(|e| {
277 Error::new(exception::runtime_error(), format!("Failed to get customer: {}", e))
278 })?;
279
280 Ok(customer.map(|c| c.into()))
281 }
282
283 fn get_by_email(&self, email: String) -> Result<Option<Customer>, Error> {
284 let commerce = lock_commerce!(self.commerce);
285
286 let customer = commerce.customers().get_by_email(&email).map_err(|e| {
287 Error::new(exception::runtime_error(), format!("Failed to get customer: {}", e))
288 })?;
289
290 Ok(customer.map(|c| c.into()))
291 }
292
293 fn list(&self) -> Result<Vec<Customer>, Error> {
294 let commerce = lock_commerce!(self.commerce);
295
296 let customers = commerce.customers().list(Default::default()).map_err(|e| {
297 Error::new(exception::runtime_error(), format!("Failed to list customers: {}", e))
298 })?;
299
300 Ok(customers.into_iter().map(|c| c.into()).collect())
301 }
302
303 fn count(&self) -> Result<i64, Error> {
304 let commerce = lock_commerce!(self.commerce);
305
306 let count = commerce.customers().count(Default::default()).map_err(|e| {
307 Error::new(exception::runtime_error(), format!("Failed to count customers: {}", e))
308 })?;
309
310 Ok(count)
311 }
312}
313
314#[derive(Clone)]
319#[magnus::wrap(class = "StateSet::OrderItem", free_immediately, size)]
320pub struct OrderItem {
321 id: String,
322 sku: String,
323 name: String,
324 quantity: i32,
325 unit_price: f64,
326 total: f64,
327}
328
329impl OrderItem {
330 fn id(&self) -> String {
331 self.id.clone()
332 }
333 fn sku(&self) -> String {
334 self.sku.clone()
335 }
336 fn name(&self) -> String {
337 self.name.clone()
338 }
339 fn quantity(&self) -> i32 {
340 self.quantity
341 }
342 fn unit_price(&self) -> f64 {
343 self.unit_price
344 }
345 fn total(&self) -> f64 {
346 self.total
347 }
348 fn inspect(&self) -> String {
349 format!(
350 "#<StateSet::OrderItem sku=\"{}\" qty={} price={}>",
351 self.sku, self.quantity, self.unit_price
352 )
353 }
354}
355
356#[derive(Clone)]
357#[magnus::wrap(class = "StateSet::Order", free_immediately, size)]
358pub struct Order {
359 id: String,
360 order_number: String,
361 customer_id: String,
362 status: String,
363 total_amount: f64,
364 currency: String,
365 payment_status: String,
366 fulfillment_status: String,
367 tracking_number: Option<String>,
368 items: Vec<OrderItem>,
369 version: i32,
370 created_at: String,
371 updated_at: String,
372}
373
374impl Order {
375 fn id(&self) -> String {
376 self.id.clone()
377 }
378 fn order_number(&self) -> String {
379 self.order_number.clone()
380 }
381 fn customer_id(&self) -> String {
382 self.customer_id.clone()
383 }
384 fn status(&self) -> String {
385 self.status.clone()
386 }
387 fn total_amount(&self) -> f64 {
388 self.total_amount
389 }
390 fn currency(&self) -> String {
391 self.currency.clone()
392 }
393 fn payment_status(&self) -> String {
394 self.payment_status.clone()
395 }
396 fn fulfillment_status(&self) -> String {
397 self.fulfillment_status.clone()
398 }
399 fn tracking_number(&self) -> Option<String> {
400 self.tracking_number.clone()
401 }
402 fn items(&self) -> Vec<OrderItem> {
403 self.items.clone()
404 }
405 fn version(&self) -> i32 {
406 self.version
407 }
408 fn created_at(&self) -> String {
409 self.created_at.clone()
410 }
411 fn updated_at(&self) -> String {
412 self.updated_at.clone()
413 }
414 fn item_count(&self) -> usize {
415 self.items.len()
416 }
417 fn inspect(&self) -> String {
418 format!(
419 "#<StateSet::Order number=\"{}\" status=\"{}\" total={} {}>",
420 self.order_number, self.status, self.total_amount, self.currency
421 )
422 }
423}
424
425impl From<stateset_core::Order> for Order {
426 fn from(o: stateset_core::Order) -> Self {
427 Self {
428 id: o.id.to_string(),
429 order_number: o.order_number,
430 customer_id: o.customer_id.to_string(),
431 status: format!("{}", o.status),
432 total_amount: to_f64_or_nan(o.total_amount),
433 currency: o.currency,
434 payment_status: format!("{}", o.payment_status),
435 fulfillment_status: format!("{}", o.fulfillment_status),
436 tracking_number: o.tracking_number,
437 items: o
438 .items
439 .into_iter()
440 .map(|i| OrderItem {
441 id: i.id.to_string(),
442 sku: i.sku,
443 name: i.name,
444 quantity: i.quantity,
445 unit_price: to_f64_or_nan(i.unit_price),
446 total: to_f64_or_nan(i.total),
447 })
448 .collect(),
449 version: o.version,
450 created_at: o.created_at.to_rfc3339(),
451 updated_at: o.updated_at.to_rfc3339(),
452 }
453 }
454}
455
456#[derive(Clone)]
461#[magnus::wrap(class = "StateSet::Orders", free_immediately, size)]
462pub struct Orders {
463 commerce: Arc<Mutex<RustCommerce>>,
464}
465
466impl Orders {
467 fn create(
468 &self,
469 customer_id: String,
470 items: Vec<RHash>,
471 currency: Option<String>,
472 notes: Option<String>,
473 ) -> Result<Order, Error> {
474 let commerce = lock_commerce!(self.commerce);
475 let cust_uuid = parse_uuid!(customer_id, "customer");
476
477 let order_items: Vec<stateset_core::CreateOrderItem> = items
478 .into_iter()
479 .map(|h| {
480 let sku: String = h.fetch(Symbol::new("sku")).unwrap_or_default();
481 let name: String = h.fetch(Symbol::new("name")).unwrap_or_default();
482 let quantity: i32 = h.fetch(Symbol::new("quantity")).unwrap_or(1);
483 let unit_price: f64 = h.fetch(Symbol::new("unit_price")).unwrap_or(0.0);
484
485 stateset_core::CreateOrderItem {
486 product_id: Default::default(),
487 variant_id: None,
488 sku,
489 name,
490 quantity,
491 unit_price: Decimal::from_f64_retain(unit_price).unwrap_or_default(),
492 ..Default::default()
493 }
494 })
495 .collect();
496
497 let order = commerce
498 .orders()
499 .create(stateset_core::CreateOrder {
500 customer_id: cust_uuid,
501 items: order_items,
502 currency,
503 notes,
504 ..Default::default()
505 })
506 .map_err(|e| {
507 Error::new(exception::runtime_error(), format!("Failed to create order: {}", e))
508 })?;
509
510 Ok(order.into())
511 }
512
513 fn get(&self, id: String) -> Result<Option<Order>, Error> {
514 let commerce = lock_commerce!(self.commerce);
515 let uuid = parse_uuid!(id, "order");
516
517 let order = commerce.orders().get(uuid).map_err(|e| {
518 Error::new(exception::runtime_error(), format!("Failed to get order: {}", e))
519 })?;
520
521 Ok(order.map(|o| o.into()))
522 }
523
524 fn list(&self) -> Result<Vec<Order>, Error> {
525 let commerce = lock_commerce!(self.commerce);
526
527 let orders = commerce.orders().list(Default::default()).map_err(|e| {
528 Error::new(exception::runtime_error(), format!("Failed to list orders: {}", e))
529 })?;
530
531 Ok(orders.into_iter().map(|o| o.into()).collect())
532 }
533
534 fn count(&self) -> Result<i64, Error> {
535 let commerce = lock_commerce!(self.commerce);
536
537 let count = commerce.orders().count(Default::default()).map_err(|e| {
538 Error::new(exception::runtime_error(), format!("Failed to count orders: {}", e))
539 })?;
540
541 Ok(count)
542 }
543
544 fn ship(
545 &self,
546 id: String,
547 tracking_number: Option<String>,
548 carrier: Option<String>,
549 ) -> Result<Order, Error> {
550 let commerce = lock_commerce!(self.commerce);
551 let uuid = parse_uuid!(id, "order");
552
553 let order = commerce.orders().ship(uuid, tracking_number, carrier).map_err(|e| {
554 Error::new(exception::runtime_error(), format!("Failed to ship order: {}", e))
555 })?;
556
557 Ok(order.into())
558 }
559
560 fn cancel(&self, id: String, reason: Option<String>) -> Result<Order, Error> {
561 let commerce = lock_commerce!(self.commerce);
562 let uuid = parse_uuid!(id, "order");
563
564 let order = commerce.orders().cancel(uuid, reason).map_err(|e| {
565 Error::new(exception::runtime_error(), format!("Failed to cancel order: {}", e))
566 })?;
567
568 Ok(order.into())
569 }
570
571 fn confirm(&self, id: String) -> Result<Order, Error> {
572 let commerce = lock_commerce!(self.commerce);
573 let uuid = parse_uuid!(id, "order");
574
575 let order = commerce.orders().confirm(uuid).map_err(|e| {
576 Error::new(exception::runtime_error(), format!("Failed to confirm order: {}", e))
577 })?;
578
579 Ok(order.into())
580 }
581
582 fn deliver(&self, id: String) -> Result<Order, Error> {
583 let commerce = lock_commerce!(self.commerce);
584 let uuid = parse_uuid!(id, "order");
585
586 let order = commerce.orders().deliver(uuid).map_err(|e| {
587 Error::new(exception::runtime_error(), format!("Failed to deliver order: {}", e))
588 })?;
589
590 Ok(order.into())
591 }
592}
593
594#[derive(Clone)]
599#[magnus::wrap(class = "StateSet::ProductVariant", free_immediately, size)]
600pub struct ProductVariant {
601 id: String,
602 sku: String,
603 name: String,
604 price: f64,
605 compare_at_price: Option<f64>,
606 inventory_quantity: i32,
607 weight: Option<f64>,
608 barcode: Option<String>,
609}
610
611impl ProductVariant {
612 fn id(&self) -> String {
613 self.id.clone()
614 }
615 fn sku(&self) -> String {
616 self.sku.clone()
617 }
618 fn name(&self) -> String {
619 self.name.clone()
620 }
621 fn price(&self) -> f64 {
622 self.price
623 }
624 fn compare_at_price(&self) -> Option<f64> {
625 self.compare_at_price
626 }
627 fn inventory_quantity(&self) -> i32 {
628 self.inventory_quantity
629 }
630 fn weight(&self) -> Option<f64> {
631 self.weight
632 }
633 fn barcode(&self) -> Option<String> {
634 self.barcode.clone()
635 }
636}
637
638#[derive(Clone)]
639#[magnus::wrap(class = "StateSet::Product", free_immediately, size)]
640pub struct Product {
641 id: String,
642 name: String,
643 description: Option<String>,
644 vendor: Option<String>,
645 product_type: Option<String>,
646 status: String,
647 tags: Vec<String>,
648 variants: Vec<ProductVariant>,
649 created_at: String,
650 updated_at: String,
651}
652
653impl Product {
654 fn id(&self) -> String {
655 self.id.clone()
656 }
657 fn name(&self) -> String {
658 self.name.clone()
659 }
660 fn description(&self) -> Option<String> {
661 self.description.clone()
662 }
663 fn vendor(&self) -> Option<String> {
664 self.vendor.clone()
665 }
666 fn product_type(&self) -> Option<String> {
667 self.product_type.clone()
668 }
669 fn status(&self) -> String {
670 self.status.clone()
671 }
672 fn tags(&self) -> Vec<String> {
673 self.tags.clone()
674 }
675 fn variants(&self) -> Vec<ProductVariant> {
676 self.variants.clone()
677 }
678 fn created_at(&self) -> String {
679 self.created_at.clone()
680 }
681 fn updated_at(&self) -> String {
682 self.updated_at.clone()
683 }
684 fn inspect(&self) -> String {
685 format!(
686 "#<StateSet::Product id=\"{}\" name=\"{}\" status=\"{}\">",
687 self.id, self.name, self.status
688 )
689 }
690}
691
692impl From<stateset_core::Product> for Product {
693 fn from(p: stateset_core::Product) -> Self {
694 Self {
695 id: p.id.to_string(),
696 name: p.name,
697 description: p.description,
698 vendor: p.vendor,
699 product_type: p.product_type,
700 status: format!("{}", p.status),
701 tags: p.tags,
702 variants: p
703 .variants
704 .into_iter()
705 .map(|v| ProductVariant {
706 id: v.id.to_string(),
707 sku: v.sku,
708 name: v.name,
709 price: to_f64_or_nan(v.price),
710 compare_at_price: v.compare_at_price.and_then(|p| p.to_f64()),
711 inventory_quantity: v.inventory_quantity,
712 weight: v.weight.and_then(|w| w.to_f64()),
713 barcode: v.barcode,
714 })
715 .collect(),
716 created_at: p.created_at.to_rfc3339(),
717 updated_at: p.updated_at.to_rfc3339(),
718 }
719 }
720}
721
722#[derive(Clone)]
727#[magnus::wrap(class = "StateSet::Products", free_immediately, size)]
728pub struct Products {
729 commerce: Arc<Mutex<RustCommerce>>,
730}
731
732impl Products {
733 fn create(
734 &self,
735 name: String,
736 description: Option<String>,
737 vendor: Option<String>,
738 product_type: Option<String>,
739 ) -> Result<Product, Error> {
740 let commerce = lock_commerce!(self.commerce);
741
742 let product = commerce
743 .products()
744 .create(stateset_core::CreateProduct {
745 name,
746 description,
747 vendor,
748 product_type,
749 ..Default::default()
750 })
751 .map_err(|e| {
752 Error::new(exception::runtime_error(), format!("Failed to create product: {}", e))
753 })?;
754
755 Ok(product.into())
756 }
757
758 fn get(&self, id: String) -> Result<Option<Product>, Error> {
759 let commerce = lock_commerce!(self.commerce);
760 let uuid = parse_uuid!(id, "product");
761
762 let product = commerce.products().get(uuid).map_err(|e| {
763 Error::new(exception::runtime_error(), format!("Failed to get product: {}", e))
764 })?;
765
766 Ok(product.map(|p| p.into()))
767 }
768
769 fn list(&self) -> Result<Vec<Product>, Error> {
770 let commerce = lock_commerce!(self.commerce);
771
772 let products = commerce.products().list(Default::default()).map_err(|e| {
773 Error::new(exception::runtime_error(), format!("Failed to list products: {}", e))
774 })?;
775
776 Ok(products.into_iter().map(|p| p.into()).collect())
777 }
778
779 fn count(&self) -> Result<i64, Error> {
780 let commerce = lock_commerce!(self.commerce);
781
782 let count = commerce.products().count(Default::default()).map_err(|e| {
783 Error::new(exception::runtime_error(), format!("Failed to count products: {}", e))
784 })?;
785
786 Ok(count)
787 }
788
789 fn get_by_sku(&self, sku: String) -> Result<Option<ProductVariant>, Error> {
790 let commerce = lock_commerce!(self.commerce);
791
792 let variant = commerce.products().get_variant_by_sku(&sku).map_err(|e| {
793 Error::new(exception::runtime_error(), format!("Failed to get variant: {}", e))
794 })?;
795
796 Ok(variant.map(|v| ProductVariant {
797 id: v.id.to_string(),
798 sku: v.sku,
799 name: v.name,
800 price: to_f64_or_nan(v.price),
801 compare_at_price: v.compare_at_price.and_then(|p| p.to_f64()),
802 inventory_quantity: v.inventory_quantity,
803 weight: v.weight.and_then(|w| w.to_f64()),
804 barcode: v.barcode,
805 }))
806 }
807}
808
809#[derive(Clone)]
814#[magnus::wrap(class = "StateSet::InventoryItem", free_immediately, size)]
815pub struct InventoryItem {
816 id: String,
817 sku: String,
818 quantity_on_hand: i32,
819 quantity_reserved: i32,
820 quantity_available: i32,
821 reorder_point: Option<i32>,
822 reorder_quantity: Option<i32>,
823 location_id: Option<String>,
824}
825
826impl InventoryItem {
827 fn id(&self) -> String {
828 self.id.clone()
829 }
830 fn sku(&self) -> String {
831 self.sku.clone()
832 }
833 fn quantity_on_hand(&self) -> i32 {
834 self.quantity_on_hand
835 }
836 fn quantity_reserved(&self) -> i32 {
837 self.quantity_reserved
838 }
839 fn quantity_available(&self) -> i32 {
840 self.quantity_available
841 }
842 fn reorder_point(&self) -> Option<i32> {
843 self.reorder_point
844 }
845 fn reorder_quantity(&self) -> Option<i32> {
846 self.reorder_quantity
847 }
848 fn location_id(&self) -> Option<String> {
849 self.location_id.clone()
850 }
851 fn inspect(&self) -> String {
852 format!(
853 "#<StateSet::InventoryItem sku=\"{}\" available={}>",
854 self.sku, self.quantity_available
855 )
856 }
857}
858
859impl From<stateset_core::InventoryItem> for InventoryItem {
860 fn from(i: stateset_core::InventoryItem) -> Self {
861 Self {
862 id: i.id.to_string(),
863 sku: i.sku,
864 quantity_on_hand: i.quantity_on_hand,
865 quantity_reserved: i.quantity_reserved,
866 quantity_available: i.quantity_available,
867 reorder_point: i.reorder_point,
868 reorder_quantity: i.reorder_quantity,
869 location_id: i.location_id.map(|id| id.to_string()),
870 }
871 }
872}
873
874#[derive(Clone)]
875#[magnus::wrap(class = "StateSet::Inventory", free_immediately, size)]
876pub struct Inventory {
877 commerce: Arc<Mutex<RustCommerce>>,
878}
879
880impl Inventory {
881 fn create(
882 &self,
883 sku: String,
884 quantity: i32,
885 reorder_point: Option<i32>,
886 reorder_quantity: Option<i32>,
887 ) -> Result<InventoryItem, Error> {
888 let commerce = lock_commerce!(self.commerce);
889
890 let item = commerce
891 .inventory()
892 .create(stateset_core::CreateInventoryItem {
893 sku,
894 quantity_on_hand: quantity,
895 reorder_point,
896 reorder_quantity,
897 ..Default::default()
898 })
899 .map_err(|e| {
900 Error::new(exception::runtime_error(), format!("Failed to create inventory: {}", e))
901 })?;
902
903 Ok(item.into())
904 }
905
906 fn get(&self, id: String) -> Result<Option<InventoryItem>, Error> {
907 let commerce = lock_commerce!(self.commerce);
908 let uuid = parse_uuid!(id, "inventory");
909
910 let item = commerce.inventory().get(uuid).map_err(|e| {
911 Error::new(exception::runtime_error(), format!("Failed to get inventory: {}", e))
912 })?;
913
914 Ok(item.map(|i| i.into()))
915 }
916
917 fn get_by_sku(&self, sku: String) -> Result<Option<InventoryItem>, Error> {
918 let commerce = lock_commerce!(self.commerce);
919
920 let item = commerce.inventory().get_by_sku(&sku).map_err(|e| {
921 Error::new(exception::runtime_error(), format!("Failed to get inventory: {}", e))
922 })?;
923
924 Ok(item.map(|i| i.into()))
925 }
926
927 fn list(&self) -> Result<Vec<InventoryItem>, Error> {
928 let commerce = lock_commerce!(self.commerce);
929
930 let items = commerce.inventory().list(Default::default()).map_err(|e| {
931 Error::new(exception::runtime_error(), format!("Failed to list inventory: {}", e))
932 })?;
933
934 Ok(items.into_iter().map(|i| i.into()).collect())
935 }
936
937 fn adjust(
938 &self,
939 id: String,
940 adjustment: i32,
941 reason: Option<String>,
942 ) -> Result<InventoryItem, Error> {
943 let commerce = lock_commerce!(self.commerce);
944 let uuid = parse_uuid!(id, "inventory");
945
946 let item = commerce.inventory().adjust(uuid, adjustment, reason).map_err(|e| {
947 Error::new(exception::runtime_error(), format!("Failed to adjust inventory: {}", e))
948 })?;
949
950 Ok(item.into())
951 }
952
953 fn reserve(
954 &self,
955 id: String,
956 quantity: i32,
957 order_id: Option<String>,
958 ) -> Result<InventoryItem, Error> {
959 let commerce = lock_commerce!(self.commerce);
960 let uuid = parse_uuid!(id, "inventory");
961 let order_uuid = order_id.map(|s| s.parse().ok()).flatten();
962
963 let item = commerce.inventory().reserve(uuid, quantity, order_uuid).map_err(|e| {
964 Error::new(exception::runtime_error(), format!("Failed to reserve inventory: {}", e))
965 })?;
966
967 Ok(item.into())
968 }
969
970 fn release(&self, id: String, quantity: i32) -> Result<InventoryItem, Error> {
971 let commerce = lock_commerce!(self.commerce);
972 let uuid = parse_uuid!(id, "inventory");
973
974 let item = commerce.inventory().release(uuid, quantity).map_err(|e| {
975 Error::new(exception::runtime_error(), format!("Failed to release inventory: {}", e))
976 })?;
977
978 Ok(item.into())
979 }
980}
981
982#[derive(Clone)]
987#[magnus::wrap(class = "StateSet::Return", free_immediately, size)]
988pub struct Return {
989 id: String,
990 order_id: String,
991 customer_id: String,
992 status: String,
993 reason: String,
994 refund_amount: f64,
995 created_at: String,
996 updated_at: String,
997}
998
999impl Return {
1000 fn id(&self) -> String {
1001 self.id.clone()
1002 }
1003 fn order_id(&self) -> String {
1004 self.order_id.clone()
1005 }
1006 fn customer_id(&self) -> String {
1007 self.customer_id.clone()
1008 }
1009 fn status(&self) -> String {
1010 self.status.clone()
1011 }
1012 fn reason(&self) -> String {
1013 self.reason.clone()
1014 }
1015 fn refund_amount(&self) -> f64 {
1016 self.refund_amount
1017 }
1018 fn created_at(&self) -> String {
1019 self.created_at.clone()
1020 }
1021 fn updated_at(&self) -> String {
1022 self.updated_at.clone()
1023 }
1024 fn inspect(&self) -> String {
1025 format!(
1026 "#<StateSet::Return id=\"{}\" status=\"{}\" refund={}>",
1027 self.id, self.status, self.refund_amount
1028 )
1029 }
1030}
1031
1032impl From<stateset_core::Return> for Return {
1033 fn from(r: stateset_core::Return) -> Self {
1034 Self {
1035 id: r.id.to_string(),
1036 order_id: r.order_id.to_string(),
1037 customer_id: r.customer_id.to_string(),
1038 status: format!("{}", r.status),
1039 reason: r.reason,
1040 refund_amount: to_f64_or_nan(r.refund_amount),
1041 created_at: r.created_at.to_rfc3339(),
1042 updated_at: r.updated_at.to_rfc3339(),
1043 }
1044 }
1045}
1046
1047#[derive(Clone)]
1048#[magnus::wrap(class = "StateSet::Returns", free_immediately, size)]
1049pub struct Returns {
1050 commerce: Arc<Mutex<RustCommerce>>,
1051}
1052
1053impl Returns {
1054 fn create(&self, order_id: String, reason: String) -> Result<Return, Error> {
1055 let commerce = lock_commerce!(self.commerce);
1056 let uuid = parse_uuid!(order_id, "order");
1057
1058 let reason_enum = reason
1059 .parse::<stateset_core::ReturnReason>()
1060 .unwrap_or(stateset_core::ReturnReason::Other);
1061
1062 let order = commerce.orders().get(uuid).map_err(|e| {
1063 Error::new(exception::runtime_error(), format!("Failed to fetch order: {}", e))
1064 })?;
1065
1066 let order = match order {
1067 Some(order) => order,
1068 None => {
1069 return Err(Error::new(
1070 exception::runtime_error(),
1071 format!("Order not found: {}", uuid),
1072 ));
1073 }
1074 };
1075
1076 let items: Vec<stateset_core::CreateReturnItem> = order
1077 .items
1078 .iter()
1079 .map(|item| stateset_core::CreateReturnItem {
1080 order_item_id: item.id,
1081 quantity: item.quantity,
1082 condition: None,
1083 })
1084 .collect();
1085
1086 if items.is_empty() {
1087 return Err(Error::new(
1088 exception::runtime_error(),
1089 "Return must have at least one item",
1090 ));
1091 }
1092
1093 let ret = commerce
1094 .returns()
1095 .create(stateset_core::CreateReturn {
1096 order_id: uuid,
1097 reason: reason_enum,
1098 items,
1099 ..Default::default()
1100 })
1101 .map_err(|e| {
1102 Error::new(exception::runtime_error(), format!("Failed to create return: {}", e))
1103 })?;
1104
1105 Ok(ret.into())
1106 }
1107
1108 fn get(&self, id: String) -> Result<Option<Return>, Error> {
1109 let commerce = lock_commerce!(self.commerce);
1110 let uuid = parse_uuid!(id, "return");
1111
1112 let ret = commerce.returns().get(uuid).map_err(|e| {
1113 Error::new(exception::runtime_error(), format!("Failed to get return: {}", e))
1114 })?;
1115
1116 Ok(ret.map(|r| r.into()))
1117 }
1118
1119 fn list(&self) -> Result<Vec<Return>, Error> {
1120 let commerce = lock_commerce!(self.commerce);
1121
1122 let returns = commerce.returns().list(Default::default()).map_err(|e| {
1123 Error::new(exception::runtime_error(), format!("Failed to list returns: {}", e))
1124 })?;
1125
1126 Ok(returns.into_iter().map(|r| r.into()).collect())
1127 }
1128
1129 fn approve(&self, id: String, refund_amount: Option<f64>) -> Result<Return, Error> {
1130 let commerce = lock_commerce!(self.commerce);
1131 let uuid = parse_uuid!(id, "return");
1132 let amount = refund_amount.map(|a| Decimal::from_f64_retain(a).unwrap_or_default());
1133
1134 let ret = commerce.returns().approve(uuid, amount).map_err(|e| {
1135 Error::new(exception::runtime_error(), format!("Failed to approve return: {}", e))
1136 })?;
1137
1138 Ok(ret.into())
1139 }
1140
1141 fn reject(&self, id: String, reason: Option<String>) -> Result<Return, Error> {
1142 let commerce = lock_commerce!(self.commerce);
1143 let uuid = parse_uuid!(id, "return");
1144
1145 let ret = commerce.returns().reject(uuid, reason).map_err(|e| {
1146 Error::new(exception::runtime_error(), format!("Failed to reject return: {}", e))
1147 })?;
1148
1149 Ok(ret.into())
1150 }
1151}
1152
1153#[derive(Clone)]
1158#[magnus::wrap(class = "StateSet::Payments", free_immediately, size)]
1159pub struct Payments {
1160 commerce: Arc<Mutex<RustCommerce>>,
1161}
1162
1163impl Payments {
1164 fn record(&self, order_id: String, amount: f64, method: Option<String>) -> Result<bool, Error> {
1165 let commerce = lock_commerce!(self.commerce);
1166 let uuid = parse_uuid!(order_id, "order");
1167 let decimal_amount = Decimal::from_f64_retain(amount).unwrap_or_default();
1168
1169 commerce.payments().record(uuid, decimal_amount, method).map_err(|e| {
1170 Error::new(exception::runtime_error(), format!("Failed to record payment: {}", e))
1171 })?;
1172
1173 Ok(true)
1174 }
1175}
1176
1177#[derive(Clone)]
1182#[magnus::wrap(class = "StateSet::Shipment", free_immediately, size)]
1183pub struct Shipment {
1184 id: String,
1185 shipment_number: String,
1186 order_id: String,
1187 status: String,
1188 carrier: Option<String>,
1189 tracking_number: Option<String>,
1190 shipping_method: Option<String>,
1191 weight: Option<f64>,
1192 estimated_delivery: Option<String>,
1193 shipped_at: Option<String>,
1194 delivered_at: Option<String>,
1195 created_at: String,
1196 updated_at: String,
1197}
1198
1199impl Shipment {
1200 fn id(&self) -> String {
1201 self.id.clone()
1202 }
1203 fn shipment_number(&self) -> String {
1204 self.shipment_number.clone()
1205 }
1206 fn order_id(&self) -> String {
1207 self.order_id.clone()
1208 }
1209 fn status(&self) -> String {
1210 self.status.clone()
1211 }
1212 fn carrier(&self) -> Option<String> {
1213 self.carrier.clone()
1214 }
1215 fn tracking_number(&self) -> Option<String> {
1216 self.tracking_number.clone()
1217 }
1218 fn shipping_method(&self) -> Option<String> {
1219 self.shipping_method.clone()
1220 }
1221 fn weight(&self) -> Option<f64> {
1222 self.weight
1223 }
1224 fn estimated_delivery(&self) -> Option<String> {
1225 self.estimated_delivery.clone()
1226 }
1227 fn shipped_at(&self) -> Option<String> {
1228 self.shipped_at.clone()
1229 }
1230 fn delivered_at(&self) -> Option<String> {
1231 self.delivered_at.clone()
1232 }
1233 fn created_at(&self) -> String {
1234 self.created_at.clone()
1235 }
1236 fn updated_at(&self) -> String {
1237 self.updated_at.clone()
1238 }
1239 fn inspect(&self) -> String {
1240 format!(
1241 "#<StateSet::Shipment number=\"{}\" status=\"{}\">",
1242 self.shipment_number, self.status
1243 )
1244 }
1245}
1246
1247impl From<stateset_core::Shipment> for Shipment {
1248 fn from(s: stateset_core::Shipment) -> Self {
1249 Self {
1250 id: s.id.to_string(),
1251 shipment_number: s.shipment_number,
1252 order_id: s.order_id.to_string(),
1253 status: format!("{}", s.status),
1254 carrier: s.carrier,
1255 tracking_number: s.tracking_number,
1256 shipping_method: s.shipping_method,
1257 weight: s.weight.and_then(|w| w.to_f64()),
1258 estimated_delivery: s.estimated_delivery.map(|d| d.to_rfc3339()),
1259 shipped_at: s.shipped_at.map(|d| d.to_rfc3339()),
1260 delivered_at: s.delivered_at.map(|d| d.to_rfc3339()),
1261 created_at: s.created_at.to_rfc3339(),
1262 updated_at: s.updated_at.to_rfc3339(),
1263 }
1264 }
1265}
1266
1267#[derive(Clone)]
1268#[magnus::wrap(class = "StateSet::Shipments", free_immediately, size)]
1269pub struct Shipments {
1270 commerce: Arc<Mutex<RustCommerce>>,
1271}
1272
1273impl Shipments {
1274 fn create(
1275 &self,
1276 order_id: String,
1277 carrier: Option<String>,
1278 shipping_method: Option<String>,
1279 ) -> Result<Shipment, Error> {
1280 let commerce = lock_commerce!(self.commerce);
1281 let uuid = parse_uuid!(order_id, "order");
1282 let shipment = commerce
1283 .shipments()
1284 .create(stateset_core::CreateShipment {
1285 order_id: uuid,
1286 carrier,
1287 shipping_method,
1288 ..Default::default()
1289 })
1290 .map_err(|e| {
1291 Error::new(exception::runtime_error(), format!("Failed to create shipment: {}", e))
1292 })?;
1293 Ok(shipment.into())
1294 }
1295
1296 fn get(&self, id: String) -> Result<Option<Shipment>, Error> {
1297 let commerce = lock_commerce!(self.commerce);
1298 let uuid = parse_uuid!(id, "shipment");
1299 let shipment = commerce.shipments().get(uuid).map_err(|e| {
1300 Error::new(exception::runtime_error(), format!("Failed to get shipment: {}", e))
1301 })?;
1302 Ok(shipment.map(|s| s.into()))
1303 }
1304
1305 fn get_by_tracking(&self, tracking_number: String) -> Result<Option<Shipment>, Error> {
1306 let commerce = lock_commerce!(self.commerce);
1307 let shipment = commerce.shipments().get_by_tracking(&tracking_number).map_err(|e| {
1308 Error::new(exception::runtime_error(), format!("Failed to get shipment: {}", e))
1309 })?;
1310 Ok(shipment.map(|s| s.into()))
1311 }
1312
1313 fn list(&self) -> Result<Vec<Shipment>, Error> {
1314 let commerce = lock_commerce!(self.commerce);
1315 let shipments = commerce.shipments().list(Default::default()).map_err(|e| {
1316 Error::new(exception::runtime_error(), format!("Failed to list shipments: {}", e))
1317 })?;
1318 Ok(shipments.into_iter().map(|s| s.into()).collect())
1319 }
1320
1321 fn for_order(&self, order_id: String) -> Result<Vec<Shipment>, Error> {
1322 let commerce = lock_commerce!(self.commerce);
1323 let uuid = parse_uuid!(order_id, "order");
1324 let shipments = commerce.shipments().for_order(uuid).map_err(|e| {
1325 Error::new(exception::runtime_error(), format!("Failed to get shipments: {}", e))
1326 })?;
1327 Ok(shipments.into_iter().map(|s| s.into()).collect())
1328 }
1329
1330 fn ship(&self, id: String, tracking_number: Option<String>) -> Result<Shipment, Error> {
1331 let commerce = lock_commerce!(self.commerce);
1332 let uuid = parse_uuid!(id, "shipment");
1333 let shipment = commerce.shipments().ship(uuid, tracking_number).map_err(|e| {
1334 Error::new(exception::runtime_error(), format!("Failed to ship: {}", e))
1335 })?;
1336 Ok(shipment.into())
1337 }
1338
1339 fn mark_delivered(&self, id: String) -> Result<Shipment, Error> {
1340 let commerce = lock_commerce!(self.commerce);
1341 let uuid = parse_uuid!(id, "shipment");
1342 let shipment = commerce.shipments().mark_delivered(uuid).map_err(|e| {
1343 Error::new(exception::runtime_error(), format!("Failed to mark delivered: {}", e))
1344 })?;
1345 Ok(shipment.into())
1346 }
1347
1348 fn cancel(&self, id: String) -> Result<Shipment, Error> {
1349 let commerce = lock_commerce!(self.commerce);
1350 let uuid = parse_uuid!(id, "shipment");
1351 let shipment = commerce.shipments().cancel(uuid).map_err(|e| {
1352 Error::new(exception::runtime_error(), format!("Failed to cancel: {}", e))
1353 })?;
1354 Ok(shipment.into())
1355 }
1356
1357 fn count(&self) -> Result<i64, Error> {
1358 let commerce = lock_commerce!(self.commerce);
1359 let count = commerce.shipments().count(Default::default()).map_err(|e| {
1360 Error::new(exception::runtime_error(), format!("Failed to count: {}", e))
1361 })?;
1362 Ok(count as i64)
1363 }
1364}
1365
1366#[derive(Clone)]
1371#[magnus::wrap(class = "StateSet::Warranty", free_immediately, size)]
1372pub struct Warranty {
1373 id: String,
1374 warranty_number: String,
1375 order_id: String,
1376 customer_id: String,
1377 product_id: Option<String>,
1378 serial_number: Option<String>,
1379 status: String,
1380 warranty_type: String,
1381 start_date: String,
1382 end_date: String,
1383 created_at: String,
1384 updated_at: String,
1385}
1386
1387impl Warranty {
1388 fn id(&self) -> String {
1389 self.id.clone()
1390 }
1391 fn warranty_number(&self) -> String {
1392 self.warranty_number.clone()
1393 }
1394 fn order_id(&self) -> String {
1395 self.order_id.clone()
1396 }
1397 fn customer_id(&self) -> String {
1398 self.customer_id.clone()
1399 }
1400 fn product_id(&self) -> Option<String> {
1401 self.product_id.clone()
1402 }
1403 fn serial_number(&self) -> Option<String> {
1404 self.serial_number.clone()
1405 }
1406 fn status(&self) -> String {
1407 self.status.clone()
1408 }
1409 fn warranty_type(&self) -> String {
1410 self.warranty_type.clone()
1411 }
1412 fn start_date(&self) -> String {
1413 self.start_date.clone()
1414 }
1415 fn end_date(&self) -> String {
1416 self.end_date.clone()
1417 }
1418 fn created_at(&self) -> String {
1419 self.created_at.clone()
1420 }
1421 fn updated_at(&self) -> String {
1422 self.updated_at.clone()
1423 }
1424 fn inspect(&self) -> String {
1425 format!(
1426 "#<StateSet::Warranty number=\"{}\" status=\"{}\">",
1427 self.warranty_number, self.status
1428 )
1429 }
1430}
1431
1432impl From<stateset_core::Warranty> for Warranty {
1433 fn from(w: stateset_core::Warranty) -> Self {
1434 Self {
1435 id: w.id.to_string(),
1436 warranty_number: w.warranty_number,
1437 order_id: w.order_id.to_string(),
1438 customer_id: w.customer_id.to_string(),
1439 product_id: w.product_id.map(|p| p.to_string()),
1440 serial_number: w.serial_number,
1441 status: format!("{}", w.status),
1442 warranty_type: format!("{}", w.warranty_type),
1443 start_date: w.start_date.to_string(),
1444 end_date: w.end_date.to_string(),
1445 created_at: w.created_at.to_rfc3339(),
1446 updated_at: w.updated_at.to_rfc3339(),
1447 }
1448 }
1449}
1450
1451#[derive(Clone)]
1452#[magnus::wrap(class = "StateSet::WarrantyClaim", free_immediately, size)]
1453pub struct WarrantyClaim {
1454 id: String,
1455 claim_number: String,
1456 warranty_id: String,
1457 status: String,
1458 description: String,
1459 resolution: Option<String>,
1460 created_at: String,
1461 updated_at: String,
1462}
1463
1464impl WarrantyClaim {
1465 fn id(&self) -> String {
1466 self.id.clone()
1467 }
1468 fn claim_number(&self) -> String {
1469 self.claim_number.clone()
1470 }
1471 fn warranty_id(&self) -> String {
1472 self.warranty_id.clone()
1473 }
1474 fn status(&self) -> String {
1475 self.status.clone()
1476 }
1477 fn description(&self) -> String {
1478 self.description.clone()
1479 }
1480 fn resolution(&self) -> Option<String> {
1481 self.resolution.clone()
1482 }
1483 fn created_at(&self) -> String {
1484 self.created_at.clone()
1485 }
1486 fn updated_at(&self) -> String {
1487 self.updated_at.clone()
1488 }
1489}
1490
1491impl From<stateset_core::WarrantyClaim> for WarrantyClaim {
1492 fn from(c: stateset_core::WarrantyClaim) -> Self {
1493 Self {
1494 id: c.id.to_string(),
1495 claim_number: c.claim_number,
1496 warranty_id: c.warranty_id.to_string(),
1497 status: format!("{}", c.status),
1498 description: c.description,
1499 resolution: c.resolution,
1500 created_at: c.created_at.to_rfc3339(),
1501 updated_at: c.updated_at.to_rfc3339(),
1502 }
1503 }
1504}
1505
1506#[derive(Clone)]
1507#[magnus::wrap(class = "StateSet::Warranties", free_immediately, size)]
1508pub struct Warranties {
1509 commerce: Arc<Mutex<RustCommerce>>,
1510}
1511
1512impl Warranties {
1513 fn create(
1514 &self,
1515 order_id: String,
1516 customer_id: String,
1517 warranty_type: String,
1518 duration_months: i32,
1519 ) -> Result<Warranty, Error> {
1520 let commerce = lock_commerce!(self.commerce);
1521 let order_uuid = parse_uuid!(order_id, "order");
1522 let customer_uuid = parse_uuid!(customer_id, "customer");
1523 let warranty = commerce
1524 .warranties()
1525 .create(stateset_core::CreateWarranty {
1526 order_id: order_uuid,
1527 customer_id: customer_uuid,
1528 warranty_type: warranty_type.parse().unwrap_or_default(),
1529 duration_months,
1530 ..Default::default()
1531 })
1532 .map_err(|e| {
1533 Error::new(exception::runtime_error(), format!("Failed to create warranty: {}", e))
1534 })?;
1535 Ok(warranty.into())
1536 }
1537
1538 fn get(&self, id: String) -> Result<Option<Warranty>, Error> {
1539 let commerce = lock_commerce!(self.commerce);
1540 let uuid = parse_uuid!(id, "warranty");
1541 let warranty = commerce.warranties().get(uuid).map_err(|e| {
1542 Error::new(exception::runtime_error(), format!("Failed to get warranty: {}", e))
1543 })?;
1544 Ok(warranty.map(|w| w.into()))
1545 }
1546
1547 fn list(&self) -> Result<Vec<Warranty>, Error> {
1548 let commerce = lock_commerce!(self.commerce);
1549 let warranties = commerce.warranties().list(Default::default()).map_err(|e| {
1550 Error::new(exception::runtime_error(), format!("Failed to list warranties: {}", e))
1551 })?;
1552 Ok(warranties.into_iter().map(|w| w.into()).collect())
1553 }
1554
1555 fn for_customer(&self, customer_id: String) -> Result<Vec<Warranty>, Error> {
1556 let commerce = lock_commerce!(self.commerce);
1557 let uuid = parse_uuid!(customer_id, "customer");
1558 let warranties = commerce.warranties().for_customer(uuid).map_err(|e| {
1559 Error::new(exception::runtime_error(), format!("Failed to get warranties: {}", e))
1560 })?;
1561 Ok(warranties.into_iter().map(|w| w.into()).collect())
1562 }
1563
1564 fn is_valid(&self, id: String) -> Result<bool, Error> {
1565 let commerce = lock_commerce!(self.commerce);
1566 let uuid = parse_uuid!(id, "warranty");
1567 let valid = commerce.warranties().is_valid(uuid).map_err(|e| {
1568 Error::new(exception::runtime_error(), format!("Failed to check warranty: {}", e))
1569 })?;
1570 Ok(valid)
1571 }
1572
1573 fn create_claim(
1574 &self,
1575 warranty_id: String,
1576 description: String,
1577 ) -> Result<WarrantyClaim, Error> {
1578 let commerce = lock_commerce!(self.commerce);
1579 let uuid = parse_uuid!(warranty_id, "warranty");
1580 let claim = commerce
1581 .warranties()
1582 .create_claim(stateset_core::CreateWarrantyClaim {
1583 warranty_id: uuid,
1584 description,
1585 ..Default::default()
1586 })
1587 .map_err(|e| {
1588 Error::new(exception::runtime_error(), format!("Failed to create claim: {}", e))
1589 })?;
1590 Ok(claim.into())
1591 }
1592
1593 fn approve_claim(&self, id: String) -> Result<WarrantyClaim, Error> {
1594 let commerce = lock_commerce!(self.commerce);
1595 let uuid = parse_uuid!(id, "claim");
1596 let claim = commerce.warranties().approve_claim(uuid).map_err(|e| {
1597 Error::new(exception::runtime_error(), format!("Failed to approve claim: {}", e))
1598 })?;
1599 Ok(claim.into())
1600 }
1601
1602 fn deny_claim(&self, id: String, reason: String) -> Result<WarrantyClaim, Error> {
1603 let commerce = lock_commerce!(self.commerce);
1604 let uuid = parse_uuid!(id, "claim");
1605 let claim = commerce.warranties().deny_claim(uuid, &reason).map_err(|e| {
1606 Error::new(exception::runtime_error(), format!("Failed to deny claim: {}", e))
1607 })?;
1608 Ok(claim.into())
1609 }
1610
1611 fn count(&self) -> Result<i64, Error> {
1612 let commerce = lock_commerce!(self.commerce);
1613 let count = commerce.warranties().count(Default::default()).map_err(|e| {
1614 Error::new(exception::runtime_error(), format!("Failed to count: {}", e))
1615 })?;
1616 Ok(count as i64)
1617 }
1618}
1619
1620#[derive(Clone)]
1625#[magnus::wrap(class = "StateSet::Supplier", free_immediately, size)]
1626pub struct Supplier {
1627 id: String,
1628 code: String,
1629 name: String,
1630 email: Option<String>,
1631 phone: Option<String>,
1632 status: String,
1633 created_at: String,
1634 updated_at: String,
1635}
1636
1637impl Supplier {
1638 fn id(&self) -> String {
1639 self.id.clone()
1640 }
1641 fn code(&self) -> String {
1642 self.code.clone()
1643 }
1644 fn name(&self) -> String {
1645 self.name.clone()
1646 }
1647 fn email(&self) -> Option<String> {
1648 self.email.clone()
1649 }
1650 fn phone(&self) -> Option<String> {
1651 self.phone.clone()
1652 }
1653 fn status(&self) -> String {
1654 self.status.clone()
1655 }
1656 fn created_at(&self) -> String {
1657 self.created_at.clone()
1658 }
1659 fn updated_at(&self) -> String {
1660 self.updated_at.clone()
1661 }
1662}
1663
1664impl From<stateset_core::Supplier> for Supplier {
1665 fn from(s: stateset_core::Supplier) -> Self {
1666 Self {
1667 id: s.id.to_string(),
1668 code: s.code,
1669 name: s.name,
1670 email: s.email,
1671 phone: s.phone,
1672 status: format!("{}", s.status),
1673 created_at: s.created_at.to_rfc3339(),
1674 updated_at: s.updated_at.to_rfc3339(),
1675 }
1676 }
1677}
1678
1679#[derive(Clone)]
1680#[magnus::wrap(class = "StateSet::PurchaseOrder", free_immediately, size)]
1681pub struct PurchaseOrder {
1682 id: String,
1683 po_number: String,
1684 supplier_id: String,
1685 status: String,
1686 total_amount: f64,
1687 currency: String,
1688 expected_delivery: Option<String>,
1689 created_at: String,
1690 updated_at: String,
1691}
1692
1693impl PurchaseOrder {
1694 fn id(&self) -> String {
1695 self.id.clone()
1696 }
1697 fn po_number(&self) -> String {
1698 self.po_number.clone()
1699 }
1700 fn supplier_id(&self) -> String {
1701 self.supplier_id.clone()
1702 }
1703 fn status(&self) -> String {
1704 self.status.clone()
1705 }
1706 fn total_amount(&self) -> f64 {
1707 self.total_amount
1708 }
1709 fn currency(&self) -> String {
1710 self.currency.clone()
1711 }
1712 fn expected_delivery(&self) -> Option<String> {
1713 self.expected_delivery.clone()
1714 }
1715 fn created_at(&self) -> String {
1716 self.created_at.clone()
1717 }
1718 fn updated_at(&self) -> String {
1719 self.updated_at.clone()
1720 }
1721 fn inspect(&self) -> String {
1722 format!(
1723 "#<StateSet::PurchaseOrder number=\"{}\" status=\"{}\">",
1724 self.po_number, self.status
1725 )
1726 }
1727}
1728
1729impl From<stateset_core::PurchaseOrder> for PurchaseOrder {
1730 fn from(p: stateset_core::PurchaseOrder) -> Self {
1731 Self {
1732 id: p.id.to_string(),
1733 po_number: p.po_number,
1734 supplier_id: p.supplier_id.to_string(),
1735 status: format!("{}", p.status),
1736 total_amount: to_f64_or_nan(p.total_amount),
1737 currency: p.currency,
1738 expected_delivery: p.expected_delivery.map(|d| d.to_string()),
1739 created_at: p.created_at.to_rfc3339(),
1740 updated_at: p.updated_at.to_rfc3339(),
1741 }
1742 }
1743}
1744
1745#[derive(Clone)]
1746#[magnus::wrap(class = "StateSet::PurchaseOrders", free_immediately, size)]
1747pub struct PurchaseOrders {
1748 commerce: Arc<Mutex<RustCommerce>>,
1749}
1750
1751impl PurchaseOrders {
1752 fn create_supplier(
1753 &self,
1754 code: String,
1755 name: String,
1756 email: Option<String>,
1757 ) -> Result<Supplier, Error> {
1758 let commerce = lock_commerce!(self.commerce);
1759 let supplier = commerce
1760 .purchase_orders()
1761 .create_supplier(stateset_core::CreateSupplier {
1762 code,
1763 name,
1764 email,
1765 ..Default::default()
1766 })
1767 .map_err(|e| {
1768 Error::new(exception::runtime_error(), format!("Failed to create supplier: {}", e))
1769 })?;
1770 Ok(supplier.into())
1771 }
1772
1773 fn get_supplier(&self, id: String) -> Result<Option<Supplier>, Error> {
1774 let commerce = lock_commerce!(self.commerce);
1775 let uuid = parse_uuid!(id, "supplier");
1776 let supplier = commerce.purchase_orders().get_supplier(uuid).map_err(|e| {
1777 Error::new(exception::runtime_error(), format!("Failed to get supplier: {}", e))
1778 })?;
1779 Ok(supplier.map(|s| s.into()))
1780 }
1781
1782 fn list_suppliers(&self) -> Result<Vec<Supplier>, Error> {
1783 let commerce = lock_commerce!(self.commerce);
1784 let suppliers =
1785 commerce.purchase_orders().list_suppliers(Default::default()).map_err(|e| {
1786 Error::new(exception::runtime_error(), format!("Failed to list suppliers: {}", e))
1787 })?;
1788 Ok(suppliers.into_iter().map(|s| s.into()).collect())
1789 }
1790
1791 fn create(
1792 &self,
1793 supplier_id: String,
1794 currency: Option<String>,
1795 ) -> Result<PurchaseOrder, Error> {
1796 let commerce = lock_commerce!(self.commerce);
1797 let uuid = parse_uuid!(supplier_id, "supplier");
1798 let po = commerce
1799 .purchase_orders()
1800 .create(stateset_core::CreatePurchaseOrder {
1801 supplier_id: uuid,
1802 currency,
1803 ..Default::default()
1804 })
1805 .map_err(|e| {
1806 Error::new(exception::runtime_error(), format!("Failed to create PO: {}", e))
1807 })?;
1808 Ok(po.into())
1809 }
1810
1811 fn get(&self, id: String) -> Result<Option<PurchaseOrder>, Error> {
1812 let commerce = lock_commerce!(self.commerce);
1813 let uuid = parse_uuid!(id, "purchase_order");
1814 let po = commerce.purchase_orders().get(uuid).map_err(|e| {
1815 Error::new(exception::runtime_error(), format!("Failed to get PO: {}", e))
1816 })?;
1817 Ok(po.map(|p| p.into()))
1818 }
1819
1820 fn list(&self) -> Result<Vec<PurchaseOrder>, Error> {
1821 let commerce = lock_commerce!(self.commerce);
1822 let pos = commerce.purchase_orders().list(Default::default()).map_err(|e| {
1823 Error::new(exception::runtime_error(), format!("Failed to list POs: {}", e))
1824 })?;
1825 Ok(pos.into_iter().map(|p| p.into()).collect())
1826 }
1827
1828 fn submit(&self, id: String) -> Result<PurchaseOrder, Error> {
1829 let commerce = lock_commerce!(self.commerce);
1830 let uuid = parse_uuid!(id, "purchase_order");
1831 let po = commerce.purchase_orders().submit(uuid).map_err(|e| {
1832 Error::new(exception::runtime_error(), format!("Failed to submit PO: {}", e))
1833 })?;
1834 Ok(po.into())
1835 }
1836
1837 fn approve(&self, id: String, approved_by: String) -> Result<PurchaseOrder, Error> {
1838 let commerce = lock_commerce!(self.commerce);
1839 let uuid = parse_uuid!(id, "purchase_order");
1840 let po = commerce.purchase_orders().approve(uuid, &approved_by).map_err(|e| {
1841 Error::new(exception::runtime_error(), format!("Failed to approve PO: {}", e))
1842 })?;
1843 Ok(po.into())
1844 }
1845
1846 fn cancel(&self, id: String) -> Result<PurchaseOrder, Error> {
1847 let commerce = lock_commerce!(self.commerce);
1848 let uuid = parse_uuid!(id, "purchase_order");
1849 let po = commerce.purchase_orders().cancel(uuid).map_err(|e| {
1850 Error::new(exception::runtime_error(), format!("Failed to cancel PO: {}", e))
1851 })?;
1852 Ok(po.into())
1853 }
1854
1855 fn complete(&self, id: String) -> Result<PurchaseOrder, Error> {
1856 let commerce = lock_commerce!(self.commerce);
1857 let uuid = parse_uuid!(id, "purchase_order");
1858 let po = commerce.purchase_orders().complete(uuid).map_err(|e| {
1859 Error::new(exception::runtime_error(), format!("Failed to complete PO: {}", e))
1860 })?;
1861 Ok(po.into())
1862 }
1863
1864 fn count(&self) -> Result<i64, Error> {
1865 let commerce = lock_commerce!(self.commerce);
1866 let count = commerce.purchase_orders().count(Default::default()).map_err(|e| {
1867 Error::new(exception::runtime_error(), format!("Failed to count: {}", e))
1868 })?;
1869 Ok(count as i64)
1870 }
1871}
1872
1873#[derive(Clone)]
1878#[magnus::wrap(class = "StateSet::Invoice", free_immediately, size)]
1879pub struct Invoice {
1880 id: String,
1881 invoice_number: String,
1882 customer_id: String,
1883 order_id: Option<String>,
1884 status: String,
1885 subtotal: f64,
1886 tax_amount: f64,
1887 total_amount: f64,
1888 amount_paid: f64,
1889 amount_due: f64,
1890 currency: String,
1891 due_date: Option<String>,
1892 created_at: String,
1893 updated_at: String,
1894}
1895
1896impl Invoice {
1897 fn id(&self) -> String {
1898 self.id.clone()
1899 }
1900 fn invoice_number(&self) -> String {
1901 self.invoice_number.clone()
1902 }
1903 fn customer_id(&self) -> String {
1904 self.customer_id.clone()
1905 }
1906 fn order_id(&self) -> Option<String> {
1907 self.order_id.clone()
1908 }
1909 fn status(&self) -> String {
1910 self.status.clone()
1911 }
1912 fn subtotal(&self) -> f64 {
1913 self.subtotal
1914 }
1915 fn tax_amount(&self) -> f64 {
1916 self.tax_amount
1917 }
1918 fn total_amount(&self) -> f64 {
1919 self.total_amount
1920 }
1921 fn amount_paid(&self) -> f64 {
1922 self.amount_paid
1923 }
1924 fn amount_due(&self) -> f64 {
1925 self.amount_due
1926 }
1927 fn currency(&self) -> String {
1928 self.currency.clone()
1929 }
1930 fn due_date(&self) -> Option<String> {
1931 self.due_date.clone()
1932 }
1933 fn created_at(&self) -> String {
1934 self.created_at.clone()
1935 }
1936 fn updated_at(&self) -> String {
1937 self.updated_at.clone()
1938 }
1939 fn inspect(&self) -> String {
1940 format!("#<StateSet::Invoice number=\"{}\" due={}>", self.invoice_number, self.amount_due)
1941 }
1942}
1943
1944impl From<stateset_core::Invoice> for Invoice {
1945 fn from(i: stateset_core::Invoice) -> Self {
1946 Self {
1947 id: i.id.to_string(),
1948 invoice_number: i.invoice_number,
1949 customer_id: i.customer_id.to_string(),
1950 order_id: i.order_id.map(|o| o.to_string()),
1951 status: format!("{}", i.status),
1952 subtotal: to_f64_or_nan(i.subtotal),
1953 tax_amount: to_f64_or_nan(i.tax_amount),
1954 total_amount: to_f64_or_nan(i.total_amount),
1955 amount_paid: to_f64_or_nan(i.amount_paid),
1956 amount_due: to_f64_or_nan(i.amount_due),
1957 currency: i.currency,
1958 due_date: i.due_date.map(|d| d.to_string()),
1959 created_at: i.created_at.to_rfc3339(),
1960 updated_at: i.updated_at.to_rfc3339(),
1961 }
1962 }
1963}
1964
1965#[derive(Clone)]
1966#[magnus::wrap(class = "StateSet::Invoices", free_immediately, size)]
1967pub struct Invoices {
1968 commerce: Arc<Mutex<RustCommerce>>,
1969}
1970
1971impl Invoices {
1972 fn create(
1973 &self,
1974 customer_id: String,
1975 order_id: Option<String>,
1976 due_days: Option<i32>,
1977 ) -> Result<Invoice, Error> {
1978 let commerce = lock_commerce!(self.commerce);
1979 let cust_uuid = parse_uuid!(customer_id, "customer");
1980 let order_uuid = order_id.map(|s| s.parse().ok()).flatten();
1981 let invoice = commerce
1982 .invoices()
1983 .create(stateset_core::CreateInvoice {
1984 customer_id: cust_uuid,
1985 order_id: order_uuid,
1986 due_days,
1987 ..Default::default()
1988 })
1989 .map_err(|e| {
1990 Error::new(exception::runtime_error(), format!("Failed to create invoice: {}", e))
1991 })?;
1992 Ok(invoice.into())
1993 }
1994
1995 fn get(&self, id: String) -> Result<Option<Invoice>, Error> {
1996 let commerce = lock_commerce!(self.commerce);
1997 let uuid = parse_uuid!(id, "invoice");
1998 let invoice = commerce.invoices().get(uuid).map_err(|e| {
1999 Error::new(exception::runtime_error(), format!("Failed to get invoice: {}", e))
2000 })?;
2001 Ok(invoice.map(|i| i.into()))
2002 }
2003
2004 fn list(&self) -> Result<Vec<Invoice>, Error> {
2005 let commerce = lock_commerce!(self.commerce);
2006 let invoices = commerce.invoices().list(Default::default()).map_err(|e| {
2007 Error::new(exception::runtime_error(), format!("Failed to list invoices: {}", e))
2008 })?;
2009 Ok(invoices.into_iter().map(|i| i.into()).collect())
2010 }
2011
2012 fn for_customer(&self, customer_id: String) -> Result<Vec<Invoice>, Error> {
2013 let commerce = lock_commerce!(self.commerce);
2014 let uuid = parse_uuid!(customer_id, "customer");
2015 let invoices = commerce.invoices().for_customer(uuid).map_err(|e| {
2016 Error::new(exception::runtime_error(), format!("Failed to get invoices: {}", e))
2017 })?;
2018 Ok(invoices.into_iter().map(|i| i.into()).collect())
2019 }
2020
2021 fn send(&self, id: String) -> Result<Invoice, Error> {
2022 let commerce = lock_commerce!(self.commerce);
2023 let uuid = parse_uuid!(id, "invoice");
2024 let invoice = commerce.invoices().send(uuid).map_err(|e| {
2025 Error::new(exception::runtime_error(), format!("Failed to send invoice: {}", e))
2026 })?;
2027 Ok(invoice.into())
2028 }
2029
2030 fn record_payment(
2031 &self,
2032 id: String,
2033 amount: f64,
2034 method: Option<String>,
2035 ) -> Result<Invoice, Error> {
2036 let commerce = lock_commerce!(self.commerce);
2037 let uuid = parse_uuid!(id, "invoice");
2038 let invoice = commerce
2039 .invoices()
2040 .record_payment(
2041 uuid,
2042 stateset_core::RecordInvoicePayment {
2043 amount: Decimal::from_f64_retain(amount).unwrap_or_default(),
2044 payment_method: method,
2045 ..Default::default()
2046 },
2047 )
2048 .map_err(|e| {
2049 Error::new(exception::runtime_error(), format!("Failed to record payment: {}", e))
2050 })?;
2051 Ok(invoice.into())
2052 }
2053
2054 fn void(&self, id: String) -> Result<Invoice, Error> {
2055 let commerce = lock_commerce!(self.commerce);
2056 let uuid = parse_uuid!(id, "invoice");
2057 let invoice = commerce.invoices().void(uuid).map_err(|e| {
2058 Error::new(exception::runtime_error(), format!("Failed to void invoice: {}", e))
2059 })?;
2060 Ok(invoice.into())
2061 }
2062
2063 fn get_overdue(&self) -> Result<Vec<Invoice>, Error> {
2064 let commerce = lock_commerce!(self.commerce);
2065 let invoices = commerce.invoices().get_overdue().map_err(|e| {
2066 Error::new(exception::runtime_error(), format!("Failed to get overdue: {}", e))
2067 })?;
2068 Ok(invoices.into_iter().map(|i| i.into()).collect())
2069 }
2070
2071 fn customer_balance(&self, customer_id: String) -> Result<f64, Error> {
2072 let commerce = lock_commerce!(self.commerce);
2073 let uuid = parse_uuid!(customer_id, "customer");
2074 let balance = commerce.invoices().customer_balance(uuid).map_err(|e| {
2075 Error::new(exception::runtime_error(), format!("Failed to get balance: {}", e))
2076 })?;
2077 Ok(to_f64_or_nan(balance))
2078 }
2079
2080 fn count(&self) -> Result<i64, Error> {
2081 let commerce = lock_commerce!(self.commerce);
2082 let count = commerce.invoices().count(Default::default()).map_err(|e| {
2083 Error::new(exception::runtime_error(), format!("Failed to count: {}", e))
2084 })?;
2085 Ok(count as i64)
2086 }
2087}
2088
2089#[derive(Clone)]
2094#[magnus::wrap(class = "StateSet::BillOfMaterials", free_immediately, size)]
2095pub struct BillOfMaterials {
2096 id: String,
2097 bom_number: String,
2098 name: String,
2099 product_id: Option<String>,
2100 status: String,
2101 version: i32,
2102 created_at: String,
2103 updated_at: String,
2104}
2105
2106impl BillOfMaterials {
2107 fn id(&self) -> String {
2108 self.id.clone()
2109 }
2110 fn bom_number(&self) -> String {
2111 self.bom_number.clone()
2112 }
2113 fn name(&self) -> String {
2114 self.name.clone()
2115 }
2116 fn product_id(&self) -> Option<String> {
2117 self.product_id.clone()
2118 }
2119 fn status(&self) -> String {
2120 self.status.clone()
2121 }
2122 fn version(&self) -> i32 {
2123 self.version
2124 }
2125 fn created_at(&self) -> String {
2126 self.created_at.clone()
2127 }
2128 fn updated_at(&self) -> String {
2129 self.updated_at.clone()
2130 }
2131 fn inspect(&self) -> String {
2132 format!(
2133 "#<StateSet::BillOfMaterials number=\"{}\" name=\"{}\">",
2134 self.bom_number, self.name
2135 )
2136 }
2137}
2138
2139impl From<stateset_core::BillOfMaterials> for BillOfMaterials {
2140 fn from(b: stateset_core::BillOfMaterials) -> Self {
2141 Self {
2142 id: b.id.to_string(),
2143 bom_number: b.bom_number,
2144 name: b.name,
2145 product_id: b.product_id.map(|p| p.to_string()),
2146 status: format!("{}", b.status),
2147 version: b.version,
2148 created_at: b.created_at.to_rfc3339(),
2149 updated_at: b.updated_at.to_rfc3339(),
2150 }
2151 }
2152}
2153
2154#[derive(Clone)]
2155#[magnus::wrap(class = "StateSet::BomComponent", free_immediately, size)]
2156pub struct BomComponent {
2157 id: String,
2158 bom_id: String,
2159 sku: String,
2160 name: String,
2161 quantity: f64,
2162 unit: String,
2163}
2164
2165impl BomComponent {
2166 fn id(&self) -> String {
2167 self.id.clone()
2168 }
2169 fn bom_id(&self) -> String {
2170 self.bom_id.clone()
2171 }
2172 fn sku(&self) -> String {
2173 self.sku.clone()
2174 }
2175 fn name(&self) -> String {
2176 self.name.clone()
2177 }
2178 fn quantity(&self) -> f64 {
2179 self.quantity
2180 }
2181 fn unit(&self) -> String {
2182 self.unit.clone()
2183 }
2184}
2185
2186impl From<stateset_core::BomComponent> for BomComponent {
2187 fn from(c: stateset_core::BomComponent) -> Self {
2188 Self {
2189 id: c.id.to_string(),
2190 bom_id: c.bom_id.to_string(),
2191 sku: c.sku,
2192 name: c.name,
2193 quantity: to_f64_or_nan(c.quantity),
2194 unit: c.unit,
2195 }
2196 }
2197}
2198
2199#[derive(Clone)]
2200#[magnus::wrap(class = "StateSet::BomApi", free_immediately, size)]
2201pub struct BomApi {
2202 commerce: Arc<Mutex<RustCommerce>>,
2203}
2204
2205impl BomApi {
2206 fn create(&self, name: String, product_id: Option<String>) -> Result<BillOfMaterials, Error> {
2207 let commerce = lock_commerce!(self.commerce);
2208 let prod_uuid = product_id.map(|s| s.parse().ok()).flatten();
2209 let bom = commerce
2210 .bom()
2211 .create(stateset_core::CreateBom { name, product_id: prod_uuid, ..Default::default() })
2212 .map_err(|e| {
2213 Error::new(exception::runtime_error(), format!("Failed to create BOM: {}", e))
2214 })?;
2215 Ok(bom.into())
2216 }
2217
2218 fn get(&self, id: String) -> Result<Option<BillOfMaterials>, Error> {
2219 let commerce = lock_commerce!(self.commerce);
2220 let uuid = parse_uuid!(id, "bom");
2221 let bom = commerce.bom().get(uuid).map_err(|e| {
2222 Error::new(exception::runtime_error(), format!("Failed to get BOM: {}", e))
2223 })?;
2224 Ok(bom.map(|b| b.into()))
2225 }
2226
2227 fn list(&self) -> Result<Vec<BillOfMaterials>, Error> {
2228 let commerce = lock_commerce!(self.commerce);
2229 let boms = commerce.bom().list(Default::default()).map_err(|e| {
2230 Error::new(exception::runtime_error(), format!("Failed to list BOMs: {}", e))
2231 })?;
2232 Ok(boms.into_iter().map(|b| b.into()).collect())
2233 }
2234
2235 fn add_component(
2236 &self,
2237 bom_id: String,
2238 sku: String,
2239 name: String,
2240 quantity: f64,
2241 unit: String,
2242 ) -> Result<BomComponent, Error> {
2243 let commerce = lock_commerce!(self.commerce);
2244 let uuid = parse_uuid!(bom_id, "bom");
2245 let component = commerce
2246 .bom()
2247 .add_component(
2248 uuid,
2249 stateset_core::CreateBomComponent {
2250 sku,
2251 name,
2252 quantity: Decimal::from_f64_retain(quantity).unwrap_or_default(),
2253 unit,
2254 ..Default::default()
2255 },
2256 )
2257 .map_err(|e| {
2258 Error::new(exception::runtime_error(), format!("Failed to add component: {}", e))
2259 })?;
2260 Ok(component.into())
2261 }
2262
2263 fn get_components(&self, bom_id: String) -> Result<Vec<BomComponent>, Error> {
2264 let commerce = lock_commerce!(self.commerce);
2265 let uuid = parse_uuid!(bom_id, "bom");
2266 let components = commerce.bom().get_components(uuid).map_err(|e| {
2267 Error::new(exception::runtime_error(), format!("Failed to get components: {}", e))
2268 })?;
2269 Ok(components.into_iter().map(|c| c.into()).collect())
2270 }
2271
2272 fn remove_component(&self, component_id: String) -> Result<bool, Error> {
2273 let commerce = lock_commerce!(self.commerce);
2274 let uuid = parse_uuid!(component_id, "component");
2275 commerce.bom().remove_component(uuid).map_err(|e| {
2276 Error::new(exception::runtime_error(), format!("Failed to remove component: {}", e))
2277 })?;
2278 Ok(true)
2279 }
2280
2281 fn activate(&self, id: String) -> Result<BillOfMaterials, Error> {
2282 let commerce = lock_commerce!(self.commerce);
2283 let uuid = parse_uuid!(id, "bom");
2284 let bom = commerce.bom().activate(uuid).map_err(|e| {
2285 Error::new(exception::runtime_error(), format!("Failed to activate BOM: {}", e))
2286 })?;
2287 Ok(bom.into())
2288 }
2289
2290 fn delete(&self, id: String) -> Result<bool, Error> {
2291 let commerce = lock_commerce!(self.commerce);
2292 let uuid = parse_uuid!(id, "bom");
2293 commerce.bom().delete(uuid).map_err(|e| {
2294 Error::new(exception::runtime_error(), format!("Failed to delete BOM: {}", e))
2295 })?;
2296 Ok(true)
2297 }
2298
2299 fn count(&self) -> Result<i64, Error> {
2300 let commerce = lock_commerce!(self.commerce);
2301 let count = commerce.bom().count(Default::default()).map_err(|e| {
2302 Error::new(exception::runtime_error(), format!("Failed to count: {}", e))
2303 })?;
2304 Ok(count as i64)
2305 }
2306}
2307
2308#[derive(Clone)]
2313#[magnus::wrap(class = "StateSet::WorkOrder", free_immediately, size)]
2314pub struct WorkOrder {
2315 id: String,
2316 work_order_number: String,
2317 bom_id: Option<String>,
2318 product_id: Option<String>,
2319 status: String,
2320 quantity_ordered: f64,
2321 quantity_completed: f64,
2322 priority: String,
2323 started_at: Option<String>,
2324 completed_at: Option<String>,
2325 created_at: String,
2326 updated_at: String,
2327}
2328
2329impl WorkOrder {
2330 fn id(&self) -> String {
2331 self.id.clone()
2332 }
2333 fn work_order_number(&self) -> String {
2334 self.work_order_number.clone()
2335 }
2336 fn bom_id(&self) -> Option<String> {
2337 self.bom_id.clone()
2338 }
2339 fn product_id(&self) -> Option<String> {
2340 self.product_id.clone()
2341 }
2342 fn status(&self) -> String {
2343 self.status.clone()
2344 }
2345 fn quantity_ordered(&self) -> f64 {
2346 self.quantity_ordered
2347 }
2348 fn quantity_completed(&self) -> f64 {
2349 self.quantity_completed
2350 }
2351 fn priority(&self) -> String {
2352 self.priority.clone()
2353 }
2354 fn started_at(&self) -> Option<String> {
2355 self.started_at.clone()
2356 }
2357 fn completed_at(&self) -> Option<String> {
2358 self.completed_at.clone()
2359 }
2360 fn created_at(&self) -> String {
2361 self.created_at.clone()
2362 }
2363 fn updated_at(&self) -> String {
2364 self.updated_at.clone()
2365 }
2366 fn inspect(&self) -> String {
2367 format!(
2368 "#<StateSet::WorkOrder number=\"{}\" status=\"{}\">",
2369 self.work_order_number, self.status
2370 )
2371 }
2372}
2373
2374impl From<stateset_core::WorkOrder> for WorkOrder {
2375 fn from(w: stateset_core::WorkOrder) -> Self {
2376 Self {
2377 id: w.id.to_string(),
2378 work_order_number: w.work_order_number,
2379 bom_id: w.bom_id.map(|b| b.to_string()),
2380 product_id: w.product_id.map(|p| p.to_string()),
2381 status: format!("{}", w.status),
2382 quantity_ordered: to_f64_or_nan(w.quantity_ordered),
2383 quantity_completed: to_f64_or_nan(w.quantity_completed),
2384 priority: format!("{}", w.priority),
2385 started_at: w.started_at.map(|d| d.to_rfc3339()),
2386 completed_at: w.completed_at.map(|d| d.to_rfc3339()),
2387 created_at: w.created_at.to_rfc3339(),
2388 updated_at: w.updated_at.to_rfc3339(),
2389 }
2390 }
2391}
2392
2393#[derive(Clone)]
2394#[magnus::wrap(class = "StateSet::WorkOrders", free_immediately, size)]
2395pub struct WorkOrders {
2396 commerce: Arc<Mutex<RustCommerce>>,
2397}
2398
2399impl WorkOrders {
2400 fn create(
2401 &self,
2402 bom_id: Option<String>,
2403 product_id: Option<String>,
2404 quantity: f64,
2405 ) -> Result<WorkOrder, Error> {
2406 let commerce = lock_commerce!(self.commerce);
2407 let bom_uuid = bom_id.map(|s| s.parse().ok()).flatten();
2408 let prod_uuid = product_id.map(|s| s.parse().ok()).flatten();
2409 let wo = commerce
2410 .work_orders()
2411 .create(stateset_core::CreateWorkOrder {
2412 bom_id: bom_uuid,
2413 product_id: prod_uuid,
2414 quantity_ordered: Decimal::from_f64_retain(quantity).unwrap_or_default(),
2415 ..Default::default()
2416 })
2417 .map_err(|e| {
2418 Error::new(
2419 exception::runtime_error(),
2420 format!("Failed to create work order: {}", e),
2421 )
2422 })?;
2423 Ok(wo.into())
2424 }
2425
2426 fn get(&self, id: String) -> Result<Option<WorkOrder>, Error> {
2427 let commerce = lock_commerce!(self.commerce);
2428 let uuid = parse_uuid!(id, "work_order");
2429 let wo = commerce.work_orders().get(uuid).map_err(|e| {
2430 Error::new(exception::runtime_error(), format!("Failed to get work order: {}", e))
2431 })?;
2432 Ok(wo.map(|w| w.into()))
2433 }
2434
2435 fn list(&self) -> Result<Vec<WorkOrder>, Error> {
2436 let commerce = lock_commerce!(self.commerce);
2437 let wos = commerce.work_orders().list(Default::default()).map_err(|e| {
2438 Error::new(exception::runtime_error(), format!("Failed to list work orders: {}", e))
2439 })?;
2440 Ok(wos.into_iter().map(|w| w.into()).collect())
2441 }
2442
2443 fn start(&self, id: String) -> Result<WorkOrder, Error> {
2444 let commerce = lock_commerce!(self.commerce);
2445 let uuid = parse_uuid!(id, "work_order");
2446 let wo = commerce.work_orders().start(uuid).map_err(|e| {
2447 Error::new(exception::runtime_error(), format!("Failed to start: {}", e))
2448 })?;
2449 Ok(wo.into())
2450 }
2451
2452 fn complete(&self, id: String, quantity_completed: f64) -> Result<WorkOrder, Error> {
2453 let commerce = lock_commerce!(self.commerce);
2454 let uuid = parse_uuid!(id, "work_order");
2455 let wo = commerce
2456 .work_orders()
2457 .complete(uuid, Decimal::from_f64_retain(quantity_completed).unwrap_or_default())
2458 .map_err(|e| {
2459 Error::new(exception::runtime_error(), format!("Failed to complete: {}", e))
2460 })?;
2461 Ok(wo.into())
2462 }
2463
2464 fn hold(&self, id: String) -> Result<WorkOrder, Error> {
2465 let commerce = lock_commerce!(self.commerce);
2466 let uuid = parse_uuid!(id, "work_order");
2467 let wo = commerce.work_orders().hold(uuid).map_err(|e| {
2468 Error::new(exception::runtime_error(), format!("Failed to hold: {}", e))
2469 })?;
2470 Ok(wo.into())
2471 }
2472
2473 fn resume(&self, id: String) -> Result<WorkOrder, Error> {
2474 let commerce = lock_commerce!(self.commerce);
2475 let uuid = parse_uuid!(id, "work_order");
2476 let wo = commerce.work_orders().resume(uuid).map_err(|e| {
2477 Error::new(exception::runtime_error(), format!("Failed to resume: {}", e))
2478 })?;
2479 Ok(wo.into())
2480 }
2481
2482 fn cancel(&self, id: String) -> Result<WorkOrder, Error> {
2483 let commerce = lock_commerce!(self.commerce);
2484 let uuid = parse_uuid!(id, "work_order");
2485 let wo = commerce.work_orders().cancel(uuid).map_err(|e| {
2486 Error::new(exception::runtime_error(), format!("Failed to cancel: {}", e))
2487 })?;
2488 Ok(wo.into())
2489 }
2490
2491 fn count(&self) -> Result<i64, Error> {
2492 let commerce = lock_commerce!(self.commerce);
2493 let count = commerce.work_orders().count(Default::default()).map_err(|e| {
2494 Error::new(exception::runtime_error(), format!("Failed to count: {}", e))
2495 })?;
2496 Ok(count as i64)
2497 }
2498}
2499
2500#[derive(Clone)]
2505#[magnus::wrap(class = "StateSet::CartItem", free_immediately, size)]
2506pub struct CartItem {
2507 id: String,
2508 sku: String,
2509 name: String,
2510 quantity: i32,
2511 unit_price: f64,
2512 total: f64,
2513}
2514
2515impl CartItem {
2516 fn id(&self) -> String {
2517 self.id.clone()
2518 }
2519 fn sku(&self) -> String {
2520 self.sku.clone()
2521 }
2522 fn name(&self) -> String {
2523 self.name.clone()
2524 }
2525 fn quantity(&self) -> i32 {
2526 self.quantity
2527 }
2528 fn unit_price(&self) -> f64 {
2529 self.unit_price
2530 }
2531 fn total(&self) -> f64 {
2532 self.total
2533 }
2534}
2535
2536#[derive(Clone)]
2537#[magnus::wrap(class = "StateSet::Cart", free_immediately, size)]
2538pub struct Cart {
2539 id: String,
2540 customer_id: Option<String>,
2541 status: String,
2542 items: Vec<CartItem>,
2543 subtotal: f64,
2544 total: f64,
2545 currency: String,
2546 created_at: String,
2547 updated_at: String,
2548}
2549
2550impl Cart {
2551 fn id(&self) -> String {
2552 self.id.clone()
2553 }
2554 fn customer_id(&self) -> Option<String> {
2555 self.customer_id.clone()
2556 }
2557 fn status(&self) -> String {
2558 self.status.clone()
2559 }
2560 fn items(&self) -> Vec<CartItem> {
2561 self.items.clone()
2562 }
2563 fn subtotal(&self) -> f64 {
2564 self.subtotal
2565 }
2566 fn total(&self) -> f64 {
2567 self.total
2568 }
2569 fn currency(&self) -> String {
2570 self.currency.clone()
2571 }
2572 fn created_at(&self) -> String {
2573 self.created_at.clone()
2574 }
2575 fn updated_at(&self) -> String {
2576 self.updated_at.clone()
2577 }
2578 fn inspect(&self) -> String {
2579 format!(
2580 "#<StateSet::Cart id=\"{}\" items={} total={} {}>",
2581 self.id,
2582 self.items.len(),
2583 self.total,
2584 self.currency
2585 )
2586 }
2587}
2588
2589impl From<stateset_core::Cart> for Cart {
2590 fn from(c: stateset_core::Cart) -> Self {
2591 Self {
2592 id: c.id.to_string(),
2593 customer_id: c.customer_id.map(|id| id.to_string()),
2594 status: format!("{}", c.status),
2595 items: c
2596 .items
2597 .into_iter()
2598 .map(|i| CartItem {
2599 id: i.id.to_string(),
2600 sku: i.sku,
2601 name: i.name,
2602 quantity: i.quantity,
2603 unit_price: to_f64_or_nan(i.unit_price),
2604 total: to_f64_or_nan(i.total),
2605 })
2606 .collect(),
2607 subtotal: to_f64_or_nan(c.subtotal),
2608 total: to_f64_or_nan(c.total),
2609 currency: c.currency,
2610 created_at: c.created_at.to_rfc3339(),
2611 updated_at: c.updated_at.to_rfc3339(),
2612 }
2613 }
2614}
2615
2616#[derive(Clone)]
2617#[magnus::wrap(class = "StateSet::Carts", free_immediately, size)]
2618pub struct Carts {
2619 commerce: Arc<Mutex<RustCommerce>>,
2620}
2621
2622impl Carts {
2623 fn create(&self, customer_id: Option<String>, currency: Option<String>) -> Result<Cart, Error> {
2624 let commerce = lock_commerce!(self.commerce);
2625 let cust_uuid = customer_id.map(|s| s.parse().ok()).flatten();
2626
2627 let cart = commerce
2628 .carts()
2629 .create(stateset_core::CreateCart {
2630 customer_id: cust_uuid,
2631 currency,
2632 ..Default::default()
2633 })
2634 .map_err(|e| {
2635 Error::new(exception::runtime_error(), format!("Failed to create cart: {}", e))
2636 })?;
2637
2638 Ok(cart.into())
2639 }
2640
2641 fn get(&self, id: String) -> Result<Option<Cart>, Error> {
2642 let commerce = lock_commerce!(self.commerce);
2643 let uuid = parse_uuid!(id, "cart");
2644
2645 let cart = commerce.carts().get(uuid).map_err(|e| {
2646 Error::new(exception::runtime_error(), format!("Failed to get cart: {}", e))
2647 })?;
2648
2649 Ok(cart.map(|c| c.into()))
2650 }
2651
2652 fn list(&self) -> Result<Vec<Cart>, Error> {
2653 let commerce = lock_commerce!(self.commerce);
2654
2655 let carts = commerce.carts().list(Default::default()).map_err(|e| {
2656 Error::new(exception::runtime_error(), format!("Failed to list carts: {}", e))
2657 })?;
2658
2659 Ok(carts.into_iter().map(|c| c.into()).collect())
2660 }
2661
2662 fn add_item(
2663 &self,
2664 cart_id: String,
2665 sku: String,
2666 name: String,
2667 quantity: i32,
2668 unit_price: f64,
2669 ) -> Result<Cart, Error> {
2670 let commerce = lock_commerce!(self.commerce);
2671 let uuid = parse_uuid!(cart_id, "cart");
2672 let price = Decimal::from_f64_retain(unit_price).unwrap_or_default();
2673
2674 let cart = commerce
2675 .carts()
2676 .add_item(
2677 uuid,
2678 stateset_core::AddCartItem {
2679 sku,
2680 name,
2681 quantity,
2682 unit_price: price,
2683 ..Default::default()
2684 },
2685 )
2686 .map_err(|e| {
2687 Error::new(exception::runtime_error(), format!("Failed to add item: {}", e))
2688 })?;
2689
2690 Ok(cart.into())
2691 }
2692
2693 fn checkout(&self, cart_id: String) -> Result<Order, Error> {
2694 let commerce = lock_commerce!(self.commerce);
2695 let uuid = parse_uuid!(cart_id, "cart");
2696
2697 let order = commerce.carts().checkout(uuid).map_err(|e| {
2698 Error::new(exception::runtime_error(), format!("Failed to checkout: {}", e))
2699 })?;
2700
2701 Ok(order.into())
2702 }
2703}
2704
2705#[derive(Clone)]
2710#[magnus::wrap(class = "StateSet::SalesSummary", free_immediately, size)]
2711pub struct SalesSummary {
2712 total_revenue: f64,
2713 total_orders: i64,
2714 average_order_value: f64,
2715 total_items_sold: i64,
2716}
2717
2718impl SalesSummary {
2719 fn total_revenue(&self) -> f64 {
2720 self.total_revenue
2721 }
2722 fn total_orders(&self) -> i64 {
2723 self.total_orders
2724 }
2725 fn average_order_value(&self) -> f64 {
2726 self.average_order_value
2727 }
2728 fn total_items_sold(&self) -> i64 {
2729 self.total_items_sold
2730 }
2731}
2732
2733#[derive(Clone)]
2734#[magnus::wrap(class = "StateSet::Analytics", free_immediately, size)]
2735pub struct Analytics {
2736 commerce: Arc<Mutex<RustCommerce>>,
2737}
2738
2739impl Analytics {
2740 fn sales_summary(&self, days: Option<i64>) -> Result<SalesSummary, Error> {
2741 let commerce = lock_commerce!(self.commerce);
2742
2743 let summary = commerce.analytics().sales_summary(days.unwrap_or(30)).map_err(|e| {
2744 Error::new(exception::runtime_error(), format!("Failed to get sales summary: {}", e))
2745 })?;
2746
2747 Ok(SalesSummary {
2748 total_revenue: to_f64_or_nan(summary.total_revenue),
2749 total_orders: summary.total_orders,
2750 average_order_value: to_f64_or_nan(summary.average_order_value),
2751 total_items_sold: summary.total_items_sold,
2752 })
2753 }
2754}
2755
2756#[derive(Clone)]
2761#[magnus::wrap(class = "StateSet::ExchangeRate", free_immediately, size)]
2762pub struct ExchangeRate {
2763 id: String,
2764 from_currency: String,
2765 to_currency: String,
2766 rate: f64,
2767 effective_date: String,
2768 created_at: String,
2769}
2770
2771impl ExchangeRate {
2772 fn id(&self) -> String {
2773 self.id.clone()
2774 }
2775 fn from_currency(&self) -> String {
2776 self.from_currency.clone()
2777 }
2778 fn to_currency(&self) -> String {
2779 self.to_currency.clone()
2780 }
2781 fn rate(&self) -> f64 {
2782 self.rate
2783 }
2784 fn effective_date(&self) -> String {
2785 self.effective_date.clone()
2786 }
2787 fn created_at(&self) -> String {
2788 self.created_at.clone()
2789 }
2790}
2791
2792impl From<stateset_core::ExchangeRate> for ExchangeRate {
2793 fn from(r: stateset_core::ExchangeRate) -> Self {
2794 Self {
2795 id: r.id.to_string(),
2796 from_currency: format!("{}", r.from_currency),
2797 to_currency: format!("{}", r.to_currency),
2798 rate: to_f64_or_nan(r.rate),
2799 effective_date: r.effective_date.to_string(),
2800 created_at: r.created_at.to_rfc3339(),
2801 }
2802 }
2803}
2804
2805#[derive(Clone)]
2806#[magnus::wrap(class = "StateSet::CurrencyOps", free_immediately, size)]
2807pub struct CurrencyOps {
2808 commerce: Arc<Mutex<RustCommerce>>,
2809}
2810
2811impl CurrencyOps {
2812 fn get_rate(&self, from: String, to: String) -> Result<Option<ExchangeRate>, Error> {
2813 let commerce = lock_commerce!(self.commerce);
2814 let from_currency = from.parse().unwrap_or_default();
2815 let to_currency = to.parse().unwrap_or_default();
2816 let rate = commerce.currency().get_rate(from_currency, to_currency).map_err(|e| {
2817 Error::new(exception::runtime_error(), format!("Failed to get rate: {}", e))
2818 })?;
2819 Ok(rate.map(|r| r.into()))
2820 }
2821
2822 fn list_rates(&self) -> Result<Vec<ExchangeRate>, Error> {
2823 let commerce = lock_commerce!(self.commerce);
2824 let rates = commerce.currency().list_rates(Default::default()).map_err(|e| {
2825 Error::new(exception::runtime_error(), format!("Failed to list rates: {}", e))
2826 })?;
2827 Ok(rates.into_iter().map(|r| r.into()).collect())
2828 }
2829
2830 fn set_rate(&self, from: String, to: String, rate: f64) -> Result<ExchangeRate, Error> {
2831 let commerce = lock_commerce!(self.commerce);
2832 let exchange_rate = commerce
2833 .currency()
2834 .set_rate(stateset_core::SetExchangeRate {
2835 from_currency: from.parse().unwrap_or_default(),
2836 to_currency: to.parse().unwrap_or_default(),
2837 rate: Decimal::from_f64_retain(rate).unwrap_or_default(),
2838 ..Default::default()
2839 })
2840 .map_err(|e| {
2841 Error::new(exception::runtime_error(), format!("Failed to set rate: {}", e))
2842 })?;
2843 Ok(exchange_rate.into())
2844 }
2845
2846 fn convert(&self, amount: f64, from: String, to: String) -> Result<f64, Error> {
2847 let commerce = lock_commerce!(self.commerce);
2848 let result = commerce
2849 .currency()
2850 .convert_amount(
2851 Decimal::from_f64_retain(amount).unwrap_or_default(),
2852 from.parse().unwrap_or_default(),
2853 to.parse().unwrap_or_default(),
2854 )
2855 .map_err(|e| {
2856 Error::new(exception::runtime_error(), format!("Failed to convert: {}", e))
2857 })?;
2858 Ok(to_f64_or_nan(result))
2859 }
2860
2861 fn base_currency(&self) -> Result<String, Error> {
2862 let commerce = lock_commerce!(self.commerce);
2863 let currency = commerce.currency().base_currency().map_err(|e| {
2864 Error::new(exception::runtime_error(), format!("Failed to get base currency: {}", e))
2865 })?;
2866 Ok(format!("{}", currency))
2867 }
2868
2869 fn enabled_currencies(&self) -> Result<Vec<String>, Error> {
2870 let commerce = lock_commerce!(self.commerce);
2871 let currencies = commerce.currency().enabled_currencies().map_err(|e| {
2872 Error::new(exception::runtime_error(), format!("Failed to get currencies: {}", e))
2873 })?;
2874 Ok(currencies.into_iter().map(|c| format!("{}", c)).collect())
2875 }
2876
2877 fn format(&self, amount: f64, currency: String) -> String {
2878 let formatted = (|| -> Result<String, Error> {
2879 let commerce = lock_commerce!(self.commerce);
2880 Ok(commerce.currency().format(
2881 Decimal::from_f64_retain(amount).unwrap_or_default(),
2882 currency.parse().unwrap_or_default(),
2883 ))
2884 })();
2885 formatted.unwrap_or_default()
2886 }
2887}
2888
2889#[derive(Clone)]
2894#[magnus::wrap(class = "StateSet::SubscriptionPlan", free_immediately, size)]
2895pub struct SubscriptionPlan {
2896 id: String,
2897 code: String,
2898 name: String,
2899 description: Option<String>,
2900 price: f64,
2901 currency: String,
2902 billing_interval: String,
2903 trial_days: Option<i32>,
2904 status: String,
2905 created_at: String,
2906 updated_at: String,
2907}
2908
2909impl SubscriptionPlan {
2910 fn id(&self) -> String {
2911 self.id.clone()
2912 }
2913 fn code(&self) -> String {
2914 self.code.clone()
2915 }
2916 fn name(&self) -> String {
2917 self.name.clone()
2918 }
2919 fn description(&self) -> Option<String> {
2920 self.description.clone()
2921 }
2922 fn price(&self) -> f64 {
2923 self.price
2924 }
2925 fn currency(&self) -> String {
2926 self.currency.clone()
2927 }
2928 fn billing_interval(&self) -> String {
2929 self.billing_interval.clone()
2930 }
2931 fn trial_days(&self) -> Option<i32> {
2932 self.trial_days
2933 }
2934 fn status(&self) -> String {
2935 self.status.clone()
2936 }
2937 fn created_at(&self) -> String {
2938 self.created_at.clone()
2939 }
2940 fn updated_at(&self) -> String {
2941 self.updated_at.clone()
2942 }
2943}
2944
2945impl From<stateset_core::SubscriptionPlan> for SubscriptionPlan {
2946 fn from(p: stateset_core::SubscriptionPlan) -> Self {
2947 Self {
2948 id: p.id.to_string(),
2949 code: p.code,
2950 name: p.name,
2951 description: p.description,
2952 price: to_f64_or_nan(p.price),
2953 currency: p.currency,
2954 billing_interval: format!("{}", p.billing_interval),
2955 trial_days: p.trial_days,
2956 status: format!("{}", p.status),
2957 created_at: p.created_at.to_rfc3339(),
2958 updated_at: p.updated_at.to_rfc3339(),
2959 }
2960 }
2961}
2962
2963#[derive(Clone)]
2964#[magnus::wrap(class = "StateSet::Subscription", free_immediately, size)]
2965pub struct Subscription {
2966 id: String,
2967 subscription_number: String,
2968 customer_id: String,
2969 plan_id: String,
2970 status: String,
2971 current_period_start: String,
2972 current_period_end: String,
2973 trial_end: Option<String>,
2974 canceled_at: Option<String>,
2975 created_at: String,
2976 updated_at: String,
2977}
2978
2979impl Subscription {
2980 fn id(&self) -> String {
2981 self.id.clone()
2982 }
2983 fn subscription_number(&self) -> String {
2984 self.subscription_number.clone()
2985 }
2986 fn customer_id(&self) -> String {
2987 self.customer_id.clone()
2988 }
2989 fn plan_id(&self) -> String {
2990 self.plan_id.clone()
2991 }
2992 fn status(&self) -> String {
2993 self.status.clone()
2994 }
2995 fn current_period_start(&self) -> String {
2996 self.current_period_start.clone()
2997 }
2998 fn current_period_end(&self) -> String {
2999 self.current_period_end.clone()
3000 }
3001 fn trial_end(&self) -> Option<String> {
3002 self.trial_end.clone()
3003 }
3004 fn canceled_at(&self) -> Option<String> {
3005 self.canceled_at.clone()
3006 }
3007 fn created_at(&self) -> String {
3008 self.created_at.clone()
3009 }
3010 fn updated_at(&self) -> String {
3011 self.updated_at.clone()
3012 }
3013 fn inspect(&self) -> String {
3014 format!(
3015 "#<StateSet::Subscription number=\"{}\" status=\"{}\">",
3016 self.subscription_number, self.status
3017 )
3018 }
3019}
3020
3021impl From<stateset_core::Subscription> for Subscription {
3022 fn from(s: stateset_core::Subscription) -> Self {
3023 Self {
3024 id: s.id.to_string(),
3025 subscription_number: s.subscription_number,
3026 customer_id: s.customer_id.to_string(),
3027 plan_id: s.plan_id.to_string(),
3028 status: format!("{}", s.status),
3029 current_period_start: s.current_period_start.to_rfc3339(),
3030 current_period_end: s.current_period_end.to_rfc3339(),
3031 trial_end: s.trial_end.map(|d| d.to_rfc3339()),
3032 canceled_at: s.canceled_at.map(|d| d.to_rfc3339()),
3033 created_at: s.created_at.to_rfc3339(),
3034 updated_at: s.updated_at.to_rfc3339(),
3035 }
3036 }
3037}
3038
3039#[derive(Clone)]
3040#[magnus::wrap(class = "StateSet::Subscriptions", free_immediately, size)]
3041pub struct Subscriptions {
3042 commerce: Arc<Mutex<RustCommerce>>,
3043}
3044
3045impl Subscriptions {
3046 fn create_plan(
3047 &self,
3048 code: String,
3049 name: String,
3050 price: f64,
3051 currency: String,
3052 billing_interval: String,
3053 ) -> Result<SubscriptionPlan, Error> {
3054 let commerce = lock_commerce!(self.commerce);
3055 let plan = commerce
3056 .subscriptions()
3057 .create_plan(stateset_core::CreateSubscriptionPlan {
3058 code,
3059 name,
3060 price: Decimal::from_f64_retain(price).unwrap_or_default(),
3061 currency,
3062 billing_interval: billing_interval.parse().unwrap_or_default(),
3063 ..Default::default()
3064 })
3065 .map_err(|e| {
3066 Error::new(exception::runtime_error(), format!("Failed to create plan: {}", e))
3067 })?;
3068 Ok(plan.into())
3069 }
3070
3071 fn get_plan(&self, id: String) -> Result<Option<SubscriptionPlan>, Error> {
3072 let commerce = lock_commerce!(self.commerce);
3073 let uuid = parse_uuid!(id, "plan");
3074 let plan = commerce.subscriptions().get_plan(uuid).map_err(|e| {
3075 Error::new(exception::runtime_error(), format!("Failed to get plan: {}", e))
3076 })?;
3077 Ok(plan.map(|p| p.into()))
3078 }
3079
3080 fn list_plans(&self) -> Result<Vec<SubscriptionPlan>, Error> {
3081 let commerce = lock_commerce!(self.commerce);
3082 let plans = commerce.subscriptions().list_plans(Default::default()).map_err(|e| {
3083 Error::new(exception::runtime_error(), format!("Failed to list plans: {}", e))
3084 })?;
3085 Ok(plans.into_iter().map(|p| p.into()).collect())
3086 }
3087
3088 fn subscribe(&self, customer_id: String, plan_id: String) -> Result<Subscription, Error> {
3089 let commerce = lock_commerce!(self.commerce);
3090 let cust_uuid = parse_uuid!(customer_id, "customer");
3091 let plan_uuid = parse_uuid!(plan_id, "plan");
3092 let sub = commerce
3093 .subscriptions()
3094 .subscribe(stateset_core::CreateSubscription {
3095 customer_id: cust_uuid,
3096 plan_id: plan_uuid,
3097 ..Default::default()
3098 })
3099 .map_err(|e| {
3100 Error::new(exception::runtime_error(), format!("Failed to subscribe: {}", e))
3101 })?;
3102 Ok(sub.into())
3103 }
3104
3105 fn get(&self, id: String) -> Result<Option<Subscription>, Error> {
3106 let commerce = lock_commerce!(self.commerce);
3107 let uuid = parse_uuid!(id, "subscription");
3108 let sub = commerce.subscriptions().get(uuid).map_err(|e| {
3109 Error::new(exception::runtime_error(), format!("Failed to get subscription: {}", e))
3110 })?;
3111 Ok(sub.map(|s| s.into()))
3112 }
3113
3114 fn list(&self) -> Result<Vec<Subscription>, Error> {
3115 let commerce = lock_commerce!(self.commerce);
3116 let subs = commerce.subscriptions().list(Default::default()).map_err(|e| {
3117 Error::new(exception::runtime_error(), format!("Failed to list subscriptions: {}", e))
3118 })?;
3119 Ok(subs.into_iter().map(|s| s.into()).collect())
3120 }
3121
3122 fn pause(&self, id: String) -> Result<Subscription, Error> {
3123 let commerce = lock_commerce!(self.commerce);
3124 let uuid = parse_uuid!(id, "subscription");
3125 let sub = commerce
3126 .subscriptions()
3127 .pause(uuid, stateset_core::PauseSubscription::default())
3128 .map_err(|e| {
3129 Error::new(exception::runtime_error(), format!("Failed to pause: {}", e))
3130 })?;
3131 Ok(sub.into())
3132 }
3133
3134 fn resume(&self, id: String) -> Result<Subscription, Error> {
3135 let commerce = lock_commerce!(self.commerce);
3136 let uuid = parse_uuid!(id, "subscription");
3137 let sub = commerce.subscriptions().resume(uuid).map_err(|e| {
3138 Error::new(exception::runtime_error(), format!("Failed to resume: {}", e))
3139 })?;
3140 Ok(sub.into())
3141 }
3142
3143 fn cancel(&self, id: String, immediately: bool) -> Result<Subscription, Error> {
3144 let commerce = lock_commerce!(self.commerce);
3145 let uuid = parse_uuid!(id, "subscription");
3146 let sub = commerce
3147 .subscriptions()
3148 .cancel(
3149 uuid,
3150 stateset_core::CancelSubscription {
3151 cancel_immediately: immediately,
3152 ..Default::default()
3153 },
3154 )
3155 .map_err(|e| {
3156 Error::new(exception::runtime_error(), format!("Failed to cancel: {}", e))
3157 })?;
3158 Ok(sub.into())
3159 }
3160
3161 fn for_customer(&self, customer_id: String) -> Result<Vec<Subscription>, Error> {
3162 let commerce = lock_commerce!(self.commerce);
3163 let uuid = parse_uuid!(customer_id, "customer");
3164 let subs = commerce.subscriptions().get_customer_subscriptions(uuid).map_err(|e| {
3165 Error::new(exception::runtime_error(), format!("Failed to get subscriptions: {}", e))
3166 })?;
3167 Ok(subs.into_iter().map(|s| s.into()).collect())
3168 }
3169
3170 fn is_active(&self, id: String) -> Result<bool, Error> {
3171 let commerce = lock_commerce!(self.commerce);
3172 let uuid = parse_uuid!(id, "subscription");
3173 let active = commerce.subscriptions().is_active(uuid).map_err(|e| {
3174 Error::new(exception::runtime_error(), format!("Failed to check: {}", e))
3175 })?;
3176 Ok(active)
3177 }
3178}
3179
3180#[derive(Clone)]
3185#[magnus::wrap(class = "StateSet::Promotion", free_immediately, size)]
3186pub struct Promotion {
3187 id: String,
3188 code: String,
3189 name: String,
3190 description: Option<String>,
3191 discount_type: String,
3192 discount_value: f64,
3193 min_purchase: Option<f64>,
3194 max_uses: Option<i32>,
3195 times_used: i32,
3196 status: String,
3197 starts_at: Option<String>,
3198 ends_at: Option<String>,
3199 created_at: String,
3200 updated_at: String,
3201}
3202
3203impl Promotion {
3204 fn id(&self) -> String {
3205 self.id.clone()
3206 }
3207 fn code(&self) -> String {
3208 self.code.clone()
3209 }
3210 fn name(&self) -> String {
3211 self.name.clone()
3212 }
3213 fn description(&self) -> Option<String> {
3214 self.description.clone()
3215 }
3216 fn discount_type(&self) -> String {
3217 self.discount_type.clone()
3218 }
3219 fn discount_value(&self) -> f64 {
3220 self.discount_value
3221 }
3222 fn min_purchase(&self) -> Option<f64> {
3223 self.min_purchase
3224 }
3225 fn max_uses(&self) -> Option<i32> {
3226 self.max_uses
3227 }
3228 fn times_used(&self) -> i32 {
3229 self.times_used
3230 }
3231 fn status(&self) -> String {
3232 self.status.clone()
3233 }
3234 fn starts_at(&self) -> Option<String> {
3235 self.starts_at.clone()
3236 }
3237 fn ends_at(&self) -> Option<String> {
3238 self.ends_at.clone()
3239 }
3240 fn created_at(&self) -> String {
3241 self.created_at.clone()
3242 }
3243 fn updated_at(&self) -> String {
3244 self.updated_at.clone()
3245 }
3246 fn inspect(&self) -> String {
3247 format!("#<StateSet::Promotion code=\"{}\" status=\"{}\">", self.code, self.status)
3248 }
3249}
3250
3251impl From<stateset_core::Promotion> for Promotion {
3252 fn from(p: stateset_core::Promotion) -> Self {
3253 Self {
3254 id: p.id.to_string(),
3255 code: p.code,
3256 name: p.name,
3257 description: p.description,
3258 discount_type: format!("{}", p.discount_type),
3259 discount_value: to_f64_or_nan(p.discount_value),
3260 min_purchase: p.min_purchase.and_then(|m| m.to_f64()),
3261 max_uses: p.max_uses,
3262 times_used: p.times_used,
3263 status: format!("{}", p.status),
3264 starts_at: p.starts_at.map(|d| d.to_rfc3339()),
3265 ends_at: p.ends_at.map(|d| d.to_rfc3339()),
3266 created_at: p.created_at.to_rfc3339(),
3267 updated_at: p.updated_at.to_rfc3339(),
3268 }
3269 }
3270}
3271
3272#[derive(Clone)]
3273#[magnus::wrap(class = "StateSet::Promotions", free_immediately, size)]
3274pub struct Promotions {
3275 commerce: Arc<Mutex<RustCommerce>>,
3276}
3277
3278impl Promotions {
3279 fn create(
3280 &self,
3281 code: String,
3282 name: String,
3283 discount_type: String,
3284 discount_value: f64,
3285 ) -> Result<Promotion, Error> {
3286 let commerce = lock_commerce!(self.commerce);
3287 let promo = commerce
3288 .promotions()
3289 .create(stateset_core::CreatePromotion {
3290 code,
3291 name,
3292 discount_type: discount_type.parse().unwrap_or_default(),
3293 discount_value: Decimal::from_f64_retain(discount_value).unwrap_or_default(),
3294 ..Default::default()
3295 })
3296 .map_err(|e| {
3297 Error::new(exception::runtime_error(), format!("Failed to create promotion: {}", e))
3298 })?;
3299 Ok(promo.into())
3300 }
3301
3302 fn get(&self, id: String) -> Result<Option<Promotion>, Error> {
3303 let commerce = lock_commerce!(self.commerce);
3304 let uuid = parse_uuid!(id, "promotion");
3305 let promo = commerce.promotions().get(uuid).map_err(|e| {
3306 Error::new(exception::runtime_error(), format!("Failed to get promotion: {}", e))
3307 })?;
3308 Ok(promo.map(|p| p.into()))
3309 }
3310
3311 fn get_by_code(&self, code: String) -> Result<Option<Promotion>, Error> {
3312 let commerce = lock_commerce!(self.commerce);
3313 let promo = commerce.promotions().get_by_code(&code).map_err(|e| {
3314 Error::new(exception::runtime_error(), format!("Failed to get promotion: {}", e))
3315 })?;
3316 Ok(promo.map(|p| p.into()))
3317 }
3318
3319 fn list(&self) -> Result<Vec<Promotion>, Error> {
3320 let commerce = lock_commerce!(self.commerce);
3321 let promos = commerce.promotions().list(Default::default()).map_err(|e| {
3322 Error::new(exception::runtime_error(), format!("Failed to list promotions: {}", e))
3323 })?;
3324 Ok(promos.into_iter().map(|p| p.into()).collect())
3325 }
3326
3327 fn activate(&self, id: String) -> Result<Promotion, Error> {
3328 let commerce = lock_commerce!(self.commerce);
3329 let uuid = parse_uuid!(id, "promotion");
3330 let promo = commerce.promotions().activate(uuid).map_err(|e| {
3331 Error::new(exception::runtime_error(), format!("Failed to activate: {}", e))
3332 })?;
3333 Ok(promo.into())
3334 }
3335
3336 fn deactivate(&self, id: String) -> Result<Promotion, Error> {
3337 let commerce = lock_commerce!(self.commerce);
3338 let uuid = parse_uuid!(id, "promotion");
3339 let promo = commerce.promotions().deactivate(uuid).map_err(|e| {
3340 Error::new(exception::runtime_error(), format!("Failed to deactivate: {}", e))
3341 })?;
3342 Ok(promo.into())
3343 }
3344
3345 fn get_active(&self) -> Result<Vec<Promotion>, Error> {
3346 let commerce = lock_commerce!(self.commerce);
3347 let promos = commerce.promotions().get_active().map_err(|e| {
3348 Error::new(exception::runtime_error(), format!("Failed to get active: {}", e))
3349 })?;
3350 Ok(promos.into_iter().map(|p| p.into()).collect())
3351 }
3352
3353 fn is_valid(&self, id: String) -> Result<bool, Error> {
3354 let commerce = lock_commerce!(self.commerce);
3355 let uuid = parse_uuid!(id, "promotion");
3356 let valid = commerce.promotions().is_valid(uuid).map_err(|e| {
3357 Error::new(exception::runtime_error(), format!("Failed to check: {}", e))
3358 })?;
3359 Ok(valid)
3360 }
3361
3362 fn delete(&self, id: String) -> Result<bool, Error> {
3363 let commerce = lock_commerce!(self.commerce);
3364 let uuid = parse_uuid!(id, "promotion");
3365 commerce.promotions().delete(uuid).map_err(|e| {
3366 Error::new(exception::runtime_error(), format!("Failed to delete: {}", e))
3367 })?;
3368 Ok(true)
3369 }
3370}
3371
3372#[derive(Clone)]
3377#[magnus::wrap(class = "StateSet::TaxJurisdiction", free_immediately, size)]
3378pub struct TaxJurisdiction {
3379 id: String,
3380 code: String,
3381 name: String,
3382 country: String,
3383 state: Option<String>,
3384 created_at: String,
3385}
3386
3387impl TaxJurisdiction {
3388 fn id(&self) -> String {
3389 self.id.clone()
3390 }
3391 fn code(&self) -> String {
3392 self.code.clone()
3393 }
3394 fn name(&self) -> String {
3395 self.name.clone()
3396 }
3397 fn country(&self) -> String {
3398 self.country.clone()
3399 }
3400 fn state(&self) -> Option<String> {
3401 self.state.clone()
3402 }
3403 fn created_at(&self) -> String {
3404 self.created_at.clone()
3405 }
3406}
3407
3408impl From<stateset_core::TaxJurisdiction> for TaxJurisdiction {
3409 fn from(j: stateset_core::TaxJurisdiction) -> Self {
3410 Self {
3411 id: j.id.to_string(),
3412 code: j.code,
3413 name: j.name,
3414 country: j.country,
3415 state: j.state,
3416 created_at: j.created_at.to_rfc3339(),
3417 }
3418 }
3419}
3420
3421#[derive(Clone)]
3422#[magnus::wrap(class = "StateSet::TaxRate", free_immediately, size)]
3423pub struct TaxRate {
3424 id: String,
3425 jurisdiction_id: String,
3426 name: String,
3427 rate: f64,
3428 category: String,
3429 created_at: String,
3430}
3431
3432impl TaxRate {
3433 fn id(&self) -> String {
3434 self.id.clone()
3435 }
3436 fn jurisdiction_id(&self) -> String {
3437 self.jurisdiction_id.clone()
3438 }
3439 fn name(&self) -> String {
3440 self.name.clone()
3441 }
3442 fn rate(&self) -> f64 {
3443 self.rate
3444 }
3445 fn category(&self) -> String {
3446 self.category.clone()
3447 }
3448 fn created_at(&self) -> String {
3449 self.created_at.clone()
3450 }
3451}
3452
3453impl From<stateset_core::TaxRate> for TaxRate {
3454 fn from(r: stateset_core::TaxRate) -> Self {
3455 Self {
3456 id: r.id.to_string(),
3457 jurisdiction_id: r.jurisdiction_id.to_string(),
3458 name: r.name,
3459 rate: to_f64_or_nan(r.rate),
3460 category: format!("{}", r.category),
3461 created_at: r.created_at.to_rfc3339(),
3462 }
3463 }
3464}
3465
3466#[derive(Clone)]
3467#[magnus::wrap(class = "StateSet::Tax", free_immediately, size)]
3468pub struct Tax {
3469 commerce: Arc<Mutex<RustCommerce>>,
3470}
3471
3472impl Tax {
3473 fn calculate(
3474 &self,
3475 unit_price: f64,
3476 quantity: f64,
3477 category: String,
3478 country: String,
3479 state: Option<String>,
3480 ) -> Result<f64, Error> {
3481 let commerce = lock_commerce!(self.commerce);
3482 let address = stateset_core::TaxAddress {
3483 country,
3484 state,
3485 city: None,
3486 postal_code: None,
3487 line1: None,
3488 };
3489 let result = commerce
3490 .tax()
3491 .calculate_for_item(
3492 Decimal::from_f64_retain(unit_price).unwrap_or_default(),
3493 Decimal::from_f64_retain(quantity).unwrap_or_default(),
3494 category.parse().unwrap_or_default(),
3495 &address,
3496 )
3497 .map_err(|e| {
3498 Error::new(exception::runtime_error(), format!("Failed to calculate: {}", e))
3499 })?;
3500 Ok(to_f64_or_nan(result))
3501 }
3502
3503 fn get_effective_rate(
3504 &self,
3505 category: String,
3506 country: String,
3507 state: Option<String>,
3508 ) -> Result<f64, Error> {
3509 let commerce = lock_commerce!(self.commerce);
3510 let address = stateset_core::TaxAddress {
3511 country,
3512 state,
3513 city: None,
3514 postal_code: None,
3515 line1: None,
3516 };
3517 let rate = commerce
3518 .tax()
3519 .get_effective_rate(&address, category.parse().unwrap_or_default())
3520 .map_err(|e| {
3521 Error::new(exception::runtime_error(), format!("Failed to get rate: {}", e))
3522 })?;
3523 Ok(to_f64_or_nan(rate))
3524 }
3525
3526 fn list_jurisdictions(&self) -> Result<Vec<TaxJurisdiction>, Error> {
3527 let commerce = lock_commerce!(self.commerce);
3528 let jurisdictions = commerce.tax().list_jurisdictions(Default::default()).map_err(|e| {
3529 Error::new(exception::runtime_error(), format!("Failed to list: {}", e))
3530 })?;
3531 Ok(jurisdictions.into_iter().map(|j| j.into()).collect())
3532 }
3533
3534 fn create_jurisdiction(
3535 &self,
3536 code: String,
3537 name: String,
3538 country: String,
3539 state: Option<String>,
3540 ) -> Result<TaxJurisdiction, Error> {
3541 let commerce = lock_commerce!(self.commerce);
3542 let j = commerce
3543 .tax()
3544 .create_jurisdiction(stateset_core::CreateTaxJurisdiction {
3545 code,
3546 name,
3547 country,
3548 state,
3549 ..Default::default()
3550 })
3551 .map_err(|e| {
3552 Error::new(exception::runtime_error(), format!("Failed to create: {}", e))
3553 })?;
3554 Ok(j.into())
3555 }
3556
3557 fn list_rates(&self) -> Result<Vec<TaxRate>, Error> {
3558 let commerce = lock_commerce!(self.commerce);
3559 let rates = commerce.tax().list_rates(Default::default()).map_err(|e| {
3560 Error::new(exception::runtime_error(), format!("Failed to list: {}", e))
3561 })?;
3562 Ok(rates.into_iter().map(|r| r.into()).collect())
3563 }
3564
3565 fn create_rate(
3566 &self,
3567 jurisdiction_id: String,
3568 name: String,
3569 rate: f64,
3570 category: String,
3571 ) -> Result<TaxRate, Error> {
3572 let commerce = lock_commerce!(self.commerce);
3573 let uuid = parse_uuid!(jurisdiction_id, "jurisdiction");
3574 let r = commerce
3575 .tax()
3576 .create_rate(stateset_core::CreateTaxRate {
3577 jurisdiction_id: uuid,
3578 name,
3579 rate: Decimal::from_f64_retain(rate).unwrap_or_default(),
3580 category: category.parse().unwrap_or_default(),
3581 ..Default::default()
3582 })
3583 .map_err(|e| {
3584 Error::new(exception::runtime_error(), format!("Failed to create: {}", e))
3585 })?;
3586 Ok(r.into())
3587 }
3588
3589 fn is_enabled(&self) -> Result<bool, Error> {
3590 let commerce = lock_commerce!(self.commerce);
3591 let enabled = commerce.tax().is_enabled().map_err(|e| {
3592 Error::new(exception::runtime_error(), format!("Failed to check: {}", e))
3593 })?;
3594 Ok(enabled)
3595 }
3596
3597 fn set_enabled(&self, enabled: bool) -> Result<bool, Error> {
3598 let commerce = lock_commerce!(self.commerce);
3599 commerce
3600 .tax()
3601 .set_enabled(enabled)
3602 .map_err(|e| Error::new(exception::runtime_error(), format!("Failed to set: {}", e)))?;
3603 Ok(enabled)
3604 }
3605}
3606
3607#[magnus::init]
3612fn init(ruby: &Ruby) -> Result<(), Error> {
3613 let module = ruby.define_module("StateSet")?;
3614
3615 let commerce_class = module.define_class("Commerce", ruby.class_object())?;
3617 commerce_class.define_singleton_method("new", function!(Commerce::new, 1))?;
3618 commerce_class.define_method("customers", method!(Commerce::customers, 0))?;
3619 commerce_class.define_method("orders", method!(Commerce::orders, 0))?;
3620 commerce_class.define_method("products", method!(Commerce::products, 0))?;
3621 commerce_class.define_method("inventory", method!(Commerce::inventory, 0))?;
3622 commerce_class.define_method("returns", method!(Commerce::returns, 0))?;
3623 commerce_class.define_method("payments", method!(Commerce::payments, 0))?;
3624 commerce_class.define_method("shipments", method!(Commerce::shipments, 0))?;
3625 commerce_class.define_method("warranties", method!(Commerce::warranties, 0))?;
3626 commerce_class.define_method("purchase_orders", method!(Commerce::purchase_orders, 0))?;
3627 commerce_class.define_method("invoices", method!(Commerce::invoices, 0))?;
3628 commerce_class.define_method("bom", method!(Commerce::bom, 0))?;
3629 commerce_class.define_method("work_orders", method!(Commerce::work_orders, 0))?;
3630 commerce_class.define_method("carts", method!(Commerce::carts, 0))?;
3631 commerce_class.define_method("analytics", method!(Commerce::analytics, 0))?;
3632 commerce_class.define_method("currency", method!(Commerce::currency, 0))?;
3633 commerce_class.define_method("subscriptions", method!(Commerce::subscriptions, 0))?;
3634 commerce_class.define_method("promotions", method!(Commerce::promotions, 0))?;
3635 commerce_class.define_method("tax", method!(Commerce::tax, 0))?;
3636
3637 let customer_class = module.define_class("Customer", ruby.class_object())?;
3639 customer_class.define_method("id", method!(Customer::id, 0))?;
3640 customer_class.define_method("email", method!(Customer::email, 0))?;
3641 customer_class.define_method("first_name", method!(Customer::first_name, 0))?;
3642 customer_class.define_method("last_name", method!(Customer::last_name, 0))?;
3643 customer_class.define_method("phone", method!(Customer::phone, 0))?;
3644 customer_class.define_method("status", method!(Customer::status, 0))?;
3645 customer_class.define_method("accepts_marketing", method!(Customer::accepts_marketing, 0))?;
3646 customer_class.define_method("created_at", method!(Customer::created_at, 0))?;
3647 customer_class.define_method("updated_at", method!(Customer::updated_at, 0))?;
3648 customer_class.define_method("full_name", method!(Customer::full_name, 0))?;
3649 customer_class.define_method("inspect", method!(Customer::inspect, 0))?;
3650 customer_class.define_method("to_s", method!(Customer::inspect, 0))?;
3651
3652 let customers_class = module.define_class("Customers", ruby.class_object())?;
3654 customers_class.define_method("create", method!(Customers::create, 5))?;
3655 customers_class.define_method("get", method!(Customers::get, 1))?;
3656 customers_class.define_method("get_by_email", method!(Customers::get_by_email, 1))?;
3657 customers_class.define_method("list", method!(Customers::list, 0))?;
3658 customers_class.define_method("count", method!(Customers::count, 0))?;
3659
3660 let order_item_class = module.define_class("OrderItem", ruby.class_object())?;
3662 order_item_class.define_method("id", method!(OrderItem::id, 0))?;
3663 order_item_class.define_method("sku", method!(OrderItem::sku, 0))?;
3664 order_item_class.define_method("name", method!(OrderItem::name, 0))?;
3665 order_item_class.define_method("quantity", method!(OrderItem::quantity, 0))?;
3666 order_item_class.define_method("unit_price", method!(OrderItem::unit_price, 0))?;
3667 order_item_class.define_method("total", method!(OrderItem::total, 0))?;
3668 order_item_class.define_method("inspect", method!(OrderItem::inspect, 0))?;
3669
3670 let order_class = module.define_class("Order", ruby.class_object())?;
3672 order_class.define_method("id", method!(Order::id, 0))?;
3673 order_class.define_method("order_number", method!(Order::order_number, 0))?;
3674 order_class.define_method("customer_id", method!(Order::customer_id, 0))?;
3675 order_class.define_method("status", method!(Order::status, 0))?;
3676 order_class.define_method("total_amount", method!(Order::total_amount, 0))?;
3677 order_class.define_method("currency", method!(Order::currency, 0))?;
3678 order_class.define_method("payment_status", method!(Order::payment_status, 0))?;
3679 order_class.define_method("fulfillment_status", method!(Order::fulfillment_status, 0))?;
3680 order_class.define_method("tracking_number", method!(Order::tracking_number, 0))?;
3681 order_class.define_method("items", method!(Order::items, 0))?;
3682 order_class.define_method("version", method!(Order::version, 0))?;
3683 order_class.define_method("created_at", method!(Order::created_at, 0))?;
3684 order_class.define_method("updated_at", method!(Order::updated_at, 0))?;
3685 order_class.define_method("item_count", method!(Order::item_count, 0))?;
3686 order_class.define_method("inspect", method!(Order::inspect, 0))?;
3687 order_class.define_method("to_s", method!(Order::inspect, 0))?;
3688
3689 let orders_class = module.define_class("Orders", ruby.class_object())?;
3691 orders_class.define_method("create", method!(Orders::create, 4))?;
3692 orders_class.define_method("get", method!(Orders::get, 1))?;
3693 orders_class.define_method("list", method!(Orders::list, 0))?;
3694 orders_class.define_method("count", method!(Orders::count, 0))?;
3695 orders_class.define_method("ship", method!(Orders::ship, 3))?;
3696 orders_class.define_method("cancel", method!(Orders::cancel, 2))?;
3697 orders_class.define_method("confirm", method!(Orders::confirm, 1))?;
3698 orders_class.define_method("deliver", method!(Orders::deliver, 1))?;
3699
3700 let variant_class = module.define_class("ProductVariant", ruby.class_object())?;
3702 variant_class.define_method("id", method!(ProductVariant::id, 0))?;
3703 variant_class.define_method("sku", method!(ProductVariant::sku, 0))?;
3704 variant_class.define_method("name", method!(ProductVariant::name, 0))?;
3705 variant_class.define_method("price", method!(ProductVariant::price, 0))?;
3706 variant_class
3707 .define_method("compare_at_price", method!(ProductVariant::compare_at_price, 0))?;
3708 variant_class
3709 .define_method("inventory_quantity", method!(ProductVariant::inventory_quantity, 0))?;
3710 variant_class.define_method("weight", method!(ProductVariant::weight, 0))?;
3711 variant_class.define_method("barcode", method!(ProductVariant::barcode, 0))?;
3712
3713 let product_class = module.define_class("Product", ruby.class_object())?;
3715 product_class.define_method("id", method!(Product::id, 0))?;
3716 product_class.define_method("name", method!(Product::name, 0))?;
3717 product_class.define_method("description", method!(Product::description, 0))?;
3718 product_class.define_method("vendor", method!(Product::vendor, 0))?;
3719 product_class.define_method("product_type", method!(Product::product_type, 0))?;
3720 product_class.define_method("status", method!(Product::status, 0))?;
3721 product_class.define_method("tags", method!(Product::tags, 0))?;
3722 product_class.define_method("variants", method!(Product::variants, 0))?;
3723 product_class.define_method("created_at", method!(Product::created_at, 0))?;
3724 product_class.define_method("updated_at", method!(Product::updated_at, 0))?;
3725 product_class.define_method("inspect", method!(Product::inspect, 0))?;
3726 product_class.define_method("to_s", method!(Product::inspect, 0))?;
3727
3728 let products_class = module.define_class("Products", ruby.class_object())?;
3730 products_class.define_method("create", method!(Products::create, 4))?;
3731 products_class.define_method("get", method!(Products::get, 1))?;
3732 products_class.define_method("list", method!(Products::list, 0))?;
3733 products_class.define_method("count", method!(Products::count, 0))?;
3734 products_class.define_method("get_by_sku", method!(Products::get_by_sku, 1))?;
3735
3736 let inv_item_class = module.define_class("InventoryItem", ruby.class_object())?;
3738 inv_item_class.define_method("id", method!(InventoryItem::id, 0))?;
3739 inv_item_class.define_method("sku", method!(InventoryItem::sku, 0))?;
3740 inv_item_class
3741 .define_method("quantity_on_hand", method!(InventoryItem::quantity_on_hand, 0))?;
3742 inv_item_class
3743 .define_method("quantity_reserved", method!(InventoryItem::quantity_reserved, 0))?;
3744 inv_item_class
3745 .define_method("quantity_available", method!(InventoryItem::quantity_available, 0))?;
3746 inv_item_class.define_method("reorder_point", method!(InventoryItem::reorder_point, 0))?;
3747 inv_item_class
3748 .define_method("reorder_quantity", method!(InventoryItem::reorder_quantity, 0))?;
3749 inv_item_class.define_method("location_id", method!(InventoryItem::location_id, 0))?;
3750 inv_item_class.define_method("inspect", method!(InventoryItem::inspect, 0))?;
3751 inv_item_class.define_method("to_s", method!(InventoryItem::inspect, 0))?;
3752
3753 let inventory_class = module.define_class("Inventory", ruby.class_object())?;
3755 inventory_class.define_method("create", method!(Inventory::create, 4))?;
3756 inventory_class.define_method("get", method!(Inventory::get, 1))?;
3757 inventory_class.define_method("get_by_sku", method!(Inventory::get_by_sku, 1))?;
3758 inventory_class.define_method("list", method!(Inventory::list, 0))?;
3759 inventory_class.define_method("adjust", method!(Inventory::adjust, 3))?;
3760 inventory_class.define_method("reserve", method!(Inventory::reserve, 3))?;
3761 inventory_class.define_method("release", method!(Inventory::release, 2))?;
3762
3763 let return_class = module.define_class("Return", ruby.class_object())?;
3765 return_class.define_method("id", method!(Return::id, 0))?;
3766 return_class.define_method("order_id", method!(Return::order_id, 0))?;
3767 return_class.define_method("customer_id", method!(Return::customer_id, 0))?;
3768 return_class.define_method("status", method!(Return::status, 0))?;
3769 return_class.define_method("reason", method!(Return::reason, 0))?;
3770 return_class.define_method("refund_amount", method!(Return::refund_amount, 0))?;
3771 return_class.define_method("created_at", method!(Return::created_at, 0))?;
3772 return_class.define_method("updated_at", method!(Return::updated_at, 0))?;
3773 return_class.define_method("inspect", method!(Return::inspect, 0))?;
3774 return_class.define_method("to_s", method!(Return::inspect, 0))?;
3775
3776 let returns_class = module.define_class("Returns", ruby.class_object())?;
3778 returns_class.define_method("create", method!(Returns::create, 2))?;
3779 returns_class.define_method("get", method!(Returns::get, 1))?;
3780 returns_class.define_method("list", method!(Returns::list, 0))?;
3781 returns_class.define_method("approve", method!(Returns::approve, 2))?;
3782 returns_class.define_method("reject", method!(Returns::reject, 2))?;
3783
3784 let payments_class = module.define_class("Payments", ruby.class_object())?;
3786 payments_class.define_method("record", method!(Payments::record, 3))?;
3787
3788 let shipment_class = module.define_class("Shipment", ruby.class_object())?;
3790 shipment_class.define_method("id", method!(Shipment::id, 0))?;
3791 shipment_class.define_method("shipment_number", method!(Shipment::shipment_number, 0))?;
3792 shipment_class.define_method("order_id", method!(Shipment::order_id, 0))?;
3793 shipment_class.define_method("status", method!(Shipment::status, 0))?;
3794 shipment_class.define_method("carrier", method!(Shipment::carrier, 0))?;
3795 shipment_class.define_method("tracking_number", method!(Shipment::tracking_number, 0))?;
3796 shipment_class.define_method("shipping_method", method!(Shipment::shipping_method, 0))?;
3797 shipment_class.define_method("weight", method!(Shipment::weight, 0))?;
3798 shipment_class.define_method("estimated_delivery", method!(Shipment::estimated_delivery, 0))?;
3799 shipment_class.define_method("shipped_at", method!(Shipment::shipped_at, 0))?;
3800 shipment_class.define_method("delivered_at", method!(Shipment::delivered_at, 0))?;
3801 shipment_class.define_method("inspect", method!(Shipment::inspect, 0))?;
3802 shipment_class.define_method("to_s", method!(Shipment::inspect, 0))?;
3803
3804 let shipments_class = module.define_class("Shipments", ruby.class_object())?;
3806 shipments_class.define_method("create", method!(Shipments::create, 3))?;
3807 shipments_class.define_method("get", method!(Shipments::get, 1))?;
3808 shipments_class.define_method("get_by_tracking", method!(Shipments::get_by_tracking, 1))?;
3809 shipments_class.define_method("list", method!(Shipments::list, 0))?;
3810 shipments_class.define_method("for_order", method!(Shipments::for_order, 1))?;
3811 shipments_class.define_method("ship", method!(Shipments::ship, 2))?;
3812 shipments_class.define_method("mark_delivered", method!(Shipments::mark_delivered, 1))?;
3813 shipments_class.define_method("cancel", method!(Shipments::cancel, 1))?;
3814 shipments_class.define_method("count", method!(Shipments::count, 0))?;
3815
3816 let _warranties_class = module.define_class("Warranties", ruby.class_object())?;
3818
3819 let _po_class = module.define_class("PurchaseOrders", ruby.class_object())?;
3821
3822 let _invoices_class = module.define_class("Invoices", ruby.class_object())?;
3824
3825 let _bom_class = module.define_class("BomApi", ruby.class_object())?;
3827
3828 let _wo_class = module.define_class("WorkOrders", ruby.class_object())?;
3830
3831 let cart_item_class = module.define_class("CartItem", ruby.class_object())?;
3833 cart_item_class.define_method("id", method!(CartItem::id, 0))?;
3834 cart_item_class.define_method("sku", method!(CartItem::sku, 0))?;
3835 cart_item_class.define_method("name", method!(CartItem::name, 0))?;
3836 cart_item_class.define_method("quantity", method!(CartItem::quantity, 0))?;
3837 cart_item_class.define_method("unit_price", method!(CartItem::unit_price, 0))?;
3838 cart_item_class.define_method("total", method!(CartItem::total, 0))?;
3839
3840 let cart_class = module.define_class("Cart", ruby.class_object())?;
3842 cart_class.define_method("id", method!(Cart::id, 0))?;
3843 cart_class.define_method("customer_id", method!(Cart::customer_id, 0))?;
3844 cart_class.define_method("status", method!(Cart::status, 0))?;
3845 cart_class.define_method("items", method!(Cart::items, 0))?;
3846 cart_class.define_method("subtotal", method!(Cart::subtotal, 0))?;
3847 cart_class.define_method("total", method!(Cart::total, 0))?;
3848 cart_class.define_method("currency", method!(Cart::currency, 0))?;
3849 cart_class.define_method("created_at", method!(Cart::created_at, 0))?;
3850 cart_class.define_method("updated_at", method!(Cart::updated_at, 0))?;
3851 cart_class.define_method("inspect", method!(Cart::inspect, 0))?;
3852 cart_class.define_method("to_s", method!(Cart::inspect, 0))?;
3853
3854 let carts_class = module.define_class("Carts", ruby.class_object())?;
3856 carts_class.define_method("create", method!(Carts::create, 2))?;
3857 carts_class.define_method("get", method!(Carts::get, 1))?;
3858 carts_class.define_method("list", method!(Carts::list, 0))?;
3859 carts_class.define_method("add_item", method!(Carts::add_item, 5))?;
3860 carts_class.define_method("checkout", method!(Carts::checkout, 1))?;
3861
3862 let sales_class = module.define_class("SalesSummary", ruby.class_object())?;
3864 sales_class.define_method("total_revenue", method!(SalesSummary::total_revenue, 0))?;
3865 sales_class.define_method("total_orders", method!(SalesSummary::total_orders, 0))?;
3866 sales_class
3867 .define_method("average_order_value", method!(SalesSummary::average_order_value, 0))?;
3868 sales_class.define_method("total_items_sold", method!(SalesSummary::total_items_sold, 0))?;
3869
3870 let analytics_class = module.define_class("Analytics", ruby.class_object())?;
3872 analytics_class.define_method("sales_summary", method!(Analytics::sales_summary, 1))?;
3873
3874 let _currency_class = module.define_class("CurrencyOps", ruby.class_object())?;
3876
3877 let _subs_class = module.define_class("Subscriptions", ruby.class_object())?;
3879
3880 let _promos_class = module.define_class("Promotions", ruby.class_object())?;
3882
3883 let _tax_class = module.define_class("Tax", ruby.class_object())?;
3885
3886 Ok(())
3887}