PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.28
Booktics – Appointment Booking Calendar for Service Businesses v1.0.28
1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 All 31 releases
booktics / core / module / woocommerce-addon / service / status-mapper.php

status-mapper.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.28, at core/module/woocommerce-addon/service/status-mapper.php

193 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Booktics\Module\Woocommerce_Addon\Service;
4
5 if ( ! defined( 'ABSPATH' ) ) exit;
6
7 use Booktics\Payment\Payment_Status;
8
9 /**
10 * Translates between the WooCommerce and Booktics order status domains, and
11 * stops the two sides from chasing each other.
12 *
13 * WooCommerce has seven statuses; a Booktics order has three. Writing one
14 * domain's value straight into the other leaves an order carrying a status its
15 * own screens cannot render, so every crossing goes through here.
16 *
17 * The pair lock exists because the sync runs in both directions: cancelling in
18 * WooCommerce cancels the booking, which cancels the Booktics order, which
19 * would push a cancellation straight back to WooCommerce. Marking a pair as
20 * syncing for the duration of one crossing breaks that loop, and a `finally`
21 * always releases it so a thrown exception cannot wedge a pair shut.
22 */
23 class Status_Mapper {
24
25 /**
26 * Booktics order id => WooCommerce order id pairs currently syncing.
27 *
28 * @var array<string, bool>
29 */
30 private static $syncing_pairs = array();
31
32 /**
33 * Map a WooCommerce order status onto the Booktics order status domain.
34 *
35 * `failed` deliberately maps to `open`, not `canceled`: WooCommerce treats a
36 * failed order as retryable and the customer can pay it from the same URL,
37 * so the slot stays held rather than being handed to someone else.
38 *
39 * @param string $wc_status WooCommerce status, with or without the wc- prefix.
40 *
41 * @return string|null Booktics order status, or null when it has no mapping.
42 */
43 public static function to_booktics_status( string $wc_status ) {
44 $wc_status = str_replace( 'wc-', '', $wc_status );
45
46 $map = array(
47 'pending' => 'open',
48 'on-hold' => 'open',
49 'processing' => 'open',
50 'failed' => 'open',
51 'completed' => 'completed',
52 'cancelled' => 'canceled',
53 'refunded' => 'canceled',
54 );
55
56 return $map[ $wc_status ] ?? null;
57 }
58
59 /**
60 * Map a WooCommerce order status onto a Booktics payment status.
61 *
62 * This is a straight, per-status mapping rather than a paid/not-paid guess:
63 * a processing order reads as "processing", not "fully paid", so the booking
64 * is only marked fully paid once WooCommerce marks the order completed (by a
65 * gateway that settles late, or by the admin closing it out).
66 *
67 * completed -> fully_paid
68 * processing -> processing
69 * on-hold -> not_paid
70 * pending -> not_paid
71 * failed -> not_paid
72 * cancelled -> cancelled
73 * refunded -> refunded
74 *
75 * @param string $wc_status WooCommerce status, with or without the wc- prefix.
76 *
77 * @return string Booktics payment status.
78 */
79 public static function to_payment_status( string $wc_status ): string {
80 $wc_status = str_replace( 'wc-', '', $wc_status );
81
82 $map = array(
83 'completed' => Payment_Status::COMPLETED,
84 'processing' => Payment_Status::PROCESSING,
85 'on-hold' => Payment_Status::FAILED,
86 'pending' => Payment_Status::FAILED,
87 'failed' => Payment_Status::FAILED,
88 'cancelled' => Payment_Status::CANCELLED,
89 'refunded' => Payment_Status::REFUNDED,
90 );
91
92 return $map[ $wc_status ] ?? Payment_Status::FAILED;
93 }
94
95 /**
96 * Whether a payment status means WooCommerce has the customer's money.
97 *
98 * Both processing and fully paid mean the money has been taken; the booking
99 * confirmation fires on the first crossing into either, so a later move from
100 * processing to completed does not mail the customer a second time.
101 *
102 * @param string $payment_status Booktics payment status.
103 *
104 * @return bool
105 */
106 public static function is_paid_payment_status( string $payment_status ): bool {
107 return in_array(
108 $payment_status,
109 array( Payment_Status::PROCESSING, Payment_Status::COMPLETED ),
110 true
111 );
112 }
113
114 /**
115 * Map a Booktics order status onto a WooCommerce status.
116 *
117 * `open` has no counterpart: it covers everything from unpaid to paid and
118 * would otherwise drag a processing order backwards.
119 *
120 * @param string $booktics_status Booktics order status.
121 *
122 * @return string|null WooCommerce status without the wc- prefix, or null.
123 */
124 public static function to_wc_status( string $booktics_status ) {
125 $map = array(
126 'canceled' => 'cancelled',
127 'cancelled' => 'cancelled',
128 'completed' => 'completed',
129 );
130
131 return $map[ $booktics_status ] ?? null;
132 }
133
134 /**
135 * Whether a Booktics status means the booking is over and the slot is free.
136 *
137 * @param string $booktics_status Booktics order status.
138 *
139 * @return bool
140 */
141 public static function is_canceled( string $booktics_status ): bool {
142 return in_array( $booktics_status, array( 'canceled', 'cancelled' ), true );
143 }
144
145 /**
146 * Is this booking/order pair mid-sync?
147 *
148 * @param int $booktics_order_id Booktics order id.
149 * @param int $wc_order_id WooCommerce order id.
150 *
151 * @return bool
152 */
153 public static function is_syncing( int $booktics_order_id, int $wc_order_id ): bool {
154 return isset( self::$syncing_pairs[ self::pair_key( $booktics_order_id, $wc_order_id ) ] );
155 }
156
157 /**
158 * Mark a pair as syncing.
159 *
160 * @param int $booktics_order_id Booktics order id.
161 * @param int $wc_order_id WooCommerce order id.
162 *
163 * @return void
164 */
165 public static function begin_sync( int $booktics_order_id, int $wc_order_id ): void {
166 self::$syncing_pairs[ self::pair_key( $booktics_order_id, $wc_order_id ) ] = true;
167 }
168
169 /**
170 * Release a pair.
171 *
172 * @param int $booktics_order_id Booktics order id.
173 * @param int $wc_order_id WooCommerce order id.
174 *
175 * @return void
176 */
177 public static function end_sync( int $booktics_order_id, int $wc_order_id ): void {
178 unset( self::$syncing_pairs[ self::pair_key( $booktics_order_id, $wc_order_id ) ] );
179 }
180
181 /**
182 * Key identifying a booking/order pair.
183 *
184 * @param int $booktics_order_id Booktics order id.
185 * @param int $wc_order_id WooCommerce order id.
186 *
187 * @return string
188 */
189 private static function pair_key( int $booktics_order_id, int $wc_order_id ): string {
190 return $booktics_order_id . '_' . $wc_order_id;
191 }
192 }
193