PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.19
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.19
1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 All 163 releases
woocommerce-pos / includes / Services / Order_Write_Intent.php

Order_Write_Intent.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.19, at includes/Services/Order_Write_Intent.php

154 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * POS order write intent.
4 *
5 * @package WCPOS\WooCommercePOS
6 */
7
8 namespace WCPOS\WooCommercePOS\Services;
9
10 /**
11 * Names the subject and client intent before priority-10 order-write readers run.
12 *
13 * Priority 1 binds the prepared order and applies declared date/meta mutations first.
14 * Tax needs the client's status: WooCommerce recalculates before applying it, and
15 * around_paid_create() neutralises the request to pending before preparation.
16 * Stock_Validator deliberately reads that neutralised request, not this intent:
17 * its pending exemption avoids validating the unsaved order before reservation.
18 */
19 final class Order_Write_Intent {
20 /**
21 * Declared contexts, with an optional last ad-hoc observation below.
22 *
23 * @var self[]
24 */
25 private static $stack = array();
26 /**
27 * Whether a lane declared this context.
28 *
29 * @var bool
30 */
31 private $declared = false;
32 /**
33 * Declaration or request-derived intent.
34 *
35 * @var array
36 */
37 private $data = array();
38 /**
39 * Exact prepared order, once bound.
40 *
41 * @var \WC_Abstract_Order|null
42 */
43 private $subject;
44
45 /** Register once, before the stock gate and other order-write readers. */
46 public static function register(): void {
47 add_filter( 'woocommerce_rest_pre_insert_shop_order_object', array( self::class, 'publish' ), 1, 3 );
48 }
49
50 /**
51 * Bind a declared subject or replace the last ad-hoc POS observation.
52 *
53 * @param mixed $order Prepared order or an upstream filter result.
54 * @param mixed $request REST request when available.
55 * @param bool $creating Whether WooCommerce is creating the order.
56 * @return mixed The filter value, unchanged.
57 */
58 public static function publish( $order, $request = null, $creating = false ) {
59 if ( ! $order instanceof \WC_Abstract_Order ) {
60 return $order;
61 }
62 $intent = self::current();
63 if ( null !== $intent && $intent->declared ) {
64 // A declaration is the lane's own word that this is a POS write; only
65 // the ad-hoc observation below has to learn that from the request.
66 $matches = $intent->is_create() ? $creating : ( ! $creating && $order->get_id() === $intent->id() );
67 if ( null === $intent->subject && $matches ) {
68 $intent->subject = $order;
69 foreach ( $intent->data['fill_meta'] ?? array() as $key => $value ) {
70 $order->update_meta_data( $key, $value );
71 }
72 if ( isset( $intent->data['created_gmt'] ) ) {
73 $order->set_date_created( $intent->data['created_gmt'] );
74 }
75 }
76 } elseif ( \wcpos_request() ) {
77 $intent = new self();
78 $intent->data = array(
79 'operation' => $creating ? 'create' : 'update',
80 'id' => $order->get_id(),
81 'requested_status' => $request instanceof \WP_REST_Request ? (string) $request->get_param( 'status' ) : '',
82 'set_paid' => $request instanceof \WP_REST_Request && $request->has_param( 'set_paid' ) && rest_sanitize_boolean( $request->get_param( 'set_paid' ) ),
83 );
84 $intent->subject = $order;
85 self::$stack = array( $intent );
86 }
87 return $order;
88 }
89
90 /**
91 * Declare a write, restoring the enclosing intent even if the write throws.
92 *
93 * @param array $declared Operation, id, requested_status, set_paid, created_gmt, fill_meta.
94 * @param callable $write The order write.
95 * @return mixed The write result.
96 */
97 public static function open( array $declared, callable $write ) {
98 $intent = new self();
99 $intent->declared = true;
100 $intent->data = $declared;
101 self::$stack[] = $intent;
102 try {
103 return $write();
104 } finally {
105 array_pop( self::$stack );
106 }
107 }
108
109 /** Return the enclosing declaration or last ad-hoc observation. */
110 public static function current(): ?self {
111 return self::$stack ? end( self::$stack ) : null;
112 }
113
114 /** Return the declared or observed operation. */
115 public function operation(): string {
116 return $this->data['operation'];
117 }
118
119 /** Whether this write creates an order. */
120 public function is_create(): bool {
121 return 'create' === $this->operation();
122 }
123
124 /** Return the declared or observed order ID (zero for an unsaved create). */
125 public function id(): int {
126 return (int) ( $this->data['id'] ?? 0 );
127 }
128
129 /** Return the client's raw status, before stock neutralisation. */
130 public function requested_status(): string {
131 return (string) ( $this->data['requested_status'] ?? '' );
132 }
133
134 /** Whether the client asked to mark the order paid. */
135 public function set_paid(): bool {
136 return (bool) ( $this->data['set_paid'] ?? false );
137 }
138
139 /** Return the prepared subject, or null before binding. */
140 public function subject(): ?\WC_Abstract_Order {
141 return $this->subject;
142 }
143
144 /**
145 * Test exact prepared-order identity, never an unbound context.
146 *
147 * @param mixed $order Candidate subject.
148 * @return bool
149 */
150 public function is_subject( $order ): bool {
151 return null !== $this->subject && $this->subject === $order;
152 }
153 }
154