1use ext_php_rs::prelude::*;
14use rust_decimal::Decimal;
15use rust_decimal::prelude::ToPrimitive;
16use stateset_embedded::Commerce as RustCommerce;
17use std::sync::{Arc, Mutex};
18
19macro_rules! lock_commerce {
24 ($commerce:expr) => {
25 $commerce.lock().map_err(|e| PhpException::default(format!("Lock error: {}", e)))?
26 };
27}
28
29macro_rules! parse_uuid {
30 ($id:expr, $name:expr) => {
31 $id.parse().map_err(|_| PhpException::default(format!("Invalid {} UUID", $name)))?
32 };
33}
34
35fn to_f64_or_nan<T>(value: T) -> f64
36where
37 T: TryInto<f64>,
38 <T as TryInto<f64>>::Error: std::fmt::Display,
39{
40 match value.try_into() {
41 Ok(converted) => converted,
42 Err(err) => {
43 eprintln!("stateset-embedded: failed to convert to f64: {}", err);
44 f64::NAN
45 }
46 }
47}
48
49#[php_class(name = "StateSet\\Commerce")]
55#[derive(Clone)]
56pub struct Commerce {
57 inner: Arc<Mutex<RustCommerce>>,
58}
59
60#[php_impl]
61impl Commerce {
62 pub fn __construct(db_path: String) -> PhpResult<Self> {
63 let commerce = RustCommerce::new(&db_path)
64 .map_err(|e| PhpException::default(format!("Failed to initialize commerce: {}", e)))?;
65
66 Ok(Self { inner: Arc::new(Mutex::new(commerce)) })
67 }
68
69 pub fn customers(&self) -> Customers {
70 Customers { commerce: self.inner.clone() }
71 }
72
73 pub fn orders(&self) -> Orders {
74 Orders { commerce: self.inner.clone() }
75 }
76
77 pub fn products(&self) -> Products {
78 Products { commerce: self.inner.clone() }
79 }
80
81 pub fn inventory(&self) -> Inventory {
82 Inventory { commerce: self.inner.clone() }
83 }
84
85 pub fn returns(&self) -> Returns {
86 Returns { commerce: self.inner.clone() }
87 }
88
89 pub fn payments(&self) -> Payments {
90 Payments { commerce: self.inner.clone() }
91 }
92
93 pub fn shipments(&self) -> Shipments {
94 Shipments { commerce: self.inner.clone() }
95 }
96
97 pub fn warranties(&self) -> Warranties {
98 Warranties { commerce: self.inner.clone() }
99 }
100
101 pub fn purchase_orders(&self) -> PurchaseOrders {
102 PurchaseOrders { commerce: self.inner.clone() }
103 }
104
105 pub fn invoices(&self) -> Invoices {
106 Invoices { commerce: self.inner.clone() }
107 }
108
109 pub fn bom(&self) -> BomApi {
110 BomApi { commerce: self.inner.clone() }
111 }
112
113 pub fn work_orders(&self) -> WorkOrders {
114 WorkOrders { commerce: self.inner.clone() }
115 }
116
117 pub fn carts(&self) -> Carts {
118 Carts { commerce: self.inner.clone() }
119 }
120
121 pub fn analytics(&self) -> Analytics {
122 Analytics { commerce: self.inner.clone() }
123 }
124
125 pub fn currency(&self) -> CurrencyOps {
126 CurrencyOps { commerce: self.inner.clone() }
127 }
128
129 pub fn subscriptions(&self) -> Subscriptions {
130 Subscriptions { commerce: self.inner.clone() }
131 }
132
133 pub fn promotions(&self) -> Promotions {
134 Promotions { commerce: self.inner.clone() }
135 }
136
137 pub fn tax(&self) -> Tax {
138 Tax { commerce: self.inner.clone() }
139 }
140
141 pub fn quality(&self) -> Quality {
142 Quality { commerce: self.inner.clone() }
143 }
144
145 pub fn lots(&self) -> Lots {
146 Lots { commerce: self.inner.clone() }
147 }
148
149 pub fn serials(&self) -> Serials {
150 Serials { commerce: self.inner.clone() }
151 }
152
153 pub fn warehouse(&self) -> WarehouseApi {
154 WarehouseApi { commerce: self.inner.clone() }
155 }
156
157 pub fn receiving(&self) -> Receiving {
158 Receiving { commerce: self.inner.clone() }
159 }
160
161 pub fn fulfillment(&self) -> Fulfillment {
162 Fulfillment { commerce: self.inner.clone() }
163 }
164
165 pub fn accounts_payable(&self) -> AccountsPayable {
166 AccountsPayable { commerce: self.inner.clone() }
167 }
168
169 pub fn accounts_receivable(&self) -> AccountsReceivable {
170 AccountsReceivable { commerce: self.inner.clone() }
171 }
172
173 pub fn cost_accounting(&self) -> CostAccounting {
174 CostAccounting { commerce: self.inner.clone() }
175 }
176
177 pub fn credit(&self) -> CreditApi {
178 CreditApi { commerce: self.inner.clone() }
179 }
180
181 pub fn backorders(&self) -> Backorders {
182 Backorders { commerce: self.inner.clone() }
183 }
184
185 pub fn general_ledger(&self) -> GeneralLedger {
186 GeneralLedger { commerce: self.inner.clone() }
187 }
188}
189
190#[php_class(name = "StateSet\\Customer")]
195#[derive(Clone)]
196pub struct Customer {
197 id: String,
198 email: String,
199 first_name: String,
200 last_name: String,
201 phone: Option<String>,
202 status: String,
203 accepts_marketing: bool,
204 created_at: String,
205 updated_at: String,
206}
207
208#[php_impl]
209impl Customer {
210 #[getter]
211 pub fn get_id(&self) -> String {
212 self.id.clone()
213 }
214
215 #[getter]
216 pub fn get_email(&self) -> String {
217 self.email.clone()
218 }
219
220 #[getter]
221 pub fn get_first_name(&self) -> String {
222 self.first_name.clone()
223 }
224
225 #[getter]
226 pub fn get_last_name(&self) -> String {
227 self.last_name.clone()
228 }
229
230 #[getter]
231 pub fn get_phone(&self) -> Option<String> {
232 self.phone.clone()
233 }
234
235 #[getter]
236 pub fn get_status(&self) -> String {
237 self.status.clone()
238 }
239
240 #[getter]
241 pub fn get_accepts_marketing(&self) -> bool {
242 self.accepts_marketing
243 }
244
245 #[getter]
246 pub fn get_created_at(&self) -> String {
247 self.created_at.clone()
248 }
249
250 #[getter]
251 pub fn get_updated_at(&self) -> String {
252 self.updated_at.clone()
253 }
254
255 pub fn get_full_name(&self) -> String {
256 format!("{} {}", self.first_name, self.last_name)
257 }
258
259 pub fn __to_string(&self) -> String {
260 format!(
261 "Customer(id={}, email={}, name={} {})",
262 self.id, self.email, self.first_name, self.last_name
263 )
264 }
265}
266
267impl From<stateset_core::Customer> for Customer {
268 fn from(c: stateset_core::Customer) -> Self {
269 Self {
270 id: c.id.to_string(),
271 email: c.email,
272 first_name: c.first_name,
273 last_name: c.last_name,
274 phone: c.phone,
275 status: format!("{}", c.status),
276 accepts_marketing: c.accepts_marketing,
277 created_at: c.created_at.to_rfc3339(),
278 updated_at: c.updated_at.to_rfc3339(),
279 }
280 }
281}
282
283#[php_class(name = "StateSet\\Customers")]
288#[derive(Clone)]
289pub struct Customers {
290 commerce: Arc<Mutex<RustCommerce>>,
291}
292
293#[php_impl]
294impl Customers {
295 pub fn create(
296 &self,
297 email: String,
298 first_name: String,
299 last_name: String,
300 phone: Option<String>,
301 accepts_marketing: Option<bool>,
302 ) -> PhpResult<Customer> {
303 let commerce = lock_commerce!(self.commerce);
304
305 let customer = commerce
306 .customers()
307 .create(stateset_core::CreateCustomer {
308 email,
309 first_name,
310 last_name,
311 phone,
312 accepts_marketing,
313 ..Default::default()
314 })
315 .map_err(|e| PhpException::default(format!("Failed to create customer: {}", e)))?;
316
317 Ok(customer.into())
318 }
319
320 pub fn get(&self, id: String) -> PhpResult<Option<Customer>> {
321 let commerce = lock_commerce!(self.commerce);
322 let uuid = parse_uuid!(id, "customer");
323
324 let customer = commerce
325 .customers()
326 .get(uuid)
327 .map_err(|e| PhpException::default(format!("Failed to get customer: {}", e)))?;
328
329 Ok(customer.map(|c| c.into()))
330 }
331
332 pub fn get_by_email(&self, email: String) -> PhpResult<Option<Customer>> {
333 let commerce = lock_commerce!(self.commerce);
334
335 let customer = commerce
336 .customers()
337 .get_by_email(&email)
338 .map_err(|e| PhpException::default(format!("Failed to get customer: {}", e)))?;
339
340 Ok(customer.map(|c| c.into()))
341 }
342
343 pub fn list(&self) -> PhpResult<Vec<Customer>> {
344 let commerce = lock_commerce!(self.commerce);
345
346 let customers = commerce
347 .customers()
348 .list(Default::default())
349 .map_err(|e| PhpException::default(format!("Failed to list customers: {}", e)))?;
350
351 Ok(customers.into_iter().map(|c| c.into()).collect())
352 }
353
354 pub fn count(&self) -> PhpResult<i64> {
355 let commerce = lock_commerce!(self.commerce);
356
357 let count = commerce
358 .customers()
359 .count(Default::default())
360 .map_err(|e| PhpException::default(format!("Failed to count customers: {}", e)))?;
361
362 Ok(count)
363 }
364}
365
366#[php_class(name = "StateSet\\OrderItem")]
371#[derive(Clone)]
372pub struct OrderItem {
373 id: String,
374 sku: String,
375 name: String,
376 quantity: i32,
377 unit_price: f64,
378 total: f64,
379}
380
381#[php_impl]
382impl OrderItem {
383 #[getter]
384 pub fn get_id(&self) -> String {
385 self.id.clone()
386 }
387
388 #[getter]
389 pub fn get_sku(&self) -> String {
390 self.sku.clone()
391 }
392
393 #[getter]
394 pub fn get_name(&self) -> String {
395 self.name.clone()
396 }
397
398 #[getter]
399 pub fn get_quantity(&self) -> i32 {
400 self.quantity
401 }
402
403 #[getter]
404 pub fn get_unit_price(&self) -> f64 {
405 self.unit_price
406 }
407
408 #[getter]
409 pub fn get_total(&self) -> f64 {
410 self.total
411 }
412
413 pub fn __to_string(&self) -> String {
414 format!("OrderItem(sku={}, qty={}, price={})", self.sku, self.quantity, self.unit_price)
415 }
416}
417
418#[php_class(name = "StateSet\\Order")]
419#[derive(Clone)]
420pub struct Order {
421 id: String,
422 order_number: String,
423 customer_id: String,
424 status: String,
425 total_amount: f64,
426 currency: String,
427 payment_status: String,
428 fulfillment_status: String,
429 tracking_number: Option<String>,
430 items: Vec<OrderItem>,
431 version: i32,
432 created_at: String,
433 updated_at: String,
434}
435
436#[php_impl]
437impl Order {
438 #[getter]
439 pub fn get_id(&self) -> String {
440 self.id.clone()
441 }
442
443 #[getter]
444 pub fn get_order_number(&self) -> String {
445 self.order_number.clone()
446 }
447
448 #[getter]
449 pub fn get_customer_id(&self) -> String {
450 self.customer_id.clone()
451 }
452
453 #[getter]
454 pub fn get_status(&self) -> String {
455 self.status.clone()
456 }
457
458 #[getter]
459 pub fn get_total_amount(&self) -> f64 {
460 self.total_amount
461 }
462
463 #[getter]
464 pub fn get_currency(&self) -> String {
465 self.currency.clone()
466 }
467
468 #[getter]
469 pub fn get_payment_status(&self) -> String {
470 self.payment_status.clone()
471 }
472
473 #[getter]
474 pub fn get_fulfillment_status(&self) -> String {
475 self.fulfillment_status.clone()
476 }
477
478 #[getter]
479 pub fn get_tracking_number(&self) -> Option<String> {
480 self.tracking_number.clone()
481 }
482
483 #[getter]
484 pub fn get_items(&self) -> Vec<OrderItem> {
485 self.items.clone()
486 }
487
488 #[getter]
489 pub fn get_version(&self) -> i32 {
490 self.version
491 }
492
493 #[getter]
494 pub fn get_created_at(&self) -> String {
495 self.created_at.clone()
496 }
497
498 #[getter]
499 pub fn get_updated_at(&self) -> String {
500 self.updated_at.clone()
501 }
502
503 pub fn get_item_count(&self) -> usize {
504 self.items.len()
505 }
506
507 pub fn __to_string(&self) -> String {
508 format!(
509 "Order(number={}, status={}, total={} {})",
510 self.order_number, self.status, self.total_amount, self.currency
511 )
512 }
513}
514
515impl From<stateset_core::Order> for Order {
516 fn from(o: stateset_core::Order) -> Self {
517 Self {
518 id: o.id.to_string(),
519 order_number: o.order_number,
520 customer_id: o.customer_id.to_string(),
521 status: format!("{}", o.status),
522 total_amount: to_f64_or_nan(o.total_amount),
523 currency: o.currency,
524 payment_status: format!("{}", o.payment_status),
525 fulfillment_status: format!("{}", o.fulfillment_status),
526 tracking_number: o.tracking_number,
527 items: o
528 .items
529 .into_iter()
530 .map(|i| OrderItem {
531 id: i.id.to_string(),
532 sku: i.sku,
533 name: i.name,
534 quantity: i.quantity,
535 unit_price: to_f64_or_nan(i.unit_price),
536 total: to_f64_or_nan(i.total),
537 })
538 .collect(),
539 version: o.version,
540 created_at: o.created_at.to_rfc3339(),
541 updated_at: o.updated_at.to_rfc3339(),
542 }
543 }
544}
545
546#[php_class(name = "StateSet\\Orders")]
551#[derive(Clone)]
552pub struct Orders {
553 commerce: Arc<Mutex<RustCommerce>>,
554}
555
556#[php_impl]
557impl Orders {
558 pub fn create(
559 &self,
560 customer_id: String,
561 items: Vec<ext_php_rs::types::ZendHashTable>,
562 currency: Option<String>,
563 notes: Option<String>,
564 ) -> PhpResult<Order> {
565 let commerce = lock_commerce!(self.commerce);
566 let cust_uuid = parse_uuid!(customer_id, "customer");
567
568 let order_items: Vec<stateset_core::CreateOrderItem> = items
569 .into_iter()
570 .map(|h| {
571 let sku: String = h.get("sku").and_then(|v| v.string().ok()).unwrap_or_default();
572 let name: String = h.get("name").and_then(|v| v.string().ok()).unwrap_or_default();
573 let quantity: i32 =
574 h.get("quantity").and_then(|v| v.long().ok()).map(|l| l as i32).unwrap_or(1);
575 let unit_price: f64 =
576 h.get("unit_price").and_then(|v| v.double().ok()).unwrap_or(0.0);
577
578 stateset_core::CreateOrderItem {
579 product_id: Default::default(),
580 variant_id: None,
581 sku,
582 name,
583 quantity,
584 unit_price: Decimal::from_f64_retain(unit_price).unwrap_or_default(),
585 ..Default::default()
586 }
587 })
588 .collect();
589
590 let order = commerce
591 .orders()
592 .create(stateset_core::CreateOrder {
593 customer_id: cust_uuid,
594 items: order_items,
595 currency,
596 notes,
597 ..Default::default()
598 })
599 .map_err(|e| PhpException::default(format!("Failed to create order: {}", e)))?;
600
601 Ok(order.into())
602 }
603
604 pub fn get(&self, id: String) -> PhpResult<Option<Order>> {
605 let commerce = lock_commerce!(self.commerce);
606 let uuid = parse_uuid!(id, "order");
607
608 let order = commerce
609 .orders()
610 .get(uuid)
611 .map_err(|e| PhpException::default(format!("Failed to get order: {}", e)))?;
612
613 Ok(order.map(|o| o.into()))
614 }
615
616 pub fn list(&self) -> PhpResult<Vec<Order>> {
617 let commerce = lock_commerce!(self.commerce);
618
619 let orders = commerce
620 .orders()
621 .list(Default::default())
622 .map_err(|e| PhpException::default(format!("Failed to list orders: {}", e)))?;
623
624 Ok(orders.into_iter().map(|o| o.into()).collect())
625 }
626
627 pub fn count(&self) -> PhpResult<i64> {
628 let commerce = lock_commerce!(self.commerce);
629
630 let count = commerce
631 .orders()
632 .count(Default::default())
633 .map_err(|e| PhpException::default(format!("Failed to count orders: {}", e)))?;
634
635 Ok(count)
636 }
637
638 pub fn ship(
639 &self,
640 id: String,
641 tracking_number: Option<String>,
642 carrier: Option<String>,
643 ) -> PhpResult<Order> {
644 let commerce = lock_commerce!(self.commerce);
645 let uuid = parse_uuid!(id, "order");
646
647 let order = commerce
648 .orders()
649 .ship(uuid, tracking_number, carrier)
650 .map_err(|e| PhpException::default(format!("Failed to ship order: {}", e)))?;
651
652 Ok(order.into())
653 }
654
655 pub fn cancel(&self, id: String, reason: Option<String>) -> PhpResult<Order> {
656 let commerce = lock_commerce!(self.commerce);
657 let uuid = parse_uuid!(id, "order");
658
659 let order = commerce
660 .orders()
661 .cancel(uuid, reason)
662 .map_err(|e| PhpException::default(format!("Failed to cancel order: {}", e)))?;
663
664 Ok(order.into())
665 }
666
667 pub fn confirm(&self, id: String) -> PhpResult<Order> {
668 let commerce = lock_commerce!(self.commerce);
669 let uuid = parse_uuid!(id, "order");
670
671 let order = commerce
672 .orders()
673 .confirm(uuid)
674 .map_err(|e| PhpException::default(format!("Failed to confirm order: {}", e)))?;
675
676 Ok(order.into())
677 }
678
679 pub fn deliver(&self, id: String) -> PhpResult<Order> {
680 let commerce = lock_commerce!(self.commerce);
681 let uuid = parse_uuid!(id, "order");
682
683 let order = commerce
684 .orders()
685 .deliver(uuid)
686 .map_err(|e| PhpException::default(format!("Failed to deliver order: {}", e)))?;
687
688 Ok(order.into())
689 }
690}
691
692#[php_class(name = "StateSet\\ProductVariant")]
697#[derive(Clone)]
698pub struct ProductVariant {
699 id: String,
700 sku: String,
701 name: String,
702 price: f64,
703 compare_at_price: Option<f64>,
704 inventory_quantity: i32,
705 weight: Option<f64>,
706 barcode: Option<String>,
707}
708
709#[php_impl]
710impl ProductVariant {
711 #[getter]
712 pub fn get_id(&self) -> String {
713 self.id.clone()
714 }
715
716 #[getter]
717 pub fn get_sku(&self) -> String {
718 self.sku.clone()
719 }
720
721 #[getter]
722 pub fn get_name(&self) -> String {
723 self.name.clone()
724 }
725
726 #[getter]
727 pub fn get_price(&self) -> f64 {
728 self.price
729 }
730
731 #[getter]
732 pub fn get_compare_at_price(&self) -> Option<f64> {
733 self.compare_at_price
734 }
735
736 #[getter]
737 pub fn get_inventory_quantity(&self) -> i32 {
738 self.inventory_quantity
739 }
740
741 #[getter]
742 pub fn get_weight(&self) -> Option<f64> {
743 self.weight
744 }
745
746 #[getter]
747 pub fn get_barcode(&self) -> Option<String> {
748 self.barcode.clone()
749 }
750}
751
752#[php_class(name = "StateSet\\Product")]
753#[derive(Clone)]
754pub struct Product {
755 id: String,
756 name: String,
757 description: Option<String>,
758 vendor: Option<String>,
759 product_type: Option<String>,
760 status: String,
761 tags: Vec<String>,
762 variants: Vec<ProductVariant>,
763 created_at: String,
764 updated_at: String,
765}
766
767#[php_impl]
768impl Product {
769 #[getter]
770 pub fn get_id(&self) -> String {
771 self.id.clone()
772 }
773
774 #[getter]
775 pub fn get_name(&self) -> String {
776 self.name.clone()
777 }
778
779 #[getter]
780 pub fn get_description(&self) -> Option<String> {
781 self.description.clone()
782 }
783
784 #[getter]
785 pub fn get_vendor(&self) -> Option<String> {
786 self.vendor.clone()
787 }
788
789 #[getter]
790 pub fn get_product_type(&self) -> Option<String> {
791 self.product_type.clone()
792 }
793
794 #[getter]
795 pub fn get_status(&self) -> String {
796 self.status.clone()
797 }
798
799 #[getter]
800 pub fn get_tags(&self) -> Vec<String> {
801 self.tags.clone()
802 }
803
804 #[getter]
805 pub fn get_variants(&self) -> Vec<ProductVariant> {
806 self.variants.clone()
807 }
808
809 #[getter]
810 pub fn get_created_at(&self) -> String {
811 self.created_at.clone()
812 }
813
814 #[getter]
815 pub fn get_updated_at(&self) -> String {
816 self.updated_at.clone()
817 }
818
819 pub fn __to_string(&self) -> String {
820 format!("Product(id={}, name={}, status={})", self.id, self.name, self.status)
821 }
822}
823
824impl From<stateset_core::Product> for Product {
825 fn from(p: stateset_core::Product) -> Self {
826 Self {
827 id: p.id.to_string(),
828 name: p.name,
829 description: p.description,
830 vendor: p.vendor,
831 product_type: p.product_type,
832 status: format!("{}", p.status),
833 tags: p.tags,
834 variants: p
835 .variants
836 .into_iter()
837 .map(|v| ProductVariant {
838 id: v.id.to_string(),
839 sku: v.sku,
840 name: v.name,
841 price: to_f64_or_nan(v.price),
842 compare_at_price: v.compare_at_price.and_then(|p| p.to_f64()),
843 inventory_quantity: v.inventory_quantity,
844 weight: v.weight.and_then(|w| w.to_f64()),
845 barcode: v.barcode,
846 })
847 .collect(),
848 created_at: p.created_at.to_rfc3339(),
849 updated_at: p.updated_at.to_rfc3339(),
850 }
851 }
852}
853
854#[php_class(name = "StateSet\\Products")]
859#[derive(Clone)]
860pub struct Products {
861 commerce: Arc<Mutex<RustCommerce>>,
862}
863
864#[php_impl]
865impl Products {
866 pub fn create(
867 &self,
868 name: String,
869 description: Option<String>,
870 vendor: Option<String>,
871 product_type: Option<String>,
872 ) -> PhpResult<Product> {
873 let commerce = lock_commerce!(self.commerce);
874
875 let product = commerce
876 .products()
877 .create(stateset_core::CreateProduct {
878 name,
879 description,
880 vendor,
881 product_type,
882 ..Default::default()
883 })
884 .map_err(|e| PhpException::default(format!("Failed to create product: {}", e)))?;
885
886 Ok(product.into())
887 }
888
889 pub fn get(&self, id: String) -> PhpResult<Option<Product>> {
890 let commerce = lock_commerce!(self.commerce);
891 let uuid = parse_uuid!(id, "product");
892
893 let product = commerce
894 .products()
895 .get(uuid)
896 .map_err(|e| PhpException::default(format!("Failed to get product: {}", e)))?;
897
898 Ok(product.map(|p| p.into()))
899 }
900
901 pub fn list(&self) -> PhpResult<Vec<Product>> {
902 let commerce = lock_commerce!(self.commerce);
903
904 let products = commerce
905 .products()
906 .list(Default::default())
907 .map_err(|e| PhpException::default(format!("Failed to list products: {}", e)))?;
908
909 Ok(products.into_iter().map(|p| p.into()).collect())
910 }
911
912 pub fn count(&self) -> PhpResult<i64> {
913 let commerce = lock_commerce!(self.commerce);
914
915 let count = commerce
916 .products()
917 .count(Default::default())
918 .map_err(|e| PhpException::default(format!("Failed to count products: {}", e)))?;
919
920 Ok(count)
921 }
922
923 pub fn get_by_sku(&self, sku: String) -> PhpResult<Option<ProductVariant>> {
924 let commerce = lock_commerce!(self.commerce);
925
926 let variant = commerce
927 .products()
928 .get_variant_by_sku(&sku)
929 .map_err(|e| PhpException::default(format!("Failed to get variant: {}", e)))?;
930
931 Ok(variant.map(|v| ProductVariant {
932 id: v.id.to_string(),
933 sku: v.sku,
934 name: v.name,
935 price: to_f64_or_nan(v.price),
936 compare_at_price: v.compare_at_price.and_then(|p| p.to_f64()),
937 inventory_quantity: v.inventory_quantity,
938 weight: v.weight.and_then(|w| w.to_f64()),
939 barcode: v.barcode,
940 }))
941 }
942}
943
944#[php_class(name = "StateSet\\InventoryItem")]
949#[derive(Clone)]
950pub struct InventoryItem {
951 id: String,
952 sku: String,
953 quantity_on_hand: i32,
954 quantity_reserved: i32,
955 quantity_available: i32,
956 reorder_point: Option<i32>,
957 reorder_quantity: Option<i32>,
958 location_id: Option<String>,
959}
960
961#[php_impl]
962impl InventoryItem {
963 #[getter]
964 pub fn get_id(&self) -> String {
965 self.id.clone()
966 }
967
968 #[getter]
969 pub fn get_sku(&self) -> String {
970 self.sku.clone()
971 }
972
973 #[getter]
974 pub fn get_quantity_on_hand(&self) -> i32 {
975 self.quantity_on_hand
976 }
977
978 #[getter]
979 pub fn get_quantity_reserved(&self) -> i32 {
980 self.quantity_reserved
981 }
982
983 #[getter]
984 pub fn get_quantity_available(&self) -> i32 {
985 self.quantity_available
986 }
987
988 #[getter]
989 pub fn get_reorder_point(&self) -> Option<i32> {
990 self.reorder_point
991 }
992
993 #[getter]
994 pub fn get_reorder_quantity(&self) -> Option<i32> {
995 self.reorder_quantity
996 }
997
998 #[getter]
999 pub fn get_location_id(&self) -> Option<String> {
1000 self.location_id.clone()
1001 }
1002
1003 pub fn __to_string(&self) -> String {
1004 format!("InventoryItem(sku={}, available={})", self.sku, self.quantity_available)
1005 }
1006}
1007
1008impl From<stateset_core::InventoryItem> for InventoryItem {
1009 fn from(i: stateset_core::InventoryItem) -> Self {
1010 Self {
1011 id: i.id.to_string(),
1012 sku: i.sku,
1013 quantity_on_hand: i.quantity_on_hand,
1014 quantity_reserved: i.quantity_reserved,
1015 quantity_available: i.quantity_available,
1016 reorder_point: i.reorder_point,
1017 reorder_quantity: i.reorder_quantity,
1018 location_id: i.location_id.map(|id| id.to_string()),
1019 }
1020 }
1021}
1022
1023#[php_class(name = "StateSet\\Inventory")]
1024#[derive(Clone)]
1025pub struct Inventory {
1026 commerce: Arc<Mutex<RustCommerce>>,
1027}
1028
1029#[php_impl]
1030impl Inventory {
1031 pub fn create(
1032 &self,
1033 sku: String,
1034 quantity: i32,
1035 reorder_point: Option<i32>,
1036 reorder_quantity: Option<i32>,
1037 ) -> PhpResult<InventoryItem> {
1038 let commerce = lock_commerce!(self.commerce);
1039
1040 let item = commerce
1041 .inventory()
1042 .create(stateset_core::CreateInventoryItem {
1043 sku,
1044 quantity_on_hand: quantity,
1045 reorder_point,
1046 reorder_quantity,
1047 ..Default::default()
1048 })
1049 .map_err(|e| PhpException::default(format!("Failed to create inventory: {}", e)))?;
1050
1051 Ok(item.into())
1052 }
1053
1054 pub fn get(&self, id: String) -> PhpResult<Option<InventoryItem>> {
1055 let commerce = lock_commerce!(self.commerce);
1056 let uuid = parse_uuid!(id, "inventory");
1057
1058 let item = commerce
1059 .inventory()
1060 .get(uuid)
1061 .map_err(|e| PhpException::default(format!("Failed to get inventory: {}", e)))?;
1062
1063 Ok(item.map(|i| i.into()))
1064 }
1065
1066 pub fn get_by_sku(&self, sku: String) -> PhpResult<Option<InventoryItem>> {
1067 let commerce = lock_commerce!(self.commerce);
1068
1069 let item = commerce
1070 .inventory()
1071 .get_by_sku(&sku)
1072 .map_err(|e| PhpException::default(format!("Failed to get inventory: {}", e)))?;
1073
1074 Ok(item.map(|i| i.into()))
1075 }
1076
1077 pub fn list(&self) -> PhpResult<Vec<InventoryItem>> {
1078 let commerce = lock_commerce!(self.commerce);
1079
1080 let items = commerce
1081 .inventory()
1082 .list(Default::default())
1083 .map_err(|e| PhpException::default(format!("Failed to list inventory: {}", e)))?;
1084
1085 Ok(items.into_iter().map(|i| i.into()).collect())
1086 }
1087
1088 pub fn adjust(
1089 &self,
1090 id: String,
1091 adjustment: i32,
1092 reason: Option<String>,
1093 ) -> PhpResult<InventoryItem> {
1094 let commerce = lock_commerce!(self.commerce);
1095 let uuid = parse_uuid!(id, "inventory");
1096
1097 let item = commerce
1098 .inventory()
1099 .adjust(uuid, adjustment, reason)
1100 .map_err(|e| PhpException::default(format!("Failed to adjust inventory: {}", e)))?;
1101
1102 Ok(item.into())
1103 }
1104
1105 pub fn reserve(
1106 &self,
1107 id: String,
1108 quantity: i32,
1109 order_id: Option<String>,
1110 ) -> PhpResult<InventoryItem> {
1111 let commerce = lock_commerce!(self.commerce);
1112 let uuid = parse_uuid!(id, "inventory");
1113 let order_uuid = order_id.and_then(|s| s.parse().ok());
1114
1115 let item = commerce
1116 .inventory()
1117 .reserve(uuid, quantity, order_uuid)
1118 .map_err(|e| PhpException::default(format!("Failed to reserve inventory: {}", e)))?;
1119
1120 Ok(item.into())
1121 }
1122
1123 pub fn release(&self, id: String, quantity: i32) -> PhpResult<InventoryItem> {
1124 let commerce = lock_commerce!(self.commerce);
1125 let uuid = parse_uuid!(id, "inventory");
1126
1127 let item = commerce
1128 .inventory()
1129 .release(uuid, quantity)
1130 .map_err(|e| PhpException::default(format!("Failed to release inventory: {}", e)))?;
1131
1132 Ok(item.into())
1133 }
1134}
1135
1136#[php_class(name = "StateSet\\ReturnRequest")]
1141#[derive(Clone)]
1142pub struct Return {
1143 id: String,
1144 order_id: String,
1145 customer_id: String,
1146 status: String,
1147 reason: String,
1148 refund_amount: f64,
1149 created_at: String,
1150 updated_at: String,
1151}
1152
1153#[php_impl]
1154impl Return {
1155 #[getter]
1156 pub fn get_id(&self) -> String {
1157 self.id.clone()
1158 }
1159
1160 #[getter]
1161 pub fn get_order_id(&self) -> String {
1162 self.order_id.clone()
1163 }
1164
1165 #[getter]
1166 pub fn get_customer_id(&self) -> String {
1167 self.customer_id.clone()
1168 }
1169
1170 #[getter]
1171 pub fn get_status(&self) -> String {
1172 self.status.clone()
1173 }
1174
1175 #[getter]
1176 pub fn get_reason(&self) -> String {
1177 self.reason.clone()
1178 }
1179
1180 #[getter]
1181 pub fn get_refund_amount(&self) -> f64 {
1182 self.refund_amount
1183 }
1184
1185 #[getter]
1186 pub fn get_created_at(&self) -> String {
1187 self.created_at.clone()
1188 }
1189
1190 #[getter]
1191 pub fn get_updated_at(&self) -> String {
1192 self.updated_at.clone()
1193 }
1194
1195 pub fn __to_string(&self) -> String {
1196 format!("Return(id={}, status={}, refund={})", self.id, self.status, self.refund_amount)
1197 }
1198}
1199
1200impl From<stateset_core::Return> for Return {
1201 fn from(r: stateset_core::Return) -> Self {
1202 Self {
1203 id: r.id.to_string(),
1204 order_id: r.order_id.to_string(),
1205 customer_id: r.customer_id.to_string(),
1206 status: format!("{}", r.status),
1207 reason: r.reason,
1208 refund_amount: to_f64_or_nan(r.refund_amount),
1209 created_at: r.created_at.to_rfc3339(),
1210 updated_at: r.updated_at.to_rfc3339(),
1211 }
1212 }
1213}
1214
1215#[php_class(name = "StateSet\\Returns")]
1216#[derive(Clone)]
1217pub struct Returns {
1218 commerce: Arc<Mutex<RustCommerce>>,
1219}
1220
1221#[php_impl]
1222impl Returns {
1223 pub fn create(&self, order_id: String, reason: String) -> PhpResult<Return> {
1224 let commerce = lock_commerce!(self.commerce);
1225 let uuid = parse_uuid!(order_id, "order");
1226
1227 let ret = commerce
1228 .returns()
1229 .create(stateset_core::CreateReturn { order_id: uuid, reason, ..Default::default() })
1230 .map_err(|e| PhpException::default(format!("Failed to create return: {}", e)))?;
1231
1232 Ok(ret.into())
1233 }
1234
1235 pub fn get(&self, id: String) -> PhpResult<Option<Return>> {
1236 let commerce = lock_commerce!(self.commerce);
1237 let uuid = parse_uuid!(id, "return");
1238
1239 let ret = commerce
1240 .returns()
1241 .get(uuid)
1242 .map_err(|e| PhpException::default(format!("Failed to get return: {}", e)))?;
1243
1244 Ok(ret.map(|r| r.into()))
1245 }
1246
1247 pub fn list(&self) -> PhpResult<Vec<Return>> {
1248 let commerce = lock_commerce!(self.commerce);
1249
1250 let returns = commerce
1251 .returns()
1252 .list(Default::default())
1253 .map_err(|e| PhpException::default(format!("Failed to list returns: {}", e)))?;
1254
1255 Ok(returns.into_iter().map(|r| r.into()).collect())
1256 }
1257
1258 pub fn approve(&self, id: String, refund_amount: Option<f64>) -> PhpResult<Return> {
1259 let commerce = lock_commerce!(self.commerce);
1260 let uuid = parse_uuid!(id, "return");
1261 let amount = refund_amount.map(|a| Decimal::from_f64_retain(a).unwrap_or_default());
1262
1263 let ret = commerce
1264 .returns()
1265 .approve(uuid, amount)
1266 .map_err(|e| PhpException::default(format!("Failed to approve return: {}", e)))?;
1267
1268 Ok(ret.into())
1269 }
1270
1271 pub fn reject(&self, id: String, reason: Option<String>) -> PhpResult<Return> {
1272 let commerce = lock_commerce!(self.commerce);
1273 let uuid = parse_uuid!(id, "return");
1274
1275 let ret = commerce
1276 .returns()
1277 .reject(uuid, reason)
1278 .map_err(|e| PhpException::default(format!("Failed to reject return: {}", e)))?;
1279
1280 Ok(ret.into())
1281 }
1282}
1283
1284#[php_class(name = "StateSet\\Payments")]
1289#[derive(Clone)]
1290pub struct Payments {
1291 commerce: Arc<Mutex<RustCommerce>>,
1292}
1293
1294#[php_impl]
1295impl Payments {
1296 pub fn record(&self, order_id: String, amount: f64, method: Option<String>) -> PhpResult<bool> {
1297 let commerce = lock_commerce!(self.commerce);
1298 let uuid = parse_uuid!(order_id, "order");
1299 let decimal_amount = Decimal::from_f64_retain(amount).unwrap_or_default();
1300
1301 commerce
1302 .payments()
1303 .record(uuid, decimal_amount, method)
1304 .map_err(|e| PhpException::default(format!("Failed to record payment: {}", e)))?;
1305
1306 Ok(true)
1307 }
1308}
1309
1310#[php_class(name = "StateSet\\CartItem")]
1315#[derive(Clone)]
1316pub struct CartItem {
1317 id: String,
1318 sku: String,
1319 name: String,
1320 quantity: i32,
1321 unit_price: f64,
1322 total: f64,
1323}
1324
1325#[php_impl]
1326impl CartItem {
1327 #[getter]
1328 pub fn get_id(&self) -> String {
1329 self.id.clone()
1330 }
1331
1332 #[getter]
1333 pub fn get_sku(&self) -> String {
1334 self.sku.clone()
1335 }
1336
1337 #[getter]
1338 pub fn get_name(&self) -> String {
1339 self.name.clone()
1340 }
1341
1342 #[getter]
1343 pub fn get_quantity(&self) -> i32 {
1344 self.quantity
1345 }
1346
1347 #[getter]
1348 pub fn get_unit_price(&self) -> f64 {
1349 self.unit_price
1350 }
1351
1352 #[getter]
1353 pub fn get_total(&self) -> f64 {
1354 self.total
1355 }
1356}
1357
1358#[php_class(name = "StateSet\\Cart")]
1359#[derive(Clone)]
1360pub struct Cart {
1361 id: String,
1362 customer_id: Option<String>,
1363 status: String,
1364 items: Vec<CartItem>,
1365 subtotal: f64,
1366 total: f64,
1367 currency: String,
1368 created_at: String,
1369 updated_at: String,
1370}
1371
1372#[php_impl]
1373impl Cart {
1374 #[getter]
1375 pub fn get_id(&self) -> String {
1376 self.id.clone()
1377 }
1378
1379 #[getter]
1380 pub fn get_customer_id(&self) -> Option<String> {
1381 self.customer_id.clone()
1382 }
1383
1384 #[getter]
1385 pub fn get_status(&self) -> String {
1386 self.status.clone()
1387 }
1388
1389 #[getter]
1390 pub fn get_items(&self) -> Vec<CartItem> {
1391 self.items.clone()
1392 }
1393
1394 #[getter]
1395 pub fn get_subtotal(&self) -> f64 {
1396 self.subtotal
1397 }
1398
1399 #[getter]
1400 pub fn get_total(&self) -> f64 {
1401 self.total
1402 }
1403
1404 #[getter]
1405 pub fn get_currency(&self) -> String {
1406 self.currency.clone()
1407 }
1408
1409 #[getter]
1410 pub fn get_created_at(&self) -> String {
1411 self.created_at.clone()
1412 }
1413
1414 #[getter]
1415 pub fn get_updated_at(&self) -> String {
1416 self.updated_at.clone()
1417 }
1418
1419 pub fn __to_string(&self) -> String {
1420 format!(
1421 "Cart(id={}, items={}, total={} {})",
1422 self.id,
1423 self.items.len(),
1424 self.total,
1425 self.currency
1426 )
1427 }
1428}
1429
1430impl From<stateset_core::Cart> for Cart {
1431 fn from(c: stateset_core::Cart) -> Self {
1432 Self {
1433 id: c.id.to_string(),
1434 customer_id: c.customer_id.map(|id| id.to_string()),
1435 status: format!("{}", c.status),
1436 items: c
1437 .items
1438 .into_iter()
1439 .map(|i| CartItem {
1440 id: i.id.to_string(),
1441 sku: i.sku,
1442 name: i.name,
1443 quantity: i.quantity,
1444 unit_price: to_f64_or_nan(i.unit_price),
1445 total: to_f64_or_nan(i.total),
1446 })
1447 .collect(),
1448 subtotal: to_f64_or_nan(c.subtotal),
1449 total: to_f64_or_nan(c.total),
1450 currency: c.currency,
1451 created_at: c.created_at.to_rfc3339(),
1452 updated_at: c.updated_at.to_rfc3339(),
1453 }
1454 }
1455}
1456
1457#[php_class(name = "StateSet\\Carts")]
1458#[derive(Clone)]
1459pub struct Carts {
1460 commerce: Arc<Mutex<RustCommerce>>,
1461}
1462
1463#[php_impl]
1464impl Carts {
1465 pub fn create(&self, customer_id: Option<String>, currency: Option<String>) -> PhpResult<Cart> {
1466 let commerce = lock_commerce!(self.commerce);
1467 let cust_uuid = customer_id.and_then(|s| s.parse().ok());
1468
1469 let cart = commerce
1470 .carts()
1471 .create(stateset_core::CreateCart {
1472 customer_id: cust_uuid,
1473 currency,
1474 ..Default::default()
1475 })
1476 .map_err(|e| PhpException::default(format!("Failed to create cart: {}", e)))?;
1477
1478 Ok(cart.into())
1479 }
1480
1481 pub fn get(&self, id: String) -> PhpResult<Option<Cart>> {
1482 let commerce = lock_commerce!(self.commerce);
1483 let uuid = parse_uuid!(id, "cart");
1484
1485 let cart = commerce
1486 .carts()
1487 .get(uuid)
1488 .map_err(|e| PhpException::default(format!("Failed to get cart: {}", e)))?;
1489
1490 Ok(cart.map(|c| c.into()))
1491 }
1492
1493 pub fn list(&self) -> PhpResult<Vec<Cart>> {
1494 let commerce = lock_commerce!(self.commerce);
1495
1496 let carts = commerce
1497 .carts()
1498 .list(Default::default())
1499 .map_err(|e| PhpException::default(format!("Failed to list carts: {}", e)))?;
1500
1501 Ok(carts.into_iter().map(|c| c.into()).collect())
1502 }
1503
1504 pub fn add_item(
1505 &self,
1506 cart_id: String,
1507 sku: String,
1508 name: String,
1509 quantity: i32,
1510 unit_price: f64,
1511 ) -> PhpResult<Cart> {
1512 let commerce = lock_commerce!(self.commerce);
1513 let uuid = parse_uuid!(cart_id, "cart");
1514 let price = Decimal::from_f64_retain(unit_price).unwrap_or_default();
1515
1516 let cart = commerce
1517 .carts()
1518 .add_item(
1519 uuid,
1520 stateset_core::AddCartItem {
1521 sku,
1522 name,
1523 quantity,
1524 unit_price: price,
1525 ..Default::default()
1526 },
1527 )
1528 .map_err(|e| PhpException::default(format!("Failed to add item: {}", e)))?;
1529
1530 Ok(cart.into())
1531 }
1532
1533 pub fn checkout(&self, cart_id: String) -> PhpResult<Order> {
1534 let commerce = lock_commerce!(self.commerce);
1535 let uuid = parse_uuid!(cart_id, "cart");
1536
1537 let order = commerce
1538 .carts()
1539 .checkout(uuid)
1540 .map_err(|e| PhpException::default(format!("Failed to checkout: {}", e)))?;
1541
1542 Ok(order.into())
1543 }
1544}
1545
1546#[php_class(name = "StateSet\\SalesSummary")]
1551#[derive(Clone)]
1552pub struct SalesSummary {
1553 total_revenue: f64,
1554 total_orders: i64,
1555 average_order_value: f64,
1556 total_items_sold: i64,
1557}
1558
1559#[php_impl]
1560impl SalesSummary {
1561 #[getter]
1562 pub fn get_total_revenue(&self) -> f64 {
1563 self.total_revenue
1564 }
1565
1566 #[getter]
1567 pub fn get_total_orders(&self) -> i64 {
1568 self.total_orders
1569 }
1570
1571 #[getter]
1572 pub fn get_average_order_value(&self) -> f64 {
1573 self.average_order_value
1574 }
1575
1576 #[getter]
1577 pub fn get_total_items_sold(&self) -> i64 {
1578 self.total_items_sold
1579 }
1580}
1581
1582#[php_class(name = "StateSet\\Analytics")]
1583#[derive(Clone)]
1584pub struct Analytics {
1585 commerce: Arc<Mutex<RustCommerce>>,
1586}
1587
1588#[php_impl]
1589impl Analytics {
1590 pub fn sales_summary(&self, days: Option<i64>) -> PhpResult<SalesSummary> {
1591 let commerce = lock_commerce!(self.commerce);
1592
1593 let summary = commerce
1594 .analytics()
1595 .sales_summary(days.unwrap_or(30))
1596 .map_err(|e| PhpException::default(format!("Failed to get sales summary: {}", e)))?;
1597
1598 Ok(SalesSummary {
1599 total_revenue: to_f64_or_nan(summary.total_revenue),
1600 total_orders: summary.total_orders,
1601 average_order_value: to_f64_or_nan(summary.average_order_value),
1602 total_items_sold: summary.total_items_sold,
1603 })
1604 }
1605}
1606
1607#[php_class(name = "StateSet\\Shipment")]
1612#[derive(Clone)]
1613pub struct Shipment {
1614 id: String,
1615 order_id: String,
1616 tracking_number: Option<String>,
1617 carrier: Option<String>,
1618 status: String,
1619 shipped_at: Option<String>,
1620 delivered_at: Option<String>,
1621 created_at: String,
1622 updated_at: String,
1623}
1624
1625#[php_impl]
1626impl Shipment {
1627 #[getter]
1628 pub fn get_id(&self) -> String {
1629 self.id.clone()
1630 }
1631
1632 #[getter]
1633 pub fn get_order_id(&self) -> String {
1634 self.order_id.clone()
1635 }
1636
1637 #[getter]
1638 pub fn get_tracking_number(&self) -> Option<String> {
1639 self.tracking_number.clone()
1640 }
1641
1642 #[getter]
1643 pub fn get_carrier(&self) -> Option<String> {
1644 self.carrier.clone()
1645 }
1646
1647 #[getter]
1648 pub fn get_status(&self) -> String {
1649 self.status.clone()
1650 }
1651
1652 #[getter]
1653 pub fn get_shipped_at(&self) -> Option<String> {
1654 self.shipped_at.clone()
1655 }
1656
1657 #[getter]
1658 pub fn get_delivered_at(&self) -> Option<String> {
1659 self.delivered_at.clone()
1660 }
1661
1662 #[getter]
1663 pub fn get_created_at(&self) -> String {
1664 self.created_at.clone()
1665 }
1666
1667 #[getter]
1668 pub fn get_updated_at(&self) -> String {
1669 self.updated_at.clone()
1670 }
1671
1672 pub fn __to_string(&self) -> String {
1673 format!(
1674 "Shipment(id={}, status={}, tracking={:?})",
1675 self.id, self.status, self.tracking_number
1676 )
1677 }
1678}
1679
1680impl From<stateset_core::Shipment> for Shipment {
1681 fn from(s: stateset_core::Shipment) -> Self {
1682 Self {
1683 id: s.id.to_string(),
1684 order_id: s.order_id.to_string(),
1685 tracking_number: s.tracking_number,
1686 carrier: s.carrier,
1687 status: format!("{}", s.status),
1688 shipped_at: s.shipped_at.map(|t| t.to_rfc3339()),
1689 delivered_at: s.delivered_at.map(|t| t.to_rfc3339()),
1690 created_at: s.created_at.to_rfc3339(),
1691 updated_at: s.updated_at.to_rfc3339(),
1692 }
1693 }
1694}
1695
1696#[php_class(name = "StateSet\\Shipments")]
1697#[derive(Clone)]
1698pub struct Shipments {
1699 commerce: Arc<Mutex<RustCommerce>>,
1700}
1701
1702#[php_impl]
1703impl Shipments {
1704 pub fn create(
1705 &self,
1706 order_id: String,
1707 tracking_number: Option<String>,
1708 carrier: Option<String>,
1709 ) -> PhpResult<Shipment> {
1710 let commerce = lock_commerce!(self.commerce);
1711 let uuid = parse_uuid!(order_id, "order");
1712
1713 let shipment = commerce
1714 .shipments()
1715 .create(stateset_core::CreateShipment {
1716 order_id: uuid,
1717 tracking_number,
1718 carrier,
1719 ..Default::default()
1720 })
1721 .map_err(|e| PhpException::default(format!("Failed to create shipment: {}", e)))?;
1722
1723 Ok(shipment.into())
1724 }
1725
1726 pub fn get(&self, id: String) -> PhpResult<Option<Shipment>> {
1727 let commerce = lock_commerce!(self.commerce);
1728 let uuid = parse_uuid!(id, "shipment");
1729
1730 let shipment = commerce
1731 .shipments()
1732 .get(uuid)
1733 .map_err(|e| PhpException::default(format!("Failed to get shipment: {}", e)))?;
1734
1735 Ok(shipment.map(|s| s.into()))
1736 }
1737
1738 pub fn get_by_tracking(&self, tracking_number: String) -> PhpResult<Option<Shipment>> {
1739 let commerce = lock_commerce!(self.commerce);
1740
1741 let shipment = commerce
1742 .shipments()
1743 .get_by_tracking(&tracking_number)
1744 .map_err(|e| PhpException::default(format!("Failed to get shipment: {}", e)))?;
1745
1746 Ok(shipment.map(|s| s.into()))
1747 }
1748
1749 pub fn list(&self) -> PhpResult<Vec<Shipment>> {
1750 let commerce = lock_commerce!(self.commerce);
1751
1752 let shipments = commerce
1753 .shipments()
1754 .list(Default::default())
1755 .map_err(|e| PhpException::default(format!("Failed to list shipments: {}", e)))?;
1756
1757 Ok(shipments.into_iter().map(|s| s.into()).collect())
1758 }
1759
1760 pub fn for_order(&self, order_id: String) -> PhpResult<Vec<Shipment>> {
1761 let commerce = lock_commerce!(self.commerce);
1762 let uuid = parse_uuid!(order_id, "order");
1763
1764 let shipments = commerce
1765 .shipments()
1766 .for_order(uuid)
1767 .map_err(|e| PhpException::default(format!("Failed to get shipments: {}", e)))?;
1768
1769 Ok(shipments.into_iter().map(|s| s.into()).collect())
1770 }
1771
1772 pub fn ship(&self, id: String) -> PhpResult<Shipment> {
1773 let commerce = lock_commerce!(self.commerce);
1774 let uuid = parse_uuid!(id, "shipment");
1775
1776 let shipment = commerce
1777 .shipments()
1778 .ship(uuid)
1779 .map_err(|e| PhpException::default(format!("Failed to ship: {}", e)))?;
1780
1781 Ok(shipment.into())
1782 }
1783
1784 pub fn mark_delivered(&self, id: String) -> PhpResult<Shipment> {
1785 let commerce = lock_commerce!(self.commerce);
1786 let uuid = parse_uuid!(id, "shipment");
1787
1788 let shipment = commerce
1789 .shipments()
1790 .mark_delivered(uuid)
1791 .map_err(|e| PhpException::default(format!("Failed to mark delivered: {}", e)))?;
1792
1793 Ok(shipment.into())
1794 }
1795
1796 pub fn cancel(&self, id: String) -> PhpResult<Shipment> {
1797 let commerce = lock_commerce!(self.commerce);
1798 let uuid = parse_uuid!(id, "shipment");
1799
1800 let shipment = commerce
1801 .shipments()
1802 .cancel(uuid)
1803 .map_err(|e| PhpException::default(format!("Failed to cancel shipment: {}", e)))?;
1804
1805 Ok(shipment.into())
1806 }
1807
1808 pub fn count(&self) -> PhpResult<i64> {
1809 let commerce = lock_commerce!(self.commerce);
1810
1811 let count = commerce
1812 .shipments()
1813 .count(Default::default())
1814 .map_err(|e| PhpException::default(format!("Failed to count shipments: {}", e)))?;
1815
1816 Ok(count)
1817 }
1818}
1819
1820#[php_class(name = "StateSet\\Warranty")]
1825#[derive(Clone)]
1826pub struct Warranty {
1827 id: String,
1828 product_id: String,
1829 order_id: Option<String>,
1830 customer_id: String,
1831 warranty_type: String,
1832 status: String,
1833 start_date: String,
1834 end_date: String,
1835 created_at: String,
1836}
1837
1838#[php_impl]
1839impl Warranty {
1840 #[getter]
1841 pub fn get_id(&self) -> String {
1842 self.id.clone()
1843 }
1844
1845 #[getter]
1846 pub fn get_product_id(&self) -> String {
1847 self.product_id.clone()
1848 }
1849
1850 #[getter]
1851 pub fn get_order_id(&self) -> Option<String> {
1852 self.order_id.clone()
1853 }
1854
1855 #[getter]
1856 pub fn get_customer_id(&self) -> String {
1857 self.customer_id.clone()
1858 }
1859
1860 #[getter]
1861 pub fn get_warranty_type(&self) -> String {
1862 self.warranty_type.clone()
1863 }
1864
1865 #[getter]
1866 pub fn get_status(&self) -> String {
1867 self.status.clone()
1868 }
1869
1870 #[getter]
1871 pub fn get_start_date(&self) -> String {
1872 self.start_date.clone()
1873 }
1874
1875 #[getter]
1876 pub fn get_end_date(&self) -> String {
1877 self.end_date.clone()
1878 }
1879
1880 #[getter]
1881 pub fn get_created_at(&self) -> String {
1882 self.created_at.clone()
1883 }
1884
1885 pub fn __to_string(&self) -> String {
1886 format!("Warranty(id={}, type={}, status={})", self.id, self.warranty_type, self.status)
1887 }
1888}
1889
1890impl From<stateset_core::Warranty> for Warranty {
1891 fn from(w: stateset_core::Warranty) -> Self {
1892 Self {
1893 id: w.id.to_string(),
1894 product_id: w.product_id.to_string(),
1895 order_id: w.order_id.map(|id| id.to_string()),
1896 customer_id: w.customer_id.to_string(),
1897 warranty_type: format!("{}", w.warranty_type),
1898 status: format!("{}", w.status),
1899 start_date: w.start_date.to_rfc3339(),
1900 end_date: w.end_date.to_rfc3339(),
1901 created_at: w.created_at.to_rfc3339(),
1902 }
1903 }
1904}
1905
1906#[php_class(name = "StateSet\\WarrantyClaim")]
1907#[derive(Clone)]
1908pub struct WarrantyClaim {
1909 id: String,
1910 warranty_id: String,
1911 description: String,
1912 status: String,
1913 resolution: Option<String>,
1914 created_at: String,
1915}
1916
1917#[php_impl]
1918impl WarrantyClaim {
1919 #[getter]
1920 pub fn get_id(&self) -> String {
1921 self.id.clone()
1922 }
1923
1924 #[getter]
1925 pub fn get_warranty_id(&self) -> String {
1926 self.warranty_id.clone()
1927 }
1928
1929 #[getter]
1930 pub fn get_description(&self) -> String {
1931 self.description.clone()
1932 }
1933
1934 #[getter]
1935 pub fn get_status(&self) -> String {
1936 self.status.clone()
1937 }
1938
1939 #[getter]
1940 pub fn get_resolution(&self) -> Option<String> {
1941 self.resolution.clone()
1942 }
1943
1944 #[getter]
1945 pub fn get_created_at(&self) -> String {
1946 self.created_at.clone()
1947 }
1948}
1949
1950impl From<stateset_core::WarrantyClaim> for WarrantyClaim {
1951 fn from(c: stateset_core::WarrantyClaim) -> Self {
1952 Self {
1953 id: c.id.to_string(),
1954 warranty_id: c.warranty_id.to_string(),
1955 description: c.description,
1956 status: format!("{}", c.status),
1957 resolution: c.resolution,
1958 created_at: c.created_at.to_rfc3339(),
1959 }
1960 }
1961}
1962
1963#[php_class(name = "StateSet\\Warranties")]
1964#[derive(Clone)]
1965pub struct Warranties {
1966 commerce: Arc<Mutex<RustCommerce>>,
1967}
1968
1969#[php_impl]
1970impl Warranties {
1971 pub fn create(
1972 &self,
1973 product_id: String,
1974 customer_id: String,
1975 warranty_type: String,
1976 duration_months: i32,
1977 ) -> PhpResult<Warranty> {
1978 let commerce = lock_commerce!(self.commerce);
1979 let prod_uuid = parse_uuid!(product_id, "product");
1980 let cust_uuid = parse_uuid!(customer_id, "customer");
1981
1982 let warranty = commerce
1983 .warranties()
1984 .create(stateset_core::CreateWarranty {
1985 product_id: prod_uuid,
1986 customer_id: cust_uuid,
1987 warranty_type,
1988 duration_months,
1989 ..Default::default()
1990 })
1991 .map_err(|e| PhpException::default(format!("Failed to create warranty: {}", e)))?;
1992
1993 Ok(warranty.into())
1994 }
1995
1996 pub fn get(&self, id: String) -> PhpResult<Option<Warranty>> {
1997 let commerce = lock_commerce!(self.commerce);
1998 let uuid = parse_uuid!(id, "warranty");
1999
2000 let warranty = commerce
2001 .warranties()
2002 .get(uuid)
2003 .map_err(|e| PhpException::default(format!("Failed to get warranty: {}", e)))?;
2004
2005 Ok(warranty.map(|w| w.into()))
2006 }
2007
2008 pub fn list(&self) -> PhpResult<Vec<Warranty>> {
2009 let commerce = lock_commerce!(self.commerce);
2010
2011 let warranties = commerce
2012 .warranties()
2013 .list(Default::default())
2014 .map_err(|e| PhpException::default(format!("Failed to list warranties: {}", e)))?;
2015
2016 Ok(warranties.into_iter().map(|w| w.into()).collect())
2017 }
2018
2019 pub fn for_customer(&self, customer_id: String) -> PhpResult<Vec<Warranty>> {
2020 let commerce = lock_commerce!(self.commerce);
2021 let uuid = parse_uuid!(customer_id, "customer");
2022
2023 let warranties = commerce
2024 .warranties()
2025 .for_customer(uuid)
2026 .map_err(|e| PhpException::default(format!("Failed to get warranties: {}", e)))?;
2027
2028 Ok(warranties.into_iter().map(|w| w.into()).collect())
2029 }
2030
2031 pub fn is_valid(&self, id: String) -> PhpResult<bool> {
2032 let commerce = lock_commerce!(self.commerce);
2033 let uuid = parse_uuid!(id, "warranty");
2034
2035 let valid = commerce
2036 .warranties()
2037 .is_valid(uuid)
2038 .map_err(|e| PhpException::default(format!("Failed to check warranty: {}", e)))?;
2039
2040 Ok(valid)
2041 }
2042
2043 pub fn create_claim(
2044 &self,
2045 warranty_id: String,
2046 description: String,
2047 ) -> PhpResult<WarrantyClaim> {
2048 let commerce = lock_commerce!(self.commerce);
2049 let uuid = parse_uuid!(warranty_id, "warranty");
2050
2051 let claim = commerce
2052 .warranties()
2053 .create_claim(stateset_core::CreateWarrantyClaim {
2054 warranty_id: uuid,
2055 description,
2056 ..Default::default()
2057 })
2058 .map_err(|e| PhpException::default(format!("Failed to create claim: {}", e)))?;
2059
2060 Ok(claim.into())
2061 }
2062
2063 pub fn approve_claim(
2064 &self,
2065 claim_id: String,
2066 resolution: Option<String>,
2067 ) -> PhpResult<WarrantyClaim> {
2068 let commerce = lock_commerce!(self.commerce);
2069 let uuid = parse_uuid!(claim_id, "claim");
2070
2071 let claim = commerce
2072 .warranties()
2073 .approve_claim(uuid, resolution)
2074 .map_err(|e| PhpException::default(format!("Failed to approve claim: {}", e)))?;
2075
2076 Ok(claim.into())
2077 }
2078
2079 pub fn deny_claim(&self, claim_id: String, reason: Option<String>) -> PhpResult<WarrantyClaim> {
2080 let commerce = lock_commerce!(self.commerce);
2081 let uuid = parse_uuid!(claim_id, "claim");
2082
2083 let claim = commerce
2084 .warranties()
2085 .deny_claim(uuid, reason)
2086 .map_err(|e| PhpException::default(format!("Failed to deny claim: {}", e)))?;
2087
2088 Ok(claim.into())
2089 }
2090
2091 pub fn count(&self) -> PhpResult<i64> {
2092 let commerce = lock_commerce!(self.commerce);
2093
2094 let count = commerce
2095 .warranties()
2096 .count(Default::default())
2097 .map_err(|e| PhpException::default(format!("Failed to count warranties: {}", e)))?;
2098
2099 Ok(count)
2100 }
2101}
2102
2103#[php_class(name = "StateSet\\Supplier")]
2108#[derive(Clone)]
2109pub struct Supplier {
2110 id: String,
2111 name: String,
2112 email: Option<String>,
2113 phone: Option<String>,
2114 status: String,
2115 created_at: String,
2116}
2117
2118#[php_impl]
2119impl Supplier {
2120 #[getter]
2121 pub fn get_id(&self) -> String {
2122 self.id.clone()
2123 }
2124
2125 #[getter]
2126 pub fn get_name(&self) -> String {
2127 self.name.clone()
2128 }
2129
2130 #[getter]
2131 pub fn get_email(&self) -> Option<String> {
2132 self.email.clone()
2133 }
2134
2135 #[getter]
2136 pub fn get_phone(&self) -> Option<String> {
2137 self.phone.clone()
2138 }
2139
2140 #[getter]
2141 pub fn get_status(&self) -> String {
2142 self.status.clone()
2143 }
2144
2145 #[getter]
2146 pub fn get_created_at(&self) -> String {
2147 self.created_at.clone()
2148 }
2149}
2150
2151impl From<stateset_core::Supplier> for Supplier {
2152 fn from(s: stateset_core::Supplier) -> Self {
2153 Self {
2154 id: s.id.to_string(),
2155 name: s.name,
2156 email: s.email,
2157 phone: s.phone,
2158 status: format!("{}", s.status),
2159 created_at: s.created_at.to_rfc3339(),
2160 }
2161 }
2162}
2163
2164#[php_class(name = "StateSet\\PurchaseOrder")]
2165#[derive(Clone)]
2166pub struct PurchaseOrder {
2167 id: String,
2168 po_number: String,
2169 supplier_id: String,
2170 status: String,
2171 total_amount: f64,
2172 currency: String,
2173 expected_date: Option<String>,
2174 created_at: String,
2175 updated_at: String,
2176}
2177
2178#[php_impl]
2179impl PurchaseOrder {
2180 #[getter]
2181 pub fn get_id(&self) -> String {
2182 self.id.clone()
2183 }
2184
2185 #[getter]
2186 pub fn get_po_number(&self) -> String {
2187 self.po_number.clone()
2188 }
2189
2190 #[getter]
2191 pub fn get_supplier_id(&self) -> String {
2192 self.supplier_id.clone()
2193 }
2194
2195 #[getter]
2196 pub fn get_status(&self) -> String {
2197 self.status.clone()
2198 }
2199
2200 #[getter]
2201 pub fn get_total_amount(&self) -> f64 {
2202 self.total_amount
2203 }
2204
2205 #[getter]
2206 pub fn get_currency(&self) -> String {
2207 self.currency.clone()
2208 }
2209
2210 #[getter]
2211 pub fn get_expected_date(&self) -> Option<String> {
2212 self.expected_date.clone()
2213 }
2214
2215 #[getter]
2216 pub fn get_created_at(&self) -> String {
2217 self.created_at.clone()
2218 }
2219
2220 #[getter]
2221 pub fn get_updated_at(&self) -> String {
2222 self.updated_at.clone()
2223 }
2224
2225 pub fn __to_string(&self) -> String {
2226 format!(
2227 "PurchaseOrder(number={}, status={}, total={})",
2228 self.po_number, self.status, self.total_amount
2229 )
2230 }
2231}
2232
2233impl From<stateset_core::PurchaseOrder> for PurchaseOrder {
2234 fn from(p: stateset_core::PurchaseOrder) -> Self {
2235 Self {
2236 id: p.id.to_string(),
2237 po_number: p.po_number,
2238 supplier_id: p.supplier_id.to_string(),
2239 status: format!("{}", p.status),
2240 total_amount: to_f64_or_nan(p.total_amount),
2241 currency: p.currency,
2242 expected_date: p.expected_date.map(|d| d.to_rfc3339()),
2243 created_at: p.created_at.to_rfc3339(),
2244 updated_at: p.updated_at.to_rfc3339(),
2245 }
2246 }
2247}
2248
2249#[php_class(name = "StateSet\\PurchaseOrders")]
2250#[derive(Clone)]
2251pub struct PurchaseOrders {
2252 commerce: Arc<Mutex<RustCommerce>>,
2253}
2254
2255#[php_impl]
2256impl PurchaseOrders {
2257 pub fn create_supplier(
2258 &self,
2259 name: String,
2260 email: Option<String>,
2261 phone: Option<String>,
2262 ) -> PhpResult<Supplier> {
2263 let commerce = lock_commerce!(self.commerce);
2264
2265 let supplier = commerce
2266 .purchase_orders()
2267 .create_supplier(stateset_core::CreateSupplier {
2268 name,
2269 email,
2270 phone,
2271 ..Default::default()
2272 })
2273 .map_err(|e| PhpException::default(format!("Failed to create supplier: {}", e)))?;
2274
2275 Ok(supplier.into())
2276 }
2277
2278 pub fn get_supplier(&self, id: String) -> PhpResult<Option<Supplier>> {
2279 let commerce = lock_commerce!(self.commerce);
2280 let uuid = parse_uuid!(id, "supplier");
2281
2282 let supplier = commerce
2283 .purchase_orders()
2284 .get_supplier(uuid)
2285 .map_err(|e| PhpException::default(format!("Failed to get supplier: {}", e)))?;
2286
2287 Ok(supplier.map(|s| s.into()))
2288 }
2289
2290 pub fn list_suppliers(&self) -> PhpResult<Vec<Supplier>> {
2291 let commerce = lock_commerce!(self.commerce);
2292
2293 let suppliers = commerce
2294 .purchase_orders()
2295 .list_suppliers(Default::default())
2296 .map_err(|e| PhpException::default(format!("Failed to list suppliers: {}", e)))?;
2297
2298 Ok(suppliers.into_iter().map(|s| s.into()).collect())
2299 }
2300
2301 pub fn create(
2302 &self,
2303 supplier_id: String,
2304 currency: Option<String>,
2305 ) -> PhpResult<PurchaseOrder> {
2306 let commerce = lock_commerce!(self.commerce);
2307 let uuid = parse_uuid!(supplier_id, "supplier");
2308
2309 let po = commerce
2310 .purchase_orders()
2311 .create(stateset_core::CreatePurchaseOrder {
2312 supplier_id: uuid,
2313 currency,
2314 ..Default::default()
2315 })
2316 .map_err(|e| PhpException::default(format!("Failed to create PO: {}", e)))?;
2317
2318 Ok(po.into())
2319 }
2320
2321 pub fn get(&self, id: String) -> PhpResult<Option<PurchaseOrder>> {
2322 let commerce = lock_commerce!(self.commerce);
2323 let uuid = parse_uuid!(id, "purchase_order");
2324
2325 let po = commerce
2326 .purchase_orders()
2327 .get(uuid)
2328 .map_err(|e| PhpException::default(format!("Failed to get PO: {}", e)))?;
2329
2330 Ok(po.map(|p| p.into()))
2331 }
2332
2333 pub fn list(&self) -> PhpResult<Vec<PurchaseOrder>> {
2334 let commerce = lock_commerce!(self.commerce);
2335
2336 let pos = commerce
2337 .purchase_orders()
2338 .list(Default::default())
2339 .map_err(|e| PhpException::default(format!("Failed to list POs: {}", e)))?;
2340
2341 Ok(pos.into_iter().map(|p| p.into()).collect())
2342 }
2343
2344 pub fn submit(&self, id: String) -> PhpResult<PurchaseOrder> {
2345 let commerce = lock_commerce!(self.commerce);
2346 let uuid = parse_uuid!(id, "purchase_order");
2347
2348 let po = commerce
2349 .purchase_orders()
2350 .submit(uuid)
2351 .map_err(|e| PhpException::default(format!("Failed to submit PO: {}", e)))?;
2352
2353 Ok(po.into())
2354 }
2355
2356 pub fn approve(&self, id: String) -> PhpResult<PurchaseOrder> {
2357 let commerce = lock_commerce!(self.commerce);
2358 let uuid = parse_uuid!(id, "purchase_order");
2359
2360 let po = commerce
2361 .purchase_orders()
2362 .approve(uuid)
2363 .map_err(|e| PhpException::default(format!("Failed to approve PO: {}", e)))?;
2364
2365 Ok(po.into())
2366 }
2367
2368 pub fn cancel(&self, id: String) -> PhpResult<PurchaseOrder> {
2369 let commerce = lock_commerce!(self.commerce);
2370 let uuid = parse_uuid!(id, "purchase_order");
2371
2372 let po = commerce
2373 .purchase_orders()
2374 .cancel(uuid)
2375 .map_err(|e| PhpException::default(format!("Failed to cancel PO: {}", e)))?;
2376
2377 Ok(po.into())
2378 }
2379
2380 pub fn complete(&self, id: String) -> PhpResult<PurchaseOrder> {
2381 let commerce = lock_commerce!(self.commerce);
2382 let uuid = parse_uuid!(id, "purchase_order");
2383
2384 let po = commerce
2385 .purchase_orders()
2386 .complete(uuid)
2387 .map_err(|e| PhpException::default(format!("Failed to complete PO: {}", e)))?;
2388
2389 Ok(po.into())
2390 }
2391
2392 pub fn count(&self) -> PhpResult<i64> {
2393 let commerce = lock_commerce!(self.commerce);
2394
2395 let count = commerce
2396 .purchase_orders()
2397 .count(Default::default())
2398 .map_err(|e| PhpException::default(format!("Failed to count POs: {}", e)))?;
2399
2400 Ok(count)
2401 }
2402}
2403
2404#[php_class(name = "StateSet\\Invoice")]
2409#[derive(Clone)]
2410pub struct Invoice {
2411 id: String,
2412 invoice_number: String,
2413 customer_id: String,
2414 order_id: Option<String>,
2415 status: String,
2416 subtotal: f64,
2417 tax: f64,
2418 total: f64,
2419 currency: String,
2420 due_date: Option<String>,
2421 paid_at: Option<String>,
2422 created_at: String,
2423 updated_at: String,
2424}
2425
2426#[php_impl]
2427impl Invoice {
2428 #[getter]
2429 pub fn get_id(&self) -> String {
2430 self.id.clone()
2431 }
2432
2433 #[getter]
2434 pub fn get_invoice_number(&self) -> String {
2435 self.invoice_number.clone()
2436 }
2437
2438 #[getter]
2439 pub fn get_customer_id(&self) -> String {
2440 self.customer_id.clone()
2441 }
2442
2443 #[getter]
2444 pub fn get_order_id(&self) -> Option<String> {
2445 self.order_id.clone()
2446 }
2447
2448 #[getter]
2449 pub fn get_status(&self) -> String {
2450 self.status.clone()
2451 }
2452
2453 #[getter]
2454 pub fn get_subtotal(&self) -> f64 {
2455 self.subtotal
2456 }
2457
2458 #[getter]
2459 pub fn get_tax(&self) -> f64 {
2460 self.tax
2461 }
2462
2463 #[getter]
2464 pub fn get_total(&self) -> f64 {
2465 self.total
2466 }
2467
2468 #[getter]
2469 pub fn get_currency(&self) -> String {
2470 self.currency.clone()
2471 }
2472
2473 #[getter]
2474 pub fn get_due_date(&self) -> Option<String> {
2475 self.due_date.clone()
2476 }
2477
2478 #[getter]
2479 pub fn get_paid_at(&self) -> Option<String> {
2480 self.paid_at.clone()
2481 }
2482
2483 #[getter]
2484 pub fn get_created_at(&self) -> String {
2485 self.created_at.clone()
2486 }
2487
2488 #[getter]
2489 pub fn get_updated_at(&self) -> String {
2490 self.updated_at.clone()
2491 }
2492
2493 pub fn __to_string(&self) -> String {
2494 format!(
2495 "Invoice(number={}, status={}, total={})",
2496 self.invoice_number, self.status, self.total
2497 )
2498 }
2499}
2500
2501impl From<stateset_core::Invoice> for Invoice {
2502 fn from(i: stateset_core::Invoice) -> Self {
2503 Self {
2504 id: i.id.to_string(),
2505 invoice_number: i.invoice_number,
2506 customer_id: i.customer_id.to_string(),
2507 order_id: i.order_id.map(|id| id.to_string()),
2508 status: format!("{}", i.status),
2509 subtotal: to_f64_or_nan(i.subtotal),
2510 tax: to_f64_or_nan(i.tax),
2511 total: to_f64_or_nan(i.total),
2512 currency: i.currency,
2513 due_date: i.due_date.map(|d| d.to_rfc3339()),
2514 paid_at: i.paid_at.map(|d| d.to_rfc3339()),
2515 created_at: i.created_at.to_rfc3339(),
2516 updated_at: i.updated_at.to_rfc3339(),
2517 }
2518 }
2519}
2520
2521#[php_class(name = "StateSet\\Invoices")]
2522#[derive(Clone)]
2523pub struct Invoices {
2524 commerce: Arc<Mutex<RustCommerce>>,
2525}
2526
2527#[php_impl]
2528impl Invoices {
2529 pub fn create(
2530 &self,
2531 customer_id: String,
2532 order_id: Option<String>,
2533 due_days: Option<i32>,
2534 ) -> PhpResult<Invoice> {
2535 let commerce = lock_commerce!(self.commerce);
2536 let cust_uuid = parse_uuid!(customer_id, "customer");
2537 let order_uuid = order_id.and_then(|s| s.parse().ok());
2538
2539 let invoice = commerce
2540 .invoices()
2541 .create(stateset_core::CreateInvoice {
2542 customer_id: cust_uuid,
2543 order_id: order_uuid,
2544 due_days,
2545 ..Default::default()
2546 })
2547 .map_err(|e| PhpException::default(format!("Failed to create invoice: {}", e)))?;
2548
2549 Ok(invoice.into())
2550 }
2551
2552 pub fn get(&self, id: String) -> PhpResult<Option<Invoice>> {
2553 let commerce = lock_commerce!(self.commerce);
2554 let uuid = parse_uuid!(id, "invoice");
2555
2556 let invoice = commerce
2557 .invoices()
2558 .get(uuid)
2559 .map_err(|e| PhpException::default(format!("Failed to get invoice: {}", e)))?;
2560
2561 Ok(invoice.map(|i| i.into()))
2562 }
2563
2564 pub fn list(&self) -> PhpResult<Vec<Invoice>> {
2565 let commerce = lock_commerce!(self.commerce);
2566
2567 let invoices = commerce
2568 .invoices()
2569 .list(Default::default())
2570 .map_err(|e| PhpException::default(format!("Failed to list invoices: {}", e)))?;
2571
2572 Ok(invoices.into_iter().map(|i| i.into()).collect())
2573 }
2574
2575 pub fn for_customer(&self, customer_id: String) -> PhpResult<Vec<Invoice>> {
2576 let commerce = lock_commerce!(self.commerce);
2577 let uuid = parse_uuid!(customer_id, "customer");
2578
2579 let invoices = commerce
2580 .invoices()
2581 .for_customer(uuid)
2582 .map_err(|e| PhpException::default(format!("Failed to get invoices: {}", e)))?;
2583
2584 Ok(invoices.into_iter().map(|i| i.into()).collect())
2585 }
2586
2587 pub fn send(&self, id: String) -> PhpResult<Invoice> {
2588 let commerce = lock_commerce!(self.commerce);
2589 let uuid = parse_uuid!(id, "invoice");
2590
2591 let invoice = commerce
2592 .invoices()
2593 .send(uuid)
2594 .map_err(|e| PhpException::default(format!("Failed to send invoice: {}", e)))?;
2595
2596 Ok(invoice.into())
2597 }
2598
2599 pub fn record_payment(&self, id: String, amount: f64) -> PhpResult<Invoice> {
2600 let commerce = lock_commerce!(self.commerce);
2601 let uuid = parse_uuid!(id, "invoice");
2602 let decimal_amount = Decimal::from_f64_retain(amount).unwrap_or_default();
2603
2604 let invoice = commerce
2605 .invoices()
2606 .record_payment(uuid, decimal_amount)
2607 .map_err(|e| PhpException::default(format!("Failed to record payment: {}", e)))?;
2608
2609 Ok(invoice.into())
2610 }
2611
2612 pub fn void(&self, id: String) -> PhpResult<Invoice> {
2613 let commerce = lock_commerce!(self.commerce);
2614 let uuid = parse_uuid!(id, "invoice");
2615
2616 let invoice = commerce
2617 .invoices()
2618 .void(uuid)
2619 .map_err(|e| PhpException::default(format!("Failed to void invoice: {}", e)))?;
2620
2621 Ok(invoice.into())
2622 }
2623
2624 pub fn get_overdue(&self) -> PhpResult<Vec<Invoice>> {
2625 let commerce = lock_commerce!(self.commerce);
2626
2627 let invoices = commerce
2628 .invoices()
2629 .get_overdue()
2630 .map_err(|e| PhpException::default(format!("Failed to get overdue invoices: {}", e)))?;
2631
2632 Ok(invoices.into_iter().map(|i| i.into()).collect())
2633 }
2634
2635 pub fn customer_balance(&self, customer_id: String) -> PhpResult<f64> {
2636 let commerce = lock_commerce!(self.commerce);
2637 let uuid = parse_uuid!(customer_id, "customer");
2638
2639 let balance = commerce
2640 .invoices()
2641 .customer_balance(uuid)
2642 .map_err(|e| PhpException::default(format!("Failed to get balance: {}", e)))?;
2643
2644 Ok(to_f64_or_nan(balance))
2645 }
2646
2647 pub fn count(&self) -> PhpResult<i64> {
2648 let commerce = lock_commerce!(self.commerce);
2649
2650 let count = commerce
2651 .invoices()
2652 .count(Default::default())
2653 .map_err(|e| PhpException::default(format!("Failed to count invoices: {}", e)))?;
2654
2655 Ok(count)
2656 }
2657}
2658
2659#[php_class(name = "StateSet\\BomComponent")]
2664#[derive(Clone)]
2665pub struct BomComponent {
2666 id: String,
2667 bom_id: String,
2668 component_sku: String,
2669 quantity: i32,
2670 unit_cost: f64,
2671}
2672
2673#[php_impl]
2674impl BomComponent {
2675 #[getter]
2676 pub fn get_id(&self) -> String {
2677 self.id.clone()
2678 }
2679
2680 #[getter]
2681 pub fn get_bom_id(&self) -> String {
2682 self.bom_id.clone()
2683 }
2684
2685 #[getter]
2686 pub fn get_component_sku(&self) -> String {
2687 self.component_sku.clone()
2688 }
2689
2690 #[getter]
2691 pub fn get_quantity(&self) -> i32 {
2692 self.quantity
2693 }
2694
2695 #[getter]
2696 pub fn get_unit_cost(&self) -> f64 {
2697 self.unit_cost
2698 }
2699}
2700
2701impl From<stateset_core::BomComponent> for BomComponent {
2702 fn from(c: stateset_core::BomComponent) -> Self {
2703 Self {
2704 id: c.id.to_string(),
2705 bom_id: c.bom_id.to_string(),
2706 component_sku: c.component_sku,
2707 quantity: c.quantity,
2708 unit_cost: to_f64_or_nan(c.unit_cost),
2709 }
2710 }
2711}
2712
2713#[php_class(name = "StateSet\\BillOfMaterials")]
2714#[derive(Clone)]
2715pub struct BillOfMaterials {
2716 id: String,
2717 product_id: String,
2718 name: String,
2719 version: String,
2720 status: String,
2721 total_cost: f64,
2722 created_at: String,
2723 updated_at: String,
2724}
2725
2726#[php_impl]
2727impl BillOfMaterials {
2728 #[getter]
2729 pub fn get_id(&self) -> String {
2730 self.id.clone()
2731 }
2732
2733 #[getter]
2734 pub fn get_product_id(&self) -> String {
2735 self.product_id.clone()
2736 }
2737
2738 #[getter]
2739 pub fn get_name(&self) -> String {
2740 self.name.clone()
2741 }
2742
2743 #[getter]
2744 pub fn get_version(&self) -> String {
2745 self.version.clone()
2746 }
2747
2748 #[getter]
2749 pub fn get_status(&self) -> String {
2750 self.status.clone()
2751 }
2752
2753 #[getter]
2754 pub fn get_total_cost(&self) -> f64 {
2755 self.total_cost
2756 }
2757
2758 #[getter]
2759 pub fn get_created_at(&self) -> String {
2760 self.created_at.clone()
2761 }
2762
2763 #[getter]
2764 pub fn get_updated_at(&self) -> String {
2765 self.updated_at.clone()
2766 }
2767
2768 pub fn __to_string(&self) -> String {
2769 format!("BillOfMaterials(id={}, name={}, version={})", self.id, self.name, self.version)
2770 }
2771}
2772
2773impl From<stateset_core::BillOfMaterials> for BillOfMaterials {
2774 fn from(b: stateset_core::BillOfMaterials) -> Self {
2775 Self {
2776 id: b.id.to_string(),
2777 product_id: b.product_id.to_string(),
2778 name: b.name,
2779 version: b.version,
2780 status: format!("{}", b.status),
2781 total_cost: to_f64_or_nan(b.total_cost),
2782 created_at: b.created_at.to_rfc3339(),
2783 updated_at: b.updated_at.to_rfc3339(),
2784 }
2785 }
2786}
2787
2788#[php_class(name = "StateSet\\BomApi")]
2789#[derive(Clone)]
2790pub struct BomApi {
2791 commerce: Arc<Mutex<RustCommerce>>,
2792}
2793
2794#[php_impl]
2795impl BomApi {
2796 pub fn create(
2797 &self,
2798 product_id: String,
2799 name: String,
2800 version: Option<String>,
2801 ) -> PhpResult<BillOfMaterials> {
2802 let commerce = lock_commerce!(self.commerce);
2803 let uuid = parse_uuid!(product_id, "product");
2804
2805 let bom = commerce
2806 .bom()
2807 .create(stateset_core::CreateBom {
2808 product_id: uuid,
2809 name,
2810 version,
2811 ..Default::default()
2812 })
2813 .map_err(|e| PhpException::default(format!("Failed to create BOM: {}", e)))?;
2814
2815 Ok(bom.into())
2816 }
2817
2818 pub fn get(&self, id: String) -> PhpResult<Option<BillOfMaterials>> {
2819 let commerce = lock_commerce!(self.commerce);
2820 let uuid = parse_uuid!(id, "bom");
2821
2822 let bom = commerce
2823 .bom()
2824 .get(uuid)
2825 .map_err(|e| PhpException::default(format!("Failed to get BOM: {}", e)))?;
2826
2827 Ok(bom.map(|b| b.into()))
2828 }
2829
2830 pub fn list(&self) -> PhpResult<Vec<BillOfMaterials>> {
2831 let commerce = lock_commerce!(self.commerce);
2832
2833 let boms = commerce
2834 .bom()
2835 .list(Default::default())
2836 .map_err(|e| PhpException::default(format!("Failed to list BOMs: {}", e)))?;
2837
2838 Ok(boms.into_iter().map(|b| b.into()).collect())
2839 }
2840
2841 pub fn add_component(
2842 &self,
2843 bom_id: String,
2844 component_sku: String,
2845 quantity: i32,
2846 unit_cost: f64,
2847 ) -> PhpResult<BomComponent> {
2848 let commerce = lock_commerce!(self.commerce);
2849 let uuid = parse_uuid!(bom_id, "bom");
2850 let cost = Decimal::from_f64_retain(unit_cost).unwrap_or_default();
2851
2852 let component = commerce
2853 .bom()
2854 .add_component(stateset_core::AddBomComponent {
2855 bom_id: uuid,
2856 component_sku,
2857 quantity,
2858 unit_cost: cost,
2859 })
2860 .map_err(|e| PhpException::default(format!("Failed to add component: {}", e)))?;
2861
2862 Ok(component.into())
2863 }
2864
2865 pub fn get_components(&self, bom_id: String) -> PhpResult<Vec<BomComponent>> {
2866 let commerce = lock_commerce!(self.commerce);
2867 let uuid = parse_uuid!(bom_id, "bom");
2868
2869 let components = commerce
2870 .bom()
2871 .get_components(uuid)
2872 .map_err(|e| PhpException::default(format!("Failed to get components: {}", e)))?;
2873
2874 Ok(components.into_iter().map(|c| c.into()).collect())
2875 }
2876
2877 pub fn remove_component(&self, component_id: String) -> PhpResult<bool> {
2878 let commerce = lock_commerce!(self.commerce);
2879 let uuid = parse_uuid!(component_id, "component");
2880
2881 commerce
2882 .bom()
2883 .remove_component(uuid)
2884 .map_err(|e| PhpException::default(format!("Failed to remove component: {}", e)))?;
2885
2886 Ok(true)
2887 }
2888
2889 pub fn activate(&self, id: String) -> PhpResult<BillOfMaterials> {
2890 let commerce = lock_commerce!(self.commerce);
2891 let uuid = parse_uuid!(id, "bom");
2892
2893 let bom = commerce
2894 .bom()
2895 .activate(uuid)
2896 .map_err(|e| PhpException::default(format!("Failed to activate BOM: {}", e)))?;
2897
2898 Ok(bom.into())
2899 }
2900
2901 pub fn delete(&self, id: String) -> PhpResult<bool> {
2902 let commerce = lock_commerce!(self.commerce);
2903 let uuid = parse_uuid!(id, "bom");
2904
2905 commerce
2906 .bom()
2907 .delete(uuid)
2908 .map_err(|e| PhpException::default(format!("Failed to delete BOM: {}", e)))?;
2909
2910 Ok(true)
2911 }
2912
2913 pub fn count(&self) -> PhpResult<i64> {
2914 let commerce = lock_commerce!(self.commerce);
2915
2916 let count = commerce
2917 .bom()
2918 .count(Default::default())
2919 .map_err(|e| PhpException::default(format!("Failed to count BOMs: {}", e)))?;
2920
2921 Ok(count)
2922 }
2923}
2924
2925#[php_class(name = "StateSet\\WorkOrder")]
2930#[derive(Clone)]
2931pub struct WorkOrder {
2932 id: String,
2933 work_order_number: String,
2934 bom_id: String,
2935 quantity: i32,
2936 status: String,
2937 priority: String,
2938 started_at: Option<String>,
2939 completed_at: Option<String>,
2940 created_at: String,
2941 updated_at: String,
2942}
2943
2944#[php_impl]
2945impl WorkOrder {
2946 #[getter]
2947 pub fn get_id(&self) -> String {
2948 self.id.clone()
2949 }
2950
2951 #[getter]
2952 pub fn get_work_order_number(&self) -> String {
2953 self.work_order_number.clone()
2954 }
2955
2956 #[getter]
2957 pub fn get_bom_id(&self) -> String {
2958 self.bom_id.clone()
2959 }
2960
2961 #[getter]
2962 pub fn get_quantity(&self) -> i32 {
2963 self.quantity
2964 }
2965
2966 #[getter]
2967 pub fn get_status(&self) -> String {
2968 self.status.clone()
2969 }
2970
2971 #[getter]
2972 pub fn get_priority(&self) -> String {
2973 self.priority.clone()
2974 }
2975
2976 #[getter]
2977 pub fn get_started_at(&self) -> Option<String> {
2978 self.started_at.clone()
2979 }
2980
2981 #[getter]
2982 pub fn get_completed_at(&self) -> Option<String> {
2983 self.completed_at.clone()
2984 }
2985
2986 #[getter]
2987 pub fn get_created_at(&self) -> String {
2988 self.created_at.clone()
2989 }
2990
2991 #[getter]
2992 pub fn get_updated_at(&self) -> String {
2993 self.updated_at.clone()
2994 }
2995
2996 pub fn __to_string(&self) -> String {
2997 format!(
2998 "WorkOrder(number={}, status={}, qty={})",
2999 self.work_order_number, self.status, self.quantity
3000 )
3001 }
3002}
3003
3004impl From<stateset_core::WorkOrder> for WorkOrder {
3005 fn from(w: stateset_core::WorkOrder) -> Self {
3006 Self {
3007 id: w.id.to_string(),
3008 work_order_number: w.work_order_number,
3009 bom_id: w.bom_id.to_string(),
3010 quantity: w.quantity,
3011 status: format!("{}", w.status),
3012 priority: format!("{}", w.priority),
3013 started_at: w.started_at.map(|t| t.to_rfc3339()),
3014 completed_at: w.completed_at.map(|t| t.to_rfc3339()),
3015 created_at: w.created_at.to_rfc3339(),
3016 updated_at: w.updated_at.to_rfc3339(),
3017 }
3018 }
3019}
3020
3021#[php_class(name = "StateSet\\WorkOrders")]
3022#[derive(Clone)]
3023pub struct WorkOrders {
3024 commerce: Arc<Mutex<RustCommerce>>,
3025}
3026
3027#[php_impl]
3028impl WorkOrders {
3029 pub fn create(
3030 &self,
3031 bom_id: String,
3032 quantity: i32,
3033 priority: Option<String>,
3034 ) -> PhpResult<WorkOrder> {
3035 let commerce = lock_commerce!(self.commerce);
3036 let uuid = parse_uuid!(bom_id, "bom");
3037
3038 let work_order = commerce
3039 .work_orders()
3040 .create(stateset_core::CreateWorkOrder {
3041 bom_id: uuid,
3042 quantity,
3043 priority,
3044 ..Default::default()
3045 })
3046 .map_err(|e| PhpException::default(format!("Failed to create work order: {}", e)))?;
3047
3048 Ok(work_order.into())
3049 }
3050
3051 pub fn get(&self, id: String) -> PhpResult<Option<WorkOrder>> {
3052 let commerce = lock_commerce!(self.commerce);
3053 let uuid = parse_uuid!(id, "work_order");
3054
3055 let work_order = commerce
3056 .work_orders()
3057 .get(uuid)
3058 .map_err(|e| PhpException::default(format!("Failed to get work order: {}", e)))?;
3059
3060 Ok(work_order.map(|w| w.into()))
3061 }
3062
3063 pub fn list(&self) -> PhpResult<Vec<WorkOrder>> {
3064 let commerce = lock_commerce!(self.commerce);
3065
3066 let work_orders = commerce
3067 .work_orders()
3068 .list(Default::default())
3069 .map_err(|e| PhpException::default(format!("Failed to list work orders: {}", e)))?;
3070
3071 Ok(work_orders.into_iter().map(|w| w.into()).collect())
3072 }
3073
3074 pub fn start(&self, id: String) -> PhpResult<WorkOrder> {
3075 let commerce = lock_commerce!(self.commerce);
3076 let uuid = parse_uuid!(id, "work_order");
3077
3078 let work_order = commerce
3079 .work_orders()
3080 .start(uuid)
3081 .map_err(|e| PhpException::default(format!("Failed to start work order: {}", e)))?;
3082
3083 Ok(work_order.into())
3084 }
3085
3086 pub fn complete(&self, id: String) -> PhpResult<WorkOrder> {
3087 let commerce = lock_commerce!(self.commerce);
3088 let uuid = parse_uuid!(id, "work_order");
3089
3090 let work_order = commerce
3091 .work_orders()
3092 .complete(uuid)
3093 .map_err(|e| PhpException::default(format!("Failed to complete work order: {}", e)))?;
3094
3095 Ok(work_order.into())
3096 }
3097
3098 pub fn hold(&self, id: String) -> PhpResult<WorkOrder> {
3099 let commerce = lock_commerce!(self.commerce);
3100 let uuid = parse_uuid!(id, "work_order");
3101
3102 let work_order = commerce
3103 .work_orders()
3104 .hold(uuid)
3105 .map_err(|e| PhpException::default(format!("Failed to hold work order: {}", e)))?;
3106
3107 Ok(work_order.into())
3108 }
3109
3110 pub fn resume(&self, id: String) -> PhpResult<WorkOrder> {
3111 let commerce = lock_commerce!(self.commerce);
3112 let uuid = parse_uuid!(id, "work_order");
3113
3114 let work_order = commerce
3115 .work_orders()
3116 .resume(uuid)
3117 .map_err(|e| PhpException::default(format!("Failed to resume work order: {}", e)))?;
3118
3119 Ok(work_order.into())
3120 }
3121
3122 pub fn cancel(&self, id: String) -> PhpResult<WorkOrder> {
3123 let commerce = lock_commerce!(self.commerce);
3124 let uuid = parse_uuid!(id, "work_order");
3125
3126 let work_order = commerce
3127 .work_orders()
3128 .cancel(uuid)
3129 .map_err(|e| PhpException::default(format!("Failed to cancel work order: {}", e)))?;
3130
3131 Ok(work_order.into())
3132 }
3133
3134 pub fn count(&self) -> PhpResult<i64> {
3135 let commerce = lock_commerce!(self.commerce);
3136
3137 let count = commerce
3138 .work_orders()
3139 .count(Default::default())
3140 .map_err(|e| PhpException::default(format!("Failed to count work orders: {}", e)))?;
3141
3142 Ok(count)
3143 }
3144}
3145
3146#[php_class(name = "StateSet\\ExchangeRate")]
3151#[derive(Clone)]
3152pub struct ExchangeRate {
3153 from_currency: String,
3154 to_currency: String,
3155 rate: f64,
3156 updated_at: String,
3157}
3158
3159#[php_impl]
3160impl ExchangeRate {
3161 #[getter]
3162 pub fn get_from_currency(&self) -> String {
3163 self.from_currency.clone()
3164 }
3165
3166 #[getter]
3167 pub fn get_to_currency(&self) -> String {
3168 self.to_currency.clone()
3169 }
3170
3171 #[getter]
3172 pub fn get_rate(&self) -> f64 {
3173 self.rate
3174 }
3175
3176 #[getter]
3177 pub fn get_updated_at(&self) -> String {
3178 self.updated_at.clone()
3179 }
3180}
3181
3182impl From<stateset_core::ExchangeRate> for ExchangeRate {
3183 fn from(r: stateset_core::ExchangeRate) -> Self {
3184 Self {
3185 from_currency: r.from_currency,
3186 to_currency: r.to_currency,
3187 rate: to_f64_or_nan(r.rate),
3188 updated_at: r.updated_at.to_rfc3339(),
3189 }
3190 }
3191}
3192
3193#[php_class(name = "StateSet\\CurrencyOps")]
3194#[derive(Clone)]
3195pub struct CurrencyOps {
3196 commerce: Arc<Mutex<RustCommerce>>,
3197}
3198
3199#[php_impl]
3200impl CurrencyOps {
3201 pub fn get_rate(&self, from: String, to: String) -> PhpResult<Option<ExchangeRate>> {
3202 let commerce = lock_commerce!(self.commerce);
3203
3204 let rate = commerce
3205 .currency()
3206 .get_rate(&from, &to)
3207 .map_err(|e| PhpException::default(format!("Failed to get rate: {}", e)))?;
3208
3209 Ok(rate.map(|r| r.into()))
3210 }
3211
3212 pub fn list_rates(&self) -> PhpResult<Vec<ExchangeRate>> {
3213 let commerce = lock_commerce!(self.commerce);
3214
3215 let rates = commerce
3216 .currency()
3217 .list_rates()
3218 .map_err(|e| PhpException::default(format!("Failed to list rates: {}", e)))?;
3219
3220 Ok(rates.into_iter().map(|r| r.into()).collect())
3221 }
3222
3223 pub fn set_rate(&self, from: String, to: String, rate: f64) -> PhpResult<ExchangeRate> {
3224 let commerce = lock_commerce!(self.commerce);
3225 let decimal_rate = Decimal::from_f64_retain(rate).unwrap_or_default();
3226
3227 let exchange_rate = commerce
3228 .currency()
3229 .set_rate(&from, &to, decimal_rate)
3230 .map_err(|e| PhpException::default(format!("Failed to set rate: {}", e)))?;
3231
3232 Ok(exchange_rate.into())
3233 }
3234
3235 pub fn convert(&self, amount: f64, from: String, to: String) -> PhpResult<f64> {
3236 let commerce = lock_commerce!(self.commerce);
3237 let decimal_amount = Decimal::from_f64_retain(amount).unwrap_or_default();
3238
3239 let converted = commerce
3240 .currency()
3241 .convert(decimal_amount, &from, &to)
3242 .map_err(|e| PhpException::default(format!("Failed to convert: {}", e)))?;
3243
3244 Ok(to_f64_or_nan(converted))
3245 }
3246
3247 pub fn base_currency(&self) -> PhpResult<String> {
3248 let commerce = lock_commerce!(self.commerce);
3249
3250 let base = commerce
3251 .currency()
3252 .base_currency()
3253 .map_err(|e| PhpException::default(format!("Failed to get base currency: {}", e)))?;
3254
3255 Ok(base)
3256 }
3257
3258 pub fn enabled_currencies(&self) -> PhpResult<Vec<String>> {
3259 let commerce = lock_commerce!(self.commerce);
3260
3261 let currencies = commerce
3262 .currency()
3263 .enabled_currencies()
3264 .map_err(|e| PhpException::default(format!("Failed to get currencies: {}", e)))?;
3265
3266 Ok(currencies)
3267 }
3268
3269 pub fn format(&self, amount: f64, currency: String) -> PhpResult<String> {
3270 let commerce = lock_commerce!(self.commerce);
3271 let decimal_amount = Decimal::from_f64_retain(amount).unwrap_or_default();
3272
3273 let formatted = commerce
3274 .currency()
3275 .format(decimal_amount, ¤cy)
3276 .map_err(|e| PhpException::default(format!("Failed to format: {}", e)))?;
3277
3278 Ok(formatted)
3279 }
3280}
3281
3282#[php_class(name = "StateSet\\SubscriptionPlan")]
3287#[derive(Clone)]
3288pub struct SubscriptionPlan {
3289 id: String,
3290 name: String,
3291 description: Option<String>,
3292 price: f64,
3293 currency: String,
3294 interval: String,
3295 interval_count: i32,
3296 trial_days: Option<i32>,
3297 status: String,
3298 created_at: String,
3299}
3300
3301#[php_impl]
3302impl SubscriptionPlan {
3303 #[getter]
3304 pub fn get_id(&self) -> String {
3305 self.id.clone()
3306 }
3307
3308 #[getter]
3309 pub fn get_name(&self) -> String {
3310 self.name.clone()
3311 }
3312
3313 #[getter]
3314 pub fn get_description(&self) -> Option<String> {
3315 self.description.clone()
3316 }
3317
3318 #[getter]
3319 pub fn get_price(&self) -> f64 {
3320 self.price
3321 }
3322
3323 #[getter]
3324 pub fn get_currency(&self) -> String {
3325 self.currency.clone()
3326 }
3327
3328 #[getter]
3329 pub fn get_interval(&self) -> String {
3330 self.interval.clone()
3331 }
3332
3333 #[getter]
3334 pub fn get_interval_count(&self) -> i32 {
3335 self.interval_count
3336 }
3337
3338 #[getter]
3339 pub fn get_trial_days(&self) -> Option<i32> {
3340 self.trial_days
3341 }
3342
3343 #[getter]
3344 pub fn get_status(&self) -> String {
3345 self.status.clone()
3346 }
3347
3348 #[getter]
3349 pub fn get_created_at(&self) -> String {
3350 self.created_at.clone()
3351 }
3352}
3353
3354impl From<stateset_core::SubscriptionPlan> for SubscriptionPlan {
3355 fn from(p: stateset_core::SubscriptionPlan) -> Self {
3356 Self {
3357 id: p.id.to_string(),
3358 name: p.name,
3359 description: p.description,
3360 price: to_f64_or_nan(p.price),
3361 currency: p.currency,
3362 interval: format!("{}", p.interval),
3363 interval_count: p.interval_count,
3364 trial_days: p.trial_days,
3365 status: format!("{}", p.status),
3366 created_at: p.created_at.to_rfc3339(),
3367 }
3368 }
3369}
3370
3371#[php_class(name = "StateSet\\Subscription")]
3372#[derive(Clone)]
3373pub struct Subscription {
3374 id: String,
3375 plan_id: String,
3376 customer_id: String,
3377 status: String,
3378 current_period_start: String,
3379 current_period_end: String,
3380 canceled_at: Option<String>,
3381 created_at: String,
3382 updated_at: String,
3383}
3384
3385#[php_impl]
3386impl Subscription {
3387 #[getter]
3388 pub fn get_id(&self) -> String {
3389 self.id.clone()
3390 }
3391
3392 #[getter]
3393 pub fn get_plan_id(&self) -> String {
3394 self.plan_id.clone()
3395 }
3396
3397 #[getter]
3398 pub fn get_customer_id(&self) -> String {
3399 self.customer_id.clone()
3400 }
3401
3402 #[getter]
3403 pub fn get_status(&self) -> String {
3404 self.status.clone()
3405 }
3406
3407 #[getter]
3408 pub fn get_current_period_start(&self) -> String {
3409 self.current_period_start.clone()
3410 }
3411
3412 #[getter]
3413 pub fn get_current_period_end(&self) -> String {
3414 self.current_period_end.clone()
3415 }
3416
3417 #[getter]
3418 pub fn get_canceled_at(&self) -> Option<String> {
3419 self.canceled_at.clone()
3420 }
3421
3422 #[getter]
3423 pub fn get_created_at(&self) -> String {
3424 self.created_at.clone()
3425 }
3426
3427 #[getter]
3428 pub fn get_updated_at(&self) -> String {
3429 self.updated_at.clone()
3430 }
3431
3432 pub fn __to_string(&self) -> String {
3433 format!("Subscription(id={}, status={})", self.id, self.status)
3434 }
3435}
3436
3437impl From<stateset_core::Subscription> for Subscription {
3438 fn from(s: stateset_core::Subscription) -> Self {
3439 Self {
3440 id: s.id.to_string(),
3441 plan_id: s.plan_id.to_string(),
3442 customer_id: s.customer_id.to_string(),
3443 status: format!("{}", s.status),
3444 current_period_start: s.current_period_start.to_rfc3339(),
3445 current_period_end: s.current_period_end.to_rfc3339(),
3446 canceled_at: s.canceled_at.map(|t| t.to_rfc3339()),
3447 created_at: s.created_at.to_rfc3339(),
3448 updated_at: s.updated_at.to_rfc3339(),
3449 }
3450 }
3451}
3452
3453#[php_class(name = "StateSet\\Subscriptions")]
3454#[derive(Clone)]
3455pub struct Subscriptions {
3456 commerce: Arc<Mutex<RustCommerce>>,
3457}
3458
3459#[php_impl]
3460impl Subscriptions {
3461 pub fn create_plan(
3462 &self,
3463 name: String,
3464 price: f64,
3465 interval: String,
3466 interval_count: Option<i32>,
3467 trial_days: Option<i32>,
3468 ) -> PhpResult<SubscriptionPlan> {
3469 let commerce = lock_commerce!(self.commerce);
3470 let decimal_price = Decimal::from_f64_retain(price).unwrap_or_default();
3471
3472 let plan = commerce
3473 .subscriptions()
3474 .create_plan(stateset_core::CreateSubscriptionPlan {
3475 name,
3476 price: decimal_price,
3477 interval,
3478 interval_count,
3479 trial_days,
3480 ..Default::default()
3481 })
3482 .map_err(|e| PhpException::default(format!("Failed to create plan: {}", e)))?;
3483
3484 Ok(plan.into())
3485 }
3486
3487 pub fn get_plan(&self, id: String) -> PhpResult<Option<SubscriptionPlan>> {
3488 let commerce = lock_commerce!(self.commerce);
3489 let uuid = parse_uuid!(id, "plan");
3490
3491 let plan = commerce
3492 .subscriptions()
3493 .get_plan(uuid)
3494 .map_err(|e| PhpException::default(format!("Failed to get plan: {}", e)))?;
3495
3496 Ok(plan.map(|p| p.into()))
3497 }
3498
3499 pub fn list_plans(&self) -> PhpResult<Vec<SubscriptionPlan>> {
3500 let commerce = lock_commerce!(self.commerce);
3501
3502 let plans = commerce
3503 .subscriptions()
3504 .list_plans(Default::default())
3505 .map_err(|e| PhpException::default(format!("Failed to list plans: {}", e)))?;
3506
3507 Ok(plans.into_iter().map(|p| p.into()).collect())
3508 }
3509
3510 pub fn subscribe(&self, plan_id: String, customer_id: String) -> PhpResult<Subscription> {
3511 let commerce = lock_commerce!(self.commerce);
3512 let plan_uuid = parse_uuid!(plan_id, "plan");
3513 let cust_uuid = parse_uuid!(customer_id, "customer");
3514
3515 let subscription = commerce
3516 .subscriptions()
3517 .subscribe(stateset_core::CreateSubscription {
3518 plan_id: plan_uuid,
3519 customer_id: cust_uuid,
3520 ..Default::default()
3521 })
3522 .map_err(|e| PhpException::default(format!("Failed to subscribe: {}", e)))?;
3523
3524 Ok(subscription.into())
3525 }
3526
3527 pub fn get(&self, id: String) -> PhpResult<Option<Subscription>> {
3528 let commerce = lock_commerce!(self.commerce);
3529 let uuid = parse_uuid!(id, "subscription");
3530
3531 let subscription = commerce
3532 .subscriptions()
3533 .get(uuid)
3534 .map_err(|e| PhpException::default(format!("Failed to get subscription: {}", e)))?;
3535
3536 Ok(subscription.map(|s| s.into()))
3537 }
3538
3539 pub fn list(&self) -> PhpResult<Vec<Subscription>> {
3540 let commerce = lock_commerce!(self.commerce);
3541
3542 let subscriptions = commerce
3543 .subscriptions()
3544 .list(Default::default())
3545 .map_err(|e| PhpException::default(format!("Failed to list subscriptions: {}", e)))?;
3546
3547 Ok(subscriptions.into_iter().map(|s| s.into()).collect())
3548 }
3549
3550 pub fn pause(&self, id: String) -> PhpResult<Subscription> {
3551 let commerce = lock_commerce!(self.commerce);
3552 let uuid = parse_uuid!(id, "subscription");
3553
3554 let subscription = commerce
3555 .subscriptions()
3556 .pause(uuid)
3557 .map_err(|e| PhpException::default(format!("Failed to pause subscription: {}", e)))?;
3558
3559 Ok(subscription.into())
3560 }
3561
3562 pub fn resume(&self, id: String) -> PhpResult<Subscription> {
3563 let commerce = lock_commerce!(self.commerce);
3564 let uuid = parse_uuid!(id, "subscription");
3565
3566 let subscription = commerce
3567 .subscriptions()
3568 .resume(uuid)
3569 .map_err(|e| PhpException::default(format!("Failed to resume subscription: {}", e)))?;
3570
3571 Ok(subscription.into())
3572 }
3573
3574 pub fn cancel(&self, id: String, at_period_end: Option<bool>) -> PhpResult<Subscription> {
3575 let commerce = lock_commerce!(self.commerce);
3576 let uuid = parse_uuid!(id, "subscription");
3577
3578 let subscription = commerce
3579 .subscriptions()
3580 .cancel(uuid, at_period_end.unwrap_or(true))
3581 .map_err(|e| PhpException::default(format!("Failed to cancel subscription: {}", e)))?;
3582
3583 Ok(subscription.into())
3584 }
3585
3586 pub fn for_customer(&self, customer_id: String) -> PhpResult<Vec<Subscription>> {
3587 let commerce = lock_commerce!(self.commerce);
3588 let uuid = parse_uuid!(customer_id, "customer");
3589
3590 let subscriptions = commerce
3591 .subscriptions()
3592 .for_customer(uuid)
3593 .map_err(|e| PhpException::default(format!("Failed to get subscriptions: {}", e)))?;
3594
3595 Ok(subscriptions.into_iter().map(|s| s.into()).collect())
3596 }
3597
3598 pub fn is_active(&self, id: String) -> PhpResult<bool> {
3599 let commerce = lock_commerce!(self.commerce);
3600 let uuid = parse_uuid!(id, "subscription");
3601
3602 let active = commerce
3603 .subscriptions()
3604 .is_active(uuid)
3605 .map_err(|e| PhpException::default(format!("Failed to check subscription: {}", e)))?;
3606
3607 Ok(active)
3608 }
3609}
3610
3611#[php_class(name = "StateSet\\Promotion")]
3616#[derive(Clone)]
3617pub struct Promotion {
3618 id: String,
3619 code: String,
3620 name: String,
3621 description: Option<String>,
3622 discount_type: String,
3623 discount_value: f64,
3624 min_purchase: Option<f64>,
3625 max_uses: Option<i32>,
3626 uses_count: i32,
3627 starts_at: Option<String>,
3628 ends_at: Option<String>,
3629 status: String,
3630 created_at: String,
3631}
3632
3633#[php_impl]
3634impl Promotion {
3635 #[getter]
3636 pub fn get_id(&self) -> String {
3637 self.id.clone()
3638 }
3639
3640 #[getter]
3641 pub fn get_code(&self) -> String {
3642 self.code.clone()
3643 }
3644
3645 #[getter]
3646 pub fn get_name(&self) -> String {
3647 self.name.clone()
3648 }
3649
3650 #[getter]
3651 pub fn get_description(&self) -> Option<String> {
3652 self.description.clone()
3653 }
3654
3655 #[getter]
3656 pub fn get_discount_type(&self) -> String {
3657 self.discount_type.clone()
3658 }
3659
3660 #[getter]
3661 pub fn get_discount_value(&self) -> f64 {
3662 self.discount_value
3663 }
3664
3665 #[getter]
3666 pub fn get_min_purchase(&self) -> Option<f64> {
3667 self.min_purchase
3668 }
3669
3670 #[getter]
3671 pub fn get_max_uses(&self) -> Option<i32> {
3672 self.max_uses
3673 }
3674
3675 #[getter]
3676 pub fn get_uses_count(&self) -> i32 {
3677 self.uses_count
3678 }
3679
3680 #[getter]
3681 pub fn get_starts_at(&self) -> Option<String> {
3682 self.starts_at.clone()
3683 }
3684
3685 #[getter]
3686 pub fn get_ends_at(&self) -> Option<String> {
3687 self.ends_at.clone()
3688 }
3689
3690 #[getter]
3691 pub fn get_status(&self) -> String {
3692 self.status.clone()
3693 }
3694
3695 #[getter]
3696 pub fn get_created_at(&self) -> String {
3697 self.created_at.clone()
3698 }
3699
3700 pub fn __to_string(&self) -> String {
3701 format!(
3702 "Promotion(code={}, type={}, value={})",
3703 self.code, self.discount_type, self.discount_value
3704 )
3705 }
3706}
3707
3708impl From<stateset_core::Promotion> for Promotion {
3709 fn from(p: stateset_core::Promotion) -> Self {
3710 Self {
3711 id: p.id.to_string(),
3712 code: p.code,
3713 name: p.name,
3714 description: p.description,
3715 discount_type: format!("{}", p.discount_type),
3716 discount_value: to_f64_or_nan(p.discount_value),
3717 min_purchase: p.min_purchase.and_then(|m| m.to_f64()),
3718 max_uses: p.max_uses,
3719 uses_count: p.uses_count,
3720 starts_at: p.starts_at.map(|t| t.to_rfc3339()),
3721 ends_at: p.ends_at.map(|t| t.to_rfc3339()),
3722 status: format!("{}", p.status),
3723 created_at: p.created_at.to_rfc3339(),
3724 }
3725 }
3726}
3727
3728#[php_class(name = "StateSet\\Promotions")]
3729#[derive(Clone)]
3730pub struct Promotions {
3731 commerce: Arc<Mutex<RustCommerce>>,
3732}
3733
3734#[php_impl]
3735impl Promotions {
3736 pub fn create(
3737 &self,
3738 code: String,
3739 name: String,
3740 discount_type: String,
3741 discount_value: f64,
3742 min_purchase: Option<f64>,
3743 max_uses: Option<i32>,
3744 ) -> PhpResult<Promotion> {
3745 let commerce = lock_commerce!(self.commerce);
3746 let decimal_value = Decimal::from_f64_retain(discount_value).unwrap_or_default();
3747 let decimal_min = min_purchase.map(|m| Decimal::from_f64_retain(m).unwrap_or_default());
3748
3749 let promotion = commerce
3750 .promotions()
3751 .create(stateset_core::CreatePromotion {
3752 code,
3753 name,
3754 discount_type,
3755 discount_value: decimal_value,
3756 min_purchase: decimal_min,
3757 max_uses,
3758 ..Default::default()
3759 })
3760 .map_err(|e| PhpException::default(format!("Failed to create promotion: {}", e)))?;
3761
3762 Ok(promotion.into())
3763 }
3764
3765 pub fn get(&self, id: String) -> PhpResult<Option<Promotion>> {
3766 let commerce = lock_commerce!(self.commerce);
3767 let uuid = parse_uuid!(id, "promotion");
3768
3769 let promotion = commerce
3770 .promotions()
3771 .get(uuid)
3772 .map_err(|e| PhpException::default(format!("Failed to get promotion: {}", e)))?;
3773
3774 Ok(promotion.map(|p| p.into()))
3775 }
3776
3777 pub fn get_by_code(&self, code: String) -> PhpResult<Option<Promotion>> {
3778 let commerce = lock_commerce!(self.commerce);
3779
3780 let promotion = commerce
3781 .promotions()
3782 .get_by_code(&code)
3783 .map_err(|e| PhpException::default(format!("Failed to get promotion: {}", e)))?;
3784
3785 Ok(promotion.map(|p| p.into()))
3786 }
3787
3788 pub fn list(&self) -> PhpResult<Vec<Promotion>> {
3789 let commerce = lock_commerce!(self.commerce);
3790
3791 let promotions = commerce
3792 .promotions()
3793 .list(Default::default())
3794 .map_err(|e| PhpException::default(format!("Failed to list promotions: {}", e)))?;
3795
3796 Ok(promotions.into_iter().map(|p| p.into()).collect())
3797 }
3798
3799 pub fn activate(&self, id: String) -> PhpResult<Promotion> {
3800 let commerce = lock_commerce!(self.commerce);
3801 let uuid = parse_uuid!(id, "promotion");
3802
3803 let promotion = commerce
3804 .promotions()
3805 .activate(uuid)
3806 .map_err(|e| PhpException::default(format!("Failed to activate promotion: {}", e)))?;
3807
3808 Ok(promotion.into())
3809 }
3810
3811 pub fn deactivate(&self, id: String) -> PhpResult<Promotion> {
3812 let commerce = lock_commerce!(self.commerce);
3813 let uuid = parse_uuid!(id, "promotion");
3814
3815 let promotion = commerce
3816 .promotions()
3817 .deactivate(uuid)
3818 .map_err(|e| PhpException::default(format!("Failed to deactivate promotion: {}", e)))?;
3819
3820 Ok(promotion.into())
3821 }
3822
3823 pub fn get_active(&self) -> PhpResult<Vec<Promotion>> {
3824 let commerce = lock_commerce!(self.commerce);
3825
3826 let promotions = commerce.promotions().get_active().map_err(|e| {
3827 PhpException::default(format!("Failed to get active promotions: {}", e))
3828 })?;
3829
3830 Ok(promotions.into_iter().map(|p| p.into()).collect())
3831 }
3832
3833 pub fn is_valid(&self, code: String, order_total: Option<f64>) -> PhpResult<bool> {
3834 let commerce = lock_commerce!(self.commerce);
3835 let decimal_total = order_total.map(|t| Decimal::from_f64_retain(t).unwrap_or_default());
3836
3837 let valid = commerce
3838 .promotions()
3839 .is_valid(&code, decimal_total)
3840 .map_err(|e| PhpException::default(format!("Failed to validate promotion: {}", e)))?;
3841
3842 Ok(valid)
3843 }
3844
3845 pub fn delete(&self, id: String) -> PhpResult<bool> {
3846 let commerce = lock_commerce!(self.commerce);
3847 let uuid = parse_uuid!(id, "promotion");
3848
3849 commerce
3850 .promotions()
3851 .delete(uuid)
3852 .map_err(|e| PhpException::default(format!("Failed to delete promotion: {}", e)))?;
3853
3854 Ok(true)
3855 }
3856}
3857
3858#[php_class(name = "StateSet\\TaxJurisdiction")]
3863#[derive(Clone)]
3864pub struct TaxJurisdiction {
3865 id: String,
3866 name: String,
3867 country: String,
3868 state: Option<String>,
3869 city: Option<String>,
3870 postal_code: Option<String>,
3871 status: String,
3872}
3873
3874#[php_impl]
3875impl TaxJurisdiction {
3876 #[getter]
3877 pub fn get_id(&self) -> String {
3878 self.id.clone()
3879 }
3880
3881 #[getter]
3882 pub fn get_name(&self) -> String {
3883 self.name.clone()
3884 }
3885
3886 #[getter]
3887 pub fn get_country(&self) -> String {
3888 self.country.clone()
3889 }
3890
3891 #[getter]
3892 pub fn get_state(&self) -> Option<String> {
3893 self.state.clone()
3894 }
3895
3896 #[getter]
3897 pub fn get_city(&self) -> Option<String> {
3898 self.city.clone()
3899 }
3900
3901 #[getter]
3902 pub fn get_postal_code(&self) -> Option<String> {
3903 self.postal_code.clone()
3904 }
3905
3906 #[getter]
3907 pub fn get_status(&self) -> String {
3908 self.status.clone()
3909 }
3910}
3911
3912impl From<stateset_core::TaxJurisdiction> for TaxJurisdiction {
3913 fn from(j: stateset_core::TaxJurisdiction) -> Self {
3914 Self {
3915 id: j.id.to_string(),
3916 name: j.name,
3917 country: j.country,
3918 state: j.state,
3919 city: j.city,
3920 postal_code: j.postal_code,
3921 status: format!("{}", j.status),
3922 }
3923 }
3924}
3925
3926#[php_class(name = "StateSet\\TaxRate")]
3927#[derive(Clone)]
3928pub struct TaxRate {
3929 id: String,
3930 jurisdiction_id: String,
3931 name: String,
3932 rate: f64,
3933 tax_type: String,
3934 status: String,
3935}
3936
3937#[php_impl]
3938impl TaxRate {
3939 #[getter]
3940 pub fn get_id(&self) -> String {
3941 self.id.clone()
3942 }
3943
3944 #[getter]
3945 pub fn get_jurisdiction_id(&self) -> String {
3946 self.jurisdiction_id.clone()
3947 }
3948
3949 #[getter]
3950 pub fn get_name(&self) -> String {
3951 self.name.clone()
3952 }
3953
3954 #[getter]
3955 pub fn get_rate(&self) -> f64 {
3956 self.rate
3957 }
3958
3959 #[getter]
3960 pub fn get_tax_type(&self) -> String {
3961 self.tax_type.clone()
3962 }
3963
3964 #[getter]
3965 pub fn get_status(&self) -> String {
3966 self.status.clone()
3967 }
3968}
3969
3970impl From<stateset_core::TaxRate> for TaxRate {
3971 fn from(r: stateset_core::TaxRate) -> Self {
3972 Self {
3973 id: r.id.to_string(),
3974 jurisdiction_id: r.jurisdiction_id.to_string(),
3975 name: r.name,
3976 rate: to_f64_or_nan(r.rate),
3977 tax_type: format!("{}", r.tax_type),
3978 status: format!("{}", r.status),
3979 }
3980 }
3981}
3982
3983#[php_class(name = "StateSet\\Tax")]
3984#[derive(Clone)]
3985pub struct Tax {
3986 commerce: Arc<Mutex<RustCommerce>>,
3987}
3988
3989#[php_impl]
3990impl Tax {
3991 pub fn calculate(&self, amount: f64, jurisdiction_id: String) -> PhpResult<f64> {
3992 let commerce = lock_commerce!(self.commerce);
3993 let decimal_amount = Decimal::from_f64_retain(amount).unwrap_or_default();
3994 let uuid = parse_uuid!(jurisdiction_id, "jurisdiction");
3995
3996 let tax = commerce
3997 .tax()
3998 .calculate(decimal_amount, uuid)
3999 .map_err(|e| PhpException::default(format!("Failed to calculate tax: {}", e)))?;
4000
4001 Ok(to_f64_or_nan(tax))
4002 }
4003
4004 pub fn get_effective_rate(&self, jurisdiction_id: String) -> PhpResult<f64> {
4005 let commerce = lock_commerce!(self.commerce);
4006 let uuid = parse_uuid!(jurisdiction_id, "jurisdiction");
4007
4008 let rate = commerce
4009 .tax()
4010 .get_effective_rate(uuid)
4011 .map_err(|e| PhpException::default(format!("Failed to get rate: {}", e)))?;
4012
4013 Ok(to_f64_or_nan(rate))
4014 }
4015
4016 pub fn list_jurisdictions(&self) -> PhpResult<Vec<TaxJurisdiction>> {
4017 let commerce = lock_commerce!(self.commerce);
4018
4019 let jurisdictions = commerce
4020 .tax()
4021 .list_jurisdictions(Default::default())
4022 .map_err(|e| PhpException::default(format!("Failed to list jurisdictions: {}", e)))?;
4023
4024 Ok(jurisdictions.into_iter().map(|j| j.into()).collect())
4025 }
4026
4027 pub fn create_jurisdiction(
4028 &self,
4029 name: String,
4030 country: String,
4031 state: Option<String>,
4032 city: Option<String>,
4033 ) -> PhpResult<TaxJurisdiction> {
4034 let commerce = lock_commerce!(self.commerce);
4035
4036 let jurisdiction = commerce
4037 .tax()
4038 .create_jurisdiction(stateset_core::CreateTaxJurisdiction {
4039 name,
4040 country,
4041 state,
4042 city,
4043 ..Default::default()
4044 })
4045 .map_err(|e| PhpException::default(format!("Failed to create jurisdiction: {}", e)))?;
4046
4047 Ok(jurisdiction.into())
4048 }
4049
4050 pub fn list_rates(&self, jurisdiction_id: String) -> PhpResult<Vec<TaxRate>> {
4051 let commerce = lock_commerce!(self.commerce);
4052 let uuid = parse_uuid!(jurisdiction_id, "jurisdiction");
4053
4054 let rates = commerce
4055 .tax()
4056 .list_rates(uuid)
4057 .map_err(|e| PhpException::default(format!("Failed to list rates: {}", e)))?;
4058
4059 Ok(rates.into_iter().map(|r| r.into()).collect())
4060 }
4061
4062 pub fn create_rate(
4063 &self,
4064 jurisdiction_id: String,
4065 name: String,
4066 rate: f64,
4067 tax_type: String,
4068 ) -> PhpResult<TaxRate> {
4069 let commerce = lock_commerce!(self.commerce);
4070 let uuid = parse_uuid!(jurisdiction_id, "jurisdiction");
4071 let decimal_rate = Decimal::from_f64_retain(rate).unwrap_or_default();
4072
4073 let tax_rate = commerce
4074 .tax()
4075 .create_rate(stateset_core::CreateTaxRate {
4076 jurisdiction_id: uuid,
4077 name,
4078 rate: decimal_rate,
4079 tax_type,
4080 ..Default::default()
4081 })
4082 .map_err(|e| PhpException::default(format!("Failed to create rate: {}", e)))?;
4083
4084 Ok(tax_rate.into())
4085 }
4086
4087 pub fn is_enabled(&self) -> PhpResult<bool> {
4088 let commerce = lock_commerce!(self.commerce);
4089
4090 let enabled = commerce
4091 .tax()
4092 .is_enabled()
4093 .map_err(|e| PhpException::default(format!("Failed to check tax status: {}", e)))?;
4094
4095 Ok(enabled)
4096 }
4097
4098 pub fn set_enabled(&self, enabled: bool) -> PhpResult<bool> {
4099 let commerce = lock_commerce!(self.commerce);
4100
4101 commerce
4102 .tax()
4103 .set_enabled(enabled)
4104 .map_err(|e| PhpException::default(format!("Failed to set tax status: {}", e)))?;
4105
4106 Ok(true)
4107 }
4108}
4109
4110#[php_class(name = "StateSet\\Quality")]
4115#[derive(Clone)]
4116pub struct Quality {
4117 commerce: Arc<Mutex<RustCommerce>>,
4118}
4119
4120#[php_impl]
4121impl Quality {
4122 pub fn create_inspection(
4123 &self,
4124 inspection_type: String,
4125 reference_type: String,
4126 reference_id: String,
4127 ) -> PhpResult<String> {
4128 let commerce = lock_commerce!(self.commerce);
4129 let ref_uuid = parse_uuid!(reference_id, "reference");
4130
4131 let inspection = commerce
4132 .quality()
4133 .create_inspection(stateset_core::CreateInspection {
4134 inspection_type,
4135 reference_type,
4136 reference_id: ref_uuid,
4137 ..Default::default()
4138 })
4139 .map_err(|e| PhpException::default(format!("Failed to create inspection: {}", e)))?;
4140
4141 Ok(inspection.id.to_string())
4142 }
4143
4144 pub fn list_inspections(&self) -> PhpResult<Vec<String>> {
4145 let commerce = lock_commerce!(self.commerce);
4146
4147 let inspections = commerce
4148 .quality()
4149 .list_inspections(Default::default())
4150 .map_err(|e| PhpException::default(format!("Failed to list inspections: {}", e)))?;
4151
4152 Ok(inspections.into_iter().map(|i| i.id.to_string()).collect())
4153 }
4154
4155 pub fn create_hold(
4156 &self,
4157 sku: String,
4158 quantity: i32,
4159 reason: String,
4160 hold_type: String,
4161 ) -> PhpResult<String> {
4162 let commerce = lock_commerce!(self.commerce);
4163
4164 let hold = commerce
4165 .quality()
4166 .create_hold(stateset_core::CreateQualityHold {
4167 sku,
4168 quantity_held: quantity,
4169 reason,
4170 hold_type,
4171 ..Default::default()
4172 })
4173 .map_err(|e| PhpException::default(format!("Failed to create hold: {}", e)))?;
4174
4175 Ok(hold.id.to_string())
4176 }
4177
4178 pub fn release_hold(&self, id: String, released_by: String) -> PhpResult<bool> {
4179 let commerce = lock_commerce!(self.commerce);
4180 let uuid = parse_uuid!(id, "hold");
4181
4182 commerce
4183 .quality()
4184 .release_hold(uuid, &released_by)
4185 .map_err(|e| PhpException::default(format!("Failed to release hold: {}", e)))?;
4186
4187 Ok(true)
4188 }
4189}
4190
4191#[php_class(name = "StateSet\\Lots")]
4196#[derive(Clone)]
4197pub struct Lots {
4198 commerce: Arc<Mutex<RustCommerce>>,
4199}
4200
4201#[php_impl]
4202impl Lots {
4203 pub fn create(&self, sku: String, quantity_produced: i32) -> PhpResult<String> {
4204 let commerce = lock_commerce!(self.commerce);
4205
4206 let lot = commerce
4207 .lots()
4208 .create(stateset_core::CreateLot { sku, quantity_produced, ..Default::default() })
4209 .map_err(|e| PhpException::default(format!("Failed to create lot: {}", e)))?;
4210
4211 Ok(lot.id.to_string())
4212 }
4213
4214 pub fn get(&self, id: String) -> PhpResult<Option<String>> {
4215 let commerce = lock_commerce!(self.commerce);
4216 let uuid = parse_uuid!(id, "lot");
4217
4218 let lot = commerce
4219 .lots()
4220 .get(uuid)
4221 .map_err(|e| PhpException::default(format!("Failed to get lot: {}", e)))?;
4222
4223 Ok(lot.map(|l| l.lot_number))
4224 }
4225
4226 pub fn list(&self) -> PhpResult<Vec<String>> {
4227 let commerce = lock_commerce!(self.commerce);
4228
4229 let lots = commerce
4230 .lots()
4231 .list(Default::default())
4232 .map_err(|e| PhpException::default(format!("Failed to list lots: {}", e)))?;
4233
4234 Ok(lots.into_iter().map(|l| l.id.to_string()).collect())
4235 }
4236
4237 pub fn quarantine(&self, id: String, reason: String) -> PhpResult<bool> {
4238 let commerce = lock_commerce!(self.commerce);
4239 let uuid = parse_uuid!(id, "lot");
4240
4241 commerce
4242 .lots()
4243 .quarantine(uuid, &reason)
4244 .map_err(|e| PhpException::default(format!("Failed to quarantine: {}", e)))?;
4245
4246 Ok(true)
4247 }
4248
4249 pub fn release_quarantine(&self, id: String) -> PhpResult<bool> {
4250 let commerce = lock_commerce!(self.commerce);
4251 let uuid = parse_uuid!(id, "lot");
4252
4253 commerce
4254 .lots()
4255 .release_quarantine(uuid)
4256 .map_err(|e| PhpException::default(format!("Failed to release: {}", e)))?;
4257
4258 Ok(true)
4259 }
4260}
4261
4262#[php_class(name = "StateSet\\Serials")]
4267#[derive(Clone)]
4268pub struct Serials {
4269 commerce: Arc<Mutex<RustCommerce>>,
4270}
4271
4272#[php_impl]
4273impl Serials {
4274 pub fn create(&self, sku: String, lot_number: Option<String>) -> PhpResult<String> {
4275 let commerce = lock_commerce!(self.commerce);
4276
4277 let serial = commerce
4278 .serials()
4279 .create(stateset_core::CreateSerial { sku, lot_number, ..Default::default() })
4280 .map_err(|e| PhpException::default(format!("Failed to create serial: {}", e)))?;
4281
4282 Ok(serial.serial_number)
4283 }
4284
4285 pub fn get(&self, id: String) -> PhpResult<Option<String>> {
4286 let commerce = lock_commerce!(self.commerce);
4287 let uuid = parse_uuid!(id, "serial");
4288
4289 let serial = commerce
4290 .serials()
4291 .get(uuid)
4292 .map_err(|e| PhpException::default(format!("Failed to get serial: {}", e)))?;
4293
4294 Ok(serial.map(|s| s.serial_number))
4295 }
4296
4297 pub fn list(&self) -> PhpResult<Vec<String>> {
4298 let commerce = lock_commerce!(self.commerce);
4299
4300 let serials = commerce
4301 .serials()
4302 .list(Default::default())
4303 .map_err(|e| PhpException::default(format!("Failed to list serials: {}", e)))?;
4304
4305 Ok(serials.into_iter().map(|s| s.serial_number).collect())
4306 }
4307
4308 pub fn mark_sold(
4309 &self,
4310 id: String,
4311 customer_id: String,
4312 order_id: Option<String>,
4313 ) -> PhpResult<bool> {
4314 let commerce = lock_commerce!(self.commerce);
4315 let uuid = parse_uuid!(id, "serial");
4316 let cust_uuid = parse_uuid!(customer_id, "customer");
4317 let ord_uuid = order_id
4318 .map(|o| o.parse())
4319 .transpose()
4320 .map_err(|_| PhpException::default("Invalid order UUID".to_string()))?;
4321
4322 commerce
4323 .serials()
4324 .mark_sold(uuid, cust_uuid, ord_uuid)
4325 .map_err(|e| PhpException::default(format!("Failed to mark sold: {}", e)))?;
4326
4327 Ok(true)
4328 }
4329}
4330
4331#[php_class(name = "StateSet\\Warehouse")]
4336#[derive(Clone)]
4337pub struct WarehouseApi {
4338 commerce: Arc<Mutex<RustCommerce>>,
4339}
4340
4341#[php_impl]
4342impl WarehouseApi {
4343 pub fn create_warehouse(
4344 &self,
4345 code: String,
4346 name: String,
4347 warehouse_type: String,
4348 ) -> PhpResult<i32> {
4349 let commerce = lock_commerce!(self.commerce);
4350
4351 let warehouse = commerce
4352 .warehouse()
4353 .create_warehouse(stateset_core::CreateWarehouse {
4354 code,
4355 name,
4356 warehouse_type,
4357 ..Default::default()
4358 })
4359 .map_err(|e| PhpException::default(format!("Failed to create warehouse: {}", e)))?;
4360
4361 Ok(warehouse.id)
4362 }
4363
4364 pub fn get_warehouse(&self, id: i32) -> PhpResult<Option<String>> {
4365 let commerce = lock_commerce!(self.commerce);
4366
4367 let warehouse = commerce
4368 .warehouse()
4369 .get_warehouse(id)
4370 .map_err(|e| PhpException::default(format!("Failed to get warehouse: {}", e)))?;
4371
4372 Ok(warehouse.map(|w| w.name))
4373 }
4374
4375 pub fn list_warehouses(&self) -> PhpResult<Vec<i32>> {
4376 let commerce = lock_commerce!(self.commerce);
4377
4378 let warehouses = commerce
4379 .warehouse()
4380 .list_warehouses()
4381 .map_err(|e| PhpException::default(format!("Failed to list warehouses: {}", e)))?;
4382
4383 Ok(warehouses.into_iter().map(|w| w.id).collect())
4384 }
4385
4386 pub fn create_location(
4387 &self,
4388 warehouse_id: i32,
4389 location_type: String,
4390 zone: Option<String>,
4391 aisle: Option<String>,
4392 ) -> PhpResult<i32> {
4393 let commerce = lock_commerce!(self.commerce);
4394
4395 let location = commerce
4396 .warehouse()
4397 .create_location(stateset_core::CreateLocation {
4398 warehouse_id,
4399 location_type,
4400 zone,
4401 aisle,
4402 ..Default::default()
4403 })
4404 .map_err(|e| PhpException::default(format!("Failed to create location: {}", e)))?;
4405
4406 Ok(location.id)
4407 }
4408}
4409
4410#[php_class(name = "StateSet\\Receiving")]
4415#[derive(Clone)]
4416pub struct Receiving {
4417 commerce: Arc<Mutex<RustCommerce>>,
4418}
4419
4420#[php_impl]
4421impl Receiving {
4422 pub fn create_receipt(
4423 &self,
4424 receipt_type: String,
4425 warehouse_id: i32,
4426 po_id: Option<String>,
4427 ) -> PhpResult<String> {
4428 let commerce = lock_commerce!(self.commerce);
4429 let po_uuid = po_id
4430 .map(|p| p.parse())
4431 .transpose()
4432 .map_err(|_| PhpException::default("Invalid PO UUID".to_string()))?;
4433
4434 let receipt = commerce
4435 .receiving()
4436 .create_receipt(stateset_core::CreateReceipt {
4437 receipt_type,
4438 warehouse_id,
4439 purchase_order_id: po_uuid,
4440 ..Default::default()
4441 })
4442 .map_err(|e| PhpException::default(format!("Failed to create receipt: {}", e)))?;
4443
4444 Ok(receipt.id.to_string())
4445 }
4446
4447 pub fn get_receipt(&self, id: String) -> PhpResult<Option<String>> {
4448 let commerce = lock_commerce!(self.commerce);
4449 let uuid = parse_uuid!(id, "receipt");
4450
4451 let receipt = commerce
4452 .receiving()
4453 .get_receipt(uuid)
4454 .map_err(|e| PhpException::default(format!("Failed to get receipt: {}", e)))?;
4455
4456 Ok(receipt.map(|r| r.receipt_number))
4457 }
4458
4459 pub fn list_receipts(&self) -> PhpResult<Vec<String>> {
4460 let commerce = lock_commerce!(self.commerce);
4461
4462 let receipts = commerce
4463 .receiving()
4464 .list_receipts(Default::default())
4465 .map_err(|e| PhpException::default(format!("Failed to list receipts: {}", e)))?;
4466
4467 Ok(receipts.into_iter().map(|r| r.id.to_string()).collect())
4468 }
4469
4470 pub fn complete_receipt(&self, id: String) -> PhpResult<bool> {
4471 let commerce = lock_commerce!(self.commerce);
4472 let uuid = parse_uuid!(id, "receipt");
4473
4474 commerce
4475 .receiving()
4476 .complete_receipt(uuid)
4477 .map_err(|e| PhpException::default(format!("Failed to complete receipt: {}", e)))?;
4478
4479 Ok(true)
4480 }
4481}
4482
4483#[php_class(name = "StateSet\\Fulfillment")]
4488#[derive(Clone)]
4489pub struct Fulfillment {
4490 commerce: Arc<Mutex<RustCommerce>>,
4491}
4492
4493#[php_impl]
4494impl Fulfillment {
4495 pub fn create_wave(
4496 &self,
4497 warehouse_id: i32,
4498 order_ids: Vec<String>,
4499 priority: i32,
4500 ) -> PhpResult<String> {
4501 let commerce = lock_commerce!(self.commerce);
4502 let uuids: Result<Vec<_>, _> = order_ids.iter().map(|id| id.parse()).collect();
4503 let uuids = uuids.map_err(|_| PhpException::default("Invalid order UUID".to_string()))?;
4504
4505 let wave = commerce
4506 .fulfillment()
4507 .create_wave(stateset_core::CreateWave {
4508 warehouse_id,
4509 order_ids: uuids,
4510 priority,
4511 ..Default::default()
4512 })
4513 .map_err(|e| PhpException::default(format!("Failed to create wave: {}", e)))?;
4514
4515 Ok(wave.id.to_string())
4516 }
4517
4518 pub fn get_wave(&self, id: String) -> PhpResult<Option<String>> {
4519 let commerce = lock_commerce!(self.commerce);
4520 let uuid = parse_uuid!(id, "wave");
4521
4522 let wave = commerce
4523 .fulfillment()
4524 .get_wave(uuid)
4525 .map_err(|e| PhpException::default(format!("Failed to get wave: {}", e)))?;
4526
4527 Ok(wave.map(|w| w.wave_number))
4528 }
4529
4530 pub fn release_wave(&self, id: String) -> PhpResult<bool> {
4531 let commerce = lock_commerce!(self.commerce);
4532 let uuid = parse_uuid!(id, "wave");
4533
4534 commerce
4535 .fulfillment()
4536 .release_wave(uuid)
4537 .map_err(|e| PhpException::default(format!("Failed to release wave: {}", e)))?;
4538
4539 Ok(true)
4540 }
4541
4542 pub fn complete_wave(&self, id: String) -> PhpResult<bool> {
4543 let commerce = lock_commerce!(self.commerce);
4544 let uuid = parse_uuid!(id, "wave");
4545
4546 commerce
4547 .fulfillment()
4548 .complete_wave(uuid)
4549 .map_err(|e| PhpException::default(format!("Failed to complete wave: {}", e)))?;
4550
4551 Ok(true)
4552 }
4553}
4554
4555#[php_class(name = "StateSet\\AccountsPayable")]
4560#[derive(Clone)]
4561pub struct AccountsPayable {
4562 commerce: Arc<Mutex<RustCommerce>>,
4563}
4564
4565#[php_impl]
4566impl AccountsPayable {
4567 pub fn create_bill(
4568 &self,
4569 supplier_id: String,
4570 due_date: String,
4571 payment_terms: Option<String>,
4572 ) -> PhpResult<String> {
4573 let commerce = lock_commerce!(self.commerce);
4574 let supp_uuid = parse_uuid!(supplier_id, "supplier");
4575
4576 let bill = commerce
4577 .accounts_payable()
4578 .create_bill(stateset_core::CreateBill {
4579 supplier_id: supp_uuid,
4580 due_date,
4581 payment_terms,
4582 ..Default::default()
4583 })
4584 .map_err(|e| PhpException::default(format!("Failed to create bill: {}", e)))?;
4585
4586 Ok(bill.id.to_string())
4587 }
4588
4589 pub fn get_bill(&self, id: String) -> PhpResult<Option<String>> {
4590 let commerce = lock_commerce!(self.commerce);
4591 let uuid = parse_uuid!(id, "bill");
4592
4593 let bill = commerce
4594 .accounts_payable()
4595 .get_bill(uuid)
4596 .map_err(|e| PhpException::default(format!("Failed to get bill: {}", e)))?;
4597
4598 Ok(bill.map(|b| b.bill_number))
4599 }
4600
4601 pub fn list_bills(&self) -> PhpResult<Vec<String>> {
4602 let commerce = lock_commerce!(self.commerce);
4603
4604 let bills = commerce
4605 .accounts_payable()
4606 .list_bills(Default::default())
4607 .map_err(|e| PhpException::default(format!("Failed to list bills: {}", e)))?;
4608
4609 Ok(bills.into_iter().map(|b| b.id.to_string()).collect())
4610 }
4611
4612 pub fn get_total_outstanding(&self) -> PhpResult<f64> {
4613 let commerce = lock_commerce!(self.commerce);
4614
4615 let total = commerce
4616 .accounts_payable()
4617 .get_total_outstanding()
4618 .map_err(|e| PhpException::default(format!("Failed to get total: {}", e)))?;
4619
4620 Ok(to_f64_or_nan(total))
4621 }
4622}
4623
4624#[php_class(name = "StateSet\\AccountsReceivable")]
4629#[derive(Clone)]
4630pub struct AccountsReceivable {
4631 commerce: Arc<Mutex<RustCommerce>>,
4632}
4633
4634#[php_impl]
4635impl AccountsReceivable {
4636 pub fn get_total_outstanding(&self) -> PhpResult<f64> {
4637 let commerce = lock_commerce!(self.commerce);
4638
4639 let total = commerce
4640 .accounts_receivable()
4641 .get_total_outstanding()
4642 .map_err(|e| PhpException::default(format!("Failed to get total: {}", e)))?;
4643
4644 Ok(to_f64_or_nan(total))
4645 }
4646
4647 pub fn get_dso(&self, days: i32) -> PhpResult<f64> {
4648 let commerce = lock_commerce!(self.commerce);
4649
4650 let dso = commerce
4651 .accounts_receivable()
4652 .get_dso(days)
4653 .map_err(|e| PhpException::default(format!("Failed to get DSO: {}", e)))?;
4654
4655 Ok(dso)
4656 }
4657
4658 pub fn create_credit_memo(
4659 &self,
4660 customer_id: String,
4661 amount: f64,
4662 reason: String,
4663 ) -> PhpResult<String> {
4664 let commerce = lock_commerce!(self.commerce);
4665 let cust_uuid = parse_uuid!(customer_id, "customer");
4666 let decimal_amount = Decimal::from_f64_retain(amount).unwrap_or_default();
4667
4668 let memo = commerce
4669 .accounts_receivable()
4670 .create_credit_memo(stateset_core::CreateCreditMemo {
4671 customer_id: cust_uuid,
4672 amount: decimal_amount,
4673 reason,
4674 ..Default::default()
4675 })
4676 .map_err(|e| PhpException::default(format!("Failed to create credit memo: {}", e)))?;
4677
4678 Ok(memo.id.to_string())
4679 }
4680}
4681
4682#[php_class(name = "StateSet\\CostAccounting")]
4687#[derive(Clone)]
4688pub struct CostAccounting {
4689 commerce: Arc<Mutex<RustCommerce>>,
4690}
4691
4692#[php_impl]
4693impl CostAccounting {
4694 pub fn get_item_cost(&self, sku: String) -> PhpResult<Option<f64>> {
4695 let commerce = lock_commerce!(self.commerce);
4696
4697 let cost = commerce
4698 .cost_accounting()
4699 .get_item_cost(&sku)
4700 .map_err(|e| PhpException::default(format!("Failed to get cost: {}", e)))?;
4701
4702 Ok(cost.map(|c| to_f64_or_nan(c.current_cost)))
4703 }
4704
4705 pub fn set_item_cost(
4706 &self,
4707 sku: String,
4708 standard_cost: f64,
4709 current_cost: Option<f64>,
4710 ) -> PhpResult<bool> {
4711 let commerce = lock_commerce!(self.commerce);
4712 let std = Decimal::from_f64_retain(standard_cost).unwrap_or_default();
4713 let curr = current_cost.map(|c| Decimal::from_f64_retain(c).unwrap_or_default());
4714
4715 commerce
4716 .cost_accounting()
4717 .set_item_cost(&sku, std, curr)
4718 .map_err(|e| PhpException::default(format!("Failed to set cost: {}", e)))?;
4719
4720 Ok(true)
4721 }
4722
4723 pub fn get_total_inventory_value(&self) -> PhpResult<f64> {
4724 let commerce = lock_commerce!(self.commerce);
4725
4726 let total = commerce
4727 .cost_accounting()
4728 .get_total_inventory_value()
4729 .map_err(|e| PhpException::default(format!("Failed to get total: {}", e)))?;
4730
4731 Ok(to_f64_or_nan(total))
4732 }
4733}
4734
4735#[php_class(name = "StateSet\\Credit")]
4740#[derive(Clone)]
4741pub struct CreditApi {
4742 commerce: Arc<Mutex<RustCommerce>>,
4743}
4744
4745#[php_impl]
4746impl CreditApi {
4747 pub fn create_account(&self, customer_id: String, credit_limit: f64) -> PhpResult<String> {
4748 let commerce = lock_commerce!(self.commerce);
4749 let cust_uuid = parse_uuid!(customer_id, "customer");
4750 let limit = Decimal::from_f64_retain(credit_limit).unwrap_or_default();
4751
4752 let account = commerce
4753 .credit()
4754 .create_account(stateset_core::CreateCreditAccount {
4755 customer_id: cust_uuid,
4756 credit_limit: limit,
4757 ..Default::default()
4758 })
4759 .map_err(|e| PhpException::default(format!("Failed to create account: {}", e)))?;
4760
4761 Ok(account.id.to_string())
4762 }
4763
4764 pub fn check_credit(&self, customer_id: String, order_amount: f64) -> PhpResult<bool> {
4765 let commerce = lock_commerce!(self.commerce);
4766 let cust_uuid = parse_uuid!(customer_id, "customer");
4767 let amount = Decimal::from_f64_retain(order_amount).unwrap_or_default();
4768
4769 let result = commerce
4770 .credit()
4771 .check_credit(cust_uuid, amount)
4772 .map_err(|e| PhpException::default(format!("Failed to check credit: {}", e)))?;
4773
4774 Ok(result.approved)
4775 }
4776
4777 pub fn adjust_limit(
4778 &self,
4779 customer_id: String,
4780 new_limit: f64,
4781 reason: String,
4782 ) -> PhpResult<bool> {
4783 let commerce = lock_commerce!(self.commerce);
4784 let cust_uuid = parse_uuid!(customer_id, "customer");
4785 let limit = Decimal::from_f64_retain(new_limit).unwrap_or_default();
4786
4787 commerce
4788 .credit()
4789 .adjust_limit(cust_uuid, limit, &reason)
4790 .map_err(|e| PhpException::default(format!("Failed to adjust limit: {}", e)))?;
4791
4792 Ok(true)
4793 }
4794}
4795
4796#[php_class(name = "StateSet\\Backorders")]
4801#[derive(Clone)]
4802pub struct Backorders {
4803 commerce: Arc<Mutex<RustCommerce>>,
4804}
4805
4806#[php_impl]
4807impl Backorders {
4808 pub fn create(
4809 &self,
4810 order_id: String,
4811 sku: String,
4812 quantity: i32,
4813 expected_date: Option<String>,
4814 ) -> PhpResult<String> {
4815 let commerce = lock_commerce!(self.commerce);
4816 let ord_uuid = parse_uuid!(order_id, "order");
4817
4818 let backorder = commerce
4819 .backorders()
4820 .create(stateset_core::CreateBackorder {
4821 order_id: ord_uuid,
4822 sku,
4823 quantity,
4824 expected_date,
4825 ..Default::default()
4826 })
4827 .map_err(|e| PhpException::default(format!("Failed to create backorder: {}", e)))?;
4828
4829 Ok(backorder.id.to_string())
4830 }
4831
4832 pub fn get(&self, id: String) -> PhpResult<Option<String>> {
4833 let commerce = lock_commerce!(self.commerce);
4834 let uuid = parse_uuid!(id, "backorder");
4835
4836 let backorder = commerce
4837 .backorders()
4838 .get(uuid)
4839 .map_err(|e| PhpException::default(format!("Failed to get backorder: {}", e)))?;
4840
4841 Ok(backorder.map(|b| b.backorder_number))
4842 }
4843
4844 pub fn list(&self) -> PhpResult<Vec<String>> {
4845 let commerce = lock_commerce!(self.commerce);
4846
4847 let backorders = commerce
4848 .backorders()
4849 .list(Default::default())
4850 .map_err(|e| PhpException::default(format!("Failed to list backorders: {}", e)))?;
4851
4852 Ok(backorders.into_iter().map(|b| b.id.to_string()).collect())
4853 }
4854
4855 pub fn cancel(&self, id: String) -> PhpResult<bool> {
4856 let commerce = lock_commerce!(self.commerce);
4857 let uuid = parse_uuid!(id, "backorder");
4858
4859 commerce
4860 .backorders()
4861 .cancel(uuid)
4862 .map_err(|e| PhpException::default(format!("Failed to cancel backorder: {}", e)))?;
4863
4864 Ok(true)
4865 }
4866
4867 pub fn count_pending(&self) -> PhpResult<i32> {
4868 let commerce = lock_commerce!(self.commerce);
4869
4870 let count = commerce
4871 .backorders()
4872 .count_pending()
4873 .map_err(|e| PhpException::default(format!("Failed to count: {}", e)))?;
4874
4875 Ok(count as i32)
4876 }
4877}
4878
4879#[php_class(name = "StateSet\\GeneralLedger")]
4884#[derive(Clone)]
4885pub struct GeneralLedger {
4886 commerce: Arc<Mutex<RustCommerce>>,
4887}
4888
4889#[php_impl]
4890impl GeneralLedger {
4891 pub fn create_account(
4892 &self,
4893 account_number: String,
4894 name: String,
4895 account_type: String,
4896 ) -> PhpResult<String> {
4897 let commerce = lock_commerce!(self.commerce);
4898
4899 let account = commerce
4900 .general_ledger()
4901 .create_account(stateset_core::CreateGlAccount {
4902 account_number,
4903 name,
4904 account_type,
4905 ..Default::default()
4906 })
4907 .map_err(|e| PhpException::default(format!("Failed to create account: {}", e)))?;
4908
4909 Ok(account.id.to_string())
4910 }
4911
4912 pub fn get_account(&self, id: String) -> PhpResult<Option<String>> {
4913 let commerce = lock_commerce!(self.commerce);
4914 let uuid = parse_uuid!(id, "account");
4915
4916 let account = commerce
4917 .general_ledger()
4918 .get_account(uuid)
4919 .map_err(|e| PhpException::default(format!("Failed to get account: {}", e)))?;
4920
4921 Ok(account.map(|a| a.name))
4922 }
4923
4924 pub fn list_accounts(&self) -> PhpResult<Vec<String>> {
4925 let commerce = lock_commerce!(self.commerce);
4926
4927 let accounts = commerce
4928 .general_ledger()
4929 .list_accounts()
4930 .map_err(|e| PhpException::default(format!("Failed to list accounts: {}", e)))?;
4931
4932 Ok(accounts.into_iter().map(|a| a.id.to_string()).collect())
4933 }
4934
4935 pub fn get_account_balance(
4936 &self,
4937 account_id: String,
4938 as_of_date: Option<String>,
4939 ) -> PhpResult<f64> {
4940 let commerce = lock_commerce!(self.commerce);
4941 let uuid = parse_uuid!(account_id, "account");
4942
4943 let balance = commerce
4944 .general_ledger()
4945 .get_account_balance(uuid, as_of_date.as_deref())
4946 .map_err(|e| PhpException::default(format!("Failed to get balance: {}", e)))?;
4947
4948 Ok(to_f64_or_nan(balance))
4949 }
4950
4951 pub fn initialize_chart_of_accounts(&self) -> PhpResult<Vec<String>> {
4952 let commerce = lock_commerce!(self.commerce);
4953
4954 let accounts = commerce
4955 .general_ledger()
4956 .initialize_chart_of_accounts()
4957 .map_err(|e| PhpException::default(format!("Failed to initialize COA: {}", e)))?;
4958
4959 Ok(accounts.into_iter().map(|a| a.id.to_string()).collect())
4960 }
4961}
4962
4963#[php_module]
4968pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
4969 module
4970}